Linux基础与汇编入门:从命令行到栈帧

各位同学,欢迎来到CTF Pwn的第一站。很多人觉得Pwn的门槛高,其实说白了,就是两件事:看懂程序在干什么,以及找到控制程序的方法。而这两件事,都离不开Linux基础和汇编知识。

我个人习惯把这一章叫做“地基”。地基打不牢,后面堆栈溢出、ROP链什么的,你学起来会非常痛苦。我见过太多选手,漏洞原理讲得头头是道,结果连chmod和chown都分不清,调试时连寄存器都看不懂——嗯,那基本就是白给。

Linux命令行与权限管理

CTF的Pwn题目,99%跑在Linux上。你至少得会这几样:

  • 文件操作:ls、cd、cp、mv、rm、cat、file——别笑,我真见过有人用cat读二进制文件然后终端卡死的。
  • 权限管理:chmod、chown、umask。Pwn题经常给你一个flag文件,权限是400,只有特定用户能读。你提权不了,就看不到flag。
  • 进程管理:ps、top、kill、strace、ltrace。调试时strace能帮你看到程序调用了哪些系统调用,是不是在偷偷读flag。
  • 网络工具:nc、ncat、socat。远程打Pwn全靠它们。

权限模型速记:Linux权限分三组——所有者(u)、所属组(g)、其他人(o)。每组三个位:读(r=4)、写(w=2)、执行(x=1)。chmod 755 file就是所有者rwx,组和他人r-x。

我的小技巧:拿到一个陌生二进制,先跑file看架构,再跑checksec看保护。这是Pwn的“望闻问切”。

x86/x64汇编基础

汇编是Pwn的“母语”。你不一定要能手写汇编,但一定要能读懂。我刚开始学的时候,对着mov eax, 0和xor eax, eax纠结了半天——为什么清零要用两种写法?后来才知道,xor的机器码更短,是编译器优化的小把戏。

核心寄存器,你记住这几个就够了:

架构 通用寄存器 特殊寄存器
x86 eax, ebx, ecx, edx, esi, edi, ebp, esp eip(指令指针)
x64 rax, rbx, rcx, rdx, rsi, rdi, r8~r15 rip(指令指针)

为什么x64多了r8~r15?因为64位系统需要更多寄存器来传参,减少内存访问。嗯,这就引出了函数调用约定。

函数调用约定

调用约定,说白了就是“函数之间怎么传参数、怎么收尾”的一套规矩。不同平台、不同编译器,规矩不一样。Pwn里最常碰到的有三种:

cdecl(32位Linux默认)

  • 参数通过栈传递,从右向左压栈。
  • 调用者负责清理栈。
  • 返回值放在eax。

fastcall(32位Windows常用)

  • 前两个参数用ecx和edx传递,其余走栈。
  • 被调用者清理栈。

System V AMD64(64位Linux默认)

  • 前6个整数参数依次放在rdi、rsi、rdx、rcx、r8、r9。
  • 多余的参数走栈。
  • 返回值放在rax。

我曾经踩过的坑:有一次打一个64位Pwn题,我ROP链里传第7个参数,直接往栈上怼,结果程序崩了。后来才发现,64位下前6个参数走寄存器,第7个才开始走栈。我漏掉了这个细节,浪费了整整一个下午。

栈帧结构

栈帧是函数调用的“现场记录”。每次调用一个函数,系统都会在栈上分配一块区域,用来存局部变量、保存的寄存器、返回地址等等。你想想看,如果这块区域被破坏了——比如你输入了一个超长的字符串——那返回地址就可能被覆盖成你想要的任意地址。这就是最经典的栈溢出。

一个典型的栈帧长这样:

高地址
+------------------+
| 调用者的栈帧      |
+------------------+
| 返回地址          |  ← 被溢出覆盖的目标
+------------------+
| 保存的 ebp/rbp   |  ← 旧基址指针
+------------------+
| 局部变量          |
| (缓冲区等)      |  ← 输入点
+------------------+
低地址

为什么返回地址在“上面”?因为栈是向下增长的。每次push,esp/rsp减小;每次pop,esp/rsp增大。所以局部变量在低地址,返回地址在高地址。你从低地址往高地址写数据,就能一路写到返回地址。

核心逻辑:栈溢出利用的本质,就是用输入数据覆盖返回地址,让程序跳转到我们指定的位置(比如shellcode或ROP链)。

下面这张图,把本章的知识体系串起来了:

Linux基础与汇编入门 · 知识体系 Linux命令行 x86/x64汇编 函数调用约定 文件操作 权限管理 进程管理 网络工具 通用寄存器 指令指针 常用指令 寻址模式 cdecl fastcall SysV AMD64 栈帧结构 栈溢出利用 三个模块汇聚到栈帧结构,最终指向栈溢出利用——Pwn的核心战场

你看,Linux命令行是操作环境,汇编是理解程序的语言,调用约定和栈帧是漏洞发生的舞台。这三者缺一不可。

我的建议:学完这一章,你可以自己写一个简单的C程序,然后用gcc -S生成汇编,对照着看栈帧结构。亲手做一遍,比看十遍书都管用。