diff --git a/plugins/builtin/include/content/providers/process_memory_provider.hpp b/plugins/builtin/include/content/providers/process_memory_provider.hpp index ed57b094d..e22dbbca1 100644 --- a/plugins/builtin/include/content/providers/process_memory_provider.hpp +++ b/plugins/builtin/include/content/providers/process_memory_provider.hpp @@ -86,6 +86,7 @@ namespace hex::plugin::builtin { struct Process { u32 id; std::string name; + std::string commandLine; ImGuiExt::Texture icon; }; diff --git a/plugins/builtin/source/content/providers/process_memory_provider.cpp b/plugins/builtin/source/content/providers/process_memory_provider.cpp index 0bd9fefe3..01a71ceb7 100644 --- a/plugins/builtin/source/content/providers/process_memory_provider.cpp +++ b/plugins/builtin/source/content/providers/process_memory_provider.cpp @@ -5,6 +5,7 @@ #if defined(OS_WINDOWS) #include + #include #include #include #elif defined(OS_MACOS) @@ -30,9 +31,77 @@ #include #include #include +#include namespace hex::plugin::builtin { + using namespace wolv::literals; + +#if defined(OS_WINDOWS) + + using NtQueryInformationProcessFunc = NTSTATUS (NTAPI*)( + HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength + ); + + std::string getProcessCommandLine(HANDLE processHandle) { + // Get NtQueryInformationProcess function + HMODULE ntdll = GetModuleHandleA("ntdll.dll"); + if (!ntdll) return ""; + + auto funcNtQueryInformationProcess = (NtQueryInformationProcessFunc) + (void*)GetProcAddress(ntdll, "NtQueryInformationProcess"); + if (!funcNtQueryInformationProcess) return ""; + + // Query for PROCESS_BASIC_INFORMATION to get PEB address + PROCESS_BASIC_INFORMATION pbi = {}; + ULONG len = 0; + NTSTATUS status = funcNtQueryInformationProcess( + processHandle, + ProcessBasicInformation, + &pbi, + sizeof(pbi), + &len + ); + + if (status != 0 || !pbi.PebBaseAddress) return ""; + + // Read PEB to get ProcessParameters address + PEB peb = {}; + SIZE_T bytesRead = 0; + if (!ReadProcessMemory(processHandle, pbi.PebBaseAddress, &peb, sizeof(peb), &bytesRead)) + return ""; + + // Read RTL_USER_PROCESS_PARAMETERS + RTL_USER_PROCESS_PARAMETERS params = {}; + if (!ReadProcessMemory(processHandle, peb.ProcessParameters, ¶ms, sizeof(params), &bytesRead)) + return ""; + + // Read the command line string + std::vector cmdLine(params.CommandLine.Length / sizeof(wchar_t) + 1, 0); + if (!ReadProcessMemory(processHandle, params.CommandLine.Buffer, cmdLine.data(), + params.CommandLine.Length, &bytesRead)) + return ""; + + // Convert wide string to narrow string + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, cmdLine.data(), -1, nullptr, 0, nullptr, nullptr); + if (sizeNeeded <= 0) return ""; + + std::string result(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, cmdLine.data(), -1, &result[0], sizeNeeded, nullptr, nullptr); + + // Remove trailing null terminator + if (!result.empty() && result.back() == '\0') + result.pop_back(); + + return result; + } + +#endif + bool ProcessMemoryProvider::open() { if (m_selectedProcess == nullptr) return false; @@ -202,7 +271,7 @@ namespace hex::plugin::builtin { } } - m_processes.push_back({ u32(processId), processName, std::move(texture) }); + m_processes.push_back({ u32(processId), processName, getProcessCommandLine(processHandle), std::move(texture) }); } #elif defined(OS_MACOS) std::array pids; @@ -213,7 +282,7 @@ namespace hex::plugin::builtin { const auto result = proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE); if (result == PROC_PIDTBSDINFO_SIZE) { - m_processes.emplace_back(pids[i], proc.pbi_name, ImGuiExt::Texture()); + m_processes.emplace_back(pids[i], proc.pbi_name, proc->pbi_comm, ImGuiExt::Texture()); } } #elif defined(OS_LINUX) @@ -228,13 +297,40 @@ namespace hex::plugin::builtin { continue; // not a PID } - wolv::io::File file(path /"cmdline", wolv::io::File::Mode::Read); + // Parse status file + wolv::io::File file(path / "status", wolv::io::File::Mode::Read); + std::map statusInfo; + if (file.isValid()) { + const auto statusContent = file.readString(1_MiB); + for (const auto &line : wolv::util::splitString(statusContent, "\n")) { + const auto delimiterPos = line.find(':'); + if (delimiterPos != std::string::npos) { + const auto key = line.substr(0, delimiterPos); + const auto value = line.substr(delimiterPos + 1); + statusInfo[key] = wolv::util::trim(value); + } + } + } + + // Skip kernel threads + if (statusInfo.contains("Kthread") && statusInfo["Kthread"] == "1") + continue; + + // Parse process name + std::string processName; + if (statusInfo.contains("Name")) { + processName = statusInfo["Name"]; + } + + wolv::io::File cmdlineFile(path / "cmdline", wolv::io::File::Mode::Read); if (!file.isValid()) continue; - std::string processName = file.readString(0xF'FFFF); + auto commandLine = cmdlineFile.readString(1_MiB); + if (processName.empty()) + processName = commandLine; - m_processes.emplace_back(processId, processName, ImGuiExt::Texture()); + m_processes.emplace_back(processId, processName, commandLine, ImGuiExt::Texture()); } #endif } @@ -265,7 +361,11 @@ namespace hex::plugin::builtin { ImGui::TableNextColumn(); auto height = ImGui::GetTextLineHeight(); - ImGui::Image(process->icon, { height, height }); + if (process->icon.isValid()) { + ImGui::Image(process->icon, { height, height }); + } else { + ImGui::Dummy({ height, height }); + } ImGui::TableNextColumn(); ImGuiExt::TextFormatted("{}", process->id); @@ -274,6 +374,15 @@ namespace hex::plugin::builtin { if (ImGui::Selectable(process->name.c_str(), m_selectedProcess != nullptr && process->id == m_selectedProcess->id, ImGuiSelectableFlags_SpanAllColumns)) m_selectedProcess = process; + if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal)) { + if (ImGui::BeginTooltip()) { + ImGui::PushTextWrapPos(200_scaled); + ImGui::TextWrapped("%s", process->commandLine.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + } + ImGui::PopID(); }