Search code examples
windowsassemblyx86-64code-injectionshellcode

Shellcode causes an Access Violation upon mapping into process


So after creating a remote thread in a 64-bit process, I'm mapping my shellcode:

push rax
push r9
push r8
push rdx
push rcx
push r11

push 0x00000000
push 0x00000000
mov  r9,  0
mov  r8,  StartRoutine
mov  rdx, 0
mov  rcx, 0

mov  rax, CreateThread
call rax

add  rsp, 8*2 ;clear shadow space

pop r11
pop rcx
pop rdx
pop r8
pop r9
pop rax

mov r15, PreviousRip
jmp r15 ;return to previous instruction pointer

The names you see above are placeholders which are replaced before I map my shellcode:

  • StartRoutine is the address of an existing function in the remote process memory
  • CreateThread is the address of CreateThread in virtual memory
  • PreviousRip is the previous Instruction Pointer took from the thread context

However, after the shellcode runs I get an Access violation error due to a ret instruction going to the wrong rip.

For me that indicates that the stack is corrupted/unbalanced, however I double checked and the stack address is the same before and after the shellcode, I also checked all the general-purpose registers before and after my shellcode and they are succesfully restored.


Solution

  • If you're asynchronously running this between any two arbitrary instructions in an existing program, you need to make sure you save/restore ALL the architectural state that isn't call-preserved, like an interrupt handler would.

    You missed r10, rflags, and XMM0..51. https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160

    For safety, you also need to make sure you reserve the full 32 bytes of shadow space, so the DLL functions don't step on any of your saved register values. You say your testing shows that wasn't a problem now, but some future Windows version might have DLL functions that do take advantage of that shadow space.


    Footnote 1: Also x87 st0..7 or MM0..7. And AVX YMM0..15, although Windows API functions are unlikely to be affecting their high halves by running vzeroupper or anything. Or touching AVX-512 ZMM0..31 or k0..7. So you can probably get away without doing an xsave / xrstor, instead just saving XMM0..5.