You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过进程PID获取命令行参数?代码触发访问违例

进程命令行参数获取问题修复

问题场景

需要获取进程创建时传入的命令行参数,例如在CMD中执行notepad.exe --abc,希望获取完整命令行notepad.exe --abc或仅参数部分--abc。基于相关问题的第二个答案编写代码后,在peb.ProcessParameters->CommandLine处触发访问违例异常,原可编译代码如下:

#include <iostream>
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")

std::wstring GetProcCommandLine(DWORD pid)
{
    std::wstring commandLine;

    // Open a handle to the process
    HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (process != NULL)
    {
        // Get the address of the PEB
        PROCESS_BASIC_INFORMATION pbi;
        if (NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) == 0) // 0: STATUS_SUCCESS
        {
            // Get the address of the process parameters in the PEB
            PEB peb;
            ZeroMemory(&peb, sizeof(peb));
            if (ReadProcessMemory(process, pbi.PebBaseAddress, &peb, sizeof(peb), NULL))
            {
                // Get the command line arguments from the process parameters
                UNICODE_STRING commandLineArgs = peb.ProcessParameters->CommandLine;
                WCHAR* buffer = new WCHAR[commandLineArgs.Length + 1];
                ZeroMemory(buffer, (commandLineArgs.Length + 1) * sizeof(WCHAR));
                if (ReadProcessMemory(process, commandLineArgs.Buffer, buffer, commandLineArgs.Length, NULL))
                {
                    std::wstring wideCommandLine(buffer, commandLineArgs.Length / sizeof(WCHAR));
                    commandLine = wideCommandLine;
                }
                delete[] buffer;
            }
        }
        CloseHandle(process);
    }

    return commandLine;
}

int main()
{
    std::wstring commandLine = L"C:\\WINDOWS\\system32\\notepad.exe arg1 arg2 arg3";
    STARTUPINFO si = {};
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = {};
    if (!CreateProcessW(NULL, &commandLine[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
        std::cout << "failed in creating the process.";

    std::wstring procCommandLine = GetProcCommandLine(pi.dwProcessId);
}

错误原因

PEB结构体中的ProcessParameters是目标进程虚拟地址空间内的指针,不是当前进程的有效内存地址。直接在本地进程中解引用这个指针,会因为访问不属于当前进程的内存空间而触发访问违例。

修复方案

需要先从目标进程中读取RTL_USER_PROCESS_PARAMETERS结构体的内容到本地,再从中获取命令行信息。修复后的代码如下:

#include <iostream>
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")

std::wstring GetProcCommandLine(DWORD pid)
{
    std::wstring commandLine;

    // 打开目标进程,需要足够权限
    HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (!process)
        return commandLine;

    // 获取PEB基地址
    PROCESS_BASIC_INFORMATION pbi{};
    if (NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), nullptr) != 0)
    {
        CloseHandle(process);
        return commandLine;
    }

    // 读取PEB结构体到本地
    PEB peb{};
    if (!ReadProcessMemory(process, pbi.PebBaseAddress, &peb, sizeof(peb), nullptr))
    {
        CloseHandle(process);
        return commandLine;
    }

    // 读取RTL_USER_PROCESS_PARAMETERS结构体到本地
    RTL_USER_PROCESS_PARAMETERS params{};
    if (!ReadProcessMemory(process, peb.ProcessParameters, &params, sizeof(params), nullptr))
    {
        CloseHandle(process);
        return commandLine;
    }

    // 分配缓冲区读取命令行内容
    WCHAR* buffer = new WCHAR[params.CommandLine.Length / sizeof(WCHAR) + 1]{};
    if (ReadProcessMemory(process, params.CommandLine.Buffer, buffer, params.CommandLine.Length, nullptr))
    {
        commandLine = std::wstring(buffer, params.CommandLine.Length / sizeof(WCHAR));
    }

    delete[] buffer;
    CloseHandle(process);
    return commandLine;
}

// 从完整命令行中提取仅参数部分(第一个空格后的内容)
std::wstring ExtractArguments(const std::wstring& fullCommandLine)
{
    size_t firstSpace = fullCommandLine.find(L' ');
    if (firstSpace == std::wstring::npos)
        return L"";
    return fullCommandLine.substr(firstSpace + 1);
}

int main()
{
    std::wstring commandLine = L"C:\\WINDOWS\\system32\\notepad.exe arg1 arg2 arg3";
    STARTUPINFO si{};
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi{};

    if (!CreateProcessW(nullptr, &commandLine[0], nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
    {
        std::cout << "进程创建失败,错误码:" << GetLastError() << std::endl;
        return 1;
    }

    // 等待进程初始化完成,避免读取时PEB未完全初始化
    Sleep(100);

    std::wstring fullCmdLine = GetProcCommandLine(pi.dwProcessId);
    std::wstring argsOnly = ExtractArguments(fullCmdLine);

    std::wcout << L"完整命令行:" << fullCmdLine << std::endl;
    std::wcout << L"仅参数部分:" << argsOnly << std::endl;

    // 关闭进程和线程句柄
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    return 0;
}

额外说明

  • 权限问题:确保当前进程有足够权限打开目标进程,否则OpenProcess会失败。
  • 进程初始化等待:创建进程后直接读取可能因为PEB未完全初始化导致读取失败,加入Sleep或等待进程进入就绪状态可以避免该问题。
  • 参数提取:ExtractArguments函数通过查找第一个空格,提取空格后的内容作为参数部分,适用于标准命令行格式。

内容的提问来源于stack exchange,提问作者Cesar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:45:07