我想在我的 C# 应用程序中嵌入一个命令行实用程序,以便我可以将其字节作为数组获取并运行可执行文件,而无需将其作为单独的文件保存到磁盘(避免将可执行文件存储为单独的文件并避免需要能够在任何地方写入临时文件)。
我找不到仅从其字节流运行可执行文件的方法。Windows 是否要求它位于磁盘上,或者有没有办法从内存中运行它?如果 Windows 要求它在磁盘上,.NET 框架中是否有一种简单的方法来创建某种虚拟驱动器/文件并将文件映射到可执行文件的内存流?
我想在我的 C# 应用程序中嵌入一个命令行实用程序,以便我可以将其字节作为数组获取并运行可执行文件,而无需将其作为单独的文件保存到磁盘(避免将可执行文件存储为单独的文件并避免需要能够在任何地方写入临时文件)。
我找不到仅从其字节流运行可执行文件的方法。Windows 是否要求它位于磁盘上,或者有没有办法从内存中运行它?如果 Windows 要求它在磁盘上,.NET 框架中是否有一种简单的方法来创建某种虚拟驱动器/文件并将文件映射到可执行文件的内存流?
您要求在高级托管环境中实现非常低级的、特定于平台的功能。一切皆有可能……但没有人说这很容易……
(顺便说一句,我不知道您为什么认为临时文件管理繁重。BCL 为您完成:http: //msdn.microsoft.com/en-us/library/system.io.path.gettempfilename.aspx)
分配足够的内存来保存可执行文件。当然,它不能驻留在托管堆上,因此与本练习中的几乎所有内容一样,您需要 PInvoke。(实际上,我推荐 C++/CLI,以免把自己逼疯)。请特别注意应用于分配的内存页的属性位:如果弄错了,您将打开一个巨大的安全漏洞,或者让您的进程被 DEP 关闭(即,您将崩溃)。请参阅http://msdn.microsoft.com/en-us/library/aa366553(VS.85).aspx
在程序集的资源库中找到可执行文件并获取它的固定句柄。
Memcpy() 从托管堆的固定区域到本机块的代码。
释放 GCHandle。
调用VirtualProtect以防止进一步写入可执行内存块。
根据您从 VirtualAlloc 获得的句柄和 DUMPBIN 或类似工具显示的文件中的偏移量,计算可执行文件的 Main 函数在进程的虚拟地址空间内的地址。
将所需的命令行参数放在堆栈上。(Windows 标准调用约定)。当然,任何指针都必须指向本机或固定区域。
跳转到计算出的地址。可能最容易使用 _call(内联汇编语言)。
向上帝祈祷可执行映像中没有任何绝对跳转,这些跳转可以通过以正常方式调用 LoadLibrary 来修复。(当然,除非您想在第 3 步中重新实现 LoadLibrary 的大脑)。
从@eax 寄存器中检索返回值。
调用 VirtualFree。
步骤 #5 和 #11 应该在 finally 块中完成和/或使用 IDisposable 模式。
另一个主要选项是创建一个 RAMdrive,在那里编写可执行文件,运行它并进行清理。这可能会更安全一些,因为您没有尝试编写自修改代码(这在任何情况下都很难,但尤其是当代码甚至不是您的代码时)。但我相当肯定它需要比动态代码注入选项更多的平台 API 调用——所有这些都需要 C++ 或 PInvoke,自然而然。
查看本文的“内存中”部分。意识到它是从远程 DLL 注入的角度来看的,但概念应该是相同的。
创建 RAMdisk 或将代码转储到内存中然后执行它们都是可能的,但解决方案极其复杂(在托管代码中可能更复杂)。
它需要是可执行文件吗?如果将其打包为程序集,则可以从内存流中使用 Assembly.Load() - 几行简单的代码。
或者,如果它真的必须是可执行文件,那么编写临时文件实际上有什么问题?需要几行代码才能将其转储到临时文件,执行它,等待它退出,然后删除临时文件 - 在您删除它之前它甚至可能无法从磁盘缓存中取出!有时,简单、明显的解决方案是最好的解决方案。
这在 Vista+ 中是明确不允许的。您可以在 XP 中使用一些未记录的 Win32 API 调用来执行此操作,但在 Vista+ 中它被破坏了,因为它是一个巨大的安全漏洞,并且唯一使用它的人是恶意软件编写者。