【问题标题】:Getting another process command line in Windows在 Windows 中获取另一个进程命令行
【发布时间】:2011-09-25 17:04:49
【问题描述】:

我正在尝试获取另一个进程命令行(在 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 之后,我得到了我的进程的命令行,而不是请求一个。

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

有人可以帮忙吗?

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

【问题讨论】:

  • 也许您在 OpenProcess 调用中的进程 ID 为 0,或者类似的东西?
  • 没有。 hProcess 是正确的,我得到的 pbi 也是正确的。
  • 可能你自己进程的命令行和其他进程的命令行一样? :-)
  • 没有。它不是。我正在尝试从 c++ 程序中查找 javaw 进程。

标签: c++ windows winapi command-line process


【解决方案1】:

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

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

【讨论】:

  • 我已将代码编辑到帖子中。另外,我标记了失败的行(第一次调用 ZwReadVirtualMemory)。我收到错误代码 122(缓冲区不足)。但 BytesNeeded 不会改变它的价值。
【解决方案2】:

您无需读取目标进程的虚拟机即可执行此操作。只需确保您具有目标进程的正确进程 ID。

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

【讨论】:

  • 看起来确实需要 ZwReadVirtualMemory 或类似的东西。
【解决方案3】:

How to query a running process for it's parameters list? (windows, C++) 的副本,所以我将在这里复制我的答案:

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

【讨论】:

  • 问题不重复;它询问为什么特定代码不起作用。其他进程的 PEB 中的信息不可靠这一事实很有趣,但它并不能回答问题。
  • 我想 oldnewthing 已经过时了。在这里我读到了相反的内容:stackoverflow.com/questions/24754844/… 原始命令行保留在 PEB 中,GetCommandLine() 仅返回它的副本!
【解决方案4】:

看起来 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,您最终会得到本地进程的命令行。

【讨论】:

  • 感谢您的帮助,但我已经在另一个问题中找到了解决方案。据我记得,您的代码看起来更好一些(没有魔术常量等)。
  • 这段代码真的很丑很神秘。为什么不使用 ProcessBasicInformation 而不是零。为什么不使用 sizeof(PROCESS_BASIC_INFORMATION) 而不是 sizeof(PVOID)*6 ?
  • 请注意:您需要在 wBufferCopy 中添加终止 NULL 字符。
【解决方案5】:

我试图用 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

【讨论】:

  • 此代码错过了几次错误检查并泄漏了CommandLineExecutablePath BSTR 的内存。但是,使用 WMI 是获取命令行参数的唯一可靠方法,所以我 +1。
猜你喜欢
  • 2014-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 1970-01-01
  • 2012-10-28
相关资源
最近更新 更多