前面的程序显示了程序的命令行参数,在程序的栈中,在命令行参数字符指针的上面就是保存的环境变量的字符串指针,以下程序用于显示环境变量:
SECTION .data ; Section containing initialised data
ErrMsg db "Terminated with error.",10
ERRLEN equ $-ErrMsg
SECTION .bss ; Section containing uninitialized data
MAXVARS equ 300
VarLens: resd MAXVARS ; Table of argument lengths
SECTION .text ; Section containing code
global _start ; Linker needs this to find the entry point!
_start:
nop ; This no-op keeps gdb happy...
mov ebp,esp ; Save the initial stack pointer in EBP
xor eax,eax ; Searching for 0, so clear EAX to 0
xor esi,esi ; Start (argumentsfor table addressing reasons) at 0
FindEnv:
mov ecx,0000ffffh ; Limit search to 65535 bytes max
mov edi,ebp ; Put address of string to search in EDI
mov edx,edi ; Copy starting address into EDX
cld ; Set search direction to up-memory
repne scasd ; Search for null ptr in string at edi
jnz Error ; REPNE SCASB ended without finding AL
mov ebp,edi ; Env var addrs begin after null ptr
xor ebx,ebx ; Zero EBX for use as addr counter
; We now have the address of the first env var address in EBP. Now we scan
; through them and determine their lengths:
ScanOne:
mov ecx,0000ffffh ; Limit search to 65535 bytes max
mov edi,dword [ebp+ebx*4] ; Put address of string to search in EDI
cmp edi,0 ; See if we hit the second null ptr
je Showem ; If so, we've scanned em all, so show 'em
mov edx,edi ; Copy starting address into EDX
cld ; Set search direction to up-memory
repne scasb ; Search for null (0 char) in string at edi
jnz Error ; REPNE SCASB ended without finding AL
mov byte [edi-1],10 ; Store an EOL where the null used to be
sub edi,edx ; Subtract position of 0 from start address
mov dword [VarLens+ebx*4],edi ; Put length of var into table
inc ebx ; Add 1 to environment variable counter
jmp ScanOne ; If not, loop back and do another one
; Display all environment variables to stdout:
Showem:
mov ecx,[ebp+esi*4] ; Pass offset of the environment var
mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 1: Standard Output
mov edx,[VarLens+esi*4] ; Pass the length of the message
int 80H ; Make kernel call
inc esi ; Increment the env var counter
cmp dword [ebp+esi*4],0 ; See if we've displayed all the variables
jne Showem ; If not, loop back and do another
jmp Exit ; We're done! Let's pack it in!
Error: mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 2: Standard Error
mov ecx,ErrMsg ; Pass offset of the error message
mov edx,ERRLEN ; Pass the length of the message
int 80H ; Make kernel call
Exit: mov eax,1 ; Code for Exit Syscall
mov ebx,0 ; Return a code of zero
int 80H ; Make kernel call
程序分析:
mov ebp,esp //ebp=esp,保存当前的栈指针
xor eax,eax //eax=0
xor esi,esi //esi=0
FindEnv:
mov ecx,0000ffffh //ecx=65535
mov edi,ebp //edi=ebp,ebp指向的栈位置存放命令行参数个数,在此上方依次保存命令行参数字符串的地址,然后是空指针,接下来就是环境变量字符串的地址。
mov edx,edi //edx=edi
cld //清除DF标志位,控制字符地址扫描方向由低到高
repne scasd //按照4字节进行扫描,直到找到0(eax里面保存的是0)。
jnz Error //如果ZF标志位未设置,说明循环结束是因为ecx=0,对于这种异常情况,跳转到Error
mov ebp,edi //ebp=edi,edi此时指向空指针上方的栈的位置,因此是环境变量字符串指针数组的起始位置
xor ebx,ebx //ebx清零
ScanOne:
mov ecx,0000ffffh //ecx=65535
mov edi,dword [ebp+ebx*4] //把环境变量字符串地址装入edi
cmp edi,0 //如果环境变量字符串地址是0,说明环境变量字符串数组已经到末尾了
je Showem //如果是0,退出循环,跳转到Showem,显示环境变量
mov edx,edi //edx=edi,保存edi值
cld //清除DF标志位,控制字符地址扫描方向由低到高
repne scasb //扫描字符串,直到遇到0字符
jnz Error //如果ZF标志位未设置,说明循环结束是因为ecx=0,对于这种异常情况,跳转到Error
mov byte [edi-1],10 //把0字符换成换行符
sub edi,edx //获取字符串的长度,长度包括换行符
mov dword [VarLens+ebx*4],edi //保存长度到VarLens数组
inc ebx //ebx=ebx+1
jmp ScanOne //继续循环
Showem:
mov ecx,[ebp+esi*4] //ecx=环境变量字符串地址
mov eax,4
mov ebx,1
mov edx,[VarLens+esi*4] //edx=字符串长度
int 80H
inc esi //esi=esi+1
cmp dword [ebp+esi*4],0 //比较环境变量字符串地址和0
jne Showem //如果不等于0继续循环
jmp Exit
makefile文件内容:
showenv2: showenv2.o
ld -o showenv2 showenv2.o
showenv2.o: showenv2.asm
nasm -f elf -g -F stabs showenv2.asm -l showenv2.lst
测试:
[root@bogon showenv2]# make
nasm -f elf -g -F stabs showenv2.asm -l showenv2.lst
ld -o showenv2 showenv2.o
[root@bogon showenv2]# ./showenv2
SSH_AGENT_PID=4280
HOSTNAME=bogon
DESKTOP_STARTUP_ID=
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=27263057
OLDPWD=/asm/asmsbs3e/chapter11
USER=root
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;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;35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:
GNOME_KEYRING_SOCKET=/tmp/keyring-lzg8jE/socket
SSH_AUTH_SOCK=/tmp/ssh-NHPLxk4244/agent.4244
SESSION_MANAGER=local/bogon:/tmp/.ICE-unix/4244
MAIL=/var/spool/mail/root
PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
DESKTOP_SESSION=default
GDM_XSERVER_LOCATION=local
INPUTRC=/etc/inputrc
PWD=/asm/asmsbs3e/chapter11/showenv2
XMODIFIERS=@im=none
LANG=en_US.UTF-8
GDMSESSION=default
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHLVL=2
HOME=/root
GNOME_DESKTOP_SESSION_ID=Default
LOGNAME=root
CVS_RSH=ssh
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-huZ90bmSif,guid=c3258403d8a90dafebfc230054b253dd
LESSOPEN=|/usr/bin/lesspipe.sh %s
DISPLAY=:0.0
G_BROKEN_FILENAMES=1
COLORTERM=gnome-terminal
XAUTHORITY=/tmp/.gdmFABESX
_=./showenv2