【发布时间】:2018-06-02 18:23:42
【问题描述】:
我正在编写一个小工具,它应该能够检查感兴趣的任意进程并检查其任何静态链接函数是否为trampolined。 (example of a trampoline 可能是 Microsoft Detours 对进程所做的事情。)
为此,我解析目标进程的PE header 并检索其所有导入的DLL,其中包含所有导入的函数。然后我可以比较磁盘上的 DLL 和目标进程内存中加载的 DLL:
A. Import Address Table 中的条目用于每个导入的函数。
B.每个函数的机器码的前 N 个字节。
如果以上任何一项不匹配,这肯定意味着 trampoline 已应用于特定函数(或 WinAPI)。
这很好用,除了目标进程可以导入全局变量而不是函数的情况。例如_acmdln 就是这样的全局变量。你仍然可以在msvcrt.dll 中找到它并使用它:
//I'm not sure why you'd want to do it this way,
//but it will give you the current command line.
//So just to prove the concept ...
HMODULE hMod = ::GetModuleHandle(L"msvcrt.dll");
char* pVar = (char*)::GetProcAddress(hMod, "_acmdln");
char* pCmdLine = pVar ? *(char**)pVar : NULL;
因此,这对我的蹦床检查工具意味着我需要区分导入函数 (WinAPI) 和全局变量。知道怎么做吗?
PS。如果我不这样做,我上面描述的算法将比较全局变量的“代码字节”,就像它是一个函数一样,它只是一个指向肯定会不同的命令行的指针,然后标记它作为一个蹦床函数。
PS2。不完全是我的代码,但解析 PE 标头的类似方法可以是found here。 (搜索DumpImports提取DLL导入的函数。)
【问题讨论】:
-
可能不可能 100%,但检查内存是否可执行应该关闭。
-
@DavidHeffernan:谢谢。这也是我的想法。虽然我希望 PE 标头本身有一些东西可以区分它们,但我忽略了。
-
映射每个目标 DLL 的内存部分并根据这些范围检查您的指针应该是相当可靠的。全局变量可能会驻留在“.data”部分或等效部分中。同样正如@DavidHeffernan 指出的那样,您可以测试这些部分特征(例如读/写/执行等标志),而不是硬编码部分名称。我无法想象编译器生成的代码将导出的全局变量放在任何代码段中。快速查看MSVC 10的CRT DLL,我碰巧发现_acmdln确实在“.data”段范围内。
标签: c winapi reverse-engineering portable-executable trampolines