本帖最后由 鸦领主 于 2021-4-15 17:09 编辑
1.1 通过修改PE加载DLL
1.1.1 修改思路
PE文件中导入的DLL信息以结构体的形式存储在IDT(Import Directory Table)中,只需要将DLL添加到列表尾部就可以了,需要知道IDT有无足够的内存空间
1.1.2 查看IDT是否有足够的空间
IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress的值即是IDT的地址(RVA)
通过修改PE加载DLL
通过修改PE加载DLL
通过修改PE加载DLL
将视图改成File offset,意思是地址显示为文件偏移,从图中可以看出总共有5个结构体数组,每个导入的dll文件对应一个结构体,最后末尾以NULL结构体结束(),整个IID的大小以文件偏移来看是76CC-772F,64个字节,那么每个结构体就是14个字节
通过修改PE加载DLL
从图中可以看到,IID结构体数组下面是有其他数据的,没有足够的空间来添加其他的DLL文件
1.1.3 移动IDT
有三种方式分别是:
- 查找文件中的空白区域
- 增加文件最后一个节区的大小
- 在文件末尾添加新的节区
通过修改PE加载DLL
尝试第一种方法,这个节区的最后正好有空白区域,查看这个区域是否可用
通过修改PE加载DLL
通过修改PE加载DLL
磁盘文件中大小为2E00,实际被加载到内存中的只有2C56,还是1AA没有被使用(2E00-2C56),那么这个区域是可以使用的,那么我们就在8C80(7E80 RAW)这个位置创建IDT
1.1.4 修改导入表的值
通过修改PE加载DLL
一个是IDT的地址,一个是IDT的大小,我们将IDT移动到了8C80,那么将RVA改为8C80,又要添加一个结构体,那么64个字节还需要在加14个字节
通过修改PE加载DLL
那么从现在开始IDT位于8C80,大小是78
1.1.5 删除绑定导入表(BOUND IMPORT TABLE)
通过修改PE加载DLL
BOUND IMPORT TABLE(绑定导入表)是一种提高DLL加载速度的技术
若想正常导入自己的dll,需要向绑定导入表添加信息,但是该导入表是个可选项,不是必须存在的,可以删除(修改其值为0),当前实例中绑定导入表的值是0,不需要修改
1.1.6 创建新IDT
先将原先的IDT复制到8C80(7E80RAW)的位置,
通过修改PE加载DLL
在文件偏移的7ED0的地方分别设置INT,Name,IAT的内存(RVA)地址,地址可以选择别的地方
通过修改PE加载DLL
这个IID设置好了,那么接下来就是设置INT,Name,IAT的值了
1.1.7 设置INT,Name,IAT的值
通过修改PE加载DLL
黑色表示INT,青色表示Name,品红色表示IAT
INT(Import Name Taple,导入名称表)是RVA数组,每个数组中都是一个RVA地址记录着导入函数的名称字符串,上图中只有一个元素,其值是2E590 RVA,该地址是导入的函数的Ordinal(2个字节)和函数的名称字符串(dummy)
Name 是包含DLL文件名称字符串,来到RVA地址 02E570可以看到DLL的名称
IAT 也是RVA数组,各元素既可以拥有和INT一样的值,若INT中的数据准确也可以拥有不同的值,实际运行中,PE装载器会将虚拟内存中的IAT替换为实际函数的地址
1.1.8 修改IAT相关节区的属性
加载PE文件到内存时,PE装载器会修改IAT,写入函数的实际地址,所以你将IAT写入到了哪个节区,那么相应的节区头必须拥有WRITE(可写)属性。
通过修改PE加载DLL
我是将IAT写入到了data节区,节区头的属性拥有WRITE并不需要修改了,可以看到原属性值是C0000040,由3个属性组成
那么可以看出来如果不想要WRITE属性,只需要将原属性值改为40000040,因为80000000是WRITE的值(C0000040-40000040)。
--------------------------------------------------------------------------------------------------------------------------------
我们还没有修改的程序,IAT位于.rdata节区,且.rdata节区头本来就没有可写属性,为什么程序可以正常运行,而我们修改后就不能运行。
原因在于IMAGE_OPTIONAL_HEADER结构体的Data Directory数组中存在IAT
通过修改PE加载DLL
若修改的IAT的地址是在6000-6154的位置,那么就不用再修改IAT的节区头的属性了
比如将dummy设置在6000-6154之间的位置,再将IAT(SIZE)增加8个字节(Ordinal(2个字节)+dummy(5个字节)+0结尾(1个字节))
--------------------------------------------------------------------------------------------------------------------------------
1.1.9 检测验证
通过修改PE加载DLL
通过修改PE加载DLL
查看IDT可以看到,向IDT导入的dll的IID结构体设置正常,INT也是正常的说明没有问题。
通过修改PE加载DLL
通过修改PE加载DLL
运行程序也是成功了,我的dll只是显示一个消息框
1.1.10 查看dll文件代码
<div>#include<Windows.h>
</div><div>BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, TEXT("注入成功!!"), TEXT("PE注入"), MB_OK);
break;
}
return TRUE;//必须返回1
}
#ifdef __cplusplus
extern"C" {
#endif
__declspec(dllexport) void dummy()
{
return;
}
#ifdef __cplusplus
}
#endif // __cplusplus</div>
代码非常的简单只是弹出一个消息框,但是有一个地方需要注意dummy函数是一个导出函数,可以看见它没有执行任何功能,这是为了保持形式上的完整性,使dll能够顺利的添加到导入表。
在PE文件中导入某个dll,实质就是在文件代码内调用该dll提供的导出函数,PE文件头中记录着dll的名称,函数的名称信息等,所以需要提供至少一个以上的导出函数才能保持形式上的完整性。
|