首先让我告诉您,您想要的东西是不可能的,因为众所周知的 DLL 是如何工作的。您可以尝试使用诸如PEBundle或dllpackager 之类的工具进行类似的操作,但是通常(我肯定会说)会因众所周知的 DLL(例如系统 DLL 甚至 MSVC 运行时 DLL 的不同版本)而失败。请参阅有关知名 DLL 的相关性和含义的this和this。
kernel32.dll
在 Win32 子系统中起着非常特殊的作用,它有助于向子系统 ( csrss.exe
)注册 Win32 线程和进程。
从 OP 对该问题的评论中回答部分:
事实上,我并不是在寻找性能优势。我想,通过这种方式,我可以像 Linux 剥离的二进制文件一样删除每个符号,并使反转更难。
那样做是没有意义的。您仍然只能导入单个函数并使用复杂的方式导入 DLL 和/或解析函数。即隐藏您正在从哪些 DLL 导入哪些函数。在黑客圈中比较流行的一件事是散列导出的函数名称,然后自己遍历加载图像的导出,散列找到的每个函数名称并与已知的散列值进行比较。
这是一篇关于用于您想要的方法的好论文,因为 shell 代码不知道在被劫持的进程中导入的函数地址。
正如 Igor 指出的那样,kernel32.dll
将被加载到进程中,AFAIR 的顺序也随着 Vista 发生了变化(以前ntdll.dll
是PEB的 DLL 列表中的第一个,又名LoaderData
)。所以确切的方法已经在上面的论文中列出了。
还有几点:
- 如果您不想使用
LoadLibrary
(或其ntdll.dll
对应物)动态加载 DLL,您可以在 IAT 中保留对单个导入函数的引用 - 这就是一些可执行加壳程序的做法。
- 如果没有,请从解析开始
LoadLibraryA
,加载您想要的 DLL,然后使用解析的GetProcAddress
(或您自己的方法已经使用kernel32.dll
并在论文中概述)加载更多功能。
- 你可能会让你的生活变得更艰难,而对熟练/经验丰富的逆向工程师来说却没有明显地困难。他们中的大多数人都会看到类似的方案;) ...动态分析将很容易揭示您的技巧并使逆向工程师能够解决它们。
作为替代方案,您可以通过编写一个简化的反汇编程序来求助于系统调用号,该程序能够将索引挑选到 SSDT(系统服务描述符表)中,然后您自己完成其余的工作。这在很久以前就已记录在案,因为当人们想从内核模式驱动程序中挂钩它时,人们习惯于在 SSDT 中查找索引。粗略地说,如果您有指向ntdll.dll
需要 SSDT 索引的函数的指针,您将检查您的假设,然后检索适当的值。在 Windows NT 4 到 2003(32 位)中,这看起来像
B8 ?? ?? ?? ??
其中B8
是 for mov eax, ????????
,问号是 SSDT 的索引。所以在检查之后B8
你会跳过它并获取下一个 DWORD。C 代码示例:
if ((lpAddr) && *((unsigned char *)lpAddr) == 0xB8)
{
result = *((ULONG *)((unsigned char *)lpAddr+1));
}
不同的操作系统版本和位数会有所不同 - 您已被警告。
但我没有看到任何优势——无论是在性能方面还是在阻止逆向工程努力方面。