showargs1程序是把命令行参数字符串的地址从栈中拷贝到全局数组变量里,然后把结果显示出来,showargs2程序对此进行了改进,直接从栈上读取命令行参数字符串的地址,而不再进行拷贝操作,程序大同小异:
SECTION .data ; Section containing initialised data
ErrMsg db "Terminated with error.",10
ERRLEN equ $-ErrMsg
SECTION .bss ; Section containing uninitialized data
; This program handles up to MAXARGS command-line arguments. Change the
; value of MAXARGS if you need to handle more arguments than the default 10.
; Argument lengths are stored in a table. Access arg lengths this way:
; [ArgLens + <index reg>*4]
; Note that when the argument lengths are calculated, an EOL char (10h) is
; stored into each string where the terminating null was originally. This
; makes it easy to print out an argument using sys_write.
MAXARGS equ 10 ; Maximum # of args we support
ArgLens: resd MAXARGS ; 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
; Copy the command line argument count from the stack and validate it:
cmp dword [ebp],MAXARGS ; See if the arg count exceeds MAXARGS
ja Error ; If so, exit with an error message
; Here we calculate argument lengths and store lengths in table ArgLens:
xor eax,eax ; Searching for 0, so clear AL to 0
xor ebx,ebx ; Stack address offset starts at 0
ScanOne:
mov ecx,0000ffffh ; Limit search to 65535 bytes max
mov edi,dword [ebp+4+ebx*4] ; Put address of string to search in EDI
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 [ArgLens+ebx*4],edi ; Put length of arg into table
inc ebx ; Add 1 to argument counter
cmp ebx,[ebp] ; See if arg counter exceeds argument count
jb ScanOne ; If not, loop back and do another one
; Display all arguments to stdout:
xor esi,esi ; Start (for table addressing reasons) at 0
Showem:
mov ecx,[ebp+4+esi*4] ; Pass offset of the message
mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 1: Standard Output
mov edx,[ArgLens+esi*4] ; Pass the length of the message
int 80H ; Make kernel call
inc esi ; Increment the argument counter
cmp esi,[ebp] ; See if we've displayed all the arguments
jb 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,保存栈指针到ebp
cmp dword [ebp],MAXARGS //比较ebp保存的地址指向的内存的内容(命令行参数个数),与最大参数个数
ja Error //如果大于,跳转到Error
xor eax,eax //eax=0,al=0
xor ebx,ebx //ebx=0
ScanOne:
mov ecx,0000ffffh //ecx=65535
mov edi,dword [ebp+4+ebx*4] //edi = &ebp[ebx*4+4],命令行参数的首地址
mov edx,edi //edx=edi
cld //清除DF标志,内存地址变化方向从低到高
repne scasb //扫描字符串,如果遇到0,则结束。
jnz Error //如果循环结束,ZF标志位没有设置,则说明ecx=0,属于异常情况,跳转到Error
mov byte [edi-1],10 //字符串的结尾的0字符替换成10(换行符)
sub edi,edx //算出字符串长度,包括换行符
mov dword [ArgLens+ebx*4],edi //保存长度到数组中
inc ebx //ebx=ebx+1
cmp ebx,[ebp] //比较ebx和命令行参数个数
jb ScanOne //如果小于继续循环
xor esi,esi //esi=0
Showem:
mov ecx,[ebp+4+esi*4] //ecx=命令行参数字符串起始地址
mov eax,4
mov ebx,1
mov edx,[ArgLens+esi*4] //字符串长度
int 80H
inc esi //esi = esi +1
cmp esi,[ebp] //比较esi与命令行参数个数
jb Showem //小于继续循环
jmp Exit
测试:
[root@bogon showargs2]# make
nasm -f elf -g -F stabs showargs2.asm -l showargs2.lst
ld -o showargs2 showargs2.o
[root@bogon showargs2]# ./showargs2 p1 p2
./showargs2
p1
p2