From fe4d41db47f5af4f33c5bf40a295eff3aebfc250 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Wed, 27 Jul 2016 18:20:36 +0200 Subject: [PATCH] Add/Update some doc --- CHANGELOG | 2 +- README.md | 4 +- TODO | 12 ------ doc/source/debug.rst | 19 ++++++++- doc/source/handle.rst | 10 +++++ doc/source/internals.rst | 7 +--- doc/source/sample.rst | 62 ++++++++++++++++++--------- doc/source/various.rst | 3 +- doc/source/winproxy.rst | 1 + samples/current_process.py | 6 +-- samples/debugger_membp_singlestep.py | 11 +++-- samples/debugger_print_LdrLoaddll.py | 2 +- samples/system.py | 16 ++++++- windows/debug/breakpoints.py | 4 ++ windows/debug/debugger.py | 63 ++++++++++++++++++---------- windows/debug/localdbg.py | 9 +++- windows/test/test_syswow.py | 9 ++-- windows/winobject/handle.py | 14 ++++++- windows/winobject/process.py | 17 ++++++-- 19 files changed, 186 insertions(+), 85 deletions(-) create mode 100644 doc/source/handle.rst diff --git a/CHANGELOG b/CHANGELOG index d86dd27..cb63ee0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,7 +14,7 @@ Since 0.2: * WinProcess.execute_python does not regenerate/reinject the python_exec_shellcode for each execution * generate.py cleaned with class and stuff / usable for extern project (cc lkd) * Added COMImplementation to com interface - * fix x86.assemble + add x64.assemble | fix some enconding problem in x64 + * fix x86.assemble + add x64.assemble | fix some encoding problem in x64 * Add test_code.py sample * Remove OptionExport from winproxy * MemoryBP + single_step for windows.debug.Debugger diff --git a/README.md b/README.md index 1be6ff1..489b9f7 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ True # execute_python raise if process died Traceback (most recent call last): ... -ValueError: Unknown exit code 0xc000004bL +WindowsError: died during execution of python command >>> calc >>> calc.exit_code @@ -238,7 +238,7 @@ func = windows.native_exec.create_function(code, [PVOID]) print("Code addr = 0x{0:x}".format(func.code_addr)) # Create a thread that will infinite loop t = windows.current_process.create_thread(func.code_addr, 0) -# Add a breakpoint on the infitine loop +# Add a breakpoint on the infinite loop d.add_bp(RewriteBreakpoint(func.code_addr)) t.wait() print("Done!") diff --git a/TODO b/TODO index 8b0a8cf..119bfaa 100644 --- a/TODO +++ b/TODO @@ -8,32 +8,20 @@ TODO: - Verif multiple pending at same place - Test !! (bp, BP_HX, bp on only on process, bp_hx on only one thread..) - test breakpoint with specific target - - Add test for debugger with breakpoint that add another breakpoint on trigger - - Handle MemBP of multiple types of the same page.. - remotectypes - pretty sur I can get rid of PointerToStruct64/PointerToStruct32 - - Some test/doc on windows.system.handles - registry - test ! - - processes - - TEST / DOC of Process.context_syswow - - - - Injection - - death of process in execute_python raise a WindowsError with explicit message (change sample in readme) - Documentation * verif samples * COMImplementation (example in LKD) - * MemoryBP + single_step() in Debugger FIXME: - - setup.py build seems to raise an error - Push("[ECX]") in simple_x64 as a "H" rex and i think it should not.. RESSOURCE diff --git a/doc/source/debug.rst b/doc/source/debug.rst index 01d9e07..6f7fb94 100644 --- a/doc/source/debug.rst +++ b/doc/source/debug.rst @@ -13,8 +13,13 @@ The :class:`Debugger` is the base class to perform the debugging of a remote process. The :class:`Debugger` have some functions called on given event that can be implemented by subclasses. +All Memory-breakpoint are disabled when callind a public callback or a breakpoint ``trigger()`` function. + +This means that those methods see the original ``current_process`` memory access rights. + .. autoclass:: Debugger :members: + :member-order: bysource .. automethod:: __init__ @@ -64,4 +69,16 @@ When a breakpoint is hit, its ``trigger`` function is called with the debugger a .. autoclass:: HXBreakpoint :members: - :inherited-members: \ No newline at end of file + :inherited-members: + +.. autoclass:: MemoryBreakpoint + :members: + :inherited-members: + :special-members: __init__ + + +.. note:: + + MemoryBreakpoint are triggered based on the fault address only (as I don't know a way to get the size of the read/write causing the fault without embeding a disassembler). + + This means that a MEMBP at address ``X`` won't be triggered by a write of size 4 at address ``X - 1`` (it's sad I know :( ) \ No newline at end of file diff --git a/doc/source/handle.rst b/doc/source/handle.rst new file mode 100644 index 0000000..1497638 --- /dev/null +++ b/doc/source/handle.rst @@ -0,0 +1,10 @@ +Handle -- Processes handles +============================ + +.. note:: + + See sample :ref:`sample_system` + +.. module:: windows.winobject.handle + +.. autoclass:: Handle \ No newline at end of file diff --git a/doc/source/internals.rst b/doc/source/internals.rst index 39ea6c6..7163570 100644 --- a/doc/source/internals.rst +++ b/doc/source/internals.rst @@ -68,7 +68,7 @@ To be able to perform those operation we must be able to execute code in the ``6 .. note:: - See `Knockin’ on Heaven’s Gate – Dynamic Processor Mode Switching `_ + See `Knockin’ on Heaven’s Gate – Dynamic Processor Mode Switching `_ For that we need to jump to the 64bits segment of our process, execute some code then return. @@ -122,7 +122,4 @@ Existing function are: .. function:: NtSetContextThread_32_to_64 -.. function:: LdrLoadDll_32_to_64 - - -.. _heaven_gate: http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/ \ No newline at end of file +.. function:: LdrLoadDll_32_to_64 \ No newline at end of file diff --git a/doc/source/sample.rst b/doc/source/sample.rst index ad71782..4488242 100644 --- a/doc/source/sample.rst +++ b/doc/source/sample.rst @@ -11,16 +11,15 @@ Samples of code Output:: (cmd λ) python32.exe current_process.py - current process is + current process is current process is a <32> bits process current process is a SysWow64 process ? - current process pid <7432> and ppid <5412> - Here are the current process threads: <[]> + current process pid <8264> and ppid <4100> + Here are the current process threads: <[]> Let's execute some native code ! (0x41 + 1) - Waiting for execution to finish ! - Native code returned <0x42L> + Native code returned <0x42> Allocating memory in current process - Allocated memory is at <0x3f0000> + Allocated memory is at <0xd60000> Writing 'SOME STUFF' in allocation memory Reading memory : <'SOME STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'> @@ -118,8 +117,8 @@ Output:: product_type = VER_NT_WORKSTATION(0x1L) version_name = Windows 8.1 - There is 95 processes - There is 1021 threads + There is 117 processes + There is 1246 threads Dumping first logical drive: @@ -139,8 +138,16 @@ Output:: name = Appinfo description = Application Information status = ServiceStatus(type=SERVICE_WIN32_SHARE_PROCESS(0x20L), state=SERVICE_RUNNING(0x4L), control_accepted=129L, flags=0L) - process = + process = + Enumerating handles: + There are 40664 handles: + First handle is: in process pid=4> + Enumerating handles of the current process: + There are 255 handles for this process + Looking for a File handle: + Handle is in process pid=14340> + Name is <\Device\ConDrv> .. _sample_iat_hook: @@ -332,11 +339,11 @@ Debugging :class:`Debugger` ''''''''''''''''' -.. literalinclude:: ..\..\samples\debugger.py +.. literalinclude:: ..\..\samples\debugger_print_LdrLoaddll.py Ouput:: - (cmd λ) python.exe .\samples\debugger.py + (cmd λ) python.exe .\samples\debugger_print_LdrLoaddll.py Loading Got exception EXCEPTION_BREAKPOINT(0x80000003L) at 0x77a73bad Loading @@ -355,6 +362,23 @@ Ouput:: Ask to load : exiting process +.. literalinclude:: ..\..\samples\debugger_membp_singlestep.py + +Ouput:: + + (cmd λ) python.exe .\samples\debugger_membp_singlestep.py + Got exception EXCEPTION_BREAKPOINT(0x80000003L) at 0x77ae3c7d + Instruction at <0x8d0006> wrote at <0x8e0000> + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d000c + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d0011 + Instruction at <0x8d0011> wrote at <0x8e0004> + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d0017 + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d001c + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d0022 + Got single_step EXCEPTION_SINGLE_STEP(0x80000004L) at 0x8d0023 + No more single step: exiting + + .. _sample_local_debugger: :class:`LocalDebugger` @@ -368,15 +392,13 @@ In current process Ouput:: (cmd λ) python.exe .\samples\local_debugger.py - Your main thread is 3864 - Code addr = 0x46000b - GOT AN HXBP <3 at 0x46000b - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x46000c - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x46000d - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x46000e - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x46000f - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x460010 - EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x460011 + Code addr = 0xcf0002 + GOT AN HXBP at 0xcf0002 + EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0xcf0003 + EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0xcf0004 + EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0xcf0005 + EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004L) at 0x770d7c04 + Done! In remote process diff --git a/doc/source/various.rst b/doc/source/various.rst index 4a9ea92..f0c2e60 100644 --- a/doc/source/various.rst +++ b/doc/source/various.rst @@ -15,4 +15,5 @@ This sections describes them by group of relation. network.rst service.rst volume.rst - wmi.rst \ No newline at end of file + wmi.rst + handle.rst \ No newline at end of file diff --git a/doc/source/winproxy.rst b/doc/source/winproxy.rst index 87a68d2..f437b61 100644 --- a/doc/source/winproxy.rst +++ b/doc/source/winproxy.rst @@ -137,6 +137,7 @@ Transparent proxies: * Wow64RevertWow64FsRedirection(OldValue) * lstrcmpA(lpString1, lpString2) * lstrcmpW(lpString1, lpString2) + Functions: * AddVectoredContinueHandler:: diff --git a/samples/current_process.py b/samples/current_process.py index bca8c6f..e93edd4 100644 --- a/samples/current_process.py +++ b/samples/current_process.py @@ -27,13 +27,11 @@ else: code += x64.Mov("RAX", 0x41) code += x64.Inc("RAX") code += x64.Ret() - + native_code = code.get_code() v = windows.current_process.execute(native_code) -print("Waiting for execution to finish !") -v.wait() -print("Native code returned <{0}>".format(hex(v.exit_code))) +print("Native code returned <{0}>".format(hex(v))) print("Allocating memory in current process") addr = cp.virtual_alloc(0x1000) # Default alloc is RWX (so secure !) diff --git a/samples/debugger_membp_singlestep.py b/samples/debugger_membp_singlestep.py index 903fbc8..7440d49 100644 --- a/samples/debugger_membp_singlestep.py +++ b/samples/debugger_membp_singlestep.py @@ -28,20 +28,19 @@ class MyDebugger(windows.debug.Debugger): self.single_step_counter -= 1 if self.single_step_counter > 0: return self.single_step() + else: + print("No more single step: exiting") + self.current_process.exit() class SingleStepOnWrite(windows.debug.MemoryBreakpoint): - DEFAULT_PROTECT = PAGE_READONLY """Check that BP/dbg can trigger single step and that instruction follows""" def trigger(self, dbg, exc): fault_addr = exc.ExceptionRecord.ExceptionInformation[1] + import pdb;pdb.set_trace() eip = dbg.current_thread.context.pc print("Instruction at <{0:#x}> wrote at <{1:#x}>".format(eip, fault_addr)) dbg.single_step_counter = 4 - #import pdb;pdb.set_trace() - #if fault_addr == self.addr + 4: - # print("Delete self BP") - # dbg.del_bp(self) return dbg.single_step() @@ -63,7 +62,7 @@ injected += x86.Nop() injected += x86.Ret() calc.write_memory(code, injected.get_code()) -d.add_bp(SingleStepOnWrite(data + 1, size=5)) +d.add_bp(SingleStepOnWrite(data, size=8, events="W")) calc.create_thread(code, 0) d.loop() diff --git a/samples/debugger_print_LdrLoaddll.py b/samples/debugger_print_LdrLoaddll.py index 5979645..623c7d5 100644 --- a/samples/debugger_print_LdrLoaddll.py +++ b/samples/debugger_print_LdrLoaddll.py @@ -41,6 +41,6 @@ class PrintUnicodeString(windows.debug.Breakpoint): calc = windows.test.pop_calc_32(dwCreationFlags=DEBUG_PROCESS) d = MyDebugger(calc) -d.add_bp(PrintUnicodeString("ntdll.dll!LdrLoadDll", argument_position=2)) +d.add_bp(PrintUnicodeString("ntdll!LdrLoadDll", argument_position=2)) d.loop() diff --git a/samples/system.py b/samples/system.py index 7bb7c95..06d65a1 100644 --- a/samples/system.py +++ b/samples/system.py @@ -39,4 +39,18 @@ print(" " + str(serv)) print((" " * 8) + "name = {0}".format(serv.name)) print((" " * 8) + "description = {0}".format(serv.description)) print((" " * 8) + "status = {0}".format(serv.status)) -print((" " * 8) + "process = {0}".format(repr(serv.process))) \ No newline at end of file +print((" " * 8) + "process = {0}".format(repr(serv.process))) +print("") + +print("Enumerating handles:") +handles = system.handles +print(" There are {0} handles:".format(len(handles))) +print(" First handle is: " + str(handles[0])) + +print(" Enumerating handles of the current process:") +cp_handles = [h for h in system.handles if h.dwProcessId == windows.current_process.pid] +print(" There are {0} handles for this process".format(len(cp_handles))) +print(" Looking for a File handle:") +file_h = [h for h in cp_handles if h.type == "File"][0] +print(" Handle is {0}".format(file_h)) +print(" Name is <{0}>".format(file_h.name)) \ No newline at end of file diff --git a/windows/debug/breakpoints.py b/windows/debug/breakpoints.py index 983aacc..4455450 100644 --- a/windows/debug/breakpoints.py +++ b/windows/debug/breakpoints.py @@ -42,10 +42,14 @@ class HXBreakpoint(Breakpoint): return isinstance(target, WinThread) class MemoryBreakpoint(Breakpoint): + """A memory breakpoint (type == ``MEMORY_BREAKPOINT``)""" type = MEMORY_BREAKPOINT DEFAULT_EVENTS = "RWX" DEFAULT_SIZE = 0x1000 def __init__(self, addr, size=None, events=None): + """``size``: the size of the memory breakpoint. + + ``events``: a string representing the events that interest the BP (any of "RWX")""" super(MemoryBreakpoint, self).__init__(addr) self.size = size if size is not None else self.DEFAULT_SIZE events = events if events is not None else self.DEFAULT_EVENTS diff --git a/windows/debug/debugger.py b/windows/debug/debugger.py index 29287a3..046edc4 100644 --- a/windows/debug/debugger.py +++ b/windows/debug/debugger.py @@ -35,11 +35,14 @@ WatchedPage = namedtuple('WatchedPage', ["original_prot", "bps"]) class Debugger(object): - """A debugger based on standard Win32 API. Handle standard (int3) and Hardware-Exec Breakpoints""" - def __init__(self, target): - """``target`` must be a WinProcess. + """A debugger based on standard Win32 API. Handle : - ``already_debuggable`` must be set to ``True`` if process is already expecting a debugger (created with ``DEBUG_PROCESS``)""" + * Standard BP (int3) + * Hardware-Exec BP (DrX) + * Memory BP (virtual_protect)""" + + def __init__(self, target): + """``target`` must be a debuggable :class:`WinProcess`.""" self._init_dispatch_handlers() self.target = target self.is_target_launched = False @@ -73,11 +76,17 @@ class Debugger(object): @classmethod def attach(cls, target): + """attach to ``target`` (must be a :class:`WinProcess`) + + :rtype: :class:`Debugger`""" winproxy.DebugActiveProcess(target.pid) return cls(target) @classmethod def debug(cls, path, args=None, dwCreationFlags=0, show_windows=False): + """Create a process and debug it. + + :rtype: :class:`Debugger`""" dwCreationFlags |= DEBUG_PROCESS c = windows.utils.create_process(path, args=args, dwCreationFlags=dwCreationFlags, show_windows=show_windows) return cls(c) @@ -235,7 +244,7 @@ class Debugger(object): pass return True - ## MemBP helpers + ## MemBP internal helpers def _compute_page_access_for_event(self, target, events): if "R" in events: return PAGE_NOACCESS @@ -636,7 +645,7 @@ class Debugger(object): with self.DisabledMemoryBreakpoint(): return self.on_rip(rip_info) - # Public API + ## Public API def loop(self): """Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead""" for debug_event in self._debug_event_generator(): @@ -650,12 +659,12 @@ class Debugger(object): def add_bp(self, bp, addr=None, type=None, target=None): """Add a breakpoint, bp can be: - * a :class:`Breakpoint` (addr and type must be None) - * any callable (addr and type must NOT be None) (NON-TESTED) + * a :class:`Breakpoint` (addr and type must be ``None``) + * any callable (addr and type must NOT be ``None``) (NON-TESTED) - If the ``bp`` type is ``STANDARD_BP``, target can be None (all targets) or a process. + If the ``bp`` type is ``STANDARD_BP`` or ``MEMORY_BREAKPOINT``, target can be ``None`` (all targets) or a process. - If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be None (all targets), a process or a thread. + If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be ``None`` (all targets), a process or a thread. """ if getattr(bp, "addr", None) is None: if addr is None or type is None: @@ -680,6 +689,7 @@ class Debugger(object): return self._setup_breakpoint(bp, target) def del_bp(self, bp, targets=None): + """Delete a breakpoint, if targets is ``None``: delete it from all targets""" #if targets is not None: # raise NotImplementedError("TODO: DEL BP with targets") original_target = targets @@ -698,6 +708,7 @@ class Debugger(object): return self.remove_pending_breakpoint(bp, original_target) def single_step(self): + """Make the ``current_thread`` ``single_step``. ``Debugger.on_single_step`` will be called after that""" t = self.current_thread ctx = t.context ctx.EEFlags.TF = 1 @@ -705,9 +716,11 @@ class Debugger(object): ## Memory Breakpoint helper def get_memory_breakpoint_at(self, addr, process=None): - """Get the memory breakpoint the handle `addr` + """Get the memory breakpoint that handle ``addr`` + Return values are: - * ``False`` if the page as no memory breakpoint (real fault) + + * ``False`` if the page has no memory breakpoint (real fault) * ``None`` if the page as memBP but None handle ``addr`` * ``bp`` the MemBP that handle ``addr`` """ @@ -724,6 +737,10 @@ class Debugger(object): return None def disable_all_memory_breakpoints(self, target=None): + """Restore all pages to their original access rights. + If target is ``None``, use ``current_process`` + + :return: a mapping of all disabled breakpoints that must be passed to :func:`restore_all_memory_breakpoints`""" if target is None: target = self.current_process res = {} @@ -734,32 +751,34 @@ class Debugger(object): res[page_addr] = page_protection.value return res - def restore_all_memory_breakpoints(self, data, target=None): - if target is None: - target = self.current_process - for page_addr, protection in data.items(): - target.virtual_protect(page_addr, PAGE_SIZE, protection, None) - return - #TODO: better stuff :DD - # default implem ? why would I reput the removed BP.. - def restore_all_memory_breakpoints_verif_remove(self, data, target=None): + def restore_all_memory_breakpoints(self, data, target=None): + """Re-setup all memory breakpoints, affecting pages access rights. + If target is ``None``, use ``current_process`` + + ``data`` is the result of the corresponding call to :func:`disable_all_memory_breakpoints`""" if target is None: target = self.current_process for page_addr, protection in data.items(): + # Prevent restoring deleted breakpoints if page_addr in self._watched_pages[target.pid]: target.virtual_protect(page_addr, PAGE_SIZE, protection, None) return @contextmanager def DisabledMemoryBreakpoint(self, target=None): + """A context-manager that disable all memory breakpoints and restore them on exit""" data = self.disable_all_memory_breakpoints(target) try: yield finally: - self.restore_all_memory_breakpoints_verif_remove(data, target) + self.restore_all_memory_breakpoints(data, target) def get_exception_bitness(self, exc): + """Return the bitness in which the exception occured. + Useful when debugingg a 32b process from a 64bits one + + :return: :class:`int` -- 32 or 64""" if windows.current_process.bitness == 32: return 32 if exc.ExceptionRecord.ExceptionCode in [STATUS_WX86_BREAKPOINT, STATUS_WX86_SINGLE_STEP]: diff --git a/windows/debug/localdbg.py b/windows/debug/localdbg.py index 00767e1..ef443a8 100644 --- a/windows/debug/localdbg.py +++ b/windows/debug/localdbg.py @@ -11,7 +11,13 @@ from .breakpoints import * class LocalDebugger(object): - """A debugger interface around :func:`AddVectoredExceptionHandler`""" + """A debugger interface around :func:`AddVectoredExceptionHandler`. + + Handle: + + * Standard BP (int3) + * Hardware-Exec BP (DrX)""" + def __init__(self): self.breakpoints = {} self._memory_save = {} @@ -96,6 +102,7 @@ class LocalDebugger(object): return windef.EXCEPTION_CONTINUE_EXECUTION def del_bp(self, bp): + """Delete a breakpoint""" if bp.type == STANDARD_BP: with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE): windows.current_process.write_memory(bp.addr, self._memory_save[bp.addr]) diff --git a/windows/test/test_syswow.py b/windows/test/test_syswow.py index 4e6d280..2ea47f3 100644 --- a/windows/test/test_syswow.py +++ b/windows/test/test_syswow.py @@ -42,15 +42,17 @@ class SyswowTestCase(unittest.TestCase): import windows import windows.native_exec.simple_x64 as x64 windows.utils.create_console() + windows.current_process.write_qword({0}, 0x8877665544332211) x64_code = x64.assemble("mov r11, 0x1122334455667788; label :loop; jmp :loop; nop; nop; ret") res = windows.syswow64.execute_64bits_code_from_syswow(x64_code) print("res = {{0}}".format(hex(res))) windows.current_process.write_qword({0}, res) - import time - time.sleep(10) """.format(addr) t = calc.execute_python_unsafe(textwrap.dedent(remote_python_code)) + # Wait for python execution + while calc.read_qword(addr) != 0x8877665544332211: + pass ctx = t.context_syswow # Check the get context self.assertEqual(ctx.R11, 0x1122334455667788) @@ -63,6 +65,7 @@ class SyswowTestCase(unittest.TestCase): ctx.Rip += 2 t.set_syswow_context(ctx) t.resume() - time.sleep(0.1) + t.wait() self.assertEqual(RETURN_VALUE, calc.read_qword(addr)) + diff --git a/windows/winobject/handle.py b/windows/winobject/handle.py index 96e0f52..e38735f 100644 --- a/windows/winobject/handle.py +++ b/windows/winobject/handle.py @@ -11,17 +11,27 @@ class EPUBLIC_OBJECT_TYPE_INFORMATION(ctypes.Structure): class Handle(SYSTEM_HANDLE): + """A handle of the system""" @windows.utils.fixedpropety def process(self): + """The process possessing the handle + + :type: :class:`WinProcess `""" "TODO: something smart ? :D" return [p for p in windows.system.processes if p.pid == self.dwProcessId][0] @windows.utils.fixedpropety def name(self): + """The name of the handle + + :type: :class:`str`""" return self._get_object_name() @windows.utils.fixedpropety def type(self): + """The type of the handle + + :type: :class:`str`""" return self._get_object_type() def _get_object_name(self): @@ -47,6 +57,9 @@ class Handle(SYSTEM_HANDLE): @windows.utils.fixedpropety def local_handle(self): + """A local copy of the handle, acquired with ``DuplicateHandle`` + + :type: :class:`int`""" if self.dwProcessId == windows.current_process.pid: return self.wValue res = HANDLE() @@ -63,7 +76,6 @@ class Handle(SYSTEM_HANDLE): return winproxy.CloseHandle(self._local_handle) - def enumerate_handles(): size_needed = ULONG() size = 0x1000 diff --git a/windows/winobject/process.py b/windows/winobject/process.py index 000adc6..7f616b1 100644 --- a/windows/winobject/process.py +++ b/windows/winobject/process.py @@ -112,6 +112,10 @@ class WinThread(THREADENTRY32, AutoHandle): @property def context_syswow(self): + """The 64 bits context of a syswow thread. + + :type: :class:`windows.exception.ECONTEXT64` + """ if not self.owner.is_wow_64: raise ValueError("Not a syswow process") x = exception.ECONTEXT64.new_aligned() @@ -124,7 +128,7 @@ class WinThread(THREADENTRY32, AutoHandle): def set_context(self, context): - """Set the thread context to ``context``""" + """Set the thread's context to ``context``""" if self.owner.bitness == windows.current_process.bitness: return winproxy.SetThreadContext(self.handle, context) if windows.current_process.bitness == 64 and self.owner.bitness == 32: @@ -133,6 +137,7 @@ class WinThread(THREADENTRY32, AutoHandle): def set_syswow_context(self, context): + """Set a syswow thread's 64 context to ``context``""" if not self.owner.is_wow_64: raise ValueError("Not a syswow process") if windows.current_process.bitness == 64: @@ -358,6 +363,7 @@ class Process(AutoHandle): self.virtual_protect(addr, size, old_protect.value, old_protect) def virtual_protect(self, addr, size, protect, old_protect): + """Change the access right of one or more page of the process""" if windows.current_process.bitness == 32 and self.bitness == 64: #addr = (addr >> 12) << 12 #addr = ULONG64(addr) @@ -570,9 +576,9 @@ class Process(AutoHandle): @property def time_info(self): - """The time information of the process (creation, kernel/user time, exit time + """The time information of the process (creation, kernel/user time, exit time) - :type: :class:`TimeInfo""" + :type: :class:`TimeInfo`""" CreationTime = FILETIME() ExitTime = FILETIME() KernelTime = FILETIME() @@ -734,6 +740,9 @@ class CurrentProcess(Process): return WinThread._from_handle(handle) def execute(self, code, parameter=0): + """Execute native code ``code`` in the current thread. + + :rtype: :class:`int` the return value of the native code""" f = windows.native_exec.create_function(code, [PVOID, PVOID]) return f(parameter) @@ -916,7 +925,7 @@ class WinProcess(Process): def execute_python_unsafe(self, pycode): """Execute Python code into the remote process. - Unsafe means that no information are returned about the execution of the thread + :rtype: :rtype: :class:`WinThread` or :class:`DeadThread` : The thread executing the python code """ return injection.execute_python_code(self, pycode)