第13章:PE文件格式(2)

DLL(Dynamic Linked Library)

加载DLL的方式实际有两种:一种是显示链接(Explicit Linking),程序使用DLL时加载,完毕时再释放内存; 一种是隐式链接(Implicit Linking),程序开始时就加载DLL,程序终止时再释放占用的内存.

IAT提供的机制与隐式链接相关.

 

 如图,没有直接选择 call 7C8107F0,而是选择使用间接地址,在执行文件时,PE装载器只需要将CreatFileW()函数的地址写到01001104的位置.

第二个原因是,如果原来的地址被占用,PE装载器就只能查找其它空白内存区域将dll文件载入

实际操作中无法保证DLL一定会被加载到PE头指定的ImageBase处,但是EXE文件有自己的虚拟空间,因此可以实现这个操作.

IMAGE_IMPORT_DESCRIPTOR

该结构体中记录着PE文件要导入哪些库文件.其本身处在PE体中,查找到它的位置需要在可选头中的DataDirectory[1]找到.

由于本身是RVA,故需要转换为文件偏移,由于内存中的最小单位和硬盘中的最小单位的不同导致的.只需要抓住最重要的一点就是:它们距离这一个节区开头的距离都是相同的.

 一个程序导入多少库就存在多少个Image_Import_Descripotor结构体.结构体数组最后以NULL结构体结束.

有以下重要成员(全部都是相对虚拟地址):

1#.OriginalFirstThunk

INT(Import Name Table)的地址.INT中各个元素的值为Image_Import_By_Name结构体指针.INT和IAT的大小相同,且都是长整型(4字节数据类型)数组.

2#.Name

库名称字符串的地址.

3#.FirstThunk

IAT地址.

 注意一点:在实际的环境中,win xp系统的notepad.exe文件中,找到的结构体中没有包含characteristics,是直接从INT开始的.

原因是:union结构体内的元素是互斥的,只有一个元素能取得内存的占有权.

                                   ??????始终疑惑的是Characteristics这个值到底在哪个地方能用上??????

INT(导入函数名称地址)和IAT(导入函数地址)各元素一般都指向相同的地址(如下图),但也有很多情况是不一致的.

即描述导入函数的结构体--Image_Import_By_Name已存在,IAT和INT指向它们.

 PE装载器把导入函数输入至IAT的顺序:

1.首先程序读取PE头中可选头(以PE为标志找)中的DataDirectory[1]中读取Image_Import_Directory的地址(在PE体中),然后读取结构体中的Name成员,获得库名称字符串.

2.通过LoadLibrary()函数通过读取到的库名称装载库文件.

3.读取IID的OriginalFirstThunk成员(存储着导入函数的地址),获取INT地址.

4.读取INT结构体中的值,每四个字节为一个地址.每个地址指向的是一个Image_Import_By_Name结构体.该结构体中包含有Ordinal(库中函数的固有编号,2字节),

后面是一个函数名称字符串.

5.使用ordinal,Hint,Name中的任何一个,获取相应函数的起始地址,用GetProcAddress()函数,但一般都使用函数名作为参数.

6.读取IID的FirstThunk成员即 IAT成员,获得IAT的地址

7.将得到的函数地址写入得到的IAT地址.

8.重复进行4-7,直到遇见NULL结构体.

GetProcAddress是如何工作的呢? 与EAT有关.

 以win xp系统中的notepad.exe为例

1#.首先找到可选头,在PE首地址加18H即是:

 然后找到DataDirectory结构体的首地址,即在可选头首地址+60H:

找到DataDirectory[1]:即00007604,000000c8.并将VirtualAddress的RVA转为FileOffset---6A04.

Import Directory也称为Import Descriptor.最后是用一个NULL结构体填充的.

 

找到第一个结构体数组.获取Name字段的地址:7AAC(RVA) -> 6EAC(文件偏移).name字段NULL字符结尾.

2#.然后获取INT(OriginalFirstThunk)的地址即:7990(RVA) -> 6C90(文件偏移).最后以一个四字节NULL填充.

3#. 7A7A(RVA) -> 6E7A(文件偏移),即找到第一个Image_Import_By_Name结构体{ordinal,name string,''}

4#.读取IAT(FirstThunk)的地址:12C4(RVA) -> 6C4(文件偏移).每个都是四字节,最后以四个字节的NULL结尾.

第一个值,76344906是硬编码的,没有实际的意义.当notepad.exe文件加载到内存时,准确的地址会取代他们.

在实际的环境中看一下:

总结一下:

在win7系统中,此notepad程序也能运行(在win xp中可以发现,IAT的第一个值就是它),就是因为替换了它.

Windows在制作服务包过程中会重建相关系统文件,此时会硬编入准确地址(普通的dll不会被硬编码,常常带有与IAT相同的值).

普通DLL文件的ImageBase为10000000,所以经常会发生重定位,但Windows系统DLL文件拥有自身固定的ImageBase,不会出现重定位现象.

原文地址:https://www.cnblogs.com/Rev-omi/p/13183316.html