您的位置:首页 > 运维架构 > Linux

linux反汇编调试

2016-11-25 18:28 225 查看
反汇编有有以下几种方法:
1.使用gcc -S test.c 或者gcc -S test.c>out.txt
2.使用gdb调试,在调试中输入disass 函数名 就可以
3.objdump -D test 一般常用1,2两种,
~~~~~C语言代码example.c

int triangle( int width, int height)

{

int arr{0,1,2,3,4};

int area;

area = width * height /2;

return (area);

}

void main()

{

triangle(5,4);

}

~~~~~gdb反汇编代码

$ gdb example

(gdb) disass main

Dump of assembler code for function main:

0x080483f6 <+0>:    push   %ebp

0x080483f7 <+1>:    mov    %esp,%ebp

0x080483f9 <+3>:    sub    $0x8,%esp

目标码格式列表

--demangle

-C 将底层的符号名解码成用户级名字,除了去掉所有开头的下划线之外,还使得C++函数名以可理解的方式显示出来。

—disassemble或者-d
反汇编可执行段

.—dissassemble-all或者-D
反汇编所有段

--debugging显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。

--prefix-addresses反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。

--disassemble-zeroes  一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编

-EB -EL  指定字节序  --endian={big|little}这个选项将影响反汇编出来的指令。little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,x86都是这种。

--file-headers或者  -f 显示objfile中每个文件的整体头部摘要信息。

--section-headers;--headers 或者-h 显示目标文件各个section的头部摘要信息。

--info或者-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。显示支持的目标文件格式和CPU架构

--section=name或者-j name 仅仅显示指定section的信息

--line-numbers或者-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之  类的调试编译选项。

--architecture=machine 或者-m machine指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述架构信息的时候(比如S-records),这个选项很有用。可以用-i选项列出这里能够指定的架构

--reloc或者-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。

--dynamic-reloc    -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。

--full-contents  -s 显示指定section的完整内容。

objdump --section=.text -s inet.o | more

--source    -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。

--show-raw-insn反汇编的时候,显示每条汇编指令对应的机器码,除非指定了--prefix-addresses,这将是缺省选项。

--no-show-raw-insn

-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,
   效果比较明显。隐含了-d参数。
-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用
  使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求
  编译时使用了-g之类的调试编译选项。
-j name 仅仅显示指定section的信息

如何使用[b]linux下objdump命令对任意一个二进制文件进行反汇编?[/b]
可以使用如下命令:
objdump -D -b binary -m i386 a.bin
-D表示对全部文件进行反汇编,-b表示二进制,-m表示指令集架构,a.bin就是我们要反汇编的二进制文件
objdump -m可以查看更多支持的指令集架构,如i386:x86-64,i8086等
另外上面的所有objdump命令的参数同样适用于arm-linux-objdump。
同时我们也可以指定big-endian或little-endian(-EB或-EL),我们可以指定从某一个位置开始反汇编等。

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它还有其他作用,下面以ELF格式可执行文件test为例详细介绍:
objdump -f test  显示test的文件头信息
objdump -d test  反汇编test中的需要执行指令的那些section
objdump -D test  与-d类似,但反汇编test中的所有section
objdump -h test  显示test的Section Header信息
objdump -x test  显示test的全部Header信息
objdump -s test  除了显示test的全部Header信息,还显示他们对应的十六进制文件代码

使用arm-linux 工具链里面的arm-linux-objdump 就能反汇编

cd到bin文件所在的目录, 在命令行下输入:

arm-linux-objdump -D -b binary -m arm xxx.bin > xxx.asm

参数:

-D 反编译所有代码

-m 主机类型, arm

-b 文件格式, binary

对于ELF格式的文件只要一个-D参数即可

就可以把xxx.bin反汇编到xxx.asm文件

Arm-linux-objdump –D elf_file > dis_file
或者

Arm-linux-objdump –D –b binary –m arm bin_file > dis_file

内存地址反向查找出问题的程序:

<1>.通过汇编去查找.
Linux 平台:
  1. 在程序信号处理部分, 加入代码捕捉引起错误点的地址,简单来说,方法就是在注册自己的信号处理函数,在这个函数中加入获取内存错误地址的代码,并把结果写到一个日志文件中。

  2. 编译 DEBUG 版本 程序 (compile 时用 -g , 生成可执行文件后不用 strip 去掉symbol 信息)

  3. 在程序出问题时, 查看日志记录, 得到错误点的地址.

  4. 用objdump -S 导出Debug 版本的汇编代码, 查找错误地址, 则得出那条语句出错.
windows 下c 语言调试
  1. release 版编译/连接选项, 把"generate debug info/" 打钩选择

  2.dumpbin /DISASM /OUT:dump.out.txt.1 prep.exe 可反编译exe文件

  3.得到程序非法地址(可从管理工具-》事件查看器里得到),与汇编比较。

对目标文件:***.o:

arm-none-Linux-gnueabi-objdump
-D  ./kernel/sched.o > sched.S


对可执行文件***.bin:

arm-linux-objdump -D -b binary -m arm xxx.bin > xxx.asm

为了运行ARM汇编代码,需要使用交叉编译器arm-linux-gcc对ARM汇编代码进行编译。下载交叉编译器安装完毕后,对ARM汇编代码进行编译。

arm-linux-gcc main.s -o main -nostdlib

编译选项“-nostdlib”表示不使用任何运行时库文件,编译生成的可执行文件main只能在ARM体系结构的系统上运行。

通常认为,产生异常的地址是lr寄存器的值,从上面的异常信息可以看到[lr]的值是c01a4e30。
接下来,我们可以通过内核镜像文件反汇编来找到这个地址。内核编译完成后,会在内核代码根目录下生成vmlinux文件,我们可以通过以下命令来反汇编:
arm-none-eabi-objdump -Dz -S vmlinux >linux.dump
值得注意的是,arm-none-eabi-objdump的参数-S表示尽可能的把原来的代码和反汇编出来的代码一起呈现出来,-S参数需要结合 arm-linux-gcc编译参数-g,才能达到反汇编时同时输出原来的代码。所以,我在linux内核代码根目录的Makefile中增加-g编译参 数:
KBUILD_CFLAGS   := -g -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \

     -fno-strict-aliasing -fno-common \

     -Werror-implicit-function-declaration \

     -Wno-format-security \

     -fno-delete-null-pointer-checks
修改Makefile后,重新编译内核,在根目录中生成的vmlinux文件就会包含了原来的代码信息,因此,该文件的大小也比原来大一倍!
最后执行“arm-none-eabi-objdump -Dz-S vmlinux >linux.dump”,由于加入了-g编译参数,执行这个反汇编命令需要很长时间(本人在虚拟机上执行,花了近6个小时!),反汇编出来的linux.dump文件也比原来的44MB增大到惊人的503MB。
(博主加:这里有一点要注意,如果是ko模块文件,反汇编时如果想看到ko文件的某函数反汇编代码,该函数不能加static关键字修饰,而且module_init修饰的入口函数,其名字即为module_init)
接下来可以用UltraEdit打开linux.dump文件,查找“c01a4e30”字符串。
最后定位到的信息是:

==================================================================================================================
/*

 * tasklet handling tty stuff outside the interrupt handler.

 */

static void atmel_tasklet_func(unsigned long data)

{

c01a4e20: e92d45f0  push {r4, r5, r6, r7, r8, sl, lr}

c01a4e24: e24dd01c  sub sp, sp, #28 ; 0x1c

c01a4e28: e1a04000  mov r4, r0

 /* The interrupt handler does not take the lock */

 spin_lock(&port->lock);
 if (atmel_use_pdc_tx(port))

  atmel_tx_pdc(port);
 else if (atmel_use_dma_tx(port))
c01a4e2c: ebfffda1  bl c01a44b8 <atmel_use_dma_tx>
c01a4e30: e3500000  cmp r0, #0 ; 0x0
c01a4e34: e5943034  ldr r3, [r4, #52]

c01a4e38: 0a00007b  beq c01a502c <atmel_tasklet_func+0x20c>
==================================================================================================================
可以看出来,异常的产生位于atmel_tasklet_func函数的 else if (atmel_use_dma_tx(port))一行
估计atmel_use_dma_tx(port)的“port”参数为空指针所致!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: