【学习记录】Week2(四):底层基石——汇编速查 lea/mov/push/pop/call/ret

📅 2026/7/1 6:52:17
【学习记录】Week2(四):底层基石——汇编速查 lea/mov/push/pop/call/ret
写在前面在 PWN 的世界里汇编语言就是我们的“母语”。无论是用objdump看反汇编还是在 GDB 里单步调试或者是手工挑选 ROP Gadget都离不开对每一条汇编指令的精准理解。本文将为你梳理最核心的 6 条指令特别是新手最容易混淆的lea和mov。 目录movvslea取值与取址的终极对决栈操作双雄push与pop控制流转移call与retROP 的灵魂模拟实战一段汇编的栈帧变化推演1.movvslea取值与取址的终极对决这两个指令是看懂反汇编的第一道门槛。记住一句话mov看重内容lea看重地址。1.1mov(Move Data)作用把源操作数的值复制给目的操作数。格式mov dest, srcPWN 场景mov rax, 0x10—— 将常数0x10赋值给rax。mov rdi, rsi—— 将rsi寄存器的值赋给rdi64位传参常用。mov rax, [rbp - 0x10]——注意这里的方括号方括号代表解引用像 C 语言的*。这条指令的意思是把内存地址rbp - 0x10处存放的数据读出来放进rax。1.2lea(Load Effective Address)作用计算源操作数的地址并把这个地址值赋给目的操作数。不访问内存格式lea dest, srcPWN 场景lea rax, [rbp - 0x10]—— 这里没有解引用这条指令的意思是计算出rbp - 0x10这个地址值然后把这个地址值本身放进rax。用途通常用于把一个局部变量比如char buf[64]的首地址传给函数。比如lea rax, [rbp-0x40]然后mov rdi, rax接着call gets这就是把buf的地址传给gets函数的第一个参数。2. 栈操作双雄push与pop栈是向下生长的高地址向低地址RSP栈顶指针始终指向栈顶元素。这两个指令会自动修改RSP。2.1push(压栈)动作RSP RSP - 8(64位) 或RSP RSP - 4(32位)然后将操作数存入RSP指向的新内存地址。PWN 场景函数序言中的push rbp就是把主调函数的栈底保存起来以便函数返回时恢复。2.2pop(出栈)动作将RSP当前指向的内存地址中的数据读入目标操作数然后RSP RSP 8(或 4)。PWN 场景函数结语中的pop rbp就是恢复主调函数的栈底。更重要的是ROP 攻击中我们要寻找大量的pop rdi; ret这样的 Gadget用来把栈上的数据弹入寄存器从而控制函数参数3. 控制流转移call与retROP 的灵魂这两个指令决定了程序执行到哪里去也是我们劫持控制流的核心目标。3.1call(调用函数)动作等价于push 下一条指令的地址jmp 目标函数地址。本质它把返回地址call指令后面的那条指令地址压入栈中然后跳转。这就解释了为什么发生栈溢出时我们覆盖的正是这个被call压入栈中的返回地址。3.2ret(函数返回)动作等价于pop rip。本质从栈顶弹出一个 8 字节的数据直接塞进指令指针寄存器RIP中CPU 接着就会跳转到这个地址执行。PWN 启示这就是栈溢出能拿 Shell 的灵魂只要我们通过溢出覆盖了栈顶的返回地址当函数执行到ret时就会乖乖跳到我们指定的地址去执行。如果是连续的ret就会不断从栈上弹出地址并跳转这就是 ROPReturn-Oriented Programming链的底层逻辑。4. 模拟实战一段汇编的栈帧变化推演我们模拟一段常见的函数调用汇编推演一下栈和寄存器的变化0x401100 main: call 0x401200 vulnerable ; 1. 压入返回地址 0x401105RSP-8跳转 0x401200 vulnerable: push rbp ; 2. 压入旧 rbpRSP-8 mov rbp, rsp ; 3. 设置当前栈底此时 rbp rsp sub rsp, 0x40 ; 4. 开辟 0x40 字节栈空间RSP-0x40 lea rax, [rbp-0x40] ; 5. 计算 buf 地址存入 rax mov rdi, rax ; 6. 把 buf 地址传给 rdi (作为 gets 参数) call 0x401040 getsplt ; 7. 压入返回地址调用 gets ; 假设此时输入了 72 个 A p64(0xdeadbeef) leave ; 8. 等价于 mov rsp, rbp; pop rbp ret ; 9. 此时栈顶正好是 0xdeadbeefpop rip跳转假设性说明GDB 模拟推演当程序执行到ret指令前如果你在 GDB 中查看栈顶 (x/gx $rsp)你会看到0x7fffffffde08: 0x00000000deadbeef执行ret后查看寄存器 (info registers rip)你会看到rip 0xdeadbeef 0xdeadbeef控制流被完美劫持5. 结语汇编并不难难在建立“寄存器与内存联动”的空间感。牢记lea算地址、mov拿数据、ret弹栈跳转你就已经掌握了 PWN 所需的 80% 的汇编底子。下一部分我们将面对实战中常见的“拦路虎”——反调试检测教你如何用ptrace绕过它。如果本文对你有帮助请点赞收藏支持