windowsassemblywinapix86

Writing and running self-modifying code (or a simple JIT template) on modern systems


I decided to study SMC (self-modifying code). I thought it would be easier to make it in assembly language. But I encountered a problem that Windows does not allow me to pass control to the stack and throws exception_access_violation.
How can I do it?
Example I tried:

.386
.model flat, stdcall
option casemap:none

include smc.Inc; include win32api and define MsgCaption and MsgBox

.code
magic:
    mov eax, esp
    sub esp, 28
    mov dword ptr[espx], 1778421864; push MBOK; nop; push
    mov dword ptr[esp+4], offset MsgCaption
    mov dword ptr[esp+8], 2425393256; nop nop nop push
    mov dword ptr[esp+12], offset MsgBoxText
    mov dword ptr[esp+16], 1778421992; push NULL; nop; call
    mov dword ptr[esp+20], MessageBox
    mov dword ptr[esp+24], 3281031312; ret nop nop nop
    
    call dword ptr[esp]
    ret
start:
    ;invoke MessageBox, NULL,addr MsgBoxText, addr MsgCaption, MB_OK
    call magic
    invoke ExitProcess,NULL
end start

Solution

  • Those several lines that are using a big decimal constant couldn't possibly have created the correct code.

    eg. 1778421864 is 6A009068h in hexadecimal and the assembler, acknowledgeing that x86 is little endian, would have stored to memory the bytes 68h, 90h, 00h, and 6Ah. That's the opposite of what you need:

    push MB_OK   ; 64h 00h
    nop          ; 90h
    push         ; 68h
    

    The indirect call [mem] does not find an address.

    The call dword ptr[esp] instruction wants to jump to the address that is stored at the location where the stackpointer is pointing. Sadly it's instructions that reside there, so not an address at all. You want call esp to set EIP = ESP, rather than loading a new EIP from a pointer in memory.

    The ret won't find the return address.

    The ret instruction is currently popping the first 4 bytes of your code snippet. There's no way this could go back to just below call magic and invoke ExitProcess. The block of machine code you're JITing is cdecl not stdcall, since it ends with plain C3 (ret) not C2 1C 00 (ret 28).

    magic:
        sub  esp, 28
        mov  eax, esp
        mov  dword ptr [eax],    6890006Ah            ; push MB_OK : nop : push
        mov  dword ptr [eax+4],  offset MsgCaption
        mov  dword ptr [eax+8],  68909090h            ; nop : nop : nop : push
        mov  dword ptr [eax+12], offset MsgBoxText
        mov  dword ptr [eax+16], 0E890006Ah           ; push NULL : nop : call
        mov  dword ptr [eax+20], MessageBox
        mov  dword ptr [eax+24], 909090C3h            ; ret : nop : nop : nop
        
        call eax
        add  esp, 28
        ret
    start:
        ;invoke MessageBox, NULL, addr MsgBoxText, addr MsgCaption, MB_OK
        call magic
        invoke ExitProcess, NULL
    end start
    

    mov dword ptr[espx]
    

    I hope this espx is a mere typo and that your assembler didn't approve this.