6

I've seen a few memory forensics tutorials, which start by looking for injected code in the "victim's" process memory. They always seem to find the injected code in pages which have RWX access (i.e. PAGE_EXECUTE_READWRITE).

Does this assumption always hold? Does code injected (e.g. by malware) into the process memory of a "victim", always belong to a page with RWX access? Or can the page access be changed, by the code that is injected? If so, how can this change be done via winapi?

3 Answers 3

8

The assumption does not hold true, as it is possible to alter page protection flags after you've allocated memory.

The usual mechanism for code injection on Windows is as follows:

  1. Call OpenProcess for the target process, to get a handle that has appropriate access privileges.
  2. Use VirtualAllocEx to allocate a buffer in the target process, with a set of memory page access flags.
  3. Use WriteProcessMemory to copy the memory to the target.
  4. Either patch existing code to jump to the new code block, or use CreateRemoteThread to execute within the process via a new thread.

Now, there are two options here. The first is that you can specify PAGE_EXECUTE_READWRITE as a flag to VirtualAllocEx, so that you have the right to use WriteProcessMemory on that page, and also the right to execute that memory when you get to step 4. This is the "lazy" way that leads to having RWX buffers hanging around. The alternative way is to pass PAGE_READWRITE when allocating the block, then write the code, and call VirtualProtectEx to swap the flag over to PAGE_EXECUTE_READ before step 4. This gives you an RW buffer when copying data, then an RX buffer when executing.

Pseudocode:

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!\0";
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);
2
  • thanks for the detailed answer. In the alternative ("non-lazy") way of code injection you described, is it possible to swap the PAGE_EXECUTE_READWRITE with PAGE_EXECUTE or PAGE_EXECUTE_WRITECOPY, as an alternative to PAGE_EXECUTE_READ which you gave in your example?
    – Benny
    Commented Jan 19, 2014 at 15:48
  • PAGE_EXECUTE should work fine, but looks weird since it's rare to see PAGE_EXECUTE on allocations. Using PAGE_EXECUTE_WRITECOPY should work, but VirtualAllocEx doesn't support it. I don't know if VirtualProtectEx does.
    – Polynomial
    Commented Jan 19, 2014 at 16:02
2

The reason for targeting the RWX pages is that the injected code most often carries data in the same region as the code, and requires that the data are writable. Thus the W flag is needed. The X flag is required to support DEP, in case the process opted in, or if the system enforces it for everyone. The R flag is entirely optional when requesting the page. Windows will ensure that it is set anyway. It is of course possible for malware to allocate two regions, one [R]X and one [R]W, and write only to the writable section, but I don't recall ever seeing this technique being used. For one thing, it complicates the injected code.

1

Page access can be changed. See VirtualProtect, Memory Protection Constants and VirtualProtectEx documentation. Injected code should be executable and readable, but not necessarily writable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.