当前位置首页 > Linux知识

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

阅读次数:411 次  来源:admin  发布时间:

最近在调试一些问题,发现几个命令很实用,记录一下。

也许大家都遇到过这种场景,就是有二进制代码,比如深度分析下此文件到底是什么格式的图片等,这篇文章就记录我分析下二进制可执行文件的过程,已经自己读写二进制文件的一些坑。分析的二进制执行文件为linux下的可执行文件。

二 常用二进制文件静态分析命令 2.1 file基本信息查看

Linux下有个最常用的通用命令,来分析任何文件的基本格式,那就是file,来看下:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

可以看到基本信息,比如是什么类型文件,只是概述,还有些其他选项,可以用 -h 查看。

2.2 ldd动态链接库信息

动态链接库,即没有在编译链接的时候直接打入到程序中的,而是运行时候动态加载了,从而节省内存,通过动态链接库,我们可以知道这个可执行文件用了哪些动态库,方法也比较简单。

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

这是我写的一个小程序的动态链接库信息,通过链接库分析的信息也同样比较少,用这个命令多查看依赖链接库找不到的问题。

2.3 nm符号查看

m可以列出二进制可执行文件,动态库,静态库中的符号信息,包括符号的类型,符号名称,比如函数名,全局变量等,通过这些信息可以看到不少有用的信息,通过函数名猜函数功能,使用的帮助如下:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

配合grep信息可以很方便的进行符号搜索:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

对于一些动态库,直接nm可能查不到信息,可以通过 nm -D 命令查看。

2.4 strings 查看二进制文件中的字符串

trings信息可以打印二进制文件中的字符串信息,结合grep进行搜索,用grep命令其实可以直接在二进制文件中搜索内容,但是不够直观,用strings看起来的更直观些:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

trings 会把任何可打印字符串都显示出来,比nm的内容更多,截取部分如下:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

2.5 objdump 将二进制代码转汇编指令

objdump是个值得深入学习的指令,不光可以还原汇编指令,还可以读取二进制中特定段的信息,更可怕的是,如果我们的程序是以-g -o0等调试不优化的情况下,用objdump -S指令可能尽可能地还原源代码信息(没看错,是还原出源代码信息),其实也可以理解这些信息是完整的在可执行文件中的,要不然gdb调试的时候没办法单步追踪了,测试如下:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

参数选项:

  1 --archive-headers 
  2 -a 
  3 显示档案库的成员信息,类似ls -l将lib*.a的信息列出。 
  4  
  5 -b bfdname 
  6 --target=bfdname 
  7 指定目标码格式。这不是必须的,objdump能自动识别许多格式,比如: 
  8  
  9 objdump -b oasys -m vax -h fu.o 
 10 显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys编译器生成的目标文件。objdump -i将给出这里可以指定的目标码格式列表。 
 11  
 12 -C 
 13 --demangle 
 14 将底层的符号名解码成用户级名字,除了去掉所开头的下划线之外,还使得C++函数名以可理解的方式显示出来。 
 15  
 16 --debugging 
 17 -g 
 18 显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被readelf -w支持。 
 19  
 20 -e 
 21 --debugging-tags 
 22 类似-g选项,但是生成的信息是和ctags工具相兼容的格式。 
 23  
 24 --disassemble 
 25 -d 
 26 从objfile中反汇编那些特定指令机器码的section。 
 27  
 28 -D 
 29 --disassemble-all 
 30 与 -d 类似,但反汇编所有section. 
 31  
 32 --prefix-addresses 
 33 反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。 
 34  
 35 -EB 
 36 -EL 
 37 --endian={big|little} 
 38 指定目标文件的小端。这个项将影响反汇编出来的指令。在反汇编的文件没描述小端信息的时候用。例如S-records. 
 39  
 40 -f 
 41 --file-headers 
 42 显示objfile中每个文件的整体头部摘要信息。 
 43  
 44 -h 
 45 --section-headers 
 46 --headers 
 47 显示目标文件各个section的头部摘要信息。 
 48  
 49 -H 
 50 --help 
 51 简短的帮助信息。 
 52  
 53 -i 
 54 --info 
 55 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。 
 56  
 57 -j name
 58 --section=name 
 59 仅仅显示指定名称为name的section的信息 
 60  
 61 -l
 62 --line-numbers 
 63 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。 
 64  
 65 -m machine 
 66 --architecture=machine 
 67 指定反汇编目标文件时使用的架构,当待反汇编文件本身没描述架构信息的时候(比如S-records),这个选项很有用。可以用-i选项列出这里能够指定的架构. 
 68  
 69 --reloc 
 70 -r 
 71 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。 
 72  
 73 --dynamic-reloc 
 74 -R 
 75 显示文件的动态重定位入口,仅仅对于动态目标文件意义,比如某些共享库。 
 76  
 77 -s 
 78 --full-contents 
 79 显示指定section的完整内容。默认所有的非空section都会被显示。 
 80  
 81 -S 
 82 --source 
 83 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。 
 84  
 85 --show-raw-insn 
 86 反汇编的时候,显示每条汇编指令对应的机器码,如不指定--prefix-addresses,这将是缺省选项。 
 87  
 88 --no-show-raw-insn 
 89 反汇编时,不显示汇编指令的机器码,如不指定--prefix-addresses,这将是缺省选项。 
 90  
 91 --start-address=address 
 92 从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。 
 93  
 94 --stop-address=address 
 95 显示数据直到指定地址为止,该项影响-d、-r和-s选项的输出。 
 96  
 97 -t 
 98 --syms 
 99 显示文件的符号表入口。类似于nm -s提供的信息 
100  
101 -T 
102 --dynamic-syms 
103 显示文件的动态符号表入口,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。 
104  
105 -V 
106 --version 
107 版本信息 
108  
109 --all-headers 
110 -x 
111 显示所可用的头信息,包括符号表、重定位入口。-x 等价于-a -f -h -r -t 同时指定。 
112  
113 -z 
114 --disassemble-zeroes 
115 一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。 
116  
117 @file 
118 可以将选项集中到一个文件中,然后使用这个@file选项载入。

关于符号表字段下面直接只介绍部分常用的:

 1 .text:已编译程序的机器代码。
 2 .rodata:只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。
 3 .data:已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。
 4 .bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
 5 .symtab:一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。
 6 .rel.text:当链接噐把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非使用者显式地指示链接器包含这些信息。
 7 .rel.data:被模块定义或引用的任何全局变量的信息。一般而言,任何已初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要被修改。
 8 .debug:一个调试符号表,其有些表目是程序中定义的局部变量和类型定义,有些表目是程序中定义和引用的全局变量,有些是原始的C源文件。只有以-g选项调用编译驱动程序时,才会得到这张表。
 9 .line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译驱动程序时,才会得到这张表。
10 .strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。

2.6 readelf 读取ELF文件格式

如果二进制文件是ELF格式的,通过 file 文件可以查看文件格式.使用 readelf 指令可以方便分析ELF文件的结构,比如节信息,elf头文件信息,比如我们在分析文件是否为病毒文件的时候,需要读取 elf 文件头信息,做一些特征的判断,或作为特征参与机器学习的判断。

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

readelf读取文件头

还有些其他命令,有兴趣的小伙伴,可以通过-h命令还原看下。

三 动态查看文件结构 3.1 ltrace 跟踪进程调用库函数过程

这也是一个很棒的命令,我们可以查看程序执行的时候调用库函数信息,还可以在线查看执行的进程的库函数调用情况,找几个比较典型的命令,测试的代码比较简单如下:

 1 #include <stdlib.h>
 2 #include <stdio.h>
 3 
 4 int main(void)
 5 {
 6    short shs[5] ={1,234,567,789,890};
 7    int   ins[5] ={890,88111,23333,7777,6666};
 8    FILE * fp = fopen("a.bin","wb");
 9    for (int i = 0; i < 5; i++) {
10        fwrite(&shs[i],sizeof(short),1,fp);
11        fwrite(&ins[i],sizeof(int),1,fp);
12    }
13    printf("read....\n");
14    fclose(fp);
15    fp = fopen("a.bin","rb");
16    short a;
17    int b;
18   for (int i = 0; i <5;i++) {
19      fread(&a,sizeof(short),1,fp);
20      fread(&b,sizeof(int),1,fp);
21      printf("i:%d a:%d,b:%d\n", i,a,b);
22   }
23    fclose(fp);
24   return 0;
25 }

3.1.1 ltrace 查看库函数调用情况

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

ltrace 查看库函数调用情况

3.1.2 ltrace 查看库函数调用占用时间

这在查看系统调用耗时很有用。

1 # -T 是查看调用时间开销
2 ltrace -T
3 #-t -tt -ttt 是查看调用绝对时间,t越多越精确
4 ltrace -t

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

ltrace 查看绝对时间。

3.1.3 ltrace 查看系统调用信息

1 ltrace -S

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

系统调用信息显然比库函数显示更多,追踪更复杂的情况可以使用。

还有 -p pid 追踪具体的进行id的调用情况也很有用,这里面就不举例子了。如果没有这个命令,如果是centos环境可以通过yum install -y ltrace安装。

3.2 strace

trace和ltrace的命令差不多,strace更偏向于系统调用的追踪或信号产生的情况。安装命令yum -y install strace

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

强大地方在于可以指定系统调用的类型:

 1 -e trace=set 
 2 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all. 
 3 -e trace=file 
 4 只跟踪有关文件操作的系统调用. 
 5 -e trace=process 
 6 只跟踪有关进程控制的系统调用. 
 7 -e trace=network 
 8 跟踪与网络有关的所有系统调用. 
 9 -e strace=signal 
10 跟踪所有与系统信号有关的 系统调用 
11 -e trace=ipc 
12 跟踪所有与进程通讯有关的系统调用 
13 -e abbrev=set 
14 设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all. 
15 -e raw=set 
16 将指 定的系统调用的参数以十六进制显示. 
17 -e signal=set 
18 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号. 
19 -e read=set 
20 输出从指定文件中读出 的数据.例如: 
21 -e read=3,5 
22 -e write=set 

比如以下命令:

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

还有很多有用的选项,有兴趣的可以尝试下。

四 图形化界面分析二进制执行文件

网上找到一个图形化界面分析二进制程序的,名字叫Relyze 虽然是收费的,但是可以正常用一段时间,一段时间后才提示,界面如下,强大之处在于可以显示调用关系信息等。其实原理都类似,没有比命令行更多的功能,只是看起来更方便而已。

4.1 基本文件信息

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

可执行文件基本信息

4.2 头和段信息查看

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

头和段信息

4.3 搜索

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

字符信息搜索

4.4 调用关系图

双击可以看到调用关系图信息,便于做进一步分析。

Linux下二进制可执行文件分析(nm,readelf,objdump命令使用)

其他的也没啥特殊点了,有兴趣的朋友可以下载试试。

参考文章:

https://blog.csdn.net/mseaspring/article/details/110359312

objdump 命令解析:

https://blog.csdn.net/q2519008/article/details/82349869

简书--使用 readelf 和 objdump 解析目标文件:

https://www.jianshu.com/p/863b279c941e

上一篇:《Apachekafka实战》读书笔记-管理Kafka集群安全之ACL篇
下一篇:playbook实现nginx安装