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.

Apr 16, 2025 - 12:50
 0
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.