逆向工程核心原理——通过修改PE加载DLL

通过修改PE加载DLL

修改思路

PE文件中导入的DLL信息以结构体列表的形式存储在IDT中,所以我们把要注入的DLL(myhack3.dll)添加到列表尾部就行了,在这之前我们还需要确认一下IDT中是否有足够的空间。

查看IDT是否有足够的空间

首先,使用PEVIEW查看TextView.exe的IDT地址,存放在IMAGE_OPTIONAL_HEADER的导入表位置,是一个RVA

以RVA视角查看,发现IDT存在于data节

IDT中是由IMAGE_IMPORT_DESCRIPTOR结构体组成的数组,每个大小为0x14个字节,整个IDT的大小为0x14*5=0x64字节。

IID结构体定义

typedef struct _IMAGE_IMPORT_DESCRIPTOR {										
     union {										
         DWORD   Characteristics;           										
         DWORD   OriginalFirstThunk;       //RVA 指向IMAGE_THUNK_DATA结构数组		
     };										
     DWORD   TimeDateStamp;               	//时间戳				
     DWORD   ForwarderChain;              										
     DWORD   Name;							//RVA,指向dll名字,该名字以0结尾				
     DWORD   FirstThunk;                 	//RVA,指向IMAGE_THUNK_DATA结构数组		
 } IMAGE_IMPORT_DESCRIPTOR;										
 typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;								

我们查看一下文件偏移0x76CC,通过010Editor查看

IDT的文件偏移为76CC-772F,大小为0x64字节,共有5个IID结构体,并且最后一个为NULL,我们从图中发现IDT尾部存在数据,所以我们没用足够的空间来添加myhack3.dll的IID结构体

移动IDT

如遇到空间不够,我们需要将整个IDT移动到其他更广阔的位置,然后再添加新的IID。

我们可以采用下面三种方法:

1.查找文件中的空白区域

2.增加文件最后一个节区的大小

3.在文件末尾添加新节区

方法一

我们发现rdata尾部存在大片空白区域,这种空白区域被称为Null-Padding区域

那我们就把原IDT表移动到RVA:8C60-8DFF中合适的位置

我们查看rdata节区头,rdata节区在文件中的大小为2E00,在内存中为2C56,剩余未被使用的大小为1AA(2E00-2C56),所以我们创建是没有什么问题的。

注意

在PE尾部有些部分填充NULL,但不意味着这些部分都是空白可用区域,它们可能是程序使用的区域,也有可能不会加载到内存中,在移动IDT之前我们需要通过节区头判断。

1.修改导入表的RVA值

IMAGE_OPTIONAL_HEADER的导入表结构体中用来指出IDT的位置和大小

RVA为84CC,我们改成新的IDT的RVA-8C80,size在原有的基础上+0x14,修改为0x78

2.删除绑定导入表

如果我们正常导入myhack3.dll,需要向绑定导入表添加信息,但幸运的是,这个绑定注入表是个可选项,我们直接删除,这样比较方便。如果存在,但是内部记录信息错误,可能会在程序运行时引发错误。

3.创建新的IDT

首先把原来的IDT(RAW:76CC-772F)复制到新的地址(7E80)上,然后在后面添加一个IID结构用来描述myhack3.dll

typedef struct _IMAGE_IMPORT_DESCRIPTOR {										
     union {										
         DWORD   Characteristics;           										
         DWORD   OriginalFirstThunk;       //00008D00 RVA to INT		
     };										
     DWORD   TimeDateStamp;               	//0				
     DWORD   ForwarderChain;              	//0									
     DWORD   Name;							//00008D10 RVA to DLL NAME				
     DWORD   FirstThunk;                 	//00008D20 RVA to IAT		
 } IMAGE_IMPORT_DESCRIPTOR;										
 typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;	

4.设置Name、INT、IAT

我们添加了IID的成员指向了其他的数据结构的RVA值,因此我们需要对这些数据进行修复,才能保证程序正常执行,

RVA RAW
INT 8D00 7F00
NAME 8D10 7F10
IAT 8D20 7F20

0x7040是RAW,对应的RVA是8D30,在运行前,INT和IAT都指向函数名,所以RAW为7F00和7F20的地方填写30 8D 00 00 ,而7F10则是DLL的名字。

5.修改IAT节区的属性值

由于PE加载到内存时,PE装载器会修改IAT,写入函数的实际地址,所以rdata节区需要有可写属性,只有这样,PE装载器才有正常进行写入操作。

我们需要添加IMAGE_SCN_MEM_WRITE(80000000)属性,按位or运算后,属性值变为C0000040

6.检测验证

已经注入成功了。

总结:

通过修改PE加载DLL,其实就是通过将dll添加到IDT中,利用程序加载时会自动加载dll,将myhack3.dll加载到程序中。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 767778848@qq.com