在NT环境下隐藏进程,也就是说在用户不知情的条件下,执行自己的代码的 方法有很多种,比如说使用注册表插入DLL,使用Windows挂钩等等。其中比较有 代表性的是Jeffrey Richer在《Windows核心编程》中介绍的LoadLibrary方法和 罗云彬在《windows环境下32位汇编语言程序设计》中介绍的方法。两种方法的 共同特点是:都采用远程线程,让自己的代码作为宿主进程的线程在宿主进程的 地址空间中执行,从而达到隐藏的目的。相比较而言,Richer的方法由于可以使 用c/c++等高级语言完成,理解和实现都比较容易,但他让宿主进程使用 LoadLibrary来装入新的DLL,所以难免留下蛛丝马迹,隐藏效果并不十分完美。 罗云彬的方法在隐藏效果上绝对一流,不过,由于他使用的是汇编语言,实现起 来比较难(起码我写不了汇编程序:))。笔者下面介绍的方法可以说是对上述两 种方法的综合:采用c/c++编码,实现完全隐藏。并且,笔者的方法极大的简化 了远程线程代码的编写,使其编写难度与普通程序基本一致。
基础知识
让自己的代码作为宿主进程的线程,在宿主进程的地址空间中执行确实是个 不错的主意。但是要自己把程序放到其他进程的地址空间中去运行,将面临一个 严峻的问题:如何实现代码重定位。关于重定位问题,请看下面的程序:
…
int func()//函数func的定义
…
int a = func();//对func的调用
…
这段程序经过编译链接后,可能会变成下面的样子:
…
0x00401800: push ebp//这是函数func的入口
0x00401801: mov ebp, esp
…
0x00402000: call 00401800//对函数func的调用
0x00402005: mov dword ptr [ebp-08], eax
…
请注意“0x00402000”处的直接寻址指令“call 00401800”。上面的程序在正常执行(由windows装入并执行)时,因为PE 文件的文件头中含有足够的信息,所以系统能够将代码装入到合适的位置从而保 证地址“00401800”处就是函数func的入口。但是当我们自己把程序 装入到其他进程的地址空间中时,我们无法保证这一点,最终的结果可能会象下 面这样:
…
0x00801800: push ebp//这是函数func的入口
0x00801801: mov ebp, esp
…
0x00802000: call 00401800//00401800处是什么
0x00802005: mov dword ptr [ebp-08], eax
…
显然,运行上面的代码将产生不可预料的结果(最大的可能就是执行我们费 尽千辛万苦才装入的代码的线程连同宿主进程一起被系统杀死)。 不知大家注 意过系统中动态链接库(dll)的装入没有:一个dll被装入不同进程时,装入的 地址可能不同,所以系统在这种情况下也必须解决dll中直接寻址指令的重定位 问题。原来,绝大多数dll中都包含一些由编译器插入的用于重定位的数据,这 些数据就构成了重定位表。系统根据重定位表中的数据,修改dll的代码,完成 重定位操作。Richer使用的LoadLibrary也是借用了这一点。所以我们的重定位 方法就是:替系统来完成工作,自己根据重定位表中的数据进行重定位。既然如 此,那就让我们来了解一下重定位表吧。
先来分析一下重定位表中需要保存哪些信息。还以上面的代码为例,要让它 能正确执行,就必须把指令“call 00401800”改为“call 00801800”。进行这一改动需要两个数据,第一是改哪,也就是哪个内存 地址中的数据需要修改,这里是“0x00802001”(不是 “0x00802000”);第二是怎么改,也就是应该给该位置的数据加上 多少,这里是“0x00400000”。这第二个数据可以从dll的实际装入 地址和建议装入地址计算而来,只要让前者减后者就行了。其中实际装入地址装 入的时候就会知道,而建议装入地址记录在文件头的ImageBase字段中。所以, 综上所述,重定位表中需要保存的信息是:有待修正的数据的地址。
位置 | 数据 | 描述 |
0000h | 00001000h | 页起始地址(RVA) |
0004h | 00000010h | 重定位块长度 |
0008h | 3006h | 第一个重定位项,32位都须修正 |
000ah | 300dh | 第二个重定位项,32位都须修正 |
000ch | 3015h | 第三个重定位项,32位都须修正 |
000eh | 0000h | 第四个重定位项,用于对齐 |
0010h | 00003000h | 页起始地址(RVA) |
0014h | 0000000ch | 重定位块长度 |
0018h | 3008h | 第一个重定位项,32位都须修正 |
001ah | 302ah | 第二个重定位项,32位都须修正 |
… | … | 其他重定位块 |
0100h | 0000h | 重定位表结束标志 |