Shellcode call calc.exe - Why cannot use C write shellcode
Why C-Based Code Cannot Be Used as Shellcode Using C to Build a calc.exe Executable #include #include int main() { WinExec("calc.exe", SW_SHOW); return 0; } Then extract the .text section using objcopy: x86_64-w64-mingw32-objcopy -O binary --only-section=.text exec_calc.exe exec_calc.bin Disassemble with ndisasm ndisasm -b 64 exec_calc.bin From the disassembly, we can observe: NOP Padding – Instructions such as nop word [cs:rax+rax+0x0] are inserted by the compiler for alignment or anti-disassembly purposes. Strong Dependencies – Frequent usage of rel instructions (e.g., mov rax, [rel 0x34a0]) and API calls via the Import Address Table (IAT), such as jmp [rel 0x72ac] for WinExec. Indirect Jumps – Instructions like FF25 (e.g., jmp [rel 0x72ac]) rely on fixed addresses in the import table, indicating tight coupling with the PE loader. PE Format Reliance – The code uses system APIs through the IAT and expects the Windows PE loader to handle relocation, section initialization, and runtime setup. Static API References – API calls like WinExec are accessed through statically defined entries in the import table. Excessive Padding and Alignment – Compiler-inserted padding like 0F1F4000 nop dword [rax+0x0] and section alignment at addresses like 0x1800. Using msfvenom to Generate Shellcode for calc.exe payload: $ msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin Disassemble the Shellcode $ ndisasm -b 64 calc.bin Compare MSF shellcode and C Msfvenom C Generator Tool Metaploit’s msfvenom Extracted .text section from compiled PE Code Type Pure position-independent code (PIC) Compiler-generated, PE-dependent Dependency No external dependencies, self-contained Requires PE runtime, IAT, and relocation support Size Compact (about 66 bytes) Large (about 6KB) Feature Direct syscalls; dynamic API resolution via PEB; no redundant data IAT-based API calls; compiler-added prologue/epilogue; includes relocation/debug info Execute Environmental Can run from any memory address Requires full PE loader support API call method Dynamic resolution (gs:[0x60]→PEB→kernel32.dll) Hard-coded address via IAT Code alignment No padding bytes Contains NOPs and alignment for structure Minimal, self-contained Contains additional runtime/library code How to Improve C-Based Shellcode To make your payload suitable for shellcode use, consider the following techniques: PEB Traversal: Manually traverse the Process Environment Block (PEB) to locate loaded modules like kernel32.dll and resolve API addresses dynamically. Dynamic API Resolution: Avoid using the Import Table; instead, resolve API addresses at runtime using hash-based or name-based lookup. Avoid Compiler-Inserted Code: Use assembly or minimal C with inline assembly to bypass compiler-generated code (e.g., prologue/epilogue, SEH). Position-Independent Code: Ensure your code can execute from any memory address without relying on relocations or fixed sections.

Why C-Based Code Cannot Be Used as Shellcode
Using C to Build a calc.exe
Executable
#include
#include
int main() {
WinExec("calc.exe", SW_SHOW);
return 0;
}
Then extract the .text section using objcopy:
x86_64-w64-mingw32-objcopy -O binary --only-section=.text exec_calc.exe exec_calc.bin
Disassemble with ndisasm
ndisasm -b 64 exec_calc.bin
From the disassembly, we can observe:
NOP Padding – Instructions such as nop word [cs:rax+rax+0x0] are inserted by the compiler for alignment or anti-disassembly purposes.
Strong Dependencies – Frequent usage of rel instructions (e.g., mov rax, [rel 0x34a0]) and API calls via the Import Address Table (IAT), such as jmp [rel 0x72ac] for WinExec.
Indirect Jumps – Instructions like FF25 (e.g., jmp [rel 0x72ac]) rely on fixed addresses in the import table, indicating tight coupling with the PE loader.
PE Format Reliance – The code uses system APIs through the IAT and expects the Windows PE loader to handle relocation, section initialization, and runtime setup.
Static API References – API calls like WinExec are accessed through statically defined entries in the import table.
Excessive Padding and Alignment – Compiler-inserted padding like 0F1F4000 nop dword [rax+0x0] and section alignment at addresses like 0x1800.
Using msfvenom
to Generate Shellcode for calc.exe
payload:
$ msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
Disassemble the Shellcode
$ ndisasm -b 64 calc.bin
Compare MSF shellcode and C
Msfvenom | C | |
---|---|---|
Generator Tool | Metaploit’s msfvenom
|
Extracted .text section from compiled PE |
Code Type | Pure position-independent code (PIC) | Compiler-generated, PE-dependent |
Dependency | No external dependencies, self-contained | Requires PE runtime, IAT, and relocation support |
Size | Compact (about 66 bytes) | Large (about 6KB) |
Feature | Direct syscalls; dynamic API resolution via PEB; no redundant data | IAT-based API calls; compiler-added prologue/epilogue; includes relocation/debug info |
Execute Environmental | Can run from any memory address | Requires full PE loader support |
API call method | Dynamic resolution (gs:[0x60]→PEB→kernel32.dll) | Hard-coded address via IAT |
Code alignment | No padding bytes | Contains NOPs and alignment for structure |
Minimal, self-contained | Contains additional runtime/library code |
How to Improve C-Based Shellcode
To make your payload suitable for shellcode use, consider the following techniques:
PEB Traversal: Manually traverse the Process Environment Block (PEB) to locate loaded modules like kernel32.dll and resolve API addresses dynamically.
Dynamic API Resolution: Avoid using the Import Table; instead, resolve API addresses at runtime using hash-based or name-based lookup.
Avoid Compiler-Inserted Code: Use assembly or minimal C with inline assembly to bypass compiler-generated code (e.g., prologue/epilogue, SEH).
Position-Independent Code: Ensure your code can execute from any memory address without relying on relocations or fixed sections.