Adding more and more documentation

This commit is contained in:
Clement Rouault
2016-01-06 20:11:20 +01:00
parent b2eede98b1
commit 9c8a4dcfad
22 changed files with 467 additions and 30 deletions
+2
View File
@@ -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 ------------------------------------------------
+113
View File
@@ -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
+3
View File
@@ -17,6 +17,9 @@ Contents:
utils.rst
native_exec.rst
various.rst
iat_hook.rst
wip.rst
internals.rst
sample.rst
+122
View File
@@ -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
+3 -2
View File
@@ -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
View File
@@ -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`
+25
View File
@@ -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
View File
@@ -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`
+25
View File
@@ -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