一、CPU、寄存器、内存 —— 计算机的“大脑、手稿、办公桌”1. CPU中央处理器 大脑作用执行一切计算和决策。通俗理解你脑子里有个“算术单元”ALU能做加减乘除、比较大小。还有个“指挥单元”CU决定下一步做什么。脑子里有个“时钟”一下一下地指挥你做动作3GHz 每秒30亿次心跳。类比CPU 就像餐厅的主厨—— 他只负责做菜执行指令不负责存菜数据。2. 寄存器 大脑里的“瞬时记忆”作用CPU 直接使用的“极速便签纸”。为什么需要它CPU 速度太快内存RAM根本追不上。所以 CPU 把“正在算的数”先抄到寄存器里算完再写回去。容量总共几十个字节相当于一张小纸条。速度比内存快 100 倍以上。类比你心算17 × 23时脑子里暂时记住的“17”“23”“中间结果”就是寄存器。你记不住的东西要低头看草稿纸内存。3. 内存RAM 办公桌作用存放当前正在运行的程序和正在处理的数据。特点容量大8GB~64GB速度中等纳秒级断电即丢所以程序要保存到硬盘类比你办公桌上摊开的文件、图纸就是你“正在处理的工作”。书架硬盘存所有资料但取用慢桌面内存只放当前要用的。4. 常见的寄存器1通用寄存器可存数据或地址寄存器名称主要用途EAX累加器算术运算、函数返回值EBX基址寄存器存放内存基地址如数组起始地址ECX计数器循环计数如LOOP指令EDX数据寄存器I/O操作、辅助EAX乘除法结果ESI源索引寄存器字符串/数组操作的源指针EDI目的索引寄存器字符串/数组操作的目标指针ESP栈指针寄存器指向当前栈顶地址EBP基址指针寄存器指向函数栈帧基址调试常用2专用寄存器寄存器名称用途EIP指令指针存放下一条要执行的指令地址EFLAGS标志寄存器存储状态标志ZF、CF、SF等3段寄存器32位Windows中重要寄存器用途CS代码段DS数据段SS栈段ES附加数据段FSWindows中指向TEB线程环境块GSx64中常用替代FS4标志寄存器EFLAGS关键位标志位名称触发条件典型用途CF0进位标志无符号数溢出进位/借位JC/JNCADC/SBBPF2奇偶标志结果低8位中1的个数为偶数数据校验已少用AF4辅助进位BCD码低4位向高4位进位/借位BCD运算ZF6零标志运算结果为0JE/JNZCMP/TESTSF7符号标志结果为负数与最高位相同有符号数比较OF11溢出标志有符号数溢出正正负JO/JNODF10方向标志STD/CLD设置字符串指令自动减/增地址串操作方向IF9中断允许STI/CLI控制是否响应可屏蔽中断中断控制TF8陷阱标志置1时单步执行调试器单步二、为什么需要“地址”和“指针”内存像一个巨大的带编号的柜子地址从0x0000到0xFFFFFFFF。CPU 要拿数据必须说清楚“柜子号”地址。汇编中[地址]表示“去这个柜子里取值”不加[]表示“取这个数字本身”。示例MOV EAX, 42 ; 把数字42直接放进脑子寄存器 MOV EAX, [0x1000] ; 去内存地址0x1000取数放进脑子三、指令集 —— CPU 的“语言”CPU 不认识 Java/C只认识机器码二进制。汇编语言是机器码的“可读英文缩写版”。1. 数据传送指令指令示例作用备注MOVMOV EAX, 42数据/地址复制到寄存器或内存不修改标志位XCHGXCHG EAX, EBX交换两个操作数可原子操作LEALEA ESI, [EBX4]加载有效地址计算偏移不访存常用于地址计算2. 算术运算指令指令示例作用标志位影响ADDADD EAX, EBX加法影响ZF、CF等SUBSUB AX, 10减法同ADDINC/DECINC DWORD [ESI]自增/自减不影响CFMULMUL ECX无符号乘法EAX*ECX→EDX:EAX结果高低位IMULIMUL EBX有符号乘法DIVDIV CL无符号除法AX/CL→AL…AHIDIVIDIV CL有符号除法3. 逻辑与位操作指令示例作用标志位影响ANDAND AL, 0xF0按位与清零特定位ZF1若结果为0OROR DX, 0x01按位或置位特定位XORXOR EAX, EAX按位异或快速清零NOTNOT BYTE [EDI]按位取反不影响标志位SHL/SHRSHL AX, 3逻辑左移/右移CF最后移出位SARSAR BL, 1算术右移保留符号位4. 流程控制指令指令示例作用依赖标志位JMPJMP label无条件跳转-CALLCALL function调用函数压入返回地址-RETRET从函数返回-CMPCMP EAX, EBX比较SUB但不保存结果设置ZF、CF等TESTTEST AL, 0x01位测试AND不保存结果条件跳转表常用指令条件标志位JE/JZ等于/为零ZF1JNE/JNZ不等于/不为零ZF0JS符号为负SF1JC/JB进位/低于无符号CF1JNC/JNB无进位/不低于CF0JO溢出OF1JL/JNGE小于有符号SF≠OFJGE/JNL大于等于有符号SFOFJG/JNLE大于有符号ZF0且SFOFJA/JNBE高于无符号CF0且ZF05. 栈操作指令指令示例作用等价操作PUSHPUSH EAX压栈SUB ESP,4 MOV [ESP],EAXPOPPOP EBX出栈MOV EBX,[ESP] ADD ESP,4PUSHADPUSHAD保存所有通用寄存器中断处理常用POPADPOPAD恢复所有通用寄存器6. 字符串操作指令指令作用重复前缀MOVSB/MOVSW/MOVSD复制字节[ESI]→[EDI]REPCMPSB/CMPSW/CMPSD比较字节REPE/REPNESTOSB/STOSW/STOSD存储字节AL→[EDI]REPLODSB/LODSW/LODSD加载字节[ESI]→AL通常不用REP7. 系统与特权指令指令作用权限INT 0x80触发软中断Linux系统调用用户态SYSCALLx64快速系统调用用户态IRET从中断返回内核态CPUID获取CPU信息用户态为什么叫“栈”Stack像一叠盘子后放先取LIFO。函数调用时用栈保存“返回地址”和“局部变量”。嵌套调用越深栈越高。四、标志寄存器 —— CPU 的“状态指示灯”EFLAGS像一个仪表盘记录上一步运算的结果特征标志含义通俗实际用处ZF零标志上次结果是否为 0判断 if (a b)CF进位标志无符号数是否超出范围判断无符号溢出OF溢出标志有符号数是否超出范围判断正负数溢出SF符号标志结果是否为负数判断 0例CMP EAX, EBX实际做减法但不保存结果只更新标志位随后JE如果相等则跳转就是看 ZF。五、函数调用流程栈帧每次调用函数CPU 会把返回地址压栈PUSH跳转到函数入口CALL函数内部保存旧EBP栈帧基址设置新EBP ESP在栈上分配局部变量空间返回时恢复旧EBP弹出返回地址RET继续执行调用者这就是为什么递归太深会“栈溢出” —— 盘子叠太多倒了。六、Windows 特有的TEB 和 PEB进程/线程“户口本”为什么需要它们操作系统要管理多个进程程序和多个线程程序内的并行任务。每个线程和进程都有自己独特的“环境信息”比如我是谁ID我的栈在哪里我是否在被调试我加载了哪些 DLLWindows 把这些信息存在内存中的固定结构里叫 TEB 和 PEB。1. TEB线程环境块 线程的“身份证工作台”每个线程有一个 TEB。存放线程 ID所属进程 ID栈的起止地址异常处理链SEH线程局部存储TLS每个线程自己的私有变量指向 PEB 的指针类比每个流水线工人线程都有自己的工位TEB工位上贴着工号、所在车间进程、工具箱位置栈、操作手册异常处理。2. PEB进程环境块 进程的“户口簿”每个进程只有一个 PEB。存放进程基址EXE 加载地址是否被调试BeingDebugged已加载模块链表EXE 所有 DLL进程堆句柄命令行参数类比一个车间进程有一本总台账PEB记录车间里有哪些设备模块、谁在监控调试器、原料堆在哪堆内存。3. 怎么访问它们32位 WindowsFS寄存器是个“特殊指针”始终指向当前线程的 TEB。FS:[0x30]就是 TEB 里指向 PEB 的指针。// 获取是否被调试反调试常用BeingDebugged*(char*)(FS:[0x30]0x02);这就是很多反调试工具检测调试器的原理 —— 直接看 PEB 里的标志位。4. 为什么其他方向的工程师要了解这个安全/运维恶意代码常通过 PEB 检测调试器、遍历模块反调试、反 Hook。Java/Go 工程师JVM/Go runtime 在底层也会用类似机制管理线程虽然不直接操作 FS。性能优化理解 TLB/缓存/寄存器有助于写出缓存友好的代码。排查线上问题Crash 时栈回溯、线程堆栈溢出都和 TEB/栈概念直接相关。七、完整执行流程从代码到运行以你写的一行a b c为例极端简化步骤发生了什么涉及概念1编译器将高级语言转成汇编指令集2CPU 从内存取指令EIP指向内存 寄存器3从内存加载b、c到寄存器数据传送4ALU 执行加法算术运算5结果存入寄存器EAX寄存器6若需要写回内存内存7更新标志寄存器ZF/CF 等标志位8下一条指令可能跳转if/循环流程控制9若在 Windows系统可能通过 TEB/PEB 获取环境信息内核结构八、关键概念速查表新手友好版术语一句话解释生活类比CPU执行计算的大脑主厨寄存器CPU 内部的极速便签脑子里的临时数字内存RAM运行中程序的数据区办公桌面地址内存中每个字节的编号柜子编号指令CPU 能识别的操作命令菜谱步骤标志位上一步结果的属性标签“结果为负”的备忘贴栈后进先出的临时存储区叠盘子栈帧一次函数调用的私有栈区域每道菜的操作台TEB线程专属信息块Windows工人的工位PEB进程专属信息块Windows车间的台账调试器检测检查 PEB 中是否被调试标志看有没有人在监控九、举例用通俗流程解释一段汇编mov eax, 10 ; 脑子记住数字10 add eax, [num] ; 从办公桌内存取 num 的值加到脑子里 mov [num], eax ; 把脑子里的结果写回办公桌 cmp eax, 0 ; 比较脑子里的数和0不保存结果只更新仪表盘 jz zero_label ; 如果仪表盘显示“等于0”ZF1就跳转这就是计算机执行if (num 0)的底层实际动作。