Get full executable path of SYSTEM process without full admin rights/elevation on Windows?

392 views Asked by At

On Windows, it is possible to enumerate all running processes on the system, e.g. via the CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS...) API.

However, this will directly only report the executable name and the process ID without further info.

To identify the binary of the process, you need to OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION...) and QueryFullProcessImageName.

And OpenProcess, even with PROCESS_QUERY_LIMITED_INFORMATION, requires the SeDebugPrivilege when you need to query processes run under an elevated context - which means you need to be admin / elevated.

Question: Is it possible to get the full path to the binary of ANY given process (ID), including Windows services running as SYSTEM and other admin processes, without the SeDebugPrivilege or even better without requiring any additional rights from a non-elevated context?

OS: Windows 10 from v1809 upwards.

Here is the C++ example code to read out the info: https://gist.github.com/bilbothebaggins/f5852dbf22177620975207fe60f2f1f1

On my machine, given that I run with UAC, non-elevated, I see:

  • I can query my own processes (non-elevated)
  • I can also query a process started by another local, non-privileged user.
  • Query on all Windows services fails. (These running as SYSTEM)

X Y problem disclaimer: As a pre-build step, I need to determine for a given local directory if any (different user / admin / Windows service) background processes are running who's executables/binaries reside in the local directory. The build runs as non-admin. I can think of various other workarounds for this (all more complicated in our case), so can we stick to the question as asked above for this here? Thanks.

1

There are 1 answers

8
RbMm On

you can do it by using NtQuerySystemInformation with SystemProcessIdInformation (0x58) - with this way - we not need open process handle.

typedef struct SYSTEM_PROCESS_ID_INFORMATION
{
    HANDLE ProcessId;
    UNICODE_STRING ImageName;
} *PSYSTEM_PROCESS_ID_INFORMATION;

NTSTATUS GetProcessPathById(HANDLE ProcessId)
{
    SYSTEM_PROCESS_ID_INFORMATION spii = { ProcessId, { 0, 0x80 } };
    NTSTATUS status;

    do 
    {
        if (spii.ImageName.Buffer)
        {
            LocalFree(spii.ImageName.Buffer);
        }

        if (!(spii.ImageName.Buffer = (PWSTR)LocalAlloc(LMEM_FIXED, spii.ImageName.MaximumLength)))
        {
            status = STATUS_NO_MEMORY;
            break;
        }

    } while (STATUS_INFO_LENGTH_MISMATCH == (status = 
        NtQuerySystemInformation(SystemProcessIdInformation, &spii, sizeof(spii), 0)));

    if (0 > status)
    {
        DbgPrint("%p - %x\n", ProcessId, status); 
    }
    else
    {
        DbgPrint("%p - %wZ\n", ProcessId, &spii.ImageName);
    }

    if (spii.ImageName.Buffer)
    {
        LocalFree(spii.ImageName.Buffer);
    }

    return status;
}