x64汇编之前篇内容复习(上)

家好,你们可以叫我凌,是个16岁的网络安全学习者。

经过前面几篇"杂七杂八"的内容,我们是时候也应该开始正式写写代码了!

不过,前面"八宝粥"式的内容我相信肯定有许多人满头雾水(比如为什么 rax=1 是写,rax=60 是退出)。所以,我将再写两篇"复习篇",帮助各位将前面的知识点彻底梳理起来,为后续动手实战铺平道路。

本篇为上篇,重点复习内存、寄存器、系统调用和GDB调试速查。读完你会掌握一个汇编程序的最小骨架,并能用调试器看清它的每一步。


内存与数据表示(极简回顾 + 技巧)

原文链接1:

x64汇编之内存工作的基本原理https://blog.csdn.net/2401_88251163/article/details/159761650

原文链接2:

x64汇编之进制讲解和寄存器https://blog.csdn.net/2401_88251163/article/details/160631471

计算机内部只存储 0 和 1,但我们需要用更友好的方式表示它们。

二进制 ↔ 十六进制 ↔ 十进制

|------|----|------|------------|
| 进制 | 基数 | 示例 | 用途 |
| 二进制 | 2 | 1011 | 计算机内部存储 |
| 十六进制 | 16 | 0xB | 缩短二进制,便于阅读 |
| 十进制 | 10 | 11 | 人类习惯 |

快速手动转换技巧:

  • 二进制 → 十六进制:从右向左每4位一组,每组转成一位十六进制。

例如 11010110 → 1101 0110 → D6。

  • 十六进制 → 二进制:每一位十六进制写成4位二进制,去掉前导零。

例如 0xA3 → 1010 0011。

  • 十六进制 ↔ 十进制:记住 0xA=10、0xF=15,按权展开即可。

常用值:0xFF = 255,0x100 = 256。

小技巧:一个字节(8位)的最大值 0xFF 是255,在调试内存时看到 0x7F = 127,0x80 = 128(也是 -128 补码)。

有符号整数:补码

计算机无法存储"负号",负数通过补码表示。

核心规律(8 位示例):

  • 11111111 = -1

  • 10000000 = -128

  • 01111111 = 127(最大正数)

速算技巧:对于负数补码,直接看十六进制:0xFF = -1,0xFE = -2,0x80 = -128。即 0x100 - 该数绝对值 就是补码。

浮点数(仅提及)

浮点数用于小数或极大数,采用 IEEE 754 标准,分为符号、指数、尾数三部分。例如单精度 32 位:1 位符号 + 8 位指数 + 23 位尾数。

> 现在只需知道浮点数的存在,暂不涉及具体转换。

x64 寄存器速查表

原文链接:

x64汇编之进制讲解和寄存器https://blog.csdn.net/2401_88251163/article/details/160631471

寄存器是 CPU 内部的"高速便签纸"。以下是最常用的几个:

|--------|-----|-----|-------|------------------|
| 64位 | 32位 | 16位 | 8位(低) | 常见用途 |
| rax | eax | ax | al | 系统调用号 / 返回值 |
| rdi | edi | di | dil | 系统调用第1参数 / 目的指针 |
| rsi | esi | si | sil | 系统调用第2参数 / 源指针 |
| rdx | edx | dx | dl | 系统调用第3参数 / 乘除法辅助 |
| rsp | esp | sp | spl | 栈顶指针(仅64位常用) |
| rbp | ebp | bp | bpl | 栈帧基址 |
| rip | (无) ||| 指令指针 |
| eflags | (无) ||| 标志寄存器 |

子寄存器关系:修改 al 会影响 ax 和 eax 和 rax 的低8位;修改 eax 会清零 rax 的高32位。

小技巧:调试时,用 info registers 看到 rax 很大,可能实际只需要它的低32位,可以单独看 eax。例如 info registers eax。
记忆技巧:大多数寄存器都是由16位寄存器发展过来了,只需要记住16位寄存器其他大部分寄存器就可以快速记忆

汇编程序的最小骨架

原文链接:

x64汇编之从程序编辑到系统调用https://blog.csdn.net/2401_88251163/article/details/160027656

一个最简单的汇编程序只需要 .text 段、_start 标签和退出系统调用。

程序结构

cpp 复制代码
section .text          ; 代码段
    global _start      ; 告诉链接器程序的入口

_start:                ; 入口标签
    ; 这里写指令

最简退出程序(完整可运行)

cpp 复制代码
; exit.asm
section .text
    global _start

_start:
    mov rax, 60        ; 系统调用号 60 = exit
    mov rdi, 0         ; 退出码 0 = 成功
    syscall

编译链接运行:

nasm -f elf64 exit.asm -o exit.o

ld exit.o -o exit

./exit

echo ? ; 输出 0 小技巧:如果想返回非0值,将 mov rdi, 0 改为 mov rdi, 42,然后 echo ? 会显示 42。

系统调用核心(write&exit)

原文链接:

x64汇编之从程序编辑到系统调用https://blog.csdn.net/2401_88251163/article/details/160027656系统调用是用户程序请求内核服务的接口。在 x64 Linux 中,通过 syscall 指令触发,参数通过特定寄存器传递。

系统调用号

|-----------|--------|------------|
| 系统调用 | 号(rax) | 功能 |
| sys_write | 1 | 向文件描述符写入数据 |
| sys_exit | 60 | 退出进程 |

参数传递

sys_write:

  • rdi:文件描述符(1 = 标准输出,即屏幕)

  • rsi:要写入的数据地址

  • rdx:数据长度(字节数)

sys_exit:

  • rdi:退出码(0 = 成功,非 0 = 错误)

完整输出示例(Hello 加换行)

cpp 复制代码
; hello.asm
section .data
    msg db "Hello", 10      ; 10 是换行符 ASCII
    len equ $ - msg         ; 自动计算长度(6)

section .text
    global _start

_start:
    mov rax, 1              ; sys_write
    mov rdi, 1              ; stdout
    mov rsi, msg            ; 字符串地址
    mov rdx, len            ; 长度
    syscall

    mov rax, 60             ; sys_exit
    mov rdi, 0
    syscall

编译链接运行:

nasm -f elf64 hello.asm -o hello.o

ld hello.o -o hello

./hello

输出:

Hello
小技巧:如果想要不换行,去掉 10 并将长度改为 5。

常见误区澄清

|----------------|----------------------------------------------------|
| 错误理解 | 正确理解 |
| rax=1 表示成功 | rax=1 是系统调用号,代表"写入";成功退出是 rax=60 + rdi=0 |
| rdi=1 永远表示标准输出 | 仅在 sys_write 中;在 sys_exit 中 rdi=1 表示退出码 1(错误) |
| 字符串长度不包括换行符 | 如果希望输出换行,必须在字符串中包含 10,长度也要算上它 |
| 中括号 \[\] 可以省略 | mov rsi, msg 得到地址;mov al, msg 得到该地址的第一个字节。切勿混淆 |

GDB 调试速查

原文链接1:

x64汇编之用调试器进行程序分析:GDBhttps://blog.csdn.net/2401_88251163/article/details/161540677原文链接2:

x64汇编之GDB进阶与printfhttps://blog.csdn.net/2401_88251163/article/details/161548434调试是理解汇编执行过程的最佳方式。以下命令足以应付初期调试。

启动与断点

gdb ./程序 # 启动 GDB 并加载程序

(gdb) break _start # 在 _start 处设置断点(可简写 b _start)

(gdb) run # 运行程序,停在断点(可简写 r)

单步执行

(gdb) stepi # 执行一条指令(可简写 si)

按回车键会重复上条命令,方便连续单步。

小技巧:设置 display/i rip 可以每次单步后自动显示下一条指令,避免反复输入 x/i rip。

查看信息

寄存器:info registers(简写 i r)

  • 只查看特定寄存器:i r rax rdi

内存:

  • 字符串:x/s &msg

  • 十六进制字节:x/6xb &msg(b 表示字节,6 表示数量)

  • 十进制字节:x/6db &msg

  • 指令:x/i $rip

小技巧:x 命令格式为 x/重复次数格式单位 地址。常用格式:s(字符串)、x(十六进制)、d(十进制)、i(指令)。常用单位:b(字节)、h(半字)、w(字)、g(巨字)。

自动显示(display)

(gdb) display/i $rip # 每次停下自动显示下一条指令

(gdb) display/x $rax # 自动显示 rax 的十六进制值

设置后,每次 stepi 或 run 遇到断点都会自动打印这些信息。用 info display 查看列表,undisplay 编号 取消。

函数调用栈(了解)

  • backtrace(简写 bt)显示调用栈

  • frame 1(简写 f 1)切换到上层栈帧

  • disas 反汇编当前函数

监视变量变化

汇编变量没有类型信息,需要强制转换:

watch (unsigned long)count

watch *(unsigned long *)&count

小技巧:如果变量是 db(1字节),用 (unsigned char);dw 用 (unsigned short);dd 用 (unsigned int);dq 用 (unsigned long)。

内容小结

本篇复习了:

  • 内存中数据的二进制/十六进制表示、补码和手动转换技巧

  • x64 常用寄存器及其子寄存器

  • 一个汇编程序的最小骨架(_start + sys_exit)

  • 系统调用 sys_write 和 sys_exit 的用法与常见误区(含中括号作用)

  • GDB 调试的核心命令、display 自动显示、x 命令格式、info 用法

相关推荐
AOwhisky17 小时前
Redis 学习笔记(第三期):持久化与主从复制
运维·数据库·redis·笔记·学习·云计算
c2385617 小时前
Linux C++ 进度条进阶美化与工程化封装
linux·运维·服务器
李小白6617 小时前
第四天-WEB服务器基本原理,IIS服务
运维·服务器·前端
2401_8346369918 小时前
Nginx 从入门到实战:静态 / 动态站点、PHP 部署与反向代理全解析
运维·nginx·php
爱喝水的鱼丶18 小时前
SAP-ABAP:SAP视图开发入门:四类标准视图的适用场景与创建步骤详解
服务器·数据库·性能优化·sap·abap
aosky18 小时前
一台电脑配置多个 SSH Key 对应不同的 GitHub 账号
运维·ssh·github
云登指纹浏览器19 小时前
WebDriver反检测技术详解:如何让自动化脚本看起来像真实浏览器
运维·自动化·跨境电商
xmtxz20 小时前
计算机网络基础课程学习心得:从理论抽象到硬核实战的进阶之路
运维·学习
RisunJan20 小时前
Linux命令-pgrep (通过进程名查找进程 ID)
linux·运维
回忆2012初秋21 小时前
【Nginx】优雅地走进高性能 Web 服务器世界(1)
服务器·前端·nginx