0%

objdump反汇编对于小白的一个坑

作为一名小白,在WSL中做一个操作系统小实验的时候,需要查看编译后代码的汇编实现,于是使用objdump工具直接反汇编。发现跟以前学过的汇编有些不一样(学过8086汇编),上网查询Intel x86汇编的格式(因为我的机器是Intel的CPU),发现得到的汇编格式于Intel标准的汇编格式有些不一样,如下(部分):

1
2
3
4
5
6
7
8
9
0000000000001203 <do_sum>:
1203: f3 0f 1e fa endbr64
1207: 48 8b 15 0a 2e 00 00 mov 0x2e0a(%rip),%rdx # 4018 <sum>
120e: b8 00 e1 f5 05 mov $0x5f5e100,%eax
1213: 83 e8 01 sub $0x1,%eax
1216: 75 fb jne 1213 <do_sum+0x10>
1218: 48 8d 82 00 e1 f5 05 lea 0x5f5e100(%rdx),%rax
121f: 48 89 05 f2 2d 00 00 mov %rax,0x2df2(%rip) # 4018 <sum>
1226: c3 retq

主要是对于第一个操作数和第二个操作数顺序的疑惑,以一个mov命令为例:mov A, B

这里的A、B只是表示一个操作数的符号,用于代替一个寄存器or一个地址

在Intel标准中,mov的第一个操作数A是目的操作数,第二个操作数B是源操作数,即这条汇编的含义是,把B的值赋值给A。

如果按照这种标准,我上面反汇编得到的第二条mov指令(mov $0x5f5e100,%eax)就是把eax寄存器的值赋值给$0x5f5e100,但是这个16进制的值是我定义的一个循环次数n(100,000,000,也就是一亿),于是感觉有问题。

经过大佬的指点,在Windows中使用IDA软件对这个程序进行反汇编,得到如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:0000000000001203                 public do_sum
.text:0000000000001203 do_sum proc near ; DATA XREF: main+8↓o
.text:0000000000001203 ; main+14↓o
.text:0000000000001203 ; __unwind {
.text:0000000000001203 endbr64
.text:0000000000001207 mov rdx, cs:sum
.text:000000000000120E mov eax, 5F5E100h
.text:0000000000001213
.text:0000000000001213 loc_1213: ; CODE XREF: do_sum+13↓j
.text:0000000000001213 sub eax, 1
.text:0000000000001216 jnz short loc_1213
.text:0000000000001218 lea rax, [rdx+5F5E100h]
.text:000000000000121F mov cs:sum, rax
.text:0000000000001226 retn
.text:0000000000001226 ; } // starts at 1203
.text:0000000000001226 do_sum endp

可以很明显的观察到,与objdump的反汇编结果的样子有较大区别,对比第二条mov指令,发现两个操作数的顺序反了过来,对头!(这熟悉的味道,这才是Intel的汇编嘛)这种的汇编按照Intel的解释也很合理(将循环次数赋值给eax,然后不断减一判断循环)。

于是发现问题所在,可以确定objdump所得到的汇编肯定不是Intel格式的,经过进一步的搜索,发现objdump默认的汇编格式是AT&T。并且知道了objdump可以通过-M参数来修改反汇编的格式(具体请man objdump),于是使用objudmp -d test -M intel | less(其中test是我需要反汇编的文件名)命令得到如下汇编结果(部分):

1
2
3
4
5
6
7
8
9
0000000000001203 <do_sum>:
1203: f3 0f 1e fa endbr64
1207: 48 8b 15 0a 2e 00 00 mov rdx,QWORD PTR [rip+0x2e0a] # 4018 <sum>
120e: b8 00 e1 f5 05 mov eax,0x5f5e100
1213: 83 e8 01 sub eax,0x1
1216: 75 fb jne 1213 <do_sum+0x10>
1218: 48 8d 82 00 e1 f5 05 lea rax,[rdx+0x5f5e100]
121f: 48 89 05 f2 2d 00 00 mov QWORD PTR [rip+0x2df2],rax # 4018 <sum>
1226: c3 ret

基本与IDA的结果一致(差异可忽略)。

总结一下本次经历学到的一些知识:

x86架构汇编指令一般有两种格式Intel汇编和AT&T汇编,DOS、Windows使用Intel汇编,而Unix、Linux、MacOS使用AT&T汇编。

下面简单列出几个Intel和AT&T汇编格式的区别:

  1. 第一当然是两个操作数的顺序啦:Intel的第一个操作数是目标操作数,第二个操作数是源操作数;AT&T的第一个操作数是源操作数,第二个操作数是目标操作数。
  2. 寄存器的表示:Intel的寄存器直接写寄存器的名字就行(eax);AT&T的寄存器需要在前面加一个百分号%修饰(%eax)。
  3. 立即数表示:Intel的立即数前不用加任何标志(1);AT&T的立即数前需要加$符号修饰($1)。
  4. 括号的使用:Intel中寻址时用的括号是中括号[];AT&T中使用的是小括号()

  5. ……