嗨,我正在尝试在 python 脚本中创建一个导入表构建器,就像 MacT 的导入重构器一样。
但是我很难找到从转发的 API 中获取原始 API 信息的方法。
例如,我从 IAT 获得了“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我不知道如何找到它。
我是否必须在 Import 目录中搜索每个加载的模块的 ForwarderChain?
嗨,我正在尝试在 python 脚本中创建一个导入表构建器,就像 MacT 的导入重构器一样。
但是我很难找到从转发的 API 中获取原始 API 信息的方法。
例如,我从 IAT 获得了“ntdll!RtlDecodePointer”,但它是从“kernel32!DecodePointer”转发的,我不知道如何找到它。
我是否必须在 Import 目录中搜索每个加载的模块的 ForwarderChain?
听起来您希望能够在解析模块的导出表时将 Fowarder 字符串与常规函数地址区分开来。
我不建议在您知道它是转发器字符串之前尝试解析此转发器字符串,因为有一个更简单的方法。诀窍是检查导出函数的地址是否在导出部分的内存范围内。这是 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 检索到函数的数组指针,无论函数是按名称还是按序号导出,下面的检查都有效。如果函数 #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
}
不,ForwarderChain in Import directory
与此无关。
当加载程序kernel32.DecodePointer
从某些PE导入解析时 - 它认为它指向kernel32.dll内部 IMAGE_EXPORT_DIRECTORY
的某个地址- 这就是所谓的转发导出。在这种情况下,加载器理解这一点不是代码,而是以形式或形式作为结果加载器的字符串尝试加载并按名称或按序号搜索。这个搜索如何查看可以(如果前向导出是)递归的。当我们最终得到不在内部的函数地址时,它停止了- 这个地址将存储在IAT中或进程失败 - 我们在这个 dll 中找不到 dll/或导出。kernel32.DecodePointer
somedll.somefunction
somedll.#someordinal
somedll
somefunction
someordinal
IMAGE_EXPORT_DIRECTORY
请注意这里的分隔符(在dll和函数名称之间 - 不是!
但.
)有趣的问题 - 如果 somedll
包含.
在自己的名称中会怎样 - 说my.x64.dll
。旧版本的 windows 不正确的进程名称像这样 ( 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(需要检查)这是正确的并且可以正常工作-dll.DecodePointer
在kernel32
win10搜索时将搜索xp DecodePointer
in kernel32.dll
。也从这里指出,早期我们不能在没有.dll
扩展的情况下将导出转发到模块,现在 - 没有这样的限制。(加载器默认.DLL
为加载的库名称附加后缀,如果它不包含.
- 所以在调用时LoadLibrary("my")
- 实际上将被打开并加载"my.DLL"
,但不会附加for"my."
或"my.x64"
后缀(名称中的字符)).DLL
.
因此,如果返回您的具体示例-kernel32.DecodePointer
指向kernel32.dllIMAGE_EXPORT_DIRECTORY
内部的某些内容。加载程序通过此地址读取字符串--在此字符串上调用(或旧版本)以进行查找并最终加载-> (添加后缀,因为名称中没有)并搜索-找到地址并且它不在ntdll.dll内-所以这是代码地址。这里进程停止并且地址存储在初始PE IAT中。NTDLL.RtlDecodePointer
strrchr
strchr
.
NTDLL
NTDLL.DLL
.
RtlDecodePointer
IMAGE_EXPORT_DIRECTORY
RtlDecodePointer
你自己需要重复加载步骤。但是现代操作系统中存在一个问题 - 许多字符串以API-MS-*
dll 名称开头。这不是真正的 dll,而是API Set Schema - 未记录且可变的方式,加载程序如何解析这些名称