ProcEnvInjection – 通过滥用进程环境字符串进行远程代码注入

原文链接:https://www.x86matthew.com/view_post?id=proc_env_injection

在从个人项目中休息了几个月后,我花了一些时间开发替代流程注入技术。这篇文章将记录我最近发现的一种新方法。此方法允许我们在不使用WriteProcessMemory

的情况下将自定义代码注入远程进程- 我们将使用CreateProcess中的lpEnvironment参数将我们的代码复制到目标进程中。此技术可用于将 DLL 加载到远程进程中,或简单地执行代码块。但是,为了使其可靠地工作,我们需要解决各种问题 – 这些将在下面描述。 lpEnvironment参数在

CreateProcess允许我们为目标进程指定一个自定义环境字符串。环境字符串包含一组环境变量条目,例如PATH=C:\Windows\system32;C:\Windows。列表中的每个环境变量都由一个空终止符分隔,列表中的最后一项是一个空白字符串(两个空终止符)。创建新进程时,环境字符串将被复制到进程的虚拟内存中,然后可以通过 PEB 访问它。

我最初的想法是使用lpEnvironment参数将二进制代码复制到目标进程中执行。这里有几个明显的问题——第一个是PEB中包含字符串数据的内存没有执行权限,第二个问题是lpEnvironment是一个字符串字段,这意味着我们的二进制代码数据不能包含空字符。

第一个问题很容易通过从注入器进程调用VirtualProtectEx来解决。我们可以很容易地通过PEB计算出远程进程中环境字符串的地址,并使内存可执行。

第二个问题更难解决 – 大多数代码块将包含空 (0x00) 字符。通过手动编写汇编操作码来特别避免 0x00 字节来解决这个问题是相当容易的,但这对于大代码块是不切实际的。

这个问题可以通过编写一个通用的固定“外部代码加载器”存根来解决,该存根经过仔细编写以避免任何 0x00 字符。这个小加载器将用于分配和执行真正的有效负载,它现在可以包含任何字节。通用加载器将使用PAGE_EXECUTE_READWRITE保护分配必要的字节数,调用OpenProcess以打开注入器进程,并使用ReadProcessMemory将有效负载从父进程读取到新分配的缓冲区中。然后将执行真正的有效载荷,并在执行完成后释放临时内存。这意味着仍然不需要WriteProcessMemory来传输数据 – 目标进程使用ReadProcessMemory从父进程读取数据。

总之,注入器过程采取以下步骤:

1. 创建一个不包含任何 0x00 字符的通用“代码加载器”块 – 如果需要,值将使用 XOR 进行编码。
2.使用GetEnvironmentStringsW检索现有的环境字符串并将其复制到临时缓冲区。我们的“通用代码加载器”条目将附加到现有条目的末尾。某些程序会使用环境变量,因此覆盖现有条目不是​​一个好主意。
3. 使用CreateProcess和我们的自定义环境字符串 ( lpEnvironment ) 创建目标 EXE 进程的挂起实例。我们还将使用CREATE_UNICODE_ENVIRONMENT标志来指定宽字符环境值,否则字符串将从 ANSI 转换为宽字符,这将破坏我们的加载程序代码。
4. 使用NtQueryInformationProcess检索目标进程的 PEB 地址。
5. 打电话NtCreateThreadEx在目标进程中调用Sleep(0)并等待线程退出。这将强制必要的 PEB 字段在目标进程中被初始化。
6. 计算目标进程中环境字符串的地址(PEB->RtlUserProcessParameters->Environment)
7. 在环境字符串中定位我们的加载器代码的地址。调用VirtualProtectEx使该数据可执行。
8. 调用NtCreateThreadEx在目标进程内执行加载器代码。此代码将从注入器进程中读取最终的有效载荷并执行它。
9、payload执行完毕后恢复原来的内存保护。
10、调用ResumeThread继续正常执行目标进程。

最终的有效载荷可以是任意大小并包含任意字节。对于此示例,我编写了以下代码来使用LoadLibrary加载user32.dll,然后调用MessageBoxA:

完整代码如下:

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部