----很多轉載,找不到原始出處了----
我們都知道,在程式裡擷取命令列參數很簡單,WinMain函數會以參數的形式傳遞給我們,或者可以調用API GetCommandLine
擷取。但是GetCommandLine函數不接受參數,擷取的只是自己程式的命令列參數。那麼如果我們想擷取別的應用程式的命令列參數應該怎麼辦呢?
有的同學說,既然GetCommandLine只能擷取本程式的命令列參數,我們可以在其它進程裡插入一個Dll,在那個進程的地址空間調用GetCommandLine函數,然後傳回來就可以了。這樣好像有點兒不太友好。讓我們想想還有沒有別的辦法。
我們想,自己的命令列參數既然隨時都可以擷取到,那麼在該進程裡一定有一個地方存放它。那麼在哪兒呢?看一下GetCommandLine函數的反組譯碼代碼,我們發現,原來世界是如此的美好!
以下是WinXP系統的GetCommandLine函數反組譯碼代碼:
.text:7C812C8D GetCommandLineA proc near<br />.text:7C812C8D mov eax, dword_7C8835F4 //dword_7C8835F4 就是命令列參數字串的地址<br />//該指令機器碼為 A1 F4 35 88 7C,從第2個位元組開始的4個位元組就是我們要的地址<br />.text:7C812C92 retn<br />.text:7C812C92 GetCommandLineA endp
既然知道了放在哪兒了,我們自己去拿就可以了。因為GetCommandLine函數的地址在各個進程內都是一樣的,所以可以直接用我們進程裡的地址。
win2000/xp系統很簡單,98下稍微麻煩一點兒,需要進行一些簡單的計算。
以下是GetCommandLine函數在win98下的彙編代碼:
.text:BFF8C907 GetCommandLineA proc near<br />.text:BFF8C907 mov eax, dword_BFFCADE4<br />.text:BFF8C90C mov ecx, [eax]<br />.text:BFF8C90E mov eax, [ecx+0C0h]<br />.text:BFF8C914 test eax, eax<br />.text:BFF8C916 jnz short locret_BFF8C91E<br />.text:BFF8C918 mov eax, [ecx+40h]<br />.text:BFF8C91B mov eax, [eax+8] //算到這兒,才是我們想要的地址<br />.text:BFF8C91E<br />.text:BFF8C91E locret_BFF8C91E: ; CODE XREF: GetCommandLineA+F.<br />.text:BFF8C91E retn
這樣,我們就可以調用OpenProcess函數開啟其它進程,然後用ReadProcessMemory讀取相應的資料即可。 範例程式碼:
// 擷取其他進程的命令列<br />DWORD g_GetCmdLine(DWORD dwPID,TCHAR* pCmdLine,DWORD dwBufLen)<br />{<br />// reading buffer for the commandline<br />#define BUFFER_LEN 512 </p><p>HANDLE hProc = OpenProcess(PROCESS_VM_READ,FALSE,dwPID);<br />if(hProc == NULL)<br />{<br />return GetLastError();<br />}</p><p>DWORD dwRet = -1;<br />//第2個位元組開始才是我們要讀的地址<br />DWORD dwAddr = *(DWORD*)((DWORD)GetCommandLine + 1);<br />TCHAR tcBuf[BUFFER_LEN] = {0};<br />DWORD dwRead = 0;</p><p>//判斷平台<br />DWORD dwVer = GetVersion();<br />try<br />{<br />// Windows NT/2000/XP<br />if(dwVer < 0x80000000)<br />{<br />if(ReadProcessMemory(hProc,(LPVOID)dwAddr,&dwAddr,4,&dwRead))<br />{<br />if(ReadProcessMemory(hProc,(LPVOID)dwAddr,tcBuf,BUFFER_LEN,&dwRead))<br />{<br />//最好檢查一下dwRead和dwBufLen的大小,使用較小的那個<br />_tcsncpy(pCmdLine,tcBuf,dwBufLen);<br />dwRet = 0;<br />}<br />}<br />}<br />else // Windows 95/98/Me and Win32s<br />{<br />while(true) //使用while是為了出錯時方便跳出迴圈<br />{<br />if(!ReadProcessMemory(hProc,(LPVOID)dwAddr,&dwAddr,4,&dwRead)) break;<br />if(!ReadProcessMemory(hProc,(LPVOID)dwAddr,&dwAddr,4,&dwRead)) break;</p><p>if(!ReadProcessMemory(hProc,(LPVOID)(dwAddr + 0xC0),tcBuf,BUFFER_LEN,&dwRead)) break;<br />if(*tcBuf == 0)<br />{<br />if(!ReadProcessMemory(hProc,(LPVOID)(dwAddr + 0x40),&dwAddr,4,&dwRead)) break;<br />if(!ReadProcessMemory(hProc,(LPVOID)(dwAddr + 0x8),&dwAddr,4,&dwRead)) break;<br />if(!ReadProcessMemory(hProc,(LPVOID)dwAddr,tcBuf,BUFFER_LEN,&dwRead)) break;<br />}<br />_tcsncpy(pCmdLine,tcBuf,dwBufLen); //最好檢查一下dwRead和dwBufLen的大小,使用較小的那個<br />dwRet = 0;<br />break;<br />}<br />}<br />}<br />catch(...)<br />{<br />dwRet = ERROR_INVALID_ACCESS; //exception<br />}<br />CloseHandle(hProc);<br />return dwRet;<br />}