x86 64架构汇编语言

11、x64汇编框架

1、课程目标

  1. 掌握x64架构的寄存器体系
  2. 理解x64调用约定
  3. 学会编写x64汇编程序
  4. 掌握x64与x86的关键差异

2、名词解释

术语英文说明
x64/AMD6464-bit Extended64位扩展架构
RAX/RBX…Extended Registers64位通用寄存器
R8-R15Additional Registers新增的8个通用寄存器
Shadow Space影子空间为前4个参数预留的栈空间
RIP相对寻址RIP-Relative相对指令指针的寻址方式

3、使用工具

工具用途
MASM64 (ml64.exe)编译x64汇编代码
Visual Studio x6464位开发环境
x64dbg64位调试器

4、技术原理

4.1、x64寄存器体系

64位32位16位8位
RAXEAXAXAL
RBXEBXBXBL
RCXECXCXCL
RDXEDXDXDL
RSIESISISIL
RDIEDIDIDIL
RBPEBPBPBPL
RSPESPSPSPL
R8-R15R8D-R15DR8W-R15WR8B-R15B

4.2、x64调用约定 (Windows)

参数顺序整数/指针浮点数
第1参数RCXXMM0
第2参数RDXXMM1
第3参数R8XMM2
第4参数R9XMM3
第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汇编实现一个简单的内存读写工具