您好我正在尝试在python脚本中创建一个导入表格构建器,就像MacT的Import Reconstructor一样。如何解决来自PE上IAT的转发API?
但是我很难找到从转发的API获取原始API信息的方法。例如,我从IAT得到了一个“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我没有任何想法可以找到它。
我是否需要搜索导入目录中的每个加载模块的ForwarderChain?
您好我正在尝试在python脚本中创建一个导入表格构建器,就像MacT的Import Reconstructor一样。如何解决来自PE上IAT的转发API?
但是我很难找到从转发的API获取原始API信息的方法。例如,我从IAT得到了一个“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我没有任何想法可以找到它。
我是否需要搜索导入目录中的每个加载模块的ForwarderChain?
不,ForwarderChain in Import directory
与此无关。
当装载机决心kernel32.DecodePointer
一些PE进口 - 它认为,这点有些地址内IMAGE_EXPORT_DIRECTORY
KERNEL32.DLL的 - 这就是所谓的转出口。装载机在这种情况下明白kernel32.DecodePointer
点不是代码,而是在格式字符串somedll.somefunction
或形式somedll.#someordinal
作为结果装载机是尽量负载somedll
并通过名称或搜索somefunction
someordinal
按顺序。这个搜索如何查看can(以及在向前输出的情况下)递归。它停了,当我们终于得到函数地址不在里面IMAGE_EXPORT_DIRECTORY
- 这个地址,将存储在IAT或进程失败 - 我们没有找到dll /或在此dll导出。
注意,这里deliminator(DLL和功能名之间 - 不!
但.
)有趣的问题 - 什么是如果包含.
在自己的名字somedll
- 说my.x64.dll
。老版本的窗口不正确的进程名是这样的(my.x64.dll.somefunc
),因为它在搜索字符串第一.
通过strchr
- 因此将搜索x64.dll.somefunc
在my
DLL和失败。但现在这是固定的 - 装载机使用strrchr
- 他搜索字符串中的最后.
。
为每年因此我们不能扩展指定全DLL名称 -
#pragma comment(linker, "/export:fn=kernel32.dll.DecodePointer");
GetProcAddress((HMODULE)&__ImageBase, "fn");
不能说在XP。但现在 - 确切地说win10,可能是win8.1(需要检查),这是正确的,将是工作 - xp将搜索dll.DecodePointer
在kernel32
而win10搜索DecodePointer
在kernel32.dll
。也从这里指出,如果没有.dll
扩展名,现在我们不能将输出转发到模块,现在 - 没有这种限制。(加载默认追加.DLL
后缀对加载库名,如果不包含.
它 - 所以,当呼叫LoadLibrary("my")
- 实际上将被打开并装载"my.DLL"
,但"my."
或"my.x64"
后缀.DLL
将不追加(在名称.
字符))
所以如果回到你的具体例子 - kernel32.DecodePointer
指向IMAGE_EXPORT_DIRECTORY
的kernel32.dll。装载机阅读串通过这个地址 - NTDLL.RtlDecodePointer
- 调用strrchr
(或strchr
旧版本)在这个字符串找到.
最后加载NTDLL
- >NTDLL.DLL
(后缀添加,因为在名称中没有.
)并搜索RtlDecodePointer
- 地址找到它不在IMAGE_EXPORT_DIRECTORY
的ntdll.dll - 所以这是代码地址。这里过程已停止,地址RtlDecodePointer
保存在初始PEIAT。
你从自己身边需要重复装载程序步骤。但在现代操作系统中存在一个问题 - 许多字符串以API-MS-*
dll名称开头。这不是真正的DLL,但The API Set Schema - 无文件和可变的方式,如何加载程序解析此名称
听起来像你想要区分Fowarder字符串和常规函数地址,同时解析模块的导出表的能力。
我不建议在你知道它的Forwarder字符串之前解析这个Forwarder字符串,因为有一个更简单的方法。诀窍是检查导出的函数的地址是否在导出段内存范围内。这是PE/COFF规范的“导出地址表”部分所述的官方方法。
对不起,我下面的示例代码是在C中,而不是Python,但仍应该给你这个想法。另请注意,下面的检查适用于PE32和PE64图像。
当您解析导出表时,您将已经有一个指向IMAGE_DATA_DIRECTORY导出部分的指针。从那里你可以获得一个指向IMAGE_EXPORT_DIRECTORY的指针。例如。
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>的Forwarder字符串。 <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
}
非常感谢。你的例子对我很有帮助! – Vanz
谢谢你的解释。我想我需要学习更多关于操作原理......:' – Vanz