背景
这里进行导入表,方便自己使用代码,导入一个代码进行测试,这个比较方便。
逻辑
- 分析导入表的逻辑
- 添加导入表
知识点
- 导入表的地址是虚拟地址(内存地址,不是相对地址)
- 需要一个节点起始地址(否则无效),所以如果需要单独节点地址
- 其他的参数需要分析
typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // 包含指向IMAGE_THUNK_DATA(输入名称表)结构的数组
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 当可执行文件不与被输入的DLL进行绑定时,此字段为0
DWORD ForwarderChain; // 第一个被转向的API的索引
DWORD Name; // 指向被输入的DLL的ASCII字符串的RVA
DWORD FirstThunk; // 指向输入地址表(IAT)的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32
{
union {
DWORD ForwarderString; // 转发字符串的RAV
DWORD Function; // 被导入函数的地址
DWORD Ordinal; // 被导入函数的序号
DWORD AddressOfData; // 指向输入名称表 PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME
{
WORD Hint; // 函数序号
CHAR Name[1]; // 导入函数的名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
2.5 PE结构:导入表详细解析-腾讯云开发者社区-腾讯云 (tencent.com)
过程
- 找到导入表的描述,填充十六进制 0x2000
- 在文件偏移0x600 按照 IMAGE_IMPORT_DESCRIPTOR 填写
- 在文件偏移0x700 按照 IMAGE_THUNK_DATA32 填写
- 在文件偏移0x400 修改代码改成 call 导入表的地址调用函数的数组地址
导入表目录,这个地址必须指向节起始虚地址:0x2000指向第二个节点起始地址,对应的文件地址0x600(pe头用0x400 ,再加上一个节0x200,那么就是0x600)
0x600填写一个字段
OriginalFirstThunk:00 21 00 00 :0x2100 (虚拟地址)
TimeDateStamp:00 00 00 00 :0x0(用不到,无所谓)
ForwarderChain: 00 00 00 00 :0x0(用不到)
Name:80 21 00 00:0x2180 (虚拟地址):文件地址0x780,这里在0x780位置直接编辑字符串
FirstThunk:这里偷懒跟OriginalFirstThunk 一样地址
0x700编辑
Ordinal:1C 04 00 80:0x800041C 这里80 代表最高位是1,代表这个序号导入的,所以序号是0x41C 这个函数是:OutputDebugstringA
0x2100 + 0x400000 = 0x402100
修改0x400代码
我直接用x64 dbg 调试编辑代码,然后打补丁,因为我不记得机器码,懒的自己转换。
call dword ptr ds:[0x00402100]
运行代码
这里运行代码会报错,这里地址写死,但基地址会变化,所以我们要在pe头修改属性,不要动态调整基本地址,否则会有问题。
修改后你会发现不异常,因为我们没有压入字符串,我们直接用pe导入表那个地址,减少不必要麻烦。
现在重写代码
0x2180 + 0x400000 = 0x402180(之前的虚拟地址)
push 0x402180
call dword ptr ds:[0x00402100]
直接x64 dbg直接改,进行就可以了,或者通过C语言编译得到机器码,然后对应修改,直接填写(但这种容易写错,直接反汇编工具直接修改即可。)
效果图
一个简单程序打印就完成了,你可以用改成puts 函数进行打印,逻辑还是一样。这里我全部变量,同时基地址不变化,不然就需要重定位表了,后面增加重定位表了。
文件
上版本原始文件,没有重定位表的
链接:https://pan.quark.cn/s/e1303184a80e
建立重定位表用序号导入函数的exe
链接:https://pan.quark.cn/s/116f05487951