x86 64架构汇编语言
1、汇编的概念与常用寄存器
1、课程目标
- 理解汇编语言的基本概念
- 掌握x86/x64架构的寄存器体系
- 理解通用寄存器的用途
- 了解特殊寄存器的功能
2、名词解释
| 术语 | 说明 |
|---|---|
| 汇编语言 | 机器码的助记符表示 |
| x86 | 32位Intel架构 |
| x64/AMD64 | 64位扩展架构 |
| 寄存器 | CPU内部的高速存储单元 |
| 通用寄存器 | 可存储任意数据的寄存器 |
| 指令指针 | 指向当前执行指令的寄存器(EIP/RIP) |
| 栈指针 | 指向栈顶的寄存器(ESP/RSP) |
| 基址指针 | 指向栈帧基址的寄存器(EBP/RBP) |
3、使用工具
| 工具 | 用途 |
|---|---|
| MASM | 微软官方汇编器 |
| NASM | 跨平台汇编器 |
| x64dbg | Windows 64位调试器 |
| OllyDbg | Windows 32位调试器 |
| WinDbg | 内核调试器 |
| IDA Pro | 反汇编分析工具 |
4、技术原理
4.1、x86通用寄存器 (32位)
31 16 15 8 7 0
+----------------+--------+--------+
| | AH | AL | <- 8位
+----------------+--------+--------+
| | AX | <- 16位
+----------------+------------------+
| EAX | <- 32位
+-----------------------------------+
寄存器 传统用途 Windows调用约定
------ -------- ----------------
EAX 索引器/返回值 函数返回值
EBX 基址寄存器 被调用者保存
ECX 计数器 函数参数
EDX 数据寄存器 函数参数
ESI 源索引 被调用者保存
EDI 目标索引 被调用者保存
EBP 栈帧基址指针 被调用者保存
ESP 栈指针 栈指针
EIP 指令指针 不可直接访问
4.2、x64寄存器扩展 (64位)
63 32 31 0
+--------------------------------+------------------+
| | EAX |
+--------------------------------+------------------+
| RAX | <- 64位
+--------------------------------------------------+
新增寄存器: R8, R9, R10, R11, R12, R13, R14, R15
Windows x64调用约定:
- 参数: RCX, RDX, R8, R9 (前4个)
- 返回值: RAX
- 保存: RBX, RBP, RDI, RSI, R12-R15
- 易失: RAX, RCX, RDX, R8-R11
5、代码实现
1. 查看寄存器(内联汇编)
#include <stdio.h>
#include <windows.h>
void PrintRegisters() {
DWORD eax, ebx, ecx, edx;
DWORD esi, edi, ebp, esp;
__asm {
mov eax, eax
mov ebx, ebx
mov ecx, ecx
mov edx, edx
mov [eax], eax
mov [ebx], ebx
mov [ecx], ecx
mov [edx], edx
mov [esi], esi
mov [edi], edi
mov [ebp], ebp
mov [esp], esp
}
printf("【寄存器值】\n");
printf("EAX = 0x%08X\n", eax);
printf("EBX = 0x%08X\n", ebx);
printf("ECX = 0x%08X\n", ecx);
printf("EDX = 0x%08X\n", edx);
printf("ESI = 0x%08X\n", esi);
printf("EDI = 0x%08X\n", edi);
printf("EBP = 0x%08X\n", ebp);
printf("ESP = 0x%08X\n", esp);
}
int main() {
printf("=== 寄存器演示 ===\n\n");
// 通过GetThreadContext获取寄存器
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(GetCurrentThread(), &ctx);
printf("【通过API获取】\n");
#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("RSP = 0x%016llX\n", ctx.Rsp);
printf("RBP = 0x%016llX\n", ctx.Rbp);
printf("RIP = 0x%016llX\n", ctx.Rip);
#else
printf("EAX = 0x%08lX\n", ctx.Eax);
printf("EBX = 0x%08lX\n", ctx.Ebx);
printf("ECX = 0x%08lX\n", ctx.Ecx);
printf("EDX = 0x%08lX\n", ctx.Edx);
printf("ESP = 0x%08lX\n", ctx.Esp);
printf("EBP = 0x%08lX\n", ctx.Ebp);
printf("EIP = 0x%08lX\n", ctx.Eip);
#endif
return 0;
}
2. 寄存器的子寄存器访问
; MASM语法示例
.code
main PROC
; RAX 的各部分
mov rax, 0123456789ABCDEFh
; 现在:
; RAX = 0123456789ABCDEF
; EAX = 89ABCDEF (低32位)
; AX = CDEF (低16位)
; AH = CD (高8位 of AX)
; AL = EF (低8位)
; 修改AL不影响AH
mov al, 0
; RAX = 0123456789ABCD00
; 但修改EAX会清除高32位!
mov eax, 12345678h
; RAX = 0000000012345678 (x64特性)
ret
main ENDP
END
3、课后作业
3.1、作业1:寄存器记忆
背诵所有通用寄存器的名称和传统用途。
3.2、作业2:调试器观察
使用x64dbg观察计算器程序的寄存器变化。
3.3、作业3:调用约定
编写一个程序,在函数调用前后打印寄存器变化。