LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3136|回复: 7

stack explorer

[复制链接]
发表于 2005-8-27 15:28:58 | 显示全部楼层 |阅读模式
在main函数执行之前,用户栈里面已经有了一些数据,那么这些数据究竟是什么呢? 下面我们就来简单看一下:
我们得有一个最简单的程序,嗯,一个没有不输出hello world的hello world程序:

  1. [rick@Fedora-Core test]$ cat test.c
  2. int main(int argc,char**argv,char**env)
  3. {
  4.         return 0;
  5. }
复制代码

下面我们就来调试他:

  1. [rick@Fedora-Core test]$ gcc -g test.c -o test
  2. [rick@Fedora-Core test]$ gdb -q test
  3. Using host libthread_db library "/lib/tls/libthread_db.so.1".
  4. (gdb) l
  5. 1       int main(int argc,char**argv,char**env)
  6. 2       {
  7. 3               return 0;
  8. 4       }
  9. (gdb) b 3
  10. Breakpoint 1 at 0x8048350: file test.c, line 3.
  11. (gdb) r
  12. Starting program: /home/rick/test/test
  13. Reading symbols from shared object read from target memory...done.
  14. Loaded system supplied DSO at 0xffffe000

  15. Breakpoint 1, main (argc=1, argv=0xbffff844, env=0xbffff84c) at test.c:3
  16. 3               return 0;
  17. (gdb) p &argc
  18. $1 = (int *) 0xbffff7c0
复制代码

好的,我们得到了main函数第一个参数所在的栈指针 0xbffff7c0,现在就来看看在这个地方有什么:

  1. (gdb) x/40x 0xbffff7c0
  2. 0xbffff7c0:     0x00000001      0xbffff844      0xbffff84c      0x00931ab6
  3. 0xbffff7d0:     0x00a65ff4      0x00000000      0xbffff7d0      0xbffff818
  4. 0xbffff7e0:     0xbffff7c0      0x00957df5      0x00000000      0x00000000
  5. 0xbffff7f0:     0x00000000      0x0093bfb4      0x00000001      0x0804828c
  6. 0xbffff800:     0x00000000      0x009319f0      0x00932340      0x0093bfb4
  7. 0xbffff810:     0x00000001      0x0804828c      0x00000000      0x080482ad
  8. 0xbffff820:     0x08048334      0x00000001      0xbffff844      0x08048358
  9. 0xbffff830:     0x080483ac      0x00932340      0xbffff83c      0x00938e31
  10. 0xbffff840:     0x00000001      0xbffff998      0x00000000      0xbffff9ad
  11. 0xbffff850:     0xbffff9c0      0xbffff9d5      0xbffff9e0      0xbffff9f0
复制代码

ok,我们看到:0xbffff844就是argv,0xbffff84c就是env,但是后面是什么呢? 这个问题留待后面解释,再看0xbffff844的地方是 0xbffff998,0x00000000,很明显,这是argv[0]所在的地址,我们来看看:

  1. (gdb) x/s 0xbffff998
  2. 0xbffff998:      "/home/rick/test/test"
复制代码

嗯,没错.
接着就是env,他指向的地方(0xbffff84c)是0xbffff9ad,那自然这里就是env[0]咯,那我们知道环境变量会有很多,所以从0xbffff84c开始一直到0x00000000都是环境变量,嗯,我们来证实一下:

  1. (gdb) x/20s 0xbffff9ad
  2. 0xbffff9ad:      "SSH_AGENT_PID=4166"
  3. 0xbffff9c0:      "HOSTNAME=Fedora-Core"
  4. 0xbffff9d5:      "TERM=xterm"
  5. 0xbffff9e0:      "SHELL=/bin/bash"
  6. 0xbffff9f0:      "DESKTOP_STARTUP_ID="
  7. 0xbffffa04:      "HISTSIZE=1000"
  8. 0xbffffa12:      "CVSROOT=:pserver:xbx@172.16.64.229:/home/snixcvs"
  9. 0xbffffa43:      "GTK_RC_FILES=/etc/gtk/gtkrc:/home/rick/.gtkrc-1.2-gnome2"
  10. 0xbffffa7c:      "WINDOWID=39897253"
  11. 0xbffffa8e:      "QTDIR=/usr/lib/qt-3.3"
  12. 0xbffffaa4:      "USER=rick"
  13. 0xbffffaae:      "http_proxy=http://210.28.131.136:808/"
  14. 0xbffffad4:      "LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00"...
  15. 0xbffffb9c:      ";32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;3"...
  16. 0xbffffc64:      "5:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:"
  17. 0xbffffc97:      "SSH_AUTH_SOCK=/tmp/ssh-TzjWre4164/agent.4164"
  18. 0xbffffcc4:      "GNOME_KEYRING_SOCKET=/tmp/keyring-XwAZqD/socket"
  19. 0xbffffcf4:      "KDEDIR=/usr"
  20. 0xbffffd00:      "SESSION_MANAGER=local/Fedora-Core:/tmp/.ICE-unix/4135"
  21. 0xbffffd36:      "COLUMNS=100"
  22. (gdb)
  23. 0xbffffd42:      "PATH=/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/rick/bin:/home/rick/Program/eclipse/:/usr/local/Adobe/Acrobat7.0/bin/:/opt/slickedit/bin/"
  24. 0xbffffde5:      "DESKTOP_SESSION=default"
  25. 0xbffffdfd:      "MAIL=/var/spool/mail/rick"
  26. 0xbffffe17:      "_=/bin/bash"
  27. 0xbffffe23:      "PWD=/home/rick/test"
  28. 0xbffffe37:      "INPUTRC=/etc/inputrc"
  29. 0xbffffe4c:      "XMODIFIERS=@im=fcitx"
  30. 0xbffffe61:      "LANG=en_US.UTF-8"
  31. 0xbffffe72:      "LINES=35"
  32. 0xbffffe7b:      "GDMSESSION=default"
  33. 0xbffffe8e:      "SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass"
  34. 0xbffffec1:      "SHLVL=2"
  35. 0xbffffec9:      "HOME=/home/rick"
  36. 0xbffffed9:      "MOZ_ENABLE_PANG0=1"
  37. 0xbffffeec:      "GNOME_DESKTOP_SESSION_ID=Default"
  38. 0xbfffff0d:      "LOGNAME=rick"
  39. 0xbfffff1a:      "DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-rUJyoJSxJB"
  40. 0xbfffff56:      "LESSOPEN=|/usr/bin/lesspipe.sh %s"
  41. 0xbfffff78:      "DISPLAY=:0.0"
  42. 0xbfffff85:      "GTK_IM_MODULE=xim"
  43. (gdb)
  44. 0xbfffff97:      "G_BROKEN_FILENAMES=1"
  45. 0xbfffffac:      "XAUTHORITY=/home/rick/.Xauthority"
  46. 0xbfffffce:      "COLORTERM=gnome-terminal"
  47. 0xbfffffe7:      "/home/rick/test/test"
  48. 0xbffffffc:      ""
  49. 0xbffffffd:      ""
  50. 0xbffffffe:      ""
  51. 0xbfffffff:      ""
复制代码

嗯,确实都是环境变量,他们就紧跟在argv[0](如果有多个argv,自然也就会有argv[1],argv[2]...)的后面,一直到用户空间的尽头.
结束了吗? 等等,看下面:

  1. 0xbffff860:     0xbffffa04      0xbffffa12      0xbffffa43      0xbffffa7c
  2. 0xbffff870:     0xbffffa8e      0xbffffaa4      0xbffffaae      0xbffffad4
  3. 0xbffff880:     0xbffffc97      0xbffffcc4      0xbffffcf4      0xbffffd00
  4. 0xbffff890:     0xbffffd36      0xbffffd42      0xbffffde5      0xbffffdfd
  5. 0xbffff8a0:     0xbffffe17      0xbffffe23      0xbffffe37      0xbffffe4c
  6. 0xbffff8b0:     0xbffffe61      0xbffffe72      0xbffffe7b      0xbffffe8e
  7. 0xbffff8c0:     0xbffffec1      0xbffffec9      0xbffffed9      0xbffffeec
  8. 0xbffff8d0:     0xbfffff0d      0xbfffff1a      0xbfffff56      0xbfffff78
  9. 0xbffff8e0:     0xbfffff85      0xbfffff97      0xbfffffac      0xbfffffce
  10. 0xbffff8f0:     0x00000000      0x00000020      0xffffe400      0x00000021
复制代码

*env 到0xbffff8f0就结束了,但是argv[0]是从0xbffff998开始的,中间有一段数据是做什么的?我们先来看看这些数据:

  1. (gdb) x/40x 0xbffff8f4
  2. 0xbffff8f4:     0x00000020      0xffffe400      0x00000021      0xffffe000
  3. 0xbffff904:     0x00000010      0xbfebfbf7      0x00000006      0x00001000
  4. 0xbffff914:     0x00000011      0x00000064      0x00000003      0x08048034
  5. 0xbffff924:     0x00000004      0x00000020      0x00000005      0x00000007
  6. 0xbffff934:     0x00000007      0x00000000      0x00000008      0x00000000
  7. 0xbffff944:     0x00000009      0x0804828c      0x0000000b      0x000001f4
  8. 0xbffff954:     0x0000000c      0x000001f4      0x0000000d      0x000001f4
  9. 0xbffff964:     0x0000000e      0x000001f4      0x00000017      0x00000000
  10. 0xbffff974:     0x0000000f      0xbffff993      0x00000000      0x00000000
  11. 0xbffff984:     0x00000000      0x00000000      0x00000000      0x69000000
复制代码

呵呵,这一段可能没想到吧,其实这一段是动态连接器ld.so用的,从0xbffff8f4开始是一个数组,数组类型其实就是这么一个结构:

  1.     900 typedef struct
  2.     901 {
  3.     902   int a_type;           /* Entry type */
  4.     903   union
  5.     904     {
  6.     905       long int a_val;       /* Integer value */
  7.     906       void *a_ptr;      /* Pointer value */
  8.     907       void (*a_fcn) (void); /* Function pointer value */
  9.     908     } a_un;
  10.     909 } Elf32_auxv_t;
复制代码

其中type指出了其类型,后面是一个相关的值,下面列出部分类型:

  1.     922 /* Legal values for a_type (entry type).  */
  2.     923
  3.     924 #define AT_NULL     0       /* End of vector */
  4.     925 #define AT_IGNORE   1       /* Entry should be ignored */
  5.     926 #define AT_EXECFD   2       /* File descriptor of program */
  6.     927 #define AT_PHDR     3       /* Program headers for program */
  7.     928 #define AT_PHENT    4       /* Size of program header entry */
  8.     929 #define AT_PHNUM    5       /* Number of program headers */
  9.     930 #define AT_PAGESZ   6       /* System page size */
  10.     931 #define AT_BASE     7       /* Base address of interpreter */
  11.     932 #define AT_FLAGS    8       /* Flags */
  12.     933 #define AT_ENTRY    9       /* Entry point of program */
  13.     934 #define AT_NOTELF   10      /* Program is not ELF */
  14.     935 #define AT_UID      11      /* Real uid */
  15.     936 #define AT_EUID     12      /* Effective uid */
  16.     937 #define AT_GID      13      /* Real gid */
  17.     938 #define AT_EGID     14      /* Effective gid */
  18.     939 #define AT_CLKTCK   17      /* Frequency of times() */
  19.     940
  20.     941 /* Some more special a_type values describing the hardware.  */
  21.     942 #define AT_PLATFORM 15      /* String identifying platform.  */
  22.     943 #define AT_HWCAP    16      /* Machine dependent hints about
  23.     944                        processor capabilities.  */
  24.     945
  25.     946 /* This entry gives some information about the FPU initialization
  26.     947    performed by the kernel.  */
  27.     948 #define AT_FPUCW    18      /* Used FPU control word.  */
  28.     949
  29.     950 /* Cache block sizes.  */
  30.     951 #define AT_DCACHEBSIZE  19      /* Data cache block size.  */
  31.     952 #define AT_ICACHEBSIZE  20      /* Instruction cache block size.  */
  32.     953 #define AT_UCACHEBSIZE  21      /* Unified cache block size.  */
复制代码

这些都是当前的可执行文件有关的信息
好了,在这个向量后面就是我们的参数字符串了.
好像还有一个问题没有解释,就是从 &env 到了0xbffff844 之间是些什么东西,好,我们再看一遍:

  1. (gdb) p &env
  2. $2 = (char ***) 0xbffff7c8
  3. (gdb) x/40x 0xbffff7cc
  4. 0xbffff7cc:     0x00931ab6      0x00a65ff4      0x00000000      0xbffff7d0
  5. 0xbffff7dc:     0xbffff818      0xbffff7c0      0x00957df5      0x00000000
  6. 0xbffff7ec:     0x00000000      0x00000000      0x0093bfb4      0x00000001
  7. 0xbffff7fc:     0x0804828c      0x00000000      0x009319f0      0x00932340
  8. 0xbffff80c:     0x0093bfb4      0x00000001      0x0804828c      0x00000000
  9. 0xbffff81c:     0x080482ad      0x08048334      0x00000001      0xbffff844
  10. 0xbffff82c:     0x08048358      0x080483ac      0x00932340      0xbffff83c
  11. 0xbffff83c:     0x00938e31      0x00000001      0xbffff998      0x00000000
  12. 0xbffff84c:     0xbffff9ad      0xbffff9c0      0xbffff9d5      0xbffff9e0
  13. 0xbffff85c:     0xbffff9f0      0xbffffa04      0xbffffa12      0xbffffa43
复制代码

关于这个,我们有必要提醒一下,事实上 c 程序的入口并不是main,而是 _start,这样说,明白了多少?呵呵,我的意思是,我们要看看这个神秘的 _start 究竟是什么玩意,

  1.      62 _start:
  2.      63     /* Clear the frame pointer.  The ABI suggests this be done, to mark
  3.      64        the outermost frame obviously.  */
  4.      65     xorl %ebp, %ebp
  5.      66
  6.      67     /* Extract the arguments as encoded on the stack and set up
  7.      68        the arguments for `main': argc, argv.  envp will be determined
  8.      69        later in __libc_start_main.  */
  9.      70     popl %esi       /* Pop the argument count.  */
  10.      71     movl %esp, %ecx     /* argv starts just at the current stack top.*/
  11.      72
  12.      73     /* Before pushing the arguments align the stack to a 16-byte
  13.      74     (SSE needs 16-byte alignment) boundary to avoid penalties from
  14.      75     misaligned accesses.  Thanks to Edward Seidl <seidl@janed.com>
  15.      76     for pointing this out.  */
  16.      77     andl $0xfffffff0, %esp
  17.      78     pushl %eax      /* Push garbage because we allocate
  18.      79                    28 more bytes.  */
  19.      80
  20.      81     /* Provide the highest stack address to the user code (for stacks
  21.      82        which grow downwards).  */
  22.      83     pushl %esp
  23.      84
  24.      85     pushl %edx      /* Push address of the shared library
  25.      86                    termination function.  */
  26.      87
  27.      88 #ifdef SHARED
  28.      89     /* Load PIC register.  */
  29.      90     call 1f
  30.      91     addl $_GLOBAL_OFFSET_TABLE_, %ebx
  31.      92
  32.      93     /* Push address of our own entry points to .fini and .init.  */
  33.      94     leal __libc_csu_fini@GOTOFF(%ebx), %eax
  34.      95     pushl %eax
  35.      96     leal __libc_csu_init@GOTOFF(%ebx), %eax
  36.      97     pushl %eax
  37.      98
  38.      99     pushl %ecx      /* Push second argument: argv.  */
  39.     100     pushl %esi      /* Push first argument: argc.  */
  40.     101
  41.     102     pushl BP_SYM (main)@GOT(%ebx)
  42.     103
  43.     104     /* Call the user's main function, and exit with its value.
  44.     105        But let the libc call main.    */
  45.     106     call BP_SYM (__libc_start_main)@PLT
  46.     107 #else
  47.     108     /* Push address of our own entry points to .fini and .init.  */
  48.     109     pushl $__libc_csu_fini
  49.     110     pushl $__libc_csu_init
  50.     111
  51.     112     pushl %ecx      /* Push second argument: argv.  */
  52.     113     pushl %esi      /* Push first argument: argc.  */
  53.     114
  54.     115     pushl $BP_SYM (main)
  55.     116
  56.     117     /* Call the user's main function, and exit with its value.
  57.     118        But let the libc call main.    */
  58.     119     call BP_SYM (__libc_start_main)
复制代码

好的,就看这么多吧,至于要看写什么,我就不多说了,注释已经很详细了,栈里面从 0xbffff820 到 0xbffff840 之间的数据就是这个时候进栈的,在调用 __libc_start_main 之前,栈里面最后压入的事实上就是我们自己写的main函数的地址,他将在 __libc_start_main 函数中被调用.
so,形势就明朗了,既然 main 是在 __libc_start_main 中调用的,那么 0xbffff7cc 到 0xbffff814之间都是 __libc_start_main 的局部变量了.呵呵,为什么是 0xbffff814? 那 0xbffff818 和 0xbffff81c 呢? 这个,就留给读者自己了.

以上实验在 fc3 上进行,内核使用 2.6.10,  gcc 3.4.2,  glibc 2.3.5代码
发表于 2005-8-27 17:46:07 | 显示全部楼层
高,实在是高.
偶的msn enjoylinux [at] hotmail.com
有空交流交流啊.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-28 08:25:25 | 显示全部楼层
我的msn一直都挂着,只是一般不聊,如果同时在线,不打招呼似乎又不大好
如果你还是想加的话,决定权给你吧 ^_^
xu_baoxi@hotmail.com
回复 支持 反对

使用道具 举报

发表于 2005-8-28 22:41:26 | 显示全部楼层
总结得不错!加精啦!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-29 08:39:17 | 显示全部楼层
嘻嘻,就是冲着为精华区填砖加瓦的目标写的
回复 支持 反对

使用道具 举报

发表于 2009-3-13 13:49:47 | 显示全部楼层
怎么我 查看不了 argc 的地址啊???
--------------------------------------
mojian@mojian:~/c_test$ gcc -Wall -g test_main.c  -o test_main
                                                                |mojian@mojian:~/c_test$ gdb -q test_main
                                                                |(gdb) l
                                                                |1       int main(int argc,char **argv,char **env)
                                                                |2       {
                                                                |3         return 0;
                                                                |4       }
                                                                |5
                                                                |(gdb) b 3
                                                                |Breakpoint 1 at 0x8048382: file test_main.c, line 3.
                                                                |(gdb) r
                                                                |Starting program: /home/mojian/c_test/test_main
                                                                |
                                                                |Breakpoint 1, main () at test_main.c:3
                                                                |3         return 0;
                                                                |(gdb)  p &argc
                                                                |Can't take address of "argc" which isn't an lvalue.
                                                                |(gdb)
回复 支持 反对

使用道具 举报

发表于 2009-3-13 13:50:31 | 显示全部楼层
怎么我 查看不了 argc 的地址啊???
--------------------------------------
mojian@mojian:~/c_test$ gcc -Wall -g test_main.c  -o test_main
                                                                |mojian@mojian:~/c_test$ gdb -q test_main
                                                                |(gdb) l
                                                                |1       int main(int argc,char **argv,char **env)
                                                                |2       {
                                                                |3         return 0;
                                                                |4       }
                                                                |5
                                                                |(gdb) b 3
                                                                |Breakpoint 1 at 0x8048382: file test_main.c, line 3.
                                                                |(gdb) r
                                                                |Starting program: /home/mojian/c_test/test_main
                                                                |
                                                                |Breakpoint 1, main () at test_main.c:3
                                                                |3         return 0;
                                                                |(gdb)  p &argc
                                                                |Can't take address of "argc" which isn't an lvalue.
                                                                |(gdb)
回复 支持 反对

使用道具 举报

发表于 2009-3-13 13:56:28 | 显示全部楼层
debian 5.0
gcc 4.3.2 ---debian
gdb 6.8  ---debian
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表