Find the path of every running process in Python

3.3k views Asked by At

I want to find the path of every running process in windows. I tried to use the psutil module but it doesn't show me all the paths. It can't find many processes' paths because of the error: "psutil.AccessDenied"

c = wmi.WMI()
for process in c.Win32_Process():
        p = psutil.Process(int(process.ProcessId))
        try:
            path = p.exe()
        except:
            path = "-"

Is there another way to get a path of a process?

1

There are 1 answers

0
Eryk Sun On BEST ANSWER

As an administrator you might be able to get PROCESS_QUERY_LIMITED_INFORMATION (0x1000) access for a given process if you can't get PROCESS_QUERY_INFORMATION (0x400). QueryFullProcessImageNameW only requires limited access. However, not even this will work in all cases. For example, the security descriptor on csrss.exe only grants access to the SYSTEM account, not administrators. Another example is services.exe, which runs at System (S-1-16-16384) integrity level, while an administrator token is only at High (S-1-16-12288) integrity level.

You normally can't open a handle to such processes. But as an administrator you have the almost omnipotent SeDebugPrivilege. If you enable this privilege, Windows AccessCheck will suddenly become your best friend (but even best friends have their limits).

Below is some ctypes code to enable and disable a privilege in the current process access token. The privilege has to be present in the token to begin with, so be sure to run this using the Administrator account or as an elevated administrator if using UAC.

from ctypes import *
from ctypes.wintypes import *

kernel32 = WinDLL('kernel32', use_last_error=True)
advapi32 = WinDLL('advapi32', use_last_error=True)

SE_PRIVILEGE_ENABLED = 0x00000002
TOKEN_ALL_ACCESS = 0x000F0000 | 0x01FF

class LUID(Structure):
    _fields_ = (('LowPart',  DWORD),
                ('HighPart', LONG))

class LUID_AND_ATTRIBUTES(Structure):
    _fields_ = (('Luid',       LUID),
                ('Attributes', DWORD))

class TOKEN_PRIVILEGES(Structure):
    _fields_ = (('PrivilegeCount', DWORD),
                ('Privileges', LUID_AND_ATTRIBUTES * 1))
    def __init__(self, PrivilegeCount=1, *args):
        super(TOKEN_PRIVILEGES, self).__init__(PrivilegeCount, *args)

PDWORD = POINTER(DWORD)
PHANDLE = POINTER(HANDLE)
PLUID = POINTER(LUID)
PTOKEN_PRIVILEGES = POINTER(TOKEN_PRIVILEGES)

def errcheck_bool(result, func, args):
    if not result:
        raise WinError(get_last_error())
    return args

kernel32.CloseHandle.argtypes = (HANDLE,)

kernel32.GetCurrentProcess.errcheck = errcheck_bool
kernel32.GetCurrentProcess.restype = HANDLE

# https://msdn.microsoft.com/en-us/library/aa379295
advapi32.OpenProcessToken.errcheck = errcheck_bool
advapi32.OpenProcessToken.argtypes = (
    HANDLE,  # _In_  ProcessHandle
    DWORD,   # _In_  DesiredAccess
    PHANDLE) # _Out_ TokenHandle

# https://msdn.microsoft.com/en-us/library/aa379180
advapi32.LookupPrivilegeValueW.errcheck = errcheck_bool
advapi32.LookupPrivilegeValueW.argtypes = (
    LPCWSTR, # _In_opt_ lpSystemName
    LPCWSTR, # _In_     lpName
    PLUID)   # _Out_    lpLuid

# https://msdn.microsoft.com/en-us/library/aa375202
advapi32.AdjustTokenPrivileges.errcheck = errcheck_bool
advapi32.AdjustTokenPrivileges.argtypes = (
    HANDLE,            # _In_      TokenHandle
    BOOL,              # _In_      DisableAllPrivileges
    PTOKEN_PRIVILEGES, # _In_opt_  NewState
    DWORD,             # _In_      BufferLength
    PTOKEN_PRIVILEGES, # _Out_opt_ PreviousState
    PDWORD)            # _Out_opt_ ReturnLength

def enable_privilege(privilege):
    hToken = HANDLE()
    luid = LUID()
    advapi32.LookupPrivilegeValueW(None, privilege, byref(luid))
    try:
        advapi32.OpenProcessToken(kernel32.GetCurrentProcess(),
                                  TOKEN_ALL_ACCESS,
                                  byref(hToken))
        tp = TOKEN_PRIVILEGES()
        tp.Privileges[0].Luid = luid
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED
        advapi32.AdjustTokenPrivileges(hToken, False,
                                       byref(tp),
                                       sizeof(tp),
                                       None, None)
    finally:
        if hToken:
            kernel32.CloseHandle(hToken)

def disable_privilege(privilege):
    hToken = HANDLE()
    luid = LUID()
    advapi32.LookupPrivilegeValueW(None, privilege, byref(luid))
    try:
        advapi32.OpenProcessToken(kernel32.GetCurrentProcess(),
                                  TOKEN_ALL_ACCESS,
                                  byref(hToken))
        tp = TOKEN_PRIVILEGES()
        tp.Privileges[0].Luid = luid
        tp.Privileges[0].Attributes = 0
        advapi32.AdjustTokenPrivileges(hToken, False,
                                       byref(tp),
                                       sizeof(tp),
                                       None, None)
    finally:
        if hToken:
            kernel32.CloseHandle(hToken)

Test:

if __name__ == '__main__':
    import psutil
    system_process_names = {'smss.exe',
                            'csrss.exe',
                            'wininit.exe',
                            'winlogon.exe',
                            'services.exe',
                            'lsass.exe',
                            'lsm.exe'}
    system_processes = []

    print('SeDebugPrivilege Enabled')
    enable_privilege('SeDebugPrivilege')    
    for proc in psutil.process_iter():
        try:
            name = proc.name().lower()
            path = proc.exe()
        except psutil.AccessDenied:
            print('{:04d} ACCESS_DENIED'.format(proc.pid))
            continue
        if name in system_process_names:
            system_process_names.remove(name)
            system_processes.append(proc)
            print('{:04d} {}'.format(proc.pid, path))
    assert not system_process_names

    print('\nSeDebugPrivilege Disabled')
    disable_privilege('SeDebugPrivilege')
    for proc in system_processes:
        try:
            path = psutil.Process(proc.pid).exe() # avoid cache
        except psutil.AccessDenied:
            path = 'ACCESS DENIED'
        print('{:04d} {}'.format(proc.pid, path))

Output

SeDebugPrivilege Enabled
0000 ACCESS_DENIED
0004 ACCESS_DENIED
0256 C:\Windows\System32\smss.exe
0404 C:\Windows\System32\csrss.exe
0492 C:\Windows\System32\wininit.exe
0540 C:\Windows\System32\winlogon.exe
0588 C:\Windows\System32\services.exe
0596 C:\Windows\System32\lsass.exe
0604 C:\Windows\System32\lsm.exe
4704 ACCESS_DENIED

SeDebugPrivilege Disabled
0256 ACCESS DENIED
0404 ACCESS DENIED
0492 ACCESS DENIED
0540 ACCESS DENIED
0588 ACCESS DENIED
0596 ACCESS DENIED
0604 ACCESS DENIED

It's understandable to be denied access to the Idle (0) and System (4) processes. However, it's interesting that access was denied to PID 4704, even to a debugger. This is audiodg.exe, which is a protected process, as described in the "Protected Processes" white paper available for download at the Windows Hardware Dev Center Archive. Protected processes allow querying limited information, such as the image path. Let's quickly verify that this is the case:

>>> kernel32.OpenProcess(0x1000, 0, 4704)
304
>>> path = (c_wchar * 260)()
>>> size = c_uint(260)
>>> kernel32.QueryFullProcessImageNameW(304, 0, path, byref(size))
1
>>> path.value
'C:\\Windows\\System32\\audiodg.exe'