从内存缓冲区创建进程
我可以使用 CreateProcess 来启动一个 EXE.我想将 EXE 的内容保存在内存缓冲区中,并在其上执行 CreateProcess(或等效程序),而不必将其写入文件.有没有办法做到这一点?
I can use CreateProcess to launch an EXE. I want to have the contents of an EXE in a memory buffer and do CreateProcess (or an equivalent) on it without having to write it to a file. Is there any way to do that?
背景故事:我们制作游戏.我们将一个普通的 EXE 发送给我们的经销商,然后他们使用他们最喜欢的 DRM 将其打包并出售给他们的用户.曾有用户发现崩溃的情况.大多数崩溃需要 5 分钟才能修复,但补丁必须通过分销商,可能需要几天甚至几周的时间.我不能只将修补后的 EXE 发送给玩家,因为它没有经销商的 DRM.我正在考虑将真正的游戏 EXE 分发到一个加密的数据文件中,这样被包装的内容(外部 EXE)就可以解密并启动真正的 EXE.这样我就可以在不禁用 DRM 的情况下安全地分发修复程序.
The backstory : we make games. We send a plain EXE to our distributors, which then wrap it using their favorite DRM and sell it to their users. There have been instances where users find crashes. Most of the crashes take 5 minutes to fix, but the patch must go through the distributor and it may take several days, even weeks. I can't just send the patched EXE to the players because it wouldn't have the distributor's DRM. I'm thinking of distributing the real game EXE inside an encrypted datafile so what gets wrapped (the external EXE) just decrypts and launches the real EXE. This way I could safely distribute a fix without disabling the DRM.
推荐答案
其实很简单.类似的技术在我 3 年前读过的一篇论文中有所描述.
It's actually quite easy. Similar technique has been described in a paper I read like 3 years ago.
Windows 允许您使用 调用 CreateProcess 函数CREATE_SUSPENDED 标志,它告诉 API 保持进程挂起,直到 ResumeThread 函数被调用.
Windows allow you to call the CreateProcess function with CREATE_SUSPENDED flag, that tells the API to keep the process suspended until the ResumeThread function is called.
这让我们有时间使用 获取挂起线程的上下文GetThreadContext 函数,那么 EBX 寄存器将持有一个指向 PBE(Process Enviroment Block)结构,我们需要用它来确定基地址.
This gives us time to grab the suspended thread's context using GetThreadContext function, then the EBX register will hold a pointer to the PBE(Process Enviroment Block) structure, which we need to determine the base address.
从PBE结构的布局可以看出ImageBaseAddress存储在第8个字节,因此[EBX+8]会给出被挂起进程的实际基地址.
From the layout of the PBE structure we can see that the ImageBaseAddress is stored at the 8th byte, therefore [EBX+8] will give us actual base address of the process being suspended.
现在我们需要内存中的EXE,如果内存和内存中EXE的对齐方式不同,则进行适当的对齐.
Now we need the in-memory EXE and do appropiate alignment if the alignment of memory and in-memory EXE differs.
如果挂起进程和内存中exe的基地址匹配,并且内存中exe的imageSize小于或等于挂起进程'我们可以简单地使用WriteProcessMemory 将内存中的 exe 写入挂起进程的内存空间.
If the base address of suspended process and in-memory exe matches, plus if the imageSize of the in-memory exe is lesser or equal to the suspended process' we can simply use WriteProcessMemory to write in-memory exe into the memory space of the suspended process.
但如果上述条件不满足,我们需要更多的魔法.首先,我们需要使用 ZwUnmapViewOfSection 取消映射原始图像,然后分配在挂起进程的内存空间内使用 VirtualAllocEx 获得足够的内存.现在我们需要使用 WriteProcessMemory 函数.
But if the aforementioned conditions weren't met, we need a little more magic. First, we need to unmap the original image using ZwUnmapViewOfSection, and then allocate enough memory using VirtualAllocEx within the memory space of the suspended process. Now we need to write the in-memory exe into the memory space of the suspended process using the WriteProcessMemory function.
接下来,将内存中exe的BaseAddress补丁到挂起进程的PEB->ImageBaseAddress中.
Next, patch the BaseAddress of the in-memory exe into the PEB->ImageBaseAddress of the suspended process.
线程上下文的EAX寄存器保存着EntryPoint地址,我们需要用内存中exe的EntryPoint地址重写它.现在我们需要使用 SetThreadContext 函数保存更改的线程上下文.
EAX register of the thread context holds EntryPoint address, which we need to rewrite with the EntryPoint address of the in-memory exe. Now we need to save the altered thread context using the SetThreadContext function.
瞧!我们已准备好调用 ResumeThread 函数挂起的进程来执行它!
Voila! We're ready to call the ResumeThread function on the suspended process to execute it!
相关文章