【问题标题】:How to query a running process for its parameters list? (Windows, C++)如何查询正在运行的进程的参数列表? (Windows、C++)
【发布时间】:2011-06-29 12:11:40
【问题描述】:

对于给定的 Windows 进程,我想知道它是用什么命令行参数启动的。 Windows 任务管理器能够显示这一点。

【问题讨论】:

  • 鉴于this comment 似乎您对问题的解决方案错误,而不是询问如何做您最初想要的事情。如果您想阻止多个进程执行相同的工作,您可以使用互斥锁或信号量来“锁定”特定的工作部分。

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


【解决方案1】:

假设您知道进程 ID,请使用 OpenProcess 获取它的句柄(这需要提升权限,如文档中所述)。然后使用NtQueryInformationProcess 获取详细的进程信息。使用ProcessBasicInformation 选项获取进程的PEB - 这包含另一个结构指针,通过它可以获取命令行。

【讨论】:

  • 似乎正是我正在寻找的。谢谢。
  • “这些 API 和数据结构会随着从一个 Windows 版本到另一个 Windows 版本的变化而变化。要保持应用程序的兼容性,请避免使用这些 API 和数据结构。”嗯,没有我希望的那么容易。
  • 是的,但是该文档已存在多年,我个人怀疑这是一个主要问题。但是,如果您愿意,也可以使用 WMI 获取所需的信息。参见msdn.microsoft.com/en-us/library/aa394372%28v=VS.85%29.aspxWin32_Process 类的CommandLine 属性
  • 我今天第一次在这里“WMI”。似乎不是最直接的事情。我会研究它。再次感谢您。
  • 是的,它非常全面,但由于它基于 COM 编码实践,所以使用起来有点不严谨。如果您愿意的话,使用 .Net Framework 中的 System.Management 命名空间对 WMI 进行编码要容易得多。我的答案中的 C++/Win32 方法现在是一个更快的解决方案 - 如果需要的话,也许你可以等待使用 WMI 重新实现?
【解决方案2】:

远程线程注入:

你使用远程线程注入,调用GetCommandLine(),然后IPC返回结果。这可能在 Windows XP 上大部分时间都有效,但在 Windows Vista 或更高版本上,它不适用于系统和服务进程。这是因为CreateRemoteThread 仅适用于与调用者具有相同会话 ID 的进程——在 Windows Vista 中,服务和其他系统进程在会话 0 中运行,而用户程序在更高的会话中运行。最好和最安全的方法是读取每个 Windows 进程中存在的结构。

PEB结构:

Process Environment Block (PEB) 通常存储在进程内存的高区域,在0x7ff00000 上方。这些区域还包含线程环境块 (TEB)。几乎每个进程的 PEB 地址都不同,因此不能简单地使用硬编码常量。

#include <windows.h>
#include <stdio.h>
#include "Winternl.h"

typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
    HANDLE ProcessHandle,
    DWORD ProcessInformationClass,
    PVOID ProcessInformation,
    DWORD ProcessInformationLength,
    PDWORD ReturnLength
    );

PVOID GetPebAddress(HANDLE ProcessHandle)
{
    _NtQueryInformationProcess NtQueryInformationProcess =
        (_NtQueryInformationProcess)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
    PROCESS_BASIC_INFORMATION pbi;

    NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);

    return pbi.PebBaseAddress;
}

int wmain(int argc, WCHAR *argv[])
{
    int pid;
    HANDLE processHandle;
    PVOID pebAddress;
    PVOID rtlUserProcParamsAddress;
    UNICODE_STRING commandLine;
    WCHAR *commandLineContents;

    if (argc < 2)
    {
        printf("Usage: getprocesscommandline [pid]\n");
        return 1;
    }

    pid = _wtoi(argv[1]);

    if ((processHandle = OpenProcess(
        PROCESS_QUERY_INFORMATION | /* required for NtQueryInformationProcess */
        PROCESS_VM_READ, /* required for ReadProcessMemory */
        FALSE, pid)) == 0)
    {
        printf("Could not open process!\n");
        return GetLastError();
    }

    pebAddress = GetPebAddress(processHandle);

    /* get the address of ProcessParameters */
    if (!ReadProcessMemory(processHandle,
            &(((_PEB*) pebAddress)->ProcessParameters),
            &rtlUserProcParamsAddress,
            sizeof(PVOID), NULL))
    {
        printf("Could not read the address of ProcessParameters!\n");
        return GetLastError();
    }

    /* read the CommandLine UNICODE_STRING structure */
    if (!ReadProcessMemory(processHandle,
        &(((_RTL_USER_PROCESS_PARAMETERS*) rtlUserProcParamsAddress)->CommandLine),
        &commandLine, sizeof(commandLine), NULL))
    {
        printf("Could not read CommandLine!\n");
        return GetLastError();
    }

    /* allocate memory to hold the command line */
    commandLineContents = (WCHAR *)malloc(commandLine.Length);

    /* read the command line */
    if (!ReadProcessMemory(processHandle, commandLine.Buffer,
        commandLineContents, commandLine.Length, NULL))
    {
        printf("Could not read the command line string!\n");
        return GetLastError();
    }

    /* print it */
    /* the length specifier is in characters, but commandLine.Length is in bytes */
    /* a WCHAR is 2 bytes */
    printf("%.*S\n", commandLine.Length / 2, commandLineContents);
    CloseHandle(processHandle);
    free(commandLineContents);

    return 0;
}

更多详情请关注Get the command line of a process

编辑(附加信息):

第一作者说:

CreateRemoteThread 仅适用于与会话 ID 相同的进程 调用者——在 Windows Vista 中,服务和其他系统进程运行 在会话 0 中,而用户程序在更高的会话中运行。最好和最安全的方法是读取每个 Windows 进程中存在的结构。

OpenProcess 也一样,你不能打开一个服务进程,或者 SYSTEMLOCAL SERVICE 打开的进程>NETWORK SERVICE,如果您是由用户(甚至是管理员)运行程序。

如果您的程序是服务,它可能已经由本地系统帐户运行,所以没问题。 但如果没有,一个解决方案是使用psexec 启动它:

  1. 下载PSEXEC并解压到某个文件夹。
  2. 以管理员身份打开提升的 CMD 提示符。
  3. 导航到解压 PSEXEC.EXE 的文件夹
  4. 运行:PSEXEC -i -s -d CMD
  5. 您将打开一个新的 CMD 提示符。
  6. 在新的 CMD 提示符中键入以下内容以证明您的身份:WHOAMI

你应该看到你是SYSTEM,现在你可以启动你的程序并查看所有进程的命令行。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 也谢谢你。这是一个有趣的链接。由于我只想检查我们自己进程的命令行(不是故意更改它),我应该没问题。我需要命令行的原因是我正在编写一个服务,以确保定义配置中的一组进程在某个时间点运行或不运行。为了验证我比较了路径到exe文件和进程的命令行。
    • @beutelfuchs:在这种情况下,互斥体或信号量会更好。
    • 如果要获取当前进程的命令行,GetCommandLine(链接ANSI版本,对于Unicode/"multibyte"/"wide-char",请参见GetCommandLineW按照约定)是你想要的程序。
    【解决方案4】:

    你可以inject a dll进入外部进程地址空间,然后调用GetCommandLine

    【讨论】:

    • 绝对是一个解决方案,但在我的情况下,与史蒂夫的方法相比更复杂。因为我必须使用 IPC 将“被盗”的命令行返回到我的查询过程中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2019-09-22
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多