Win32 Shellcode - Spawn MessageBox

4 minute read


Introduction

In the last post we spawned the Calculator on Windows x86. Now we’ll move on a more complicated object. We want to spawn a MessageBox with custom title and text. In this case the MesssageBox function is in the User32.dll library so we need to intruduce a new method: loading a different dll, searching the MessageBox function inside the library and use the with the right parameters. For our puproses we need to introdice a new function called LoadLibraryA that help us loading the new User32.dll.
The others concepts are more or less the same of the past shellcode

The Shellcode

As you can see the first part is the same of the Calc shellcode, but instead of call the CreateProcessA function, we need the LoadLibraryA function. Once the EAX is populated with its address we can call LoadLibrary("User32.dll") to load the right dll. Then we can use GetProcAddress to find the MessageBoxA function inside the right library, GetProcAddress(User32.dll, MessageBoxA), last thing is to build the right data structure that rapresent the arguments of the MessageBoxA function, accordingly with the MSDN documentation.
The comments in the code help understand how it works:

global _start

section .text
_start:


getkernel32:
	xor ecx, ecx                ; zeroing register ECX
	mul ecx                     ; zeroing register EAX EDX
	mov eax, [fs:ecx + 0x030]   ; PEB loaded in eax
	mov eax, [eax + 0x00c]      ; LDR loaded in eax
	mov esi, [eax + 0x014]      ; InMemoryOrderModuleList loaded in esi
	lodsd                       ; program.exe address loaded in eax (1st module)
	xchg esi, eax				
	lodsd                       ; ntdll.dll address loaded (2nd module)
	mov ebx, [eax + 0x10]       ; kernel32.dll address loaded in ebx (3rd module)

	; EBX = base of kernel32.dll address

getAddressofName:
	mov edx, [ebx + 0x3c]       ; load e_lfanew address in ebx
	add edx, ebx				
	mov edx, [edx + 0x78]       ; load data directory
	add edx, ebx
	mov esi, [edx + 0x20]       ; load "address of name"
	add esi, ebx
	xor ecx, ecx

	; ESI = RVAs

getProcAddress:
	inc ecx                             ; ordinals increment
	lodsd                               ; get "address of name" in eax
	add eax, ebx				
	cmp dword [eax], 0x50746547         ; GetP
	jnz getProcAddress
	cmp dword [eax + 0x4], 0x41636F72   ; rocA
	jnz getProcAddress
	cmp dword [eax + 0x8], 0x65726464   ; ddre
	jnz getProcAddress

getProcAddressFunc:
	mov esi, [edx + 0x24]       ; offset ordinals
	add esi, ebx                ; pointer to the name ordinals table
	mov cx, [esi + ecx * 2]     ; CX = Number of function
	dec ecx
	mov esi, [edx + 0x1c]       ; ESI = Offset address table
	add esi, ebx                ; we placed at the begin of AddressOfFunctions array
	mov edx, [esi + ecx * 4]    ; EDX = Pointer(offset)
	add edx, ebx                ; EDX = getProcAddress
	mov ebp, edx                ; save getProcAddress in EBP for future purpose

getLoadLibraryA:
	xor ecx, ecx                ; zeroing ecx
	push ecx                    ; push 0 on stack
	push 0x41797261             ; 
	push 0x7262694c             ;  AyrarbiLdaoL
	push 0x64616f4c             ;
	push esp
	push ebx                    ; kernel32.dll
	call edx                    ; call GetProcAddress and find LoadLibraryA address

	; EAX = LoadLibraryA address
	; EBX = Kernel32.dll address
	; EDX = GetProcAddress address 

getUser32:
	push 0x61616c6c                 ;
	sub word [esp + 0x2], 0x6161    ; aalld.23resU
	push 0x642e3233                 ; 
	push 0x72657355                 ; 
	push esp
	call eax                        ; call Loadlibrary and load User32.dll

	; EAX = User32.dll address
	; EBX = Kernel32.dll address
	; EBP = GetProcAddress address 

getMessageBox:
	push 0x6141786f                 ; aAxo : 6141786f
	sub word [esp + 0x3], 0x61
	push 0x42656761                 ; Bega : 42656761
	push 0x7373654d	                ; sseM : 7373654d
	push esp
	push eax                        ; User32.dll
	call ebp                        ; GetProcAddress(User32.dll, MessageBoxA)

	; EAX 76C6EA71 User32.MessageBoxA
	; ECX 76C10000 OFFSET User32.#2499
	; EDX 00005A12
	; EBX 75290000 kernel32.75290000
	; ESP 0022FF74 ASCII "32.dll"
	; EBP 752E1837 kernel32.GetProcAddress
	; ESI 75344DD0 kernel32.75344DD0
	; EDI 00000000
	; EIP 004010A4 getMessa.004010A4

MessageBoxA:
	add esp, 0x010                  ; clean the stack
	xor edx, edx
	xor ecx, ecx
    push edx 						
    push 'Pwnd'
    mov edi, esp
    push edx
    push 'Yess'
    mov ecx, esp
	push edx                        ; hWnd = NULL
	push edi                        ; the title "dnwP"
	push ecx                        ; the message "sseY"
	push edx                        ; uType = NULL
	call eax                        ; MessageBoxA(windowhandle,msg,title,type)

Exit:
	add esp, 0x010              ; clean the stack
	push 0x61737365             ; asse
	sub word [esp + 0x3], 0x61  ; asse -a 
	push 0x636F7250	            ; corP
	push 0x74697845             ; tixE
	push esp
	push ebx
	call ebp

	xor ecx, ecx
	push ecx
	call eax



Compile it and test it

root@root:# nasm -f elf32 getMessagebox_shellcode.nasm ; ld -melf_i386 -o getMessagebox_shellcode getMessagebox_shellcode.o 

root@root:# objdump -d ./getMessagebox_shellcode|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc9\xf7\xe1\x64\x8b\x41\x30\x8b\x40"
"\x0c\x8b\x70\x14\xad\x96\xad\x8b\x58\x10"
"\x8b\x53\x3c\x01\xda\x8b\x52\x78\x01\xda"
"\x8b\x72\x20\x01\xde\x31\xc9\x41\xad\x01"
"\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81"
"\x78\x04\x72\x6f\x63\x41\x75\xeb\x81\x78"
"\x08\x64\x64\x72\x65\x75\xe2\x8b\x72\x24"
"\x01\xde\x66\x8b\x0c\x4e\x49\x8b\x72\x1c"
"\x01\xde\x8b\x14\x8e\x01\xda\x89\xd5\x31"
"\xc9\x51\x68\x61\x72\x79\x41\x68\x4c\x69"
"\x62\x72\x68\x4c\x6f\x61\x64\x54\x53\xff"
"\xd2\x68\x6c\x6c\x61\x61\x66\x81\x6c\x24"
"\x02\x61\x61\x68\x33\x32\x2e\x64\x68\x55"
"\x73\x65\x72\x54\xff\xd0\x68\x6f\x78\x41"
"\x61\x66\x83\x6c\x24\x03\x61\x68\x61\x67"
"\x65\x42\x68\x4d\x65\x73\x73\x54\x50\xff"
"\xd5\x83\xc4\x10\x31\xd2\x31\xc9\x52\x68"
"\x50\x77\x6e\x64\x89\xe7\x52\x68\x59\x65"
"\x73\x73\x89\xe1\x52\x57\x51\x52\xff\xd0"
"\x83\xc4\x10\x68\x65\x73\x73\x61\x66\x83"
"\x6c\x24\x03\x61\x68\x50\x72\x6f\x63\x68"
"\x45\x78\x69\x74\x54\x53\xff\xd5\x31\xc9"
"\x51\xff\xd0"

The shellcode.c file

#include <stdio.h>
#include <windows.h>

int main()
{

    char* shellcode = \
    "\x31\xc9\xf7\xe1\x64\x8b\x41\x30\x8b\x40"
	"\x0c\x8b\x70\x14\xad\x96\xad\x8b\x58\x10"
	"\x8b\x53\x3c\x01\xda\x8b\x52\x78\x01\xda"
	"\x8b\x72\x20\x01\xde\x31\xc9\x41\xad\x01"
	"\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81"
	"\x78\x04\x72\x6f\x63\x41\x75\xeb\x81\x78"
	"\x08\x64\x64\x72\x65\x75\xe2\x8b\x72\x24"
	"\x01\xde\x66\x8b\x0c\x4e\x49\x8b\x72\x1c"
	"\x01\xde\x8b\x14\x8e\x01\xda\x89\xd5\x31"
	"\xc9\x51\x68\x61\x72\x79\x41\x68\x4c\x69"
	"\x62\x72\x68\x4c\x6f\x61\x64\x54\x53\xff"
	"\xd2\x68\x6c\x6c\x61\x61\x66\x81\x6c\x24"
	"\x02\x61\x61\x68\x33\x32\x2e\x64\x68\x55"
	"\x73\x65\x72\x54\xff\xd0\x68\x6f\x78\x41"
	"\x61\x66\x83\x6c\x24\x03\x61\x68\x61\x67"
	"\x65\x42\x68\x4d\x65\x73\x73\x54\x50\xff"
	"\xd5\x83\xc4\x10\x31\xd2\x31\xc9\x52\x68"
	"\x50\x77\x6e\x64\x89\xe7\x52\x68\x59\x65"
	"\x73\x73\x89\xe1\x52\x57\x51\x52\xff\xd0"
	"\x83\xc4\x10\x68\x65\x73\x73\x61\x66\x83"
	"\x6c\x24\x03\x61\x68\x50\x72\x6f\x63\x68"
	"\x45\x78\x69\x74\x54\x53\xff\xd5\x31\xc9"
	"\x51\xff\xd0"; 
			
    printf("shellcode length: %i", strlen(shellcode));

    LPVOID lpAlloc = VirtualAlloc(0, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(lpAlloc, shellcode, strlen(shellcode));

    ((void(*)())lpAlloc)();

    return 0;
}

Compile it on Windows

c:\MinGW\bin>gcc shellcode.c -o shellcode

Windows 7 (x86)

and Windows 10 (x86)


The length is 223 bytes and is NULL free.
In the next post we will spawn a Reverse shell.

References