从 32 位进程获取 64 位进程的命令行字符串
Posted
技术标签:
【中文标题】从 32 位进程获取 64 位进程的命令行字符串【英文标题】:Get command line string of 64-bit process from 32-bit process 【发布时间】:2011-11-18 19:12:12 【问题描述】:下面的代码非常适合我从 32 位应用程序获取 32 位进程的命令行字符串,从 64 位应用程序获取 64 位进程,从 64 位应用程序获取 32 位进程。如果我尝试从 32 位应用程序用于 64 位进程,这将中断。原因是 PROCESS_BASIC_INFORMATION 和地址大小的结构大小差异。所以这是我的问题 -
1) 进程黑客 (http://processhacker.sourceforge.net/forums/viewtopic.php?f=15&t=181) 中给出的使用 wow64 功能的建议似乎不起作用并且失败并出现以下错误 -
NtWow64ReadVirtualMemory64 错误:8000000D 同时从 A68291A0004028E0 读取 ProcessParameters 地址
有没有人试过这个并且可以成功获取信息?我在他们的论坛上发布了同样的内容,征求他们的意见。
2) 是否有任何其他方法可以可靠地查询 x86 和 x64 的 peb 信息?
int get_cmdline_from_pid( DWORD dwPid, char** cmdLine )
DWORD dw, read;
HANDLE hProcess;
NtQueryInformationProcess* pNtQip;
PROCESS_BASIC_INFORMATION pbInfo;
UNICODE_STRING cmdline;
WCHAR* wcmdLine;
*cmdLine = NULL;
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid );
if( !hProcess )
return FALSE;
pNtQip = (NtQueryInformationProcess*) GetProcAddress(GetModuleHandle("ntdll.dll"),
"NtQueryInformationProcess");
if(!pNtQip)
return FALSE;
pNtQip(hProcess, PROCESSBASICINFOMATION, &pbInfo, sizeof(pbInfo), NULL);
#ifdef _WIN64
ReadProcessMemory(hProcess, pbInfo.PebBaseAddress + 0x20, &dw, sizeof(dw),
&read);
#else
ReadProcessMemory(hProcess, pbInfo.PebBaseAddress + 0x10, &dw, sizeof(dw),
&read);
#endif
#ifdef _WIN64
ReadProcessMemory(hProcess, (PCHAR)dw+112, &cmdline, sizeof(cmdline), &read);
#else
ReadProcessMemory(hProcess, (PCHAR)dw+64, &cmdline, sizeof(cmdline), &read);
#endif
wcmdLine = (WCHAR *)malloc(sizeof(char)*(cmdline.Length + 2));
if( !wcmdLine )
return FALSE;
ReadProcessMemory(hProcess, (PVOID)cmdline.Buffer, wcmdLine,
cmdline.Length+2, &read);
*cmdLine = mmwin32_util_widetoansi(wcmdLine);
free(wcmdLine);
CloseHandle(hProcess);
return TRUE;
【问题讨论】:
地址 A68291A0004028E0 看起来非常无效。 【参考方案1】:答案可能有点晚,但这里有一个代码。它支持 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;
【讨论】:
我猜我的回答被否决是因为有人认为我的回答是错误的。但我看不出这是怎么回事。您如何将 64 位地址放入 32 位变量中?如果指针的 yop 32 it 恰好为零,您的代码可能会起作用。 我不知道您的反对意见,我什至没有赞成我的意见,但是您在哪里看到存储在代码中的 32 位变量中的 64 位地址? 还有一个错误。当进程是 32 位并且您从 64 位进程中读取 PEB 地址时,您将其存储在 PVOID64 中 - 也许您希望在此变量中存储 64 位,但事实并非如此。 MSDN 说这个指针将被截断,然后你使用这个截断的指针作为 64 位进程地址空间中的地址。有趣的是,截断的指针有时有效(如果原始指针的高位字节为 0),有时则无效。例如,在获取 IE 11 x64 的命令行时,您的代码总是在 Win8 上失败。解决方案很简单 - 在所有地方都使用 ULONG64 而不是 PVOID64。 感谢@Ezh 指出截断指针问题。基于此,我只是更改了行: PBYTE* parameters = (PBYTE*)*(LPVOID*)(peb + ProcessParametersOffset);至此行:PVOID64 参数 = (PVOID64) * ((PVOID64*)(peb + ProcessParametersOffset));此更改仅在 wow 块中。这解决了问题。 @ceztko - 是的,该代码应该适用于每个 32/64 进程/操作系统组合......嗯......除了 32 位操作系统上的 64 位进程 :-)【参考方案2】:您的 32 位指针不够宽,无法在目标进程的 64 位地址空间中存储地址,将被截断。因此,您正在尝试的事情是不可能的。这是 Raymond Chen 建议您停止使用模拟器的情况之一。
在调用了 Raymond Chen 的名字后,我快速搜索了一下他是否有任何有用的掘金。该搜索出现了这篇文章:Why is there no supported way to get the command line of another process?。有用的金块是观察到Win32_Process.CommandLine
为您提供了您需要的东西(不知何故)。所以,我的建议是尝试一下 WMI。
【讨论】:
感谢您的回复。我已经在我的描述中提到了它。我现在是为什么哇功能不像进程黑客中所建议的那样工作。如果有任何其他方法可以解决此问题,请添加您的评论。 每个提到 Raymond Chen 的答案都应该被投票。当我阅读问题标题时,他写的关于获取命令行的内容也是我想到的第一件事:-) @Joey 我什至两次输入了他的名字,以确保任何愿意为 Raymond 自动投票的人在扫描时不会错过! ;-)以上是关于从 32 位进程获取 64 位进程的命令行字符串的主要内容,如果未能解决你的问题,请参考以下文章