Evil UniqueProcessId
Completely hiding a process on a modern Windows system is nearly impossible, not only because of the many detection vectors but also because of PatchGuard protecting most important structures.
However, there is one field in the EPROCESS
struct that is not protected, and it is the UniqueProcessId
. While it might not seem that interesting at first glance, there are a few funny things you can do with it.
Stealing ID of other process
While I would not recommend this, since it can cause system instability (even when you restore it afterwards), it is possible to change the process ID to match another process. If this process was created before our target process (and therefore is first after the system process in the EPROCESS
linked list), any call to OpenProcess
or similar will result in a handle being opened for the process we stole the ID from, and not our target process, therefore “hiding” our target process.
PEPROCESS process;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)1234, &process);
if (!NT_SUCCESS(status))
return status;
// EPROCESS->UniqueProcessId
*(PULONG64)((PBYTE)process + 0x440) = 6152; // PID of OneDrive
Using 64-bit ID
Most user-mode APIs like OpenProcess
, Process32First
, Process32Next
, and GetProcessId
only allow you to specify a 32-bit integer as a process ID or will strip the higher 32 bits from their return values. This means that if you set the process ID to a 64-bit integer, the kernel will handle it just fine, but user-mode APIs will start failing.
PEPROCESS process;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)1234, &process);
if (!NT_SUCCESS(status))
return status;
// EPROCESS->UniqueProcessId
*(PULONG64)((PBYTE)process + 0x440) = 0xFFFFFFFFFFFFFFFF;