;原出处:MASM32 SDK
;注释修改&中文注释添加 By 老刘
; #########################################################################
;
; 这个程序是在Iczelion和Lucifer的技术支持下开发出来的。
;
; #########################################################################
.386
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \MASM32\INCLUDE\kernel32.inc
; ------------------------------------
; 请读文本最后的用法
; ------------------------------------
ArgCl PROTO :DWORD,:DWORD
.code
; #########################################################################
ArgCl proc ArgNum:DWORD, ItemBuffer:DWORD
LOCAL cmdLine :DWORD
LOCAL cmdBuffer[192] :BYTE
LOCAL tmpBuffer[192] :BYTE
; --------------
; 储存 esi & edi
; --------------
push esi
push edi
invoke GetCommandLine
mov cmdLine, eax ; address command line
cmp ArgNum, 0
jne @F
xor eax, eax
jmp jmp_In
@@:
mov esi, cmdLine
lodsb ;al=byte ptr [esi]
cmp al, 34
je @F ;如果不是半角双引号,退出
pop edi
pop esi
xor eax, eax ; eax=0
ret
@@:
; -------------------------------------------------
; 统计引号标记来确认其是否双双匹配
; -------------------------------------------------
xor ecx, ecx ; 将ecx作为引号个数的计数器
mov esi, cmdLine
@@:
lodsb
cmp al, 0 ;到达末尾
je @F
cmp al, 34
jne @B ;不为引号则继续循环
inc ecx ;ecx+=1
jmp @B
@@:
push ecx ;储存计数的值
shr ecx, 1
shl ecx, 1 ;ecx-=ecx%2
pop eax ;eax=计数的值
cmp eax, ecx
je @F ;引号数量为偶数(匹配)则下跳
pop edi
pop esi
mov eax, 3 ;返回3
ret
@@:
; -------------------------------------------
; 下面的代码移除(程序的)路径及文件名
; cmdline只留下参数
; -------------------------------------------
mov esi, cmdLine ;esi=cmdline指针
lea edi, tmpBuffer ;edi=tmpBuffer指针
lodsb ;读第一个引号
@@: ;去除字符直到读到第二个引号
lodsb
cmp al, 34
jne @B
wtIn:
lodsb
cmp al, 0
je wtOut
stosb ;转移到tmpBuffer
jmp wtIn
wtOut:
stosb ;tmpBuffer结尾
; -----------------------------------
; 处理空参数""(其实就是""->255)
; -----------------------------------
lea esi, tmpBuffer
lea edi, cmdBuffer
xor edx, edx ;清0,作为标志寄存器使用
rnsSt:
lodsb
cmp al, 0
je rnsEnd ;esi指向null,即处理完毕
.if al != 34 ;该字符不为引号
.if edx == 1 ;前一字符为引号
xor edx, edx ;edx=0
jmp rnsWrt
.endif
.elseif al == 34 ;该字符为引号
.if edx == 1 ;前一字符为引号
mov al, 255
stosb ;edi指向地址=255,edi++,其实就是引号换255
mov al, 34 ;再写入一个引号
stosb
dec edx ;edx=0
jmp rnsSt
.elseif edx == 0
inc edx ;标记
.endif
.endif
rnsWrt:
stosb
jmp rnsSt
rnsEnd:
stosb ;cmdBuffer结尾
; ----------------------------------
; 替换有引号包裹的空格(引号中的空格->254)
; 删除引号(上面""换成的255不会被删除)
; ----------------------------------
lea esi, cmdBuffer ;下面操作的是同一个地址,但由于先读后写,并不冲突。
lea edi, cmdBuffer
subSt:
lodsb
cmp al, 0
jne @F
jmp subOut ;完成
@@:
cmp al, 34
jne subNxt
jmp subSl ;如果有引号,进入子循环
subNxt:
stosb ;写回去
jmp subSt
; --------------------------
subSl:
lodsb
cmp al, 32 ;是空格
jne @F
mov al, 254 ;空格换254
@@:
cmp al, 34
jne @F
jmp subSt ;子循环完毕,返回
@@:
stosb ;写回去
jmp subSl
; --------------------------
subOut:
stosb ;结尾
; ------------------------
;Tab-->空格
; ------------------------
lea esi, cmdBuffer
lea edi, cmdBuffer
@@:
lodsb
cmp al, 0
je rtOut
cmp al, 9
jne rtIn ;不为Tab跳
mov al, 32 ;9-->32
rtIn:
stosb
jmp @B
rtOut:
stosb
; ----------------------------------------------------
; 下面的代码分割正确的arg,
; 并且将arg写入目的地址
; ----------------------------------------------------
lea eax, cmdBuffer
mov esi, eax
mov edi, ItemBuffer ; 目标地址
mov ecx, 1 ; 做计数器
; ---------------------------
; 去掉先导空格(如果有的话)
; ---------------------------
@@:
lodsb
cmp al, 32
je @B
l2St:
cmp ecx, ArgNum ; 传入的ArgNum
je clSubLp2 ;到达需要取参的位置
lodsb
cmp al, 0
je cl2Out
cmp al, 32
jne cl2Ovr ; if not space
@@:
lodsb
cmp al, 32 ; 捕捉连续的空格
je @B
inc ecx ;arg计数器++
cmp al, 0
je cl2Out
cl2Ovr:
jmp l2St
clSubLp2:
stosb ;参数第一个字符写进去
@@:
lodsb
cmp al, 32
je cl2Out
cmp al, 0
je cl2Out
stosb
jmp @B
cl2Out:
mov al, 0
stosb
; ---------------------------------
; 空格换回去
; ---------------------------------
mov esi, ItemBuffer
mov edi, ItemBuffer
@@:
lodsb
cmp al, 0
je @F
cmp al, 254
jne nxt1
mov al, 32
nxt1:
stosb
jmp @B
@@:
; -------------------------------------------------
; 替换“”为0
; -------------------------------------------------
mov esi, ItemBuffer
mov edi, ItemBuffer
lodsb
cmp al, 255 ;见108+行
jne @F
mov al, 0 ;替换为0
stosb
mov eax, 4 ;返回4
pop edi
pop esi
ret
@@:
; ----------------------------------
; 如果参数数量不够,
; ItemBuffer首位设0
; ----------------------------------
.if ecx < ArgNum
jmp_In:
mov edi, ItemBuffer
mov al, 0
stosb ;mov byte ptr [edi],al
mov eax, 2 ;返回值为2说明参数数量不够或ArgNum为0
jmp @F
.endif
mov eax, 1 ;cmdline成功处理
@@:
pop edi
pop esi
ret
ArgCl endp
; #########################################################################
;
; 这个子程序接收2个参数
;
; 1. 你要获取的命令行参数的序号。
; 2. 结果的缓存区地址。
;
; Example: 如果有4个参数放置于CmdLine中,
; 如progname arg1 arg2 arg3 arg4 , 使用如下手段调用该函数,
;
; invoke ArgCl,3,ADDR buffer
;
; 将会将arg3放入buffer。
;
; 注意,引用的Arg支持长文件名。
;
; 确保缓存区的大小足够接收参数,
; cmdline最大只能有128字节长(现在为32kb(非CMD中传参),该代码的编写日期为1999年),
; 在一个函数中,使用
;
; LOCAL Buffer[128]:BYTE
;
; eax中的返回值
;
; 0 = GetCommandLine返回Null
; 1 = cmdline成功处理
; 2 = 已经尝试处理,但是参数数量不够或ArgNum为0
; 3 = 引号数目不匹配(不为偶数)
; 4 = 空的引号标记(类似"")
;
; Tab和空格都为界定字符
;
; #########################################################################
end
## 漏洞分析 ##