我看过一些内存取证教程,这些教程从在“受害者”的过程内存中查找注入的代码开始。他们似乎总是在具有RWX访问权限的页面(即PAGE_EXECUTE_READWRITE)中找到注入的代码。

这个假设是否一直成立?注入(例如通过恶意软件)“受害者”进程内存中的代码是否始终属于具有RWX访问权限的页面?还是可以通过注入的代码来更改页面访问权限?如果是这样,如何通过winapi进行此更改?

#1 楼

该假设不成立,因为分配内存后可以更改页面保护标志。

Windows上代码注入的常用机制如下:


为目标进程调用OpenProcess,以获得具有适当访问权限的句柄。
使用VirtualAllocEx在目标进程中分配带有一组内存页访问标志的缓冲区。
使用WriteProcessMemory将内存复制到目标。
要么修补现有代码以跳转到新代码块,要么使用CreateRemoteThread通过新线程在进程内执行。

现在,在那里这里有两个选择。第一个是可以将PAGE_EXECUTE_READWRITE指定为VirtualAllocEx的标志,以便您有权在该页面上使用WriteProcessMemory,并且有权在执行步骤4时执行该内存。这是“懒惰”的方式导致RWX缓冲区悬空。另一种方法是在分配块时传递PAGE_READWRITE,然后编写代码,并在第4步之前调用VirtualProtectEx将标志交换到PAGE_EXECUTE_READ。这在复制数据时为您提供了RW缓冲区,在执行时为您提供了RX缓冲区。 />
伪代码:

rights = PROCESS_VM_OPERATION |
         PROCESS_VM_READ | PROCESS_VM_WRITE | 
         PROCESS_QUERY_INFORMATION | 
         PROCESS_CREATE_THREAD;

handle = OpenProcess(rights, false, pid);

targetAddr = VirtualAllocEx(handle, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

buffer = "Hello, world!q4312078q";
bytesWritten = 0;
WriteProcessMemory(handle, targetAddr, buffer, 14, &bytesWritten);

oldProtect = 0;
VirtualProtectEx(handle, targetAddr, 4096, PAGE_EXECUTE_READ, &oldProtect);

threadId = 0;
CreateRemoteThread(handle, NULL, 0, targetAddr, NULL, 0, &threadId);


评论


感谢您的详细回答。在您描述的替代代码注入方式(“非惰性”)中,是否可以将PAGE_EXECUTE_READWRITE与PAGE_EXECUTE或PAGE_EXECUTE_WRITECOPY交换,作为您在示例中提供的PAGE_EXECUTE_READ的替代方式?

–本尼
2014年1月19日15:48

PAGE_EXECUTE应该可以正常工作,但是看起来很奇怪,因为很少会在分配上看到PAGE_EXECUTE。使用PAGE_EXECUTE_WRITECOPY应该可以,但是VirtualAllocEx不支持它。我不知道VirtualProtectEx是否可以。

–多项式
2014年1月19日下午16:02

#2 楼

定位RWX页面的原因是,注入的代码最经常在与代码相同的区域中承载数据,并且要求数据可写。因此,需要W标志。如果选择了进程,或者系统为每个人强制执行DEP,则需要X标志来支持DEP。请求页面时,R标志是完全可选的。 Windows会确保无论如何设置它。回想一下曾经使用过这种技术。一方面,它使注入的代码变得复杂。

#3 楼

页面访问可以更改。请参阅VirtualProtect,内存保护常量和VirtualProtectEx文档。
注入的代码应是可执行的且可读的,但不一定
可写。