From cddb4f6ff58b0184287557d13147f99f750a4b17 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:10:18 -0500 Subject: [PATCH 01/39] feat: Add command entry by reading process PEB --- main.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/main.cpp b/main.cpp index 4d1db61..d5055c8 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ // This file is part of win-witr. #include +#include #include #include #include @@ -399,6 +400,48 @@ std::string GetProcessNameFromPid(DWORD pid) { return ""; } +wchar_t* GetEnvironmentStringsW( HANDLE hproc ) +{ + void* p_ntdll = GetModuleHandle( L"ntdll.dll" ) ; + typedef NTSTATUS (__stdcall* tfn_qip ) ( HANDLE, + PROCESSINFOCLASS, void*, ULONG, PULONG ) ; + tfn_qip pfn_qip = tfn_qip( GetProcAddress( p_ntdll, + "NtQueryInformationProcess" ) ) ; + assert( pfn_qip ) ; + PROCESS_BASIC_INFORMATION pbi ; + ULONG res_len = 0 ; + NTSTATUS status = pfn_qip( hproc, ProcessBasicInformation, + &pbi, sizeof(pbi), &res_len ) ; + assert( res_len == sizeof(pbi) ) ; + size_t ppeb = size_t( pbi.PebBaseAddress ) ; + + char peb[ sizeof(PEB) ] ; + DWORD read ; + ReadProcessMemory( hproc, pbi.PebBaseAddress, + peb, sizeof(peb), &read ) ; + assert( read == sizeof(peb) ) ; + + enum { OFFSET_PEB = 0x10, OFFSET_X = 0x48 }; + + void* ptr = (void*) *(INT_PTR*)(peb + OFFSET_PEB ) ; + char buffer[ OFFSET_X + sizeof(void*) ] ; + ReadProcessMemory( hproc, ptr, buffer, sizeof(buffer), &read ) ; + assert( read == sizeof(buffer) ) ; + + void* penv = (void*) *(INT_PTR*)( buffer + OFFSET_X ) ; + enum { MAX_ENV_SIZE = 4096 }; + wchar_t* env = new wchar_t[ MAX_ENV_SIZE ] ; + ReadProcessMemory( hproc, penv, env, MAX_ENV_SIZE, &read ) ; + assert( read > 0 ) ; + + return env ; +} + +// i found this on this forum https://www.daniweb.com/programming/software-development/threads/114975/c-win-api-capture-env-variables-of-another-process +// i accidentally found a bunch of so called red teamers and malware documentation along the way which is... uh... weird... since i didn't even need it... +// and how is that stuff even on google man that sounds like something straight from the dark web + + void PrintAncestry(DWORD pid) { // now we're geting the name // we're making it slower by adding a bunch of snapshots @@ -692,6 +735,17 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void std::cout << "User: N/A (Failed to access info)"; } } + + wchar_t command = GetEnvironmentStringsW(hProcess); + + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\033[1;34mCommand\033[0m: " << WideToString(command); + } else { + std::cout << "Command: " << WideToString(command); + } + + + // literally very rough start i just rushed to get this done // still needs lots of error handling, some code modifying From 3c3df854a009e13d551479362255b7a822eef756 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:12:37 -0500 Subject: [PATCH 02/39] fix: try adding #define --- main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index d5055c8..20d3885 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2025 supervoidcoder // This file is part of win-witr. - + +#define _WIN32_WINNT 0x0501 +#define NO_STRICT #include #include #include From b66390e0273a936ecc7088c52693b0381c7b4882 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:23:48 -0500 Subject: [PATCH 03/39] fix: return pointer instead of character and add missing include statement --- main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 20d3885..6b725f3 100644 --- a/main.cpp +++ b/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #define windows_time_to_unix_epoch(x) ((x) - 116444736000000000LL) / 10000000LL // The above macro converts Windows FILETIME to Unix epoch time in seconds. @@ -738,7 +739,7 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void } } - wchar_t command = GetEnvironmentStringsW(hProcess); + wchar_t* command = GetEnvironmentStringsW(hProcess); if (IsVirtualTerminalModeEnabled()) { std::cout << "\033[1;34mCommand\033[0m: " << WideToString(command); From f20951d4f5bd3f87531aa8b59e1f309ee73e8643 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:26:54 -0500 Subject: [PATCH 04/39] fix: try changing dword to size_t --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 6b725f3..dae7560 100644 --- a/main.cpp +++ b/main.cpp @@ -419,7 +419,7 @@ wchar_t* GetEnvironmentStringsW( HANDLE hproc ) size_t ppeb = size_t( pbi.PebBaseAddress ) ; char peb[ sizeof(PEB) ] ; - DWORD read ; + SIZE_T read; ReadProcessMemory( hproc, pbi.PebBaseAddress, peb, sizeof(peb), &read ) ; assert( read == sizeof(peb) ) ; From 13e2acecd0f09b1ddacfef48553573d2baf75dc9 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:30:52 -0500 Subject: [PATCH 05/39] fix: remove stupid useless defines --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index dae7560..3f1ac32 100644 --- a/main.cpp +++ b/main.cpp @@ -2,8 +2,8 @@ // Copyright (C) 2025 supervoidcoder // This file is part of win-witr. -#define _WIN32_WINNT 0x0501 -#define NO_STRICT + + #include #include #include From 07fce973c613bed51c62e92d8972c55f8fb6244b Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:32:09 -0500 Subject: [PATCH 06/39] fix: a --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 3f1ac32..a26ae92 100644 --- a/main.cpp +++ b/main.cpp @@ -3,7 +3,7 @@ // This file is part of win-witr. - +#define NO_STRICT #include #include #include From 43b03304977bc88a53dc28db23e30827ff2cd29b Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:11:46 -0500 Subject: [PATCH 07/39] fix: Add x64 implementation to manually read process PEB and return command line string --- main.cpp | 97 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/main.cpp b/main.cpp index a26ae92..e3f7a1a 100644 --- a/main.cpp +++ b/main.cpp @@ -403,46 +403,66 @@ std::string GetProcessNameFromPid(DWORD pid) { return ""; } -wchar_t* GetEnvironmentStringsW( HANDLE hproc ) +std::string GetCommandLine(HANDLE hproc) { - void* p_ntdll = GetModuleHandle( L"ntdll.dll" ) ; - typedef NTSTATUS (__stdcall* tfn_qip ) ( HANDLE, - PROCESSINFOCLASS, void*, ULONG, PULONG ) ; - tfn_qip pfn_qip = tfn_qip( GetProcAddress( p_ntdll, - "NtQueryInformationProcess" ) ) ; - assert( pfn_qip ) ; - PROCESS_BASIC_INFORMATION pbi ; - ULONG res_len = 0 ; - NTSTATUS status = pfn_qip( hproc, ProcessBasicInformation, - &pbi, sizeof(pbi), &res_len ) ; - assert( res_len == sizeof(pbi) ) ; - size_t ppeb = size_t( pbi.PebBaseAddress ) ; - - char peb[ sizeof(PEB) ] ; - SIZE_T read; - ReadProcessMemory( hproc, pbi.PebBaseAddress, - peb, sizeof(peb), &read ) ; - assert( read == sizeof(peb) ) ; - - enum { OFFSET_PEB = 0x10, OFFSET_X = 0x48 }; - - void* ptr = (void*) *(INT_PTR*)(peb + OFFSET_PEB ) ; - char buffer[ OFFSET_X + sizeof(void*) ] ; - ReadProcessMemory( hproc, ptr, buffer, sizeof(buffer), &read ) ; - assert( read == sizeof(buffer) ) ; - - void* penv = (void*) *(INT_PTR*)( buffer + OFFSET_X ) ; - enum { MAX_ENV_SIZE = 4096 }; - wchar_t* env = new wchar_t[ MAX_ENV_SIZE ] ; - ReadProcessMemory( hproc, penv, env, MAX_ENV_SIZE, &read ) ; - assert( read > 0 ) ; - - return env ; + // we have to read the PEB, which is essentially the "header" of a process' RAM + // so far this implementation only works with x64 --> x64 process + // so it's wip + + auto queryInfo = (pNtQueryInformationProcess)GetProcAdress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +// this is a very sketchy line of code +// it calls NtQueryInformationProcess from internal kernel functions +// i would've saved myself all this pain if I just used the WMI wrapper +// but wmi has a reputation for being slow as heck +// thankfully, even though microslop claims this stuff is "undocumented", many generous +// red teamers and other people have created a quite sizable amount of docs +// so thank them, not me + +PROCCESS_BASIC_INFORMATION pbi; +if (NtQueryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { + // all this code seems very C-style since most of the stuff like docs were thought for c + // the code I'm basing this off manually creates a handle right here but we already created one + // so the handle gets passed to this function and we don't need to clean up our handle just yet, just return + // but we still should add a cout to see where it failed + + std::cerr << "NtQuery Failed"; + return ""; // failure + +} + +// the PEB for the cmd line stuff is somewhere in the PEB, called ProcessParamters +// then from there we can read the CommandLine struct +// it's all a bunch of dang structs with pointers to other structs + +PVOID procParamPtr = nullptr; +if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, sizeof(PVOID), NULL)) { + std:cerr << "Failed to read ProcessParameters pointer"; + // I know, very cryptic, but I will make this better later + + return ""; + +} + +UNICODE_STRING cmdLStruct; +if (!ReadProcessMemory(hproc, (BYTE)*procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), NULL)) { + std::cerr << "Failed to read CommandLine struct"; + return ""; + } -// i found this on this forum https://www.daniweb.com/programming/software-development/threads/114975/c-win-api-capture-env-variables-of-another-process -// i accidentally found a bunch of so called red teamers and malware documentation along the way which is... uh... weird... since i didn't even need it... -// and how is that stuff even on google man that sounds like something straight from the dark web +std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) +{ + std::cerr << "Failed to read buffer"; + return ""; +} + +std::wstring stringBuffer = buffer.data(); +// we don't wanna return a wstring so let's convert it +return WideToString(stringBuffer); +} + + void PrintAncestry(DWORD pid) { @@ -739,7 +759,8 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void } } - wchar_t* command = GetEnvironmentStringsW(hProcess); + std::string command = GetCommandLine(hProcess); + if (IsVirtualTerminalModeEnabled()) { std::cout << "\033[1;34mCommand\033[0m: " << WideToString(command); From 09e2dead4ca0d77c1bd753b5c8872351f4fe417a Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:18:11 -0500 Subject: [PATCH 08/39] fix: Fix typos and wrong functions --- main.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/main.cpp b/main.cpp index e3f7a1a..cb39729 100644 --- a/main.cpp +++ b/main.cpp @@ -409,7 +409,8 @@ std::string GetCommandLine(HANDLE hproc) // so far this implementation only works with x64 --> x64 process // so it's wip - auto queryInfo = (pNtQueryInformationProcess)GetProcAdress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); // this is a very sketchy line of code // it calls NtQueryInformationProcess from internal kernel functions // i would've saved myself all this pain if I just used the WMI wrapper @@ -418,8 +419,8 @@ std::string GetCommandLine(HANDLE hproc) // red teamers and other people have created a quite sizable amount of docs // so thank them, not me -PROCCESS_BASIC_INFORMATION pbi; -if (NtQueryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { +PROCESS_BASIC_INFORMATION pbi; +if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { // all this code seems very C-style since most of the stuff like docs were thought for c // the code I'm basing this off manually creates a handle right here but we already created one // so the handle gets passed to this function and we don't need to clean up our handle just yet, just return @@ -436,7 +437,7 @@ if (NtQueryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { PVOID procParamPtr = nullptr; if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, sizeof(PVOID), NULL)) { - std:cerr << "Failed to read ProcessParameters pointer"; + std::cerr << "Failed to read ProcessParameters pointer"; // I know, very cryptic, but I will make this better later return ""; @@ -444,7 +445,8 @@ if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, s } UNICODE_STRING cmdLStruct; -if (!ReadProcessMemory(hproc, (BYTE)*procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), NULL)) { +SIZE_T bytesRead2 = 0; +if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { std::cerr << "Failed to read CommandLine struct"; return ""; @@ -760,12 +762,12 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void } std::string command = GetCommandLine(hProcess); - + if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mCommand\033[0m: " << WideToString(command); + std::cout << "\033[1;34mCommand\033[0m: " << command; } else { - std::cout << "Command: " << WideToString(command); + std::cout << "Command: " << command; } From 8487b74fc06cd9f8de2286dc320b4d02aaf16e1c Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:23:38 -0500 Subject: [PATCH 09/39] fix: add missing newline after User entry --- main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index cb39729..ba1ff9a 100644 --- a/main.cpp +++ b/main.cpp @@ -748,16 +748,16 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void auto user = GetUserNameFromProcess(pid); // dang it dude it feels like such a war crime using auto in c++ 😭✌️ if (user.has_value()) { if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mUser\033[0m: " << WideToString(user.value()); + std::cout << "\033[1;34mUser\033[0m: " << WideToString(user.value()) << std::endl; } else { - std::cout << "User: " << WideToString(user.value()); + std::cout << "User: " << WideToString(user.value()) << std::endl; } } else { if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mUser\033[0m: \033[1;31mN/A (Failed to access info)\033[0m"; + std::cout << "\033[1;34mUser\033[0m: \033[1;31mN/A (Failed to access info)\033[0m" << std::endl; } else { - std::cout << "User: N/A (Failed to access info)"; + std::cout << "User: N/A (Failed to access info)" << std::endl; } } From 4e731b53a5db992805b00faf524616a1a1c69099 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:57:37 -0500 Subject: [PATCH 10/39] style: made error statements nicer --- main.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/main.cpp b/main.cpp index ba1ff9a..1a5c232 100644 --- a/main.cpp +++ b/main.cpp @@ -427,7 +427,7 @@ if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { // but we still should add a cout to see where it failed std::cerr << "NtQuery Failed"; - return ""; // failure + return "Failed to Access (wwitr:ntqueryfailed)"; // failure } @@ -437,26 +437,25 @@ if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { PVOID procParamPtr = nullptr; if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, sizeof(PVOID), NULL)) { - std::cerr << "Failed to read ProcessParameters pointer"; - // I know, very cryptic, but I will make this better later + - return ""; + return "Failed to Access (wwitr:procParamPtrRead)"; } UNICODE_STRING cmdLStruct; SIZE_T bytesRead2 = 0; if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { - std::cerr << "Failed to read CommandLine struct"; - return ""; + + return "Failed to Access (wwitr:cmdLStructFail)"; } std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { - std::cerr << "Failed to read buffer"; - return ""; + + return "Failed to Access (wwitr:bufferReadFail)"; } std::wstring stringBuffer = buffer.data(); From aca34163976a98de7a641d10a551b84c7775b857 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:02:48 -0500 Subject: [PATCH 11/39] fix: stdcerr remove --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 1a5c232..779ec58 100644 --- a/main.cpp +++ b/main.cpp @@ -426,7 +426,7 @@ if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { // so the handle gets passed to this function and we don't need to clean up our handle just yet, just return // but we still should add a cout to see where it failed - std::cerr << "NtQuery Failed"; + return "Failed to Access (wwitr:ntqueryfailed)"; // failure } From 529119529049f02267fe42aef800265c6a345c85 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:27:09 -0500 Subject: [PATCH 12/39] feat: add force_ansi env var --- .github/workflows/build.yml | 1 + main.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0564a0..c8eee48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,7 @@ jobs: shell: pwsh run: | # Run all test .bat files + $env:force_ansi = 1 Get-ChildItem -Path tests -Recurse -Filter *.bat | ForEach-Object { Write-Host "Running test: $($_.FullName)" & $_.FullName diff --git a/main.cpp b/main.cpp index 779ec58..0527555 100644 --- a/main.cpp +++ b/main.cpp @@ -100,6 +100,10 @@ void EnsureCurrentParentExe() { bool IsVirtualTerminalModeEnabled() { + if (GetEnvironmentVariableA("force_ansi", NULL, 0) > 0) { + return true; + } + // i'll probably make force-ansi a flag later but eh HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) return false; From c561d7b9acf92812f7b605fc83bb68965a427cdb Mon Sep 17 00:00:00 2001 From: supervoidcoder Date: Sun, 1 Feb 2026 23:01:19 -0500 Subject: [PATCH 13/39] fix: update error messages for Git Bash and add architecture support in GetCommandLine function --- main.cpp | 120 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/main.cpp b/main.cpp index 0527555..4135187 100644 --- a/main.cpp +++ b/main.cpp @@ -296,7 +296,7 @@ void PrintErrorHints(int errorCode) { std::cerr << "Hah, you're running this in Windows Subsystem for Linux. Run wsl as Admin!" << std::endl; - } else if (currentParentExe == "git-bash.exe") { + } else if (currentParentExe == "bash.exe") { std::cerr << "Uh, you're running this from Git Bash. Try running this program from an elevated Command Prompt or Powershell." << std::endl; } else { @@ -407,8 +407,15 @@ std::string GetProcessNameFromPid(DWORD pid) { return ""; } -std::string GetCommandLine(HANDLE hproc) -{ +std::string GetCommandLine(HANDLE hproc) { +#ifdef _M_X64 + + + BOOL isWow64 = IsWow64Process(hproc, &isWow64); + +bool isWoW64 = isWow64; // this variable naming will surely not cause any problemes in the forseeable future + +if (!isWoW64) { // we have to read the PEB, which is essentially the "header" of a process' RAM // so far this implementation only works with x64 --> x64 process // so it's wip @@ -465,6 +472,21 @@ if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Lengt std::wstring stringBuffer = buffer.data(); // we don't wanna return a wstring so let's convert it return WideToString(stringBuffer); + + +} else { + // unfortunately getting the PEB of a WoW64 process from an x64 process is not + // as straightforward as x64 --> x64 but uh... + // i like scraping my sanity off so i'm going to do it anyway MWAHAHAH + + #elif defined(_M_IX86) + return "x86 not supported yet"; + #elif defined(_M_ARM64) + return "ARM64 not supported yet"; +#else + return "Failed to Access (wwitr:unknownarch)"; +#endif +} } @@ -718,8 +740,11 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void DWORD size = MAX_PATH; if (QueryFullProcessImageNameA(hProcess, 0, exePath, &size)) { - - std::cout << "Executable Path: " << exePath << std::endl; + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\033[34mExecutable Path:\033[0m " << exePath << std::endl; + } else { + std::cout << "Executable Path: " << exePath << std::endl; + } } else { errorCode = GetLastError(); @@ -744,51 +769,56 @@ void PIDinspect(DWORD pid) { // ooh guys look i'm in the void // with the limited process info } - - } + + } - // Use our little lookup table to give hints for specific errors - auto user = GetUserNameFromProcess(pid); // dang it dude it feels like such a war crime using auto in c++ 😭✌️ - if (user.has_value()) { - if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mUser\033[0m: " << WideToString(user.value()) << std::endl; - } else { - std::cout << "User: " << WideToString(user.value()) << std::endl; - } - - } else { - if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mUser\033[0m: \033[1;31mN/A (Failed to access info)\033[0m" << std::endl; - } else { - std::cout << "User: N/A (Failed to access info)" << std::endl; - } - } + // Use our little lookup table to give hints for specific errors + auto user = GetUserNameFromProcess(pid); // dang it dude it feels like such a war crime using auto in c++ 😭✌️ + if (user.has_value()) { + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\033[34mUser\033[0m: " << WideToString(user.value()) << std::endl; + } else { + std::cout << "User: " << WideToString(user.value()) << std::endl; + } + + } else { + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\033[1;34mUser\033[0m: \033[1;31mN/A (Failed to access info)\033[0m" << std::endl; + } else { + std::cout << "User: N/A (Failed to access info)" << std::endl; + } + } - std::string command = GetCommandLine(hProcess); + std::string command = GetCommandLine(hProcess); - - if (IsVirtualTerminalModeEnabled()) { - std::cout << "\033[1;34mCommand\033[0m: " << command; - } else { - std::cout << "Command: " << command; - } - - - - - // literally very rough start i just rushed to get this done - // still needs lots of error handling, some code modifying - // so far i dont even know if the function works due to how rushed i did this - - + + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\033[1;32mCommand\033[0m: " << command; + } else { + std::cout << "Command: " << command; + } + + + + + + - - // TODO: add color text - - std::cout << "\nWhy It Exists:\n"; - PrintAncestry(pid); + + // TODO: add color text + + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\n\033[1;35mWhy It Exists:\033[0m\n"; + } else { + std::cout << "\nWhy It Exists:\n"; + } + PrintAncestry(pid); - std::cout << "\nStarted: " << GetReadableFileTime(pid) << std::endl; + if (IsVirtualTerminalModeEnabled()) { + std::cout << "\n\033[1;35mStarted:\033[0m " << GetReadableFileTime(pid) << std::endl; + } else { + std::cout << "\nStarted: " << GetReadableFileTime(pid) << std::endl; + } /* TODO: This definitely needs a lot more details to be complete like witr. Unfortunately, windows needs even more shenanigans and a whole From 1fab3626da29440ef6cf2b124731a851dde22f89 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:22:50 -0500 Subject: [PATCH 14/39] fix: add fail-fast false --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 200a4f5..c98f861 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,7 @@ permissions: jobs: build-and-test: strategy: + fail-fast: false matrix: include: - arch: x64 From 51d6a746c8adce8d0d0857dd566cd2fbabea94f2 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:28:09 -0500 Subject: [PATCH 15/39] fix: CI TESTS --- .github/workflows/build.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c98f861..4bff8e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,15 +66,16 @@ jobs: # Verify the exe is accessible Write-Host "Checking win-witr.exe availability..." win-witr --version + if ($LASTEXITCODE -ne 0) { + Write-Error "Test failed: $($_.Name)" + exit 1 + } # Run all test .bat files $env:force_ansi = 1 Get-ChildItem -Path tests -Recurse -Filter *.bat | ForEach-Object { Write-Host "Running test: $($_.FullName)" & $_.FullName - if ($LASTEXITCODE -ne 0) { - Write-Error "Test failed: $($_.Name)" - exit 1 - } + } From 7d4d5526a3b9ec9beb619581741f590c1f65554c Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:34:49 -0500 Subject: [PATCH 16/39] fix: add debug log --- main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 4135187..24a6922 100644 --- a/main.cpp +++ b/main.cpp @@ -478,7 +478,8 @@ return WideToString(stringBuffer); // unfortunately getting the PEB of a WoW64 process from an x64 process is not // as straightforward as x64 --> x64 but uh... // i like scraping my sanity off so i'm going to do it anyway MWAHAHAH - + return "Reading WoW64 process not supported yet"; +} #elif defined(_M_IX86) return "x86 not supported yet"; #elif defined(_M_ARM64) @@ -486,7 +487,7 @@ return WideToString(stringBuffer); #else return "Failed to Access (wwitr:unknownarch)"; #endif -} + } From 932b51f8c1d2ff58e23d8e33d6f90673cc398019 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:41:30 -0500 Subject: [PATCH 17/39] fix: correct wow64 checking --- main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 24a6922..8a5b86e 100644 --- a/main.cpp +++ b/main.cpp @@ -411,8 +411,11 @@ std::string GetCommandLine(HANDLE hproc) { #ifdef _M_X64 - BOOL isWow64 = IsWow64Process(hproc, &isWow64); - +BOOL isWow64 = FALSE; +if (!IsWow64Process(hproc, &isWow64)) { + + return "Failed to Access (wwitr:wow64checkfail)"; +} bool isWoW64 = isWow64; // this variable naming will surely not cause any problemes in the forseeable future if (!isWoW64) { From 3d24e3c3ad4766a6e86ef42dd63231d94c2ef8ac Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:49:50 -0500 Subject: [PATCH 18/39] ci: add way more process tests ci --- tests/process/process.bat | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/process/process.bat b/tests/process/process.bat index 84ae8e1..48390df 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -2,4 +2,16 @@ win-witr winlogon.exe win-witr lsass.exe win-witr win-witr.exe win-witr wininit.exe - +win-witr explorer.exe +win-witr Registry +win-witr csrss.exe +win-witr fontdrvhost.exe +win-witr svchost.exe +win-witr smss.exe +win-witr services.exe +win-witr userinit.exe +win-witr MsMpEng.exe +win-witr NisSrv.exe +win-witr dllhost.exe +win-witr powershell.exe +win-witr Runner.Listener From 960e7ea3247a513a5cf86fbfef8115a600c64912 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:12:37 +0000 Subject: [PATCH 19/39] feat: make it so the program actually knows the version --- .github/workflows/release.yml | 2 +- main.cpp | 72 ++++++++++++++++++++++++----------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15b6d95..4676874 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -335,7 +335,7 @@ jobs: set outName=win-witr-${{ matrix.arch }}.exe echo Compiling %outName%... - cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:%outName% + cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /DVERSION_NUMBER="${{ needs.prepare.outputs.version }}" /Fe:%outName% if errorlevel 1 exit /b 1 - name: Upload build artifact for ${{ matrix.arch }} diff --git a/main.cpp b/main.cpp index 8a5b86e..91f57ba 100644 --- a/main.cpp +++ b/main.cpp @@ -57,7 +57,10 @@ This is kept as a bunch of strings to be easier to call than a dictionary, map, Less words to type ;) */ std::string forkAuthor = ""; // if this is a fork of this project, put your name here! Please be nice and leave my name too :) -std::string version = "v0.1.0"; // Version of this Windows port +#ifndef VERSION_NUMBER +#define VERSION_NUMBER "v0.1.0" +#endif +std::string version = VERSION_NUMBER; // Version of this Windows port thread_local std::string currentParentExe = ""; // to store the name of our own parent process for error hints std::string WideToString(const std::wstring& wstr); @@ -413,8 +416,11 @@ std::string GetCommandLine(HANDLE hproc) { BOOL isWow64 = FALSE; if (!IsWow64Process(hproc, &isWow64)) { - - return "Failed to Access (wwitr:wow64checkfail)"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:wow64checkfail)\033[0m"; + } else { + return "Failed to Access (wwitr:wow64checkfail)"; + } } bool isWoW64 = isWow64; // this variable naming will surely not cause any problemes in the forseeable future @@ -440,9 +446,11 @@ if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { // so the handle gets passed to this function and we don't need to clean up our handle just yet, just return // but we still should add a cout to see where it failed - - return "Failed to Access (wwitr:ntqueryfailed)"; // failure - + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; // failure + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; // failure + } } // the PEB for the cmd line stuff is somewhere in the PEB, called ProcessParamters @@ -451,25 +459,31 @@ if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { PVOID procParamPtr = nullptr; if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, sizeof(PVOID), NULL)) { - - - return "Failed to Access (wwitr:procParamPtrRead)"; - + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } } UNICODE_STRING cmdLStruct; SIZE_T bytesRead2 = 0; if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { - - return "Failed to Access (wwitr:cmdLStructFail)"; - + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } } std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { - - return "Failed to Access (wwitr:bufferReadFail)"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } } std::wstring stringBuffer = buffer.data(); @@ -481,20 +495,32 @@ return WideToString(stringBuffer); // unfortunately getting the PEB of a WoW64 process from an x64 process is not // as straightforward as x64 --> x64 but uh... // i like scraping my sanity off so i'm going to do it anyway MWAHAHAH - return "Reading WoW64 process not supported yet"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mReading WoW64 process not supported yet\033[0m"; + } else { + return "Reading WoW64 process not supported yet"; + } } #elif defined(_M_IX86) - return "x86 not supported yet"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mx86 not supported yet\033[0m"; + } else { + return "x86 not supported yet"; + } #elif defined(_M_ARM64) - return "ARM64 not supported yet"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mARM64 not supported yet\033[0m"; + } else { + return "ARM64 not supported yet"; + } #else - return "Failed to Access (wwitr:unknownarch)"; + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:unknownarch)\033[0m"; + } else { + return "Failed to Access (wwitr:unknownarch)"; + } #endif -} - - - void PrintAncestry(DWORD pid) { // now we're geting the name From 4e70436fc356e558b399fe07c873f048ed44feaa Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:53:02 -0500 Subject: [PATCH 20/39] fix: missing bracket --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 91f57ba..bc7794d 100644 --- a/main.cpp +++ b/main.cpp @@ -520,7 +520,7 @@ return WideToString(stringBuffer); return "Failed to Access (wwitr:unknownarch)"; } #endif - +} void PrintAncestry(DWORD pid) { // now we're geting the name From f27388cc2126f82d61d1ea993bafdf22361c8d0c Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:13:11 -0500 Subject: [PATCH 21/39] ci: Remove wrong or not working tests from process.bat test CI --- tests/process/process.bat | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/process/process.bat b/tests/process/process.bat index 48390df..fcd7499 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -9,9 +9,7 @@ win-witr fontdrvhost.exe win-witr svchost.exe win-witr smss.exe win-witr services.exe -win-witr userinit.exe win-witr MsMpEng.exe -win-witr NisSrv.exe -win-witr dllhost.exe win-witr powershell.exe -win-witr Runner.Listener +win-witr Runner.Listener.exe + From 038d1726b05050961811b769153df3e0e4a5ae30 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:35:27 +0000 Subject: [PATCH 22/39] ci: more tests --- tests/process/process.bat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/process/process.bat b/tests/process/process.bat index fcd7499..e6b3e00 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -12,4 +12,6 @@ win-witr services.exe win-witr MsMpEng.exe win-witr powershell.exe win-witr Runner.Listener.exe - +win-witr cmd.exe +win-witr pwsh.exe +win-witr Runner.Worker.exe From 151bbfcad2b41284ba03c58e565fde3c8f2c5007 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:39:35 +0000 Subject: [PATCH 23/39] ci: add hosted-compute-agent to process.bat --- tests/process/process.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/process/process.bat b/tests/process/process.bat index e6b3e00..994eeae 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -15,3 +15,4 @@ win-witr Runner.Listener.exe win-witr cmd.exe win-witr pwsh.exe win-witr Runner.Worker.exe +win-witr hosted-compute-agent \ No newline at end of file From 3e3e1a1c9e14326533c5a2440cbaa10f504c8c23 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:45:41 +0000 Subject: [PATCH 24/39] ci: add provjobd.exe entries to process.bat --- tests/process/process.bat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/process/process.bat b/tests/process/process.bat index 994eeae..aebcc88 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -15,4 +15,6 @@ win-witr Runner.Listener.exe win-witr cmd.exe win-witr pwsh.exe win-witr Runner.Worker.exe -win-witr hosted-compute-agent \ No newline at end of file +win-witr hosted-compute-agent +win-witr provjobd.exe1953947029 +win-witr provjobd.exe \ No newline at end of file From 6cc86f372266d9e9b2a2b5c2c52a1c7014106eea Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:49:39 +0000 Subject: [PATCH 25/39] ci: add provjobd.exe handling to process.bat and create provdjob.bat --- tests/process/process.bat | 3 +-- tests/process/provdjob.bat | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/process/provdjob.bat diff --git a/tests/process/process.bat b/tests/process/process.bat index aebcc88..3882df9 100644 --- a/tests/process/process.bat +++ b/tests/process/process.bat @@ -16,5 +16,4 @@ win-witr cmd.exe win-witr pwsh.exe win-witr Runner.Worker.exe win-witr hosted-compute-agent -win-witr provjobd.exe1953947029 -win-witr provjobd.exe \ No newline at end of file + diff --git a/tests/process/provdjob.bat b/tests/process/provdjob.bat new file mode 100644 index 0000000..7c3efc7 --- /dev/null +++ b/tests/process/provdjob.bat @@ -0,0 +1,9 @@ +@echo off +REM Get the full process name using tasklist +for /f "tokens=1" %%i in ('tasklist ^| findstr /B "provjobd.exe"') do ( + echo Found: %%i + win-witr %%i + goto :done +) +echo Could not find provjobd.exe* +:done \ No newline at end of file From e586c8e179aba3720dd0070df47b034d57c8d041 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:15:12 +0000 Subject: [PATCH 26/39] feat: Add support for reading command line of 32-bit and 64-bit processes in Windows using undocumented ntdll.dll functions. That's right! UNDOCUMENTED!! MWAHHAHAHAH --- main.cpp | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index bc7794d..2b00bd7 100644 --- a/main.cpp +++ b/main.cpp @@ -34,6 +34,27 @@ #pragma comment(lib, "ws2_32.lib") // For Winsock (Networking) #pragma comment(lib, "shell32.lib") // For ShellExecute (Elevation) + +// i stole the following from google in totally NOT sketchy sites +// go to GetCommandLine function to see how these are used (and why) +typedef struct _UNICODE_STRING64 { + USHORT Length; + USHORT MaximumLength; + ULONG Pad; + ULONG64 Buffer; +} UNICODE_STRING64; + +typedef struct _RTL_USER_PROCESS_PARAMETERS64 { + BYTE Reserved1[16]; + ULONG64 Reserved2[10]; + UNICODE_STRING64 ImagePathName; + UNICODE_STRING64 CommandLine; // Offset 0x70 +} RTL_USER_PROCESS_PARAMETERS64; + +// --- Function Pointers for Undocumented NT Functions --- +typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(HANDLE, UINT, PVOID, ULONG, PULONG); +typedef NTSTATUS (NTAPI *pNtWow64ReadVirtualMemory64)(HANDLE, ULONG64, PVOID, ULONG64, PULONG64); + /* This is a Windows version of the tool witr, which is a utility for finding details about specific processes. @@ -502,17 +523,254 @@ return WideToString(stringBuffer); } } #elif defined(_M_IX86) + // so yknow, this part is for 32 bit processes + // but you can run 32 bit processes on 64 bit windows + // how? 🤔 it's because of what's called WoW64 (Windows on Windows 64) + // it's not really emulation (unlike Prism on Arm64) but more like a compatibility layer + // x64 processors can already run x86 code natively since x64 is sorta an "extension" of x86 + // it has the same instruction set plus more instructions, as well as the ability to use more than 4GB of RAM + // (not just more, but basically infinite amounts of RAM theoretically) + // so wow64 doesn't emulate the CPU, it just intercepts certain system calls and redirects them to 32 bit versions + // stored as different dlls in SysWoW64 folder + // This means that Windows is basically LYING to us if we are a 32 bit process running on 64 bit Windows + // Windows will look us dead in the eye and say "Yup, you're running on a 32 bit windows!" + // We can bypass this with this below function call + BOOL areWeWoW64 = FALSE; + AreWeWoW64(GetCurrentProcess(), &areWeWoW64); // check if WE are wow64 + if (!areWeWoW64) { + // if we're not wow64, then we're genuinely 32 bit windows + // we can run the same code as above but with 32 bit offsets + typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + +PROCESS_BASIC_INFORMATION pbi; +if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { + + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } +} + +PVOID procParamPtr = nullptr; +//for wow64 processes, the offset is different +if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x10, &procParamPtr, sizeof(PVOID), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } +} + +UNICODE_STRING cmdLStruct; +SIZE_T bytesRead2 = 0; +if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } +} + +std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) +{ + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } +} + +std::wstring stringBuffer = buffer.data(); +return WideToString(stringBuffer); +} else { + // if we are wow64, then we are a 32 bit process running on 64 bit windows + // so now we should check if the target process is wow64 too + + //⚠️🚨 indentation alert + // since literally like half this function and its different + // branches for compilation is copy pasted code, + // my indents got mangled :( + // and rn im too lazy to fix them so screw indentations + + + BOOL targetIsWow64 = FALSE; + + // if the target process is WoW64 too, then we can use the same code as above! + // easy peasy + + TargetIsWoW64(hproc, &targetIsWow64); + if (targetIsWow64) { + + typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + +PROCESS_BASIC_INFORMATION pbi; +if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { + + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } +} + +PVOID procParamPtr = nullptr; +//for wow64 processes, the offset is different +if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x10, &procParamPtr, sizeof(PVOID), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } +} + +UNICODE_STRING cmdLStruct; +SIZE_T bytesRead2 = 0; +if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { if (IsVirtualTerminalModeEnabled()) { - return "\033[31mx86 not supported yet\033[0m"; + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } +} + +std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) +{ + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } +} + +std::wstring stringBuffer = buffer.data(); +return WideToString(stringBuffer); + } else { - return "x86 not supported yet"; + // if the target process is NOT WoW64, that means + // we're a 32 bit process trying to read a 64 bit process + // which we will support + // but why??? 😭😭😭✌️ + // why would people do this??? if you have a x64 OS just download the x64 version of win-witr??? + + // that's right, I did this to MYSELF!! + + + // these are the least sketchiest links bro trust + // https://guidedhacking.com/threads/how-to-read-x64-memory-from-x86-using-ntwow64readvirtualmemory64.13789/ + // https://stackoverflow.com/questions/7446887/get-command-line-string-of-64-bit-process-from-32-bit-process#:~:text=%23include%20%22stdafx.h%22,=%20si.wProcessorArchitecture%20==%20PROCESSOR_ARCHITECTURE_AMD64%20? + // thanks google!!! + + HMODULE ntdll = GetModuleHandleA("ntdll.dll"); + auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess"); + auto readMem64 = (pNtWow64ReadVirtualMemory64)GetProcAddress(ntdll, "NtWow64ReadVirtualMemory64"); + + if (!queryInfo || !readMem64) return "Failed: Function pointers missing"; + + ULONG64 peb64Address = 0; + if (queryInfo(hproc, 26, &peb64Address, sizeof(peb64Address), NULL) != 0 || peb64Address == 0) { + return "Failed to Access (wwitr:ntqueryfailed)"; + } + + ULONG64 procParamPtr64 = 0; + if (readMem64(hproc, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL) != 0) { + return "Failed to Access (wwitr:procParamPtrRead)"; + } + + UNICODE_STRING64 cmdLStruct64; + if (readMem64(hproc, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL) != 0) { + return "Failed to Access (wwitr:cmdLStructFail)"; + } + + if (cmdLStruct64.Length == 0) return ""; + std::vector buffer(cmdLStruct64.Length / sizeof(wchar_t) + 1, 0); + if (readMem64(hproc, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL) != 0) { + return "Failed to Access (wwitr:bufferReadFail)"; + } + + std::wstring wstr(buffer.data()); + return WideToString(wstr); + + + } + +} #elif defined(_M_ARM64) + + +// this is just the same code apparently +//idk i don't use no surface laptops +BOOL isWow64 = FALSE; +if (!IsWow64Process(hproc, &isWow64)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:wow64checkfail)\033[0m"; + } else { + return "Failed to Access (wwitr:wow64checkfail)"; + } +} +bool isWoW64 = isWow64; + +if (!isWoW64) { + +typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + +PROCESS_BASIC_INFORMATION pbi; +if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { + + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } +} + +PVOID procParamPtr = nullptr; +if (!ReadProcessMemory(hproc, (BYTE*)pbi.PebBaseAddress + 0x20, &procParamPtr, sizeof(PVOID), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } +} + +UNICODE_STRING cmdLStruct; +SIZE_T bytesRead2 = 0; +if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cmdLStruct), &bytesRead2)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } +} + +std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) +{ if (IsVirtualTerminalModeEnabled()) { - return "\033[31mARM64 not supported yet\033[0m"; + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; } else { - return "ARM64 not supported yet"; + return "Failed to Access (wwitr:bufferReadFail)"; } +} + +std::wstring stringBuffer = buffer.data(); +return WideToString(stringBuffer); + + +} else { + + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mReading WoW64 process not supported yet\033[0m"; + } else { + return "Reading WoW64 process not supported yet"; + } +} #else if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:unknownarch)\033[0m"; From 5e352713a05594d4ccf32f57a4fedf51f1e7082f Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:17:52 +0000 Subject: [PATCH 27/39] fix: Replace AreWeWoW64 with IsWow64Process because i'm stupid and used teh wrong functions that didn't exis tbecause they don't exist and they don't exist due to the fact that they don't exist since they don't exist in the existence of the existencial nt kernel --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 2b00bd7..42a0284 100644 --- a/main.cpp +++ b/main.cpp @@ -536,7 +536,7 @@ return WideToString(stringBuffer); // Windows will look us dead in the eye and say "Yup, you're running on a 32 bit windows!" // We can bypass this with this below function call BOOL areWeWoW64 = FALSE; - AreWeWoW64(GetCurrentProcess(), &areWeWoW64); // check if WE are wow64 + IsWow64Process(GetCurrentProcess(), &areWeWoW64); // check if WE are wow64 if (!areWeWoW64) { // if we're not wow64, then we're genuinely 32 bit windows // we can run the same code as above but with 32 bit offsets @@ -601,7 +601,7 @@ return WideToString(stringBuffer); // if the target process is WoW64 too, then we can use the same code as above! // easy peasy - TargetIsWoW64(hproc, &targetIsWow64); + IsWow64Process(hproc, &targetIsWow64); if (targetIsWow64) { typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); From 942e3147e76c938993c6f98c8e080012a77a9b54 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:34:49 +0000 Subject: [PATCH 28/39] fix: Improve error handling for function pointer retrieval and memory access in process command line reading --- main.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/main.cpp b/main.cpp index 42a0284..b71049b 100644 --- a/main.cpp +++ b/main.cpp @@ -665,33 +665,77 @@ return WideToString(stringBuffer); // https://stackoverflow.com/questions/7446887/get-command-line-string-of-64-bit-process-from-32-bit-process#:~:text=%23include%20%22stdafx.h%22,=%20si.wProcessorArchitecture%20==%20PROCESSOR_ARCHITECTURE_AMD64%20? // thanks google!!! - HMODULE ntdll = GetModuleHandleA("ntdll.dll"); + HMODULE ntdll = GetModuleHandleA("ntdll.dll"); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess"); auto readMem64 = (pNtWow64ReadVirtualMemory64)GetProcAddress(ntdll, "NtWow64ReadVirtualMemory64"); - if (!queryInfo || !readMem64) return "Failed: Function pointers missing"; + if (!queryInfo || !readMem64) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } + } + + + HANDLE targetHandle = hproc; + HANDLE openedHandle = NULL; + DWORD targetPid = 0; + if (hproc != NULL) { + targetPid = GetProcessId(hproc); + } + if (targetPid != 0) { + + openedHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, targetPid); + if (openedHandle) targetHandle = openedHandle; + } ULONG64 peb64Address = 0; - if (queryInfo(hproc, 26, &peb64Address, sizeof(peb64Address), NULL) != 0 || peb64Address == 0) { - return "Failed to Access (wwitr:ntqueryfailed)"; + if (queryInfo(targetHandle, 26, &peb64Address, sizeof(peb64Address), NULL) != 0 || peb64Address == 0) { + if (openedHandle) CloseHandle(openedHandle); + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } } ULONG64 procParamPtr64 = 0; - if (readMem64(hproc, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL) != 0) { - return "Failed to Access (wwitr:procParamPtrRead)"; + if (readMem64(targetHandle, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL) != 0) { + if (openedHandle) CloseHandle(openedHandle); + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } } UNICODE_STRING64 cmdLStruct64; - if (readMem64(hproc, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL) != 0) { - return "Failed to Access (wwitr:cmdLStructFail)"; + if (readMem64(targetHandle, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL) != 0) { + if (openedHandle) CloseHandle(openedHandle); + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } + } + + if (cmdLStruct64.Length == 0) { + if (openedHandle) CloseHandle(openedHandle); + return ""; } - if (cmdLStruct64.Length == 0) return ""; std::vector buffer(cmdLStruct64.Length / sizeof(wchar_t) + 1, 0); - if (readMem64(hproc, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL) != 0) { - return "Failed to Access (wwitr:bufferReadFail)"; + if (readMem64(targetHandle, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL) != 0) { + if (openedHandle) CloseHandle(openedHandle); + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } } + if (openedHandle) CloseHandle(openedHandle); std::wstring wstr(buffer.data()); return WideToString(wstr); From 3a77bd61257aba69061c5dc60c4bf7405b8b2120 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:41:47 +0000 Subject: [PATCH 29/39] just add a bunch of debugs for now --- main.cpp | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index b71049b..1c6f45a 100644 --- a/main.cpp +++ b/main.cpp @@ -669,7 +669,12 @@ return WideToString(stringBuffer); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess"); auto readMem64 = (pNtWow64ReadVirtualMemory64)GetProcAddress(ntdll, "NtWow64ReadVirtualMemory64"); + std::cerr << "[DEBUG] ntdll=" << (void*)ntdll + << " queryInfo=" << (void*)queryInfo + << " readMem64=" << (void*)readMem64 << std::endl; + if (!queryInfo || !readMem64) { + std::cerr << "[ERROR] required functions not found in ntdll.dll" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; } else { @@ -677,22 +682,32 @@ return WideToString(stringBuffer); } } - HANDLE targetHandle = hproc; HANDLE openedHandle = NULL; DWORD targetPid = 0; if (hproc != NULL) { targetPid = GetProcessId(hproc); } + std::cerr << "[DEBUG] original hproc=" << (void*)hproc << " pid=" << targetPid << std::endl; + if (targetPid != 0) { - openedHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, targetPid); - if (openedHandle) targetHandle = openedHandle; + if (!openedHandle) { + std::cerr << "[WARN] OpenProcess failed pid=" << targetPid << " GetLastError=" << GetLastError() << std::endl; + } else { + std::cerr << "[DEBUG] openedHandle=" << (void*)openedHandle << std::endl; + targetHandle = openedHandle; + } } ULONG64 peb64Address = 0; - if (queryInfo(targetHandle, 26, &peb64Address, sizeof(peb64Address), NULL) != 0 || peb64Address == 0) { + NTSTATUS status = queryInfo(targetHandle, 26, &peb64Address, sizeof(peb64Address), NULL); + std::cerr << "[DEBUG] NtQueryInformationProcess returned=0x" << std::hex << status << std::dec + << " peb64Address=0x" << std::hex << peb64Address << std::dec << std::endl; + + if (status != 0 || peb64Address == 0) { if (openedHandle) CloseHandle(openedHandle); + std::cerr << "[ERROR] NtQueryInformationProcess failed or returned empty PEB64" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; } else { @@ -701,8 +716,13 @@ return WideToString(stringBuffer); } ULONG64 procParamPtr64 = 0; - if (readMem64(targetHandle, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL) != 0) { + status = readMem64(targetHandle, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL); + std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(peb+0x20) returned=0x" << std::hex << status << std::dec + << " procParamPtr64=0x" << std::hex << procParamPtr64 << std::dec << std::endl; + + if (status != 0) { if (openedHandle) CloseHandle(openedHandle); + std::cerr << "[ERROR] failed to read ProcessParameters pointer from PEB64" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; } else { @@ -711,8 +731,14 @@ return WideToString(stringBuffer); } UNICODE_STRING64 cmdLStruct64; - if (readMem64(targetHandle, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL) != 0) { + status = readMem64(targetHandle, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL); + std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(procParams+0x70) returned=0x" << std::hex << status << std::dec + << " Length=" << cmdLStruct64.Length << " MaximumLength=" << cmdLStruct64.MaximumLength + << " Buffer=0x" << std::hex << cmdLStruct64.Buffer << std::dec << std::endl; + + if (status != 0) { if (openedHandle) CloseHandle(openedHandle); + std::cerr << "[ERROR] failed to read UNICODE_STRING64 from remote process" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; } else { @@ -722,12 +748,17 @@ return WideToString(stringBuffer); if (cmdLStruct64.Length == 0) { if (openedHandle) CloseHandle(openedHandle); + std::cerr << "[DEBUG] CommandLine length is zero" << std::endl; return ""; } std::vector buffer(cmdLStruct64.Length / sizeof(wchar_t) + 1, 0); - if (readMem64(targetHandle, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL) != 0) { + status = readMem64(targetHandle, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL); + std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(cmdBuffer) returned=0x" << std::hex << status << std::dec << std::endl; + + if (status != 0) { if (openedHandle) CloseHandle(openedHandle); + std::cerr << "[ERROR] failed to read commandline buffer at 0x" << std::hex << cmdLStruct64.Buffer << std::dec << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; } else { From 8054ef091a29a00052b94b6ff431de81ee43f600 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:46:07 +0000 Subject: [PATCH 30/39] feat: Add PROCESS_BASIC_INFORMATION64 structure and update function pointer for NtWow64QueryInformationProcess64 --- main.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index 1c6f45a..8a47560 100644 --- a/main.cpp +++ b/main.cpp @@ -44,6 +44,14 @@ typedef struct _UNICODE_STRING64 { ULONG64 Buffer; } UNICODE_STRING64; +typedef struct _PROCESS_BASIC_INFORMATION64 { + ULONG64 Reserved1; + ULONG64 PebBaseAddress; + ULONG64 Reserved2[2]; + ULONG64 UniqueProcessId; + ULONG64 Reserved3; +} PROCESS_BASIC_INFORMATION64; + typedef struct _RTL_USER_PROCESS_PARAMETERS64 { BYTE Reserved1[16]; ULONG64 Reserved2[10]; @@ -54,6 +62,7 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS64 { // --- Function Pointers for Undocumented NT Functions --- typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(HANDLE, UINT, PVOID, ULONG, PULONG); typedef NTSTATUS (NTAPI *pNtWow64ReadVirtualMemory64)(HANDLE, ULONG64, PVOID, ULONG64, PULONG64); +typedef NTSTATUS (NTAPI *pNtWow64QueryInformationProcess64)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); /* @@ -666,14 +675,14 @@ return WideToString(stringBuffer); // thanks google!!! HMODULE ntdll = GetModuleHandleA("ntdll.dll"); - auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(ntdll, "NtQueryInformationProcess"); + auto queryInfo64 = (pNtWow64QueryInformationProcess64)GetProcAddress(ntdll, "NtWow64QueryInformationProcess64"); auto readMem64 = (pNtWow64ReadVirtualMemory64)GetProcAddress(ntdll, "NtWow64ReadVirtualMemory64"); std::cerr << "[DEBUG] ntdll=" << (void*)ntdll - << " queryInfo=" << (void*)queryInfo + << " queryInfo64=" << (void*)queryInfo64 << " readMem64=" << (void*)readMem64 << std::endl; - if (!queryInfo || !readMem64) { + if (!queryInfo64 || !readMem64) { std::cerr << "[ERROR] required functions not found in ntdll.dll" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; @@ -700,14 +709,17 @@ return WideToString(stringBuffer); } } - ULONG64 peb64Address = 0; - NTSTATUS status = queryInfo(targetHandle, 26, &peb64Address, sizeof(peb64Address), NULL); - std::cerr << "[DEBUG] NtQueryInformationProcess returned=0x" << std::hex << status << std::dec + PROCESS_BASIC_INFORMATION64 pbi64{}; + ULONG returnLen = 0; + NTSTATUS status = queryInfo64(targetHandle, ProcessBasicInformation, &pbi64, sizeof(pbi64), &returnLen); + ULONG64 peb64Address = pbi64.PebBaseAddress; + std::cerr << "[DEBUG] NtWow64QueryInformationProcess64 returned=0x" << std::hex << status << std::dec + << " returnLen=" << returnLen << " peb64Address=0x" << std::hex << peb64Address << std::dec << std::endl; if (status != 0 || peb64Address == 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[ERROR] NtQueryInformationProcess failed or returned empty PEB64" << std::endl; + std::cerr << "[ERROR] NtWow64QueryInformationProcess64 failed or returned empty PEB64" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; } else { From 629046443fd470fa8ce5fe14b857b7a0180f0fac Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:51:55 +0000 Subject: [PATCH 31/39] refactor: Remove debug and error logging statements for function pointer retrieval and memory access SINCE IT FINALLY WORKS NOW --- main.cpp | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/main.cpp b/main.cpp index 8a47560..25dd0c8 100644 --- a/main.cpp +++ b/main.cpp @@ -678,12 +678,7 @@ return WideToString(stringBuffer); auto queryInfo64 = (pNtWow64QueryInformationProcess64)GetProcAddress(ntdll, "NtWow64QueryInformationProcess64"); auto readMem64 = (pNtWow64ReadVirtualMemory64)GetProcAddress(ntdll, "NtWow64ReadVirtualMemory64"); - std::cerr << "[DEBUG] ntdll=" << (void*)ntdll - << " queryInfo64=" << (void*)queryInfo64 - << " readMem64=" << (void*)readMem64 << std::endl; - if (!queryInfo64 || !readMem64) { - std::cerr << "[ERROR] required functions not found in ntdll.dll" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; } else { @@ -697,29 +692,17 @@ return WideToString(stringBuffer); if (hproc != NULL) { targetPid = GetProcessId(hproc); } - std::cerr << "[DEBUG] original hproc=" << (void*)hproc << " pid=" << targetPid << std::endl; - if (targetPid != 0) { openedHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, targetPid); - if (!openedHandle) { - std::cerr << "[WARN] OpenProcess failed pid=" << targetPid << " GetLastError=" << GetLastError() << std::endl; - } else { - std::cerr << "[DEBUG] openedHandle=" << (void*)openedHandle << std::endl; - targetHandle = openedHandle; - } + if (openedHandle) targetHandle = openedHandle; } PROCESS_BASIC_INFORMATION64 pbi64{}; ULONG returnLen = 0; NTSTATUS status = queryInfo64(targetHandle, ProcessBasicInformation, &pbi64, sizeof(pbi64), &returnLen); ULONG64 peb64Address = pbi64.PebBaseAddress; - std::cerr << "[DEBUG] NtWow64QueryInformationProcess64 returned=0x" << std::hex << status << std::dec - << " returnLen=" << returnLen - << " peb64Address=0x" << std::hex << peb64Address << std::dec << std::endl; - if (status != 0 || peb64Address == 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[ERROR] NtWow64QueryInformationProcess64 failed or returned empty PEB64" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; } else { @@ -729,12 +712,8 @@ return WideToString(stringBuffer); ULONG64 procParamPtr64 = 0; status = readMem64(targetHandle, peb64Address + 0x20, &procParamPtr64, sizeof(procParamPtr64), NULL); - std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(peb+0x20) returned=0x" << std::hex << status << std::dec - << " procParamPtr64=0x" << std::hex << procParamPtr64 << std::dec << std::endl; - if (status != 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[ERROR] failed to read ProcessParameters pointer from PEB64" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; } else { @@ -744,13 +723,8 @@ return WideToString(stringBuffer); UNICODE_STRING64 cmdLStruct64; status = readMem64(targetHandle, procParamPtr64 + 0x70, &cmdLStruct64, sizeof(cmdLStruct64), NULL); - std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(procParams+0x70) returned=0x" << std::hex << status << std::dec - << " Length=" << cmdLStruct64.Length << " MaximumLength=" << cmdLStruct64.MaximumLength - << " Buffer=0x" << std::hex << cmdLStruct64.Buffer << std::dec << std::endl; - if (status != 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[ERROR] failed to read UNICODE_STRING64 from remote process" << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; } else { @@ -760,17 +734,13 @@ return WideToString(stringBuffer); if (cmdLStruct64.Length == 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[DEBUG] CommandLine length is zero" << std::endl; return ""; } std::vector buffer(cmdLStruct64.Length / sizeof(wchar_t) + 1, 0); status = readMem64(targetHandle, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL); - std::cerr << "[DEBUG] NtWow64ReadVirtualMemory64(cmdBuffer) returned=0x" << std::hex << status << std::dec << std::endl; - if (status != 0) { if (openedHandle) CloseHandle(openedHandle); - std::cerr << "[ERROR] failed to read commandline buffer at 0x" << std::hex << cmdLStruct64.Buffer << std::dec << std::endl; if (IsVirtualTerminalModeEnabled()) { return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; } else { From 90a7a56111bda436a0f531a58972d8c28aeb2a7e Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:21:20 +0000 Subject: [PATCH 32/39] feat: Address code review issues. Fixed label with undefined block in build.yml. Fixed version number in release.yml and main.cpp. --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 46 +++++++++++++++-------------------- main.cpp | 30 +++++++++++++++++------ 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bff8e2..63af9a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,7 +67,7 @@ jobs: Write-Host "Checking win-witr.exe availability..." win-witr --version if ($LASTEXITCODE -ne 0) { - Write-Error "Test failed: $($_.Name)" + Write-Error "it's broken 💥" exit 1 } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4676874..d21e5a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -310,33 +310,27 @@ jobs: - uses: actions/checkout@v4 - name: Compile for ${{ matrix.arch }} - shell: cmd + shell: pwsh run: | - @echo off - REM Find vcvarsall.bat dynamically - for /f "usebackq tokens=*" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do ( - set "VS_PATH=%%i" - ) - - if not exist "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" ( - echo Error: vcvarsall.bat not found. - exit /b 1 - ) - - REM Map architecture for cross-compilation (host_target) - REM GitHub Actions windows-latest runners are x64, so we need x64_ for cross-compilation - set "TARGET_ARCH=${{ matrix.arch }}" - set "VCVARS_ARCH=%TARGET_ARCH%" - if "%TARGET_ARCH%"=="x86" set "VCVARS_ARCH=x64_x86" - if "%TARGET_ARCH%"=="arm64" set "VCVARS_ARCH=x64_arm64" - - REM Initialize environment for the target architecture - call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" %VCVARS_ARCH% - - set outName=win-witr-${{ matrix.arch }}.exe - echo Compiling %outName%... - cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /DVERSION_NUMBER="${{ needs.prepare.outputs.version }}" /Fe:%outName% - if errorlevel 1 exit /b 1 + # Set version as environment variable + $env:VERSION_NUMBER = "${{ needs.prepare.outputs.version }}" + + # Find and initialize MSVC + $vsPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ` + -latest -products * ` + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` + -property installationPath + + $vcvarsPath = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat" + + $targetArch = "${{ matrix.arch }}" + $vcvarsArch = $targetArch + if ($targetArch -eq "x86") { $vcvarsArch = "x64_x86" } + if ($targetArch -eq "arm64") { $vcvarsArch = "x64_arm64" } + + # Use cmd to call vcvarsall and then cl + $outName = "win-witr-${{ matrix.arch }}.exe" + cmd /c "`"$vcvarsPath`" $vcvarsArch && cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:$outName" - name: Upload build artifact for ${{ matrix.arch }} uses: actions/upload-artifact@v4 diff --git a/main.cpp b/main.cpp index 25dd0c8..b4be88e 100644 --- a/main.cpp +++ b/main.cpp @@ -87,10 +87,13 @@ This is kept as a bunch of strings to be easier to call than a dictionary, map, Less words to type ;) */ std::string forkAuthor = ""; // if this is a fork of this project, put your name here! Please be nice and leave my name too :) -#ifndef VERSION_NUMBER -#define VERSION_NUMBER "v0.1.0" -#endif -std::string version = VERSION_NUMBER; // Version of this Windows port +std::string version = []() { + char buf[256]; + if (GetEnvironmentVariableA("VERSION_NUMBER", buf, sizeof(buf)) > 0) { + return std::string(buf); + } + return std::string("v0.1.0"); +}(); thread_local std::string currentParentExe = ""; // to store the name of our own parent process for error hints std::string WideToString(const std::wstring& wstr); @@ -506,7 +509,12 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cm } } -std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { + return ""; +} + +size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); +std::vector buffer(wchar_count + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { if (IsVirtualTerminalModeEnabled()) { @@ -732,12 +740,13 @@ return WideToString(stringBuffer); } } - if (cmdLStruct64.Length == 0) { + if (cmdLStruct64.Length == 0 || (cmdLStruct64.Length % sizeof(wchar_t)) != 0 || cmdLStruct64.Length > 65534) { if (openedHandle) CloseHandle(openedHandle); return ""; } - std::vector buffer(cmdLStruct64.Length / sizeof(wchar_t) + 1, 0); + size_t wchar_count = cmdLStruct64.Length / sizeof(wchar_t); + std::vector buffer(wchar_count + 1, 0); status = readMem64(targetHandle, cmdLStruct64.Buffer, buffer.data(), cmdLStruct64.Length, NULL); if (status != 0) { if (openedHandle) CloseHandle(openedHandle); @@ -806,7 +815,12 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x70, &cmdLStruct, sizeof(cm } } -std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { + return ""; +} + +size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); +std::vector buffer(wchar_count + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { if (IsVirtualTerminalModeEnabled()) { From 66e342ba9686133d74a990a3bd05417ce269c885 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:33:53 +0000 Subject: [PATCH 33/39] feat: read WoW64 from x64 and arm64 --- main.cpp | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 12 deletions(-) diff --git a/main.cpp b/main.cpp index b4be88e..5aac4c4 100644 --- a/main.cpp +++ b/main.cpp @@ -44,6 +44,12 @@ typedef struct _UNICODE_STRING64 { ULONG64 Buffer; } UNICODE_STRING64; +typedef struct _UNICODE_STRING32 { + USHORT Length; + USHORT MaximumLength; + ULONG Buffer; +} UNICODE_STRING32; + typedef struct _PROCESS_BASIC_INFORMATION64 { ULONG64 Reserved1; ULONG64 PebBaseAddress; @@ -530,14 +536,65 @@ return WideToString(stringBuffer); } else { - // unfortunately getting the PEB of a WoW64 process from an x64 process is not - // as straightforward as x64 --> x64 but uh... - // i like scraping my sanity off so i'm going to do it anyway MWAHAHAH - if (IsVirtualTerminalModeEnabled()) { - return "\033[31mReading WoW64 process not supported yet\033[0m"; - } else { - return "Reading WoW64 process not supported yet"; + // haahhahahah reading wow64 from x64 is so funny waahahahah + // what if we do the most logical thing and just put everything in a + // try catch... honestly I don't know what's all the hate with + // it, the performance hit is negligible if there's no errors + // and I think it's only slow in python + auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } + } + + ULONG_PTR peb32Address = 0; + NTSTATUS status = queryInfo(hproc, ProcessWow64Information, &peb32Address, sizeof(peb32Address), NULL); + if (status != 0 || peb32Address == 0) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } + } + + ULONG procParamPtr32 = 0; + if (!ReadProcessMemory(hproc, (BYTE*)peb32Address + 0x10, &procParamPtr32, sizeof(procParamPtr32), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } + } + + UNICODE_STRING32 cmdLStruct32{}; + if (!ReadProcessMemory(hproc, (BYTE*)(ULONG_PTR)procParamPtr32 + 0x40, &cmdLStruct32, sizeof(cmdLStruct32), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } + } + + if (cmdLStruct32.Length == 0 || (cmdLStruct32.Length % sizeof(wchar_t)) != 0 || cmdLStruct32.Length > 65534) { + return ""; + } + + size_t wchar_count = cmdLStruct32.Length / sizeof(wchar_t); + std::vector buffer(wchar_count + 1, 0); + if (!ReadProcessMemory(hproc, (PVOID)(ULONG_PTR)cmdLStruct32.Buffer, buffer.data(), cmdLStruct32.Length, NULL)) + { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } } + + std::wstring stringBuffer = buffer.data(); + return WideToString(stringBuffer); } #elif defined(_M_IX86) // so yknow, this part is for 32 bit processes @@ -835,12 +892,62 @@ return WideToString(stringBuffer); } else { - - if (IsVirtualTerminalModeEnabled()) { - return "\033[31mReading WoW64 process not supported yet\033[0m"; - } else { - return "Reading WoW64 process not supported yet"; + // no clue if this works + + auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } + } + + ULONG_PTR peb32Address = 0; + NTSTATUS status = queryInfo(hproc, ProcessWow64Information, &peb32Address, sizeof(peb32Address), NULL); + if (status != 0 || peb32Address == 0) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:ntqueryfailed)\033[0m"; + } else { + return "Failed to Access (wwitr:ntqueryfailed)"; + } } + + ULONG procParamPtr32 = 0; + if (!ReadProcessMemory(hproc, (BYTE*)peb32Address + 0x10, &procParamPtr32, sizeof(procParamPtr32), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:procParamPtrRead)\033[0m"; + } else { + return "Failed to Access (wwitr:procParamPtrRead)"; + } + } + + UNICODE_STRING32 cmdLStruct32{}; + if (!ReadProcessMemory(hproc, (BYTE*)(ULONG_PTR)procParamPtr32 + 0x40, &cmdLStruct32, sizeof(cmdLStruct32), NULL)) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } + } + + if (cmdLStruct32.Length == 0 || (cmdLStruct32.Length % sizeof(wchar_t)) != 0 || cmdLStruct32.Length > 65534) { + return ""; + } + + size_t wchar_count = cmdLStruct32.Length / sizeof(wchar_t); + std::vector buffer(wchar_count + 1, 0); + if (!ReadProcessMemory(hproc, (PVOID)(ULONG_PTR)cmdLStruct32.Buffer, buffer.data(), cmdLStruct32.Length, NULL)) + { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:bufferReadFail)\033[0m"; + } else { + return "Failed to Access (wwitr:bufferReadFail)"; + } + } + + std::wstring stringBuffer = buffer.data(); + return WideToString(stringBuffer); } #else if (IsVirtualTerminalModeEnabled()) { From bb1be524d9fb31dd78f5c7aa44da35ff71af318a Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:36:23 +0000 Subject: [PATCH 34/39] fix: Add error handling for build process in release.yml --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d21e5a3..1d27435 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -331,6 +331,10 @@ jobs: # Use cmd to call vcvarsall and then cl $outName = "win-witr-${{ matrix.arch }}.exe" cmd /c "`"$vcvarsPath`" $vcvarsArch && cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:$outName" + if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed with exit code $LASTEXITCODE" + Exit $LASTEXITCODE + } - name: Upload build artifact for ${{ matrix.arch }} uses: actions/upload-artifact@v4 From 68c537d6d98a0441f848a3bd02b94cd5dac33390 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:07:40 +0000 Subject: [PATCH 35/39] feat: Add error handling for function pointer retrieval in main.cpp --- main.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/main.cpp b/main.cpp index 5aac4c4..6384268 100644 --- a/main.cpp +++ b/main.cpp @@ -470,6 +470,13 @@ if (!isWoW64) { typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } +} // this is a very sketchy line of code // it calls NtQueryInformationProcess from internal kernel functions // i would've saved myself all this pain if I just used the WMI wrapper @@ -616,6 +623,13 @@ return WideToString(stringBuffer); // we can run the same code as above but with 32 bit offsets typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } +} PROCESS_BASIC_INFORMATION pbi; if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { @@ -680,6 +694,13 @@ return WideToString(stringBuffer); typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } +} PROCESS_BASIC_INFORMATION pbi; if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) { From 65c6fbdac06c16c26578cc0a08e1921b6c8268ae Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:14:06 -0500 Subject: [PATCH 36/39] fix: version number embed during compilation --- main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.cpp b/main.cpp index 6384268..56f6363 100644 --- a/main.cpp +++ b/main.cpp @@ -94,11 +94,15 @@ Less words to type ;) */ std::string forkAuthor = ""; // if this is a fork of this project, put your name here! Please be nice and leave my name too :) std::string version = []() { +`#ifdef` VERSION_NUMBER + return std::string(VERSION_NUMBER); +`#else` char buf[256]; if (GetEnvironmentVariableA("VERSION_NUMBER", buf, sizeof(buf)) > 0) { return std::string(buf); } return std::string("v0.1.0"); +`#endif` }(); thread_local std::string currentParentExe = ""; // to store the name of our own parent process for error hints From 29a3138aeab08defe0e8455cc71e01ff8b094d8d Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:29:04 +0000 Subject: [PATCH 37/39] fix: version --- .github/workflows/release.yml | 3 ++- main.cpp | 14 +++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d27435..00b7218 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -330,7 +330,8 @@ jobs: # Use cmd to call vcvarsall and then cl $outName = "win-witr-${{ matrix.arch }}.exe" - cmd /c "`"$vcvarsPath`" $vcvarsArch && cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:$outName" + $ver = "${{ needs.prepare.outputs.version_number }}" + cmd /c "`"$vcvarsPath`" $vcvarsArch && cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /DVERSION_NUMBER=\"$ver\" /Fe:$outName" if ($LASTEXITCODE -ne 0) { Write-Host "Build failed with exit code $LASTEXITCODE" Exit $LASTEXITCODE diff --git a/main.cpp b/main.cpp index 56f6363..8704f48 100644 --- a/main.cpp +++ b/main.cpp @@ -94,15 +94,11 @@ Less words to type ;) */ std::string forkAuthor = ""; // if this is a fork of this project, put your name here! Please be nice and leave my name too :) std::string version = []() { -`#ifdef` VERSION_NUMBER - return std::string(VERSION_NUMBER); -`#else` - char buf[256]; - if (GetEnvironmentVariableA("VERSION_NUMBER", buf, sizeof(buf)) > 0) { - return std::string(buf); - } - return std::string("v0.1.0"); -`#endif` +#ifdef VERSION_NUMBER + return std::string(VERSION_NUMBER); // Release builds only +#else + return std::string("dev-build"); // Local builds - no env var check +#endif }(); thread_local std::string currentParentExe = ""; // to store the name of our own parent process for error hints From f82f6f28b253fa0499130e736b229e53f27d3e6e Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:48:01 +0000 Subject: [PATCH 38/39] IDK --- main.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 8704f48..2b106c0 100644 --- a/main.cpp +++ b/main.cpp @@ -661,7 +661,16 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cm } } -std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } +} + +size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); +std::vector buffer(wchar_count + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { if (IsVirtualTerminalModeEnabled()) { @@ -732,7 +741,16 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cm } } -std::vector buffer(cmdLStruct.Length / sizeof(wchar_t) + 1, 0); +if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; + } else { + return "Failed to Access (wwitr:cmdLStructFail)"; + } +} + +size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); +std::vector buffer(wchar_count + 1, 0); if (!ReadProcessMemory(hproc, cmdLStruct.Buffer, buffer.data(), cmdLStruct.Length, NULL)) { if (IsVirtualTerminalModeEnabled()) { From 7ddec0984543b7b94429083fd714773ac405463a Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:01:43 +0000 Subject: [PATCH 39/39] address code review --- main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/main.cpp b/main.cpp index 2b106c0..05010d2 100644 --- a/main.cpp +++ b/main.cpp @@ -662,11 +662,7 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cm } if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { - if (IsVirtualTerminalModeEnabled()) { - return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; - } else { - return "Failed to Access (wwitr:cmdLStructFail)"; - } + return ""; } size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); @@ -742,11 +738,7 @@ if (!ReadProcessMemory(hproc, (BYTE*)procParamPtr + 0x40, &cmdLStruct, sizeof(cm } if (cmdLStruct.Length == 0 || (cmdLStruct.Length % sizeof(wchar_t)) != 0 || cmdLStruct.Length > 65534) { - if (IsVirtualTerminalModeEnabled()) { - return "\033[31mFailed to Access (wwitr:cmdLStructFail)\033[0m"; - } else { - return "Failed to Access (wwitr:cmdLStructFail)"; - } + return ""; } size_t wchar_count = cmdLStruct.Length / sizeof(wchar_t); @@ -882,6 +874,14 @@ if (!isWoW64) { typedef NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); auto queryInfo = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); +if (!queryInfo) { + if (IsVirtualTerminalModeEnabled()) { + return "\033[31mFailed to Access (wwitr:functionptrs)\033[0m"; + } else { + return "Failed to Access (wwitr:functionptrs)"; + } +} + PROCESS_BASIC_INFORMATION pbi; if (queryInfo(hproc, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) != 0) {