Linux 可执行文件解析工具

2019-06-08 linux language c/cpp

主要是在如何解析 ELF 格式的文件。

objdump

-h, --section-headers, --headers
  查看目标文件的头部信息。
-x, --all-headers
  显示所有的头部信息,包括了符号表和重定位表,等价于 -a -f -h -p -r -t 。
-s, --full-contents
  显示所请求段的全部信息,通常用十六进制表示,默认只会显示非空段。
-d, --disassemble
  反汇编,一般只反汇编含有指令的段。
-t, --syms
  显示符号表,与nm类似,只是显示的格式不同,当然显示与文件的格式相关,对于ELF如下所示。
  00000000 l    d  .bss   00000000 .bss
  00000000 g       .text  00000000 fred

readelf

用于读取 ELF 格式文件,包括可执行程序和动态库,常用参数如下。

-a --all
  显示所有信息,等价于-h -l -S -s -r -d -V -A -I
-h --file-header
  文件头信息;
-l --program-headers
  程序的头部信息;
-S --section-headers
  各个段的头部信息;
-s --syms
  显示符号表,也就是.symtab段;
-e --headers
  全部头信息,等价于-h -l -S;
-x, --hex-dump=<number or name>
  十六进制方式打印某个段;

示例用法:

----- 读取dynstr段,包含了很多需要加载的符号,每个动态库后跟着需要加载函数
$ readelf -p .dynstr hello

----- 以十六进制方式读取dynstr段
$ readelf -x .dynstr hello

----- 查看是否含有调试信息
$ readelf -S hello | grep debug

objcopy

用于转换目标文件。

-S / --strip-all
  不从源文件中拷贝重定位信息和符号信息到输出文件(目的文件)中去。
-I bfdname/--input-target=bfdname
  明确告诉程序源文件的格式是什么,bfdname是BFD库中描述的标准格式名。
-O bfdname/--output-target=bfdname
  使用指定的格式来写输出文件(即目标文件),bfdname是BFD库中描述的标准格式名,
  如binary(raw binary 格式)、srec(s-record 文件)。
-R sectionname/--remove-section=sectionname
  从输出文件中删掉所有名为section-name的段。

上一步的 strip 命令只能拿掉一般 symbol table,有些信息还是沒拿掉,而这些信息对于程序的最终执行没有影响,如: .comment .note.ABI-tag .gnu.version 就是完全可以去掉的。

所以说程序还有简化的余地,我们可以使用 objcopy 命令把它们抽取掉。

$ objcopy -R .comment -R .note.ABI-tag -R .gnu.version hello hello1

nm

用来显示指定文件中的符号信息,可以是对象文件、可执行文件、动态库等。

符号

第二列标示了符号的类型,大写表示为全局变量,小写则表示为局部的变量。

  • I 对另一个符号的间接引用,一般为动态库。
  • T 位于代码区。

当出现了 I 指定的符号时,另外比较常见的是通过 @ 指定版本号,例如 memcpy@@GLIBC_2.14 或者 memcpy@GLIBC_2.2.5