diff --git a/windows/debug/debugger.py b/windows/debug/debugger.py index 70a89a3..88f4ed5 100644 --- a/windows/debug/debugger.py +++ b/windows/debug/debugger.py @@ -674,8 +674,10 @@ class Debugger(object): self._setup_pending_breakpoints_new_process(self.current_process) self._setup_pending_breakpoints_new_thread(self.current_thread) with self.DisabledMemoryBreakpoint(): - return self.on_create_process(create_process) - # TODO: close hFile + try: + return self.on_create_process(create_process) + finally: + winproxy.CloseHandle(create_process.hFile) def _handle_exit_process(self, debug_event): """Handle EXIT_PROCESS_DEBUG_EVENT""" @@ -745,7 +747,10 @@ class Debugger(object): self._module_by_process[self.current_process.pid][dll_name] = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) self._setup_pending_breakpoints_load_dll(dll_name) with self.DisabledMemoryBreakpoint(): - return self.on_load_dll(load_dll) + try: + return self.on_load_dll(load_dll) + finally: + winproxy.CloseHandle(load_dll.hFile) def _handle_unload_dll(self, debug_event): """Handle UNLOAD_DLL_DEBUG_EVENT""" diff --git a/windows/test/mytest.py b/windows/test/mytest.py index a0c9535..fe16d3e 100644 --- a/windows/test/mytest.py +++ b/windows/test/mytest.py @@ -17,8 +17,8 @@ class SystemTestCase(unittest.TestCase): @check_for_gc_garbage def test_version_name(self): - return windows.system.version_name + @check_for_gc_garbage def test_computer_name(self): return windows.system.computer_name @@ -32,10 +32,7 @@ class SystemTestCase(unittest.TestCase): return windows.system.logicaldrives @check_for_gc_garbage - def test_processes(self): - return windows.system.processes - - @check_for_gc_garbage + @check_for_handle_leak def test_threads(self): return windows.system.threads @@ -44,6 +41,7 @@ class SystemTestCase(unittest.TestCase): return windows.system.wmi.select("Win32_Process", "*") @check_for_gc_garbage + @check_for_handle_leak def test_processes(self): procs = windows.system.processes self.assertIn(windows.current_process.pid, [p.pid for p in procs]) diff --git a/windows/test/test_utils.py b/windows/test/test_utils.py index 401d252..c42a2c8 100644 --- a/windows/test/test_utils.py +++ b/windows/test/test_utils.py @@ -88,11 +88,45 @@ def check_for_gc_garbage(f): return wrapper - +def check_for_handle_leak(f): + def wrapper(testcase, *args, **kwargs): + current_process_hdebugger.refresh_handles() + res = f(testcase, *args, **kwargs) + leaked_handles = current_process_hdebugger.get_new_handle() + testcase.assertFalse(leaked_handles, "Test Leaked <{0}> handles of types ({1})".format(len(leaked_handles), set(h.type for h in leaked_handles))) + return res + return wrapper def print_call(f): def wrapper(*args, **kwargs): res = f(*args, **kwargs) print("Call to <{0}>({1}) returned <{2}>".format(f.func_name, (args, kwargs), res)) return res - return wrapper \ No newline at end of file + return wrapper + + +class HandleDebugger(object): + def __init__(self, pid): + self.pid = pid + self.handles = 0 + + def refresh_handles(self): + self.handles = self.get_handles() + + def get_handles(self): + tpid = self.pid + return [h for h in windows.system.handles if h.dwProcessId == tpid] + + def get_new_handle(self): + nh = self.get_handles() + handle_diff = set(h.wValue for h in nh) - set(h.wValue for h in self.handles) + return [h for h in nh if h.wValue in handle_diff] + + def handles_types(self, hlist): + return set(h.type for h in hlist) + + def print_new_handle_type(self): + print(self.handles_types(self.get_new_handle())) + + +current_process_hdebugger = HandleDebugger(windows.current_process.pid) diff --git a/windows/winobject/handle.py b/windows/winobject/handle.py index 4a67ed1..a514587 100644 --- a/windows/winobject/handle.py +++ b/windows/winobject/handle.py @@ -1,3 +1,4 @@ +import os import ctypes import windows @@ -9,6 +10,7 @@ from windows.generated_def.winstructs import * class EPUBLIC_OBJECT_TYPE_INFORMATION(ctypes.Structure): _fields_ = windows.utils.transform_ctypes_fields(PUBLIC_OBJECT_TYPE_INFORMATION, {"TypeName": windows.winobject.process.WinUnicodeString}) +current_process_pid = os.getpid() class Handle(SYSTEM_HANDLE): """A handle of the system""" @@ -86,7 +88,7 @@ class Handle(SYSTEM_HANDLE): return "<{0} value=<0x{1:x}> in process pid={2}>".format(type(self).__name__, self.wValue, self.dwProcessId) def __del__(self): - if self.dwProcessId == windows.current_process.pid: + if self.dwProcessId == current_process_pid: return if hasattr(self, "_local_handle"): return winproxy.CloseHandle(self._local_handle) diff --git a/windows/winobject/system.py b/windows/winobject/system.py index d666d15..b81b981 100644 --- a/windows/winobject/system.py +++ b/windows/winobject/system.py @@ -210,6 +210,7 @@ class System(object): res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) while winproxy.Process32Next(snap, process_entry): res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) + winproxy.CloseHandle(snap) return res @staticmethod @@ -222,4 +223,5 @@ class System(object): threads.append(copy.copy(thread_entry)) while winproxy.Thread32Next(snap, thread_entry): threads.append(copy.copy(thread_entry)) - return threads \ No newline at end of file + winproxy.CloseHandle(snap) + return threads