PE格式 - IAT问题

时间:2011-10-06 11:43:19

标签: c format portable-executable

我正在尝试为Windows编写一个exe包装器。到目前为止,我已经掌握了一些基础知识。我要做的部分是阅读“BOUND IMPORT目录表”(或.idata部分?),基本上是PE文件的一部分,其中包含加载程序需要导入的DLL列表。

我想知道最好的方法是什么:

[A]找出IAT的位置(因为针对几个不同的.exe运行PEView似乎表明这个列表可以包含在多个不同的地方),然后阅读列表

OR

[B]找到一种直接读取exe需要导入的DLL列表的方​​法。

有没有办法做到这一点?有没有进一步阅读的人可以推荐IAT应该在哪里以及如何阅读它?

2 个答案:

答案 0 :(得分:8)

此外,这里有一个pdf可以帮助您了解结构的命名和组织方式。一些有用的程序: CFF Explorer ,以及一个好的十六进制编辑器

我的回答与上述不同,它描述了一种在磁盘上的可执行文件中手动执行上述操作的方法。

要获取IAT的相对虚拟地址(运行时的地址,即RVA):

  1. 从二进制文件的基址开始。这是一个IMAGE_DOS_HEADER结构。
  2. 按照e_lfanew字段转到IMAGE_NT_HEADERS结构。 从偏移0开始,向下跳转0x3c并取消引用以获取IMAGE_NT_HEADERS的开始。
  3. 关注OptionalHeader(由IMAGE_NT_HEADERS包含,因此也是如此)。要进入IMAGE_OPTIONAL_HEADER结构(尽管它的名称,它不再是可选的),请知道它是IMAGE_NT_HEADER中的第三个结构。 要访问OptionalHeader,请将0x18添加到之前取消引用的值
  4. 从OptionalHeader中取消引用DataDirectory。 DataDirectory是OptionalHeader中的一个数组,位于IMAGE_NT_HEADERS中。遵循DataDirectory数组中的第24个(如果0是第一个,如在0,1,2 ......中)条目到IMAGE_DIRECTORY_ENTRY_IAT。 将0xc0添加到您当前所在的地址以获取导入地址表目录

  5. 如果你想遍历DLL的列表及其功能地址,有一些背景知识:

    • IMAGE_IMPORT_DESCRIPTOR结构中的3个重要字段:OriginalFirstThunk(导入查找表的RVA),名称(RVA of 以null结尾的ASCII Dll名称)和FirstThunk(IAT /数组的RVA) 由加载器构建的线性地址。
    • 需要两个阵列 因为一个是导入例程(ILT)的名称数组,而且 另一个是导入的例程地址(IAT)数组。例程 从DLL导入的可以通过其名称或序号导入 数。要判断例程是否是序数导入,请检查ILT数组中IMAGE_THUNK_DATA结构的Ordinal字段中设置的标志。
    • 模块在加载时导入的每个函数都将由IMAGE_THUNK_DATA结构表示。原始第一个Thunk和第一个Thunk都指向一个IMAGE_THUNK_DATA数组。然而,IMAGE_THUNK_DATA结构是一个联合,它包含另一个结构IMAGE_IMPORT_BY_NAME。这一点很重要,因为Original First Thunk使用IMAGE_IMPORT_BY_NAME结构,而First Thunk使用IMAGE_THUNK_DATA结构内部联合中的Function字段。
    • 在磁盘上指定的RVA将不允许您遍历该文件,因为RVA表示二进制文件加载到内存时的地址;要遍历磁盘上的二进制文件,您需要将RVA值转换为正确的形式。公式很简单; HMODULE + RVA = PE元素的线性地址。 HMODULE也称为基地址。但是获取基地址实际上需要一个有点冗长的算法,并且取决于你所讨论的RVA的实际价值。要获取给定RVA的基址的值,以便计算磁盘上PE元素的线性地址:

      1. 获取sectionHeader;为此,请浏览部分列表(例如.data,.text,ect),直到找到问题的RVA在currentSection.VirtualAddress和currentSection.VirtualAddress + currentSection.size中的部分。

        1.1)首先,在NT_HEADERS结构中找到FileHeader中的节数。它是FileHeader中2字节机器号后面的2个字节。 *要手动执行此操作:将0x6添加到e_lfanew取消引用的值;所以从0的偏移量跳转0x3c,取消引用该值,然后将0x6取消引用。然后读取两个字节并解释为整数。

        1.2)找到第一部分的位置;它与OptionalHeader相邻。请记住,OptionalHeader中的DataDirectories数组。 OptionalHeader长216个字节,加上结尾的2个字表示它的结束;所以取十六进制的224(0xe0)并将其添加到从开始处取消引用的值为0x3c以获得第一个部分位置。

        1.3)要查找RVA所在的部分标题,请继续针对您当前的部分执行此测试。如果测试失败,请转到下一部分。如果您遍历所有部分并发现您到达结束的NULL字,那么该文件应该已损坏或您犯了错误。测试如下:将要转换的RVA与指向该部分虚拟地址的可用指针进行比较; RVA应该> =到该部分的虚拟地址,并且<部分的虚拟地址和虚拟大小的总和。通过将12添加到该部分的地址,可以找到该部分的虚拟地址。并且该部分的虚拟大小可以通过8到该部分的地址找到。总结一下:传递if - (section.virtualAddress + section.virtualSize)> RVA> = section.virtualAddress。 *要迭代到下一节,节描述的长度为0x28;您只需将0x28添加到当前节指针即可转到下一个指针。最后一部分是一个空字节来表示结束。

      2. 从获得的部分标题中,执行以下操作:(baseAddress + RVA) - (sectionHeader.virtualAddress - sectionhHeader.PointerToRawData)。 * sectionHeader的virtualAddress与sectionHeader本身相距12字节,如上所述。 PointerToRawData距离节标题20。

      3. 获得的值表示指向RVA所需/表示的数据的实际指针。您可以使用它来查找所需数据的实际文件位置。

    那是满口的。如果你想回顾一下,你应该阅读 Rootkit Arsenal 中第5章(挂钩呼叫表)的第257-60页,但是为了更容易理解的图形,请查看openrce.org pdf链接我给了顶部附近。

    要做到这一点,不过从......开始:

    1. 如上所述,转到OptionalHeader。 OptionalHeader包含一个(DataDirectory)IMAGE_DATA_DIRECTORY元素数组作为它的最后一个元素。该数组中的第二个元素是IMAGE_DIRECTORY_ENTRY_IMPORT,它定位IAT。因此,为了澄清,IMAGE_NT_HEADER包含OptionalHeader数组,该数组包含DataDirectory数组。此数组中的最后一个条目将被清零。
    2. 从OptionalHeader取消引用到IMAGE_DIRECTORY_ENTRY_IMPORT。下一个词是导入目录的大小。 从文件中的偏移量开始,向下跳转0x68。
    3. 此值是导入目录的RVA,它是IMAGE_IMPORT_DESCRIPTOR类型的结构数组(模块导入的每个DLL一个),最后一个字段的字段设置为零。 IMAGE_IMPORT_DESCRIPTOR中的第三个单词包含指向IMAGE_THUNK_DATA的FirstThunk指针。
    4. 使用上述算法,将导入目录的RVA转换为可用指针,并使用以下算法迭代导入目录数组。

      4.1)对于importDescriptor,将名称字段RVA转换为指针以获取名称。它可能是null

      4.2)要获取导入的每个例程的名称和地址,请获取导入描述符的OriginalFirstThunk和FirstThunk RVA条目。每个OFT和FT都可以为空,这表示它是空的,所以检查一下。

      4.3)将OFT RVA转换为指针; OFT对应于ILT,FT对应于IAT。 ILT和IAT可以为空,表示它们是空的。

      4.4)获取从ILT指针导入的函数的名称,以及IAT指针中函数的地址。要移动到下一个导入的函数,请记住ILT和IAT是数组;下一个元素是一个恒定的距离。

      4.5)检查新的ILT&获得的IAT指针值不为零;如果它们不为零,重复一遍。如果其中任何一个为零,则您已经点击为该dll导入的函数列表的末尾;导入描述符也是一个重复数组,因此导入的下一个dll的偏移量是常量。基本上,您正在迭代dll,并且对于每个dll,您正在迭代以这种方式导入的函数。 19

答案 1 :(得分:4)

是的,您可以通过浏览可执行文件的标题找到IAT。在winnt.h中查找标题声明。

有关如何在标题中查找信息的详细信息,请参阅 MSDN Magazine 中的Matt Pietrek系列,“深入了解Win32可移植可执行文件格式”,部分{{3 }和I

您还可以从II获取实际的Microsoft PE规范。

TL; DR:基本上,查找顺序如下:

  1. 从二进制文件的基址开始。这是一个IMAGE_DOS_HEADER结构。
  2. 按照e_lfanew字段转到IMAGE_NT_HEADERS结构。
  3. 按照OptionalHeader进入IMAGE_OPTIONAL_HEADER结构(尽管它的名称,它不再是可选的)。
  4. 关注DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]IMAGE_IMPORT_DESCRIPTOR结构数组。每个导入的DLL都有一个条目。此数组中的最后一个条目将被清零。
  5. 每个条目中的Name字段是指向DLL名称的RVA。 FirstThunk字段是指向该DLL的IAT的RVA,该IAT是IMAGE_THUNK_DATA结构的数组。