【Java精品源码栏目提醒】:网学会员为广大网友收集整理了,二进制代码的阅读20130406 - 其它资料,希望对大家有所帮助!
Linux 下二进制代码的阅读大多数时候,我们研究的是如何阅读源代码。
但在一些情况下,比如源代码不公开或得到源代码的代价很高的情况下,我们又不得不需要了解程序的行为,这 时阅读二进制文件就非常重要。
假设现在有一个二进制可执行文件,我们木有源代码,但要了解它的实现,这里仅简单列出一些常用的工具。
阅读方式可分为两个方面:静态阅读和动态阅读。
静态阅读首先,file 命令可以查看可执行文件的大体信息。
比如是哪种格式的,哪个体系结构的,有没有调试信息等。
这些决定了需要用哪个版本的工具进行进一 步查看。
比如如果是 arm 体系的可执行文件就可用 arm toolchain 里的工具,如 arm-eabi-objdump,arm-eabi-gdb等。
众所周知,GNU Binutils 提供了一系列解析和操作二进制文件的工具,最常用的如 objdump,其最主要的功能之一是反汇编:objdump -d libxxx.so其它常用选项包括:-x 打印所有头信息-s 打印所有段的内容-S 将反汇编和源代码一起打印-r 打印重定位表-R 打印动态重定位表-D 打印所有段,即不止代码段-t 打印符号表,和 nm 的功能类似,但格式不一样-T 只打印动态链接的符号表项等等。
readelf 命令主要用于查看 elf 文件的元信息(如段信息等)。
如查看可执行文件的头信息:readelf -h libxxx.so其它常用选项包括:-s 打印符号表-r 打印重定位表-l 打印装载属性-S 打印段表等等。
除了以上两个用得最多的,其它一些辅助工具有时也必不可少:nm 打印符号表cfilt 把 c符号 unmanglestrip 把调试信息删除strings 看文件中的字符串ldd: 查看依赖关系等等动态阅读要了解可程序的行为,最可靠的还是看二进制文件跑起来时的样子。
很多时候,由于运行环境复杂,特别是多线程情况下,动态阅读能发现很多静态阅读所不可能发现的东西。
首先,在/proc/pid(pid 为进程号)下记录了二进制文件跑起来后的很多信息,如通过/proc/pid/maps 可以知道该程序链接了哪些库,分别被加载在了什么地方(这样,知道了错误地址,理论上就可以找到相应库的对应反汇编代码。
),/proc/pid/mem 是进程所用的内存镜像, /proc/pid/stat 则记录了中断统计信息等。
/proc/pid/cmdline 则记录了启动程序的命令行。
其次,通过 trace(strace,lstrace)可以看程序有哪些系统调用。
strace 用于跟踪系统调用,ltrace 用于跟踪动态库调用。
如对于在/proc/pid/maps 中看到的一些匿名内存映射,想要看它们是在什么时候或是在哪被创建可以用:strace -f -p 3742 -e tracemmap2openmprotect tee tmp.trace其中 3742 为进程 ID。
这里只过滤出关于内存映射的函数。
最后,指令级动态阅读的神器还属 gdb。
在 gdb 中查看寄存器信息可用:gdb i r在调试过程中打印出反汇编代码可用gdb disass addr或者gdb x/10i addr然后就可以和 objdump 出来的反汇编结合着看了。
如 GOT 表之类的信息在静态阅读时是无意义的,只有这时才能看。
各种 break point 能让我们迅速定位到我们关注的地方:watch point 在指定数据被访问时程序停止。
break point 在指定代码被执行时程序停止。
因为很多时候库不带 symbol,所以得直接设在地址上 break addr。
catch point 在指定事件发生时程序停止,如进程创建,动态链接库加载及异常。
条件断点有时很有用:break ... if ltconditiongt,但这玩意一旦设上不是一般的慢啊。
。
。
gdb 中的 bt 和 info frame 命令常用来看堆栈信息和函数调用关系。
但有时因为 bug 栈被写坏了,但如果不是被写得很坏,查看栈的内容常通常能找到一些线索:gdb x/40x sp该命令是查看sp 指向区域的 40 个 byte 的内存内容。
该命令也可用来查看任意指定地址的内存内容。
关于 gdb 常用的功能:http://wiki.ubuntu.org.cn/E794A8GDBE8B083E8AF95E7A88BE5BA8F其它的一些 gdb 使用备忘:http://blog.csdn.net/ariesjzj/article/details/6913671一些相关资料http://www.linuxforums.org/articles/understanding-elf-using-readelf-and-objdump_125.htmlhttp://bbs.chinaunix.net/thread-1972423-1-1.htmlelfutils-0.148: http://www.linux.it/rubini/docs/binfmt/binfmt.htmlGNU binutils amp libbfd: http://www.gnu.org/software/binutils/参考:http://blog.csdn.net/ariesjzj/article/details/7689120Binutils 是 GNU 工具之一,主要是二进制代码的处理维护工-文章出自LCD 论坛Binutils 是 GNU 工具之一,主要是二进制代码的处理维护工具。
其工具部件简介如下 add2line:将地址转换成文件名或行号对,以便调试程序。
ar:从体系文件中创建、修改、扩展程序代码。
as:生成汇编程序代码。
cfilt:建立低级语言和用户级语言的名称符号联接,并保持它们的相互关系。
gasp:汇编宏处理器。
ld:目标代码联接,联接各目标代码块,它是生成可执行代码的最终步骤。
nm:从目标代码文件中枚举所有调试符号名。
objcopy:使用 GNU BSD 库,把目标代码从一文件格试拷贝成另一种格试。
objdump:显示目标文件信息。
readelf:显示 elf 文件信息。
ranlib:生成索引以加快对归档文件的访问。
size:列出目标模块或文件的代码尺寸。
strings:打印可打印的目标代码字符(至少 4 个字符),打印字符多少可以控制。
对于其它格试的文件,打印字符串。
strip:放弃所有符号联接。
GNU 开发工具为了有效地进行嵌入式开发,至少需要了解和掌握如下几类工具:编译开发工具:即能够把一个源程序编译生成一个可执行程序的软件,如 gcc 等。
调试工具:即能够对执行程序进行
源码或汇编级调试的软件,如 gdb 等。
软件工程工具:用于协助多人开发或大型软件项目的管理的软件,如 make、cvs 等。
具体来说,我们需要对如下软件有一定了解:(1)GCC很多人把 GCC 看成只是一个 C 编译器,其实 GCC 是 GNU Compiler Collection 的简称,目前,GCC 可以支持 C、C、ADA、Object C、
JAVA、Fortran、PASCAL等多种高级语言。
GCC 主要包括如下一些工具。
cpp,GNU 预处理器gcc,符合 ISO 标准的 C 编译器g,符合 ISO 标准的 C编译器gcj,gcj 是 GCC 的
java 前端,可以生成执行速度更快的二进制本地执行码,而不是
java byte code。
gcj 为把
java 程序编译成机器代码提供了试验性的支持。
要做到这点,用户还需要安装相关的
java 运行时库。
gnat,是 GCC 的 GNU ADA 95 前端,该软件包括开发工具、文档及 ADA 95 编译器。
(2)binutilsbinutils 是一组二进制工具程序集,它包括 addr2line、ar、as、gprof、ld、nm、objcopy、objdump、ranlib、size、strings、strip 等工具,是辅助 GCC的主要软件。
as,GNU 汇编器(Assembler),用于把汇编代码转换成二进制代码,并存放到一个 object 文件中。
ld,GNU 链接器(Linker),主要用于确定相对地址,把多个 object 文件、起始代码段、库等链接起来,并最终形成一个可执行文件。
addr2line,把执行程序中的地址映射到源文件中的对应行。
ar,创建归档文件(Archive)、修改/替换库中的 object 文件,向库中添加/提取 object 文件。
cfilt,解码 C符号名。
nm,列出 object 文件中的符号名。
objcopy,复制和转换 object 文件。
objdump,用来显示对象文件的信息。
ranlib,根据归档文件(Archive)中内容建立索引。
readelf,显示 elf 格式执行文件中的各种信息。
size,显示 object 文件和执行文件各节(Section)的大小。
strings,显示可执行文件中字符串常量。
strip,去掉执行文件中多余的信息(如调试信息),可以减少执行文件的大小。
gprof,用来显示调用图表档案数据。
(3)gdbgdb 是 GNU 调试器,它允许调试用 C、C或其它语言编写的程序。
它的基本运行方式是在一个 shell 环境下用命令行方式调试程序和显示数据。
如果加上一些图形前端(如 DDD 等软件),则可以在此一个更方便的图形环境下调试程序。
(4)makeGNU make 是一个用来控制可执行程序的生成过程,从其它的
源码程序文件中生成可执行程序的 GNU 工具。
GNU make 允许用户生成和安装软件包,而无需了解生成、安装软件包的具体执行过程。
(5)diff/diff3/sdiffdiff/diff3/sdiff 是比较文本差异的工具,也可以用来产生补丁。
(6)patchpatch 是补丁安装程序,可根据 diff 生成的补丁来更新程序。
(7)CVS(Concurrent Version System)CVS 是一个版本控制系统。
它能够记录文件的修改历史(通常但并不总是包括
源码)。
CVS 只存储版本之间的区别,而不是你创建的文件的每一个版本。
CVS 还保留一个记录改变者、改变时间以及改变原因的日志。
CVS 对于管理发行版本和控制在多个作者间同时编辑
源码文件很有帮助。
CVS 为一个层次化的目录提供版本控制,目录由修改控制的文件组成,而不是在一个目录中为一组文件提供版本控制。
这些目录和文件可以被合并起来构成一个软件发行版本。
binutils 是一组二进制工具程序集,它包括 addr2line、ar、as、gprof、ld、nm、objcopy、objdump、ranlib、size、strings、strip 等工具,下面分别介绍其中一些常用的软件。
nm 工具:nm 软件主要功能是列出目标文件中的符号,这样可以帮助程序员定位和分析执行程序和目标文件中的符号信息和它的属性。
如果没有目标文件作为参数被列出,nm 假定目标文件为”a.out”,通过下面的命令可以得到 nm 的一般帮助信息nm -h。
对于每一个符号,nm 将显示下面的内容:符号的值:用某种基数表示的数值。
默认情况下用十六进制表示。
符号的类型:至少要用到下面的类型。
也可以用其它类型,这依赖于目标文件的格式。
1. A:符号的值是绝对值,并且不会被将来的链接所改变。
2. B:符号位于未初始化数据部分(被认为是 BSS)。
3. C:符号是公共的。
公共符号是未初始化的数据。
在链接时,多个公共符号可能以相同的名字出现。
如果符号在其他地方被定义,该符号会被当作未定义的引用来处理。
4. D:符号位于已初始化数据部分。
5. G:符号位于小型对象的已初始化数据部分,相对于大型的全局 array,一些对象文件格式允许对小型数据对象更有效的存取,例如全局的 int 型变量。
6. I:符号是另一个符号的间接引用。
这是对很少用到的 a.out 目标文件格式的 GNU 扩展。
7. N:符号是调试符号。
8. R:符号位于只读数据部分。
9. S:符号位于小型对象的未初始化数据部分。
10. T:符号位于文本(代码)部分。
11. U:符号未被定义。
12. W:符号是弱定义符号(Weak Symbol),也称弱符号。
当一个弱定义符号和一个已经定义的普通符号链接时,使用该已定义的普通符号不会引起错误。
当一个未定义的弱符号被链接且该符号未被定义时,该 weak 符号的值被无错误的变为 0。
13. -:符号是一个 a.out 目标文件中的 stabs 符号。
这种情况下,接下来要打印的值是 stabs other 域,stabs desc 域,以及 stab 类型。
stabs 符号用于保留调试信息。
14. :符号类型是未知的,或目标文件格式特殊。
15. o:符号名nm 的命令行参数选项的长格式和短格式是等价的,下面列出了可供选择的命令行参数格式。
1. -A/-o/--print-file-name: 在找到的各个符号的名字前加上输入文件名(或归档文件元素),而不是在此文件中的所有符号前只出现一次输入文件名。
2. -a/--debug-syms:显示所有的调试符号,即使是仅用于调试的符号。
通常情况下这些符号是不被列出的。
3. -B:等价于“--formatbsd”(为了兼容 MIPS nm)。
4. -C/--demangle:将低级符号名解码成用户级名字。
另外去除任何由系统预先生成的初始的下划线,这样可以使 C函数名具有可读性。
5. --no-demangle:不解码低级符号名。
这是默认的处理方式。
6. -D/--dynamic:显示动态符号而不是普通符号。
该选项仅对动态目标文件有意义,例如特定类型的共享库。
7. -f format/--format format:使用 format 格式输出,format 可以是 bsd、sysv 或 posix。
默认为 bsd。
仅当 format 的第一个字符是有意义的,可以是大写或小写。
8. -g/--extern-only:只显示外部符号。
9. -l/--line-numbers:对每个符号,使用调试信息去试图找到文件名和行号。
对于已定义的符号,查找符号地址的等号。
对于未定义符号,查找指向符号重定位入口的行号。
如果可以找到行号信息,在其他符号信息之后显示行号信息。
10. -n/-v/--numeric-sort:按符号对应地址的顺序排序,而不是按符号名的字符顺序。
11. -p/--no-sort:不以任何顺序对符号进行排序,按目标文件中遇到的符号顺序显示。
12. -P/--portablility:使用 POSIX.2 标准输出格式代替默认的输出格式。
等价于“-f posix”。
13. -s/--print-armap:当列出归档文件中成员的符号时,包含索引,即名字和包含该名字定义的模块的映射(通过 ar 或 ranlib 保存在归档文件中)。
14. -r/--reverse-sort:反转排序的顺序(按数字或字母顺序)显示。
15. --size-sort:按大小排列符号顺序。
该大小是按照一个符号的值与它下一个符号的值进行计算的。
16. -t radix/--radix radix:使用 radix 进制显示符号值。
radix 只能为“d”表示十进制、“o”表示八进制或“x”表示十六进制。
17. --target bfdname:指定一个目标代码的格式,而非使用系统默认格式。
18. -u/--undefined-only:仅显示没有定义的符号(那些每个目标文件的外部符号)。
19. -defined-only:仅显示每个目标文件中已经定义的符号。
20. -V/--version:显示 nm 的版本号然后退出。
21. --help:显示 nm 的所有选项然后退出。
下面介绍一个简单的使用,按照我们刚才介绍 ar 命令时的例子,执行如下命令:nm test.o输出结果如下:U Add00000000 T mainU MinusU printf执行命令:nm add.o输出结果如下:00000000 T Add执行命令:nm minus.o输出结果如下:00000000 T Minus命令 nm test.o 的输出说明了 test.o 定义了 main 函数,但没有定义 Add、Minus 和 printf 函数符号。
命令 nm add.o 的输出说明 add.o 定义了 Add 函数符号。
命令 nm minus.o 的输出说明了 minus.o 定义了 Minus 函数符号。
test.o 中没有定义但使用了 printf 函数符号,printf 函数实际上定义在 libc.a 库中。
objdump 工具:objdump 显示一个或多个目标文件的信息。
由其选项来控制显示哪些特定的信息。
这些信息只对那些编写编译工具的程序员有帮助,而对那些只想让自己编写 。
, 。
的程序编译和运行起来的程序员来说没有更多意义 但在嵌入式系统级开发时 通过这个软件可以方便地查看执行文件或库文件的信息 如我们可以通过 objdump软件反汇编执行程序,看到执行程序的汇编格式。
当目标文件是归档文件时,objdump 显示的是归档文件中每个成员文件的信息。
选项的长格式和短格式是等价的。
下面描述了作为可选择的参数格式(除“-l”之外,至少要给出一个参数选项)。
1. -a/--archive-header:如果任何一个由 object-file 指定的文件是归档文件,则显示该归档文件的头信息(类似于“ls -l”的格式)。
除了“ar -tv”能显示的信息外,“objdump -a”还可以显示每个归档文件成员的目标文件格式。
2. --adjust-vmaoffset:当转储信息时,首先给所有的节地址加上一个偏移量 offset。
如果节地址对应不上符号表时,就可以使用该选项。
当节被放在特殊的地址,而采用的是一种不能表示节地址的格式,例如 a.out,就会发生节地址对应不上符号表的情况。
3. -b bfdname/--target bfdname:指定目标文件的目标代码格式为 bfdname。
该选项可能不是必需的,因为 objdump 能够识别很多格式。
如命令“objdump-b oasys -m vax -h fu.o”执行后,将显示节头中的概要信息。
“-b oasys”表示用的是 Oasys 编译器产生的目标文件格式。
“-m vax”表示目标文件是 VAX计算机上的目标文件。
4. -C/--demangle:将低级符号名解码成用户级名字。
另外去除任何由系统预先生成的初始的下划线,这样可以使得 C函数名具有可读性。
5. --debugging:显示调试信息。
该选项试图解析保存在文件中的调试信息并且用 C 语言风格的语法打印这些信息。
仅能对特定类型的调试信息实现这个功能。
6. -d/--disassemble:显示目标文件中的机器指令使用的汇编语言。
该选项仅仅反汇编那些应该含有指令机器码的节。
7. -D/--disassemble-all:类似于“-d”,但是反汇编所有节的内容。
该选项仅对那些应该含有指令机器码的节有意义。
8. --prefix-addresses:反汇编时,打印每行的完整地址。
这是一种更古老的反汇编格式。
9. --disassemble-zeroes:通常情况下,反汇编的输出会跳过大块的零。
该选项将指引反汇编器去反汇编那些大块的零,就像处理其他数据一样。
10. -EB/-EL/--endianbiglittle:指定目标文件的字节顺序。
该选项只会影响反汇编。
当反汇编像 S-records 这样没有描述字节顺序的文件格式时,该选项是有用的。
11. -f/--file-header:显示每个由 object-file 指定的所有目标文件的文件头概要信息。
12. -h/--section-header/--header:显示目标文件的节头概要信息。
ld 使用了“-Ttext”、“-Tdata”或“-Tbss”这些选项时,可能会使得目标文件的各个节被重定向到非标准的地址。
这样通过这个选项,可以查出目标文件的各节的起始地址。
然而,一些像 a.out 这样的目标文件格式并没有存储文件段的起始地址。
在这些情况下,尽管 ld 正确地重定位了每个节。
但是使用“objdump -h”列出文件节头时,并不能显示其正确地址。
13. --help:显示 objdump 的所有选项的概要信息然后退出。
14. -i/--info:列表显示所有对“-b”或“-m”可用的体系结构和目标格式。
15. -j name/--sectionname:只显示由 name 指定的节。
16. -l/--line-numbers:用对应于目标代码的文件名和行号来标注要显示的信息(使用调试信息),仅仅和-d、-D 或-r 一起使用才有效。
17. -m machine/--architecture machine:当反汇编由 object-file 指定的目标文件时,标明所使用的体系结构。
当反汇编一个像 S-records 这样本身并没有描述体系结构信息的文件的时候,这个选项是有用的。
可以用-i 选项列出可用的体系结构。
18. -p/--private-headers:显示目标文件格式的特定信息。
要显示的信息依赖于目标文件的格式。
对于某些目标文件格式,没有附加的信息可供显示。
19. -r/--reloc:显示文件的重定位入口。
如果和“-d”或者“-D”一起使用,重定位部分以反汇编后的格式显示出来。
20. -R/-dynamic-reloc:显示文件的动态重定位入口,仅仅对于动态目标文件有意义,例如特定类型的共享库。
21. -s/--full-contents:显示任何指定节的全部内容。
22. -S/--source:尽可能显示与反汇编混和的源代码。
隐含了“-d”参数。
23. --show-raw-insn:反汇编机器指令的时候,用十六进制和符号形式同时显示机器指令码。
然而并非所有的目标都能这样正确地出来。
24. --no-show-raw-insn:反汇编机器指令的时候,不显示指令类型。
这是指定--prefix-addresses 选项时的默认设置。
25. --stabs:显示任何指定节的全部内容。
显示 ELF 格式目标文件中的.stab、.stab.index 和.stab.excl 节的内容。
一般用于 Solaris 操作系统。
在其他大部分执行文件格式中,调试符号表入口与链接符号交织在一起,并在打开了“--yms”参数选项时,在 objdump 的输出中是可见的。
26. --start-address address:从指定地址开始显示数据,该选项影响打开-d、-r 和-s 选项时的输出。
27. --stop-address address:显示数据直到指定地址为止,该选项影响打开-d、-r 和-s 选项时的输出。
28. -t/--syms:显示文件中的符号表入口。
类似于“nm”程序提供的信息。
29. -T/--dynamic-syms:显示文件中的动态符号表入口。
仅仅对于动态目标文件有意义,例如特定类型的共享库。
类似于打开了“-D”选项的“nm”程序提供的信息。
30. --version:显示 objdump 的版本号然后退出。
31. -x/--all-header:显示所有可用的头信息,包括符号表和重定位入口,使用“-x”等价于指定了“-a -f -h -r -t”参数。
32. -w/--wide:对超过 80 列的输出设备指定一些行的格式。
对于刚才生成的 test 执行文件,我们执行objdump -f test显示执行文件文件头概要信息。
使用“-d”或“-D”参数反汇编生成的目标代码:objdump -d add.oreadelf 工具:readelf 软件显示一个或多个 ELF 格式的目标文件信息。
可通过各种参数选项来控制 readelf 软件显示目标文件中的特定信息。
size 工具: List section sizes and total sizesize 显示一个目标文件或者链接库文件中的目标文件的各个段的大小。
1、输出格式size 有两种输出格式,一种为quotsysvquot,另一种为quotberkeleyquot,默认为 berkeley 的格式。
第一种格式可以用quot-Aquot或者quot--formatsysvquot指定,第二种格式用quot-Bquot或quot--formatberkeleyquot指定2、数字输出格式有三种格式,octal decimal 及 hex,对应的参数为quot-oquotquot-dquot及quot-xquot,也可以用quot--radix8quotquot--radix10quot及quot-.
上一篇:
【精品】XQuery高级应用开发应用程序惯用法
下一篇:
多年来只想说一句,我不怪你