系统未建立
分类: LINUX
2017-03-13 11:01:45
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 <>
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
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
804837f: b8 00 00 00 00 mov $0x0,?x
8048384: 85 c0 test ?x,?x
8048386: 74 09 je 8048391
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
#include
#include
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
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
}
80483c3: c9 leave
80483c4: c3 ret
080483c5
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
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个字节
for example
../prebuilt/-x86/toolchain/arm-eabi-4.4.3/arm-eabi/bin/objdump -j .text -SlD arch/arm/mach-sc/xxxxxx.o | tee uuu.txt