The key differences between x86 and x64 that matter for reverse engineering — sixteen registers, the Microsoft x64 ABI calling convention, shadow space, RIP-relative addressing, and reading API calls in x64dbg.
Sign in to mark this article as read and track your progress.
The 64-bit x86-64 (AMD64) architecture extends the 32-bit x86 instruction set. As more malware targets 64-bit Windows exclusively, analysts must read x64 disassembly as fluently as x86.
Learning x86 first gives you the foundation. x64 differences are incremental — the core concepts (registers, flags, stack frames, calling conventions) are identical. Only the specifics change.
| Feature | x86 (32-bit) | x64 (64-bit) |
|---|---|---|
| General-purpose registers | 8 (EAX–EDI) | 16 (RAX–RDI extended + R8–R15) |
| Register size | 32 bits (4 bytes) | 64 bits (8 bytes) |
| Instruction pointer | EIP | RIP |
| Calling convention (Windows) | Arguments pushed on stack right-to-left | First four args in RCX, RDX, R8, R9 |
| Return value | EAX | RAX |
| Address space | 4 GB (2³²) | 256 TB (2⁴⁸, current implementations) |
| Stack alignment | 4-byte before CALL | 16-byte before CALL |
| Data reference | Absolute addresses | RIP-relative offsets |
x64 extends all eight x86 registers to 64 bits and adds eight new registers:
Extended: RAX RBX RCX RDX RSP RBP RSI RDI
New: R8 R9 R10 R11 R12 R13 R14 R15
Each 64-bit register still provides access to its lower portions:
RAX (64 bits)
└── EAX (lower 32 bits)
└── AX (lower 16 bits)
├── AH (high byte of AX)
└── AL (low byte of AX)
R8 (64 bits)
└── R8D (lower 32 bits)
└── R8W (lower 16 bits)
└── R8B (lower byte)
Writing to a 32-bit register (e.g., MOV EAX, 1) zero-extends to 64 bits in x64 — the upper 32 bits of RAX are cleared. This is a common source of confusion when reading x64 disassembly.
This is the most important difference for reverse engineers. In x86, all arguments go on the stack. In x64 Windows, the first four arguments go into registers.
Argument 1 → RCX
Argument 2 → RDX
Argument 3 → R8
Argument 4 → R9
Argument 5+ → Stack (RSP+0x28, RSP+0x30, ...)
Return value → RAX
Floating-point arguments use XMM0–XMM3 in the corresponding positions instead.
The caller must reserve 32 bytes of stack space before every CALL — even if the function takes fewer than four arguments. This is the "shadow space" or "home space" where the callee can spill its register arguments.
; x64 call to CreateFileW
lea rcx, [filename_buffer] ; Arg 1: lpFileName
mov edx, GENERIC_READ ; Arg 2: dwDesiredAccess (32-bit OK — zero-extends)
xor r8d, r8d ; Arg 3: dwShareMode = 0
xor r9d, r9d ; Arg 4: lpSecurityAttributes = NULL
mov dword ptr [rsp+0x20], OPEN_EXISTING ; Arg 5: dwCreationDisposition
mov dword ptr [rsp+0x28], 0 ; Arg 6: dwFlagsAndAttributes
mov qword ptr [rsp+0x30], 0 ; Arg 7: hTemplateFile
sub rsp, 0x28 ; reserve shadow space (32 bytes) + align
call CreateFileW
add rsp, 0x28 ; restore stack
Reading API calls in x64dbg: always check RCX, RDX, R8, R9 for arguments 1–4. Arguments 5+ are at [RSP+0x28], [RSP+0x30], etc. after the shadow space.
In x86, global data is referenced by absolute address: MOV EAX, [0x00403000]
In x64, global data is referenced relative to the current instruction pointer: MOV RAX, [RIP+0x2A7F]
IDA Pro and Ghidra resolve RIP-relative references automatically, displaying the computed address and symbol name. This difference is largely transparent during analysis — the tools handle it for you.
| Looking for | Check here |
|---|---|
| Argument 1 | RCX |
| Argument 2 | RDX |
| Argument 3 | R8 |
| Argument 4 | R9 |
| Arguments 5+ | Stack: RSP+0x28, RSP+0x30, ... |
| Return value | RAX |
| String pointer (arg 1) | RCX — follow in dump |
C++ this pointer | RCX (implicit first argument) |
| Loop counter | RCX or any R-register |
| Global data reference | Look for [RIP+offset] pattern |
Heaven's Gate is a technique that allows a 32-bit process running on 64-bit Windows (via WOW64) to switch the CPU into 64-bit mode using a far JMP to segment selector 0x33.
; 32-bit code transitioning to 64-bit
jmp 0x33:Heaven_Gate_64bit_code
Heaven_Gate_64bit_code:
; CPU is now in 64-bit mode
; can make 64-bit syscalls directly
; bypasses 32-bit API hooks installed by security tools
Malware uses Heaven's Gate to bypass 32-bit API hooks installed by AV and EDR products — those hooks only monitor 32-bit code paths.
Bypass for analysts: use a 64-bit debugger (x64dbg in 64-bit mode) or a kernel-level monitor that observes both 32-bit and 64-bit execution paths.
MITRE ATT&CK: T1055.014 — Process Injection: VDSO Hijacking (closest mapping)