linux程序运行分析

来源:互联网 发布:花椒直播苹果mac版 编辑:程序博客网 时间:2024/04/30 01:21

 试验环境:

[jackie@cluster3 cLearn]$ gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.6/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.6 20060404 (Red Hat 3.4.6-3)
[jackie@cluster3 cLearn]$ uname -a
Linux cluster3 2.6.20 #11 SMP Tue Feb 13 20:53:01 CST 2007 i686 i686 i386 GNU/Linux


函数调用分析

1. 编写测试程序
#include <stdlib.h>

int foo(int fi,int fj)
{
        int fk;
        fk = 3;
        return 0;
}
int main()
{
        int mi;
        int mj;
        mi=1;
        mj=2;
        foo(mi,mj);
        return 0;
}
2.编译代码
gcc -g -o cc cc.c
3.用gdb进行debug:gdb cc
(gdb) list
4       {
5               int fk;
6               fk = 3;
7               return 0;
8       }
9       int main()
10      {
11              int mi;
12              int mj;
13              mi=1;
(gdb)
14              mj=2;
15              foo(mi,mj);
16              return 0;
17      }
(2)查看汇编代码:
(gdb) disass main
Dump of assembler code for function main:
0x08048348 <main+0>:    push   %ebp
0x08048349 <main+1>:    mov    %esp,%ebp
0x0804834b <main+3>:    sub    $0x8,%esp
0x0804834e <main+6>:    and    $0xfffffff0,%esp
0x08048351 <main+9>:    mov    $0x0,%eax
0x08048356 <main+14>:   add    $0xf,%eax
0x08048359 <main+17>:   add    $0xf,%eax
0x0804835c <main+20>:   shr    $0x4,%eax
0x0804835f <main+23>:   shl    $0x4,%eax
0x08048362 <main+26>:   sub    %eax,%esp
0x08048364 <main+28>:   movl   $0x1,0xfffffffc(%ebp)
0x0804836b <main+35>:   movl   $0x2,0xfffffff8(%ebp)
0x08048372 <main+42>:   pushl  0xfffffff8(%ebp)
0x08048375 <main+45>:   pushl  0xfffffffc(%ebp)
0x08048378 <main+48>:   call   0x8048334 <foo>
0x0804837d <main+53>:   add    $0x8,%esp
0x08048380 <main+56>:   mov    $0x0,%eax
0x08048385 <main+61>:   leave
0x08048386 <main+62>:   ret
End of assembler dump.
(gdb) disass foo
Dump of assembler code for function foo:
0x08048334 <foo+0>:     push   %ebp
0x08048335 <foo+1>:     mov    %esp,%ebp
0x08048337 <foo+3>:     sub    $0x4,%esp
0x0804833a <foo+6>:     movl   $0x3,0xfffffffc(%ebp)
0x08048341 <foo+13>:    mov    $0x0,%eax
0x08048346 <foo+18>:    leave
0x08048347 <foo+19>:    ret
End of assembler dump.
(3)在主函数设置断点,并执行程序,让程序在main函数刚开始时暂停:
(gdb) break 9
Breakpoint 1 at 0x8048348: file cc.c, line 9.
(gdb) run
Starting program: /home/jackie/SourceDIR/cLearn/cc
Reading symbols from shared object read from target memory...done.
Loaded system supplied DSO at 0xb7f44000

Breakpoint 1, main () at cc.c:10
10      {
(4)查看关键寄存器内容:
(gdb) i reg esp
esp            0xbffbb77c       0xbffbb77c
(gdb) i reg ebp
ebp            0xbffbb7d8       0xbffbb7d8
(gdb) i reg eip
eip            0x8048348        0x8048348
可以看到当前eip的内容是0x8048348,要执行的指令是push   %ebp
(5)查看栈空间内容:
(gdb) x/32xw 0xbffbb770
0xbffbb770:     0x00000000      0xb7f23ca0      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
0xbffbb790:     0xb7ee6ff4      0x00000000      0xbffbb790      0xbffbb7d8
0xbffbb7a0:     0xbffbb780      0xb7dd4da5      0x00000000      0x00000000
0xbffbb7b0:     0x00000000      0xb7f23fd4      0x00000001      0x0804828c
0xbffbb7c0:     0x00000000      0xb7f19ba0      0xb7f1a6f0      0xb7f23fd4
0xbffbb7d0:     0x00000001      0x0804828c      0x00000000      0x080482ad
0xbffbb7e0:     0x08048348      0x00000001      0xbffbb804      0x08048388
我们这里使用的地址是0xbffbb770,而当前栈顶指针是0xbffbb77c,从上面的数据可以看出,其对应的栈内容是0xbffbb7d8
ebp的内容为0xbffbb7d8,则可以知道其对应的栈内容为0x00000000
(6)执行一条机器指令(0x08048348 <main+0>:    push   %ebp),即将ebp压栈:
(gdb) si
0x08048349      10      {
(gdb) i reg esp
esp            0xbffbb778       0xbffbb778
(gdb) i reg ebp
ebp            0xbffbb7d8       0xbffbb7d8
(gdb) i reg eip
eip            0x8048349        0x8048349
(gdb) x/12xw 0xbffbb770
0xbffbb770:     0x00000000      0xb7f23ca0      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
0xbffbb790:     0xb7ee6ff4      0x00000000      0xbffbb790      0xbffbb7d8
可以看到,ebp(内容是0xbffbb7d8)被压到0xbffbb778(esp指出的)处。
(7)执行一条机器指令(0x08048349 <main+1>:    mov    %esp,%ebp),即将esp->ebp,查看寄存器内容和堆内容:
(gdb) si
0x0804834b      10      {
(gdb) i reg esp
esp            0xbffbb778       0xbffbb778
(gdb) i reg ebp
ebp            0xbffbb778       0xbffbb778
(gdb) i reg eip
eip            0x804834b        0x804834b
(gdb) x/12xw 0xbffbb770
0xbffbb770:     0x00000000      0xb7f23ca0      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
0xbffbb790:     0xb7ee6ff4      0x00000000      0xbffbb790      0xbffbb7d8
可以看到,栈空间内容没变,只是寄存器ebp变成了esp的值。

(8) 在下面有连续的多条指令:
x0804834b <main+3>:    sub    $0x8,%esp
0x0804834e <main+6>:    and    $0xfffffff0,%esp
0x08048351 <main+9>:    mov    $0x0,%eax
0x08048356 <main+14>:   add    $0xf,%eax
0x08048359 <main+17>:   add    $0xf,%eax
0x0804835c <main+20>:   shr    $0x4,%eax
0x0804835f <main+23>:   shl    $0x4,%eax
0x08048362 <main+26>:   sub    %eax,%esp
其中前两条指令可以用来进行栈内数据对齐。后面的几条指令也造成了esp指针的移动,但是还不知道用来干什么。总之,上述的多条指令执行完毕后,使得栈指针向低地址方向移动了24byte(0xbfe57618-0xbfe57600)
后续的指令是
0x08048364 <main+28>:   movl   $0x1,0xfffffffc(%ebp)
0x0804836b <main+35>:   movl   $0x2,0xfffffff8(%ebp)
也就是为mi,mi赋值
(gdb) si
13              mi=1;
(gdb) i reg esp
esp            0xbffbb760       0xbffbb760
(gdb)
esp            0xbffbb760       0xbffbb760
(gdb) x/12xw 0xbffbb760
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000000      0xb7f23ca0      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
(gdb) si
14              mj=2;
(gdb) i reg esp
esp            0xbffbb760       0xbffbb760
(gdb) x/12xw 0xbffbb760
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000000      0x00000001      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
(gdb) si
15              foo(mi,mj);
(gdb) i reg esp
esp            0xbffbb760       0xbffbb760
(gdb) x/12xw 0xbffbb760
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000002      0x00000001      0xbffbb7d8      0xb7dd4de3
0xbffbb780:     0x00000001      0xbffbb804      0xbffbb80c      0xb7f19c66
可以看到执行这两天指令后,esp的内容并没有改变,mi和mj的值也不是放在栈顶的,在movl指令中有特别的参数指定,例如第一个指令中有个0xfffffffc,具体如何操作不明,但是从栈的内容可以看到mi和mj的内容分别在0xbffbb774和0xbffbb770中。
(9)执行下两条指令,堆将要调用的函数foo(int, int)的参数压栈。两条指令是:
0x08048372 <main+42>:   pushl  0xfffffff8(%ebp)
0x08048375 <main+45>:   pushl  0xfffffffc(%ebp)
这两条指令的作用是对调用的函数foo(int fi, int fj)的参数压栈。
(gdb) si
0x08048375      15              foo(mi,mj);
(gdb) si
0x08048378      15              foo(mi,mj);
(gdb) i reg esp
esp            0xbffbb758       0xbffbb758
(gdb) i reg ebp
ebp            0xbffbb778       0xbffbb778
(gdb) i reg eip
eip            0x8048378        0x8048378
(gdb) x/12xw 0xbffbb750
0xbffbb750:     0x00000000      0x00000000      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000002      0x00000001      0xbffbb7d8      0xb7dd4de3
可以看到,esp的内容由0xbffbb760变成了0xbffbb758,其中0xbffbb758地址中的内容是mi的值1,0xbffbb75c地址中的内容是mj的值2,
(10)执行下一条指令:0x08048378 <main+48>:   call   0x8048334 <foo>
这条call指令可以分解成两条指令:push eip;jmp 0x8048430。
其作用是保存main函数的foo(int, int)函数调用后的下一条指令的地址(0x0804837d),以便foo(int ,int)函数调用返回后在main()函数继续指令,同时还要跳转到foo(int, int)的地址(0x8048334),以便执行函数foo(int ,int);

注意:此时的eip为0x8048334,对应的指令是:0x08048334 <foo+0>:     push   %ebp
(gdb) si
foo (fi=1, fj=2) at cc.c:4
4       {
(gdb) i reg esp
esp            0xbffbb754       0xbffbb754
(gdb) i reg ebp
ebp            0xbffbb778       0xbffbb778
(gdb) i reg eip
eip            0x8048334        0x8048334
(11)执行下两条指令,并查看寄存器和栈内容:
这两条指令是:
0x08048334 <foo+0>:     push   %ebp
0x08048335 <foo+1>:     mov    %esp,%ebp
这两条指令的作用是对main函数中的 ebp进行压栈,以便返回时用(和第15步对应),同时将esp赋值为ebp(ebp->esp),一般本函数返回(和第16步对应)。
(gdb) si
0x08048335      4       {
(gdb) si
0x08048337      4       {
(gdb) i reg esp
esp            0xbffbb750       0xbffbb750
(gdb) i reg ebp
ebp            0xbffbb750       0xbffbb750
(gdb) i reg eip
eip            0x8048337        0x8048337
(gdb) x/12xw 0xbffbb750
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000002      0x00000001      0xbffbb7d8      0xb7dd4de3
可以看到ebp的内容被压栈,压在地址0xbffbb750处,其内容为0xbffbb778,与(12)中的值是一样的。
(12)执行下两条指令,并查看寄存器和栈的内容:
这两条指令是:
0x08048337 <foo+3>:     sub    $0x4,%esp
0x0804833a <foo+6>:     movl   $0x3,0xfffffffc(%ebp)
其作用是为fk赋值。
(gdb) si
6               fk = 3;
(gdb) i reg esp
esp            0xbffbb74c       0xbffbb74c
(gdb) i reg ebp
ebp            0xbffbb750       0xbffbb750
(gdb) i reg eip
eip            0x804833a        0x804833a
(gdb) x/12xw 0xbffbb740
0xbffbb740:     0x08048388      0x0804954c      0xbffbb758      0x08048269
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
(gdb) si
7               return 0;
(gdb) i reg esp
esp            0xbffbb74c       0xbffbb74c
(gdb) i reg ebp
ebp            0xbffbb750       0xbffbb750
(gdb) i reg eip
eip            0x8048341        0x8048341
(gdb) x/12xw 0xbffbb740
0xbffbb740:     0x08048388      0x0804954c      0xbffbb758      0x00000003
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
可以看到fk的值为3,地址为0xbffbb74c
(13)执行下一条指令(0x08048341 <foo+13>:    mov    $0x0,%eax),其作用是为eax清空,即使返回值为0。
(gdb) si
8       }
(gdb) i reg esp
esp            0xbffbb74c       0xbffbb74c
(gdb) i reg ebp
ebp            0xbffbb750       0xbffbb750
(gdb) i reg eip
eip            0x8048346        0x8048346
(gdb) i reg eax
eax            0x0      0
(gdb) x/12xw 0xbffbb740
0xbffbb740:     0x08048388      0x0804954c      0xbffbb758      0x00000003
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
(14)执行下一条指令(0x08048346 <foo+18>:    leave),这条指令可以分解为mov %ebp %esp和pop %ebp,其作用使弹出12步中保存的main函数中的ebp,为返回做准备.
(gdb) si
0x08048347 in foo (fi=1, fj=-1074022396) at cc.c:8
8       }
(gdb) i reg esp
esp            0xbffbb754       0xbffbb754
(gdb) i reg ebp
ebp            0xbffbb778       0xbffbb778
(gdb) i reg eip
eip            0x8048347        0x8048347
(gdb) x/12xw 0xbffbb750
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000002      0x00000001      0xbffbb7d8      0xb7dd4de3
(15)执行下一条指令(0x08048347 <foo+19>:    ret),即pop eip,返回10步中保存的返回地址:
(gdb) si
0x0804837d in main () at cc.c:15
15              foo(mi,mj);
(gdb) i reg esp
esp            0xbffbb758       0xbffbb758
(gdb) i reg ebp
ebp            0xbffbb778       0xbffbb778
(gdb) i reg eip
eip            0x804837d        0x804837d
(gdb) x/12xw 0xbffbb750
0xbffbb750:     0xbffbb778      0x0804837d      0x00000001      0x00000002
0xbffbb760:     0xb7ee6ff4      0xb7ee6ff4      0x0804946c      0xb7ee6ff4
0xbffbb770:     0x00000002      0x00000001      0xbffbb7d8      0xb7dd4de3
(16)此时的eip值为0x804837d:0x0804837d <main+53>:   add    $0x8,%esp
后面的指令还有:
0x08048380 <main+56>:   mov    $0x0,%eax
0x08048385 <main+61>:   leave
0x08048386 <main+62>:   ret
其作用跟foo函数的一样,这里就不讲了,函数调用至此结束。

参考:http://ailantianlinux.spaces.live.com/blog/cns!170F6237803E4DB7!189.entry?_c=BlogPart

备注:程序的foo函数最开始的两句:

0x08048348 <main+0>:    push   %ebp
0x08048349 <main+1>:    mov    %esp,%ebp

可以使用gcc的omit-frame-pointer选项去掉,不过,这样在访问局部变量时会发生变化,可以写一个读取局部变量的小程序,调试对比其差别。

编译时使用static参数,将库函数包含到可执行文件中就可以对其进行反编译,查看到系统调用级的指令代码

原创粉丝点击