mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Adding more and more documentation
This commit is contained in:
@@ -24,6 +24,8 @@ sys.path.append(os.path.abspath(__file__ + "..\..\..\.."))
|
||||
|
||||
print("Adding <{0}>".format(sys.path[-1]))
|
||||
|
||||
os.environ["SPHINX_BUILD"] = "1"
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
IAT hooking
|
||||
"""""""""""
|
||||
|
||||
.. note::
|
||||
|
||||
See sample :ref:`sample_iat_hook`
|
||||
|
||||
Put a IAT hook
|
||||
''''''''''''''
|
||||
|
||||
To setup your IAT hook you just need:
|
||||
|
||||
* A callback that respect the :ref:`hook_protocol`
|
||||
* The :class:`windows.pe_parse.IATEntry` to hook
|
||||
|
||||
|
||||
You just need to use the function :func:`windows.pe_parse.IATEntry.set_hook`
|
||||
|
||||
Putting a hook::
|
||||
|
||||
import windows
|
||||
from windows.hooks import *
|
||||
|
||||
@CreateFileACallback
|
||||
def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function):
|
||||
print("Trying to open {0}".format(lpFileName))
|
||||
if "secret" in lpFileName:
|
||||
return 0xffffffff
|
||||
return real_function()
|
||||
|
||||
my_exe = windows.current_process.peb.modules[0]
|
||||
imp = my_exe.pe.imports
|
||||
|
||||
iat_create_file = [entry for entry in imp['kernel32.dll'] if entry.name == "CreateFileA"]
|
||||
iat_create_file.set_hook(createfile_callback)
|
||||
|
||||
|
||||
.. _hook_protocol:
|
||||
|
||||
Hook protocol
|
||||
'''''''''''''
|
||||
|
||||
Callback arguments
|
||||
------------------
|
||||
|
||||
A hook callback must have the same number of argument as the hooked API, PLUS a last argument ``real_function``.
|
||||
|
||||
|
||||
|
||||
The ``real_function`` argument is a callable that represent the hooked API, it can be called in two ways:
|
||||
|
||||
* Without argument, the call will be done with the argument originaly passed to your callback. This allows simple redirection to the real API.
|
||||
|
||||
* With arguments it will simply call the API with these.
|
||||
|
||||
Example::
|
||||
|
||||
def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function):
|
||||
print("Trying to open {0}".format(lpFileName))
|
||||
if "secret" in lpFileName:
|
||||
return 0xffffffff
|
||||
# Perform the real call
|
||||
return real_function()
|
||||
|
||||
|
||||
A hook callback must also embed some :ref:`Type Information <type_information>`
|
||||
|
||||
|
||||
.. _type_information:
|
||||
|
||||
Callback type information
|
||||
--------------------------
|
||||
|
||||
In order make the magic behind Python Hook Callback, :mod:`ctypes` need to have type information about the API parameters.
|
||||
|
||||
There is (again) two ways to give those informations to your hook callback. Both techniques use a decorator to setup type information to the callback.
|
||||
|
||||
* Giving the type manualy using the decorator :class:`windows.hooks.Callback`::
|
||||
|
||||
from windows.hooks import *
|
||||
# First type is return type, others are parameters types
|
||||
@Callback(ctypes.c_void_p, ctypes.c_ulong)
|
||||
def exit_callback(x, real_function):
|
||||
print("Try to quit with {0} | {1}".format(x, type(x)))
|
||||
if x == 3:
|
||||
print("TRYING TO REAL EXIT")
|
||||
return real_function(1234)
|
||||
return 0x4242424243444546
|
||||
|
||||
* Using the `Callback` decorator generated from known functions::
|
||||
|
||||
from windows.hooks import *
|
||||
# Decorator name is always API_NAME + "CallBack"
|
||||
@CreateFileACallback
|
||||
def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function):
|
||||
print("Trying to open {0}".format(lpFileName))
|
||||
if "secret" in lpFileName:
|
||||
return 0xffffffff
|
||||
return real_function()
|
||||
|
||||
.. note::
|
||||
|
||||
See the list of known functions
|
||||
|
||||
|
||||
:mod:`windows.hooks`
|
||||
''''''''''''''''''''
|
||||
|
||||
.. module:: windows.hooks
|
||||
|
||||
.. autoclass:: windows.hooks.Callback
|
||||
|
||||
.. autoclass:: windows.hooks.IATHook
|
||||
@@ -17,6 +17,9 @@ Contents:
|
||||
utils.rst
|
||||
native_exec.rst
|
||||
various.rst
|
||||
iat_hook.rst
|
||||
wip.rst
|
||||
internals.rst
|
||||
sample.rst
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
Internals
|
||||
=========
|
||||
|
||||
Because some horrible hacks of ``PythonForWindows`` are hidden and I wanted to talk about it.
|
||||
|
||||
remotectypes.py
|
||||
'''''''''''''''
|
||||
|
||||
.. module:: windows.remotectypes
|
||||
|
||||
Performing parsing of PEB / PE in remote process may be painful and i didn't want
|
||||
to have two versions of all my parsing code.
|
||||
|
||||
So I made a wrapper around :mod:`ctypes` that is able to do two things:
|
||||
|
||||
- Transform a 32bits ctypes structure into a 64bits one and reverse
|
||||
|
||||
This is done by replacing the ``c_void_p``/``c_char_p`` by ``DWORD`` or
|
||||
``QWORD`` and rewriting a wrapper around the :mod:`ctypes` ``POINTER`` and other stuff.
|
||||
I might not works for every structure by i didn't have any problem for now.
|
||||
|
||||
- Read the memory in another process
|
||||
|
||||
For this one I rewrote a class that use the standard :mod:`ctypes` structure
|
||||
offset-size calcultation, extract those information when asked for a field and read it from the target process.
|
||||
We just need to take care of special cases: ``POINTER`` / ``ARRAY`` / ``STRING``
|
||||
|
||||
We also need to be carreful about the inheritance, we need to inherit from "hidden"
|
||||
:class:`ctypes` classes to keep the magic working.
|
||||
|
||||
|
||||
This module exports the following API:
|
||||
|
||||
.. autofunction:: transform_type_to_remote32bits
|
||||
|
||||
.. autofunction:: transform_type_to_remote64bits
|
||||
|
||||
Both functions return a class that represent the structure in a remote process.
|
||||
The class.__init__ accept two arguments:
|
||||
|
||||
* ``base_addr``: the address of the object in the remote process
|
||||
* ``target``: an object with a method ``read_memory`` (so a :class:`windows.winobject.WinProcess` in our case)
|
||||
|
||||
Example ``WinProcess.peb``::
|
||||
|
||||
def peb(self):
|
||||
if windows.current_process.bitness == 32 and self.bitness == 64:
|
||||
return RemotePEB64(self.peb_addr, self)
|
||||
if windows.current_process.bitness == 64 and self.bitness == 32:
|
||||
return RemotePEB32(self.peb_addr, self)
|
||||
return RemotePEB(self.peb_addr, self)
|
||||
|
||||
I am pretty sure that this code does NOT handle all the cases, so it might break some day.
|
||||
|
||||
syswow64.py -- Crossing the heaven gate
|
||||
'''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
.. module:: windows.syswow64
|
||||
|
||||
One of my goal with ``PythonForWindows`` is to have some abstraction of the bitness of the processes.
|
||||
It means being able to work on a ``32bits Python`` or a ``64bits Python``.
|
||||
|
||||
In the case of a 32bits python on a ``64bits`` system (``SysWow64``) it's not trivial to perform operation on
|
||||
other ``64bits`` processes. For example directly calling :func:`CreateRemoteThread` will not work.
|
||||
|
||||
To be able to perform those operation we must be able to execute code in the ``64bits`` part of our
|
||||
``SysWow64`` process.
|
||||
|
||||
.. note::
|
||||
|
||||
TODO link to ``Heaven Gate``
|
||||
|
||||
For that we need to jump to the 64bits segment of our process, execute some code then return.
|
||||
To do so, we need to use some ``far jump`` / ``far ret`` with the segments selector ``0x23`` (CS_32bits) and ``0x33`` (CS_64bits).
|
||||
|
||||
The generation of this is quite ugly in my case.
|
||||
This code is in:
|
||||
|
||||
.. function:: execute_64bits_code_from_syswow
|
||||
|
||||
Once we are able to execute some code in the ``64bits`` part we need to create the code that will call our API (in NTDLL).
|
||||
To do that, I rely on the type information already present in the function of :mod:`windows.winproxy`.
|
||||
With these information we are able to know
|
||||
|
||||
* The name of the API
|
||||
* The number of arguments
|
||||
|
||||
With that I generate the correct x64 stub (using :mod:`windows.native_exec.simple_x64`). With the function:
|
||||
|
||||
.. function:: generate_syswow64_call
|
||||
|
||||
One problem I encountered is that our function must be able to pass values of 64bits, so passing arguments by register is not possible.
|
||||
|
||||
For now I allocate a buffer where a python wrapper copy the parameters and the x64 stub retrieves them from here.
|
||||
|
||||
(It might be possible to do something by creating a WINCFUNC with only ULONG64 parameters).
|
||||
|
||||
.. function:: try_generate_stub_target
|
||||
|
||||
The final result is a ``Python`` function like the one in :mod:`windows.winproxy`
|
||||
|
||||
* It copies the arguments in the buffer
|
||||
* Jump on the 32->64 stub
|
||||
* X64 bits code retrieves the arguments in the buffer and setup the registers and the stack for the call
|
||||
* Call the API
|
||||
* Return to 32bits mode.
|
||||
|
||||
.. class:: Syswow64ApiProxy
|
||||
|
||||
Existing function are:
|
||||
|
||||
.. function:: NtCreateThreadEx_32_to_64
|
||||
|
||||
.. function:: NtQueryInformationProcess_32_to_64
|
||||
|
||||
.. function:: NtQueryInformationThread_32_to_64
|
||||
|
||||
.. function:: NtQueryVirtualMemory_32_to_64
|
||||
|
||||
.. function:: NtGetContextThread_32_to_64
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
.. module:: windows.native_exec
|
||||
|
||||
``windows.native_exec`` -- Native Code Execution
|
||||
************************************************
|
||||
|
||||
.. currentmodule:: windows.native_exec
|
||||
|
||||
The :mod:`windows.native_exec` allows to create `Python` functions calling native code.
|
||||
it also provide a simple assembler for x86 and x64.
|
||||
|
||||
The :mod:`windows.native_exec` provides those functions:
|
||||
|
||||
.. automodule:: windows.native_exec
|
||||
.. autofunction:: windows.native_exec.create_function
|
||||
|
||||
The :mod:`windows.native_exec` also contains some submodules:
|
||||
* :mod:`windows.native_exec.cpuid`
|
||||
|
||||
+35
-2
@@ -65,8 +65,41 @@ The :class:`PEB` is accessible via ``process.peb`` and is of type :class:`PEB`.
|
||||
.. autoclass:: LoadedModule
|
||||
|
||||
|
||||
.. warning::
|
||||
PEFile
|
||||
""""""
|
||||
|
||||
TODO: pe_parse.PEFile (sorry) but example at :ref:`sample_peb_exploration`
|
||||
:mod:`windows.pe_parse`
|
||||
'''''''''''''''''''''''
|
||||
|
||||
.. module:: windows.pe_parse
|
||||
|
||||
.. autofunction:: windows.pe_parse.GetPEFile
|
||||
|
||||
.. autoclass:: PEFile
|
||||
|
||||
|
||||
.. autoclass:: IATEntry
|
||||
|
||||
.. data:: addr
|
||||
|
||||
:class:`int` : Address of the IAT Entry
|
||||
|
||||
.. data:: ord
|
||||
|
||||
:class:`int` : Ordinal of the imported function
|
||||
|
||||
.. data:: name
|
||||
|
||||
:class:`int` : Name of the imported function
|
||||
|
||||
.. data:: value
|
||||
|
||||
:class:`int` : The content (destination) of the IAT entry
|
||||
|
||||
.. warning::
|
||||
|
||||
`value` is a descriptor. Setting its value will actually CHANGE THE IAT ENTRY, resulting in a segfault if no VirtualProtect have been done.
|
||||
|
||||
.. note::
|
||||
|
||||
See: :class:`windows.utils.VirtualProtected`
|
||||
@@ -99,6 +99,31 @@ Output::
|
||||
Sections: [<PESection ".text">, <PESection ".rdata">, <PESection ".data">, <PESection ".rsrc">, <PESection ".reloc">]
|
||||
|
||||
|
||||
.. _sample_iat_hook:
|
||||
|
||||
IAT hooking
|
||||
"""""""""""
|
||||
|
||||
.. literalinclude:: ..\..\samples\iat_hook.py
|
||||
|
||||
Output::
|
||||
|
||||
(cmd λ) python iat_hook.py
|
||||
Asking for <MY_SECRET_KEY>
|
||||
<in hook> Hook called | hKey = 0x12d687 | lpSubKey = <MY_SECRET_KEY>
|
||||
<in hook> Secret key asked, returning magic handle 0x12345678
|
||||
Result = 0x12345678
|
||||
|
||||
Asking for <MY_FAIL_KEY>
|
||||
<in hook> Hook called | hKey = 0x12d687 | lpSubKey = <MY_FAIL_KEY>
|
||||
<in hook> Asked for a failing key: returning 0x2a
|
||||
WindowsError(42, 'Windows Error 0x2A')
|
||||
|
||||
Asking for <HKEY_CURRENT_USER/Software>
|
||||
<in hook> Hook called | hKey = 0x80000001L | lpSubKey = <Software>
|
||||
<in hook> Non-secret key : calling normal function
|
||||
Result = 0x108
|
||||
|
||||
.. _sample_network_exploration:
|
||||
|
||||
:class:`Network` - socket exploration
|
||||
|
||||
+10
-10
@@ -1,33 +1,33 @@
|
||||
The ``windows`` module
|
||||
**********************
|
||||
|
||||
The ``windows`` module is the module installed by :file:`setup.py` (that does not exists right now).
|
||||
The ``windows`` module is the module installed by :file:`setup.py` (that does not exists right now).
|
||||
This module export some object representing the current state of the system. It also offers some submodules aimed to help the interface with ``Windows`` and native code exection.
|
||||
|
||||
The defaults objects accessible in ``windows`` are:
|
||||
* ``system`` of type :class:`windows.winobject.System`
|
||||
* ``current_process`` of type :class:`CurrentProcess`
|
||||
* ``current_thread`` of type :class:`CurrentThread`
|
||||
|
||||
* ``current_process`` of type :class:`windows.winobject.CurrentProcess`
|
||||
* ``current_thread`` of type :class:`windows.winobject.CurrentThread`
|
||||
|
||||
The submodules that you might use by themself are:
|
||||
* :mod:`windows.native_exec`
|
||||
* :mod:`windows.winproxy`
|
||||
* :mod:`windows.utils`
|
||||
|
||||
|
||||
.. _object_system:
|
||||
|
||||
|
||||
The ``system`` object
|
||||
"""""""""""""""""""""
|
||||
|
||||
.. autoclass:: windows.winobject.System
|
||||
:no-show-inheritance:
|
||||
|
||||
|
||||
.. autoattribute:: windows.winobject.System.registry
|
||||
:annotation:
|
||||
|
||||
|
||||
Object of class :class:`windows.registry.Registry`
|
||||
|
||||
|
||||
.. autoattribute:: windows.winobject.System.network
|
||||
:annotation:
|
||||
|
||||
|
||||
Object of class :class:`windows.network.Network`
|
||||
@@ -0,0 +1,25 @@
|
||||
Early Work In Progress
|
||||
======================
|
||||
|
||||
Here are some features that are still work in progress. Code might be unstable and/or ultra-ugly.
|
||||
|
||||
Wintrust -- Signature check
|
||||
"""""""""""""""""""""""""""
|
||||
|
||||
Should it juste be part of :mod:`windows.utils` ?
|
||||
|
||||
.. module:: windows.wintrust
|
||||
|
||||
.. autofunction:: windows.wintrust.check_signature
|
||||
|
||||
|
||||
|
||||
WMI -- WMI request
|
||||
""""""""""""""""""
|
||||
|
||||
Unstable code: not fully tested, ugly COM initialisation
|
||||
|
||||
|
||||
.. module:: windows.wmi
|
||||
|
||||
.. autoclass:: windows.wmi.WmiRequester
|
||||
Reference in New Issue
Block a user