1618 words
8 minutes
一点点汇编笔记

写在前面
封面是辟浪。26拜年纪单品
还有个pwnlearn后面再写吧,一上大二事情真几把多,我去你大坝的。
今晚写汇编逆向又被整不会了,原来是又把调用给忘记了,故出一个复习一下
32位linux系统调用
EAX指定要调用的函数(系统调用号)
EBX传递函数的第一个参数
ECX传递函数的第二个参数
EDX传递函数的第三个参数
返回值EAX
还有两个滚木esi和edi分别传第四第五个参数
系统调用还有一个超大的表,这里仅列举5个常见的
| 系统调用 | 编号 (eax) | 参数 1 (ebx) | 参数 2 (ecx) | 参数 3 (edx) | 使用场景 |
|---|---|---|---|---|---|
exit | 1 | 返回码 (0) | - | - | 让程序正常结束,避免 crash |
read | 3 | fd (0=stdin) | 缓冲区地址 | 读取长度 | 读取输入 |
write | 4 | fd (1=stdout) | 缓冲区地址 | 写入长度 | 输出信息或泄露地址 |
open | 5 | 文件名地址 | flags (0=只读) | mode (0) | ORW(Open-Read-Write) 中打开 flag 文件 |
execve | 11 | 程序路径 (/bin/sh) | argv 地址 (0) | envp 地址 (0) | 拿 Shell 的核心(最常用) |
以及64位的纵向对比
| 系统调用 | 32位 (int 0x80) | 64位 (syscall) |
|---|---|---|
read | 3 | 0 |
write | 4 | 1 |
open | 5 | 2 |
execve | 11 | 59 |
Linux 系统调用 read 的返回值(存入 eax)是“实际读取的字节数”,而不是读取到的数据本身。
64位
| 寄存器 | read 调用前 | write 调用前 |
|---|---|---|
rax | 0(系统调用号) | 1(系统调用号) |
rdi | 文件描述符(0=stdin) | 文件描述符(1=stdout) |
rsi | 缓冲区地址(buf) | 缓冲区地址(buf) |
rdx | 读取长度(count) | 写入长度(count) |
r10 | 未使用 | 未使用(注意:第4个参数用 r10,但 read/write 只需3个) |
rax:变为返回值(成功时返回实际读/写的字节数;失败时返回负数错误码,如-EFAULT)。rcx和r11:被破坏(这是关键!syscall指令内部会使用rcx保存返回地址,用r11保存rflags)。调用后这两个寄存器的值是不可预测的。- 参数寄存器(
rdi、rsi、rdx):保持不变(内核会保存它们的值)。
32位
| 寄存器 | read 调用前 | write 调用前 |
|---|---|---|
eax | 3(系统调用号) | 4(系统调用号) |
ebx | 文件描述符(0) | 文件描述符(1) |
ecx | 缓冲区地址 | 缓冲区地址 |
edx | 读取长度 | 写入长度 |
eax:变为返回值(成功时返回实际字节数,失败时返回负错误码)。- 所有参数寄存器(
ebx、ecx、edx):保持不变(int 0x80会保存恢复寄存器上下文)。
NASM汇编器伪指令
数据传输相关
mov a, b | 将 b 的值赋给 a | 基础搬运 |
|---|---|---|
movzx a, b | 零扩展后搬运(小→大) | 处理 char 转 int |
movsx a, b | 符号扩展后搬运 | 处理有符号数扩展 |
lea a, [b + c*n] | 计算地址(不访存) | 常用于快速计算(如 lea rax, [rax+rax*4] 实现乘5) |
逻辑运算
add a, b | a = a + b | OF/SF/ZF/AF/CF/PF |
|---|---|---|
sub a, b | a = a - b | 同上 |
imul a | 有符号乘法(rax *= a,结果在 rdx:rax) | OF/CF |
mul a | 无符号乘法 | 同上 |
idiv a | 有符号除法(rdx:rax / a,商→rax,余数→rdx) | 无 |
div a | 无符号除法 | 无 |
inc a | a++ | OF/SF/ZF/AF/PF(CF不变) |
dec a | a— | 同上 |
and a, b | a &= b | OF=0, CF=0, 影响SF/ZF/PF |
or a, b | a |= b | 同上 |
xor a, b | a ^= b | 同上 |
not a | a = ~a | 不影响标志 |
shl a, cl | 逻辑左移 | 影响CF/OF/SF/ZF/PF |
shr a, cl | 逻辑右移(补0) | 同上 |
sar a, cl | 算术右移(补符号位) | 同上 |
栈
push a | rsp -= 8; [rsp] = a | 栈向下增长 |
|---|---|---|
pop a | a = [rsp]; rsp += 8 | |
pushf | 压入 rflags | 少用 |
popf | 弹出 rflags | 少用 |
跳转
jmp | 无条件跳转 | - |
|---|---|---|
call func | 压入返回地址,跳转至 func | - |
ret | 弹出返回地址,跳转 | - |
条件跳转
je / jz | 同左 | 相等 (ZF=1) |
|---|---|---|
jne / jnz | 同左 | 不等 (ZF=0) |
jg / jnle | ja / jnbe | 大于 (>),无符号用 a 表示 above |
jge / jnl | jae / jnb | 大于等于 (>=) |
jl / jnge | jb / jnae | 小于 (<) |
jle / jng | jbe / jna | 小于等于 (<=) |
比较与测试
cmp a, b | 计算 a - b,不写结果 | 影响 ZF/OF/SF/CF/AF/PF |
|---|---|---|
test a, b | 计算 a & b,不写结果 | 影响 ZF/SF/PF(OF=0, CF=0) |
系统调用
syscall | 发起系统调用 | Linux x86-64 中,系统调用号放在 rax,参数依次放入 rdi, rsi, rdx, r10, r8, r9。 |
|---|---|---|
int 0x80 | 32位系统调用 | 较少见,通常在32位兼容模式或 shellcode 中。 |
标志位
- OF 溢出标志位
OF = 1 表示带符号整数运算时结果发生溢出。对无符号整数运算无意义
-
ZF 零标志位
ZF = 1 表示结果为0,无论是有符号数还是无符号数,ZF都有意义
-
CF 进/借位标志
表示无符号整数数加/减运算时的进位/借位。
- 加法时,CF = 1表示无符号数加法溢出,CF=最高进位输出
- 减法时,CF = 1表示有借位,即不够减,CF=最高位进位输出取反
对于有有符号数的整数运算没有意义
- 但是真要看的话,CF=最高位进位输出
- SF 符号标志
结果为负,即符号位为1时SF为1,否则为0
- AF 辅助进位标志
- 触发条件:运算时,低 4 位(即 BCD 码的个位) 发生进位或借位。
- 用途:专用于 BCD(二进制编码的十进制)运算(如
DAA、AAA指令)。在常规的字符串、整数逆向中基本用不到,只有遇到特殊加密或古老反调试技巧时才可能涉及。
- PF 奇偶标志
运算结果的低 8 位(最末字节) 中,含有偶数个 1 时,
PF = 1。
db:定义字节串(Define Byte),末尾的0是 C 风格字符串的结束符。equ:相当于#define,不占内存,只在汇编时做文本替换。
然后是不知道哪的内容
(反正我又不用学)
- 数据段
存放赋了初始值的全局变量以及静态变量
权限为rw-
- BSS
未初始化的全局变量区
存放未初始化或初始化为 0 的全局变量和静态变量(如 C 语言中的 char buf[100];)
权限一般为rw-
- 代码段
存放CPU要执行的机器指令
权限通常为r-x
傻逼NX开启的时候这里是唯一能执行代码的地方
低地址 0x400000 → 代码段 (.text) [r-x] ↓ 数据段 (.data) [rw-] ← 有初值的全局变量 ↓ BSS 段 (.bss) [rw-] ← 无初值的全局变量(运行时清零) ↓ 堆 (Heap) [rw-] ← 动态分配(malloc) ↓ ... 空闲区域 ... ↓ 栈 (Stack) [rw-] ← 局部变量,向下增长高地址 0x7FFFFFFF → 环境变量 / 参数Some information may be outdated