如何通过进程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, ¶ms, 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
相关产品推荐
相关产品推荐

