我正在尝试获取另一个进程的命令行参数(在 WinXP 32 位上)。
我执行以下操作:
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, ProcList.proc_id_as_numbers[i]);
BytesNeeded = sizeof(PROCESS_BASIC_INFORMATION);
ZwQueryInformationProcess(hProcess, ProcessBasicInformation, UserPool, sizeof(PROCESS_BASIC_INFORMATION), &BytesNeeded);
pbi = (PPROCESS_BASIC_INFORMATION)UserPool;
BytesNeeded = sizeof(PEB);
res = ZwReadVirtualMemory(hProcess, pbi->PebBaseAddress, UserPool, sizeof(PEB), &BytesNeeded);
/* zero value returned */
peb = (PPEB)UserPool;
BytesNeeded = sizeof(RTL_USER_PROCESS_PARAMETERS);
res = ZwReadVirtualMemory(hProcess, peb->ProcessParameters, UserPool, sizeof(RTL_USER_PROCESS_PARAMETERS), &BytesNeeded);
ProcParam = (PRTL_USER_PROCESS_PARAMETERS)UserPool;
第一次通话后,
pbi.UniqueProcessID
是正确的。
但是,在调用
ZwReadVirtualMemory()
后,我得到了我的进程的命令行,而不是请求的命令行。
我也使用了
ReadProcessMemory()
和NtQueryInformationProcess()
,但得到了相同的结果。
有人可以帮忙吗?
在这个论坛帖子上,据说这段代码可以工作。不幸的是,我无法在该论坛上发帖询问他们。
看起来 ZwReadVirtualMemory 只被调用一次。这还不够。必须为每个级别的指针间接调用它。换句话说,当您检索指针时,它指向其他进程的地址空间。您无法直接读取它。您必须再次调用 ZwReadVirtualMemory。对于这些数据结构,ZwReadVirtualMemory 必须被调用 3 次:一次读取 PEB(这就是上面代码的作用),一次读取 RTL_USER_PROCESS_PARAMETERS,一次读取 UNICODE_STRING 的缓冲区。 以下代码片段对我有用(为了清楚起见,省略了错误处理,我使用记录的 ReadProcessMemory API 而不是 ZwReadVirtualMemory):
LONG status = NtQueryInformationProcess(hProcess,
0,
pinfo,
sizeof(PVOID)*6,
NULL);
PPEB ppeb = (PPEB)((PVOID*)pinfo)[1];
PPEB ppebCopy = (PPEB)malloc(sizeof(PEB));
BOOL result = ReadProcessMemory(hProcess,
ppeb,
ppebCopy,
sizeof(PEB),
NULL);
PRTL_USER_PROCESS_PARAMETERS pRtlProcParam = ppebCopy->ProcessParameters;
PRTL_USER_PROCESS_PARAMETERS pRtlProcParamCopy =
(PRTL_USER_PROCESS_PARAMETERS)malloc(sizeof(RTL_USER_PROCESS_PARAMETERS));
result = ReadProcessMemory(hProcess,
pRtlProcParam,
pRtlProcParamCopy,
sizeof(RTL_USER_PROCESS_PARAMETERS),
NULL);
PWSTR wBuffer = pRtlProcParamCopy->CommandLine.Buffer;
USHORT len = pRtlProcParamCopy->CommandLine.Length;
PWSTR wBufferCopy = (PWSTR)malloc(len);
result = ReadProcessMemory(hProcess,
wBuffer,
wBufferCopy, // command line goes here
len,
NULL);
为什么我们看到自己进程的命令行?这是因为流程是以类似的方式布局的。命令行和 PEB 相关结构可能具有相同的地址。因此,如果您错过了 ReadProcessMemory,您最终会完全使用本地进程的命令行。
我试图使用 mingw 和 Qt 做同样的事情。我遇到了“对 CLSID_WbemLocator 的未定义引用”的问题。经过一番研究,我的 mingw 版本中包含的 libwbemuuid.a 版本似乎只定义了 IID_IWbemLocator,但没有定义 CLSID_WbemLocator。
我发现手动定义 CLSID_WbemLocator 是有效的(尽管它可能不是“正确”的做事方式)。
最终工作代码:
#include <QDebug>
#include <QString>
#include <QDir>
#include <QProcess>
#define _WIN32_DCOM
#include <windows.h>
#include "TlHelp32.h"
#include <stdio.h>
#include <tchar.h>
#include <wbemidl.h>
#include <comutil.h>
const GUID CLSID_WbemLocator = { 0x4590F811,0x1D3A,0x11D0,{ 0x89,0x1F,0x00,0xAA,0x00,0x4B,0x2E,0x24 } }; //for some reason CLSID_WbemLocator isn't declared in libwbemuuid.a (although it probably should be).
int getProcessInfo(DWORD pid, QString *commandLine, QString *executable)
{
HRESULT hr = 0;
IWbemLocator *WbemLocator = NULL;
IWbemServices *WbemServices = NULL;
IEnumWbemClassObject *EnumWbem = NULL;
//initializate the Windows security
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &WbemLocator);
//connect to the WMI
hr = WbemLocator->ConnectServer(L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &WbemServices);
//Run the WQL Query
hr = WbemServices->ExecQuery(L"WQL", L"SELECT ProcessId,CommandLine,ExecutablePath FROM Win32_Process", WBEM_FLAG_FORWARD_ONLY, NULL, &EnumWbem);
qDebug() << "Got here." << (void*)hr;
// Iterate over the enumerator
if (EnumWbem != NULL) {
IWbemClassObject *result = NULL;
ULONG returnedCount = 0;
while((hr = EnumWbem->Next(WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
VARIANT ProcessId;
VARIANT CommandLine;
VARIANT ExecutablePath;
// access the properties
hr = result->Get(L"ProcessId", 0, &ProcessId, 0, 0);
hr = result->Get(L"CommandLine", 0, &CommandLine, 0, 0);
hr = result->Get(L"ExecutablePath", 0, &ExecutablePath, 0, 0);
if (ProcessId.uintVal == pid)
{
*commandLine = QString::fromUtf16((ushort*)(long)CommandLine.bstrVal);// + sizeof(int)); //bstrs have their length as an integer.
*executable = QString::fromUtf16((ushort*)(long)ExecutablePath.bstrVal);// + sizeof(int)); //bstrs have their length as an integer.
qDebug() << *commandLine << *executable;
}
result->Release();
}
}
// Release the resources
EnumWbem->Release();
WbemServices->Release();
WbemLocator->Release();
CoUninitialize();
//getchar();
return(0);
}
在我的 Qt 项目文件 (.pro) 中,我链接到以下库:
LIBS += -lole32 -lwbemuuid
重复如何查询正在运行的进程的参数列表? (Windows,C++),所以我只需从这里复制我的答案:
您无法可靠地获取该信息。有多种技巧可以尝试检索它,但不能保证目标进程尚未破坏该内存部分。 Raymond Chen 在The Old New Thing 上讨论过这个问题。
您需要更加严格地检查返回代码。您的任何
ZwReadVirtualMemory
调用都可能会产生错误代码,为您指明正确的方向。
特别是,
ProcList.proc_id_as_numbers[i]
部分表明您正在循环中执行此代码。有可能 procPeb.ProcessParameters
结构仍填充有早期循环迭代的值 - 并且由于 ZwReadVirtualMemory
调用在目标进程上失败,因此您可以看到之前查询的任何进程的命令行。
您不必读取目标进程的虚拟机即可执行此操作。只需确保您拥有目标进程的正确进程 ID。
一旦通过
OpenProcess
获得了进程句柄,您就可以使用 NtQueryInformationProcess 来获取详细的进程信息。使用 ProcessBasicInformation 选项获取进程的 PEB - 这包含另一个结构体指针RTL_USER_PROCESS_PARAMETERS,通过它可以获取命令行。
我改编了 @brucecent 的答案以使用
WHERE
子句,不需要 Qt 并且不会泄漏检索到的字符串。
#include <cstdlib>
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#define _WIN32_DCOM
#ifdef __CYGWIN__
#define sprintf_s sprintf
#endif
#include <windows.h>
#include <comutil.h>
#include <stringapiset.h>
#include <wbemidl.h>
int
main(int argc, char **argv)
{
int pid = argv[1] ? std::atoi(argv[1]) : GetCurrentProcessId();
HRESULT hr = E_FAIL;
IWbemLocator *wbem_locator = NULL;
IWbemServices *wbem_services = NULL;
IEnumWbemClassObject *enum_wbem = NULL;
CoInitializeEx(0, COINIT_MULTITHREADED);
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &wbem_locator);
wbem_locator->ConnectServer(L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &wbem_services);
wchar_t *query = new wchar_t[4096];
swprintf(query, 4096, L"select commandline from win32_process where processid = %d", pid);
wbem_services->ExecQuery(L"WQL", query, WBEM_FLAG_FORWARD_ONLY, NULL, &enum_wbem);
delete[] query;
if (enum_wbem != NULL) {
IWbemClassObject *result = NULL;
ULONG returned_count = 0;
if((hr = enum_wbem->Next(WBEM_INFINITE, 1, &result, &returned_count)) == S_OK) {
VARIANT process_id;
VARIANT command_line;
result->Get(L"CommandLine", 0, &command_line, 0, 0);
wchar_t *command_line_utf16 = command_line.bstrVal;
size_t size = WideCharToMultiByte(CP_UTF8, 0, command_line_utf16, -1, NULL, 0, NULL, NULL) + 1;
char *command_line_utf8 = new char[size];
WideCharToMultiByte(CP_UTF8, 0, command_line_utf16, -1, command_line_utf8, size, NULL, NULL);
SysFreeString(command_line_utf16);
std::cout << pid << ": " << command_line_utf8 << std::endl;
delete command_line_utf8;
result->Release();
}
}
}
使用以下命令编译 MSVC:
cl .\show-process.cpp /Fe:show-process.exe ole32.lib oleaut32.lib wbemuuid.lib
,对于 MinGW 来说:
g++ -fpermissive -Wno-write-strings -O2 show-process.cpp -o show-process.exe -lole32 -loleaut32 -lwbemuuid
.
您可以在我的存储库这里中找到这个示例,以及相同的示例,但父进程 ID 是这样的。