x86 64架构汇编语言

11、x64汇编框架

1、课程目标

  1. 掌握x64架构的寄存器体系
  2. 理解x64调用约定
  3. 学会编写x64汇编程序
  4. 掌握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、课后作业

  1. 基础练习:编写一个x64汇编程序,实现基本的数学运算
  2. 调用约定:编写一个多参数函数,演示参数传递机制
  3. 平台对比:同一个程序用x86和x64分别实现,对比差异
  4. 实战应用:用x64汇编实现一个简单的内存读写工具