@@ -186,6 +186,7 @@ std::unordered_map<int, std::string> errorHints = {
186186
187187struct Statuses {
188188bool verbose;
189+ bool all;
189190// will probably add more later
190191};
191192
@@ -1750,6 +1751,8 @@ void FindProcessPorts(DWORD targetPid) {
17501751
17511752
17521753void PIDinspect (const std::vector<DWORD>& pids, const std::vector<std::string>& names, HANDLE hshot, Statuses stats, int related ) {
1754+ if (!stats.all ) {
1755+
17531756// ^^^ ooh guys look i'm in the void
17541757 DWORD pid = pids[0 ];
17551758 std::unordered_map<DWORD, PROCESSENTRY32> pidMap;
@@ -2013,6 +2016,270 @@ std::string FRAM = ""; // fram means formatted ram, i'm so creative at var namin
20132016
20142017 CloseHandle (hProcess);
20152018
2019+ } else {
2020+ DWORD pid = pids[0 ];
2021+ std::unordered_map<DWORD, PROCESSENTRY32> pidMap;
2022+ PROCESSENTRY32 pe32{};
2023+ pe32.dwSize = sizeof (PROCESSENTRY32);
2024+ if (Process32First (hshot, &pe32)) {
2025+ do {
2026+ pidMap.emplace (pe32.th32ProcessID , pe32);
2027+ } while (Process32Next (hshot, &pe32));
2028+ }
2029+ std::string procName = GetProcessNameFromPid (pid, hshot);
2030+ if (virtualTerminalEnabled) {
2031+ if (procName == " " ){
2032+ std::cout << " \033 [34mTarget:\033 [0m N/A\n\033 [34mProcess:\033 [0m N/A\n " ;
2033+ } else {
2034+ std::cout << " \033 [34mTarget:\033 [0m " << procName << " \033 [0m" << std::endl;
2035+ std::cout << " \033 [34mProcess:\033 [0m " << procName << " \033 [90m (pid " << std::to_string (pid) << " )\033 [0m" << std::endl;
2036+ }
2037+ } else {
2038+ if (procName == " " ){
2039+ std::cout << " Target: N/A\n Process: N/A\n " ;
2040+ } else {
2041+ std::cout << " Target: " << procName << std::endl;
2042+ std::cout << " Process: " << procName << " (pid " << std::to_string (pid) << " )" << std::endl;
2043+ }
2044+ }
2045+
2046+
2047+
2048+ HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE , pid);
2049+ // The above little handle opener is currently a somwehat "agressive" flag, since it
2050+ // Requests read access directly to the process' actual memory. This can get us rejected if called
2051+ // on a very high privilege process, such as lsass.exe This means that we can't read the memory
2052+ // even WITH SeDebugPrivilege enabled. Windows doesn't want ya sneaking around in that!
2053+ // So for that reason, I've added a fallback that only requests limited memory access,
2054+ // which should hopefully allow us to read some informatoin about hte process
2055+ if (!hProcess && GetLastError () == ERROR_ACCESS_DENIED) {
2056+ // This lets us know if the error was denied specifically for access reasons. THis will initiate our little fallback.
2057+ hProcess = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE , pid); // poor little guy getting limited of his full power
2058+ // This has been tested and it does let us get info about lsass.exe and even System! Woohoo!
2059+ // But of course, you need to be running as admin for this to work.
2060+ }
2061+ int errorCode = 0 ;
2062+ bool queryError = false ;
2063+ if (!hProcess) {
2064+ errorCode = GetLastError ();
2065+
2066+
2067+ if (virtualTerminalEnabled) {
2068+
2069+ queryError = true ;
2070+ std::cerr << " \033 [1;31mError:\033 [0m Could not open process with PID "
2071+ << pid << " . Error code: " << errorCode
2072+ << " \n Maybe it doesn't exist or access is denied." << std::endl;
2073+
2074+ } else {
2075+ queryError = true ;
2076+ std::cerr << " Error: Could not open process with PID "
2077+ << pid << " . Error code: " << errorCode
2078+ << " \n Maybe it doesn't exist or access is denied." << std::endl;
2079+
2080+ }
2081+ if (queryError) {
2082+ PrintErrorHints (errorCode, hshot);
2083+ }
2084+
2085+
2086+ }
2087+
2088+
2089+ char exePath[MAX_PATH] = {0 };
2090+ DWORD size = MAX_PATH;
2091+
2092+ if (QueryFullProcessImageNameA (hProcess, 0 , exePath, &size)) {
2093+ if (virtualTerminalEnabled) {
2094+ std::cout << " \033 [34mExecutable Path:\033 [0m " << exePath << std::endl;
2095+ } else {
2096+ std::cout << " Executable Path: " << exePath << std::endl;
2097+ }
2098+ } else {
2099+
2100+ errorCode = GetLastError ();
2101+ if (virtualTerminalEnabled) {
2102+ queryError = true ;
2103+ std::cerr << " \033 [1;31mError:\033 [0m Unable to query executable path. Error code: "
2104+ << errorCode
2105+ << " \n Maybe Access is Denied or the process is running entirely in RAM." << std::endl;
2106+ } else {
2107+ queryError = true ;
2108+ std::cerr << " Error: Unable to query executable path. Error code: "
2109+ << errorCode
2110+ << " \n Maybe Access is Denied or the process is running entirely in RAM." << std::endl;
2111+ }
2112+ if (queryError) {
2113+ PrintErrorHints (errorCode, hshot);
2114+ // it might seem like overkill to call the function every time there's an error,
2115+ // but if you remember we have a fallback for opening processes, so there are multiple
2116+ // places where an error can occur.
2117+ // for example, when testing this, the hint for error 5 (access denied) didn't show up
2118+ // since immediately after it was overwritten by error code 6 (valid but insufficient permissions) created by the fallback
2119+ // with the limited process info
2120+ }
2121+
2122+
2123+ }
2124+
2125+ // Use our little lookup table to give hints for specific errors
2126+ auto user = GetUserNameFromProcess (pid); // dang it dude it feels like such a war crime using auto in c++ 😭✌️
2127+ if (user.has_value ()) {
2128+ if (virtualTerminalEnabled) {
2129+ std::cout << " \033 [34mUser\033 [0m: " << WideToString (user.value ()) << std::endl;
2130+ } else {
2131+ std::cout << " User: " << WideToString (user.value ()) << std::endl;
2132+ }
2133+
2134+ } else {
2135+ if (virtualTerminalEnabled) {
2136+ std::cout << " \033 [1;34mUser\033 [0m: \033 [1;31mN/A (Failed to access info)\033 [0m" << std::endl;
2137+ } else {
2138+ std::cout << " User: N/A (Failed to access info)" << std::endl;
2139+ }
2140+ }
2141+
2142+ std::string command = GetCommandLine (hProcess);
2143+
2144+
2145+ if (virtualTerminalEnabled) {
2146+ std::cout << " \033 [1;32mCommand\033 [0m: " << command << std::endl;
2147+ } else {
2148+ std::cout << " Command: " << command << std::endl;
2149+ }
2150+ std::string workdir = GetWorkingDir (hProcess);
2151+
2152+
2153+
2154+ if (virtualTerminalEnabled) {
2155+ std::cout << " \033 [1;32mWorking Directory\033 [0m: " << workdir << std::endl;
2156+ } else {
2157+ std::cout << " Working Directory: " << workdir << std::endl;
2158+ }
2159+
2160+ // to get memory usage,
2161+ // we have to use psapi.h
2162+ // the metric we want is WorkingSetSize because the api spits out a bunch of other metrics we don't need
2163+ // hopefully this doesn't tank performance for yet another api call
2164+ // the command and working dir don't affect it because PEB walks take like 5 ms idk
2165+ // reference: https://learn.microsoft.com/en-us/windows/win32/psapi/collecting-memory-usage-information-for-a-process
2166+
2167+ PROCESS_MEMORY_COUNTERS pmc;
2168+ if ( GetProcessMemoryInfo ( hProcess, &pmc, sizeof (pmc)) ) {
2169+ // in the original snippet from windows
2170+ // THE BRACKET IS AFTER THE IF IN THE LINE DOWN
2171+ // i can't be talking about code organization but MICROSOFT WHAT
2172+ size_t RAM = pmc.WorkingSetSize ; // should be fine for this, unless you have like 10 exabytes of RAM for a single process somehow
2173+
2174+ std::string FRAM = " " ; // fram means formatted ram, i'm so creative at var naming
2175+ if (RAM < 1000 ) {
2176+ // if less than 1000 bytes (which is a kilobyte) then just return bytes
2177+ FRAM = std::to_string (RAM) + " B" ;
2178+ }
2179+ else if (RAM < 1000ULL * 1000 ) {
2180+
2181+ FRAM = std::to_string (RAM / 1000 ) + " KB" ;
2182+ }
2183+ else if (RAM < 1000ULL * 1000 * 1000 ) {
2184+
2185+ FRAM = std::to_string (RAM /( 1000ULL * 1000 )) + " MB" ;
2186+ }
2187+ else if (RAM < 1000ULL * 1000 * 1000 * 1000 ) {
2188+ FRAM = std::to_string (RAM /( 1000ULL * 1000 * 1000 )) + " GB" ;
2189+ }
2190+ else {
2191+ FRAM = std::to_string (RAM /( 1000ULL * 1000 * 1000 * 1000 )) + " TB" ;
2192+ // if someone actually reaches this i'm concerned
2193+ }
2194+
2195+
2196+
2197+
2198+ if (virtualTerminalEnabled) {
2199+ std::cout << " \033 [1;32mRAM Usage\033 [0m: " << FRAM << std::endl;
2200+ // I know RAM is technically a "nerdy tech term" or whatever and it'd be more logical
2201+ // to say "memory" but I feel like at this point everyone knows what RAM means
2202+ // especially with the RAM shortage, it should be ingrained in their brains
2203+
2204+ } else {
2205+ std::cout << " RAM Usage: " << FRAM << std::endl;
2206+ }
2207+ }
2208+
2209+
2210+
2211+
2212+
2213+
2214+
2215+
2216+
2217+
2218+ // TODO: add color text
2219+
2220+ if (virtualTerminalEnabled) {
2221+ std::cout << " \n\033 [1;35mWhy It Exists:\033 [0m\n " ;
2222+ } else {
2223+ std::cout << " \n Why It Exists:\n " ;
2224+ }
2225+ PrintAncestry (pid, hshot, pidMap);
2226+
2227+ FindProcessPorts (pid);
2228+
2229+
2230+
2231+
2232+
2233+ if (virtualTerminalEnabled) {
2234+ std::cout << " \n\033 [1;35mStarted:\033 [0m " << GetReadableFileTime (pid, pidMap) << std::endl;
2235+ } else {
2236+ std::cout << " \n Started: " << GetReadableFileTime (pid, pidMap) << std::endl;
2237+ }
2238+
2239+ if (pids.size () > 1 ) {
2240+ if (virtualTerminalEnabled) {
2241+ std::cout << " \033 [1;35mRelated Processes:\033 [0m\n " ;
2242+ } else {
2243+ std::cout << " Related Processes:\n " ;
2244+ }
2245+
2246+ for (size_t i = 1 ; i < pids.size (); i++) {
2247+ std::string relatedProcName = names[i];
2248+ if (virtualTerminalEnabled) {
2249+ std::cout << " \t\033 [36m" << relatedProcName << " \033 [90m (PID " << pids[i] << " )\033 [0m\n " ;
2250+ } else {
2251+ std::cout << " \t " << relatedProcName << " (PID " << pids[i] << " )\n " ;
2252+ }
2253+
2254+ }
2255+ }
2256+ /*
2257+ TODO:
2258+ This definitely needs a lot more details to be complete like witr. Unfortunately, windows needs even more shenanigans and a whole
2259+ lotta more code and admin access to get the same details. I will explain this some other day.
2260+
2261+ This is the output from witr for reference:
2262+ Target : node
2263+
2264+ Process : node (pid 14233)
2265+ User : pm2
2266+ Command : node index.js
2267+ Started : 2 days ago (Mon 2025-02-02 11:42:10 +05:30)
2268+ Restarts : 1
2269+
2270+ Why It Exists :
2271+ systemd (pid 1) → pm2 (pid 5034) → node (pid 14233)
2272+
2273+ Source : pm2
2274+
2275+ Working Dir : /opt/apps/expense-manager
2276+ Git Repo : expense-manager (main)
2277+ Listening : 127.0.0.1:5001
2278+ */
2279+
2280+ CloseHandle (hProcess);
2281+
2282+ }
20162283}
20172284
20182285struct ProcInfos {
@@ -2107,6 +2374,10 @@ int main(int argc, char* argv[]) {
21072374 if (i == 0 && args.size () > 1 ) {
21082375 continue ;
21092376 }
2377+
2378+ if (args[i] == " -a" || args[i] == " -all" ) {
2379+ s.all = true ;
2380+ }
21102381
21112382
21122383
@@ -2131,6 +2402,7 @@ int main(int argc, char* argv[]) {
21312402 std::cout << " \033 [1;33m--port <port>\033 [0m Specify the port to check" << std::endl;
21322403 std::cout << " \033 [1;33m--pid <pid>\033 [0m Specify the PID to check" << std::endl;
21332404 std::cout << " \033 [1;33m <name>\033 [0m Specify the process name to check" << std::endl;
2405+ std::cout << " \033 [1;33m-a, --all\033 [0m Performs more detailed lookup " << std::endl;
21342406
21352407 } else {
21362408 if (IsProcessElevated ()) {
@@ -2145,7 +2417,7 @@ int main(int argc, char* argv[]) {
21452417 std::cout << " --port <port> Specify the port to check" << std::endl;
21462418 std::cout << " --pid <pid> Specify the PID to check" << std::endl;
21472419 std::cout << " <name> Specify the process name to check" << std::endl;
2148-
2420+ std::cout << " -a, --all Performs more detailed lookup " << std::endl;
21492421
21502422 }
21512423 return 0 ; // exit after printing help because it might try to process -help as a process name otherwise
0 commit comments