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) ------------------------------------------------------------------------------