1、课程目标
- 掌握x64架构的寄存器体系
- 理解x64调用约定
- 学会编写x64汇编程序
- 掌握x64与x86的关键差异
2、名词解释
| 术语 |
英文 |
说明 |
| x64/AMD64 |
64-bit Extended |
64位扩展架构 |
| RAX/RBX… |
Extended Registers |
64位通用寄存器 |
| R8-R15 |
Additional Registers |
新增的8个通用寄存器 |
| Shadow Space |
影子空间 |
为前4个参数预留的栈空间 |
| RIP相对寻址 |
RIP-Relative |
相对指令指针的寻址方式 |
3、使用工具
| 工具 |
用途 |
| MASM64 (ml64.exe) |
编译x64汇编代码 |
| Visual Studio x64 |
64位开发环境 |
| x64dbg |
64位调试器 |
4、技术原理
4.1、x64寄存器体系
| 64位 |
32位 |
16位 |
8位 |
| RAX |
EAX |
AX |
AL |
| RBX |
EBX |
BX |
BL |
| RCX |
ECX |
CX |
CL |
| RDX |
EDX |
DX |
DL |
| RSI |
ESI |
SI |
SIL |
| RDI |
EDI |
DI |
DIL |
| RBP |
EBP |
BP |
BPL |
| RSP |
ESP |
SP |
SPL |
| R8-R15 |
R8D-R15D |
R8W-R15W |
R8B-R15B |
4.2、x64调用约定 (Windows)
| 参数顺序 |
整数/指针 |
浮点数 |
| 第1参数 |
RCX |
XMM0 |
| 第2参数 |
RDX |
XMM1 |
| 第3参数 |
R8 |
XMM2 |
| 第4参数 |
R9 |
XMM3 |
| 第5+参数 |
栈 |
栈 |
5、代码实现
5.1、示例1:x64基本框架
; x64汇编基本框架
; ml64 /c example.asm
; link /subsystem:console /entry:main example.obj
extrn printf:proc
extrn ExitProcess:proc
.data
msg BYTE "Hello from x64 Assembly!", 13, 10, 0
fmtInt BYTE "Value: %lld", 13, 10, 0
.code
main PROC
; 分配栈空间(包括影子空间)
sub rsp, 40 ; 32字节影子 + 8字节对齐
; 调用printf
lea rcx, msg ; 第一个参数
call printf
; 清理栈并退出
add rsp, 40
xor ecx, ecx ; 退出码 = 0
call ExitProcess
main ENDP
END
5.2、示例2:x64函数定义
extrn printf:proc
.data
fmtResult BYTE "Result: %lld", 13, 10, 0
.code
; x64加法函数
; RCX = 第一个参数
; RDX = 第二个参数
; 返回值在RAX
Add64 PROC
mov rax, rcx
add rax, rdx
ret
Add64 ENDP
; 带栈帧的函数
Complex64 PROC
; 序言
push rbp
mov rbp, rsp
sub rsp, 32 ; 局部变量空间
; 保存非易失寄存器
push rbx
push rsi
push rdi
; 保存参数到影子空间(可选)
mov [rbp+16], rcx ; 第一个参数
mov [rbp+24], rdx ; 第二个参数
; 函数体
mov rax, rcx
imul rax, rdx ; rax = rcx * rdx
; 尾声
pop rdi
pop rsi
pop rbx
add rsp, 32
pop rbp
ret
Complex64 ENDP
main PROC
sub rsp, 40
; 调用Add64
mov rcx, 100
mov rdx, 200
call Add64
; 打印结果
lea rcx, fmtResult
mov rdx, rax
call printf
add rsp, 40
xor eax, eax
ret
main ENDP
END
5.3、示例3:x64与x86对比
#include <stdio.h>
#include <windows.h>
// x64与x86的关键差异演示
void ShowDifferences() {
size_t ptrSize = sizeof(void*);
size_t longSize = sizeof(long);
size_t sizeTSize = sizeof(size_t);
printf("Pointer size: %zu bytes\n", ptrSize);
printf("long size: %zu bytes\n", longSize);
printf("size_t size: %zu bytes\n", sizeTSize);
#ifdef _WIN64
printf("\n=== x64 Mode ===\n");
// x64寄存器演示
unsigned __int64 raxVal, rbxVal;
__asm {
mov raxVal, rax
mov rbxVal, rbx
}
printf("RAX: 0x%016llX\n", raxVal);
printf("RBX: 0x%016llX\n", rbxVal);
#else
printf("\n=== x86 Mode ===\n");
unsigned long eaxVal, ebxVal;
__asm {
mov eaxVal, eax
mov ebxVal, ebx
}
printf("EAX: 0x%08lX\n", eaxVal);
printf("EBX: 0x%08lX\n", ebxVal);
#endif
}
int main() {
ShowDifferences();
return 0;
}
5.4、示例4:RIP相对寻址
; x64支持RIP相对寻址
.data
globalVar QWORD 12345678h
.code
RipRelative PROC
; RIP相对寻址 - 直接访问全局变量
mov rax, globalVar ; 汇编器自动转换为RIP相对
; 显式RIP相对
mov rax, [rip + globalVar]
; 加载地址
lea rax, globalVar ; 获取地址
lea rax, [rip + globalVar]
ret
RipRelative ENDP
; 位置无关代码示例
ShellcodeStyle PROC
call getip
getip:
pop rax ; RAX = 当前RIP
; 现在可以基于RAX计算其他地址
ret
ShellcodeStyle ENDP
END
5.5、示例5:x64系统调用
#include <windows.h>
#include <stdio.h>
// x64 Windows API调用示例
void* AllocMemory64(SIZE_T size) {
void* result;
// 注意: x64 MSVC不支持内联汇编
// 需要使用单独的.asm文件或intrinsic
result = VirtualAlloc(
NULL,
size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
return result;
}
// 对应的x64汇编实现(单独.asm文件)
/*
AllocMemory64_ASM PROC
; RCX = size
sub rsp, 40 ; 影子空间
; VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
xor r9d, r9d
mov r9d, 04h ; PAGE_READWRITE
mov r8d, 3000h ; MEM_COMMIT | MEM_RESERVE
mov rdx, rcx ; size
xor ecx, ecx ; NULL
call VirtualAlloc
add rsp, 40
ret
AllocMemory64_ASM ENDP
*/
// 获取寄存器值(使用intrinsic)
void ShowRegisters() {
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&ctx);
#ifdef _WIN64
printf("RAX: 0x%016llX\n", ctx.Rax);
printf("RBX: 0x%016llX\n", ctx.Rbx);
printf("RCX: 0x%016llX\n", ctx.Rcx);
printf("RDX: 0x%016llX\n", ctx.Rdx);
printf("RSI: 0x%016llX\n", ctx.Rsi);
printf("RDI: 0x%016llX\n", ctx.Rdi);
printf("R8: 0x%016llX\n", ctx.R8);
printf("R9: 0x%016llX\n", ctx.R9);
printf("RSP: 0x%016llX\n", ctx.Rsp);
printf("RIP: 0x%016llX\n", ctx.Rip);
#endif
}
int main() {
printf("=== x64 Memory Allocation ===\n");
void* mem = AllocMemory64(4096);
printf("Allocated at: 0x%p\n", mem);
printf("\n=== Register Values ===\n");
ShowRegisters();
if (mem) VirtualFree(mem, 0, MEM_RELEASE);
return 0;
}
6、课后作业
- 基础练习:编写一个x64汇编程序,实现基本的数学运算
- 调用约定:编写一个多参数函数,演示参数传递机制
- 平台对比:同一个程序用x86和x64分别实现,对比差异
- 实战应用:用x64汇编实现一个简单的内存读写工具