C或C++:加载器/包装器是如何工作的?

9

以下是我理解的内容...

  • 用户运行LOADER.EXE程序
  • LOADER.EXE下载另一个EXE文件,但将其全部保存在内存中而不保存到磁盘上
  • 直接从内存中运行已下载的EXE文件,就像从磁盘上执行一样

我见过几个类似的应用程序,但从未看到过任何关于它如何工作的示例或说明。

有人知道吗?

另一个例子是在另一个EXE文件中嵌入加密的EXE,它在执行之前被提取并在内存中解密,从未保存到磁盘上。

我见过某些应用程序使用它来防止盗版。

编辑:附带说一句,类似UPX这样的程序是否也是这种方式工作的呢?我看了代码,但对我来说很难理解,我主要是出于好奇心询问,没有实际需求。

2个回答

4
许多程序只是解压到%TEMP%(我知道我有这样做),但大公司基本上重新实现了操作系统可执行文件加载器,该加载器必须:
  • 将可执行文件映射到内存中。这并不像听起来那么简单,因为.exe包含多个“部分”,必须以页面对齐方式加载(它们必须从4K的地址倍数开始),每个部分都有特定的要求-只读,写时复制,零初始化等。
  • 通过更新导入表部分来满足静态导入,通常使用LoadLibrary()和GetProcAddress()。
  • 在dll的情况下(它们实际上几乎相同,重要区别在于它们还有导出,而不仅仅是导入),如果编译为在已经被使用的内存地址上加载,则加载器可能还必须重新定位dll(这很常见)。然而,对于exe而言,这通常是不可能的,因为它们不包括重定位部分,列出了需要更新的加载代码中的位置,因为通常它们是加载到进程中的第一件事,因此不能被阻止。这意味着加载器必须为其自己的exe具有不会阻塞所加载exe的异常加载地址。
总之,这是一项艰巨的工作。如果您有兴趣,请查看PE格式规范,该规范描述了.exe和.dll文件以及VirtualAlloc()函数。

哇,听起来比我想象的要复杂得多。你有使用UPX的经验吗?它是否像这样工作?(我对打包程序不太了解)。我希望能够找到一些演示这个过程的源代码,而且相对容易理解。 - guitar-
@guitar:我刚看了一下代码。加载器存根是相当宏重的汇编,但看起来打包程序正在预处理很多这方面的工作,因此存根可以将一个已经对齐的文件解压缩到内存中,然后获取导入项。这显然不足以满足您的第二个和第三个示例。http://upx.hg.sourceforge.net/hgweb/upx/upx/file/c3853d205747/src/pefile.cpp 中的打包程序内容更接近,但我确定还有其他 less 特殊情况的示例。 - Simon Buchan
这似乎是一个不错的开始,但它还不够商业化。http://code.google.com/p/egodust/source/browse/trunk/pe.c - Simon Buchan

2
如果您知道可执行文件入口点的偏移量以及它所需的参数,那么您只需要使用函数指针调用地址为“exeBase + entryPointOffset”的函数即可。
值得注意的是,在x86系统上,操作系统通常不允许您执行标记为数据的内存。例如,在Windows下,可以使用“Virtual ProtectEx”函数将内存标记为可执行的。
事实上,在过去,这是一种节省内存的常见方法。您可以通过 "overlays" 来交换代码以按需保存内存。

这真的可行吗?你怎么确定可执行代码中没有跳转到绝对地址(这将在加载时解析)? - Nikola Gedelovski
绝对地址修复在EXE数据中。这就是Vista的ASLR功能的工作原理。然而,加载器可能会将其引导代码移出路径,以便EXE以其正常基址加载。 - Hans Passant
@Hans:我非常确定exe文件不包含重定位节(虽然它们可能会),因为exe是分配在地址空间中的第一件事,因此永远不需要重新定位(ASLR仅适用于.dll)。 @Goz:这行不通,因为原始文件数据中的节不是页面对齐的:请参阅PE规范,了解文件偏移和RVA之间的区别。 - Simon Buchan
请注意,当包装器被创建时,它将知道它正在封装哪个EXE文件。因此,它可以选择另一个基地址。 - MSalters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接