Search code examples
cassemblyx86gnu-assemblershellcode

Address in shellcode changing during execution


My goal is to create shellcode that calls WinExec and launches notepad.exe. The bare bones code is (although actually surrounded by some other gcc produced assembly):

push 0x00646170
push 0x65746F6E 

push 5

lea eax, [esp + 0x4]
push eax

call 0x76E137C0
add esp, 32

It pushes "notepad\0" onto the stack, then 5, then the address of the "notepad" string, then finally calls 0x76E137C0. 0x76E137C0 is the address WinExec seems to reside at when ASLR is disabled on my machine (A 32-bit Windows 10 VM).

I'm taking this code, assembling it using gcc (simply gcc shell.c -o shell.exe), then objdumping it. Once I find the instructions, I take the bytes it shows:

40141e:       68 70 61 64 00          push   $0x646170
401423:       68 6e 6f 74 65          push   $0x65746f6e
401428:       6a 05                   push   $0x5
40142a:       8d 44 24 04             lea    0x4(%esp),%eax
40142e:       50                      push   %eax
40142f:       e8 8c 23 a1 76          call   76e137c0 <.debug_str+0x76a027c0>
401434:       83 c4 20                add    $0x20,%esp

Then I'm turning that into a string of bytes, and attempting to execute it:

#include <windows.h>
#include <winbase.h>

int main() {

    char* shellcode =
       "\x68\x70\x61\x64\x00\x68\x6e\x6f\x74\x65\x6a\x05\x8d\x44\x24\x04\x50\xe8\xbc\x37\xe1\x76\x83\xc4\x20";

    ((void(*)())shellcode)();

    return 0;
}

The problem is, if I then compile the above simple C program and run it, nothing happens. If I open it in Immunity and step though, I can see that the instructions were preserved, except for the address:

Immunity Debugger Screenshot

The address is in the wrong module, and seemingly part way through a function. If I step down and into the call, I get an access violation. If however I restart, and in Immunity I replace the call 77218816 with call 0x76E137C0, then have it continue, it works fine and notepad pops up.

I can't tell why the address is changing though while nothing else is. Does anyone see what I may be doing that's off?



The whole initial assembly is

    .file   "shell.c"
    .intel_syntax noprefix
    .text
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
    .align 4
LC0:
    .ascii "notepad\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    lea ecx, [esp+4]

    and esp, -16
    push    DWORD PTR [ecx-4]
    push    ebp

    mov ebp, esp
    push    ecx

    push 0x00646170
    push 0x65746F6E 
    
    push 5
    
    lea eax, [esp + 0x4]
    push eax
    
    call 0x76E137C0
    add esp, 32
    
    sub esp, 8
    mov eax, 0
    mov ecx, DWORD PTR [ebp-4]

    leave

    lea esp, [ecx-4]

    ret

Solution

  • Using call like that is EIP-relative. You're not running your shellcode at the same address you compiled it for, so it can't call out of itself like that. For a quick-and-dirty workaround, you can do mov eax, 0x76E137C0 and then call eax, which is absolute.