PE导入表起步实操
本帖最后由 maikehong 于 2023-8-10 13:15 编辑USER32.dll:
173a8= 74 78 01 00 AC 78 01 00
是00开头的
MFC42D.DLL:
170ac= 76 0E 00 80 .... .... .... ....
是80开头的
MSVCRTD.dll:
17328=是00开头的
KERNEL32.dll:
17078=是00开头的
MFCO42D.DLL:
172f8= 是00开头的
1 data_DLL目录表:
IMAGE_DATA_DIRECTORY{
0x00 DWORD VirtualAddress-->地址中包含各种DLL 每个dll占20个字节 最后以20个字节0结尾
0x04 DWORD Size
}
A8 73 01 00
0 00 00 00 00 00 00 00 0088 78 01 00 08 77 01 00
2 DLL结构:
IMAGE_IMPORT_DESCRIPTOR STRUCT{ 20个字节
union{
Characteristics; DWORD 1
OriginalFirstThunk; && DWORD//RVA指向 IMAGE_THUNK_DATA 结构数组【用这个】
};
TimeDateStamp; DWORD 2 //时间戳
ForwarderChain; DWORD 3
Name; && DWORD 4 //RVA指向依赖的dll名字,该名字以0结尾
FirstThunk; && DWORD 5 //RVA指向 IMAGE_THUNK_DATA 结构数组
}IMAGE_IMPORT_DESCRIPTOR;
173a8 OriginalFirstThunk; && DWORD//RVA指向 IMAGE_THUNK_DATA 结构数组【用这个】
17888 DWORD 4 RVA指向依赖的dll名字,该名字以0结尾
17708 DWORD 5 RVA指向 IMAGE_THUNK_DATA 结构数组
3OriginalFirstThunk --->指向 INT表(这个表中存放[最高位不是0的话]是函数地址看得到ASCII码):
74 78 01 00 AC 78 01 00
INT表_ 导入名称表
IMAGE_THUNK_DATA 4个字节
IMAGE_THUNK_DATA 4个字节
00 01 78 74h 4个字节
00 01 78 AC 4个字节
......
(以四个字节00结束)
表里面每个成员的结构如下:
INT表_每个成员结构: 4个字节
typedef struct_IMAGE_THUNK_DATA32 {
unon{
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal; //序号
PIMAGE_IMPORT_BY_NAME AddressOfData; //指向IMAGE_IMPORT_BY_NAME
}u1;
}IMAGE_THUNK_DATA32;
实操如下:
17874 = GetSystemMetrics
178AC = MessageBoxA
Offset 01234567 89ABCDEF
00017870 47 6574 53 79 73 74 65 6D 4D GetSystemM
00017880 65 74 72 69 63 73 00 etrics
000178A0 BE 01 4D 65 ?Me
000178B0 73 73 61 67 65 42 6F 7841 00 00 00 00 00 00 00 ssageBoxA
4 FirstThunk-->一样(user.dll一样其他没查过)
=============================我亲爱的甲鱼大神笔记之乾坤大挪移 =========================================
我们得到 IMAGE_THUNK_DATA 结构的定义如下:
IMAGE_THUNK_DATA STRUC
union u1
ForwarderString DWORD? ; 指向一个转向者字符串的RVA
Function DWORD? ; 被输入的函数的内存地址
Ordinal DWORD? ; 被输入的API 的序数值
AddressOfData DWORD? ; 指向 IMAGE_IMPORT_BY_NAME
ends
IMAGE_THUNK_DATA ENDS
我们可以看出由于是union结构,所以IMAGE_THUNK_DATA 事实上是一个双字大小。
该结构在不同时候赋予不同的意义(伟大神奇不得鸟……)。其实union这种数据结构很容易理解:
说白了就是当时穷,能省就省,再说白了,就是几兄弟姐妹轮流穿一条裤子去相亲!理解了吧?哈哈
那我们怎么来区分何时是何意义呢?
规定如下:
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,
指向一个 IMAGE_IMPORT_BY_NAME 结构
IMAGE_IMPORT_BY_NAME 结构
仅仅只有一个字型数据的大小,存有一个输入函数的相关信息结构。其结构如下:
IMAGE_IMPORT_BY_NAME STRUCT
Hint WORD ? Hint 字段也表示函数的序号,不过这个字段是可选的,有些编译器总是将它设置为 0
Name BYTE ? 定义了导入函数的名称字符串,这是一个以 0 为结尾的字符串
IMAGE_IMPORT_BY_NAME ENDS
输入地址表(IAT)
为什么由两个并行的指针数组同时指向 IMAGE_IMPORT_BY_NAME 结构呢?
第一个数组(由 OriginalFirstThunk 所指向)是单独的一项,而且不能被改写,我们前边称为 INT。
第二个数组(由 FirstThunk 所指向)事实上是由 PE 装载器重写的
好了,那么 PE 装载器的核心操作时如何的呢?这里就给大家揭秘啦~
PE 装载器首先搜索 OriginalFirstThunk ,找到之后加载程序迭代搜索数组中的每个指针,
找到每个 IMAGE_IMPORT_BY_NAME 结构所指向的输入函数的地址,
然后加载器用函数真正入口地址来替代由 FirstThunk 数组中的一个入口,因此我们称为输入地址表(IAT)。
所以,当我们的 PE 文件装载内存后准备执行时,刚刚的图就会转化为下图
通过加载器之后 IAT里面的 IMAGE_THUNK_DATA 就成了 IAT里面 函数n的真正地址了 被覆盖了
也就是说 IAT 在加载器之前是个变量
{:5_106:}
页:
[1]