如何从PE上的IAT解析转发的API?

时间:2017-05-31 08:13:17

标签: python portable-executable import-table

您好我正在尝试在Python脚本中创建一个导入表生成器,就像MacT的Import Reconstructor一样。

但是我很难找到从转发的API获取原始API信息的方法。

例如,我从IAT获得了一个“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我没有想法找到它。

我是否必须在Import目录中搜索每个加载的模块'ForwarderChain?

2 个答案:

答案 0 :(得分:0)

不,ForwarderChain in Import directory与此无关。

当加载程序从某些 PE 导入中解析kernel32.DecodePointer时,它会查看它指向 kernel32 IMAGE_EXPORT_DIRECTORY内的某个地址。 dll - 这就是所谓的转发导出。在这种情况下,加载程序理解kernel32.DecodePointer不是代码,而是 somedll.somefunction {{1}形式的字符串 作为结果加载器尝试加载 somedll.#someordinal 并按名称或 somedll搜索 somefunction 按顺序排列。这个搜索如何查看can(以及转发导出的情况)是递归的。当我们最终得到功能地址不在 someordinal内时,它停止了 - 这个地址将存储在 IAT 中或处理失败 - 我们找不到dll /或导出这个dll。

请注意,此处的分隔符(在 dll 函数名称之间 - 不是IMAGE_EXPORT_DIRECTORY而是!)有趣的问题 - 如果 . 以自己的名义包含somedll - 说.。旧版本的Windows不正确的流程名称( my.x64.dll ),因为它在my.x64.dll.somefunc字符串中搜索第一个 . - 所以将在 strchr dll中搜索 x64.dll.somefunc 并失败。但现在这已经修复 - 加载器使用my - 他在字符串中搜索最后strrchr

作为结果,我们每年都无法使用扩展名指定完整的dll名称 -

.

在xp上说失败。但是现在 - 确切地说是win10,可能是win8.1(需要检查)这是正确的并且可以正常工作 - xp将在#pragma comment(linker, "/export:fn=kernel32.dll.DecodePointer"); GetProcAddress((HMODULE)&__ImageBase, "fn"); 中搜索dll.DecodePointer而win10搜索kernel32在{ {1}}。同样从这里指出,我们不能在没有DecodePointer扩展的情况下将导出转发到模块,现在 - 没有这样的限制。 (默认情况下,加载器会将 kernel32.dll 后缀附加到已加载的库名称,如果它不包含.dll - 那么当调用.DLL时 - 实际上将打开并加载{ {1}},但.LoadLibrary("my")后缀"my.DLL"将不会附加("my." char in name))

因此,如果返回到您的具体示例 - "my.x64"指向 kernel32.dll .DLL内的内容。加载程序通过此地址读取字符串 - . - 在此字符串上调用kernel32.DecodePointer(或IMAGE_EXPORT_DIRECTORY旧版本)以查找NTDLL.RtlDecodePointer,最后加载{ {1}} - > strrchr(后缀已添加,因为名称中没有strchr)并搜索. - 找到的地址且不在 ntdll.dll NTDLL内 - 所以这是代码地址。此处的进程已停止,并且存储在初始 PE IAT 中的NTDLL.DLL地址。

你自己需要重复加载步骤。但现在在现代os中存在一个问题 - 许多字符串以. dll名称开头。这不是真正的dll而是The API Set Schema - 没有文档和可变的方式,加载器如何解析这个名称

答案 1 :(得分:0)

听起来你希望能够在解析模块的导出表时区分Fowarder字符串和常规函数地址。

我不建议在你知道它的Forwarder字符串之前尝试解析这个Forwarder字符串,因为有一个更简单的方法。诀窍是检查导出的函数的地址是否在导出部分内存范围内。这是PE / COFF规范的“出口地址表”部分中所述的官方方法。

很抱歉,下面的示例代码是C语言,而不是Python,但仍然应该给你一个想法。另请注意,下面的检查适用于PE32和PE64图像。

解析导出表时,您已经有一个指向IMAGE_DATA_DIRECTORY导出部分的指针。从那里你获得一个指向IMAGE_EXPORT_DIRECTORY的指针。 E.g。

IMAGE_DATA_DIRECTORY* pExportEntry = pOptHeader->DataDirectory->arDataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
...
//code to convert pExportEntry->VirtualAddress into file offset and store in dwExportTableFileOffset
...
IMAGE_EXPORT_DIRECTORY* pExportTable = (IMAGE_EXPORT_DIRECTORY*)(ImageFileBase + dwExportTableFileOffset);

假设您已从pExportTable-> AddressOfFunctions检索到函数的数组指针,则无论函数是按名称还是oridinal导出,下面的检查都有效。如果函数#i的函数地址(由下面的arFuncs [i]显示)在导出部分(您已经解析)内,则地址指向转发器字符串,其格式为< MODULE>。< ExportName> ,否则它是一个常规功能。

if (arFuncs[i] >= pExportEntry->VirtualAddress && arFuncs[i] < pExportEntry->VirtualAddress+pExportEntry->Size)
{
    //function address is RVA to Forwarder String; e.g. NTDLL.RtlDecodePointer
}
else
{
    //function address is RVA to actual code within current module
}