Development environment: VC6 Windows XP
Test environment: Windows XP
As we all know, getting command-line arguments in a program is simple, the WinMain function is passed to us as parameters, or you can invoke API GetCommandLine to get it. However, the GetCommandLine function does not accept arguments and gets only the command-line arguments of its own program. So what if we want to get command line arguments for another application?
Some students said that since GetCommandLine can only get the command line parameters of this program, we can insert a DLL in other processes, call the GetCommandLine function in the address space of that process, and then pass it back. It seems a bit unfriendly. Let's think about a different way.
We thought that if we could get our command-line arguments at any time, there must be a place in the process to store it. So where is it? Look at the disassembly code of the GetCommandLine function, we found that the world is so beautiful!
The following is the GetCommandLine function disassembly code for the WinXP system:
.text:7C812C8D GetCommandLineA proc near
.text:7C812C8D mov eax, dword_7C8835F4 //dword_7C8835F4 就是命令行参数字符串的地址
//该指令机器码为 A1 F4 35 88 7C,从第2个字节开始的4个字节就是我们要的地址
.text:7C812C92 retn
.text:7C812C92 GetCommandLineA endp
Now that we know where to put it, we can get it by ourselves. Because the address of the GetCommandLine function is the same in every process, you can use the address in our process directly. WIN2000/XP system is very simple, 98 under a little bit of trouble, need to do some simple calculations. The following is the assembly code for the GetCommandLine function under Win98:.text:BFF8C907 GetCommandLineA proc near
.text:BFF8C907 mov eax, dword_BFFCADE4
.text:BFF8C90C mov ecx, [eax]
.text:BFF8C90E mov eax, [ecx+0C0h]
.text:BFF8C914 test eax, eax
.text:BFF8C916 jnz short locret_BFF8C91E
.text:BFF8C918 mov eax, [ecx+40h]
.text:BFF8C91B mov eax, [eax+8] //算到这儿,才是我们想要的地址
.text:BFF8C91E
.text:BFF8C91E locret_BFF8C91E: ; CODE XREF: GetCommandLineA+F.
.text:BFF8C91E retn
In this way, we can call the OpenProcess function to open other processes and then read the corresponding data with readprocessmemory. Sample code:DWORD G_getcmdline (DWORD dwpid,tchar* pcmdline,dword Dwbuflen)
{
#define Buffer_len//reading BUFFER for the commandline
HANDLE hproc = OpenProcess (process_vm_read,false,dwpid);
if (hproc = NULL)
{
return GetLastError ();
}
DWORD dwret =-1;
DWORD dwaddr = * (dword*) ((DWORD) GetCommandLine + 1)//2nd byte Start is the address we want to read
TCHAR Tcbuf[buffer_len] = {0};
DWORD dwread = 0;
Judgment platform
DWORD dwver = GetVersion ();
Try
{
if (Dwver < 0x80000000)//Windows NT/2000/XP
{
if (ReadProcessMemory (hproc), (LPVOID) dwaddr,&dwaddr,4,&dwread)
{
if (ReadProcessMemory (hproc), (LPVOID) dwaddr,tcbuf,buffer_len,&dwread)
{
_tcsncpy (Pcmdline,tcbuf,dwbuflen); It's best to check the size of Dwread and Dwbuflen and use the smaller one.
Dwret = 0;
}
}
}
else//Windows 95/98/me and Win32s
{
while (true)//use while is a convenient step out of the loop to make an error
{
if (! ReadProcessMemory (Hproc, (LPVOID) dwaddr,&dwaddr,4,&dwread)) break;
if (! ReadProcessMemory (Hproc, (LPVOID) dwaddr,&dwaddr,4,&dwread)) break;
if (! ReadProcessMemory (Hproc, (LPVOID) (dwaddr + 0xc0), tcbuf,buffer_len,&dwread)) break;
if (*tcbuf = 0)
{
if (! ReadProcessMemory (Hproc, (LPVOID) (dwaddr + 0x40), &dwaddr,4,&dwread)) break;
if (! ReadProcessMemory (Hproc, (LPVOID) (dwaddr + 0x8), &dwaddr,4,&dwread)) break;
if (! ReadProcessMemory (Hproc, (LPVOID) dwaddr,tcbuf,buffer_len,&dwread)) break;
}
_tcsncpy (Pcmdline,tcbuf,dwbuflen); It's best to check the size of Dwread and Dwbuflen and use the smaller one.
Dwret = 0;
Break
}
}
}
catch (...)
{
Dwret = error_invalid_access; exception
}
CloseHandle (hproc);
return dwret;
}
Complete Full text
This article supporting source code