共享库注射--injectso实例
共享库注射--injectso实例
来源http://www.whitecell.org/forums/viewtopic.php?topic=3885&;forum=16&0
共享库注射--injectso实例
Grip2
Joined: Feb 17, 2002
Posts: 139 Posted: 2002-08-20 10:55
--------------------------------------------------------------------------------
Code:
--------------------------------------------------------------------------------
共享库注射--injectso实例
作者:grip2
日期:2002/08/16
内容:
1 -- 介绍
2 -- injectso -- 共享库注射技术
3 -- injectso的事情步骤及实现方法
4 -- 目标进程调试函数
5 -- 符号剖析函数
6 -- 一个简单的后门步伐
7 -- 最后
8 -- 参考文献
一、 ** 介绍
本文介绍的是injectso技术,重点是利用现有技术去实际的完成一个injectso步伐,
而不是偏重于理论上的探究。这里希望你在阅读这篇文章的时候对ELF、inject有一
定的相识,当然你也可以选择在看完本文之后再去翻看相关的资料,也许如许能使你
更有针对性。需要说明的是,下面介绍的技术和给出的函数都是特定于X86下的linux
的,在别的情况下大概有一些需要转变的细节,但从基本的观点和步骤上讲应该是相
同的。
二、 ** injectso -- 共享库注射技术
利用injectso技术,我们可以注射共享库到一个运转期进程,这里注射的意思就是通
过某种操作使我们的.so共享库在指定的进程中被装载,如许再配合上函数重定向或
别的技术,我们就可以捕获或转变目标进程的举动,可以做非常多的事情。同别的
inject技术相比,injectso的一些好处是:
1. 简单 -- 仅仅经过C代码就可以完成全部的事情;
2. 扩展性好 -- 在基础代码完成之后,如果要对步伐功效进行增加、修改,仅需窜改
.so共享库即可;
3. 洁净 -- 对目标进程进行注射之后,不需要留下磁盘文件,利用的步伐及共享库
都可以删除;
4. 机动 -- 我们可以利用它完成许多事情,比方:运转期补丁、后门步伐等;
5. 目标服务不需要重新启动;
6. 无须窜改二进制文件;
7. 可以经过pax, openwall等如许的核心补丁。
三、 ** injectso的事情步骤及实现方法
完成injectso需要以下几个步骤:
1. 接洽关系到目标进程;
2. 发明装载共享库的函数,一般是_dl_open挪用,我们将利用它装载我们的.so共享
库
3. 装载指定的.so;
4. 做我们想做的,一般是经过函数重定向来完成我们需要的功效;
5. 离开进程;
下面简单介绍一下这几个步骤的实现方法,由于我们是对别的进程进行操作,因此
ptrace这个linux调试API函数将频仍的被我们利用,在<四>中,我将给出一些ptrace
包装函数。
步骤1 -- 接洽关系进程
简单的挪用ptrace(PTRACE_ATTACH,...)即可以接洽关系到目标进程,但此后我们还
需挪用waitpid()函数等待目标进程停息,以便我们进行后续操作。详见<四>中给出
的ptrace_attach()函数。
步骤2 -- 发明_dl_open
经过遍历静态毗连器利用的link_map布局及其指向的相关链表,我们可以完成
_dl_open的符号剖析事情,关于经过link_map剖析符号在phrack59包的p59_08(见参
考文献)中有细致的描述。
步骤3 -- 装载.so
由于在2中我们已经找到_dl_open的地点,所以我们只需将此函数利用的参数添
入相应的寄存器,并将进程的eip指向_dl_open即可,在此历程中还需做一些别的操
作,详细内容见<四>中的call_dl_open和ptrace_call函数。
步骤4 -- 函数重定向
我们需要做的仅仅是找到相关的函数地点,用新函数替换旧函数,并将旧函数的
地点保存。此中触及到了PLT和RELOCATION,关于它们的细致内容你应该看ELF范例中
的介绍,在<四>中的函数中有PLT和RELOCATION的相关操作,而且在最后的例子中,
我们将实现函数重定向。关于函数重定向,相关资料许多,这里不再多介绍。
步骤5 -- 离开进程
简单的挪用ptrace(PTRACE_DETACH,...)可以离开目标进程。
四、** 目标进程调试函数
在linux中,如果我们要调试一个进程,可以利用ptrace API函数,为了利用起来更
方便,我们需要对它进行一些功效上的封装。
在p59_08中作者给出了一些对ptrace进行封装的函数,但是那太少了,在下面我给出
了更多的函数,这足够我们利用了。要注意在这些函数中我并未进行太多的错误检测
,但做为一个例子利用,它已经能很好的事情了,在最后的例子中你将能看到这一点
。
/* 接洽关系到进程 */
void ptrace_attach(int pid)
{
if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
perror("ptrace_attach");
exit(-1);
}
waitpid(pid, NULL, WUNTRACED);
ptrace_readreg(pid, &oldregs);
}
/* 进程连续 */
void ptrace_cont(int pid)
{
int stat;
if(ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
perror("ptrace_cont");
exit(-1);
}
while(!WIFSTOPPED(stat))
waitpid(pid, &stat, WNOHANG);
}
/* 离开进程 */
void ptrace_detach(int pid)
{
ptrace_writereg(pid, &oldregs);
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
perror("ptrace_detach");
exit(-1);
}
}
/* 写指定进程地点 */
void ptrace_write(int pid, unsigned long addr, void *vptr, int len)
{
int count;
long word;
count = 0;
while(count < len) {
memcpy(&word, vptr + count, sizeof(word));
word = ptrace(PTRACE_POKETEXT, pid, addr + count, word);
count += 4;
if(errno != 0)
printf("ptrace_write failed\t %ld\n", addr + count);
}
}
/* 读指定进程 */
void ptrace_read(int pid, unsigned long addr, void *vptr, int len)
{
int i,count;
long word;
unsigned long *ptr = (unsigned long *)vptr;
i = count = 0;
while (count < len) {
word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
count += 4;
ptr[i++] = word;
}
}
/*
在进程指定地点读一个字符串
*/
char * ptrace_readstr(int pid, unsigned long addr)
{
char *str = (char *) malloc(64);
int i,count;
long word;
char *pa;
i = count = 0;
pa = (char *)&word;
while(i <= 60) {
word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
count += 4;
if (pa[0] == '\0') {
str[i] = '\0';
break;
}
else
str[i++] = pa[0];
if (pa[1] == '\0') {
str[i] = '\0';
break;
}
else
str[i++] = pa[1];
if (pa[2] == '\0') {
str[i] = '\0';
break;
}
else
str[i++] = pa[2];
if (pa[3] == '\0') {
str[i] = '\0';
break;
}
else
str[i++] = pa[3];
}
return str;
}
/* 读进程寄存器 */
void ptrace_readreg(int pid, struct user_regs_struct *regs)
{
if(ptrace(PTRACE_GETREGS, pid, NULL, regs))
printf("AV女优 ptrace_readreg error AV女优\n");
}
/* 写进程寄存器 */
void ptrace_writereg(int pid, struct user_regs_struct *regs)
{
if(ptrace(PTRACE_SETREGS, pid, NULL, regs))
printf("AV女优 ptrace_writereg error AV女优\n");
}
/*
将指定命据压入进程堆栈并返回堆栈指针
*/
void * ptrace_push(int pid, void *paddr, int size)
{
unsigned long esp;
struct user_regs_struct regs;
ptrace_readreg(pid, ?s);
esp = regs.esp;
esp -= size;
esp = esp - esp % 4;
regs.esp = esp;
ptrace_writereg(pid, ?s);
ptrace_write(pid, esp, paddr, size);
return (void *)esp;
}
/*
在进程内挪用指定地点的函数
*/
void ptrace_call(int pid, unsigned long addr)
{
void *pc;
struct user_regs_struct regs;
int stat;
void *pra;
pc = (void *) 0x41414140;
pra = ptrace_push(pid, &pc, sizeof(pc));
ptrace_readreg(pid, ?s);
regs.eip = addr;
ptrace_writereg(pid, ?s);
ptrace_cont(pid);
while(!WIFSIGNALED(stat))
waitpid(pid, &stat, WNOHANG);
}
上面给出的函数很简单,我想不需要更多的说明。但是,还有两个中央我想简单说一
下,第一个是在接洽关系进程的函数和离开进程的函数中对寄存器的操作是必需的,在注
射共享库的操作最后,我们需要规复目标进程的寄存器内容,以使目标进程正常的恢
复到运转形态。第二个是在ptrace_call中的这处:
pc = (void *) 0x41414140;
pra = ptrace_push(pid, &pc, sizeof(pc));
在这里,我们将无效页面地点0x41414140压入目标进程堆栈,如许当步伐实行完我们
指定的函数后,步伐会产生错误停止,所以我们就又得到了对进程的控制权,可以继
续下面的操作。
五、** 符号剖析函数
因为我们需要对进程中的函数挪用进行重定向,所以需要对一些函数符号进行剖析。
下面给出了一些剖析符号的函数,你大概会发明有些变量没有界说,这是因为它们是
全局变量,变量的范例应该很容易确定,如果你需要利用这些函数,只需简单声明全
局变量即可,如果有问题,也可与我接洽。这些函数异样没有太多的错误检测,但是
作为例子,它们已足够精良的运转了。此中ELF相关的内容你也许不是很明白,我想
仔细阅读ELF范例对你很有资助,联合ELF与下面的实例你应该能很快的明白符号剖析
/*
取得指向link_map链表首项的指针
*/
#define IMAGE_ADDR 0x08048000
struct link_map * get_linkmap(int pid)
{
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));
Elf32_Phdr *phdr = (Elf32_Phdr *) malloc(sizeof(Elf32_Phdr));
Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
Elf32_Word got;
struct link_map *map = (struct link_map *)
malloc(sizeof(struct link_map));
int i = 0;
ptrace_read(pid, IMAGE_ADDR, ehdr, sizeof(Elf32_Ehdr));
phdr_addr = IMAGE_ADDR + ehdr->e_phoff;
printf("phdr_addr\t %p\n", phdr_addr);
ptrace_read(pid, phdr_addr, phdr, sizeof(Elf32_Phdr));
while(phdr->p_type != PT_DYNAMIC)
ptrace_read(pid, phdr_addr += sizeof(Elf32_Phdr), phdr,
sizeof(Elf32_Phdr));
dyn_addr = phdr->p_vaddr;
printf("dyn_addr\t %p\n", dyn_addr);
ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
while(dyn->d_tag != DT_PLTGOT) {
ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
i++;
}
got = (Elf32_Word)dyn->d_un.d_ptr;
got += 4;
printf("GOT\t\t %p\n", got);
ptrace_read(pid, got, &map_addr, 4);
printf("map_addr\t %p\n", map_addr);
ptrace_read(pid, map_addr, map, sizeof(struct link_map));
free(ehdr);
free(phdr);
free(dyn);
return map;
}
/*
取得给定link_map指向的SYMTAB、STRTAB、HASH、JMPREL、PLTRELSZ、RELAENT、RELENT信息
这些地点信息将被保存到全局变量中,以方便利用
*/
void get_sym_info(int pid, struct link_map *lm)
{
Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
unsigned long dyn_addr;
dyn_addr = (unsigned long)lm->l_ld;
ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
while(dyn->d_tag != DT_NULL){
switch(dyn->d_tag)
{
case DT_SYMTAB:
symtab = dyn->d_un.d_ptr;
//puts("DT_SYMTAB");
break;
case DT_STRTAB:
strtab = dyn->d_un.d_ptr;
//puts("DT_STRTAB");
break;
case DT_HASH:
ptrace_read(pid, dyn->d_un.d_ptr + lm->l_addr + 4,
&nchains, sizeof(nchains));
//puts("DT_HASH");
break;
case DT_JMPREL:
jmprel = dyn->d_un.d_ptr;
//puts("DT_JMPREL");
break;
case DT_PLTRELSZ:
//puts("DT_PLTRELSZ");
totalrelsize = dyn->d_un.d_val;
break;
case DT_RELAENT:
relsize = dyn->d_un.d_val;
//puts("DT_RELAENT");
break;
case DT_RELENT:
relsize = dyn->d_un.d_val;
//puts("DT_RELENT");
break;
}
ptrace_read(pid, dyn_addr += sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
}
nrels = totalrelsize / relsize;
free(dyn);
}
/*
剖析指定符号
*/
unsigned long find_symbol(int pid, struct link_map *map, char *sym_name)
{
struct link_map *lm = (struct link_map *) malloc(sizeof(struct link_map));
unsigned long sym_addr;
- 文章作者: 福州军威计算机技术有限公司
军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。
TAG:
评论加载中...
|