反汇编工具objdump的使用以及反汇编文件的解读
什么是反汇编为什么要反汇编反汇编文件的生成和解读
什么是反汇编
反汇编顾名思义就是汇编的逆过程,将二进制文件反汇编成汇编代码。arm-linux-objdump是交叉编译工具链里的一个工具,专门用来反汇编的,将二进制代码反汇编成汇编代码来查看。
为什么要反汇编
1.逆向破解。将可执行程序反汇编得到汇编代码,再根据汇编代码推理出整个程序的逻辑。这个不是一般人能做的,能看懂大量汇编语言写的程序都很困难了,更别说反推别人的代码逻辑。
2.调试程序时可以帮助我们理解并检测生成的可执行程序是否正常,尤其是在理解链接脚本和链接地址等概念时。
3.C语言的源代码编译链接生成的可执行文件再反汇编,可以帮助我们理解C语言和汇编语言的对应关系,有助于深入理解C语言。
反汇编文件的生成和解读
反汇编文件的生成:
led.bin: start.o
arm-linux-ld -Ttext 0x0 -o led.elf $^
arm-linux-objcopy -O binary led.elf led.bin
arm-linux-objdump -D led.elf > led_elf.dis
gcc mkv210_image.c -o mkx210
./mkx210 led.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
上面是一个简单的Makefile,功能是把源文件.S和.c先编译成.o文件,再把.o文件链接成.elf的可执行文件。arm-linux-objdump -D led.elf > led_elf.dis是将led.elf反汇编成ed_elf.dis。
源文件:star.s是一个汇编文件
//.globl 表明后面的变量有全局属性,对应于C语言的全局变量
.globl _start
_start:
设置GPJ0CON的bit[0:15],配置GPJ0_0/1/2/3引脚为输出功能
// 设置GPJ0CON的bit[12:23],配置GPJ0_3/4/5引脚为输出功能
ldr r1, =0xE0200240
ldr r0, =0x00111000
str r0, [r1]
mov r2, #0x1000
//设置GPD0_1为输出模式
ldr r1, =0xE02000A0
ldr r0, =0x00000010
str r0, [r1]
led_blink:
设置GPJ2DAT的bit[0:3],使GPJ2_0/1/2/3引脚输出低电平,LED亮
// 设置GPJ0DAT的bit[3:5],使GPJ0_3/4/5引脚输出低电平,LED亮
ldr r1, =0xE0200244
mov r0, #0
str r0, [r1]
ldr r1, =0xE02000A4
mov r0, #0
str r0, [r1]
// 延时
bl delay
设置GPJ2DAT的bit[0:3],使GPJ2_0/1/2/3引脚输出高电平,LED灭
// 设置GPJ0DAT的bit[3:5],使GPJ0_3/4/5引脚输出高电平,LED灭
ldr r1, =0xE0200244
mov r0, #0x38
str r0, [r1]
ldr r1, =0xE02000A4
mov r0, #0x2
str r0, [r1]
// 延时
bl delay
sub r2, r2, #1
cmp r2,#0
bne led_blink
halt:
b halt
delay:
mov r0, #0x900000
delay_loop:
cmp r0, #0
sub r0, r0, #1
bne delay_loop
mov pc, lr
star.s是一个学习S5PV210开发板时点亮LED的汇编程序,由开始、点亮、延时和死循环组成,在这里并不关注具体实现的功能,重点是和反汇编生成的文件进行对照。
得到的反汇编文件:led_elf.dis
led.elf: file format elf32-littlearm
Disassembly of section .text:
//第一列 第二列 第三列
00000000 <_start>:
0: e59f1070 ldr r1, [pc, #112] ; 78 <delay_loop+0x10>
4: e59f0070 ldr r0, [pc, #112] ; 7c <delay_loop+0x14>
8: e5810000 str r0, [r1]
c: e3a02a01 mov r2, #4096 ; 0x1000
10: e59f1068 ldr r1, [pc, #104] ; 80 <delay_loop+0x18>
14: e3a00010 mov r0, #16
18: e5810000 str r0, [r1]
0000001c <led_blink>:
1c: e59f1060 ldr r1, [pc, #96] ; 84 <delay_loop+0x1c>
20: e3a00000 mov r0, #0
24: e5810000 str r0, [r1]
28: e59f1058 ldr r1, [pc, #88] ; 88 <delay_loop+0x20>
2c: e3a00000 mov r0, #0
30: e5810000 str r0, [r1]
34: eb00000a bl 64 <delay>
38: e59f1044 ldr r1, [pc, #68] ; 84 <delay_loop+0x1c>
3c: e3a00038 mov r0, #56 ; 0x38
40: e5810000 str r0, [r1]
44: e59f103c ldr r1, [pc, #60] ; 88 <delay_loop+0x20>
48: e3a00002 mov r0, #2
4c: e5810000 str r0, [r1]
50: eb000003 bl 64 <delay>
54: e2422001 sub r2, r2, #1
58: e3520000 cmp r2, #0
5c: 1affffee bne 1c <led_blink>
00000060 <halt>:
60: eafffffe b 60 <halt>
00000064 <delay>:
64: e3a00609 mov r0, #9437184 ; 0x900000
00000068 <delay_loop>:
68: e3500000 cmp r0, #0
6c: e2400001 sub r0, r0, #1
70: 1afffffc bne 68 <delay_loop>
74: e1a0f00e mov pc, lr
78: e0200240 eor r0, r0, r0, asr #4
7c: 00111000 andseq r1, r1, r0
80: e02000a0 eor r0, r0, r0, lsr #1
84: e0200244 eor r0, r0, r4, asr #4
88: e02000a4 eor r0, r0, r4, lsr #1
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
0: 00001a41 andeq r1, r0, r1, asr #20
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 00000010 andeq r0, r0, r0, lsl r0
10: 45543505 ldrbmi r3, [r4, #-1285] ; 0x505
14: 08040600 stmdaeq r4, {r9, sl}
18: Address 0x00000018 is out of bounds.
解析:
1.第一行:led.elf: file format elf32-littlearm。表明此汇编程序是由led.elf生成,程序是32的小端模式。
2.00000000 <_start>:前面的00000000是标号的地址,<_start>是标号,对应start.s的_start标号。其实标号就相当于C语言中的函数名,在C语言中也可以用函数名代表函数的首地址,在这里可以得到印证。反汇编的标号就是由汇编文件得来的,这样可以方便我们找到反汇编文件和汇编文件对应的部分。
3.整个反汇编文件分为三列,分别对应:指令地址、指令机器码、指令机器码反汇编到的指令。
反汇编文件led_elf.dis解读:
//汇编文件
_start:
设置GPJ0CON的bit[0:15],配置GPJ0_0/1/2/3引脚为输出功能
// 设置GPJ0CON的bit[12:23],配置GPJ0_3/4/5引脚为输出功能
ldr r1, =0xE0200240
ldr r0, =0x00111000
str r0, [r1]
mov r2, #0x1000
//对应的反汇编文件部分
00000000 <_start>:
0: e59f1070 ldr r1, [pc, #112] ; 78 <delay_loop+0x10>
4: e59f0070 ldr r0, [pc, #112] ; 7c <delay_loop+0x14>
8: e5810000 str r0, [r1]
c: e3a02a01 mov r2, #4096 ; 0x1000
......
70: 1afffffc bne 68 <delay_loop>
74: e1a0f00e mov pc, lr
78: e0200240 eor r0, r0, r0, asr #4
7c: 00111000 andseq r1, r1, r0
80: e02000a0 eor r0, r0, r0, lsr #1
84: e0200244 eor r0, r0, r4, asr #4
88: e02000a4 eor r0, r0, r4, lsr #1
我们在这里对汇编文件的前几句进行解读:
1.ldr r1, [pc, #112]:此句对应于汇编文件的ldr r1, =0xE0200240。功能是将0xE0200240存到r1寄存器中。[pc, #112]代表pc+70地址处的数据(#112是十进制),此时PC指向的是当前地址的下两级,就是pc = 0 + 8,于是pc + 70 = 78。78地址处存放的数据就是e0200240,刚好等于汇编语句要加载的数据0xE0200240。所以ldr r1, [pc, #112]和ldr r1, =0xE0200240实现的是同样的功能。
2.ldr r0, [pc, #112]对应于汇编文件的ldr r0, =0x00111000。解读的方式和上面一致,只是要注意此时PC= 4 + 8。
3. str r0, [r1]语句汇编语句和反汇编语句是一致的。
4. mov r2, #4096对应于汇编的mov r2, #0x1000,两者是相同的,十进制的4096等于十六进制的0x1000。
补充:1.PC指向当前地址的前两级是因为流水线的存在,不同型号的ARM芯片流水线的级数是不同的,但是在反汇编文件里为了统一,都是按照3级流水线处理。
2.为什么向寄存器加载数据,有的是直接加载(mov r2, #4096),有的要用相对寻址的方式加载(ldr r1, [pc, #112])?这里涉及到合法立即数和非法立即数,简单来书就是数据太大,一条语句的数据部分表达不了,于是就将要加载的数据放在某个地址处,要用到的时候就去该地址处取,此时的ldr也是伪指令。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)