How to query a running process for it's parameters list? (windows, C++)
Assuming you know the process ID, use OpenProcess to get a handle to it (this requires elevated privilege as noted in the docs). Then use NtQueryInformationProcess to get detailed process info. Use the ProcessBasicInformation
option to get the PEB of the process - this contains another structure pointer, through which you canget the command line.
Remote thread injection:
You use remote thread injection, call GetCommandLine()
, then IPC the result back. This might work most of the time on Windows XP, but on Windows Vista or later it doesn’t work on system and service processes. This is because CreateRemoteThread
only works on processes in the same session ID as the caller – in Windows Vista, services and other system processes run in session 0 while user programs run in higher sessions. The best and safest way is to read a structure present in every Windows process.
PEB structure:
The Process Environment Block (PEB) is usually stored in the high regions of process memory, above 0x7ff00000
. These regions also contain Thread Environment Blocks (TEBs). The PEB address is different for almost every process, so you can’t simply use a hardcoded constant.
#include <windows.h>
#include <stdio.h>
#include "Winternl.h"
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
HANDLE ProcessHandle,
DWORD ProcessInformationClass,
PVOID ProcessInformation,
DWORD ProcessInformationLength,
PDWORD ReturnLength
);
PVOID GetPebAddress(HANDLE ProcessHandle)
{
_NtQueryInformationProcess NtQueryInformationProcess =
(_NtQueryInformationProcess)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION pbi;
NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
return pbi.PebBaseAddress;
}
int wmain(int argc, WCHAR *argv[])
{
int pid;
HANDLE processHandle;
PVOID pebAddress;
PVOID rtlUserProcParamsAddress;
UNICODE_STRING commandLine;
WCHAR *commandLineContents;
if (argc < 2)
{
printf("Usage: getprocesscommandline [pid]\n");
return 1;
}
pid = _wtoi(argv[1]);
if ((processHandle = OpenProcess(
PROCESS_QUERY_INFORMATION | /* required for NtQueryInformationProcess */
PROCESS_VM_READ, /* required for ReadProcessMemory */
FALSE, pid)) == 0)
{
printf("Could not open process!\n");
return GetLastError();
}
pebAddress = GetPebAddress(processHandle);
/* get the address of ProcessParameters */
if (!ReadProcessMemory(processHandle,
&(((_PEB*) pebAddress)->ProcessParameters),
&rtlUserProcParamsAddress,
sizeof(PVOID), NULL))
{
printf("Could not read the address of ProcessParameters!\n");
return GetLastError();
}
/* read the CommandLine UNICODE_STRING structure */
if (!ReadProcessMemory(processHandle,
&(((_RTL_USER_PROCESS_PARAMETERS*) rtlUserProcParamsAddress)->CommandLine),
&commandLine, sizeof(commandLine), NULL))
{
printf("Could not read CommandLine!\n");
return GetLastError();
}
/* allocate memory to hold the command line */
commandLineContents = (WCHAR *)malloc(commandLine.Length);
/* read the command line */
if (!ReadProcessMemory(processHandle, commandLine.Buffer,
commandLineContents, commandLine.Length, NULL))
{
printf("Could not read the command line string!\n");
return GetLastError();
}
/* print it */
/* the length specifier is in characters, but commandLine.Length is in bytes */
/* a WCHAR is 2 bytes */
printf("%.*S\n", commandLine.Length / 2, commandLineContents);
CloseHandle(processHandle);
free(commandLineContents);
return 0;
}
For more detail, Please have a look on Get the command line of a process
EDIT (Additional information):
First author said :
CreateRemoteThread
only works on processes in the same session ID as the caller – in Windows Vista, services and other system processes run in session 0 while user programs run in higher sessions. The best and safest way is to read a structure present in every Windows process.
It is the same thing with OpenProcess, you can't open a process which is a service or a process opened by SYSTEM or LOCAL SERVICE or NETWORK SERVICE, if you are running your program by a user (even administrator).
If your program is a service, it is probably already running by local system account, so no problem. But if not, a solution is to launch it with psexec:
- Download PSEXEC and unzip to some folder.
- Open an elevated CMD prompt as an administrator.
- Navigate to the folder where you unzipped PSEXEC.EXE
- Run:
PSEXEC -i -s -d CMD
- You will have a new CMD prompt open.
- Type the following in the new CMD prompt to prove who you are:
WHOAMI
You should see that you are SYSTEM, now you can launch your program and see the command line for all processes.
Well you could inject a dll into the foreign process address space and then call GetCommandLine.