五、Section Table(节表) 节表是紧挨着 PE Header 的一结构数组。该数组成员的数目由 File Header (IMAGE_FILE_HEADER) 结构中 NumberOfSections 域的域值来决定。节表成员结构又命名为 IMAGE_SECTION_HEADER(四十字节)。其结构定义: typedef struct _IMAGE_SECTION_HEADER { IMAGE_SECTION_HEADER 结构成员含义: 1.IMAGE_SIZEOF_SHORT_NAME:不超过8字节的节名。节名仅是个标记,我们选择任何名字甚至空着也行,不能用null结束。命名不是一个ASCIIZ字符串,所以不用null结尾。 2.PhysicalAddress:指定文件地址。 3.VirtualSize:这个域的意义与程序类型有关。如果是EXE,代表当节被装入内存之后的大小总和,这是在它们被调整为最接近文件对齐粒度的倍数之前的大小。稍后的SizeOfRawData则是调整后的大小。对于OBJ文档,这个域没有意义。 4.VirtualAddress:本节的RVA(相对虚拟地址)。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址400000h处,那么本节就被载到401000h。微软把第一个Section 的此域值设为0x1000h。对于OBJ文档,此域没意义,总为0。 6.PointerToRawData:这是本节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。 如果你自己以内存映射的方式应设了一个PE程序(而不是由操作系统的装载器载入),那么你就必须根据此值找到本节的信息,而不是根据VirtualAddress 中的RVA值。 7.PointerToRelocations:在OBJs中,这是以程序开始为基准的偏移量,用来指向section 的重定位信息。每个OBJ section 的重定位信息紧跟在section 信息之后。在EXEs中,这个域(一记下一个域)没有意义,总是为0。但链接器产生一个EXE,它会决定大部分的待修正纪录(fixups),只剩下基址的重定位地址以及 imported 函数的重定位地址,留待载入时在解决。两份相同的信息放在 base relocation section 和imported function section 之中,所以EXEs 不需要在每一个 section 之后又有重定位信息。 8.PointerToLinenumbers:行号表的偏移量(以程序开始为基准)。行号表与源代码行号和其被映射到内存中的位置有关。在EXE文件中,行号信息被放在程序的最尾端。如果没有COFF行好,设为0。 9.NumberOfRelocations:重定位表格(由PointerToRelocations 指向)中的重定位项目的个数。此域只用于OBJ中。EXE中为0。 10.NumberOfLinenumbers:行号表格(由PointerToLinenumbers 指向)中的行号个数。 11.Characteristics:包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。 下面是一些标记: IMAGE_SCN_TYPE_REG Reserved.
1.PE文件有效性校验。
6.1、导入函数: 一个导入函数是被某模块调用的但又不在调用者模块中的函数,因而命名为"import(导入)"。导入函数实际位于一个或者更多的DLL里。调用者模块里只保留一些函数信息,包括函数名及其驻留的DLL名。 PE 程序被载入到内存之前,存放在 PE 文件的 .data 中的内容是给装载器用来决定函数位置并修补它们以便完成image 用的。而在被载入之后,.idata内含有的是指向 EXE/DLL 的导入函数的指针。 6.2、Data Directory: Data Directory 是一个 IMAGE_DATA_DIRECTORY 结构数组,共有16个成员。Data Directory 包含了PE文件中各重要数据结构的位置和尺寸信息。 每个成员包含了一个重要数据结构的信息。 Data Directory 的每个成员都是 IMAGE_DATA_DIRECTORY 结构类型的,其定义如下所示: typedef struct _IMAGE_DATA_DIRECTORY { IMAGE_DATA_DIRECTORY 结构成员含义: 2.Size: 含有VirtualAddress所指向数据结构的字节数。 6.3、找寻PE文件中重要数据结构的一般方法: 1、从 DOS Header 定位到 PE Header。 6.4、导入表: Data Directory 数组第一项的 VirtualAddress 包含导入表地址。导入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组。每个结构包含PE文件导入函数的一个相关DLL的信息。该数组以一个全0的成员结尾。 IMAGE_IMPORT_DESCRIPTOR结构组成: typedef struct _IMAGE_IMPORT_DESCRIPTOR { DWORD ForwarderChain; // -1 if no forwarders IMAGE_IMPORT_DESCRIPTOR 结构成员含义: 2.TimeDateStamp:程序生成的时刻。此域通常为0。微软的 BIND 程序可以将此 IMAGE_IMPORT_DESCRIPTOR 所对应的dll的生成时刻写到这里来。 3.ForwarderChain:此域涉及到 forwarding(转交),意味着一个dll 函数在调用另一个 dll。例如,在 WINNT 中,Kernel32.dll 将它的某些输出函数转交给 NTDLL.dll。应用程序可能以为它调用 Kernel32.dll,而事实上它调用的事NTDLL.dll。这个域中含有一个索引,指向 FirstThunk 数组。被这个索引所指定的函数就是一个转交函数。 3.Name:含有指向DLL名字的RVA,即指向DLL名字的指针,也是一个ASCII字符串。 4.FirstThunk:与 OriginalFirstThunk 非常相似,它也包含指向一个 IMAGE_THUNK_DATA 结构数组的RVA(当然这是另外一个IMAGE_THUNK_DATA 结构数组)。 IMAGE_IMPORT_DESCRIPTOR 数组中,最重要的部分是 imported DLL 的名称以及两个 IMAGE_THUNK_DATA 数组。每个 IMAGE_THUNK_DATA 对应一个导入函数。在exe中,两个数组(分别由 Characteristics 和 FirstThunk 域指向)平行存在,并且都以 NULL 位结束符。 为什么需要两个平行数组?第一个数组(由 Characteristics 指向)从不被修改,有时它被称为 hint-name table。第二个数组(由 FirstThunk 指向)则被装载器改写。装载器一一检查每一个 IMAGE_THUNK_DATA 并且找出它所记录的函数的地址,然后把地址写入 IMAGE_THUNK_DATA 这个 DWORD 之中。由于这个 IMAGE_THUNK_DATA 数组内容已经被装载器改写为输入函数的地址,所以它又被叫做 Import Address Table(IAT)。IAT 是一个可写区域。API Hook 就利用到这一特性。PE装载器载入PE后,FirstThunk 指向的 IMAGE_THUNK_DATA 被改写,而 Characteristics 所指向的 IMAGE_THUNK_DATA 没有被改写。所以若还反过头来查找导入函数名,PE装载器还能够根据 Characteristics 所指向的 IMAGE_THUNK_DATA 找寻到函数名。 IMAGE_THUNK_DATA是一个DWORD类型的集合。通常我们将其解释为指向一个 IMAGE_IMPORT_BY_NAME 结构的指针。注意 IMAGE_THUNK_DATA 包含了指向一个 IMAGE_IMPORT_BY_NAME 结构的指针,而不是结构本身。 IMAGE_THUNK_DATA 结构定义: IMAGE_THUNK_DATA 实在PE被载入之后才被决定的。WIN32装载器使用 IMAGE_THUNK_DATA 的初始内容(可能是函数名称也可能是函数序号)来寻找输入函数的位置。然后装载器就以获得的地址改写 IMAGE_THUNK_DATA 的内容。 6.5、IMAGE_IMPORT_BY_NAME: IMAGE_IMPORT_BY_NAME 结构定义: 1.Hint:指示本函数在其所驻留DLL的导出表中的索引号。该域被PE装载器用来在DLL的导出表里快速查询函数。该值不是必须的,一些连接器将此值设为0。 2.Name:含有导入函数的函数名。函数名是一个ASCII字符串。注意这里虽然将Name的大小定义成字节,其实它是可变尺寸域,只不过我们没有更好方法来表示结构中的可变尺寸域。这个结构被提供用于查阅描述名字的结构。 有些情况下一些函数仅由序数导出,也就是说不能用函数名来调用它们,只能用它们的位置来调用。此时,调用者模块中就不存在该函数的 IMAGE_IMPORT_BY_NAME 结构。不同的,对应该函数的 IMAGE_THUNK_DATA 值的低位字指示函数序数,而最高二进位 (MSB)设为1。例如,如果一个函数只由序数导出且其序数是1234h,那么对应该函数的 IMAGE_THUNK_DATA 值是80001234h。Microsoft提供了一个方便的常量来测试dword值的MSB位,就是 IMAGE_ORDINAL_FLAG32,其值为80000000h。 6.6、列出某个PE文件的所有导入函数步骤: 1、校验文件是否是有效的PE。 6.7、Bound Import: 当PE装载器装入PE文件时,检查导入表并将相关DLLs映射到进程地址空间。然后象我们这样遍历IMAGE_THUNK_DATA 数组并用导入函数的真实地址替换IMAGE_THUNK_DATAs 值。这一步需要很多时间。如果程序员能事先正确预测函数地址,PE装载器就不用每次装入PE文件时都去修正IMAGE_THUNK_DATAs 值了。Bound import就是这种思想的产物。 七、Export Table(导出表) 当PE装载器执行一个程序,它将相关DLLs都装入该进程的地址空间。然后根据主程序的导入函数信息,查找相关DLLs中的真实函数地址来修正主程序。PE装载器搜寻的是DLLs中的导出函数。PE 程序把它的导出函数相关信息放在.edata 中。 DLL/EXE要导出一个函数给其他DLL/EXE使用,有两种实现方法: 通过函数名导出或者仅仅通过序数导出。比如某个DLL要导出名为"GetSysConfig"的函数,如果它以函数名导出,那么其他DLLs/EXEs若要调用这个函数,必须通过函数名,就是GetSysConfig。另外一个办法就是通过序数导出。序数是唯一指定DLL中某个函数的16位数字,在所指向的DLL里是独一无二的。例如在上例中,DLL可以选择通过序数导出,假设是16,那么其他DLLs/EXEs若要调用这个函数必须以该值作为GetProcAddress调用参数。这就是所谓的仅仅靠序数导出。 7.1 导出表是数据目录的第一个成员,又可称为 IMAGE_EXPORT_DIRECTORY。结构定义: IMAGE_EXPORT_DIRECTORY 结构成员含义: 1.Characteristics:此域没有用途,总是为0。 2.TimeDateStamp:程序被生成的时刻。 3.MajorVersion/MinorVersion:无实际用途,0。 4.Name:一个 RVA 值,指向一个 ASCIIZ 字串(dll 名称,如MYDLL.dll)。模块的真实名称。本域是必须的,因为文件名可能会改变。这种情况下,PE装载器将使用这个内部名字。 3.Base:基数,加上序数就是函数地址数组的索引值了。 4.NumberOfFunctions:模块导出的函数/符号总数。 5.NumberOfNames:通过名字导出的函数/符号数目。该值不是模块导出的函数/符号总数,这是由上面的NumberOfFunctions给出。本域可以为0,表示模块可能仅仅通过序数导出。如果模块根本不导出任何函数/符号,那么数据目录中导出表的RVA为0。 6.AddressOfFunctions:模块中有一个指向所有函数/符号的RVAs数组,本域就是指向该RVAs数组的RVA。简言之,模块中所有函数的RVAs都保存在一个数组里,本域就指向这个数组的首地址。 7.AddressOfNames:类似上个域,模块中有一个指向所有函数名的RVAs数组,本域就是指向该RVAs数组的RVA。 9.AddressOfNameOrdinals:RVA,指向包含上述 AddressOfNames数组中相关函数之序数的16位数组。 导出表的设计是为了方便PE装载器工作。 现在如果有一些函数是通过名字导出的,那么模块必定也在文件中保留了这些信息。这些名字的RVAs存放在一数组中以供PE装载器查询。该数组由AddressOfNames指向,NumberOfNames包含名字数目。考虑一下PE装载器的工作机制,它知道函数名,并想以此获取这些函数的地址。至今为止,模块已有两个模块: 名字数组和地址数组,但两者之间还没有联系的纽带。因此我们还需要一些联系函数名及其地址的东东。PE参考指出使用到地址数组的索引作为联接,因此PE装载器在名字数组中找到匹配名字的同时,它也获取了指向地址表中对应元素的索引。而这些索引保存在由AddressOfNameOrdinals域指向的另一个数组(最后一个)中。由于该数组是起了联系名字和地址的作用,所以其元素数目必定和名字数组相同,比如,每个名字有且仅有一个相关地址,反过来则不一定: 每个地址可以有好几个名字来对应。因此我们给同一个地址取"别名"。为了起到连接作用,名字数组和索引数组必须并行地成对使用,譬如,索引数组的第一个元素必定含有第一个名字的索引,以此类推。 7.2 如果我们有了导出函数名并想以此获取地址,可以这么做: 1、定位到PE Header。 7.3 假设我们只有函数的序数,那么怎样获取函数地址呢,可以这么做: 1、定位到PE Header。 |
PE学习笔记(二) 选择自 rivershan 的 Blog
最新推荐文章于 2023-03-10 23:17:22 发布