百科知识库-中国实用知识供应者
网站地图设为首页加入收藏
百科知识库
您当前的位置:百科知识库科学技术电脑技术网管天地共享库注射--injectso实例
知识导航
共享库注射--injectso实例

共享库注射--injectso实例 共享库注射--injectso实例
Grip2


Joined: Feb 17, 2002
Posts: 139 Posted: 2002-08-20 10:55
--------------------------------------------------------------------------------
Code:
--------------------------------------------------------------------------------

  共享库注射--injectso实例


作者:grip2 <gript2@hotmail.com>
日期: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);
  }

  w

   

aitpid(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 == '\0') {
  str[i] = '\0';
  break;
  }
  else
  str[i++] = pa;

  if (pa == '\0') {
  str[i] = '\0';
  break;
  }
  else
  str[i++] = pa;

  if (pa == '\0') {
  str[i] = '\0';
  break;
  }
  else
  str[i++] = pa;
  }
 
  return str;
}

/* 读进程寄存器 */
void ptrace_readreg(int pid, struct user_regs_struct *regs)
{
  if(ptrace(PTRACE_GETREGS, pid, NULL, regs))
  printf("*** ptrace_readreg error ***\n");

}

/* 写进程寄存器 */
void ptrace_writereg(int pid, struct user_regs_struct *regs)
{

     

  if(ptrace(PTRACE_SETREGS, pid, NULL, regs))
  printf("*** ptrace_writereg error ***\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;
}

/*
在进

     

 
百科知识库 版权所有

Copyright © 2007-2009 www.zsku.net, All Rights Reserved

本站所收集信息资料为网络转载 版权属各作者 并已著明作者 旨在资源共享、交流、学习之用,请勿用于商业用途,本站并不保证所有信息、文本、图形、链接及其它内容的绝对准确性和完整性,故仅供访问者参照使用。