您好我正在尝试在Python脚本中创建一个导入表生成器,就像MacT的Import Reconstructor一样。
但是我很难找到从转发的API获取原始API信息的方法。
例如,我从IAT获得了一个“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我没有想法找到它。
我是否必须在Import目录中搜索每个加载的模块'ForwarderChain?
答案 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
}