Re: NtQuerySystemInformation

eryksun <[email protected]> Thu, 29 Oct 2015 08:18:33 -0500
Newsgroups gmane.comp.python.ctypes
Message-ID <CACL+1atRNA5F_eYwb6C5_ciBT2pjiZbyDTpUnvfFmveCSdN-eQ@mail.gmail.com>
On 10/29/15, Cristian Badescu <[email protected]> wrote:
> After calling NtQuerySystemInformation with _stdcall (windll) as Diez
> suggested, i am getting STATUS_INFO_LENGTH_MISMATCH. After calling realloc
> to get a new size a few time trying to get to the actual buffer size
> required (for SystemHandleInformation the function does not return the
> required buffer size) the function fails with an 0xC0000005 (Access
> Violation) error. This is the code i use:

Use SystemExtendedHandleInformation (64) because it handles PIDs
greater than 65535. Note that your version of SYSTEM_HANDLE
incorrectly makes the ProcessId field a c_ulong. Actually it's a
c_ushort followed by another reserved c_ushort.

Resize the structure to exactly the size returned in the 4th
parameter. Do this by calling ctypes.resize; don't call C realloc
directly.

Here's a version that I've tested on Windows 7 and 10 in both 32-bit
and 64-bit processes in Python 2.7, 3.4, and 3.5. It has improved type
safety, error handling, and demonstrates using a property to return an
array for a variable-sized array field.

from ctypes import *
from ctypes.wintypes import *
import functools

ntdll = WinDLL('ntdll')

PVOID       = c_void_p
PULONG      = POINTER(ULONG)
ULONG_PTR   = WPARAM
ACCESS_MASK = DWORD

@functools.total_ordering
class NTSTATUS(c_long):
    def __eq__(self, other):
        if hasattr(other, 'value'):
            other = other.value
        return self.value == other
    def __ne__(self, other):
        if hasattr(other, 'value'):
            other = other.value
        return self.value != other
    def __lt__(self, other):
        if hasattr(other, 'value'):
            other = other.value
        return self.value < other
    def __bool__(self):
        return self.value >= 0
    def __repr__(self):
        value = c_ulong.from_buffer(self).value
        return 'NTSTATUS(%08x)' % value

STATUS_INFO_LENGTH_MISMATCH = NTSTATUS(0xC0000004)

def WinErrorFromNtStatus(status):
    last_error = ntdll.RtlNtStatusToDosError(status)
    return WinError(last_error)

class SYSTEM_INFORMATION_CLASS(c_ulong):
    def __repr__(self):
        return '%s(%s)' % (type(self).__name__, self.value)

SystemExtendedHandleInformation = SYSTEM_INFORMATION_CLASS(64)

class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX(Structure):
    _fields_ = (('Object',                PVOID),
                ('UniqueProcessId',       HANDLE),
                ('HandleValue',           HANDLE),
                ('GrantedAccess',         ACCESS_MASK),
                ('CreatorBackTraceIndex', USHORT),
                ('ObjectTypeIndex',       USHORT),
                ('HandleAttributes',      ULONG),
                ('Reserved',              ULONG))

class SYSTEM_INFORMATION(Structure):
    pass

PSYSTEM_INFORMATION = POINTER(SYSTEM_INFORMATION)

class SYSTEM_HANDLE_INFORMATION_EX(SYSTEM_INFORMATION):
    _fields_ = (('NumberOfHandles', ULONG_PTR),
                ('Reserved',        ULONG_PTR),
                ('_Handles', SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX * 1))
    @property
    def Handles(self):
        arr_t = (SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *
                 self.NumberOfHandles)
        return POINTER(arr_t)(self._Handles)[0]

ntdll.NtQuerySystemInformation.restype = NTSTATUS
ntdll.NtQuerySystemInformation.argtypes = (
    SYSTEM_INFORMATION_CLASS, # SystemInformationClass
    PSYSTEM_INFORMATION,      # SystemInformation
    ULONG,                    # SystemInformationLength
    PULONG)                   # ReturnLength

def list_handles():
    info = SYSTEM_HANDLE_INFORMATION_EX()
    length = ULONG()
    while True:
        status = ntdll.NtQuerySystemInformation(
                        SystemExtendedHandleInformation,
                        byref(info),
                        sizeof(info),
                        byref(length))
        if status != STATUS_INFO_LENGTH_MISMATCH:
            break
        resize(info, length.value)
    if status < 0:
        raise WinErrorFromNtStatus(status)
    return info.Handles

if __name__ == '__main__':
    import os
    import sys
    if len(sys.argv) > 1:
        pid = int(sys.argv[1])
    else:
        pid = os.getpid()
    for h in list_handles():
        if h.UniqueProcessId != pid:
            continue
        t = (h.HandleValue, h.ObjectTypeIndex, h.GrantedAccess)
        print('%04x : %04x : %08x' % t)

------------------------------------------------------------------------------