
[討論]淺論動態(tài)環(huán)境變量cmdcmdline的缺陷前幾天嘗試解決命令行參數(shù)中含有特殊字符導致錯誤劃分的問題時出了一點小問題,檢查代碼發(fā)現(xiàn)沒有什么明顯錯誤,就開始對關(guān)鍵部分斷點糾錯,奇怪事情發(fā)生了,在僅使用 echo 的情況下,變量的值居然前后不同。(關(guān)于解決命令行參數(shù)中含有特殊字符的討論帖原帖鏈接:http://bbs.bathome.net/thread-15349-1-1.html)這個測試中出現(xiàn)的詭異的現(xiàn)象讓我納悶了許久,最后發(fā)現(xiàn)問題出在 cmdcmdline 身上,每當對 %cmdcmdline% 使用變量偏移或變量替換時(哪怕是在 echo 中),%cmdcmdline% 就會被改變,請參考代碼示例:
- @echo off
- setlocal enabledelayedexpansion
- echo 原命令行參數(shù):%cmdcmdline%
- echo;
- echo 顯示第三個字符之后的內(nèi)容:%cmdcmdline:~3%
- echo;
- echo 再次顯示命令行參數(shù):%cmdcmdline%
- echo;
- echo 未作任何賦值操作,但是卻改變了動態(tài)環(huán)境變量 %%cmdcmdline%%...
- pause>nul
nclick="copycode($('code0'));">復制代碼
而且這個 cmd 進程的命令行參數(shù)還真的被改變了...
- @echo off
- setlocal enabledelayedexpansion
- echo 原命令行參數(shù):
- wmic process where name=cmd.exe get commandline /value
- echo 按任意鍵查看 cmd 的實際命令行參數(shù):
- pause>nul %cmdcmdline:~,1% %cmdcmdline:c=cmd /c 國家機密,禁止泄露%
- echo;
- echo 再次顯示命令行參數(shù):
- wmic process where name=cmd.exe get commandline /value
- echo 可見這里是改變了當前 cmd 的命令行參數(shù),進而使動態(tài)環(huán)境變量 cmdcmdline 也隨之變更。
- pause>nul
nclick="copycode($('code1'));">復制代碼
測試后發(fā)現(xiàn)僅 cmdcmdline 變量存在此現(xiàn)象,這是否說明,絕大多數(shù)變量的引用是通過傳值來實現(xiàn)的,而 cmdcmdline 是傳址調(diào)用的呢?或者從更高的角度來思考,cmd 對于部分動態(tài)環(huán)境變量的引用是否都是通過傳址實現(xiàn)的呢?有意思的是,因為標簽和注釋也需要經(jīng)歷預處理,所以我們可以不執(zhí)行任何語句,單純靠預處理來完成對 cmdcmdline 的修改
- @echo off&setlocal enabledelayedexpansion
- echo A:!cmdcmdline!
- :%cmdcmdline:* =%
- :%cmdcmdline:=%
- :%cmdcmdline:^=^^%
- :%cmdcmdline:&=^&%
- :%cmdcmdline:(=^(%
- :%cmdcmdline:)=^)%
- set str=%cmdcmdline%
- echo;
- echo B:!str!
- pause>nul
nclick="copycode($('code2'));">復制代碼
更有趣的是,它居然還不受常規(guī)的預處理流程限制,同一行中的兩個 %cmdcmdline% 的變量替換都生效了,由于對它的修改,僅依賴于預處理中的變量替換和偏移,并不是一條語句,所以不需要連接符也能一次預處理中完成多次替換,不過也因為無法執(zhí)行語句,所以無法進行變量嵌套
- @echo off&setlocal enabledelayedexpansion
- echo A:!cmdcmdline!
- :%cmdcmdline:~,7% %cmdcmdline:cmd=cmd(批處理宿主)% %cmdcmdline:/c=/c(執(zhí)行字符串指定的命令然后終止)%
- echo;
- echo B:!cmdcmdline!
- pause>nul
nclick="copycode($('code3'));">復制代碼
真是有趣而又詭異的現(xiàn)象,其局限性比較大,但是也有一定的實用性,以下這個代碼就對它的實用性稍作探索:
- @echo off&setlocal enabledelayedexpansion
- rem %cmdcmdline:~,1% %cmdcmdline:c=#%
- set test=#
- rem 以上為初始化,%cmdcmdline% 與 %test% 分別用于兩處對比測試。
- echo %time%
- for /l %%a in (1 1 5000) do (
- break !cmdcmdline:*#=%%a#!!cmdcmdline:0=零!!cmdcmdline:1=一!!cmdcmdline:2=二!!cmdcmdline:3=三!!cmdcmdline:4=四!!cmdcmdline:5=五!!cmdcmdline:6=六!!cmdcmdline:7=七!!cmdcmdline:8=八!!cmdcmdline:9=九!
- )
- echo 5000=!cmdcmdline!
- rem 利用 cmdcmdline 的特殊性在一條語句中完成十個替換
- echo %time%
- for /l %%a in (1 1 5000) do (
- set test=!test:*#=%%a#!
- for %%b in (0=零 1=一 2=二 3=三 4=四 5=五 6=六 7=七 8=八 9=九) do set test=!test:%%~b!
- )
- echo 5000=!test!
- rem 常規(guī)方案,用十條語句分別進行替換
- echo %time%
- rem 可以發(fā)現(xiàn)前者的用時要少得多
- pause
nclick="copycode($('code4'));">復制代碼
當然除此以外,也可以用來為 cmd.exe 偽造身份,使命令行參數(shù)中的隱私信息不泄露。另外,需要注意的是,因為動態(tài)環(huán)境變量獨立于變量表,所以 setlocal 與 endlocal 無法對其進行“備份還原”的操作:
- @echo off
- echo setlocal 前:【%cmdcmdline%】
- setlocal
- echo 改變 %cmdcmdline%
- :%cmdcmdline:~-10%
- echo setlocal 后:【%cmdcmdline%】
- endlocal
- echo endlocal 后:【%cmdcmdline%】
- pause
nclick="copycode($('code5'));">復制代碼