逆向工程核心原理-第25章-通过修改PE加载DLL
前面的三种DLL都是动态注入(从外部注入), 除此之外, 还可以采用”手工修改可执行程序”(从内部注入). 这种直接修改文件的方法, 只用修改一次, 后面每当程序运行时都会自动加载指定的DLL文件.
25.1 练习文件
我们将修改TextView.exe文件, 使其再运行时加载myhack3.dll文件(效果是下载一个html文件)
25.1.1 TextView.exe
TextView.exe是一个非常简单的文本查看程序, 只用将文件拖入即可查看文件内容
将a.txt中的内容呈现出来了
然后我们用PEView查看其导入表
可以看到导入了四个DLL: KERNEL32.dll, USER32.dll, GDI32.dll, SHELL32.dll
25.1.2 TextView_patched.exe
TextView_patched.exe是修改了TextView.exe文件的IDT(导入表)后得到的文件, 即再IDT中添加了导入myhack3.dll的部分, 使其运行时会自动导入myhack3.dll文件
使用PEView查看TextView_patched.exe的IDT(导入表)
可以看到新增了一个myhack3.dll文件, 这样, 运行TextView_patched.exe时就会自动加载myhack3.dll文件
下面运行一下TextView_patched.exe试试看是否会产生myhack3.dll的效果(下载html文件)(再XP上跑不了, 反而再Win10上可以)
25.2 源代码 - myhack3.cpp
1 |
|
跟之前章节的分析有很多共通之处, 所以省去了很多分析, 就是创建线程来下载URL
我们接着分析ThreadProc中的DownloadURL()和DropFile()函数
25.2.2 DownloadURL()
1 |
|
25.2.3 DropFile()
1 |
|
25.2.4 dummy()
1 |
|
dummy()函数是myhack3.dll文件向外部提供服务的导出函数, 但是我们可以看到它并没有实现任何功能. 导出他只是为了保证形式上的完整性, 通俗点讲就是”走走样子”.
在PE文件中导入DLL, 实质就是在文件代码内调用该DLL提供的导出函数. PE文件头中记录这DLL名称, 函数名称等信息. 因此, myhack3.dll至少要向外提供1个以上的导出函数才能保持形式上的完整性.
25.3 修改 TextView.exe 文件的准备工作
25.3.1 修改思路
PE文件中导入的DLL信息以结构体列表形式存储在IDT(导入表), 只要讲myhack3.all添加到列表尾部就可以了, 此前还要确认IDT中有无足够空间.
25.3.2 查看IDT是否由足够空间
首先使用PEView查看TextView.exe的IDT地址(PE文件头的可选头中存有导入表RVA值(注意是虚拟地址值))
我们使用PEView查看
可以看到IDT的RVA地址为0x84CC, 我们找到IDT的位置, 位于.rdata节中
根据第十三章的知识: IDT是由IMAGE_IMPORT_DESCRIPTOR(以下称IID)结构体组成的数组, 且数组末尾以NULL结构体结束. 由于每个导入的DLL文件都对应一个IID结构体(每个IID结构体的大小为14个字节) 所以整个IID区域为RVA: 84CC ~ 852F, 整体大小 = 0x14 * 5 = 0x64(十进制为20个字节)
IID结构体定义
1 | IMAGE_IMPORT_DESCRIPTOR |
我们讲PEView切换为FileOffse, 来查看IDT的文件偏移
使用HxD(HexEditor)打开TextView.exe
IDT的文件偏移为76CC ~ 772F, 大小为64字节, 共有5个IID结构体(最后一个为NULL结构体)
可以看到IDT后面紧跟着其他的数据, 所以我们没有足够的空间来添加IID
25.3.3 移动 IDT
在这种情况下, 我们要先把整个IDT移动其他的更广阔的位置, 然后再添加新的IID. 移动的选择有以下的三种:
- 查找文件中的空白区域;
- 增加文件最后一个节区的大小;
- 再文件末尾添加新的节区
第一个方式是最简单的, 所以先尝试这个
我们应该遵循就近的原则, 如果原节区有足够的空白空间, 那就转移到原节区.
我们可以再PEV中看到, 在.rdata节区的尾部有大片空白空间
0x8C60 ~ 0x8DFF这片空间的大小为0x190, 看上去是足够容纳IDT的, 但是这些内存有可能不是可用区域, 并不是文件中的所有区域都会被无条件加载到进程的虚拟内存, 只有节区头中明确记录的区域才会被加载, 使用PEV工具查看TextView.exe文件的.rdata节区头, 见下图
我们整理这些描述信息
- Pointer to Raw Data = 5200, [文件]节区的起始位置
- Size of Raw Data = 2E00, [文件]节区的大小
- RVA = 6000, [内存]节区的起始位置
- Virtual Size = 2C56, [内存]节区大小
可以看到.rdata节区在磁盘文件于在内存中的大小是不相同的
那么
1 | 实际空白大小 = (节区磁盘大小 - 节区内存大小) = 2E00 - 2C56 = 1AA |
1AA的空白大小可以容纳IDT, 所以我们只用这片区域
我们在RVA:8C80处创建IDT(前面的那一小块区域已经被使用了)
25.4 修改 TextView.exe
先把TextView.exe复制到工作文件夹, 然后重命名为TextView_Patch.exe, 然后使用HxD打开该文件进行修改.
25.4.1 修改导入表的RVA值
IMAGE_OPTIONAL_HEADER(可选头)中的导入表成员用来之处IDT的位置(RVA)与大小
我们使用PEV工具查看
接下来记住其文件偏移为160(上图为RVA切换一下就好了), 使用HxD, 对其进行修改
从现在开始, 导入表位于RVA :8C80处, 大小为0x78
25.4.2 删除绑定导入表
位于可选头中的BOUND IMPORT TABLE (绑定导入表)是一种提高DLL加载速度的技术
若想正常导入myhack3.dll, 需要向导入表添加信息. 然而, 该绑定导入表是可选项, 删除也没有关系, 删除即RVA跟Size都设置为0
25.4.3 创建新IDT
先使用HxD复制原IDT(RAW: 76CC ~ 772F), 然后覆写到IDT的新位置(RAW: 7E80)
然后再新IDT尾部(RAW: 7ED0)添加与myhack3.dll对应的IID
我们根据上面对IID结构体的分析, 发现其成员每个都占四个字节, 所以这里分别设置的是第1, 4, 5个成员OriginalFirstThunk(INT), Name 和 FirstThunk(IAT).
25.4.4 设置Name, INT, IAT
前面添加的IID结构体成员拥有指向其他数据结构(INT, Name, IAT)的RVA值. 因此, 必须设置这些数据结构才能保证TextView_Patch.exe文件正常运行. 为了方便HxD修改, 我们先把RVA转换成RAW
成员 | RVA | RAW |
---|---|---|
INT | 8D00 | 7F00 |
Name | 8D10 | 7F10 |
IAT | 8D20 | 7F20 |
在指定的地址上创建好相应的数据
使用PEV工具查看虚拟地址
逐个分析
首先是第一个成员OriginalFirstThunk是指向的INT的指针, 上图指向的是8d00也就是
指向的是8D30
可以看到前两个字节为00 00, 这两个字节为导入函数的Ordinal, 后面紧跟这函数的名称字符串.
其次是第四个成员Name, 指向的是8D10
就是我们的DLL名称字符串
最后是第五个成员FirstThunk是指向IAT的指针, 其值为8D20
指向的也是8D30
所以INT跟IAT在这里相同
25.4.5 修改IAT节区的属性值
加载PE文件到内存时, PE装载器会修改IAT, 写入函数的实际地址, 所以相关节区一定要拥有WRITE(可写)的属性.
使用PEView查看.rdata节区头
原本书中为40000040, 没有可写属性的, 但是这里HxD好像直接帮我修改了
我们添加一个WRITE属性这里用的时FileOffset, 所以直接再HxD中找224
因为原本IAT所在的节区时有可写属性, 所以不用修改, 但是我们将myhack3.dll的IAT放在了.rdata节区中, 与其他的IAT在不同的节区中, 所以要修改节区属性, 使其可写.
或者我们也可以直接修改FirstThunk到原IAT的末尾(如果还有空白区域的话), 并在末尾写上myhack3.dll的导入地址, 这样就可以不用修改节区头可写属性了. 我们在后面再试试, 这里先继续
25.5 检测验证
首先使用PEV查看修改后的TextView_Patch.exe文件, 查看其IDT
可以看到以成功添加myhack3.dll
运行测试
可以发现是成功运行的
更改IAT的另一个方法
首先前面转移IDT是一样的, 我们需要修改的是IID的第五个成员FirstThunk指向的是原IAT的尾巴, 并在尾巴处添加一个dummy的IAT
首先我们看看IAT
IAT也在.rdata中, 我们只用看尾巴的那一块, 现在是文件偏移, 等会要修改的地方
然后是RVA, 因为IID成员用的是RVA
然后我们新增的IID中的第一个跟第四个成员都不用改, 跟原来的一样, 第五个成员是指向IAT的指针, 我们修改成尾巴的RVA6150, 然后再6150的RAW处修改其值(原本为0), 改成8D00指向IMAGE_IMPORT_BY_NAME结构体指针的指针
检验
可以看到IAT中有了myhack3.dll项目
同时查看IDT
里面也有myhack3.dll
运行检验一下
运行成功.