binutils工具集——objdump的用法
以下内容源于网络资源的学习与整理,如有侵权请告知删除。
一、工具简介
objdump主要用来显示目标文件的内容。
这里的显示,是指将二进制的文件内容反汇编形成汇编代码,因此“显示”约等同于“反汇编”。
使用objdump工具反汇编的原因:
(1)逆向破解。将可执行程序反汇编得到汇编代码,再根据汇编代码推理出整个程序的逻辑。这个不是一般人能做的,能看懂大量汇编语言写的程序都很困难了,更别说反推别人的代码逻辑。
(2)调试程序。反汇编调试可以帮助我们理解并检测生成的可执行程序是否正常,尤其是在理解链接脚本和链接地址等概念时。
(3)C语言的源代码编译链接生成的可执行文件再反汇编,可以帮助我们理解C语言和汇编语言的对应关系,有助于深入理解C语言。
二、选项含义
通过man手册,可知该工具的使用格式如下。
objdump [options] obj_file #[]表示可选,obj_file表示目标文件
objdump工具支持很多选项,这里仅列出一些常用的选项。
| 选项 | 描述 |
| -d --disassemble | 表示反汇编含有指令机器码的段(即需要执行指令的段)。 |
| -D --disassemble-all | 表示反汇编所有的段。 |
| -m machine --architecture=machine | 指定反汇编目标文件时使用的架构。当待反汇编文件本身没有描述架构信息时,这个选项很有用。可以用-i选项列出这里能够指定的架构。 |
| -S --source | 尽可能地反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。(即如果想要将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息) |
| -s filename | 显示文件filename的头文件信息,及所对应的十六进制文件代码 |
| -i --info | 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。 |
| -l filename(小写L) | 在反汇编代码中插入文件名和行号。 |
| -j section --section=section | 仅显示指定section的信息。 |
| -a filename | 显示文件filename的格式。 |
| -f filename | 显示文件filename的头信息。 |
| -h filename | 显示文件filename的各section的头信息(即段概述) |
| -x filename | 显示文件filename的全部头文件信息。 |
| -C filename | 将C++符号名逆向解析。 |
| -t filename | 输出文件filename的符号列表(变量名与函数名就是符号)内容。 |
三、应用之解读反汇编文件
比如在S5PV210开发板上点亮LED的工程,由一个Makefile文件、一个start.S文件组成。
1、Makefile文件
其中Makefile文件内容如下,其功能是把源文件.S和.c先编译成.o文件,再把.o文件链接成.elf格式的可执行文件。其中“arm-linux-objdump -D led.elf > led_elf.dis”表示将led.elf反汇编成ed_elf.dis。
led.bin: start.o arm-linux-ld -Ttext 0x0 -o led.elf $^ #链接:将.o文件链接成led.elf文件arm-linux-objcopy -O binary led.elf led.bin #复制与转换:将.elf格式转换为.bin格式arm-linux-objdump -D led.elf > led_elf.dis #反汇编,将led.elf文件反汇编成led_elf.dis文件gcc mkv210_image.c -o mkx210./mkx210 led.bin 210.bin%.o : %.Sarm-linux-gcc -o $@ $< -c%.o : %.carm-linux-gcc -o $@ $< -c clean:rm *.o *.elf *.bin *.dis mkx210 -f
2、源码start.S文件
而start.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, =0x00111000str r0, [r1]mov r2, #0x1000//设置GPD0_1为输出模式ldr r1, =0xE02000A0 ldr r0, =0x00000010str 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, #0str r0, [r1]ldr r1, =0xE02000A4 mov r0, #0str 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, #0x38str r0, [r1]ldr r1, =0xE02000A4 mov r0, #0x2str r0, [r1]// 延时bl delay sub r2, r2, #1cmp r2,#0bne led_blinkhalt: b haltdelay:mov r0, #0x900000
delay_loop:cmp r0, #0sub r0, r0, #1bne delay_loopmov pc, lr
3、反汇编 led_elf.dis文件内容
执行make之后,得到的反汇编文件led_elf.dis的内容如下。
(1)第一行表明此汇编程序是由led.elf反汇编生成的,程序是32的小端模式。
(2)00000000 <_start>,其中<_start>是标号,对应着start.S文件中的_start标号,而00000000是标号<_start>的地址。其实标号就相当于C语言中的函数名,在C语言中也可以用函数名代表函数的首地址,在这里可以得到印证。反汇编文件中的标号就是由汇编文件得来的,这样可以方便我们找到反汇编文件和汇编文件对应的部分。
(3)反汇编文件分为三列,分别对应:指令地址、指令机器码、指令机器码反汇编到的指令。
led.elf: file format elf32-littlearm //第一行Disassembly of section .text:
//第一列 第二列 第三列
00000000 <_start>:0: e59f1070 ldr r1, [pc, #112] ; 78 4: e59f0070 ldr r0, [pc, #112] ; 7c 8: e5810000 str r0, [r1]c: e3a02a01 mov r2, #4096 ; 0x100010: e59f1068 ldr r1, [pc, #104] ; 80 14: e3a00010 mov r0, #1618: e5810000 str r0, [r1]0000001c :1c: e59f1060 ldr r1, [pc, #96] ; 84 20: e3a00000 mov r0, #024: e5810000 str r0, [r1]28: e59f1058 ldr r1, [pc, #88] ; 88 2c: e3a00000 mov r0, #030: e5810000 str r0, [r1]34: eb00000a bl 64 38: e59f1044 ldr r1, [pc, #68] ; 84 3c: e3a00038 mov r0, #56 ; 0x3840: e5810000 str r0, [r1]44: e59f103c ldr r1, [pc, #60] ; 88 48: e3a00002 mov r0, #24c: e5810000 str r0, [r1]50: eb000003 bl 64 54: e2422001 sub r2, r2, #158: e3520000 cmp r2, #05c: 1affffee bne 1c 00000060 :60: eafffffe b 60 00000064 :64: e3a00609 mov r0, #9437184 ; 0x90000000000068 :68: e3500000 cmp r0, #06c: e2400001 sub r0, r0, #170: 1afffffc bne 68 74: e1a0f00e mov pc, lr78: e0200240 eor r0, r0, r0, asr #47c: 00111000 andseq r1, r1, r080: e02000a0 eor r0, r0, r0, lsr #184: e0200244 eor r0, r0, r4, asr #488: e02000a4 eor r0, r0, r4, lsr #1Disassembly of section .ARM.attributes:00000000 <.ARM.attributes>:0: 00001a41 andeq r1, r0, r1, asr #204: 61656100 cmnvs r5, r0, lsl #28: 01006962 tsteq r0, r2, ror #18c: 00000010 andeq r0, r0, r0, lsl r010: 45543505 ldrbmi r3, [r4, #-1285] ; 0x50514: 08040600 stmdaeq r4, {r9, sl}18: Address 0x00000018 is out of bounds.
4、反汇编 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, =0x00111000str r0, [r1]mov r2, #0x1000//对应的反汇编文件部分
00000000 <_start>:0: e59f1070 ldr r1, [pc, #112] ; 78 4: e59f0070 ldr r0, [pc, #112] ; 7c 8: e5810000 str r0, [r1]c: e3a02a01 mov r2, #4096 ; 0x1000......70: 1afffffc bne 68 74: e1a0f00e mov pc, lr78: e0200240 eor r0, r0, r0, asr #47c: 00111000 andseq r1, r1, r080: e02000a0 eor r0, r0, r0, lsr #184: e0200244 eor r0, r0, r4, asr #488: e02000a4 eor r0, r0, r4, lsr #1
(1)ldr r1, [pc, #112]
此句对应于汇编文件的ldr r1, =0xE0200240,功能是将0xE0200240存到r1寄存器中。
[pc, #112]表示pc+70地址处的数据(#112是十进制,这里的70是十六进制),此时PC指向的是当前地址的下两级,就是pc = 0 + 8,于是pc + 70 = 78。78地址处存放的数据就是e0200240,刚好等于汇编语句要加载的数据0xE0200240。所以ldr r1, [pc, #112]和ldr r1, =0xE0200240实现的是同样的功能。
注意,PC指向当前地址的下两级是因为流水线的存在,不同型号的ARM芯片流水线的级数是不同的,但是在反汇编文件里为了统一,都是按照3级流水线处理。
(2)ldr r0, [pc, #112]
对应于汇编文件的ldr r0, =0x00111000,解读方式和上面一致。第一条语句是PC=0+8,现在这是第二条语句了,因此PC= 4 + 8。
(3)str r0, [r1]
与汇编语句是一致的。
(4)mov r2, #4096
对应于汇编的mov r2, #0x1000,两者是相同的,十进制的4096等于十六进制的0x1000。
为什么向寄存器加载数据,有的是直接加载(mov r2, #4096),有的要用相对寻址的方式加载(ldr r1, [pc, #112])?这里涉及到合法立即数和非法立即数,简单来说就是数据太大,一条语句的数据部分表达不了,于是就将要加载的数据放在某个地址处,要用到的时候就去该地址处取,此时的ldr也是伪指令。
四、应用之查看符号列表
见ELF格式文件由哪些段组成?_天糊土的博客-CSDN博客。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
