第二章 计算机基础回顾:计算机组成原理、操作系统基础与汇编入门
各位同学,欢迎来到第二章。说实话,这一章的内容看起来有点“理论”,但请相信我——这是你未来逆向工程路上最坚实的垫脚石。我见过太多人一上来就扑向反汇编工具,结果连内存布局都看不懂,最后卡在某个坑里出不来。嗯,咱们先把地基打牢。
2.1 计算机组成原理——机器到底怎么工作的?
你写下的每一行代码,最终都会变成电信号在硅片上流动。计算机的核心,说白了就是三个东西:CPU、内存、I/O设备。它们之间靠总线连接,像一条高速公路。
2.1.1 CPU:大脑中的大脑
CPU内部有几个关键部件:
- 控制单元(CU):负责取指令、解码、调度。它像个指挥官,告诉其他部件该干什么。
- 算术逻辑单元(ALU):干苦力活的,做加减乘除、与或非等运算。
- 寄存器:CPU内部的小型存储单元,速度极快。我经常跟学员说:“寄存器是CPU的桌面,内存是书架,硬盘是仓库。”你想想看,从桌面拿东西当然比从仓库快得多。
重点记忆:x86架构下,通用寄存器有EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP。其中ESP指向栈顶,EBP指向栈底——这两个在逆向分析中几乎天天见。
2.1.2 内存:程序的舞台
内存是程序运行时的“舞台”。每个字节都有一个地址,CPU通过地址总线找到它。我在做漏洞分析时,经常需要追踪某个变量在内存中的位置——说白了,就是跟地址打交道。
内存的层次结构是这样的:
| 层级 | 速度 | 容量 | 典型大小 |
|---|---|---|---|
| 寄存器 | 最快(1个时钟周期) | 极小 | 几十字节 |
| L1缓存 | 极快(2-4周期) | 小 | 32KB-64KB |
| L2缓存 | 快(10-20周期) | 中等 | 256KB-1MB |
| 主存(RAM) | 较慢(100-200周期) | 大 | 8GB-64GB |
| 磁盘 | 极慢(百万级周期) | 极大 | 512GB-2TB |
我的经验:逆向分析时,重点关注寄存器和栈内存。缓存和磁盘的细节可以暂时放一放——除非你在做性能优化或侧信道攻击。
2.2 操作系统基础——程序是怎么活着的?
操作系统是硬件和应用程序之间的“管家”。它管理着进程、内存、文件系统。我个人习惯把操作系统看作一个“资源调度员”,它决定谁用CPU、谁用内存、谁读写磁盘。
2.2.1 进程:运行中的程序
进程是程序的一次执行实例。每个进程有独立的地址空间,包括:
- 代码段(.text):存放机器指令,通常是只读的。
- 数据段(.data/.bss):存放全局变量和静态变量。
- 堆(Heap):动态分配的内存,向上增长。
- 栈(Stack):函数调用、局部变量、返回地址,向下增长。
我曾经在分析一个缓冲区溢出漏洞时,发现攻击者就是通过覆盖栈上的返回地址来劫持控制流的。你想想看,如果你连栈的结构都不清楚,怎么可能看懂exploit代码?
2.2.2 内存管理:虚拟内存的魔法
现代操作系统都使用虚拟内存。每个进程以为自己拥有4GB(32位)或超大空间(64位),实际上物理内存是共享的。操作系统通过页表完成虚拟地址到物理地址的映射。
避坑指南:我曾经在调试一个内核驱动时,直接操作了物理地址,结果系统蓝屏了。记住:用户态程序只能看到虚拟地址,别试图直接访问物理内存——除非你在写内核代码。
2.2.3 文件系统:数据的持久化
文件是字节序列,操作系统提供统一的接口(open、read、write、close)来操作它们。在逆向工程中,我们经常需要分析PE(Windows)或ELF(Linux)文件格式——它们本质上就是有特定结构的二进制文件。
2.3 汇编语言入门(x86架构)——读懂机器的语言
汇编语言是机器指令的助记符。说白了,它就是人类能读懂的机器码。我刚开始学逆向时,觉得汇编很难,后来发现——其实就那么几十条常用指令,翻来覆去地用。
2.3.1 寄存器速记
x86架构下,32位寄存器有8个通用寄存器:
- EAX:累加器,常用于算术运算和函数返回值。
- EBX:基址寄存器,常用来存放地址。
- ECX:计数器,循环指令中自动递减。
- EDX:数据寄存器,常与EAX配合做乘除法。
- ESI/EDI:源/目标索引寄存器,字符串操作时用。
- EBP:基址指针,指向当前栈帧的底部。
- ESP:栈指针,指向栈顶。
记住:ESP和EBP是栈操作的核心。函数调用时,先push EBP,再mov EBP, ESP——这个模式你会在反汇编代码中看到无数次。
2.3.2 常用指令
我挑几个最常用的指令讲讲:
- mov:数据传送。mov eax, 0x10 就是把0x10放到eax里。
- push/pop:压栈和出栈。push eax 会把eax的值压到栈顶,esp减4。
- call/ret:函数调用和返回。call会把返回地址压栈,然后跳转到目标地址。
- jmp/je/jne:无条件/条件跳转。je是相等时跳转,常用于if语句。
- add/sub:加减法。add eax, ebx 就是 eax = eax + ebx。
- cmp/test:比较和测试。cmp eax, ebx 会设置标志位,但不改变寄存器值。
; 一个简单的x86汇编示例
; 计算 3 + 5,结果存到eax
mov eax, 3 ; eax = 3
add eax, 5 ; eax = eax + 5 = 8
ret ; 返回,eax就是返回值
2.3.3 函数调用约定
调用约定决定了参数怎么传、谁清理栈。最常见的两种:
| 约定 | 参数传递 | 栈清理者 | 常见于 |
|---|---|---|---|
| cdecl | 从右向左压栈 | 调用者 | C语言(默认) |
| stdcall | 从右向左压栈 | 被调用者 | Windows API |
我的建议:逆向分析时,先看函数开头和结尾。cdecl的结尾是ret(不清理参数),stdcall的结尾是ret 8(清理8字节参数)。这个细节能帮你快速判断调用约定。
2.4 知识体系总览
下面这张图是我自己整理的,把本章的核心知识点串了起来。你保存下来,以后复习时看一眼就明白了。
2.5 本章小结
这一章我们回顾了计算机组成原理、操作系统基础和x86汇编入门。说实话,内容有点多,但都是干货。我个人建议你:
- 把寄存器的名字和用途背下来——这是最基础的。
- 理解栈的结构和函数调用过程——这是逆向分析的核心。
- 动手写几个简单的汇编程序,用调试器单步执行——看寄存器怎么变,栈怎么动。
我曾经带过一个学员,他花了一周时间把本章内容吃透,后面学逆向时进步飞快。你想想看,当你面对一个陌生的二进制文件时,能看懂它的内存布局、能读懂它的汇编指令、能理解它的函数调用——那还有什么能难住你呢?