C++ 在 Windows 中获取另一个进程命令行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6530565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:19:18  来源:igfitidea点击:

Getting another process command line in Windows

c++windowswinapicommand-lineprocess

提问by Georg

I am trying to get another process commandline (on WinXP 32bit). I do the following:

我正在尝试获取另一个进程命令行(在 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;

After first call pbi.UniqueProcessID is correct. But after calling ZwReadVirtualMemory I get command line for my process, not requested one.

第一次调用后 pbi.UniqueProcessID 是正确的。但是在调用 ZwReadVirtualMemory 之后,我得到了我的进程的命令行,而不是请求的命令行。

I also used ReadProcessMemore & NtQueryInformationProcess, but get the same result.

我还使用了 ReadProcessMemore 和 NtQueryInformationProcess,但得到了相同的结果。

Can anybody help?

有人可以帮忙吗?

Here http://forum.sysinternals.com/get-commandline-of-running-processes_topic6510_page1.htmlis being said that this code works. Unfortunately, I do not have access to post on this forum to ask themselves.

这里http://forum.sysinternals.com/get-commandline-of-running-processes_topic6510_page1.html据说此代码有效。不幸的是,我无权在这个论坛上发帖问自己。

采纳答案by Jon

Duplicate of How to query a running process for it's parameters list? (windows, C++), so I'll just copy my answer from there over here:

重复如何查询正在运行的进程的参数列表?(windows, C++),所以我会从那里复制我的答案:

You can't reliablyget that information. There are various tricks to try and retrieve it, but there's no guarantee that the target process hasn't already mangled that section of memory. Raymond Chen discussed this awhile back on The Old New Thing.

您无法可靠地获取该信息。有各种技巧可以尝试检索它,但不能保证目标进程尚未损坏该部分内存。Raymond Chen 曾在The Old New Thing上讨论过这个问题。

回答by glagolig

It looks like ZwReadVirtualMemory is called only once. That is not enough. It has to be called for each level of pointer indirection. In other words when you retrieve a pointer it points to other process' address space. You cannot read it directly. You have to call ZwReadVirtualMemory again. For the case of those data structures ZwReadVirtualMemory has to be called 3 times: once to read PEB (that is what the code above does), once to read RTL_USER_PROCESS_PARAMETERS and once to read UNICODE_STRING's buffer. The following code fragment worked for me (error handling omitted for clarity and I used documented ReadProcessMemory API instead of ZwReadVirtualMemory):

看起来 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);

Why we see see the command line of our own process? That is because processes are laid out in a similar way. Command line and PEB-related structures are likely to have the same addresses. So if you missed ReadProcessMemory you end up exactly with local process' command line.

为什么我们看到的是我们自己进程的命令行?那是因为流程的布局方式类似。命令行和 PEB 相关的结构可能具有相同的地址。因此,如果您错过了 ReadProcessMemory,您最终会使用本地进程的命令行。

回答by bruceceng

I was trying to do this same thing using mingw & Qt. I ran into a problem with "undefined reference to CLSID_WbemLocator". After some research, it seems that the version of libwbemuuid.a which was included with my version of mingw only defined IID_IWbemLocator but not CLSID_WbemLocator.

我试图使用 mingw & Qt 做同样的事情。我遇到了“对 CLSID_WbemLocator 的未定义引用”的问题。经过一些研究,我的 mingw 版本中包含的 libwbemuuid.a 版本似乎只定义了 IID_IWbemLocator 而不是 CLSID_WbemLocator。

I found that manually defining CLSID_WbemLocator works (although its probably not the "correct" way of doing things).

我发现手动定义 CLSID_WbemLocator 有效(尽管它可能不是“正确”的做事方式)。

The final working code:

最终的工作代码:

#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);
}

and in my Qt project file (.pro) I link to the following libraries:

在我的 Qt 项目文件 (.pro) 中,我链接到以下库:

LIBS += -lole32 -lwbemuuid

回答by Frerich Raabe

You need to be more disciplined with checking return codes. It may be that any of your ZwReadVirtualMemorycalls yield an error code which points you into the right direction.

您需要更加严格地检查返回代码。可能您的任何ZwReadVirtualMemory调用都会产生一个错误代码,为您指明正确的方向。

In particular, the ProcList.proc_id_as_numbers[i]part suggests that you're executing this code in a loop. Chances are that the procPeb.ProcessParametersstructure is still filled with the values of an earlier loop iteration - and since the ZwReadVirtualMemorycall fails on your target process, you get to see the command line of whatever process was previously queried.

特别是,该ProcList.proc_id_as_numbers[i]部分建议您在循环中执行此代码。有可能该procPeb.ProcessParameters结构仍然填充了早期循环迭代的值 - 由于ZwReadVirtualMemory调用在您的目标进程上失败,您可以看到先前查询的任何进程的命令行。

回答by Steve Townsend

You don't have to read the VM of the target process to do this. Just make sure you have the correct Process ID for the target process.

您不必读取目标进程的 VM 即可执行此操作。只需确保您拥有目标进程的正确进程 ID。

Once you have the process handle via OpenProcess, you can then use NtQueryInformationProcessto get detailed process info. Use the ProcessBasicInformation option to get the PEB of the process - this contains another structure pointer RTL_USER_PROCESS_PARAMETERS, through which you can get the command line.

一旦您通过 获得了流程句柄OpenProcess,您就可以使用NtQueryInformationProcess来获取详细的流程信息。使用 ProcessBasicInformation 选项获取进程的 PEB - 这包含另一个结构指针RTL_USER_PROCESS_PARAMETERS,您可以通过它获取命令行。