|
在main函数执行之前,用户栈里面已经有了一些数据,那么这些数据究竟是什么呢? 下面我们就来简单看一下:
我们得有一个最简单的程序,嗯,一个没有不输出hello world的hello world程序:
- [rick@Fedora-Core test]$ cat test.c
- int main(int argc,char**argv,char**env)
- {
- return 0;
- }
复制代码
下面我们就来调试他:
- [rick@Fedora-Core test]$ gcc -g test.c -o test
- [rick@Fedora-Core test]$ gdb -q test
- Using host libthread_db library "/lib/tls/libthread_db.so.1".
- (gdb) l
- 1 int main(int argc,char**argv,char**env)
- 2 {
- 3 return 0;
- 4 }
- (gdb) b 3
- Breakpoint 1 at 0x8048350: file test.c, line 3.
- (gdb) r
- Starting program: /home/rick/test/test
- Reading symbols from shared object read from target memory...done.
- Loaded system supplied DSO at 0xffffe000
- Breakpoint 1, main (argc=1, argv=0xbffff844, env=0xbffff84c) at test.c:3
- 3 return 0;
- (gdb) p &argc
- $1 = (int *) 0xbffff7c0
复制代码
好的,我们得到了main函数第一个参数所在的栈指针 0xbffff7c0,现在就来看看在这个地方有什么:
- (gdb) x/40x 0xbffff7c0
- 0xbffff7c0: 0x00000001 0xbffff844 0xbffff84c 0x00931ab6
- 0xbffff7d0: 0x00a65ff4 0x00000000 0xbffff7d0 0xbffff818
- 0xbffff7e0: 0xbffff7c0 0x00957df5 0x00000000 0x00000000
- 0xbffff7f0: 0x00000000 0x0093bfb4 0x00000001 0x0804828c
- 0xbffff800: 0x00000000 0x009319f0 0x00932340 0x0093bfb4
- 0xbffff810: 0x00000001 0x0804828c 0x00000000 0x080482ad
- 0xbffff820: 0x08048334 0x00000001 0xbffff844 0x08048358
- 0xbffff830: 0x080483ac 0x00932340 0xbffff83c 0x00938e31
- 0xbffff840: 0x00000001 0xbffff998 0x00000000 0xbffff9ad
- 0xbffff850: 0xbffff9c0 0xbffff9d5 0xbffff9e0 0xbffff9f0
复制代码
ok,我们看到:0xbffff844就是argv,0xbffff84c就是env,但是后面是什么呢? 这个问题留待后面解释,再看0xbffff844的地方是 0xbffff998,0x00000000,很明显,这是argv[0]所在的地址,我们来看看:
- (gdb) x/s 0xbffff998
- 0xbffff998: "/home/rick/test/test"
复制代码
嗯,没错.
接着就是env,他指向的地方(0xbffff84c)是0xbffff9ad,那自然这里就是env[0]咯,那我们知道环境变量会有很多,所以从0xbffff84c开始一直到0x00000000都是环境变量,嗯,我们来证实一下:
- (gdb) x/20s 0xbffff9ad
- 0xbffff9ad: "SSH_AGENT_PID=4166"
- 0xbffff9c0: "HOSTNAME=Fedora-Core"
- 0xbffff9d5: "TERM=xterm"
- 0xbffff9e0: "SHELL=/bin/bash"
- 0xbffff9f0: "DESKTOP_STARTUP_ID="
- 0xbffffa04: "HISTSIZE=1000"
- 0xbffffa12: "CVSROOT=:pserver:xbx@172.16.64.229:/home/snixcvs"
- 0xbffffa43: "GTK_RC_FILES=/etc/gtk/gtkrc:/home/rick/.gtkrc-1.2-gnome2"
- 0xbffffa7c: "WINDOWID=39897253"
- 0xbffffa8e: "QTDIR=/usr/lib/qt-3.3"
- 0xbffffaa4: "USER=rick"
- 0xbffffaae: "http_proxy=http://210.28.131.136:808/"
- 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"...
- 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"...
- 0xbffffc64: "5:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:"
- 0xbffffc97: "SSH_AUTH_SOCK=/tmp/ssh-TzjWre4164/agent.4164"
- 0xbffffcc4: "GNOME_KEYRING_SOCKET=/tmp/keyring-XwAZqD/socket"
- 0xbffffcf4: "KDEDIR=/usr"
- 0xbffffd00: "SESSION_MANAGER=local/Fedora-Core:/tmp/.ICE-unix/4135"
- 0xbffffd36: "COLUMNS=100"
- (gdb)
- 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/"
- 0xbffffde5: "DESKTOP_SESSION=default"
- 0xbffffdfd: "MAIL=/var/spool/mail/rick"
- 0xbffffe17: "_=/bin/bash"
- 0xbffffe23: "PWD=/home/rick/test"
- 0xbffffe37: "INPUTRC=/etc/inputrc"
- 0xbffffe4c: "XMODIFIERS=@im=fcitx"
- 0xbffffe61: "LANG=en_US.UTF-8"
- 0xbffffe72: "LINES=35"
- 0xbffffe7b: "GDMSESSION=default"
- 0xbffffe8e: "SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass"
- 0xbffffec1: "SHLVL=2"
- 0xbffffec9: "HOME=/home/rick"
- 0xbffffed9: "MOZ_ENABLE_PANG0=1"
- 0xbffffeec: "GNOME_DESKTOP_SESSION_ID=Default"
- 0xbfffff0d: "LOGNAME=rick"
- 0xbfffff1a: "DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-rUJyoJSxJB"
- 0xbfffff56: "LESSOPEN=|/usr/bin/lesspipe.sh %s"
- 0xbfffff78: "DISPLAY=:0.0"
- 0xbfffff85: "GTK_IM_MODULE=xim"
- (gdb)
- 0xbfffff97: "G_BROKEN_FILENAMES=1"
- 0xbfffffac: "XAUTHORITY=/home/rick/.Xauthority"
- 0xbfffffce: "COLORTERM=gnome-terminal"
- 0xbfffffe7: "/home/rick/test/test"
- 0xbffffffc: ""
- 0xbffffffd: ""
- 0xbffffffe: ""
- 0xbfffffff: ""
复制代码
嗯,确实都是环境变量,他们就紧跟在argv[0](如果有多个argv,自然也就会有argv[1],argv[2]...)的后面,一直到用户空间的尽头.
结束了吗? 等等,看下面:
- 0xbffff860: 0xbffffa04 0xbffffa12 0xbffffa43 0xbffffa7c
- 0xbffff870: 0xbffffa8e 0xbffffaa4 0xbffffaae 0xbffffad4
- 0xbffff880: 0xbffffc97 0xbffffcc4 0xbffffcf4 0xbffffd00
- 0xbffff890: 0xbffffd36 0xbffffd42 0xbffffde5 0xbffffdfd
- 0xbffff8a0: 0xbffffe17 0xbffffe23 0xbffffe37 0xbffffe4c
- 0xbffff8b0: 0xbffffe61 0xbffffe72 0xbffffe7b 0xbffffe8e
- 0xbffff8c0: 0xbffffec1 0xbffffec9 0xbffffed9 0xbffffeec
- 0xbffff8d0: 0xbfffff0d 0xbfffff1a 0xbfffff56 0xbfffff78
- 0xbffff8e0: 0xbfffff85 0xbfffff97 0xbfffffac 0xbfffffce
- 0xbffff8f0: 0x00000000 0x00000020 0xffffe400 0x00000021
复制代码
*env 到0xbffff8f0就结束了,但是argv[0]是从0xbffff998开始的,中间有一段数据是做什么的?我们先来看看这些数据:
- (gdb) x/40x 0xbffff8f4
- 0xbffff8f4: 0x00000020 0xffffe400 0x00000021 0xffffe000
- 0xbffff904: 0x00000010 0xbfebfbf7 0x00000006 0x00001000
- 0xbffff914: 0x00000011 0x00000064 0x00000003 0x08048034
- 0xbffff924: 0x00000004 0x00000020 0x00000005 0x00000007
- 0xbffff934: 0x00000007 0x00000000 0x00000008 0x00000000
- 0xbffff944: 0x00000009 0x0804828c 0x0000000b 0x000001f4
- 0xbffff954: 0x0000000c 0x000001f4 0x0000000d 0x000001f4
- 0xbffff964: 0x0000000e 0x000001f4 0x00000017 0x00000000
- 0xbffff974: 0x0000000f 0xbffff993 0x00000000 0x00000000
- 0xbffff984: 0x00000000 0x00000000 0x00000000 0x69000000
复制代码
呵呵,这一段可能没想到吧,其实这一段是动态连接器ld.so用的,从0xbffff8f4开始是一个数组,数组类型其实就是这么一个结构:
- 900 typedef struct
- 901 {
- 902 int a_type; /* Entry type */
- 903 union
- 904 {
- 905 long int a_val; /* Integer value */
- 906 void *a_ptr; /* Pointer value */
- 907 void (*a_fcn) (void); /* Function pointer value */
- 908 } a_un;
- 909 } Elf32_auxv_t;
复制代码
其中type指出了其类型,后面是一个相关的值,下面列出部分类型:
- 922 /* Legal values for a_type (entry type). */
- 923
- 924 #define AT_NULL 0 /* End of vector */
- 925 #define AT_IGNORE 1 /* Entry should be ignored */
- 926 #define AT_EXECFD 2 /* File descriptor of program */
- 927 #define AT_PHDR 3 /* Program headers for program */
- 928 #define AT_PHENT 4 /* Size of program header entry */
- 929 #define AT_PHNUM 5 /* Number of program headers */
- 930 #define AT_PAGESZ 6 /* System page size */
- 931 #define AT_BASE 7 /* Base address of interpreter */
- 932 #define AT_FLAGS 8 /* Flags */
- 933 #define AT_ENTRY 9 /* Entry point of program */
- 934 #define AT_NOTELF 10 /* Program is not ELF */
- 935 #define AT_UID 11 /* Real uid */
- 936 #define AT_EUID 12 /* Effective uid */
- 937 #define AT_GID 13 /* Real gid */
- 938 #define AT_EGID 14 /* Effective gid */
- 939 #define AT_CLKTCK 17 /* Frequency of times() */
- 940
- 941 /* Some more special a_type values describing the hardware. */
- 942 #define AT_PLATFORM 15 /* String identifying platform. */
- 943 #define AT_HWCAP 16 /* Machine dependent hints about
- 944 processor capabilities. */
- 945
- 946 /* This entry gives some information about the FPU initialization
- 947 performed by the kernel. */
- 948 #define AT_FPUCW 18 /* Used FPU control word. */
- 949
- 950 /* Cache block sizes. */
- 951 #define AT_DCACHEBSIZE 19 /* Data cache block size. */
- 952 #define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
- 953 #define AT_UCACHEBSIZE 21 /* Unified cache block size. */
复制代码
这些都是当前的可执行文件有关的信息
好了,在这个向量后面就是我们的参数字符串了.
好像还有一个问题没有解释,就是从 &env 到了0xbffff844 之间是些什么东西,好,我们再看一遍:
- (gdb) p &env
- $2 = (char ***) 0xbffff7c8
- (gdb) x/40x 0xbffff7cc
- 0xbffff7cc: 0x00931ab6 0x00a65ff4 0x00000000 0xbffff7d0
- 0xbffff7dc: 0xbffff818 0xbffff7c0 0x00957df5 0x00000000
- 0xbffff7ec: 0x00000000 0x00000000 0x0093bfb4 0x00000001
- 0xbffff7fc: 0x0804828c 0x00000000 0x009319f0 0x00932340
- 0xbffff80c: 0x0093bfb4 0x00000001 0x0804828c 0x00000000
- 0xbffff81c: 0x080482ad 0x08048334 0x00000001 0xbffff844
- 0xbffff82c: 0x08048358 0x080483ac 0x00932340 0xbffff83c
- 0xbffff83c: 0x00938e31 0x00000001 0xbffff998 0x00000000
- 0xbffff84c: 0xbffff9ad 0xbffff9c0 0xbffff9d5 0xbffff9e0
- 0xbffff85c: 0xbffff9f0 0xbffffa04 0xbffffa12 0xbffffa43
复制代码
关于这个,我们有必要提醒一下,事实上 c 程序的入口并不是main,而是 _start,这样说,明白了多少?呵呵,我的意思是,我们要看看这个神秘的 _start 究竟是什么玩意,
- 62 _start:
- 63 /* Clear the frame pointer. The ABI suggests this be done, to mark
- 64 the outermost frame obviously. */
- 65 xorl %ebp, %ebp
- 66
- 67 /* Extract the arguments as encoded on the stack and set up
- 68 the arguments for `main': argc, argv. envp will be determined
- 69 later in __libc_start_main. */
- 70 popl %esi /* Pop the argument count. */
- 71 movl %esp, %ecx /* argv starts just at the current stack top.*/
- 72
- 73 /* Before pushing the arguments align the stack to a 16-byte
- 74 (SSE needs 16-byte alignment) boundary to avoid penalties from
- 75 misaligned accesses. Thanks to Edward Seidl <seidl@janed.com>
- 76 for pointing this out. */
- 77 andl $0xfffffff0, %esp
- 78 pushl %eax /* Push garbage because we allocate
- 79 28 more bytes. */
- 80
- 81 /* Provide the highest stack address to the user code (for stacks
- 82 which grow downwards). */
- 83 pushl %esp
- 84
- 85 pushl %edx /* Push address of the shared library
- 86 termination function. */
- 87
- 88 #ifdef SHARED
- 89 /* Load PIC register. */
- 90 call 1f
- 91 addl $_GLOBAL_OFFSET_TABLE_, %ebx
- 92
- 93 /* Push address of our own entry points to .fini and .init. */
- 94 leal __libc_csu_fini@GOTOFF(%ebx), %eax
- 95 pushl %eax
- 96 leal __libc_csu_init@GOTOFF(%ebx), %eax
- 97 pushl %eax
- 98
- 99 pushl %ecx /* Push second argument: argv. */
- 100 pushl %esi /* Push first argument: argc. */
- 101
- 102 pushl BP_SYM (main)@GOT(%ebx)
- 103
- 104 /* Call the user's main function, and exit with its value.
- 105 But let the libc call main. */
- 106 call BP_SYM (__libc_start_main)@PLT
- 107 #else
- 108 /* Push address of our own entry points to .fini and .init. */
- 109 pushl $__libc_csu_fini
- 110 pushl $__libc_csu_init
- 111
- 112 pushl %ecx /* Push second argument: argv. */
- 113 pushl %esi /* Push first argument: argc. */
- 114
- 115 pushl $BP_SYM (main)
- 116
- 117 /* Call the user's main function, and exit with its value.
- 118 But let the libc call main. */
- 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代码 |
|