Add doc on PEB + samples in sphinx

This commit is contained in:
hakril
2016-01-05 08:43:40 +01:00
parent 4e9cf7684e
commit c73fc7934a
8 changed files with 317 additions and 28 deletions
+2
View File
@@ -16,6 +16,8 @@ Contents:
winproxy.rst
utils.rst
native_exec.rst
various.rst
sample.rst
Indices and tables
+68
View File
@@ -0,0 +1,68 @@
Processes and Threads
"""""""""""""""""""""
.. module:: windows.winobject
CurrentProcess
''''''''''''''
.. note::
See sample :ref:`sample_current_process`
.. autoclass:: CurrentProcess
:members:
:inherited-members:
CurrentThread
'''''''''''''
.. autoclass:: CurrentThread
:members:
:inherited-members:
WinProcess
''''''''''
.. note::
See sample :ref:`sample_remote_process`
.. autoclass:: WinProcess
:members:
:inherited-members:
WinThread
'''''''''
.. autoclass:: WinThread
:members:
:inherited-members:
.. autoclass:: DeadThread
:members:
:inherited-members:
PEB Exploration
"""""""""""""""
The :mod:`windows` module is able to parse the PEB of the current process or remote process.
The :class:`PEB` is accessible via ``process.peb`` and is of type :class:`PEB`.
.. note::
See sample :ref:`sample_peb_exploration`
.. autoclass:: PEB
:members:
:inherited-members:
.. autoclass:: WinUnicodeString
.. autoclass:: LoadedModule
+8
View File
@@ -0,0 +1,8 @@
Registry
""""""""
.. module:: windows.registry
REGISTRY
.. class:: Registry
+99
View File
@@ -0,0 +1,99 @@
Samples of code
===============
.. _sample_current_process:
``windows.current_process``
"""""""""""""""""""""""""""
.. literalinclude:: ..\..\samples\current_process.py
Output::
(cmd λ) python32.exe current_process.py
current process is <windows.winobject.CurrentProcess object at 0x026CD190>
current process is a <32> bits process
current process is a SysWow64 process ? <True>
current process pid <7432> and ppid <5412>
Here are the current process threads: <[<WinThread 5264 owner "python.exe" at 0x28563f0>]>
Let's execute some native code ! (0x41 + 1)
Waiting for execution to finish !
Native code returned <0x42L>
Allocating memory in current process
Allocated memory is at <0x3f0000>
Writing 'SOME STUFF' in allocation memory
Reading memory : <'SOME STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'>
.. _sample_remote_process:
Remote process : :class:`WinProcess`
""""""""""""""""""""""""""""""""""""
.. literalinclude:: ..\..\samples\remote_calc.py
Output::
(cmd λ) python.exe remote_calc.py
Creating a calc
Looking for calcs in the processes
They are currently <1> calcs running on the system
Let's play with our calc: <<WinProcess "calc.exe" pid 8052 at 0x27bd5d0>>
Our calc pid is 8052
Our calc is a <32> bits process
Our calc is a SysWow64 process ? <True>
Our calc have threads ! <[<WinThread 8552 owner "calc.exe" at 0x27f7f30>, <WinThread 3464 owner "calc.exe" at 0x27f7f80>, <WinThread 3840 owner "calc.exe" at 0x27fa030>]>
Exploring our calc PEB ! <windows.winobject.RemotePEB object at 0x026DDD00>
Command line is <RemoteWinUnicodeString ""C:\windows\system32\calc.exe"" at 0x26ddee0>
Here are 3 loaded modules: [<RemoteLoadedModule "calc.exe" at 0x26dde40>, <RemoteLoadedModule "ntdll.dll" at 0x26ddf30>, <RemoteLoadedModule "kernel32.dll" at 0x26ddc60>]
Allocating memory in our calc
Allocated memory is at <0x5c90000>
Writing 'SOME STUFF' in allocated memory
Reading allocated memory : <'SOME STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'>
Execution some native code in our calc (write 0x424242 at allocated address + return 0x1337
Executing native code !
Return code = 0x1337L
Reading allocated memory : <'BBBB STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'>
Executing python code !
Reading allocated memory : <'HELLO FROM CALC\x00\x00\x00\x00\x00'>
Trying to import in remote module 'FAKE_MODULE'
Remote ERROR !
Traceback (most recent call last):
File "<string>", line 3, in <module>
File "<string>", line 2, in func
ImportError: No module named FAKE_MODULE
That's all ! killing the calc
.. _sample_peb_exploration:
:class:`PEB` exploration
""""""""""""""""""""""""
.. literalinclude:: ..\..\samples\peb.py
Output::
(cmd λ) python.exe peb.py
Exploring the current process PEB
PEB is <<windows.winobject.PEB object at 0x02649B70>>
Commandline object is <WinUnicodeString "python.exe peb.py " at 0x2649c60>
Commandline string is u'python.exe peb.py '
Imagepath <WinUnicodeString "C:\Python27\python.exe" at 0x2649d50>
Printing some modules: <LoadedModule "python.exe" at 0x272a030>
<LoadedModule "ntdll.dll" at 0x272a080>
<LoadedModule "kernel32.dll" at 0x272acb0>
<LoadedModule "kernelbase.dll" at 0x272ad00>
<LoadedModule "python27.dll" at 0x272ad50>
<LoadedModule "msvcr90.dll" at 0x272ada0>
=== K32 ===
Looking for kernel32.dll
Kernel32 module: <LoadedModule "kernel32.dll" at 0x272acb0>
Module name = <kernel32.dll> | Fullname = <C:\Windows\SYSTEM32\KERNEL32.DLL>
Kernel32 is loaded at address 0x774c0000
=== K32 PE ===
PE Representation of k32: <windows.pe_parse.PEFile object at 0x0272D350>
Here are some exports {0: 2001566688L, u'CreateFileA': 2001635616L, 42: 2001647872L, u'VirtualAlloc': 2001570704L}
Import DLL dependancies are (without api-*): [u'ntdll.dll', u'kernelbase.dll']
IAT Entry for ntdll!NtCreateFile = <IATEntry "NtCreateFile" ordinal 253> | addr = 0x77541128L
Sections: [<PESection ".text">, <PESection ".rdata">, <PESection ".data">, <PESection ".rsrc">, <PESection ".reloc">]
+13
View File
@@ -0,0 +1,13 @@
The :mod:`windows` objects
==========================
Through the :ref:`system <object_system>` object many classes representing various `Windows`
part are accessible.
This sections describes them by group of relation.
.. toctree::
:maxdepth: 2
process.rst
registry.rst
+2 -1
View File
@@ -14,7 +14,8 @@ The submodules that you might use by themself are:
* :mod:`windows.winproxy`
* :mod:`windows.utils`
.. _object_system:
The ``system`` object
"""""""""""""""""""""
+40
View File
@@ -0,0 +1,40 @@
import sys
import os.path
sys.path.append(os.path.abspath(__file__ + "\..\.."))
import windows
print("Exploring the current process PEB")
peb = windows.current_process.peb
print("PEB is <{0}>".format(peb))
commandline = peb.commandline
print("Commandline object is {0}".format(commandline))
print("Commandline string is {0}".format(repr(commandline.Buffer)))
imagepath = peb.imagepath
print("Imagepath {0}".format(imagepath))
modules = peb.modules
print("Printing some modules: {0}".format("\n".join(str(m) for m in modules[:6])))
print("=== K32 ===")
print("Looking for kernel32.dll")
k32 = [m for m in modules if m.name == "kernel32.dll"][0]
print("Kernel32 module: {0}".format(k32))
print("Module name = <{0}> | Fullname = <{1}>".format(k32.name, k32.fullname))
print("Kernel32 is loaded at address {0}".format(hex(k32.baseaddr)))
print("=== K32 PE ===")
k32pe = k32.pe
print("PE Representation of k32: {0}".format(k32pe))
exports = k32pe.exports
some_exports = dict((k,v) for k,v in exports.items() if k in [0, 42, "VirtualAlloc", "CreateFileA"])
print("Here are some exports {0}".format(some_exports))
imports = k32pe.imports
print("Import DLL dependancies are (without api-*): {0}".format([x for x in imports.keys() if not x.startswith("api-")]))
NtCreateFile_iat = [x for x in imports["ntdll.dll"] if x.name == "NtCreateFile"][0]
print("IAT Entry for ntdll!NtCreateFile = {0} | addr = {1}".format(NtCreateFile_iat, hex(NtCreateFile_iat.addr)))
print("Sections: {0}".format(k32pe.sections))
+85 -27
View File
@@ -32,15 +32,12 @@ class AutoHandle(object):
@property
def handle(self):
"""A handle on the object
"""An handle on the object
:type: HANDLE
.. note::
The handle is automaticaly closed when the object is destroyed
"""
if hasattr(self, "_handle"):
return self._handle
@@ -48,6 +45,7 @@ class AutoHandle(object):
return self._handle
def wait(self, timeout=INFINITE):
"""Wait for the object"""
return winproxy.WaitForSingleObject(self.handle, timeout)
def __del__(self):
@@ -167,6 +165,10 @@ 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:
res = ULONGLONG()
windows.syswow64.NtQueryInformationThread_32_to_64(self.handle, ThreadQuerySetWin32StartAddress, byref(res), ctypes.sizeof(res))
@@ -180,12 +182,15 @@ class WinThread(THREADENTRY32, AutoHandle):
return res.value
def exit(self, code=0):
"""Exit the thread"""
return winproxy.TerminateThread(self.handle, code)
def resume(self):
"""Resume the thread"""
return winproxy.ResumeThread(self.handle)
def suspend(self):
"""Suspend the thread"""
return winproxy.SuspendThread(self.handle)
def _get_handle(self):
@@ -193,10 +198,18 @@ 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
@property
def exit_code(self):
"""The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead
:type: :class:`int`
"""
res = DWORD()
winproxy.GetExitCodeThread(self.handle, byref(res))
return res.value
@@ -222,7 +235,7 @@ class WinThread(THREADENTRY32, AutoHandle):
return DeadThread(handle, tid)
class DeadThread(AutoHandle):
"""A simple object arround an already dead thread"""
"""An already dead thread"""
def __init__(self, handle, tid=None):
if tid is None:
tid = winproxy.GetThreadId(handle)
@@ -232,10 +245,18 @@ class DeadThread(AutoHandle):
@property
def is_exit(self):
"""Is ``True`` if the thread is terminated
:type: :class:`bool`
"""
return self.exit_code != STILL_ACTIVE
@property
def exit_code(self):
"""The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead
:type: :class:`int`
"""
res = DWORD()
winproxy.GetExitCodeThread(self.handle, byref(res))
return res.value
@@ -244,11 +265,9 @@ class DeadThread(AutoHandle):
class Process(AutoHandle):
@utils.fixedpropety
def is_wow_64(self):
"""Is True if the process is a SysWow64 process
"""Is ``True`` if the process is a SysWow64 process (32bit process on 64bits system).
This means a 32bits process on a 64bits system
:type: bool
:type: :class:`bool`
"""
return utils.is_wow_64(self.handle)
@@ -256,7 +275,7 @@ class Process(AutoHandle):
def bitness(self):
"""The bitness of the process
:returns: int -- 32 or 64"""
:returns: :class:`int` -- 32 or 64"""
if windows.system.bitness == 32:
return 32
if self.is_wow_64:
@@ -268,7 +287,6 @@ class Process(AutoHandle):
"""The threads of the process
:type: [:class:`WinThread`] -- A list of Thread
"""
return [thread for thread in windows.system.threads if thread.th32OwnerProcessID == self.pid]
@@ -277,12 +295,20 @@ 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()
winproxy.GetExitCodeProcess(self.handle, byref(res))
return res.value
@property
def is_exit(self):
"""Is ``True`` if the process is terminated
:type: :class:`bool`
"""
return self.exit_code == STILL_ACTIVE
def execute(self, code):
@@ -292,6 +318,10 @@ class Process(AutoHandle):
return self.create_thread(x, 0)
def query_memory(self, addr):
"""Query the memory informations about page at ```addr``
:rtype: :class:`MEMORY_BASIC_INFORMATION`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
res = MEMORY_BASIC_INFORMATION64()
try:
@@ -309,6 +339,10 @@ class Process(AutoHandle):
return res
def memory_state(self):
"""Yield the memory information for the whole address space of the process
:yield: :class:`MEMORY_BASIC_INFORMATION`
"""
addr = 0
res = []
while True:
@@ -386,7 +420,7 @@ class CurrentProcess(Process):
def pid(self):
"""Process ID
:type: int
:type: :class:`int`
"""
return os.getpid()
@@ -395,7 +429,7 @@ class CurrentProcess(Process):
def ppid(self):
"""Parent Process ID
:type: int
:type: :class:`int`
"""
return [p for p in windows.system.processes if p.pid == self.pid][0].ppid
@@ -411,7 +445,7 @@ class CurrentProcess(Process):
def bitness(self):
"""The bitness of the process
:returns: int -- 32 or 64"""
:type: :class:`int` -- 32 or 64"""
import platform
bits = platform.architecture()[0]
return int(bits[:2])
@@ -419,7 +453,7 @@ class CurrentProcess(Process):
def virtual_alloc(self, size):
"""Allocate memory in the current process
:returns: int
:returns: :class:`int`
"""
return winproxy.VirtualAlloc(dwSize=size)
@@ -440,6 +474,8 @@ class CurrentProcess(Process):
.. note::
CreateThread https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx
:rtype: :class:`WinThread` or :class:`DeadThread`
"""
handle = winproxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags)
return WinThread._from_handle(handle)
@@ -462,7 +498,7 @@ class WinProcess(PROCESSENTRY32, Process):
def name(self):
"""Name of the process
:type: str
:type: :class:`str`
"""
return self.szExeFile[:].decode()
@@ -470,7 +506,7 @@ class WinProcess(PROCESSENTRY32, Process):
def pid(self):
"""Process ID
:type: int
:type: :class:`int`
"""
return self.th32ProcessID
@@ -478,7 +514,7 @@ class WinProcess(PROCESSENTRY32, Process):
def ppid(self):
"""Parent Process ID
:type: int
:type: :class:`int`
"""
return self.th32ParentProcessID
@@ -491,7 +527,7 @@ class WinProcess(PROCESSENTRY32, Process):
def virtual_alloc(self, size):
"""Allocate memory in the process
:returns: int
:rtype: :class:`int`
"""
return winproxy.VirtualAllocEx(self.handle, dwSize=size)
@@ -532,12 +568,17 @@ class WinProcess(PROCESSENTRY32, Process):
# return page_data[addr & 0xfff: (addr & 0xfff) + size]
def read_memory_into(self, addr, struct):
"""Read a :mod:`ctypes` struct from `addr`"""
"""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"""
"""Create a remote thread
:rtype: :class:`WinThread` or :class:`DeadThread`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
thread_handle = HANDLE()
windows.syswow64.NtCreateThreadEx_32_to_64(ThreadHandle=byref(thread_handle) ,ProcessHandle=self.handle, lpStartAddress=addr, lpParameter=param)
@@ -552,15 +593,25 @@ 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"""
"""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)
@utils.fixedpropety
def peb_addr(self):
"""The address of the PEB
:type: :class:`int`
"""
if windows.current_process.bitness == 32 and self.bitness == 64:
x = windows.remotectypes.transform_type_to_remote64bits(PROCESS_BASIC_INFORMATION)
# Fuck-it <3
@@ -584,6 +635,10 @@ 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:
return RemotePEB64(self.peb_addr, self)
if windows.current_process.bitness == 64 and self.bitness == 32:
@@ -599,9 +654,9 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY):
"""An entry in the PEB Ldr list"""
@property
def baseaddr(self):
"""base address of the module
"""Base address of the module
:type: int
:type: :class:`int`
"""
return self.DllBase
@@ -609,7 +664,7 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY):
def name(self):
"""Name of the module
:type: str
:type: :class:`str`
"""
return str(self.BaseDllName.Buffer).lower()
@@ -617,7 +672,7 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY):
def fullname(self):
"""Full name of the module (path)
:type: str
:type: :class:`str`
"""
return self.FullDllName.Buffer.decode()
@@ -635,6 +690,9 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY):
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)))