More doc + regitry PyPhKey.get()

This commit is contained in:
Clement Rouault
2016-01-07 19:26:40 +01:00
parent 9c8a4dcfad
commit b459e291c4
32 changed files with 313 additions and 157 deletions
+18 -23
View File
@@ -1,13 +1,13 @@
# PythonForWindows
PythonForWindows is a base of code aimed to make interaction with Windows (on X86/X64) easier (with both 32 and 64 bits Python).
It's goal is to offer abstractions around some of the OS features in a (I hope) pythonic way.
PythonForWindows is a base of code aimed to make interaction with `Windows` (on X86/X64) easier (with both 32 and 64 bits Python).
Its goal is to offer abstractions around some of the OS features in a (I hope) pythonic way.
It also tries to make the barrier between python and native execution thiner in both ways.
There is no external dependencies but it relies heavily on the ctypes modules.
There is no external dependencies but it relies heavily on the `ctypes` modules.
Some of this code is clean (IMHO) and some parts are just a wreck that work for now.
Let say that the codebase evolves with my needs and my curiosity.
Some of this code is clean (IMHO) and some parts are just a wreck that works for now.
Let's say that the codebase evolves with my needs and my curiosity.
You can find some examples of code in the `samples/` directory.
@@ -15,48 +15,43 @@ You can find some examples of code in the `samples/` directory.
### Processes / Threads
PythonForWindows offer a object oriented around processes of the system and allow you to:
PythonForWindows offers objects around processes and allows you to:
- Retrieve basic process informations (pid, name, ppid, bitness, ...)
- Perform basic interprocess operation (alloc, create thread, read/write memory)
- Perform basic interprocess operation (allocation, create thread, read/write memory)
- Explore the PEB (Process Environment Block)
- Execute native and python code in the context of the process.
I try by best to make those features available for every cross-bitness processes (32 <-> 64 in both ways).
This involve relying on non-documented Windows function/behaviour and also injecting code in the 64bits world of a Syswow64 process.
- Execute `native` and `Python` code in the context of a process.
I try by best to make those features available for every cross-bitness processes (`32 <-> 64` in both ways).
This involve relying on non-documented `Windows` function/behaviour and also injecting code in the 64bits world of a `Syswow64` process.
All those operations are also available for the `current_process`.
You can also make some operation of threads (suspend/resume/wait/get( or set) context/ kill)
You can also make some operation on threads (suspend/resume/wait/get(or set) context/ kill)
### IAT Hook
This codebase is born from my need to have IAT hooks implemented in Python.
So the features is present (see `samples/`)
So the features is present (see online documentation)
### Winproxy
A pythonic wrapper around some Windows functions. Arguments name and order are the same,
A wrapper around some Windows functions. Arguments name and order are the same,
but some have default values and the functions raise exception on call error (I don't like 'if' around all my call).
### Native execution
To make the barrier beetwen native and python code,
PythonForWindows allows you to create native function callable from Python (thanks ctypes) and also embded
To make the barrier between `native` and `Python` code,
PythonForWindows allows you to create native function callable from Python (thanks `ctypes`) and also embed
a simple x86/x64 assembler.
### COM on Python
Some code to call a COM interface from Python or create a COM object implemented in Python.
### Other stuff
## Other stuff
Some code are just exploration and need improvement like:
- Wintrust
- WMI
- Registry access
- Exception
- COM
+1 -1
View File
@@ -1,7 +1,7 @@
TODO:
- Documentation
- ProcessMemory object ? (metasm like)
- Extend Registry feature (write + read 1 key)
- Extend Registry feature (write)
FIXME:
- WMI
+18 -1
View File
@@ -538,4 +538,21 @@
#define DEBUG_EVENT_CHANGE_SYMBOL_STATE 0x00001000
#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512
#define WOW64_SIZE_OF_80387_REGISTERS 80
#define WOW64_SIZE_OF_80387_REGISTERS 80
#define REG_NONE ( 0 )
#define REG_SZ ( 1 )
#define REG_EXPAND_SZ ( 2 )
#define REG_BINARY ( 3 )
#define REG_DWORD ( 4 )
#define REG_DWORD_LITTLE_ENDIAN ( 4 )
#define REG_DWORD_BIG_ENDIAN ( 5 )
#define REG_LINK ( 6 )
#define REG_MULTI_SZ ( 7 )
#define REG_RESOURCE_LIST ( 8 )
#define REG_FULL_RESOURCE_DESCRIPTOR ( 9 )
#define REG_RESOURCE_REQUIREMENTS_LIST ( 10 )
#define REG_QWORD ( 11 )
#define REG_QWORD_LITTLE_ENDIAN ( 11 )
+12
View File
@@ -0,0 +1,12 @@
COM - Component Object Model
""""""""""""""""""""""""""""
A module to call ``COM`` interfaces from `Python` or
``COM`` vtable in python.
This code is only used in :mod:`windows.wmi`.
The ability to create ``COM`` vtable is used in LKD
.. note::
TODO: link to LKD code
+1 -1
View File
@@ -43,7 +43,7 @@ extensions = [
'sphinx.ext.autodoc',
]
autodoc_default_flags = ['show-inheritance', 'inherited-members']
autodoc_default_flags = ['inherited-members']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
+3 -3
View File
@@ -5,8 +5,8 @@ IAT hooking
See sample :ref:`sample_iat_hook`
Put a IAT hook
''''''''''''''
Putting an IAT hook
'''''''''''''''''''
To setup your IAT hook you just need:
@@ -71,7 +71,7 @@ A hook callback must also embed some :ref:`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.
In order make the magic behind hook callback, :mod:`ctypes` needs 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.
+3 -2
View File
@@ -13,10 +13,10 @@ Contents:
:numbered:
windows.rst
various.rst
native_exec.rst
winproxy.rst
utils.rst
native_exec.rst
various.rst
iat_hook.rst
wip.rst
internals.rst
@@ -28,4 +28,5 @@ Indices and tables
* :ref:`modindex`
* :ref:`search`
* :ref:`genindex`
+7 -7
View File
@@ -17,13 +17,13 @@ So I made a wrapper around :mod:`ctypes` that is able to do two things:
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.
It 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``
offset-size calculation, extracts 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.
@@ -85,7 +85,7 @@ 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:
Then I generate the correct x64 stub (using :mod:`windows.native_exec.simple_x64`) with the function:
.. function:: generate_syswow64_call
@@ -100,10 +100,10 @@ For now I allocate a buffer where a python wrapper copy the parameters and the x
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
* Jumps 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.
* Calls the API
* Returns to 32bits mode.
.. class:: Syswow64ApiProxy
+13 -7
View File
@@ -4,10 +4,10 @@
************************************************
The :mod:`windows.native_exec` allows to create `Python` functions calling native code.
it also provide a simple assembler for x86 and x64.
:mod:`windows.native_exec` allows to create `Python` functions calling native code.
it also provides a simple assembler for x86 and x64.
The :mod:`windows.native_exec` provides those functions:
:mod:`windows.native_exec` provides those functions:
.. autofunction:: windows.native_exec.create_function
@@ -47,7 +47,7 @@ Demo::
The :mod:`windows.native_exec.simple_x86` module allows to create simple x86 code.
Its features are:
* Forward - Backward jump (using label)
* Forward - Backward jump (using labels)
* Non-string interface for conditional/context dependent generation
@@ -59,12 +59,13 @@ The assembler instructions are `Python` object that may accept arguments represe
the mnemonic operands.
These parameters can be of type:
* str (register)
* int (int)
* mem_access (memory access)
* :class:`str` (register)
* :class:`int` (int)
* :class:`mem_access` (memory access)
.. autoclass:: windows.native_exec.simple_x86.mem_access
:members: prefix, base, index, scale, disp
:exclude-members: count
The :class:`mem_access` object can be created:
* By hand
@@ -72,6 +73,7 @@ The :class:`mem_access` object can be created:
* Using :func:`mem`
.. autofunction:: windows.native_exec.simple_x86.create_displacement
.. autofunction:: windows.native_exec.simple_x86.deref
.. autofunction:: windows.native_exec.simple_x86.mem
Instruction assembling::
@@ -116,6 +118,7 @@ Another example from a project::
INPUT_BUFFER_PORT = x86.mem('[ECX + 4]')
INPUT_BUFFER_VALUE = x86.mem('[ECX + 8]')
out_ioctl = x86.MultipleInstr()
out_ioctl += x86.Cmp(IO_STACK_INPUT_BUFFER_LEN, 0xc) # size indicator / port / value
out_ioctl += x86.Jnz(":FAIL")
out_ioctl += x86.Mov('ECX', IO_STACK_INPUT_BUFFER)
@@ -140,6 +143,9 @@ Another example from a project::
out_ioctl += x86.Mov('EAX', 0x0C000000D)
out_ioctl += x86.Ret()
out_ioctl.get_code()
'\x81~\x08\x0c\x00\x00\x00u&\x8bN\x10\x8bQ\x04\x8bA\x08\x8b\t\x81\xf9\x01\x00\x00\x00u\x03\xee\xeb\r\x81\xf9\x02\x00\x00\x00u\x04f\xef\xeb\x01\xef1\xc0\xc3\xc7\xc0\r\x00\x00\xc0\xc3'
:mod:`windows.native_exec.simple_x64` -- X64 Assembler
""""""""""""""""""""""""""""""""""""""""""""""""""""""
+3 -5
View File
@@ -8,14 +8,12 @@ Network
See sample :ref:`sample_network_exploration`
:class:`Network` class
""""""""""""""""""""""
.. autoclass:: windows.network.Network
Connection classes
""""""""""""""""""
Connections
"""""""""""
.. autoclass:: windows.network.TCP4Connection
.. autoclass:: windows.network.TCP6Connection
+18 -2
View File
@@ -12,6 +12,7 @@ CurrentProcess
.. autoclass:: CurrentProcess
:members:
:show-inheritance:
:inherited-members:
CurrentThread
@@ -19,6 +20,7 @@ CurrentThread
.. autoclass:: CurrentThread
:members:
:show-inheritance:
:inherited-members:
WinProcess
@@ -30,6 +32,7 @@ WinProcess
.. autoclass:: WinProcess
:members:
:show-inheritance:
:inherited-members:
@@ -38,11 +41,13 @@ WinThread
.. autoclass:: WinThread
:members:
:show-inheritance:
:inherited-members:
.. autoclass:: DeadThread
:members:
:show-inheritance:
:inherited-members:
@@ -56,17 +61,23 @@ The :class:`PEB` is accessible via ``process.peb`` and is of type :class:`PEB`.
See sample :ref:`sample_peb_exploration`
PEB
'''
.. autoclass:: PEB
:members:
:inherited-members:
.. autoclass:: WinUnicodeString
LoadedModule
''''''''''''
.. autoclass:: LoadedModule
PEFile
""""""
PEFile - Parsing loaded PE
""""""""""""""""""""""""""
:mod:`windows.pe_parse`
'''''''''''''''''''''''
@@ -75,8 +86,13 @@ PEFile
.. autofunction:: windows.pe_parse.GetPEFile
PEFile
^^^^^^
.. autoclass:: PEFile
IATEntry
^^^^^^^^
.. autoclass:: IATEntry
+21 -1
View File
@@ -3,8 +3,28 @@ Registry
.. module:: windows.registry
.. note::
See sample :ref:`sample_registry`
Registry
""""""""
.. autoclass:: Registry
:special-members: __getitem__
.. autoclass:: PyHKey
PyHKey
""""""
.. autoclass:: PyHKey
.. function:: __getitem__(name)
Alias for :func:`open_subkey`
KeyValue
""""""""
.. autoclass:: KeyValue
:exclude-members: count, index
+40 -1
View File
@@ -148,4 +148,43 @@ Output::
Traceback (most recent call last):
File ".\network.py", line 45, in <module>
s.send("LAIT")
socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host
socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host
.. _sample_registry:
:class:`Registry`
"""""""""""""""""
.. literalinclude:: ..\..\samples\registry.py
Output::
(cmd λ) python.exe registry.py
Registry is <<windows.registry.Registry object at 0x02941290>>
HKEY_CURRENT_USER is <<PyHKey "HKEY_CURRENT_USER">>
HKEY_CURRENT_USER subkeys names are:
['AppEvents',
'AppXBackupContentType',
'Console',
'Control Panel',
'Environment',
'EUDC',
'Identities',
'Keyboard Layout',
'Network',
'Printers',
'Software',
'System',
'Volatile Environment']
Opening 'Software' in HKEY_CURRENT_USER: <PyHKey "HKEY_CURRENT_USER\Software">
We can also open it in one access: <PyHKey "HKEY_CURRENT_USER\Sofware">
Looking at CurrentVersion
Key is <PyHKey "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion">
values are:
[KeyValue(name='SoftwareType', value=u'System', type=1),
KeyValue(name='RegisteredOwner', value=u'hakril', type=1),
KeyValue(name='InstallDate', value=0, type=4),
...
KeyValue(name='PathName', value=u'C:\\Windows', type=1)]
registered owner = <KeyValue(name='RegisteredOwner', value=u'hakril', type=1)>
+3 -3
View File
@@ -1,4 +1,4 @@
``windows.utils`` -- Pythonic Windows Utilities
``windows.utils`` -- Windows Utilities
***********************************************
.. module:: windows.utils
@@ -14,13 +14,13 @@ VirtualProtected
.. autoclass:: windows.utils.VirtualProtected
:no-show-inheritance:
DisableWow64FsRedirection
'''''''''''''''''''''''''
.. autoclass:: windows.utils.DisableWow64FsRedirection
:no-show-inheritance:
Helper functions
""""""""""""""""
+4 -3
View File
@@ -2,13 +2,14 @@ The :mod:`windows` objects
==========================
Through the :ref:`system <object_system>` object many classes representing various `Windows`
part are accessible.
parts are accessible.
This sections describes them by group of relation.
.. toctree::
:maxdepth: 2
:maxdepth: 3
process.rst
registry.rst
network.rst
network.rst
com.rst
+5 -1
View File
@@ -2,7 +2,9 @@ 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.
This module exports some objects representing the current state of the system.
It also offers some submodules aimed to help the interfacing with ``Windows`` and native code execution.
The defaults objects accessible in ``windows`` are:
* ``system`` of type :class:`windows.winobject.System`
@@ -19,6 +21,8 @@ The submodules that you might use by themself are:
The ``system`` object
"""""""""""""""""""""
.. currentmodule:: windows.winobject
.. autoclass:: windows.winobject.System
:no-show-inheritance:
+7 -7
View File
@@ -3,15 +3,15 @@
.. module:: windows.winproxy
The :mod:`windows.winproxy` module tries to be a pythontic wrapper around windows API of various DLL.
It also hevily relies on :mod:`ctypes`.
:mod:`windows.winproxy` tries to be a pythontic wrapper around windows API of various DLL.
It also heavily relies on :mod:`ctypes` and :mod:`windows.generated_def.winfuncs`
Here are the thing to know about :mod:`windows.winproxy`
* All of this is based on :mod:`windows.generated_def.winfunc`
Here are the things to know about :mod:`windows.winproxy`
* All of this is based on :mod:`windows.generated_def.winfuncs`
* DLL is loaded the first time an API of it is called
* All parameters can be passed by ordinal or keyword
* The call with fail if an argument with default value ``NeededParamater`` have be called without another value.
* The call will raise a subclasse of :class:`WindowsError` if it fail.
* The call will fail if an argument with default value ``NeededParamater`` have been called without another value.
* The call will raise a subclass of :class:`WindowsError` if it fails.
* Some functions are 'transparent proxy' it means that all parameters are mandatory
Example: ``VirtualAlloc``
@@ -60,7 +60,7 @@ Calling it
TypeError: VirtualAlloc: Missing Mandatory parameter <dwSize>
"""
# Error raise exception
# Error raises exception
windows.winproxy.VirtualAlloc(dwSize=0xffffffff)
"""
Traceback (most recent call last):
+1 -3
View File
@@ -13,13 +13,11 @@ Should it juste be part of :mod:`windows.utils` ?
.. autofunction:: windows.wintrust.check_signature
.. module:: windows.wmi
WMI -- WMI request
""""""""""""""""""
Unstable code: not fully tested, ugly COM initialisation
.. module:: windows.wmi
.. autoclass:: windows.wmi.WmiRequester
+1 -3
View File
@@ -13,7 +13,7 @@ import windows
@windows.hooks.RegOpenKeyExACallback
def open_reg_hook(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function):
print("<in hook> Hook called | hKey = {0} | lpSubKey = <{1}>".format(hex(hKey), lpSubKey.value))
# Out hook can choose to call the real_function or not
# Our hook can choose to call the real_function or not
if "SECRET" in lpSubKey.value:
print("<in hook> Secret key asked, returning magic handle 0x12345678")
# We must respect the hooked method return-value interface
@@ -39,11 +39,9 @@ adv_imports = pythondll_module.pe.imports['advapi32.dll']
RegOpenKeyExA_iat = [n for n in adv_imports if n.name == "RegOpenKeyExA"][0]
# Setup our hook
RegOpenKeyExA_iat.set_hook(open_reg_hook)
# Use python native module _winreg that call 'RegOpenKeyExA'
print("Asking for <MY_SECRET_KEY>")
v = _winreg.OpenKey(1234567, "MY_SECRET_KEY")
print("Result = " + hex(v.handle))
+1 -8
View File
@@ -10,30 +10,23 @@ if not windows.utils.check_is_elevated():
print("!!! Demo will fail because closing a connection require elevated process !!!")
print("Working on ipv4")
conns = windows.system.network.ipv4
print("== Listening ==")
print("== Listening ==")
print("Some listening connections: {0}".format([c for c in conns if not c.established][:3]))
print("Listening ports are : {0}".format([c.local_port for c in conns if not c.established]))
print("== Established ==")
print("Some established connections: {0}".format([c for c in conns if c.established][:3]))
TARGET_HOST = "localhost"
TARGET_PORT = 80
print("== connection to {0}:{1} ==".format(TARGET_HOST, TARGET_PORT))
s = socket.create_connection((TARGET_HOST, TARGET_PORT))
our_connection = [c for c in windows.system.network.ipv4 if c.established and c.remote_port == TARGET_PORT and c.remote_addr == s.getpeername()[0]]
print("Our connection is {0}".format(our_connection))
print("Sending YOP")
s.send("YOP")
print("Closing socket")
+9 -9
View File
@@ -11,18 +11,18 @@ print("Registry is <{0}>".format(registry))
current_user = registry["HKEY_CURRENT_USER"]
print("HKEY_CURRENT_USER is <{0}>".format(current_user))
subkeys_name = [s.name for s in current_user.subkeys]
print("HKEY_CURRENT_USER subkeys names are is <{0}>".format(pprint.pprint(subkeys_name)))
print("HKEY_CURRENT_USER subkeys names are:")
pprint.pprint(subkeys_name)
print("Opening 'Software' in HKEY_CURRENT_USER: {0}".format(current_user["Software"]))
print("We can also open it in one access: {0}".format(registry[r"HKEY_CURRENT_USER\Sofware"]))
print("Looking at CurrentVersion")
print("Looking for the JIT Debugger")
windows_info = registry["HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion"]
print("Key is {0}".format(windows_info))
jit_debug_key = registry["HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug"]
print("values are:")
pprint.pprint(windows_info.values)
print("Key is {0}".format(jit_debug_key))
print("values are: {0}".format(pprint.pprint(jit_debug_key.values)))
print()
registered_owner = windows_info.get("RegisteredOwner")
print("registered owner = <{0}>".format(registered_owner))
+4 -4
View File
@@ -40,7 +40,7 @@ print("Reading allocated memory : <{0}>".format(repr(calc.read_memory(addr, 20))
# Remote Execution
print("Execution some native code in our calc (write 0x424242 at allocated address + return 0x1337")
print("Execution some native code in our calc (write 0x424242 at allocated address + return 0x1337)")
if calc.bitness == 32:
# Let's generate some native code
@@ -54,7 +54,7 @@ else:
code += x64.Mov(x64.mem("[RAX]"), 0x42424242)
code += x64.Mov("RAX", 0x1337)
code += x64.Ret()
print("Executing native code !")
t = calc.execute(code.get_code())
t.wait()
@@ -80,10 +80,10 @@ try:
except windows.injection.RemotePythonError as e:
print("Remote ERROR !")
print(e)
print("That's all ! killing the calc")
calc.exit()
+15 -1
View File
@@ -490,4 +490,18 @@ DEBUG_EVENT_CHANGE_DEBUGGEE_STATE = Flag("DEBUG_EVENT_CHANGE_DEBUGGEE_STATE", 0x
DEBUG_EVENT_CHANGE_ENGINE_STATE = Flag("DEBUG_EVENT_CHANGE_ENGINE_STATE", 0x00000800)
DEBUG_EVENT_CHANGE_SYMBOL_STATE = Flag("DEBUG_EVENT_CHANGE_SYMBOL_STATE", 0x00001000)
WOW64_MAXIMUM_SUPPORTED_EXTENSION = Flag("WOW64_MAXIMUM_SUPPORTED_EXTENSION", 512)
WOW64_SIZE_OF_80387_REGISTERS = Flag("WOW64_SIZE_OF_80387_REGISTERS", 80)
WOW64_SIZE_OF_80387_REGISTERS = Flag("WOW64_SIZE_OF_80387_REGISTERS", 80)
REG_NONE = Flag("REG_NONE", ( 0 ))
REG_SZ = Flag("REG_SZ", ( 1 ))
REG_EXPAND_SZ = Flag("REG_EXPAND_SZ", ( 2 ))
REG_BINARY = Flag("REG_BINARY", ( 3 ))
REG_DWORD = Flag("REG_DWORD", ( 4 ))
REG_DWORD_LITTLE_ENDIAN = Flag("REG_DWORD_LITTLE_ENDIAN", ( 4 ))
REG_DWORD_BIG_ENDIAN = Flag("REG_DWORD_BIG_ENDIAN", ( 5 ))
REG_LINK = Flag("REG_LINK", ( 6 ))
REG_MULTI_SZ = Flag("REG_MULTI_SZ", ( 7 ))
REG_RESOURCE_LIST = Flag("REG_RESOURCE_LIST", ( 8 ))
REG_FULL_RESOURCE_DESCRIPTOR = Flag("REG_FULL_RESOURCE_DESCRIPTOR", ( 9 ))
REG_RESOURCE_REQUIREMENTS_LIST = Flag("REG_RESOURCE_REQUIREMENTS_LIST", ( 10 ))
REG_QWORD = Flag("REG_QWORD", ( 11 ))
REG_QWORD_LITTLE_ENDIAN = Flag("REG_QWORD_LITTLE_ENDIAN", ( 11 ))
+6 -6
View File
@@ -8,7 +8,7 @@ from windows.generated_def.winstructs import *
def _bitness():
"""Return 32 or 64"""
"""Returns 32 or 64"""
import platform
bits = platform.architecture()[0]
return int(bits[:2])
@@ -82,7 +82,7 @@ do_cpuid64 = native_function.create_function(cpuid64_code.get_code(), [DWORD, DW
def x86_cpuid(req):
"""Perform a CPUID in 32bits mode
"""Performs a CPUID in 32bits mode
:rtype: :class:`X86CpuidResult`
"""
@@ -92,7 +92,7 @@ def x86_cpuid(req):
def x64_cpuid(req):
"""Perform a CPUID in 64bits mode
"""Performs a CPUID in 64bits mode
:rtype: :class:`X86CpuidResult`
"""
@@ -108,7 +108,7 @@ else:
_do_cpuid = x64_cpuid
def do_cpuid(req):
"""Perform a CPUID for the current process bitness
"""Performs a CPUID for the current process bitness
:rtype: :class:`X86CpuidResult`
"""
@@ -116,7 +116,7 @@ def do_cpuid(req):
def get_vendor_id():
"""Extract the VendorId string from CPUID
"""Extracts the VendorId string from CPUID
:rtype: :class:`str`
"""
@@ -136,7 +136,7 @@ def is_amd_proc():
def get_proc_family_model():
"""Extract the family and model based on vendorId
"""Extracts the family and model based on vendorId
:rtype: (ComputedFamily, ComputedModel)
"""
+2 -2
View File
@@ -1059,7 +1059,7 @@ if in_IDA:
def test_code():
s = MultipleInstr()
s += Mov('r8', 'r14')
s += Label(':SUCE')
s += Label(':A_POLITICALLY_CORRECT_LABEL_NAME')
s += Jnz(':END')
s += Add('r14', 0x12345678)
s += Dec('r9')
@@ -1068,7 +1068,7 @@ if in_IDA:
s += Mov('r8', 'rdx')
s += Jnz(':END')
s += Mov('r8', 'rdx')
s += Jnz(':SUCE')
s += Jnz(':A_POLITICALLY_CORRECT_LABEL_NAME')
s += Mov('r9', 'r10')
s += Label(':END')
s += Ret()
+5 -3
View File
@@ -136,7 +136,7 @@ class X86(object):
def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
"""Create an X86 memory access description"""
"""Creates a X86 memory access description"""
if index is not None and scale is None:
scale = 1
if scale and index is None:
@@ -147,12 +147,14 @@ def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
def deref(disp):
"""Create a memory access for an immediate value ``Ex: [0x42424242]``"""
return create_displacement(disp=disp)
def mem(data):
"""Parse a memory access string of format [EXPR] or seg:[EXPR]
EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order)
"""Parse a memory access string of format ``[EXPR]`` or ``seg:[EXPR]``
``EXPR`` may describe: ``BASE | INDEX * SCALE | DISPLACEMENT`` or any combinaison (in this order)
"""
if not isinstance(data, str):
raise TypeError("mem need a string to parse")
+3 -3
View File
@@ -58,7 +58,7 @@ class TCP4Connection(MIB_TCPROW_OWNER_PID):
@property
def remote_host(self):
"""Identification of the remote hostname.
Equals ``remote_addr`` if resolution fail
Equals ``remote_addr`` if the resolution fails
:type: :class:`str` or :class:`int`
"""
@@ -124,12 +124,12 @@ class TCP6Connection(MIB_TCP6ROW_OWNER_PID):
@property
def remote_proto(self):
"""Equals to self.remote_port for Ipv6"""
"""Equals to ``self.remote_port`` for Ipv6"""
return self.remote_port
@property
def remote_host(self):
"""Equals to self.remote_addr for Ipv6"""
"""Equals to ``self.remote_addr`` for Ipv6"""
return self.remote_addr
def close(self):
+12 -4
View File
@@ -48,10 +48,13 @@ def get_structure_transformer_for_target(target):
def GetPEFile(baseaddr, target=None):
"""Return a :class:`PEFile` to explore a PE loaded at `baseaddr` in process `target`.
If target is ``None`` it refers the curent process
"""Returns a :class:`PEFile` to explore a PE loaded at `baseaddr` in process `target`.
:rtype: :class:`PEFile`
.. note::
If target is ``None`` it refers to the curent process
"""
proc_bitness = windows.current_process.bitness
if target is None:
@@ -106,7 +109,7 @@ def GetPEFile(baseaddr, target=None):
class IATEntry(ctypes.Structure):
"""Represent an entry in the IAT of a module
| Can be used to get resolved value and setup hook
Can be used to get resolved value and setup hook
"""
_fields_ = [
("value", PVOID)]
@@ -136,8 +139,13 @@ def GetPEFile(baseaddr, target=None):
see :ref:`hook_protocol`
:rtype: :class:`windows.hooks.IATHook`
"""
.. warning::
This works only for PEFile with the current process as target.
"""
if target is not None:
raise NotImplementedError("Setting hook in remote process (use python code injection)")
hook = hooks.IATHook(self, callback, types)
self.hook = hook
+28 -7
View File
@@ -1,7 +1,10 @@
import _winreg
import windows
import itertools
import collections
from collections import namedtuple
import windows
from windows.generated_def.windef import KEY_READ
class ExpectWindowsError(object):
@@ -14,11 +17,14 @@ class ExpectWindowsError(object):
def __exit__(self, etype, e, tb):
return (etype == WindowsError and e.winerror == self.errornumber)
KeyValue = collections.namedtuple("KeyValue", ["name", "value", "type"])
KeyValue = namedtuple("KeyValue", ["name", "value", "type"])
"""A registry value (name, value, type)"""
class PyHKey(object):
"""A windows registry key"""
def __init__(self, surkey, name, sam=_winreg.KEY_READ):
def __init__(self, surkey, name, sam=KEY_READ):
self.surkey = surkey
self.name = name
self.fullname = self.surkey.fullname + "\\" + self.name if self.name else self.surkey.name
@@ -40,7 +46,9 @@ class PyHKey(object):
@property
def subkeys(self):
"""The subkeys of the registry key"""
"""The subkeys of the registry key
:type: [:class:`PyHKey`] - A list of keys"""
res = []
with ExpectWindowsError(259):
for i in itertools.count():
@@ -49,14 +57,28 @@ class PyHKey(object):
@property
def values(self):
"""The values of the registry key"""
"""The values of the registry key
:type: [:class:`KeyValue`] - A list of values"""
res = []
with ExpectWindowsError(259):
for i in itertools.count():
res.append(_winreg.EnumValue(self.phkey, i))
return [KeyValue(*r) for r in res]
def get(self, value_name):
"""Retrieves the value ``value_name``
:rtype: :class:`KeyValue`
"""
data = _winreg.QueryValueEx(self.phkey, value_name)
return KeyValue(value_name, data[0], data[1])
def open_subkey(self, name):
"""Open the subkey ``name``
:rtype: :class:`PyHKey`
"""
return PyHKey(self, name, self.sam)
def reopen(self, new_sam):
@@ -108,4 +130,3 @@ class Registry(object):
if base_name not in self.registry_base_keys:
raise ValueError("Unknow registry base key <{0}>".format(base_name))
return self.registry_base_keys[base_name][subkey]
-1
View File
@@ -202,7 +202,6 @@ get_syswow_ntdll_exports.value = None
class Syswow64ApiProxy(object):
APIDLL = None
"""Create a python wrapper around a function"""
def __init__(self, winproxy_function):
self.winproxy_function = winproxy_function
+48 -34
View File
@@ -119,7 +119,9 @@ class WinThread(THREADENTRY32, AutoHandle):
"""Represent a thread """
@utils.fixedpropety
def tid(self):
"""Thread ID"""
"""Thread ID
:type: :class:`int`"""
return self.th32ThreadID
@utils.fixedpropety
@@ -166,7 +168,7 @@ class WinThread(THREADENTRY32, AutoHandle):
@property
def start_address(self):
"""The start address of the thread
:type: :class:`int`
"""
if windows.current_process.bitness == 32 and self.owner.bitness == 64:
@@ -199,7 +201,7 @@ class WinThread(THREADENTRY32, AutoHandle):
@property
def is_exit(self):
"""Is ``True`` if the thread is terminated
:type: :class:`bool`
"""
return self.exit_code != STILL_ACTIVE
@@ -207,7 +209,7 @@ class WinThread(THREADENTRY32, AutoHandle):
@property
def exit_code(self):
"""The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead
:type: :class:`int`
"""
res = DWORD()
@@ -246,7 +248,7 @@ class DeadThread(AutoHandle):
@property
def is_exit(self):
"""Is ``True`` if the thread is terminated
:type: :class:`bool`
"""
return self.exit_code != STILL_ACTIVE
@@ -254,7 +256,7 @@ class DeadThread(AutoHandle):
@property
def exit_code(self):
"""The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead
:type: :class:`int`
"""
res = DWORD()
@@ -265,7 +267,7 @@ class DeadThread(AutoHandle):
class Process(AutoHandle):
@utils.fixedpropety
def is_wow_64(self):
"""Is ``True`` if the process is a SysWow64 process (32bit process on 64bits system).
"""``True`` if the process is a SysWow64 process (32bit process on 64bits system).
:type: :class:`bool`
"""
@@ -296,7 +298,7 @@ class Process(AutoHandle):
@property
def exit_code(self):
"""The exit code of the process : ``STILL_ACTIVE`` means the process is not dead
:type: :class:`int`
"""
res = DWORD()
@@ -305,21 +307,24 @@ class Process(AutoHandle):
@property
def is_exit(self):
"""Is ``True`` if the process is terminated
"""``True`` if the process is terminated
:type: :class:`bool`
"""
return self.exit_code == STILL_ACTIVE
def execute(self, code):
"""Execute some raw code in the context of the process"""
"""Execute some native code in the context of the process
:return: The return value of the native code
:rtype: :class:`int`"""
x = self.virtual_alloc(len(code))
self.write_memory(x, code)
return self.create_thread(x, 0)
def query_memory(self, addr):
"""Query the memory informations about page at ```addr``
"""Query the memory informations about page at ``addr``
:rtype: :class:`MEMORY_BASIC_INFORMATION`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
@@ -340,7 +345,7 @@ class Process(AutoHandle):
def memory_state(self):
"""Yield the memory information for the whole address space of the process
:yield: :class:`MEMORY_BASIC_INFORMATION`
"""
addr = 0
@@ -358,7 +363,9 @@ class CurrentThread(AutoHandle):
"""The current thread"""
@utils.fixedpropety
def tid(self):
"""Thread ID"""
"""Thread ID
:type: :class:`int`"""
return winproxy.GetCurrentThreadId()
@utils.fixedpropety
@@ -380,7 +387,7 @@ class CurrentThread(AutoHandle):
return winproxy.ExitThread(code)
def wait(self):
"""Raise ValueError to prevent deadlock :D"""
"""Raise ``ValueError`` to prevent deadlock :D"""
raise ValueError("wait() on current thread")
@@ -451,9 +458,10 @@ class CurrentProcess(Process):
return int(bits[:2])
def virtual_alloc(self, size):
"""Allocate memory in the current process
"""Allocate memory in the process
:returns: :class:`int`
:return: The address of the allocated memory
:rtype: :class:`int`
"""
return winproxy.VirtualAlloc(dwSize=size)
@@ -464,7 +472,11 @@ class CurrentProcess(Process):
return True
def read_memory(self, addr, size):
"""Read size from addr"""
"""Read ``size`` from ``addr``
:return: The data read
:rtype: :class:`str`
"""
dbgprint('Read CurrentProcess Memory', 'READMEM')
buffer = (c_char * size).from_address(addr)
return buffer[:]
@@ -472,10 +484,7 @@ class CurrentProcess(Process):
def create_thread(self, lpStartAddress, lpParameter, dwCreationFlags=0):
"""Create a new thread
.. note::
CreateThread https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx
:rtype: :class:`WinThread` or :class:`DeadThread`
:rtype: :class:`WinThread` or :class:`DeadThread`
"""
handle = winproxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags)
return WinThread._from_handle(handle)
@@ -485,7 +494,7 @@ class CurrentProcess(Process):
return winproxy.ExitProcess(code)
def wait(self):
"""Raise ValueError to prevent deadlock :D"""
"""Raise ``ValueError`` to prevent deadlock :D"""
raise ValueError("wait() on current thread")
@@ -527,7 +536,8 @@ class WinProcess(PROCESSENTRY32, Process):
def virtual_alloc(self, size):
"""Allocate memory in the process
:rtype: :class:`int`
:return: The address of the allocated memory
:rtype: :class:`int`
"""
return winproxy.VirtualAllocEx(self.handle, dwSize=size)
@@ -544,7 +554,11 @@ class WinProcess(PROCESSENTRY32, Process):
return winproxy.ReadProcessMemory(self.handle, addr, lpBuffer=buffer_addr, nSize=size)
def read_memory(self, addr, size):
"""Read `size` from `addr`"""
"""Read ``size`` from ``addr``
:return: The data read
:rtype: :class:`str`
"""
buffer = ctypes.create_string_buffer(size)
self.low_read_memory(addr, ctypes.byref(buffer), size)
return buffer[:]
@@ -569,14 +583,14 @@ class WinProcess(PROCESSENTRY32, Process):
def read_memory_into(self, addr, struct):
"""Read a :mod:`ctypes` struct from `addr`
:returns: struct"""
self.low_read_memory(addr, ctypes.byref(struct), ctypes.sizeof(struct))
return struct
def create_thread(self, addr, param):
"""Create a remote thread
:rtype: :class:`WinThread` or :class:`DeadThread`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
@@ -593,15 +607,15 @@ class WinProcess(PROCESSENTRY32, Process):
return self.create_thread(LoadLibrary, x)
def execute_python(self, pycode):
"""Execute Python code into the remote process.
"""Execute Python code into the remote process.
This function waits for the remote process to end and
raises an exception if the remote thread raised one"""
return injection.safe_execute_python(self, pycode)
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
"""
return injection.execute_python_code(self, pycode)
@@ -609,7 +623,7 @@ class WinProcess(PROCESSENTRY32, Process):
@utils.fixedpropety
def peb_addr(self):
"""The address of the PEB
:type: :class:`int`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
@@ -636,7 +650,7 @@ class WinProcess(PROCESSENTRY32, Process):
@utils.fixedpropety
def peb(self):
"""The PEB of the remote process (see :mod:`remotectypes`)
:type: :class:`PEB`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
@@ -692,7 +706,7 @@ class WinUnicodeString(LSA_UNICODE_STRING):
"""LSA_UNICODE_STRING with a nice `__repr__`"""
fields = [f[0] for f in LSA_UNICODE_STRING._fields_]
"""The fields of the structure"""
def __repr__(self):
return """<{0} "{1}" at {2}>""".format(type(self).__name__, self.Buffer, hex(id(self)))
+1 -1
View File
@@ -573,7 +573,7 @@ def RegGetValueA(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData):
@Advapi32Proxy('RegGetValueW', kernel32_zero_check)
def RegGetValueW(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData):
def RegGetValueW(hkey, lpSubKey=None, lpValue=NeededParameter, dwFlags=0, pdwType=None, pvData=None, pcbData=None):
return RegGetValueW.ctypes_function(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData)