gcc命令objdump用法

gcc命令之 objdump 
---------------objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具----------
以下3条命令足够那些喜欢探索目标文件与源代码之间的丝丝的关系的朋友。
objdump -x obj 以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出 <可查到该文件的所有动态库>   
objdump -t obj 输出目标文件的符号表()
objdump -h obj 输出目标文件的所有段概括()
objdump -j .text/.data -S obj 输出指定段的信息,大概就是反汇编源代码把
objdump -S obj C语言与汇编语言同时显示
以下为网上摘录文章。

关于nm -s的显示请自己man nm查看
objdump命令的man手册
objdump - 显示二进制文件信息
objdump
      [-a] [-b bfdname |
      --target=bfdname] [-C] [--debugging]
      [-d] [-D]
      [--disassemble-zeroes]
      [-EB|-EL|--endian={big|little}] [-f]
      [-h] [-i|--info]
      [-j section | --section=section]
      [-l] [-m machine ] [--prefix-addresses]
      [-r] [-R]
      [-s|--full-contents] [-S|--source]
      [--[no-]show-raw-insn] [--stabs] [-t]
      [-T] [-x]
      [--start-address=address] [--stop-address=address]
      [--adjust-vma=offset] [--version] [--help]
      objfile...

--archive-headers
-a 显示档案库的成员信息,与 ar tv 类似
    objdump -a libpcap.a
    和 ar -tv libpcap.a 显示结果比较比较
    显然这个选项没有什么意思。
--adjust-vma=offset
    When dumping information, first add offset to all
    the section addresses. This is useful if the sec-
    tion addresses do not correspond to the symbol
    table, which can happen when putting sections at
    particular addresses when using a format which can
    not represent section addresses, such as a.out.
-b bfdname
--target=bfdname
    指定目标码格式。这不是必须的,objdump能自动识别许多格式,
    比如:objdump -b oasys -m vax -h fu.o
    显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys
    编译器生成的目标文件。objdump -i将给出这里可以指定的
    目标码格式列表
--demangle
-C 将底层的符号名解码成用户级名字,除了去掉所有开头
   的下划线之外,还使得C++函数名以可理解的方式显示出来。
--debugging 
    显示调试信息。企图解析保存在文件中的调试信息并以C语言
    的语法显示出来。仅仅支持某些类型的调试信息。
--disassemble
-d 反汇编那些应该还有指令机器码的section
--disassemble-all
-D 与 -d 类似,但反汇编所有section
--prefix-addresses
    反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
    显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。
--disassemble-zeroes
    一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。
-EB
-EL
--endian={big|little}
    这个选项将影响反汇编出来的指令。
    little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,
    x86都是这种。

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

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

--help 简短的帮助信息。

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

--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
    反汇编时,不显示汇编指令的机器码,这是指定 --prefix-addresses
    选项时的缺省设置。

--stabs
    Display the contents of the .stab, .stab.index, and
    .stab.excl sections from an ELF file. This is only
    useful on systems (such as Solaris 2.0) in which
    .stab debugging symbol-table entries are carried in
    an ELF section. In most other file formats, debug-
    ging symbol-table entries are interleaved with
    linkage symbols, and are visible in the --syms output.

--start-address=address
    从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。

--stop-address=address
    显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。

--syms
-t 显示文件的符号表入口。类似于nm -s提供的信息

--dynamic-syms
-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些
   共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。

--version 版本信息

    objdump --version

--all-headers
-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于
   -a -f -h -r -t 同时指定。

    objdump -x inet.o

参看 nm(1)

★ objdump应用举例(待增加)

/*
g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
*/
#include 
#include 
int main ( int argc, char * argv[] )
{
    execl( "/bin/sh", "/bin/sh", "-i", 0 );
    return 0;
}

g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
objdump -j .text -Sl objtest | more
/main(查找)

08048750:
main():
/home/scz/src/objtest.c:7
*/
#include 
#include 
int main ( int argc, char * argv[] )
{
8048750:       55                      pushl %ebp
8048751:       89 e5                   movl   %esp,%ebp
/home/scz/src/objtest.c:8
        execl( "/bin/sh", "/bin/sh", "-i", 0 );
8048753:       6a 00                   pushl $0x0
8048755:       68 d0 87 04 08          pushl $0x80487d0
804875a:       68 d3 87 04 08          pushl $0x80487d3
804875f:       68 d3 87 04 08          pushl $0x80487d3
8048764:       e8 db fe ff ff          call   8048644 <_init+0x40>
8048769:       83 c4 10                addl   $0x10,%esp
/home/scz/src/objtest.c:9
        return 0;
804876c:       31 c0                   xorl   %eax,%eax
804876e:       eb 04                   jmp    8048774 
8048770:       31 c0                   xorl   %eax,%eax
8048772:       eb 00                   jmp    8048774 
/home/scz/src/objtest.c:10
}
8048774:       c9                      leave 
8048775:       c3                      ret    
8048776:       90                      nop

如果说上面还不够清楚,可以用下面的命令辅助一下:

objdump -j .text -Sl objtest --prefix-addresses | more
objdump -j .text -Dl objtest | more

用以上不同的命令去试会得到惊喜!


参考: http://www.cnblogs.com/shitouer/archive/2010/04/05/1704554.html 反汇编
       http://www.hackbase.com/tech/2007-07-28/161027746439.html 反汇编
      http://blog.csdn.net/sco_field/article/details/4310987 GDB调试

objdump是gcc工具,用来查看编译后目标文件的组成。
常用命令:
objdump -x obj:以某种分类信息的形式把目标文件的数据组成输出;<可查到该文件的的所有动态库>
objdump -t obj:输出目标文件的符号表()
objdump -h obj:输出目标文件的所有段概括()
objdump -j ./text/.data -S obj:输出指定段的信息(反汇编源代码)
objdump -S obj:输出目标文件的符号表()  当gcc -g时打印更明显

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

这是按Section的名称列出的,其中跟动态连接有关的Section也出现在前面名为Dynamic的Segment中,只是在那里是按类型列出的。例如,前面类型为HASH的表项说与此有关的信息在0x8048128处,而这里则说有个名为.hash的Section,其起始地址为0x8048128。还有,前面类型为PLTGOT的表项说与此有关的信息在0x804a2c4处,这里则说有个名为.got的Section,其起始地址为0x804a2c4,不过Section表中提供的信息更加详细一些,有些信息则互相补充。在Section表中,只要类型为PROGBITS,就说明这个Section的内容都来自映像文件,反之类型为NOBITS就说明这个Section的内容并非来自映像文件。
跟区段头表中的信息一对照,就可以知道在第16项.data以前的所有区段都是要装入用户空间的。这里面包括了大家所熟知的.text即“代码段”。此外,.init、.fini两个区段也有着特殊的重要性,因为映像的程序入口就在.init段中,实际上在进入main()之前的代码都在这里。而从main()返回之后的代码,包括对exit()的调用,则在.fini中。还有一个区段.plt也十分重要,plt是“Procedure Linkage Table”的缩写,这就是用来为目标映像跟共享库建立动态连接的。
有些Section名是读者本来就知道的,例如.text、.data、.bss;有些则从它们的名称就可猜测出来,例如.symtab是符号表、.rodata是只读数据、还有.comment和.debug_info等等。还有一些可能就不知道了,这里择其要者先作些简略的介绍:

(1).hash。为便于根据函数/变量名找到有关的符号表项,需要对函数/变量名进行hash计算,并根据计算值建立hash队列。
● .dynsym。需要加以动态连接的符号表,类似于内核模块中的INPORT符号表。这是动态连接符号表的数据结构部分,须与.dynstr联用。
● .dynstr。动态连接符号表的字符串部分,与.dynsym联用。
● .rel.dyn。用于动态连接的重定位信息。
● .rel.plt。一个结构数组,其中的每个元素都代表着GOP表中的一个表项GOTn(见下)。
● .init。在进入main()之前执行的代码在这个Section中。
● .plt。“过程连接表(Procedure Linking Table)”,见后。
● .fini。从main()返回之后执行的代码在这个Section中,最后会调用exit()。
● .ctors。表示“Constructor”,是一个函数指针数组,这些函数需要在程序初始化阶段(进入main()之前,在.init中)加以调用。
● .dtors。表示“Distructor”,也是一个函数指针数组,这些函数需要在程序扫尾阶段(从main()返回之后,在.fini中)加以调用。
● .got。“全局位移表(Global Offset Table)”,见后。
● .strtab。与符号表有关的字符串都集中在这个Section中。


gcc -g -o stack1 stack1.c
objdump -dS stack1 (objdump -j .text -Sl stack1 | more 只显示代码段)

Disassembly of section .init:(在进入main()之前执行的代码在这个Section中)
Disassembly of section .plt:(过程连接表(Procedure Linking Table,实现动态链接)
Disassembly of section .text:(代码段)
080482e0 <_start>:
 80482e0: 31 ed                 xor    �p,�p
 80482e2: 5e                    pop    %esi
 80482e3: 89 e1                 mov    %esp,�x
 80482e5: 83 e4 f0              and    $0xfffffff0,%esp
 80482e8: 50                    push   �x
 80482e9: 54                    push   %esp
 80482ea: 52                    push   �x
 80482eb: 68 50 84 04 08        push   $0x8048450
 80482f0: 68 f0 83 04 08        push   $0x80483f0
 80482f5: 51                    push   �x
 80482f6: 56                    push   %esi
 80482f7: 68 c5 83 04 08        push   $0x80483c5
 80482fc: e8 c3 ff ff ff        call   80482c4 <__libc_start_main@plt>
 8048301: f4                    hlt   
 8048302: 90                    nop
 8048303: 90                    nop
 8048304: 90                    nop
 8048305: 90                    nop
 8048306: 90                    nop
 8048307: 90                    nop
 8048308: 90                    nop
 8048309: 90                    nop
 804830a: 90                    nop
 804830b: 90                    nop
 804830c: 90                    nop
 804830d: 90                    nop
 804830e: 90                    nop
 804830f: 90                    nop

08048310 <__do_global_dtors_aux>:
 8048310: 55                    push   �p
 8048311: 89 e5                 mov    %esp,�p
 8048313: 53                    push   �x
 8048314: 83 ec 04              sub    $0x4,%esp
 8048317: 80 3d 10 a0 04 08 00  cmpb   $0x0,0x804a010
 804831e: 75 3f                 jne    804835f <__do_global_dtors_aux+0x4f>
 8048320: a1 14 a0 04 08        mov    0x804a014,�x
 8048325: bb 20 9f 04 08        mov    $0x8049f20,�x
 804832a: 81 eb 1c 9f 04 08     sub    $0x8049f1c,�x
 8048330: c1 fb 02              sar    $0x2,�x
 8048333: 83 eb 01              sub    $0x1,�x
 8048336: 39 d8                 cmp    �x,�x
 8048338: 73 1e                 jae    8048358 <__do_global_dtors_aux+0x48>
 804833a: 8d b6 00 00 00 00     lea    0x0(%esi),%esi
 8048340: 83 c0 01              add    $0x1,�x
 8048343: a3 14 a0 04 08        mov    �x,0x804a014
 8048348: ff 14 85 1c 9f 04 08  call   *0x8049f1c(,�x,4)
 804834f: a1 14 a0 04 08        mov    0x804a014,�x
 8048354: 39 d8                 cmp    �x,�x
 8048356: 72 e8                 jb     8048340 <__do_global_dtors_aux+0x30>
 8048358: c6 05 10 a0 04 08 01  movb   $0x1,0x804a010
 804835f: 83 c4 04              add    $0x4,%esp
 8048362: 5b                    pop    �x
 8048363: 5d                    pop    �p
 8048364: c3                    ret   
 8048365: 8d 74 26 00           lea    0x0(%esi,%eiz,1),%esi
 8048369: 8d bc 27 00 00 00 00  lea    0x0(�i,%eiz,1),�i

08048370 <frame_dummy>:
 8048370: 55                    push   �p
 8048371: 89 e5                 mov    %esp,�p
 8048373: 83 ec 18              sub    $0x18,%esp
 8048376: a1 24 9f 04 08        mov    0x8049f24,�x
 804837b: 85 c0                 test   �x,�x
 804837d: 74 12                 je     8048391 <frame_dummy+0x21>
 804837f: b8 00 00 00 00        mov    $0x0,�x
 8048384: 85 c0                 test   �x,�x
 8048386: 74 09                 je     8048391 <frame_dummy+0x21>
 8048388: c7 04 24 24 9f 04 08  movl   $0x8049f24,(%esp)
 804838f: ff d0                 call   *�x
 8048391: c9                    leave 
 8048392: c3                    ret   
 8048393: 90                    nop

08048394 <bar>:
#include<stdlib.h>
#include<stdio.h>

int bar(int c,int d)
{
 8048394: 55                    push   �p          //ebp是栈底指针      
 8048395: 89 e5                 mov    %esp,�p     //esp是栈顶指针
 8048397: 83 ec 10              sub    $0x10,%esp
  int e=c+d;                               //4个数据寄存器(eax、ebx、ecx和edx)
 804839a: 8b 45 0c              mov    0xc(�p),�x
 804839d: 8b 55 08              mov    0x8(�p),�x
 80483a0: 8d 04 02              lea    (�x,�x,1),�x
 80483a3: 89 45 fc              mov    �x,-0x4(�p)
  return e;
 80483a6: 8b 45 fc              mov    -0x4(�p),�x
}
 80483a9: c9                    leave 
 80483aa: c3                    ret   

080483ab <foo>:

int foo(int a,int b)
{
 80483ab: 55                    push   �p
 80483ac: 89 e5                 mov    %esp,�p
 80483ae: 83 ec 08              sub    $0x8,%esp
  return bar(a,b);
 80483b1: 8b 45 0c              mov    0xc(�p),�x
 80483b4: 89 44 24 04           mov    �x,0x4(%esp)
 80483b8: 8b 45 08              mov    0x8(�p),�x
 80483bb: 89 04 24              mov    �x,(%esp)
 80483be: e8 d1 ff ff ff        call   8048394 <bar>
}
 80483c3: c9                    leave 
 80483c4: c3                    ret   

080483c5 <main>:

int main(void)
{
 80483c5: 55                    push   �p
 80483c6: 89 e5                 mov    %esp,�p
 80483c8: 83 ec 08              sub    $0x8,%esp
  foo(2,3);
 80483cb: c7 44 24 04 03 00 00  movl   $0x3,0x4(%esp)
 80483d2: 00
 80483d3: c7 04 24 02 00 00 00  movl   $0x2,(%esp)
 80483da: e8 cc ff ff ff        call   80483ab <foo>
  return 0;
 80483df: b8 00 00 00 00        mov    $0x0,�x
}

Disassembly of section .fini:(main结束后调用,最后调用exit)

1:“gdb”命令启动GDB
2: file stack1
3:使用“r”命令执行(Run)被调试文件,因为尚未设置任何断点,将直接执行到程序结束
4:使用“b”命令在 main 函数开头设置一个断点(Breakpoint):
5:使用“r”命令执行(Run)被调试程序
6:使用“s”命令(Step)执行下一行代码


代码如下:
    #include "stdlib.h"
    int sum(int a,int b,int m,int n)
    {
         return a+b;
    }
    void main()
    {
         int result = sum(1,2,3,4);
         system("pause");
    }
  有四个参数的sum函数,接着在main方法中调用sum函数。在debug环境下,单步调试如下:

11:   void main()
12:   {
00401060   push        ebp
;保存ebp,执行这句之前,ESP = 0012FF4C EBP = 0012FF88
;执行后,ESP = 0012FF48(向下增长) EBP = 0012FF88,ESP减小,EBP不变
00401061   mov         ebp,esp
;将esp放入ebp中,此时ebp和esp相同,即执行后ESP = 0012FF48 EBP = 0012FF48
;原EBP值已经被压栈(位于栈顶),而新的EBP又恰恰指向栈顶。
;此时EBP寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原EBP入栈后的栈顶),
;从该地址为基准,向上(栈底方向)能获取返回地址、参数值(假如main中有参数,“获取参数值”会比较容易理解,
;不过在看下边的sum函数调用时会有体会的),向下(栈顶方向)能获取函数局部变量值,
;而该地址处又存储着上一层函数调用时的EBP值!
00401063   sub         esp,44h
;把esp往上移动一个范围
;等于在栈中空出一片空间来存局部变量
;执行这句后ESP = 0012FF04 EBP = 0012FF48
00401066   push        ebx
00401067   push        esi
00401068   push        edi
;保存三个寄存器的值
00401069   lea         edi,[ebp-44h]
;把ebp-44h加载到edi中,目的是保存局部变量的区域
0040106C   mov         ecx,11h
00401071   mov         eax,0CCCCCCCCh
00401076   rep stos    dword ptr [edi]
;从ebp-44h开始的区域初始化成全部0CCCCCCCCh,就是int3断点,初始化局部变量空间
;REP           ;CX不等于0 ,则重复执行字符串指令
;格式: STOS OPRD
;功能: 把AL(字节)或AX(字)中的数据存储到DI为目的串地址指针所寻址的存储器单元中去.指针DI将根据DF的值进行自动
;调整. 其中OPRD为目的串符号地址.
;以上的语句就是在栈中开辟一块空间放局部变量
;然后把这块空间都初始化为0CCCCCCCCh,就是int3断点,一个中断指令。
;因为局部变量不可能被执行,执行了就会出错,这时候发生中断提示开发者。
13:       int result = sum(1,2,3,4);
00401078   push        4
0040107A   push        3
0040107C   push        2
0040107E   push        1
;各个参数入栈,注意查看寄存器ESP值的变化
;亦可以看到参数入栈的顺序,从右到左
;变化为:ESP = 0012FEF8-->ESP = 0012FEF4-->ESP = 0012FEF0-->ESP = 0012FEEC-->ESP = 0012FEE8
00401080   call        @ILT+15(boxer) (00401014)
;调用sum函数,可以按F11跟进
;注:f10(step over),单步调试,遇到函数调用,直接执行,不会进入函数内部
;f11(step into),单步调试,遇到函数调用,会进入函数内部
;shift+f11(step out),进入函数内部后,想从函数内部跳出,用此快捷方式
;ctrl+f10(run to cursor),呵呵,看英语注释就应该知道是什么意思了,不再解释
00401084   add         esp,10h
;调用完函数后恢复/释放栈,执行后ESP = 0012FEF8,与sum函数的参数入栈前的数值一致
00401088   mov         dword ptr [ebp-4],eax
;将结果存放在result中,原因详看最后有关ss的注释
14:       system("pause");
0040108B   push        offset string "pause" (00422f6c)
00401090   call        system (0040eed0)
00401095   add   esp ,4

;有关system(“pause”)的处理,此处不讨论

15:   }
00401098   pop         edi
00401099   pop         esi
0040109A   pop         ebx

;恢复原来寄存器的值,怎么“吃”进去,怎么“吐”出来
0040109B   add         esp,44h
;恢复ESP,对应上边的sub esp,44h
0040109E   cmp         ebp,esp
;检查esp是否正常,不正常就进入下边的call里面debug
004010A0   call        __chkesp (004010b0)
;处理可能出现的堆栈异常,如果有的话,就会陷入debug
004010A5   mov         esp,ebp
004010A7   pop         ebp
;恢复原来的esp和ebp,让上一个调用函数正常使用
004010A8   ret

;将返回地址存入eip,转移流程
;如果函数有返回值,返回值将放在eax返回(这就是很多软件给秒杀爆破的原因了,因为eax的返回值是可以改的)

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

;以上即是主函数调用的反汇编过程,下边来看调用sum函数的过程:
;上边有说在00401080   call        @ILT+15(boxer) (00401014)这一句处,用f11单步调试,f11后如下句:
00401014   jmp         sum (00401020)
;即跳转到sum函数的代码段中,再f11如下:
6:    int sum(int a,int b,int m,int n)
7:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,40h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-40h]
0040102C   mov         ecx,10h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
;可见,上边几乎与主函数调用相同,每一步不再赘述,可对照上边主函数调用的注释
8:        return a+b;
00401038   mov         eax,dword ptr [ebp+8]
;取第一个参数放在eax
0040103B   add         eax,dword ptr [ebp+0Ch]
;取第二个参数,与eax中的数值相加并存在eax中
9:    }
0040103E   pop         edi
0040103F   pop         esi
00401040   pop         ebx
00401041   mov         esp,ebp
00401043   pop         ebp
00401044   ret
;收尾操作,比前边只是少了检查esp操作罢了
有关ss部分的注释:
;一般而言,ss:[ebp+4]处为返回地址
;ss:[ebp+8]处为第一个参数值(这里是a),ss:[ebp+0Ch]处为第二个参数(这里是b,这里8+4=12=0Ch)
;ss:[ebp-4]处为第一个局部变量(如main中的result),ss:[ebp]处为上一层EBP值
;ebp和函数返回值是32位,所以占4个字节


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值