答案可能有点晚,但这里有一个代码。它支持 32 或 64 位进程,以及 WOW64 上的 32 位进程(意味着您可以编译为 Win32 和 X64)。它使用未记录的功能,因此使用风险自负:-)
GetCmdLine.cpp:
#include "stdafx.h"
#include "GetCmdLine.h"
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
{
printf("Format is GetCmdLine <process id>\n");
return 0;
}
// get process identifier
DWORD dwId = _wtoi(argv[1]);
// open the process
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
DWORD err = 0;
if (hProcess == NULL)
{
printf("OpenProcess %u failed\n", dwId);
err = GetLastError();
return -1;
}
// determine if 64 or 32-bit processor
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
// determine if this process is running on WOW64
BOOL wow;
IsWow64Process(GetCurrentProcess(), &wow);
// use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth out
DWORD ProcessParametersOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0x20 : 0x10;
DWORD CommandLineOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0x70 : 0x40;
// read basic info to get ProcessParameters address, we only need the beginning of PEB
DWORD pebSize = ProcessParametersOffset + 8;
PBYTE peb = (PBYTE)malloc(pebSize);
ZeroMemory(peb, pebSize);
// read basic info to get CommandLine address, we only need the beginning of ProcessParameters
DWORD ppSize = CommandLineOffset + 16;
PBYTE pp = (PBYTE)malloc(ppSize);
ZeroMemory(pp, ppSize);
PWSTR cmdLine;
if (wow)
{
// we're running as a 32-bit process in a 64-bit OS
PROCESS_BASIC_INFORMATION_WOW64 pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information from 64-bit world
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtWow64QueryInformationProcess64 failed\n");
CloseHandle(hProcess);
return -1;
}
// read PEB from 64-bit address space
_NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 PEB failed\n");
CloseHandle(hProcess);
return -1;
}
// read ProcessParameters from 64-bit address space
// PBYTE* parameters = (PBYTE*)*(LPVOID*)(peb + ProcessParametersOffset); // address in remote process address space
PVOID64 parameters = (PVOID64) * ((PVOID64*)(peb + ProcessParametersOffset)); // corrected 64-bit address, see comments
err = read(hProcess, parameters, pp, ppSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 Parameters failed\n");
CloseHandle(hProcess);
return -1;
}
// read CommandLine
UNICODE_STRING_WOW64* pCommandLine = (UNICODE_STRING_WOW64*)(pp + CommandLineOffset);
cmdLine = (PWSTR)malloc(pCommandLine->MaximumLength);
err = read(hProcess, pCommandLine->Buffer, cmdLine, pCommandLine->MaximumLength, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 Parameters failed\n");
CloseHandle(hProcess);
return -1;
}
}
else
{
// we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
PROCESS_BASIC_INFORMATION pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtQueryInformationProcess failed\n");
CloseHandle(hProcess);
return -1;
}
// read PEB
if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL))
{
printf("ReadProcessMemory PEB failed\n");
CloseHandle(hProcess);
return -1;
}
// read ProcessParameters
PBYTE* parameters = (PBYTE*)*(LPVOID*)(peb + ProcessParametersOffset); // address in remote process adress space
if (!ReadProcessMemory(hProcess, parameters, pp, ppSize, NULL))
{
printf("ReadProcessMemory Parameters failed\n");
CloseHandle(hProcess);
return -1;
}
// read CommandLine
UNICODE_STRING* pCommandLine = (UNICODE_STRING*)(pp + CommandLineOffset);
cmdLine = (PWSTR)malloc(pCommandLine->MaximumLength);
if (!ReadProcessMemory(hProcess, pCommandLine->Buffer, cmdLine, pCommandLine->MaximumLength, NULL))
{
printf("ReadProcessMemory Parameters failed\n");
CloseHandle(hProcess);
return -1;
}
}
printf("%S\n", cmdLine);
return 0;
}
GetCmdLine.h:
#pragma once
#include "stdafx.h"
// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
IN HANDLE ProcessHandle,
ULONG ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
OUT PVOID Buffer,
IN SIZE_T Size,
OUT PSIZE_T NumberOfBytesRead);
// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
IN HANDLE ProcessHandle,
IN PVOID64 BaseAddress,
OUT PVOID Buffer,
IN ULONG64 Size,
OUT PULONG64 NumberOfBytesRead);
// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct _PROCESS_BASIC_INFORMATION_WOW64 {
PVOID Reserved1[2];
PVOID64 PebBaseAddress;
PVOID Reserved2[4];
ULONG_PTR UniqueProcessId[2];
PVOID Reserved3[2];
} PROCESS_BASIC_INFORMATION_WOW64;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef struct _UNICODE_STRING_WOW64 {
USHORT Length;
USHORT MaximumLength;
PVOID64 Buffer;
} UNICODE_STRING_WOW64;