分类: LINUX
2009-06-26 13:22:42
(gdb) x/20i 0x12345678 # 按汇编代码查看内容
(gdb) x/20i $esp # 按汇编代码查看内容
(gdb) x/5x $esp # 按16进制查看寄存器esp内容
(gdb) x/5x 0x12345678 # 按16进制查看内容
(gdb) x/20strings 0x12345678 # 按字符串查看内容
2. 父子程序之间的关系:
|---------------| <----- 父进程堆栈
| param_1 |
|---------------|
| param_2 |
|---------------|
| param_3 | <----- call执行之前的esp
|---------------|
|调用者eip | <----- call执行之后的esp
|---------------|
| | <----- 以下为子进程堆栈
|---------------|
通常父程序把子程序所需要的参数压入父程序的堆栈,"call 子程序地址" 把紧接call指令的下一条指令地址压入父程序的堆栈,作为子程序的返回地址。刚进入子程序时(尚未执行自程序的第1条汇编指令),ESP = 父亲的EIP。
我们根据以上的结论,把main当作子进程,向上反推,看看main的老爸是谁,以及它老爸的老爸是谁
注意:
(1)如果用jump指令,则上述不成立。比如从动态连接器进入_start时是用jmp指令而非call指令。动态连接器的开头也定义了一个全局符号:也叫_start。这样从动态连接器里jmp到后面的_start(start.s里)可以保持名字统一连贯。
(2)在Linux系统中,应用程序和解释器的装入/启动是在Linux内核中由系统调用execve()完成,2者都被映射装入用户地址空间。execve()系统调用结束,返回用户空间时进入动态连接器入口开始执行。动态连接的实现由动态连接器在用户空间完成。(参考:漫谈兼容内核之八)。所以,从进入动态连接器直到main()程序启动,全都是在用户态的事情,使用用户态堆栈,多个函数之间的call调用形成堆栈侦(push %ebp; mov %esp,%ebp)也都在同一用户堆栈上,不能从用户空间向上一直反推到内核堆栈。
3.参见before main()分析:
_start( )的堆栈内容如下:
%esp The stack contains the arguments and environment:
0(%esp) argc 堆栈顶(低地址)
4(%esp) argv[0]
8(%esp) argv[1]
...
...
(4*argc)(%esp) NULL
(4*(argc+1))(%esp) envp[0]
(4*(argc+2))(%esp) envp[1] (高地址)
...
...
NULL
从_start到_main经过多级调用,后面我们看到,到_main时,堆栈实际上并不是这样排列的,可能是main之前的例程太多,堆栈塞了一堆其他东西。但是只要给出**envp和**argv,就可以找到那些在堆栈里连续存放的字符串。
4. 在Linux上面的虚拟地址空间:
(1) user area: 0x0 - 0x0bffffff (3GB内存空间)
0x0 - 0x08048000 Thread stack
0x08048000 - 0x40000000 用户程序 Text, Data
0x40000000 - 0xbfffffff shared LIB(ld-linux.so,libm.so,libc.so...)、用户程序Stack
(2) kernel area: 0x
(一)首先上查 main的前三代
=====================================================================
[test@radhat]$ more t1.c
#include <stdio.h>
Foo(char* s)
{
char buf[16]="";
strcpy(buf, s);
# printf("The input String is %s\n", buf); # 注释掉printf函数,容易跟踪,不至于眼花。
}
main(int argc, char* argv[])
{
if(argc == 2)
{
Foo(argv[1]);
}
else
{
printf("Usage: %s \n", argv[0]);
}
}
[test@radhat]$ gcc -g -o t1 t1.c
[test@radhat]$ gdb –q t1
(gdb) b main
(gdb) r
(gdb) disass main
Dump of assembler code for function main:
0x
0x080483cb
0x080483ce
0x080483d1
0x080483d2
。。。
(gdb) info reg esp
esp 0xbfc93ea0
(gdb) x/1x 0xbfc93ea0
0xbfc93ea0: 0x
(gdb) disass 0x
Dump of assembler code for function __new_exitfn:
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
# main的上级是__new_exitfn
#####################################################################
(gdb) b * 0x
(gdb) r
(gdb) i reg esp
esp 0xbfaf
(gdb) x/1x 0xbfaf
0xbfaf
(gdb) disass 0x
Dump of assembler code for function __cxa_atexit_internal:
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
# __new_exitfn()的上级是__cxa_atexit_internal()
#####################################################################
(gdb) b * 0x
(gdb) r
(gdb) i reg esp
esp 0xbf80ddcc
(gdb) x/1x 0xbf80ddcc
0xbf80ddcc: 0x
(gdb) disass 0x
Dump of assembler code for function __libc_start_main:
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
0x
# __cxa_atexit_internal()的上级是 __libc_start_main()
######################################################################
(gdb) b * 0x
Breakpoint 4 at 0x
(gdb) r
(gdb) i reg esp
esp 0xbfe498dc
(gdb) x/1x 0xbfe498dc
0xbfe498dc: 0x08048301
(gdb) disass 0x08048301
Dump of assembler code for function _start:
0x080482e0 <_start+0>: xor %ebp,%ebp
0x080482e2 <_start+2>: pop %esi
0x080482e3 <_start+3>: mov %esp,%ecx
0x080482e5 <_start+5>: and $0xfffffff0,%esp
0x080482e8 <_start+8>: push %eax
0x080482e9 <_start+9>: push %esp
0x080482ea <_start+10>: push %edx
0x080482eb <_start+11>: push $0x8048420
0x
0x
0x
0x
0x080482fc <_start+28>: call 0x
0x08048301 <_start+33>: hlt
0x08048302 <_start+34>: nop
0x08048303 <_start+35>: nop
End of assembler dump.
# __libc_start_main()的上级是 _start()
######################################################################
(gdb) b * 0x080482e0 # 在 _start开头设断点
(gdb) r bbbbbbbbbbb # 传递1个参数“bbbbbbbbbbb”给 t1程序
(gdb) i r esp
esp 0xbfba5660
(gdb) x/1x 0xbfba5660
0xbfba5660: 0x00000002 # 0x00000002 不是一个地址,没办法继续向上追查了。实际上它是argc,即参数个数(1个参数 + 程序本身=2)。
(gdb) x/40 0xbfba5660 # 看看_start堆栈的内容。基本上都是一些地址(即:字符串指针)
0xbfb54600: 0x00000002 0xbfb55bd4 0xbfb55bdd 0x00000000
0xbfb54610: 0xbfb55be9 0xbfb55bf9 0xbfb
0xbfb54620: 0xbfb
0xbfb54630: 0xbfb
…
…
(gdb) x/40strings 0xbfb55bd4 # 看看这些指针里都放了什么内容。参考前面对_start的堆栈的说明。
0xbfb55bd4: "/foot/t1" # *argv[0]
0xbfb55bdd: 'bbbbbbbbbbb' # *argv[1]
0xbfb55be9: "HOSTNAME=radhat" # *envp[0]
0xbfb55bf9: "TERM=vt100" # *envp[1]
0xbfb
0xbfb
0xbfb
0xbfb
0xbfb
0xbfb
...
...
(gdb)
# 这样的方法有局限,如果是jmp指令进入子程序,就没办法由子程序向上反查了。
==========================================================
(二)我们看看程序执行到main()时,堆栈上大致都存放了哪些内容
[test@radhat]$ gdb –q t1
(gdb) b main
(gdb) r aaa bbb cccc dddddd # 传递4个参数
Starting program: /foot/t1 aaa bbb cccc dddddd
Breakpoint 1, main (argc=5, argv=0xbfe490e4) at t1.c:9
9 if(argc == 2)
(gdb) x/40x $esp # 看看main的堆栈。4字节1列。
0xbfe49030: 0x
0xbfe49040: 0xbfe49060 0xbfe49060 0xbfe490b8 0x
0xbfe49050: 0x
0xbfe49060: 0x00000005 0xbfe490e4 0xbfe490fc 0x
0xbfe49070: 0x00000000 0xb7fa
0xbfe49080: 0x4fa76ff4 0x
0xbfe49090: 0x00fa
# 我们1个1个来看。x指令查看1个内存地址里面放了什么内容:一般存放4种数据:1。一个地址 2。一个常数 3。一个字符串 4。汇编指令
(gdb) x/1x 0x
0x
(gdb) x/20i 0x
0x
0x
0x
0x
0x
0x
…
…
(gdb) x/1x 0xbfe490fc # 第2个:0xbfe490fc里存放1个地址:0xbfe4abe9
0xbfe490fc: 0xbfe4abe9
(gdb) x/1strings 0xbfe4abe9 # 继续看看0xbfe4abe9里存放字符串"HOSTNAME=radhat"
0xbfe4abe9: "HOSTNAME=radhat"
(gdb) x/1x 0xbfe49058 # 第3个:0xbfe49058里存放1个地址:0xbfe490b8
0xbfe49058: 0xbfe490b8
(gdb) x/1x 0xbfe490b8 # 继续看看0xbfe490b8里存放1个常量0
0xbfe490b8: 0x00000000
(gdb) x/1x 0x4fa76ff4 # 第4个:0x4fa76ff4里存放1个地址:0x4fa76d
0x4fa76ff4: 0x4fa76d
(gdb) x/1x 0x4fa76d
0x4fa76d
(gdb) x/1x 0xbfe49060 # 第5,6个:0xbfe49060里存放1个常量5
0xbfe49060: 0x00000005
(gdb) x/1x 0xbfe490b8 # 第7个:0xbfe490b8里存放1个常量0
0xbfe490b8: 0x00000000
(gdb) x/1x 0x
0x
(gdb) x/1x 0x
0x
(gdb) x/1x 0x08048430 # 第10个:0x08048430里存放汇编指令
0x8048430 <__libc_csu_init>: 0x57e58955
(gdb) x/1x 0xbfe490b8 # 第11个:0xbfe490b8里存放1个常量0
0xbfe490b8: 0x00000000
(gdb) x/1x 0x
0x
# 第13个:0x00000005 是 argc 即:参数个数
(gdb) x/1x 0xbfe490e4 # 第14个:0xbfe490e4是 **argv
0xbfe490e4: 0xbfe4abcc # 0xbfe490e4里存放一个地址0xbfe4abcc
(gdb) x/8x 0xbfffe234 # 查看指针数组*argv[]的内容,包含了1组地址,以0结尾
0xbfe490e4: 0xbfe4abcc 0xbfe4abd5 0xbfe4abd9 0xbfe4abdd
0xbfe
(gdb) x/10strings 0xbfe4abcc # 按字符形式查看指针数组*argv[]的内容
0xbfe4abcc: "/foot/t1"
0xbfe4abd5: "aaa"
0xbfe4abd9: "bbb"
0xbfe4abdd: "cccc"
0xbfe4abe2: "dddddd"
0xbfe4abe9: "HOSTNAME=radhat"
0xbfe4abf9: "TERM=vt100"
0xbfe
0xbfe
0xbfe
(gdb) x/1x 0xbfe490fc # 第15个:0xbfe490fc是 **envp
0xbfe490fc: 0xbfe4abe9
(gdb) x/1strings 0xbfe4abe9
0xbfe4abe9: "HOSTNAME=radhat" # *envp[0] = "HOSTNAME=radhat"
(gdb) x/20strings 0xbfe4abe9 # 按字符串查看指针数组*envp[]的内容
0xbfe4abe9: "HOSTNAME=radhat"
0xbfe4abf9: "TERM=vt100"
0xbfe
0xbfe
0xbfe
0xbfe
0xbfe
0xbfe
...
...
(gdb) x/1x 0x
0x
(gdb) quit #用户堆栈的最顶端是0xbfffffff,有兴趣的继续看看。
[test@radhat]$