目录
一、Why?
根据先前的代码植入实验与定位shellcode实验,我们可以发现在本机上的API入口地址与书上的API入口地址不同,因此,导致我们手工用Dependency Walker在本机上找到的API入口地址会在其他计算机上失效,极大的影响了exploit的通用性。而产生此种现象的原因主要有以下几点:
- 不同操作系统版本:windows 2000,windwos XP等会影响动态链接库的加载基址;
- 不同的补丁版本:很多安全补丁会修改这些动态链接库中的函数,使得不同版本补丁对应的动态链接库的内容有所不同,包括动态链接库文件的大小和导出函数的偏移地址。
二、How?
以Windows操作系统为例,windows的API是通过动态链接库中的导出函数来实现的,例如,内存操作等函数在kernel32.dll中实现;大量与图像界面有关的API则在user32.dll中实现。Win_32平台下的shellcode使用最广泛的方法,就是通过从进程环境块中找到动态链接库的导出表,并搜索出所需的API地址,然后注意调用。
所有的Win_32程序都会加载ntdll.dll和kernel32.dll这两个最基础的动态链接库。以定位kernel32.dll中的API地址为例,可以采用如下方法:
- 首先通过段选择字FS在内存中找到当前的线程环境块TEB。
- 线程环境块偏移位置为0x30的地方存放着指向进程环境块PEB的指针。
- 进程环境块中偏移位置为0xOC的地方存放着指向PEB_LDR_DATA结构体的指针,其中,存放着已经被进程装载的动态链接库的信息。
- PEB_LDR_DATA结构体偏移位置为0xlC的地方存放着指向模块初始化链表的头指针InInitializationOrderModuleList。
- 模块初始化链表InInitializationOrderModuleList 中按顺序存放着PE装入运行时初始化模块的信息,第一个链表结点是ntdll.dll,第二个链表结点就是kernel32.dll。
- 找到属于kernel32.dll的结点后,在其基础上再偏移0x08就是kernel32.dll在内存中的加载基地址。
- 从kernel32.dll 的加载基址算起,偏移0x3C的地方就是其PE头。
- PE头偏移Ox78的地方存放着指向函数导出表的指针。
至此,我们可以按照以下方式在函数导出表中计算出所需函数的入口地址:
- 导出表偏移Ox1C处的指针指向存储导出函数偏移地址(RVA)的列表。
- 导出表偏移0x20 处的指针指向存储导出函数函数名的列表。
- 函数的RVA地址和名字按照顺序存放在上述两个列表中,我们可以在名称列表中定位
到所需的函数是第几个,然后在地址列表中找到对应的 RVA 。 - 获得RVA后,再加上前边已经得到的动态链接库的加载基址,就获得了所需API此刻
在内存中的虚拟地址,这个地址就是我们最终在shellcode中调用时需要的地址。
流程图如下: