File: win_handles.py

package info (click to toggle)
displaycal-py3 3.9.16-1
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 29,120 kB
  • sloc: python: 115,777; javascript: 11,540; xml: 598; sh: 257; makefile: 173
file content (148 lines) | stat: -rw-r--r-- 3,916 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# -*- coding: utf-8 -*-

from ctypes import wintypes
import ctypes
import os

from DisplayCAL.win_structs import NTSTATUS, UNICODE_STRING

PVOID = ctypes.c_void_p
PULONG = ctypes.POINTER(wintypes.ULONG)
ULONG_PTR = wintypes.WPARAM
ACCESS_MASK = wintypes.DWORD
STATUS_INFO_LENGTH_MISMATCH = NTSTATUS(0xC0000004)


class SYSTEM_INFORMATION_CLASS(ctypes.c_ulong):
    def __repr__(self):
        return f"{self.__class__.__name__}({self.value})"


SystemExtendedHandleInformation = SYSTEM_INFORMATION_CLASS(64)


class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX(ctypes.Structure):
    _fields_ = [
        ("Object", PVOID),
        ("UniqueProcessId", wintypes.HANDLE),
        ("HandleValue", wintypes.HANDLE),
        ("GrantedAccess", ACCESS_MASK),
        ("CreatorBackTraceIndex", wintypes.USHORT),
        ("ObjectTypeIndex", wintypes.USHORT),
        ("HandleAttributes", wintypes.ULONG),
        ("Reserved", wintypes.ULONG),
    ]


class SYSTEM_INFORMATION(ctypes.Structure):
    pass


PSYSTEM_INFORMATION = ctypes.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 ctypes.POINTER(arr_t)(self._Handles)[0]


try:
    ntdll = ctypes.WinDLL("ntdll")
    ntdll.NtQuerySystemInformation.restype = NTSTATUS
    ntdll.NtQuerySystemInformation.argtypes = (
        SYSTEM_INFORMATION_CLASS,  # SystemInformationClass
        PSYSTEM_INFORMATION,  # SystemInformation
        wintypes.ULONG,  # SystemInformationLength
        PULONG,
    )  # ReturnLength
except WindowsError:
    # Just in case
    ntdll = None


ObjectBasicInformation = 0
ObjectNameInformation = 1
ObjectTypeInformation = 2


def _get_handle_info(handle, info_class):
    if hasattr(handle, "HandleValue"):
        handle = handle.HandleValue
    size_needed = wintypes.DWORD()
    buf = ctypes.c_buffer(0x1000)
    ntdll.NtQueryObject(
        handle,
        info_class,
        ctypes.byref(buf),
        ctypes.sizeof(buf),
        ctypes.byref(size_needed),
    )
    return UNICODE_STRING.from_buffer_copy(buf[: size_needed.value]).Buffer


def get_handle_name(handle):
    return _get_handle_info(handle, ObjectNameInformation)


def get_handle_type(handle):
    return _get_handle_info(handle, ObjectTypeInformation)


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


def get_process_handles(pid=None):
    """Get handles of process <pid> (current process if not specified)"""
    if not pid:
        pid = os.getpid()
    handles = []
    for handle in get_handles():
        if handle.UniqueProcessId != pid:
            continue
        handles.append(handle)
    return handles


if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1:
        pid = int(sys.argv[1])
    else:
        pid = None
    for handle in get_process_handles(pid):
        print(
            (
                "Handle = 0x%04x, Type = 0x%02x %r, Access = 0x%06x, Name = %r"
                % (
                    handle.HandleValue,
                    handle.ObjectTypeIndex,
                    get_handle_type(handle),
                    handle.GrantedAccess,
                    get_handle_name(handle),
                )
            )
        )