diff --git a/tests/test_handle.py b/tests/test_handle.py new file mode 100644 index 0000000..a3c4e18 --- /dev/null +++ b/tests/test_handle.py @@ -0,0 +1,35 @@ +import windows + +def test_handle_process_id(): + handle_with_process = [h for h in windows.system.handles if h.dwProcessId] + handle = handle_with_process[-1] + proc = handle.process + assert proc.pid == handle.dwProcessId == handle.pid + + +def test_local_handle_type(): + t = windows.current_process.threads[0] + th = t.handle + hobj = [h for h in windows.current_process.handles if h.value == th][0] + assert hobj.type == "Thread" + assert hobj.name == "" + assert hobj.infos + +PIPE_NAME = "PFW_Test_handle_Pipe" +TEST_FILE_FOR_HANDLE = r"C:\Windows\explorer.exe" + +def test_remote_handle_type_and_name(proc32_64): + # tmpfile + proc32_64.execute_python("import windows") + # A filename that a normal process should not have a handle on (to be sur) + proc32_64.execute_python(r"""f = open(r"{filename}")""".format(filename=TEST_FILE_FOR_HANDLE)) + proc32_64.execute_python(r"""h = windows.utils.get_handle_from_file(f)""") + with windows.pipe.create(PIPE_NAME) as np: + proc32_64.execute_python("""windows.pipe.send_object("{pipe}", h)""".format(pipe=PIPE_NAME)) + file_handle_vlue = np.recv() + remote_handle = [x for x in proc32_64.handles if x.value == file_handle_vlue][0] + assert remote_handle.pid == proc32_64.pid + assert remote_handle.type == "File" + assert remote_handle.name.startswith("\Device\HarddiskVolume") + assert remote_handle.name.endswith(TEST_FILE_FOR_HANDLE[2:]) # Remove volume letter + assert remote_handle.infos \ No newline at end of file diff --git a/windows/winobject/handle.py b/windows/winobject/handle.py index b1eeeca..d63a85d 100644 --- a/windows/winobject/handle.py +++ b/windows/winobject/handle.py @@ -4,15 +4,11 @@ import ctypes import windows from windows import winproxy from windows.generated_def import windef -from windows.generated_def.winstructs import * - -# Remove this ? -class EPUBLIC_OBJECT_TYPE_INFORMATION(PUBLIC_OBJECT_TYPE_INFORMATION): - pass +import windows.generated_def as gdef current_process_pid = os.getpid() -class Handle(SYSTEM_HANDLE): +class BaseSystemHandle(object): """A handle of the system""" @windows.utils.fixedpropety def process(self): @@ -23,6 +19,15 @@ class Handle(SYSTEM_HANDLE): # return [p for p in windows.system.processes if p.pid == self.dwProcessId][0] return windows.WinProcess(pid=self.dwProcessId) + @property + def pid(self): + return self.dwProcessId + + @property + def value(self): + return self.wValue + + @windows.utils.fixedpropety def name(self): """The name of the handle @@ -44,35 +49,33 @@ class Handle(SYSTEM_HANDLE): def _get_object_name(self): lh = self.local_handle - size_needed = DWORD() + size_needed = gdef.DWORD() yyy = ctypes.c_buffer(0x1000) - winproxy.NtQueryObject(lh, ObjectNameInformation, ctypes.byref(yyy), ctypes.sizeof(yyy), ctypes.byref(size_needed)) - return LSA_UNICODE_STRING.from_buffer_copy(yyy[:size_needed.value]).str + winproxy.NtQueryObject(lh, gdef.ObjectNameInformation, ctypes.byref(yyy), ctypes.sizeof(yyy), ctypes.byref(size_needed)) + return gdef.LSA_UNICODE_STRING.from_buffer_copy(yyy[:size_needed.value]).str def _get_object_type(self): lh = self.local_handle - xxx = EPUBLIC_OBJECT_TYPE_INFORMATION() - size_needed = DWORD() + xxx = gdef.PUBLIC_OBJECT_TYPE_INFORMATION() + size_needed = gdef.DWORD() try: - winproxy.NtQueryObject(lh, ObjectTypeInformation, ctypes.byref(xxx), ctypes.sizeof(xxx), ctypes.byref(size_needed)) + winproxy.NtQueryObject(lh, gdef.ObjectTypeInformation, ctypes.byref(xxx), ctypes.sizeof(xxx), ctypes.byref(size_needed)) except WindowsError as e: - if e.code != STATUS_INFO_LENGTH_MISMATCH: - # print("ERROR WITH {0:x}".format(lh)) + if e.code != gdef.STATUS_INFO_LENGTH_MISMATCH: raise size = size_needed.value buffer = ctypes.c_buffer(size) - winproxy.NtQueryObject(lh, ObjectTypeInformation, buffer, size, ctypes.byref(size_needed)) - xxx = EPUBLIC_OBJECT_TYPE_INFORMATION.from_buffer_copy(buffer) + winproxy.NtQueryObject(lh, gdef.ObjectTypeInformation, buffer, size, ctypes.byref(size_needed)) + xxx = gdef.PUBLIC_OBJECT_TYPE_INFORMATION.from_buffer_copy(buffer) return xxx.TypeName.str def _get_object_basic_infos(self): pass lh = self.local_handle - size_needed = DWORD() - basic_infos = PUBLIC_OBJECT_BASIC_INFORMATION() - winproxy.NtQueryObject(lh, ObjectBasicInformation, ctypes.byref(basic_infos), ctypes.sizeof(basic_infos), ctypes.byref(size_needed)) + size_needed = gdef.DWORD() + basic_infos = gdef.PUBLIC_OBJECT_BASIC_INFORMATION() + winproxy.NtQueryObject(lh, gdef.ObjectBasicInformation, ctypes.byref(basic_infos), ctypes.sizeof(basic_infos), ctypes.byref(size_needed)) return basic_infos - #PUBLIC_OBJECT_BASIC_INFORMATION @windows.utils.fixedpropety def local_handle(self): @@ -81,8 +84,8 @@ class Handle(SYSTEM_HANDLE): :type: :class:`int`""" if self.dwProcessId == windows.current_process.pid: return self.wValue - res = HANDLE() - winproxy.DuplicateHandle(self.process.handle, self.wValue, windows.current_process.handle, ctypes.byref(res), dwOptions=DUPLICATE_SAME_ACCESS) + res = gdef.HANDLE() + winproxy.DuplicateHandle(self.process.handle, self.wValue, windows.current_process.handle, ctypes.byref(res), dwOptions=gdef.DUPLICATE_SAME_ACCESS) return res.value def description(self): @@ -113,25 +116,78 @@ class Handle(SYSTEM_HANDLE): if hasattr(self, "_local_handle"): return winproxy.CloseHandle(self._local_handle) +class Handle(gdef.SYSTEM_HANDLE, BaseSystemHandle): + pass + +class HandleWow64(gdef.SYSTEM_HANDLE64, BaseSystemHandle): + pass # For wow64 process def enumerate_handles(): - size_needed = ULONG() - size = 0x1000 - buffer = ctypes.c_buffer(size) - + if windows.current_process.is_wow_64: + return enumerate_handles_syswow64() + size_needed = gdef.ULONG() + # Should at least be sizeof(gdef.SYSTEM_HANDLE_INFORMATION) + tmp_buffer = windows.utils.BUFFER(gdef.SYSTEM_HANDLE_INFORMATION)() try: - winproxy.NtQuerySystemInformation(16, buffer, size, ReturnLength=ctypes.byref(size_needed)) + winproxy.NtQuerySystemInformation(gdef.SystemHandleInformation, tmp_buffer, tmp_buffer.real_size, ReturnLength=ctypes.byref(size_needed)) except WindowsError as e: pass + size = size_needed.value + 0x1000 # In case we have some more handle created + buf = windows.utils.BUFFER(gdef.SYSTEM_HANDLE_INFORMATION)(size=size) + size_needed.value = 0 + winproxy.NtQuerySystemInformation(gdef.SystemHandleInformation, buf, buf.real_size, ReturnLength=ctypes.byref(size_needed)) + handle_array = windows.utils.resized_array(buf[0].Handles, buf[0].HandleCount, Handle) + return list(handle_array) - size = size_needed.value + 0x1000 - buffer = ctypes.c_buffer(size) - winproxy.NtQuerySystemInformation(16, buffer, size, ReturnLength=ctypes.byref(size_needed)) - x = SYSTEM_HANDLE_INFORMATION.from_buffer(buffer) - class _GENERATED_SYSTEM_HANDLE_INFORMATION(ctypes.Structure): - _fields_ = [ - ("HandleCount", ULONG), - ("Handles", Handle * x.HandleCount), - ] - return list(_GENERATED_SYSTEM_HANDLE_INFORMATION.from_buffer_copy(buffer[:size_needed.value]).Handles) +def enumerate_handles_syswow64(): + size_needed = gdef.ULONG() + # Should at least be sizeof(gdef.SYSTEM_HANDLE_INFORMATION) + tmp_buffer = windows.utils.BUFFER(gdef.SYSTEM_HANDLE_INFORMATION64)() + try: + windows.syswow64.NtQuerySystemInformation_32_to_64(gdef.SystemHandleInformation, tmp_buffer, tmp_buffer.real_size, ReturnLength=ctypes.byref(size_needed)) + except WindowsError as e: + pass + size = size_needed.value + 0x1000 # In case we have some more handle created + buf = windows.utils.BUFFER(gdef.SYSTEM_HANDLE_INFORMATION64)(size=size) + size_needed.value = 0 + windows.syswow64.NtQuerySystemInformation_32_to_64(gdef.SystemHandleInformation, buf, buf.real_size, ReturnLength=ctypes.byref(size_needed)) + handle_array = windows.utils.resized_array(buf[0].Handles, buf[0].HandleCount, HandleWow64) + return list(handle_array) + + +def enumerate_type(): + "WIP: DO NOT USE" + size_needed = DWORD() + fsize = 8 + fbuffer = ctypes.c_buffer(fsize) + try: + winproxy.NtQueryObject(None, gdef.ObjectTypesInformation, fbuffer, fsize, ctypes.byref(size_needed)) + except WindowsError as e: + if e.code != STATUS_INFO_LENGTH_MISMATCH: + raise + else: + # We had enought memory ? + return + + # Looks like the Wow64 syscall emulation is broken :D + # It write AFTER the buffer if we are a wow64 process :D + # So better allocate a standalone buffer (triggering a ACCESS_VIOLATION) that corrupting the heap + # This is a worst case scenario, as we allocation more space it should not happen ! + size = size_needed.value + 0x200 + size_needed.value = 0 + + with windows.current_process.allocated_memory(size, gdef.PAGE_READWRITE) as buffer_base: + winproxy.NtQueryObject(None, gdef.ObjectTypesInformation, buffer_base, size, ctypes.byref(size_needed)) + # Cache some exceptions ? + # Parse the buffer data in-place as string are addr-dependant + types_info = gdef.OBJECT_TYPES_INFORMATION.from_address(buffer_base) + offset = ctypes.sizeof(gdef.PVOID) # Looks like the size of the struct is PTR aligned as the struct is follower by other stuff + for i in range(types_info.NumberOfTypes): + info = gdef.PUBLIC_OBJECT_TYPE_INFORMATION.from_address(buffer_base + offset) + yield info + offset += ctypes.sizeof(gdef.PUBLIC_OBJECT_TYPE_INFORMATION) + info.TypeName.MaximumLength + if offset % ctypes.sizeof(gdef.PVOID): + offset += ctypes.sizeof(gdef.PVOID) - (offset % ctypes.sizeof(gdef.PVOID)) + # End-of ctx-manager + return