PE文件的导出表和导入表
导出表
一个loader怎么找到DLL文件的导出函数的地址呢?
首先定位dll文件的PE文件结构,然后找到导出表的位置
1 | typedef struct _IMAGE_EXPORT_DIRECTORY { |
目前就知道最后三个AddressOfFunctions
,AddressOfNames
,AddressOfNameOrdinals
指向的是什么就行
dll导出函数可以两种方式,名称导出或者序号导出
如果是函数名,loader就会先依次匹配AddressNames
里的函数名,如果有,再到AddressOfNameOrdinals
里找到对应的序号,再拿着序号去AddressOfFunctions
里找到对应的函数
如果是序号,loader先找到Base
,然后计算出真正的序号,直接找到AddressOfFunctions
里的对应的函数
demo
可以去0x090068
的位置找到Name
字段
可以看到里面的数据都是对的上的
AddressOfNameOrdinals
里面有地址特别高的函数地址,这些函数是引用外部dll的函数,在 .rdata
段而不是其他低地址在.text
段,PE文件在识别的时候是检查这个地址是否在Export Directory
的范围内,如果是,那么这个函数就是字符串而不是真正的代码
用cmd也能看
1 | dumpbin /exports C:\Windows\System32\kernel32.dll |
导入表
1 | typedef struct _IMAGE_IMPORT_DESCRIPTOR { |
PE loader 会解析IAT表,找到 IMAGE_IMPORT_DESCRIPTOR[]
数组
只关心三个,OriginalFirstThunk
,指向了Import Lookup table
;FirstThunk
,指向了 Import Address table
;Name
,指向了DLL的名字
如图,Import Lookup table
和 Import Address table
都存着Hint/Name Table
的RVA,Hint/Name Table
存着导入的函数名和序号,其中 Import Address table
会在PE文件加载时填充上函数地址
PE loader先找到DLL Name,然后把该DLL文件加载到PE文件的内存空间,然后解析DLL文件的导出表,然后开始解析 Hint/Name Table
,然后取出里面的Hint
(也就是函数序号),如果Hint
匹配上,就会从导出表里提取出该函数的地址,然后把地址填入Import Address table
里,如果通过序号没找到,就拿着函数名找
demo
计算有多少个导入函数,每个_IMAGE_IMPORT_DESCRIPTOR
结构体是5个DWORD
,也就是20字节,这里是208(hex),也即是26个导入函数,但是只有25个,因为最后一个结构体是全0(框框有误)
数据都是对的上的
Name字段
两个位置指向的地址的内容是一样的,这里的内容存储的是个地址,这个地址指向的就是 Hint/Name Table
,如果是64位,就是8字节,32位就是4字节
动态调试一下,在x64dbg的options->settings
里勾选上DLL load
,让dll在加载的时候就被断住
找到一个call
函数的地方,右键Follow in Dump
,然后进入
可以发现此时IAT表里没有内容
再多run程序几次,直到进入OEP,可以看到此时IAT表里才被填入了地址
右键一段地址,Follow QWORD in Disassemler
,可以看到是在ntdll的地址空间里
再提一句,IAT表的地址可以有两种,一种是直接就是代码,一种是需要jmp到真正的代码