- root@libin:~/program/assembly/getenv# gcc -v
-
Using built-in specs.
-
Target: i486-linux-gnu
-
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
-
Thread model: posix
-
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
-
root@libin:~/program/assembly/getenv# uname -ar
-
Linux libin 2.6.32-26-generic #48-Ubuntu SMP Wed Nov 24 09:00:03 UTC 2010 i686 GNU/Linux
当时Richard Blum大师给出了这么一个图,当时我感觉十分技痒,才决定照抄代码,验证一把。事实证明,这张图是有问题的。这是一个错误的图片。
首先我对C语言要比汇编熟悉,所以先用C语言查看了下argc argv 环境变量的地址,代码如下:
- #include<stdio.h>
-
-
-
int main(int argc,char* argv[],char** envp)
-
{
-
printf("the address of para num:is %lx\n",&argc);
- printf("the address of argv is %lx\n",&argv);
-
printf("the address of environ is %lx\n",&envp);
-
printf("the value of argv is %lx\n",argv);
- printf("the value of environ is %lx\n",envp);
-
- return 0;
-
}
我用debug跟踪了下:
- (gdb) b 6
-
Breakpoint 1 at 0x80483ed: file test.c, line 6.
-
(gdb) r 1 8
-
Starting program: /home/libin/program/assembly/getenv/test 1 8
-
-
Breakpoint 1, main (argc=3, argv=0xbffff7d4, envp=0xbffff7e4) at test.c:6
-
6 printf("the address of para num:is %lx\n",&argc);
-
(gdb) p $ebp
-
$1 = (void *) 0xbffff728
-
(gdb) p &argc
-
$2 = (int *) 0xbffff730
-
(gdb) p &argv
-
$3 = (char ***) 0xbffff734
-
(gdb) p &envp
-
$4 = (char ***) 0xbffff738
我们可以看到,argc所在的地址和argv的地址 环境变量全局指针对应的地址相邻的。
- &argc = 0xbffff730
-
&argv = 0xbffff734
-
&envp = 0xbffff738
接下来我们对argv 和envp的值比较感兴趣,我们可以查看下这片数据区存的是什么:
- (gdb) x/20x 0xbffff730
-
0xbffff730: 0x00000003 0xbffff7d4 0xbffff7e4 0xb7fff858
-
0xbffff740: 0xbffff790 0xffffffff 0x0012bff4 0x08048254
-
0xbffff750: 0x00000001 0xbffff790 0x0011d626 0x0012cab0
-
0xbffff760: 0xb7fffb48 0x00283ff4 0x00000000 0x00000000
-
0xbffff770: 0xbffff7a8 0xb84014ed 0x6f394392 0x00000000
我们看到,0xbffff730开始的这块数据区,第一个值ox00000003就是argc的值,考虑到我的程序是 r 1 8,的的确确是3个入参。这片数据区告诉我们以下信息:
- argc = 0x00000003
-
argv = 0xbffff7d4
-
envp = 0xbffff7e4
接下来我们兵分两路,分别查看argv 和envp指向的数据区。
第一路人马,观察 argv指向的数据区:
- (gdb) x/20x 0xbffff7d4
-
0xbffff7d4: 0xbffff90a 0xbffff933 0xbffff935 0x00000000
-
0xbffff7e4: 0xbffff937 0xbffff942 0xbffff952 0xbffff95c
-
0xbffff7f4: 0xbffffdfd 0xbffffe0d 0xbffffe1b 0xbffffe29
-
0xbffff804: 0xbffffe35 0xbffffe86 0xbffffe95 0xbffffebd
-
0xbffff814: 0xbffffece 0xbffffed7 0xbffffee8 0xbffffeff
我们知道,argv是指针的指针,它指向的内容是指针,指针的个数由argc决定。OK,argc = 3,所以蓝颜色的三个值,是3个指针,这三个指针指向的数据区才是真真正正的数据,即 入参字符串。
我们分别看下这三个指针指向的字符串分别是啥.
- (gdb) x/s 0xbffff90a
-
0xbffff90a: "/home/libin/program/assembly/getenv/test"
-
(gdb) x/s 0xbffff933
-
0xbffff933: "1"
-
(gdb) x/s 0xbffff935
-
0xbffff935: "8"
啥也不说了,眼泪汪汪的,终于见到我们要找的入参了。第一个参数是我的可执行程序,第二个参数是1,第三个参数是8。
兵分两路,第一路人马已经胜利,下面看第二路人马,查看环境变量。
envp = 0xbffff7e4
- (gdb) x/40x 0xbffff7e4
-
0xbffff7e4: 0xbffff937 0xbffff942 0xbffff952 0xbffff95c
-
0xbffff7f4: 0xbffffdfd 0xbffffe0d 0xbffffe1b 0xbffffe29
-
0xbffff804: 0xbffffe35 0xbffffe86 0xbffffe95 0xbffffebd
-
0xbffff814: 0xbffffece 0xbffffed7 0xbffffee8 0xbffffeff
-
0xbffff824: 0xbfffff07 0xbfffff19 0xbfffff26 0xbfffff46
-
0xbffff834: 0xbfffff53 0xbfffff61 0xbfffff83 0xbfffffba
-
0xbffff844: 0x00000000 0x00000020 0x0012d420 0x00000021
-
0xbffff854: 0x0012d000 0x00000010 0xbfebf3ff 0x00000006
-
0xbffff864: 0x00001000 0x00000011 0x00000064 0x00000003
-
0xbffff874: 0x08048034 0x00000004 0x00000020 0x00000005
环境变量envp也是指针的指针,所以这篇数据区蓝色的部分,都是指针,这些蓝色的指针,指向的就是我们的环境变量字符串。
- (gdb) x/s 0xbffff937
-
0xbffff937: "TERM=xterm"
-
(gdb) x/s 0xbffff942
-
0xbffff942: "SHELL=/bin/bash"
-
(gdb) x/s 0xbffff952
-
0xbffff952: "USER=root"
啥也不说了,我就不一一列举每个环境变量了,毕竟咱不是古龙那厮,靠行数来赚稿费。需要说明的地方是argv指向的三个蓝色指针之后面,有0x00000000,这个之后就是环境变量那一排蓝色指针了。参见上面那张图。
如果你跟着我一步步走下来,你也就明白Richard Blum 绘制的那张图有什么问题了。本来想绘制个图,今天有其他的学习任务,所以就不画图了,各位看官见谅。
下面给出通过修改后的,汇编获取环境变量的代码:
- # getenv list system environment variables
-
-
.section .data
-
output:
-
.asciz "%s\n"
-
.section .text
-
.globl main
-
main:
- movl %esp,%ebp
-
addl $12,%ebp
-
movl (%ebp),%ebx
-
loop1:
- cmpl $0,(%ebx)
-
je endls
-
pushl (%ebx)
-
pushl $output
-
call printf
-
addl $12,%esp
-
addl $4,%ebx
-
loop loop1
-
endls:
- pushl $0
-
call exit
编译执行结果如下:
- root@libin:~/program/assembly/getenv# gcc -o getenv getenv.s
-
root@libin:~/program/assembly/getenv# ./getenv
-
SHELL=/bin/bash
-
TERM=xterm
-
USER=root
-
LS_COLORS=rs=0:di=01;34:ln=01;36:hl=44;37:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
-
SUDO_USER=libin
-
SUDO_UID=1000
-
USERNAME=root
-
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin
-
PWD=/home/libin/program/assembly/getenv
-
LANG=zh_CN.UTF-8
-
SHLVL=1
-
SUDO_COMMAND=/bin/bash
-
HOME=/home/libin
-
LANGUAGE=zh_CN:en
-
LOGNAME=root
-
LESSOPEN=| /usr/bin/lesspipe %s
-
SUDO_GID=1000
-
DISPLAY=:0.0
-
LESSCLOSE=/usr/bin/lesspipe %s %s
-
COLORTERM=gnome-terminal
-
XAUTHORITY=/var/run/gdm/auth-for-libin-ckEkiQ/database
-
OLDPWD=/home/libin/program/assembly
-
_=./getenv
P.S. 本文绝没有对Richard Blum 大师有一丝不敬的意思,恰恰相反,Richard Blum老爷子是我崇拜的偶 像。