Writing documentation

This commit is contained in:
hakril
2016-01-03 01:51:46 +01:00
parent 1d4ce6986b
commit 269b6dde58
13 changed files with 108 additions and 397 deletions
+6 -5
View File
@@ -20,15 +20,15 @@ import os
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath(__file__ + "..\..\..\.."))
sys.path.append(r"C:\Users\hakril\Documents\Work\PythonForWindows")
print("Adding <{0}>".format(sys.path[-1]))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
needs_sphinx = '1.2'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -56,7 +56,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'PyWindows'
project = u'PythonForWindows'
copyright = u'2015, Clement Rouault'
# The version info for the project you're documenting, acts as replacement for
@@ -114,7 +114,8 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'alabaster'
html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
-19
View File
@@ -1,19 +0,0 @@
GENERATED Documentation test
****************************
Testing1
********
.. automodule:: windows.generated_def.winstructs
Testing2
********
.. autoclass:: _PEB_LDR_DATA
.. autoclass:: _PEB
.. autoclass:: _LIST_ENTRY
+2 -4
View File
@@ -10,11 +10,9 @@ Contents:
.. toctree::
:maxdepth: 2
:numbered:
test.rst
pe_parse.rst
native_exec.rst
rem_python.rst
windows.rst
utils.rst
-79
View File
@@ -1,79 +0,0 @@
Native code execution
***********************
.. automodule:: windows.native_exec
:members: generate_callback_stub, create_function
The native_function submodule
"""""""""""""""""""""""""""""
.. automodule:: windows.native_exec.native_function
:members: create_function
Simple machine code generation
""""""""""""""""""""""""""""""
These modules allow you to write some simple x86 / x64 shellcode. This is useful to use in adequation to :func:`create_function`.
The instruction name are as explicit as possible with the following convention:
* This is `intel syntax` so `dest, src`
* X specify a value passed as parameters::
Mov_EAX_X(0x42) # mov eax, 0x42
* D is for `dereference`::
Mov_EAX_DX(0x42424242) # mov EAX, [0x42424242]
Mov_DEAX_EDI() # mov [EAX], EDI
All instructions follow this interface:
.. py:class:: Instruction
.. py:method:: get_code(self)
:returns: :class:`str`: The raw code of the instruction
.. py:method:: get_mnemo(self)
:returns: :class:`str`: The mnemonic of the instruction
Example::
import windows.native_exec.simple_x86 as x86
i = x86.Mov_EAX_X(0x42434445)
i.get_code()
# '\xb8EDCB'
i.get_mnemo()
# 'mov EAX, 0x42434445'
You can also use a :class:`MultipleInstr` instance to merge instructions
Example::
import windows.native_exec.simple_x86 as x86
code = x86.MultipleInstr()
code += x86.Mov_EAX_X(0x42434445)
code += x86.Push_EAX()
code += x86.Ret()
code.get_code()
# '\xb8EDCBP\xc3'
print(code.get_mnemo())
# mov EAX, 0x42434445
# push EAX
# ret
simple_x86 instructions
-----------------------
.. automodule:: windows.native_exec.simple_x86
simple_x64 instructions
-----------------------
.. automodule:: windows.native_exec.simple_x64
-179
View File
@@ -1,179 +0,0 @@
Loaded DLL Exploration and IAT hooks
************************************
List of loaded modules
""""""""""""""""""""""
Accessible using::
import windows
windows.current_process.peb.modules[int].pe
..note::
See: :class:`windows.winobject.PEB` and :class:`windows.winobject.LoadedModule`
DLL Import and IAT
""""""""""""""""""
.. py:class:: PEFile
.. py:attribute:: imports
The imports of the PE
.. note::
This is a :class:`dict` DLLName -> [:class:`IATEntry`]
Example::
import windows
k32 = windows.current_process.peb.modules[2]
# <LoadedModule "KERNEL32.DLL" at 0x2deca30>
k32.pe.imports.keys()
# ['kernelbase.dll', 'api-ms-win-core-profile-l1-1-0.dll', ...]
k32.pe.imports['kernelbase.dll']
# [<IATEntry "EnumLanguageGroupLocalesW" ordinal 58>, <IATEntry "GetNamedPipeAttribute" ordinal 93>, ...]
[entry for entry in k32.pe.imports['kernelbase.dll'] if entry.name == "lstrcmpiW"][0]
# <IATEntry "lstrcmpiW" ordinal 244>
.. py:class:: IATEntry
| Reprensent An entry in the IAT of a module
| Can be used to get resolved value and setup hook
.. py:attribute:: name
| :class:`int` : The name of the import
.. py:attribute:: ord
| :class:`int` : The ordinal of the import
.. py:attribute:: addr
| :class:`int` : The address of the IAT entry
.. py:attribute:: value
| :class:`int` : The 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`
.. py:method:: set_hook(self, callback, types=None)
Setup a hook, `callback` should respect the :ref:`hook_protocol`. If `callback` have no :ref:`type_information`, `types` should provide them.
IAT Hooking
"""""""""""
.. _hook_protocol:
The 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 allow 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()
.. _type_information:
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
Put the hook
------------
To setup your IAT hook you just need:
* A callback that respect the :ref:`hook_protocol`
* The :class:`IATEntry` to hook
You just need to use the function :func:`IATEntry.set_hook`
Full Example::
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)
-77
View File
@@ -1,77 +0,0 @@
Remote Python Injection
***********************
It's possible to inject the python interpreter into remote process. All you need to do is to use the method :func:`windows.winobject.WinProcess.execute_python`.
Calling this function will trigger the interpreter injection and the python code execution.
For simpler interaction interaction with the remote python, you can an RPC master linked to the remote interpreter.
RPython 101
'''''''''''
RPython is a very simple Pythonic (I hope so) RPC slave-master. It's goal is to allow easy manipulation of a remote interpreter.
The only action up to the `slave` is the creation of it's `name_pool`: a namespace of object accessible by the `master`.
After that the `slave` will just wait for request and return the desired object.
All slave object seen by the master are proxy that redirect operation to the slave.
RPython RPC to remote process
'''''''''''''''''''''''''''''
For easy manipulation of a remote Python interpreter, you can use the RPCInjection module::
import windows
import RPCInjection
calc = [x for x in windows.system.processes if x.name == "calc.exe"][0]
master = RPCInjection.launch_remote_slave(calc)
# Master is a RPC-master to a python interpreter in calc.exe
master['windows']
# <RemoteObj |<module 'windows' from 'C:\Users\hakril\Documents\Work\PythonForWindows\windows\__init__.pyc'>|>
# This is a way to get our own (python.exe) pid
windows.current_process.pid
3624
# This is a way to get the pid of calc.exe
master['windows'].current_process
# <RemoteObj |<windows.winobject.CurrentProcess object at 0x055E6250>|>
master['windows'].current_process.pid
5052
# We can also play with the peb of the remote process
master['windows'].current_process.peb
# <RemoteObj |<windows.winobject.PEB object at 0x054A3DF0>|>
master['windows'].current_process.peb.commandline
# <RemoteObj |<WinUnicodeString ""C:\Windows\SysWOW64\calc.exe" " at 0x55e9850>|>
# we can import new stuff
x['json']
# ...
# RPython.exchange.RemoteKeyError:
# ....
# KeyError: u'json'
x.imp('json')
# <RemoteObj |None|>
x['json']
# <RemoteObj |<module 'json' from 'C:\Python27\Lib\json\__init__.pyc'>|>
.. note::
The slave `name_pool` in RPCInjection is filled with :mod:`windows`, :mod:`__import__`, :mod:`ctypes` and :mod:`self` (the RemotePythonSlave object)
The master.RemotePython
'''''''''''''''''''''''
.. autoclass:: RPython.master.RemotePython
.. py:method:: __getitem__
Alias to :func:`ask_by_name`
Remote IAT Hooking
''''''''''''''''''
See Examples directory
-13
View File
@@ -1,13 +0,0 @@
Overwiew of the `windows` objects
**************************
object exported by `windows`
""""""""""""""""""""""""""""
.. automodule:: windows
Principal classes for process exploration
"""""""""""""""""""""""""""""""""""""""""
.. automodule:: windows.winobject
:members:
+33 -3
View File
@@ -1,4 +1,34 @@
Helpers
*******
``windows.utils`` -- Pythonic Windows Utilities
***********************************************
.. automodule:: windows.utils
.. module:: windows.utils
Context Managers
""""""""""""""""
:mod:`windows.utils` provides some context managers wrapping `standard` contextual operations
like ``VirtualProtect`` or ``SysWow Redirection``
VirtualProtected
''''''''''''''''
.. autoclass:: windows.utils.VirtualProtected
:no-show-inheritance:
DisableWow64FsRedirection
'''''''''''''''''''''''''
.. autoclass:: windows.utils.DisableWow64FsRedirection
:no-show-inheritance:
Helper functions
""""""""""""""""
.. autofunction:: windows.utils.enable_privilege
.. autofunction:: windows.utils.check_is_elevated
.. autofunction:: windows.utils.check_debug
.. autofunction:: windows.utils.create_process
.. autofunction:: windows.utils.create_console
.. autofunction:: windows.utils.pop_shell
.. autofunction:: windows.utils.create_file_from_handle
.. autofunction:: windows.utils.get_handle_from_file
+32
View File
@@ -0,0 +1,32 @@
The ``windows`` module
**********************
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`
The submodules that you might use by themself are:
* :mod:`windows.native_exec`
* :mod:`windows.winproxy`
* :mod:`windows.utils`
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`
+1
View File
@@ -24,5 +24,6 @@ current_thread = CurrentThread()
import windows.vectored_exception
import windows.wmi
import windows.utils
__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread', 'winproxy']
+2 -2
View File
@@ -12,7 +12,7 @@ def get_stack_func_name(lvl):
def do_dbgprint(msg, type=None):
if (options['cats'] is None) or type.upper() in options['cats']:
if ("ALL" in options['cats']) or type.upper() in options['cats']:
frame, func = get_stack_func_name(2)
logger = logging.getLogger(frame.f_globals['__name__'] + ":" + func)
logger.debug(msg)
@@ -26,7 +26,7 @@ def parse_option(s):
if s[0] == "=":
s = s[1:]
if s:
cats = [x.upper() for x in s.split('-')]
cats = [x.upper().strip() for x in s.split('-')]
options['cats'] = cats
formt = 'DBG|%(name)s|%(message)s'
+24 -11
View File
@@ -43,18 +43,18 @@ def is_wow_64(hProcess):
def create_file_from_handle(handle, mode="r"):
"""Return a Python :class:`file` arround a windows HANDLE"""
"""Return a Python :class:`file` around a ``Windows`` HANDLE"""
fd = msvcrt.open_osfhandle(handle, os.O_TEXT)
return os.fdopen(fd, mode, 0)
def get_handle_from_file(f):
"""Get the windows HANDLE of a python :class:`file`"""
"""Get the ``Windows`` HANDLE of a python :class:`file`"""
return msvcrt.get_osfhandle(f.fileno())
def create_console():
"""Create a new console displaying STDOUT
"""Create a new console displaying STDOUT.
Useful in injection of GUI process"""
winproxy.AllocConsole()
stdout_handle = winproxy.GetStdHandle(windef.STD_OUTPUT_HANDLE)
@@ -71,6 +71,7 @@ def create_console():
def create_process(path, show_windows=False):
"""A convenient wrapper arround :func:`windows.winproxy.CreateProcessA`"""
proc_info = PROCESS_INFORMATION()
lpStartupInfo = None
if show_windows:
@@ -84,7 +85,11 @@ def create_process(path, show_windows=False):
def enable_privilege(lpszPrivilege, bEnablePrivilege):
"""Enable of disable a privilege: enable_privilege(SE_DEBUG_NAME, True)"""
"""
Enable or disable a privilege::
enable_privilege(SE_DEBUG_NAME, True)
"""
tp = TOKEN_PRIVILEGES()
luid = LUID()
hToken = HANDLE()
@@ -105,7 +110,7 @@ def enable_privilege(lpszPrivilege, bEnablePrivilege):
def check_is_elevated():
"""Return True if process is Admin"""
"""Return ``True`` if process is Admin"""
hToken = HANDLE()
elevation = TOKEN_ELEVATION()
cbsize = DWORD()
@@ -117,8 +122,10 @@ def check_is_elevated():
def check_debug():
"""Check that kernel is in debug mode
beware if NOUMEX (https://msdn.microsoft.com/en-us/library/windows/hardware/ff556253(v=vs.85).aspx#_______noumex______)"""
"""Check that kernel is in debug mode (beware of NOUMEX):
https://msdn.microsoft.com/en-us/library/windows/hardware/ff556253(v=vs.85).aspx#_______noumex______
"""
hkresult = HKEY()
cbsize = DWORD(1024)
bufferres = (c_char * cbsize.value)()
@@ -163,9 +170,8 @@ def get_kernel_modules():
class VirtualProtected(object):
"""A context manager usable like `VirtualProtect` that will restore the old protection at exit
Example::
"""
A context manager usable like `VirtualProtect` that will restore the old protection at exit ::
with utils.VirtualProtected(IATentry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE):
IATentry.value = 0x42424242
@@ -188,7 +194,14 @@ class VirtualProtected(object):
class DisableWow64FsRedirection(object):
"""A context manager that disable the Wow64 Fs Redirection"""
"""
A context manager that disable the SysWow64 Filesystem Redirection ::
if is_process_32_bits:
def pop_calc_64():
with windows.utils.DisableWow64FsRedirection():
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", True)
"""
def __enter__(self):
if windows.current_process.bitness == 64:
return self
+8 -5
View File
@@ -6,6 +6,7 @@ import struct
import windows
import windows.network
import windows.registry
import windows.syswow64
#import windows.vectored_exception
import windows.winproxy as winproxy
@@ -33,10 +34,12 @@ class AutoHandle(object):
def handle(self):
"""A handle on the object
:type: HANDLE
.. note::
The handle is automaticaly closed when the object is destroyed
:type: int
"""
if hasattr(self, "_handle"):
@@ -53,10 +56,10 @@ class AutoHandle(object):
class System(object):
"""Represent the current windows system python is running on"""
"""Represent the current ``Windows`` system ``Python`` is running on"""
network = windows.network.Network()
registry = windows.registry.Registry()
network = windows.network.Network() # Object of class :class:`windows.network.Network`
registry = windows.registry.Registry() # Object of class :class:`windows.registry.Registry`
@property
def processes(self):
@@ -80,7 +83,7 @@ class System(object):
def bitness(self):
"""The bitness of the system
:type: int -- 32 or 64
:type: :class:`int` -- 32 or 64
"""
if os.environ["PROCESSOR_ARCHITECTURE"].lower() != "x86":