
同一个CPU两套“黑话”ATT格式 vs Intel格式傻傻分不清楚寄存器前面到底要不要加“%”操作数到底谁左谁右一文终结你的所有困惑如果你曾经试图读懂Linux内核里的汇编代码或者无意间用GCC编译时加了-S选项生成了汇编文件你可能会对着屏幕发出灵魂拷问“这写的都是啥”明明CPU都是x86架构凭什么我上课学的mov ax, bx到了Linux底下就变成了movl %ebx, %eax寄存器前面多了个“%”操作数顺序还反了读起来像在照镜子——这到底是谁发明的“阴间语法”今天这篇文章就是来帮你终结这场混乱的。我们将彻底讲透ATT格式和Intel格式这对“孪生冤家”的全部区别让你以后无论在Windows下写汇编还是在Linux下读内核源码都能无缝切换、游刃有余。 第一节为什么会有两套语法一段不得不说的“恩怨情仇”故事要从上世纪讲起。Intel公司是x86架构处理器的缔造者。1978年6月8日大名鼎鼎的8086处理器诞生。为了让程序员不用对着二进制0101秃头Intel顺手设计了一套汇编语言助记符——这就是Intel风格汇编的由来。Windows和DOS世界几乎被它统治。而在另一边ATT公司的前身——贝尔实验室是C语言和Unix系统的出生地。这里的牛人们追求的是跨平台——他们希望汇编语言的语法能在不同架构的CPU上尽量统一注意是语法跨平台不是指令跨平台。于是他们抛开Intel的规范自立门户搞了一套汇编语法这就是ATT风格汇编的起源。当Unix被移植到i386处理器时自然也就沿用了ATT的汇编格式。Linux作为Unix家族的一员同样继承了这一传统。所以现状就是Windows阵营→ Intel格式你上课学的那套Linux/Unix阵营→ ATT格式GCC默认生成的那套两种格式背后是两种不同的工具链生态不存在谁对谁错只是“方言”不同罢了。而考研408统考要求掌握的是Intel格式但万一题目给的是ATT格式呢看懂它就是你比别人多一分的底气。 第二节一张表终结所有混乱核心干货废话不多说直接上表。这张表就是你以后查这两种格式区别的终极词典。对比维度ATT 格式Intel 格式操作数顺序op 源, 目的源在左目的在右op 目的, 源目的在左源在右寄存器表示%eax寄存器名前必须加“%”eax直接写寄存器名立即数表示$985立即数前必须加“$”985直接写数字内存地址表示(af996h)用小括号[af996h]用中括号数据长度标识指令后缀b(字节)、w(字)、l(双字)byte ptr、word ptr、dword ptr前缀简单偏移-8(%ebx)偏移量在括号外[ebx - 8]偏移量在括号内复杂寻址4(%ebx, %ecx, 32)偏移量(基址, 变址, 比例因子)[ebx ecx*32 4][基址 变址×比例因子 偏移量]指令大小写只能用小写字母大小写不敏感远跳转/远调用ljump $section, $offsetjmp far section:offset绝对跳转/调用操作数前加*不需要记忆口诀ATT Alot ofTrouble Trouble麻烦多多——要加%、要加$、顺序还反着来。Intel 你上课学的那套怎么顺手怎么来。 第三节逐条拆解包教包会1️⃣ 操作数顺序——最让人崩溃的区别这是两种格式最核心、最反直觉的区别。Intel格式mov eax, ebx→ 把ebx的值赋给eax目的在左源在右ATT格式movl %ebx, %eax→ 同样是把ebx的值赋给eax源在左目的在右你看同样一条指令两种写法操作数位置完全相反。如果你用Intel的思维去读ATT代码会直接把赋值方向搞反——这是新手最常见的错误。ATT之所以采用“源, 目的”的顺序是为了与之前的Unix汇编器保持兼容。而这个顺序的根源甚至可以追溯到PDP-11处理器的指令编码方式。历史遗留问题没办法。快速记忆ATT是“从左到右”读源→目的Intel是“从右到左**读”目的←源。2️⃣ 前缀符号——寄存器要“%”立即数要“$”ATT格式像个仪式感很强的人寄存器前面必须加%%eax、%ebx立即数前面必须加$$985、$0xffffIntel格式则简单粗暴寄存器直接写eax、ebx立即数直接写985、0ffffh举个例子同样是“把985赋给eax寄存器”ATTmovl $985, %eaxIntelmov eax, 985快速记忆ATT “有$有%”Intel “啥也没有”。3️⃣ 内存地址的括号——圆括号 vs 方括号ATT格式用圆括号( )表示内存地址movl (%eax), %ebx→ 把eax指向的内存地址中的值赋给ebxIntel格式用方括号[ ]表示内存地址mov ebx, [eax]→ 同样的意思快速记忆ATT “圆润”Intel “方正”。4️⃣ 数据长度——后缀 vs 前缀ATT格式在指令后面加一个字母表示数据长度b byte字节8位w word字16位l long双字32位q quad word四字64位例如movb、movw、movl、movqIntel格式则在内存操作数前面加前缀byte ptr、word ptr、dword ptr、qword ptr例如mov byte ptr [af996h], 5快速记忆ATT是“后缀党”Intel是“前缀党”。5️⃣ 寻址方式——从简单到复杂的完全对照这是最容易把人绕晕的部分我们来逐层拆解。1最简单寄存器间接寻址ATTIntelmovl (%ebx), %eaxmov eax, [ebx]就是把ebx当成指针去读它指向的内存。2带偏移量ATTIntelmovl -8(%ebx), %eaxmov eax, [ebx - 8]ebx指向的地址再减8然后读内存。3带变址和比例因子完整形态这是数组访问的底层实现。假设你要访问a[i]其中a的基址在ebxi在ecx每个元素占4字节ATTIntelmovl 6(%ebx, %ecx, 4), %eaxmov eax, [ebx ecx*4 6]完整公式ATTdisp(%base, %index, scale)→ 地址 disp base index × scaleIntel[base index × scale disp]注意在ATT中当scale/disp出现在复杂寻址中时不需要在它们前面加$前缀。 第四节为什么考研要考这个——因为真的会考408统考大纲在“程序的机器级代码表示”章节中虽然没有明确指定必须用哪种格式但历年统考真题主要考查的是Intel格式。但是如果你只认识Intel格式万一题目给了一段ATT格式的代码让你分析比如结合Linux内核源码的题目你就直接傻眼了。两种格式的互译能力是你在考场上比别人多拿几分的关键。此外GCC默认生成的汇编代码是ATT格式的。如果你在Linux下做实验、读源码、甚至做CTF逆向题遇到ATT格式是家常便饭。 第五节真题闯关看看你能对几道 真题一格式识别题以下是一段汇编代码movl $10, %eax addl %ebx, %eax请问这是哪种汇编格式A.Intel格式B.ATT格式C.两种都是D.两种都不是【参考答案】B【解析】寄存器前有%%eax、%ebx立即数前有$$10指令用小写且有l后缀表示双字——这些都是ATT格式的典型特征。如果是Intel格式应该写成mov eax, 10 add eax, ebx 真题二格式转换题将以下Intel格式的汇编指令转换为ATT格式Intel格式mov [ebx ecx*4 8], eax【参考答案】movl %eax, 8(%ebx, %ecx, 4)【解析】转换要点操作数顺序反转目的[ebxecx*48]移到右边源eax移到左边并加%寄存器加%%eax、%ebx、%ecx内存地址用圆括号8(%ebx, %ecx, 4)指令加l后缀表示双字movl 真题三ATT代码阅读理解已知以下ATT格式的汇编代码片段movl 8(%ebp), %eax movl 12(%ebp), %edx addl %edx, %eax请写出对应的C语言代码假设%ebp指向当前函数的栈帧底部。【参考答案】int a, b; a *(int *)(ebp 8); // 第一个参数 b *(int *)(ebp 12); // 第二个参数 return a b; // addl %edx, %eax结果在%eax中更简洁地这对应一个简单的加法函数int add(int a, int b) { return a b; }【解析】在x86的函数调用约定中ebp8通常是第一个参数ebp12是第二个参数。movl 8(%ebp), %eax把第一个参数读入eaxmovl 12(%ebp), %edx把第二个参数读入edxaddl %edx, %eax执行加法结果留在eax中作为返回值。 真题四寻址方式计算题ATT格式指令movl 0x10(%ebx, %ecx, 4), %eax已知%ebx 0x1000%ecx 0x5请问该指令访问的内存地址是多少A.0x1010B.0x1020C.0x1024D.0x1014【参考答案】C【解析】ATT格式中disp(%base, %index, scale)的地址计算公式为disp base index × scale。代入数值0x10 0x1000 0x5 × 4 0x10 0x1000 0x14 0x1024所以答案是C。