diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cfcacd5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +# Big Thank You to https://github.com/cclauss/Travis-CI-Python-on-three-OSes/blob/master/.travis.yml +language: python # this works for Linux but is an error on macOS or Windows +matrix: + include: + - name: "Python 3.7.5 on Windows" + os: windows # Windows 10.0.17134 N/A Build 17134 + language: shell # 'language: python' is an error on Travis CI Windows + before_install: + # https://travis-ci.community/t/windows-python-pip-module-not-found/5480/4 + - choco install python --version 3.7.5 + - python -m pip install --upgrade pip + - python -m pip install pytest + env: + - PATH=/c/Python37:/c/Python37/Scripts:$PATH +install: +script: + - pwd + - pytest -v --durations=30 tests + diff --git a/README.md b/README.md index e69de29..1f05c55 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,16 @@ +PyDbg for Python 3 and 64 bits +============================== + +This is a fork of Pedram Amini's [PyDbg](http://pedramamini.com/PaiMei/docs/) ([archived copy at OpenRCE](https://github.com/OpenRCE/pydbg)) which changes to make it run with Python 3 and on 64 machines. + +Some files from [v-p-b/PyDbg](https://github.com/v-p-b/pydbg) are also used. + +This is not entirely finished but most of Python 3 changes are done. +For 64 bits, and the general ideas of the changes are exposed: +- Print pointers with %016x instead of %08x. +- Do not really on default argtypes functions specifications used by ctypes. +- Different registers. + +What works is the hooking of functions, and it is tested in the project [Survol](https://github.com/rchateauneu/survol). + +An excellent introduction to PyDbg can be found in the book [Gray Hat Python](https://www.nostarch.com/ghpython.htm). diff --git a/__init__.py b/__init__.py index 44d909e..83b0802 100644 --- a/__init__.py +++ b/__init__.py @@ -37,14 +37,14 @@ "windows_h", ] -from breakpoint import * -from defines import * -from hardware_breakpoint import * -from memory_breakpoint import * -from memory_snapshot_block import * -from memory_snapshot_context import * -from pdx import * -from pydbg import * -from pydbg_client import * -from system_dll import * -from windows_h import * \ No newline at end of file +from .breakpoint import * +from .defines import * +from .hardware_breakpoint import * +from .memory_breakpoint import * +from .memory_snapshot_block import * +from .memory_snapshot_context import * +from .pdx import * +from .pydbg import * +from .pydbg_client import * +from .system_dll import * +from .windows_h import * \ No newline at end of file diff --git a/defines.py b/defines.py index 2f813c9..badc9a1 100644 --- a/defines.py +++ b/defines.py @@ -33,8 +33,7 @@ @organization: www.openrce.org ''' -from my_ctypes import * -from windows_h import * +from .windows_h import * ### ### manually declare entities from Tlhelp32.h since i was unable to import using h2xml.py. @@ -79,11 +78,11 @@ class MODULEENTRY32(Structure): ("th32ProcessID", DWORD), ("GlblcntUsage", DWORD), ("ProccntUsage", DWORD), - ("modBaseAddr", DWORD), + ("modBaseAddr", POINTER(CHAR)), ("modBaseSize", DWORD), - ("hModule", DWORD), - ("szModule", CHAR * 256), - ("szExePath", CHAR * 260), + ("hModule", HANDLE), + ("szModule", CHAR * 256), # MAX_MODULE_NAME32 + 1 + ("szExePath", CHAR * 260), # MAX_PATH ] class _MIB_TCPROW_OWNER_PID(Structure): diff --git a/pdx.py b/pdx.py index 65e4449..3caca18 100644 --- a/pdx.py +++ b/pdx.py @@ -24,8 +24,7 @@ import os.path -from my_ctypes import * -from defines import * +from .defines import * # macos compatability. try: diff --git a/pydbg.py b/pydbg.py index 22ff0d1..dc46a9f 100644 --- a/pydbg.py +++ b/pydbg.py @@ -24,17 +24,23 @@ @organization: www.openrce.org ''' +from __future__ import print_function + import os.path import sys +import six import copy import signal import struct -import pydasm +try: + import pydasm +except ImportError: + pass import socket +import ctypes +from ctypes import wintypes -from my_ctypes import * -from defines import * -from windows_h import * +from .windows_h import * # macos compatability. try: @@ -46,13 +52,29 @@ kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) advapi32 = kernel32 -from breakpoint import * -from hardware_breakpoint import * -from memory_breakpoint import * -from memory_snapshot_block import * -from memory_snapshot_context import * -from pdx import * -from system_dll import * +from .breakpoint import * +from .hardware_breakpoint import * +from .memory_breakpoint import * +from .memory_snapshot_block import * +from .memory_snapshot_context import * +from .pdx import * +from .system_dll import * + +if sys.version_info < (3,): + big_integer_type = long +else: + big_integer_type = int + +def process_is_wow64(pid=None): + if pid: + process_handle = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid) + else: + process_handle = GetCurrentProcess() + + temp_is_wow64 = wintypes.BOOL() + a_bool = IsWow64Process(process_handle, byref(temp_is_wow64)) + + return True if temp_is_wow64 else False class pydbg: ''' @@ -142,8 +164,9 @@ def __init__ (self, ff=True, cs=False): self.op3 = None # pydasm decoded 3rd operand, propagated by self.disasm() # control debug/error logging. - self._log = lambda msg: None #sys.stderr.write("PDBG_LOG> " + msg + "\n") - self._err = lambda msg: sys.stderr.write("PDBG_ERR> " + msg + "\n") + # self._log = lambda msg: None #sys.stderr.write("PDBG_LOG> " + msg + "\n") + self._log = lambda msg: sys.stdout.write("PDBG_LOG> " + msg + "\n") + self._err = lambda msg: sys.stdout.write("PDBG_ERR> " + msg + "\n") # determine the system page size. system_info = SYSTEM_INFO() @@ -152,7 +175,12 @@ def __init__ (self, ff=True, cs=False): # determine the system DbgBreakPoint address. this is the address at which initial and forced breaks happen. # XXX - need to look into fixing this for pydbg client/server. - self.system_break = self.func_resolve("ntdll.dll", "DbgBreakPoint") + try: + # On Travis, this does not work. + self.system_break = self.func_resolve(b"ntdll.dll", b"DbgBreakPoint") + except Exception as exc: + self.system_break = None + sys.stderr.write("Cannot get DbgBreakPoint address. Continue") self._log("system page size is %d" % self.page_size) @@ -231,6 +259,11 @@ def attach (self, pid): except: pass + if not is_64bits: + self.attach32_end() + return self.ret_self() + + def attach32_end(self): # enumerate the TEBs and add them to the internal dictionary. for thread_id in self.enumerate_threads(): thread_handle = self.open_thread(thread_id) @@ -253,7 +286,6 @@ def attach (self, pid): self.peb = self.read_process_memory(teb + 0x30, 4) self.peb = struct.unpack(" 0: + raise RuntimeError("Couldn't get process token") + self._log("my_get_process_token res=%d token=%d" % (res, token)) + return token + def get_debug_privileges (self): ''' Obtain necessary privileges for debugging. @@ -1936,10 +2032,11 @@ def get_debug_privileges (self): self._log("get_debug_privileges()") + if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, byref(h_token)): raise pdx("OpenProcessToken()", True) - if not advapi32.LookupPrivilegeValueA(0, "seDebugPrivilege", byref(luid)): + if not advapi32.LookupPrivilegeValueA(0, b"seDebugPrivilege", byref(luid)): raise pdx("LookupPrivilegeValue()", True) token_state.PrivilegeCount = 1 @@ -2058,6 +2155,48 @@ def get_system_dll (self, idx): #################################################################################################################### def get_thread_context (self, thread_handle=None, thread_id=0): + # This depends on the target process. At the moment, + # only 32 bits or 64 bits. + # https://stackoverflow.com/questions/17504174/win-64bit-getthreadcontext-returns-zeroed-out-registers-or-0x57-errorcode + if is_64bits: + return self.get_thread_context64(thread_handle, thread_id) + else: + return self.get_thread_context32(thread_handle, thread_id) + + #################################################################################################################### + def get_thread_context64 (self, thread_handle=None, thread_id=0): + #self._log("get_thread_context64 thread_id=%d" % thread_id) + + context = CONTEXT64() + + context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS + + # if a thread handle was not specified, get one from the thread id. + if not thread_handle: + h_thread = self.open_thread(thread_id) + else: + h_thread = thread_handle + + if not kernel32.GetThreadContext(h_thread, byref(context)): + self._log("get_thread_context64 error GetThreadContext") + raise pdx("GetThreadContext() thread_id=%s" % (str(thread_id)), True) + + # if we had to resolve the thread handle, close it. + if not thread_handle: + self.close_handle(h_thread) + + if False: + for register_name, register_type in context._fields_: + try: + self._log("Register:%s Value= %08x" % (register_name, getattr(context, register_name))) + except TypeError: + self._log("Register:%s Not a number" % (register_name)) + + #self._log("get_thread_context64 leaving") + return context + + #################################################################################################################### + def get_thread_context32 (self, thread_handle=None, thread_id=0): ''' Convenience wrapper around GetThreadContext(). Can obtain a thread context via a handle or thread id. @@ -2369,7 +2508,7 @@ def flip_endian (self, dword): #################################################################################################################### - def flip_endian_dword (self, bytes): + def flip_endian_dword (self, array_bytes): ''' Utility function to flip the endianess of a given set of raw bytes into a DWORD. @@ -2379,9 +2518,13 @@ def flip_endian_dword (self, bytes): @rtype: DWORD @return: Converted DWORD. ''' - - return struct.unpack("= (3,): + from .code_coverage import * + from .crash_binning import * + from .hooking import * + from .injection import * + from .udraw_connector import * +else: + from code_coverage import * + from crash_binning import * + from hooking import * + from injection import * + from udraw_connector import * diff --git a/utils/code_coverage.py b/utils/code_coverage.py new file mode 100644 index 0000000..01ea398 --- /dev/null +++ b/utils/code_coverage.py @@ -0,0 +1,403 @@ +# +# Code Coverage +# Copyright (C) 2006 Pedram Amini +# +# $Id: code_coverage.py 193 2007-04-05 13:30:01Z cameron $ +# +# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +''' +@author: Pedram Amini +@license: GNU General Public License 2.0 or later +@contact: pedram.amini@gmail.com +@organization: www.openrce.org +''' + +# we don't want to make mysql a mandatory module for the utils library. +try: import MySQLdb +except: pass + +import time +import zlib +try: + import cPickle +except ImportError: + import pickle + +class __code_coverage_struct__: + eip = 0x00000000 + tid = 0 + num = 0 + timestamp = 0 + module = "" + base = 0 + is_function = 0 + + # registers and stack values. + eax = ebx = ecx = edx = edi = esi = ebp = esp = esp_4 = esp_8 = esp_c = esp_10 = 0 + + # register dereferences. + eax_deref = ebx_deref = ecx_deref = edx_deref = edi_deref = esi_deref = ebp_deref = "" + + # stack dereferences. + esp_deref = esp_4_deref = esp_8_deref = esp_c_deref = esp_10_deref = "" + + +class code_coverage: + ''' + The purpose of this class is to provide an easy interface to keeping track of code coverage data. The Process + Stalker utility for example relies on this class. + + @note: Contains hit list in self.hits. + ''' + + hits = {} + num = 1 + heavy = None + mysql = None + main_module = "[MAIN]" + + #################################################################################################################### + def __init__ (self, mysql=None, heavy=False): + ''' + @type heavy: Boolean + @param heavy: (Optional, Def=False) Flag controlling whether or not to save context information at each point. + ''' + + self.hits = {} + self.num = 1 + self.heavy = heavy + self.mysql = mysql + self.main_module = "[MAIN]" + + + #################################################################################################################### + def add (self, pydbg, is_function): + ''' + Add the current context to the tracked code coverage. + + @type pydbg: PyDbg + @param pydbg: Debugger instance + @type is_function: Integer (bool 0/1) + @param is_function: Flag whether or not the current hit occurred at the start of a function. + + @rtype: code_coverage + @return: self + ''' + + ccs = __code_coverage_struct__() + + # assume we hit inside the main module, unless we can find the specific module we hit in. + module = self.main_module + base = 0 + + # determine the module this hit occured in. + mod32 = pydbg.addr_to_module(pydbg.context.Eip) + + if mod32: + module = mod32.szModule.lower() + base = mod32.modBaseAddr + + ccs.eip = pydbg.context.Eip + ccs.tid = pydbg.dbg.dwThreadId + ccs.num = self.num + ccs.timestamp = int(time.time()) + ccs.module = module + ccs.base = base + ccs.is_function = is_function + + context_list = pydbg.dump_context_list(stack_depth=4, print_dots=True) + + if self.heavy: + ccs.eax = pydbg.context.Eax + ccs.ebx = pydbg.context.Ebx + ccs.ecx = pydbg.context.Ecx + ccs.edx = pydbg.context.Edx + ccs.edi = pydbg.context.Edi + ccs.esi = pydbg.context.Esi + ccs.ebp = pydbg.context.Ebp + ccs.esp = pydbg.context.Esp + ccs.esp_4 = context_list["esp+04"]["value"] + ccs.esp_8 = context_list["esp+08"]["value"] + ccs.esp_C = context_list["esp+0c"]["value"] + ccs.esp_10 = context_list["esp+10"]["value"] + + ccs.eax_deref = context_list["eax"] + ccs.ebx_deref = context_list["ebx"] + ccs.ecx_deref = context_list["ecx"] + ccs.edx_deref = context_list["edx"] + ccs.edi_deref = context_list["edi"] + ccs.esi_deref = context_list["esi"] + ccs.ebp_deref = context_list["ebp"] + ccs.esp_deref = context_list["esp"] + ccs.esp_4_deref = context_list["esp+04"]["desc"] + ccs.esp_8_deref = context_list["esp+08"]["desc"] + ccs.esp_c_deref = context_list["esp+0c"]["desc"] + ccs.esp_10_deref = context_list["esp+10"]["desc"] + + if not self.hits.has_key(ccs.eip): + self.hits[ccs.eip] = [] + + self.hits[ccs.eip].append(ccs) + self.num += 1 + + return self + + + #################################################################################################################### + def clear_mysql (self, target_id, tag_id): + ''' + Removes all code coverage hits from target/tag id combination. Expects connection to database to already exist + via self.mysql. + + @see: connect_mysql(), import_mysql(), export_mysql() + + @type target_id: Integer + @param target_id: Name of target currently monitoring code coverage of + @type tag_id: Integer + @param tag_id: Name of this code coverage run + + @rtype: code_coverage + @return: self + ''' + + cursor = self.mysql.cursor() + + try: + cursor.execute("DELETE FROM cc_hits WHERE target_id = '%d' AND tag_id = '%d'" % (target_id, tag_id)) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) + print(sql) + print("") + + cursor.close() + return self + + + #################################################################################################################### + def connect_mysql (self, host, user, passwd): + ''' + Establish a connection to a MySQL server. This must be called prior to export_mysql() or import_mysql(). + Alternatively, you can connect manually and set the self.mysql member variable. + + @see: export_mysql(), import_mysql() + + @type host: String + @param host: MySQL hostname or ip address + @type user: String + @param user: MySQL username + @type passwd: String + @param passwd: MySQL password + + @rtype: code_coverage + @return: self + ''' + + self.mysql = MySQLdb.connect(host=host, user=user, passwd=passwd, db="paimei") + + return self + + + #################################################################################################################### + def export_file (self, file_name): + ''' + Dump the entire object structure to disk. + + @see: import_file() + + @type file_name: String + @param file_name: File name to export to + + @rtype: code_coverage + @return: self + ''' + + fh = open(file_name, "wb+") + fh.write(zlib.compress(cPickle.dumps(self, protocol=2))) + fh.close() + + return self + + + #################################################################################################################### + def export_mysql (self, target_id, tag_id): + ''' + Export code coverage data to MySQL. Expects connection to database to already exist via self.mysql. + + @see: clear_mysql(), connect_mysql(), import_mysql() + + @type target_id: Integer + @param target_id: Name of target currently monitoring code coverage of + @type tag_id: Integer + @param tag_id: Name of this code coverage run + + @rtype: code_coverage + @return: self + ''' + + cursor = self.mysql.cursor() + + for hits in self.hits.values(): + for ccs in hits: + sql = "INSERT INTO cc_hits" + sql += " SET target_id = '%d'," % target_id + sql += " tag_id = '%d'," % tag_id + sql += " num = '%d'," % ccs.num + sql += " timestamp = '%d'," % ccs.timestamp + sql += " eip = '%d'," % ccs.eip + sql += " tid = '%d'," % ccs.tid + sql += " eax = '%d'," % ccs.eax + sql += " ebx = '%d'," % ccs.ebx + sql += " ecx = '%d'," % ccs.ecx + sql += " edx = '%d'," % ccs.edx + sql += " edi = '%d'," % ccs.edi + sql += " esi = '%d'," % ccs.esi + sql += " ebp = '%d'," % ccs.ebp + sql += " esp = '%d'," % ccs.esp + sql += " esp_4 = '%d'," % ccs.esp_4 + sql += " esp_8 = '%d'," % ccs.esp_8 + sql += " esp_c = '%d'," % ccs.esp_c + sql += " esp_10 = '%d'," % ccs.esp_10 + sql += " eax_deref = '%s'," % ccs.eax_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " ebx_deref = '%s'," % ccs.ebx_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " ecx_deref = '%s'," % ccs.ecx_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " edx_deref = '%s'," % ccs.edx_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " edi_deref = '%s'," % ccs.edi_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esi_deref = '%s'," % ccs.esi_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " ebp_deref = '%s'," % ccs.ebp_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esp_deref = '%s'," % ccs.esp_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esp_4_deref = '%s'," % ccs.esp_4_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esp_8_deref = '%s'," % ccs.esp_8_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esp_c_deref = '%s'," % ccs.esp_c_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " esp_10_deref = '%s'," % ccs.esp_10_deref.replace("\\", "\\\\").replace("'", "\\'") + sql += " is_function = '%d'," % ccs.is_function + sql += " module = '%s'," % ccs.module + sql += " base = '%d' " % ccs.base + + try: + cursor.execute(sql) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) + print(sql) + print("") + + cursor.close() + return self + + + #################################################################################################################### + def import_file (self, file_name): + ''' + Load the entire object structure from disk. + + @see: export_file() + + @type file_name: String + @param file_name: File name to import from + + @rtype: code_coverage + @return: self + ''' + + fh = open(file_name, "rb") + tmp = cPickle.loads(zlib.decompress(fh.read())) + fh.close() + + self.hits = tmp.hits + self.num = tmp.num + self.heavy = tmp.heavy + self.mysql = tmp.mysql + self.main_module = tmp.main_module + + return self + + + #################################################################################################################### + def import_mysql (self, target_id, tag_id): + ''' + Import code coverage from MySQL. Expects connection to database to already exist via self.mysql. + + @see: clear_mysql(), connect_mysql(), export_mysql() + + @type target_id: Integer + @param target_id: Name of target currently monitoring code coverage of + @type tag_id: Integer + @param tag_id: Name of this code coverage run + + @rtype: code_coverage + @return: self + ''' + + self.reset() + + hits = self.mysql.cursor(MySQLdb.cursors.DictCursor) + hits.execute("SELECT * FROM cc_hits WHERE target_id='%d' AND tag_id='%d'" % (target_id, tag_id)) + + for hit in hits.fetchall(): + ccs = __code_coverage_struct__() + + ccs.eip = hit["eip"] + ccs.tid = hit["tid"] + ccs.num = hit["num"] + ccs.timestamp = hit["timestamp"] + ccs.module = hit["module"] + ccs.base = hit["base"] + ccs.is_function = hit["is_function"] + + if self.heavy: + ccs.eax = hit["eax"] + ccs.ebx = hit["ebx"] + ccs.ecx = hit["ecx"] + ccs.edx = hit["edx"] + ccs.edi = hit["edi"] + ccs.esi = hit["esi"] + ccs.ebp = hit["ebp"] + ccs.esp = hit["esp"] + ccs.esp_4 = hit["esp_4"] + ccs.esp_8 = hit["esp_8"] + ccs.esp_C = hit["esp_c"] + ccs.esp_10 = hit["esp_10"] + + ccs.eax_deref = hit["eax_deref"] + ccs.ebx_deref = hit["ebx_deref"] + ccs.ecx_deref = hit["ecx_deref"] + ccs.edx_deref = hit["edx_deref"] + ccs.edi_deref = hit["edi_deref"] + ccs.esi_deref = hit["esi_deref"] + ccs.ebp_deref = hit["ebp_deref"] + ccs.esp_deref = hit["esp_deref"] + ccs.esp_4_deref = hit["esp_4_deref"] + ccs.esp_8_deref = hit["esp_8_deref"] + ccs.esp_C_deref = hit["esp_c_deref"] + ccs.esp_10_deref = hit["esp_10_deref"] + + if not self.hits.has_key(ccs.eip): + self.hits[ccs.eip] = [] + + self.hits[ccs.eip].append(ccs) + self.num += 1 + + hits.close() + return self + + + #################################################################################################################### + def reset (self): + ''' + Reset the internal counter and hit list dictionary. + + @rtype: code_coverage + @return: self + ''' + + self.hits = {} + self.num = 1 diff --git a/utils/crash_binning.py b/utils/crash_binning.py new file mode 100644 index 0000000..7987958 --- /dev/null +++ b/utils/crash_binning.py @@ -0,0 +1,291 @@ +# +# Crash Binning +# Copyright (C) 2006 Pedram Amini +# +# $Id: crash_binning.py 193 2007-04-05 13:30:01Z cameron $ +# +# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +''' +@author: Pedram Amini +@license: GNU General Public License 2.0 or later +@contact: pedram.amini@gmail.com +@organization: www.openrce.org +''' + +import sys +import zlib +try: + import cPickle +except ImportError: + import pickle + +class __crash_bin_struct__: + exception_module = None + exception_address = 0 + write_violation = 0 + violation_address = 0 + violation_thread_id = 0 + context = None + context_dump = None + disasm = None + disasm_around = [] + stack_unwind = [] + seh_unwind = [] + extra = None + + +class crash_binning: + ''' + @todo: Add MySQL import/export. + ''' + + bins = {} + last_crash = None + pydbg = None + + #################################################################################################################### + def __init__ (self): + ''' + ''' + + self.bins = {} + self.last_crash = None + self.pydbg = None + + + #################################################################################################################### + def record_crash (self, pydbg, extra=None): + ''' + Given a PyDbg instantiation that at the current time is assumed to have "crashed" (access violation for example) + record various details such as the disassemly around the violating address, the ID of the offending thread, the + call stack and the SEH unwind. Store the recorded data in an internal dictionary, binning them by the exception + address. + + @type pydbg: pydbg + @param pydbg: Instance of pydbg + @type extra: Mixed + @param extra: (Optional, Def=None) Whatever extra data you want to store with this bin + ''' + + self.pydbg = pydbg + crash = __crash_bin_struct__() + + # add module name to the exception address. + exception_module = pydbg.addr_to_module(pydbg.dbg.u.Exception.ExceptionRecord.ExceptionAddress) + + if exception_module: + exception_module = exception_module.szModule + else: + exception_module = "[INVALID]" + + crash.exception_module = exception_module + crash.exception_address = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionAddress + crash.write_violation = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionInformation[0] + crash.violation_address = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionInformation[1] + crash.violation_thread_id = pydbg.dbg.dwThreadId + crash.context = pydbg.context + crash.context_dump = pydbg.dump_context(pydbg.context, print_dots=False) + crash.disasm = pydbg.disasm(crash.exception_address) + crash.disasm_around = pydbg.disasm_around(crash.exception_address, 10) + crash.stack_unwind = pydbg.stack_unwind() + crash.seh_unwind = pydbg.seh_unwind() + crash.extra = extra + + # add module names to the stack unwind. + for i in range(len(crash.stack_unwind)): + addr = crash.stack_unwind[i] + module = pydbg.addr_to_module(addr) + + if module: + module = module.szModule + else: + module = "[INVALID]" + + crash.stack_unwind[i] = "%s:%08x" % (module, addr) + + + # add module names to the SEH unwind. + for i in range(len(crash.seh_unwind)): + (addr, handler) = crash.seh_unwind[i] + + module = pydbg.addr_to_module(handler) + + if module: + module = module.szModule + else: + module = "[INVALID]" + + crash.seh_unwind[i] = (addr, handler, "%s:%08x" % (module, handler)) + + if not self.bins.has_key(crash.exception_address): + self.bins[crash.exception_address] = [] + + self.bins[crash.exception_address].append(crash) + self.last_crash = crash + + + #################################################################################################################### + def crash_synopsis (self, crash=None): + ''' + For the supplied crash, generate and return a report containing the disassemly around the violating address, + the ID of the offending thread, the call stack and the SEH unwind. If not crash is specified, then call through + to last_crash_synopsis() which returns the same information for the last recorded crash. + + @see: crash_synopsis() + + @type crash: __crash_bin_struct__ + @param crash: (Optional, def=None) Crash object to generate report on + + @rtype: String + @return: Crash report + ''' + + if not crash: + return self.last_crash_synopsis() + + if crash.write_violation: + direction = "write to" + else: + direction = "read from" + + synopsis = "%s:%08x %s from thread %d caused access violation\nwhen attempting to %s 0x%08x\n\n" % \ + ( + crash.exception_module, \ + crash.exception_address, \ + crash.disasm, \ + crash.violation_thread_id, \ + direction, \ + crash.violation_address \ + ) + + synopsis += crash.context_dump + + synopsis += "\ndisasm around:\n" + for (ea, inst) in crash.disasm_around: + synopsis += "\t0x%08x %s\n" % (ea, inst) + + if len(crash.stack_unwind): + synopsis += "\nstack unwind:\n" + for entry in crash.stack_unwind: + synopsis += "\t%s\n" % entry + + if len(crash.seh_unwind): + synopsis += "\nSEH unwind:\n" + for (addr, handler, handler_str) in crash.seh_unwind: + synopsis += "\t%08x -> %s\n" % (addr, handler_str) + + return synopsis + "\n" + + + #################################################################################################################### + def export_file (self, file_name): + ''' + Dump the entire object structure to disk. + + @see: import_file() + + @type file_name: String + @param file_name: File name to export to + + @rtype: crash_binning + @return: self + ''' + + # null out what we don't serialize but save copies to restore after dumping to disk. + last_crash = self.last_crash + pydbg = self.pydbg + + self.last_crash = self.pydbg = None + + fh = open(file_name, "wb+") + fh.write(zlib.compress(cPickle.dumps(self, protocol=2))) + fh.close() + + self.last_crash = last_crash + self.pydbg = pydbg + + return self + + + #################################################################################################################### + def import_file (self, file_name): + ''' + Load the entire object structure from disk. + + @see: export_file() + + @type file_name: String + @param file_name: File name to import from + + @rtype: crash_binning + @return: self + ''' + + fh = open(file_name, "rb") + tmp = cPickle.loads(zlib.decompress(fh.read())) + fh.close() + + self.bins = tmp.bins + + return self + + + #################################################################################################################### + def last_crash_synopsis (self): + ''' + For the last recorded crash, generate and return a report containing the disassemly around the violating + address, the ID of the offending thread, the call stack and the SEH unwind. + + @see: crash_synopsis() + + @rtype: String + @return: Crash report + ''' + + if self.last_crash.write_violation: + direction = "write to" + else: + direction = "read from" + + synopsis = "%s:%08x %s from thread %d caused access violation\nwhen attempting to %s 0x%08x\n\n" % \ + ( + self.last_crash.exception_module, \ + self.last_crash.exception_address, \ + self.last_crash.disasm, \ + self.last_crash.violation_thread_id, \ + direction, \ + self.last_crash.violation_address \ + ) + + synopsis += self.last_crash.context_dump + + synopsis += "\ndisasm around:\n" + for (ea, inst) in self.last_crash.disasm_around: + synopsis += "\t0x%08x %s\n" % (ea, inst) + + if len(self.last_crash.stack_unwind): + synopsis += "\nstack unwind:\n" + for entry in self.last_crash.stack_unwind: + synopsis += "\t%s\n" % entry + + if len(self.last_crash.seh_unwind): + synopsis += "\nSEH unwind:\n" + for (addr, handler, handler_str) in self.last_crash.seh_unwind: + try: + disasm = self.pydbg.disasm(handler) + except: + disasm = "[INVALID]" + + synopsis += "\t%08x -> %s %s\n" % (addr, handler_str, disasm) + + return synopsis + "\n" \ No newline at end of file diff --git a/utils/hooking.py b/utils/hooking.py new file mode 100644 index 0000000..239c7fd --- /dev/null +++ b/utils/hooking.py @@ -0,0 +1,292 @@ +# +# API Hooking Abstraction Helper +# Copyright (C) 2006 Pedram Amini +# +# $Id: hooking.py 193 2007-04-05 13:30:01Z cameron $ +# +# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +''' +@author: Pedram Amini +@license: GNU General Public License 2.0 or later +@contact: pedram.amini@gmail.com +@organization: www.openrce.org +''' + +from ..defines import * + +######################################################################################################################## +class hook_container: + ''' + The purpose of this class is to provide an easy interface for hooking the entry and return points of arbitrary + API calls. The hooking of one or both of the points is optional. Example usage:: + + def CreateFileA_on_entry (dbg, args): + pass + + def CreateFileA_on_return (dbg, args, return_value): + pass + + h = hooks(dbg) + h.add(dbg.func_resolve("kernel32", "CreateFileA"), 7, CreateFileA_on_entry, CreateFileA_on_exit) + + This class transparently takes care of various thread-related race conditions. + ''' + + hooks = {} + + #################################################################################################################### + def __init__ (self): + self.hooks = {} + + + #################################################################################################################### + def add (self, pydbg, address, num_args, entry_hook=None, exit_hook=None): + ''' + Add a new hook on the specified API which accepts the specified number of arguments. Optionally specify callback + functions for hooked API entry / exit events. The entry / exit callback prototypes are:: + + entry(dbg, args) + + Where entry receives the active PyDbg instance as well as a list of the arguments passed to the hooked routine:: + + exit (dbg, args, return_value) + + Where exit received the active PyDbg instance, a list of the arguments passed to the hooked routine and the + return value from the hooked routine. + + @type pydbg: PyDbg Instance + @param pydbg: PyDbg Instance + @type address: Long + @param address: Address of function to hook + @type num_args: Integer + @param num_args: (Optional, Def=0) Number of arguments in function to hook + @type entry_hook: Function Pointer + @param entry_hook: (Optional, Def=None) Function to call on hooked API entry + @type exit_hook: Function Pointer + @param exit_hook: (Optional, Def=None) Function to call on hooked API exit + + @rtype: hooks + @return: Self + ''' + + # ensure a hook doesn't already exist at the requested address. + if address in self.hooks.keys(): + return + + # create a new hook instance and activate it. + h = hook(address, num_args, entry_hook, exit_hook) + h.hook(pydbg) + + # save the newly created hook into the internal dictionary. + self.hooks[address] = h + + return self + + + #################################################################################################################### + def remove (self, pydbg, address): + ''' + De-activate and remove the hook from the specified API address. + + @type pydbg: PyDbg Instance + @param pydbg: PyDbg Instance + @type address: Long + @param address: Address of function to remove hook from + + @rtype: hooks + @return: Self + ''' + + # ensure the address maps to a valid hook point. + if address not in self.hooks.keys(): + return + + # de-activate the hook. + self.hooks[address].unhook(pydbg) + + # remove the hook from the internal dictionary. + del(self.hooks[address]) + + return self + + + #################################################################################################################### + def iterate (self, address): + ''' + A simple iterator function that can be used to iterate through all hooks. Yielded objects are of type hook(). + + @rtype: hook + @return: Iterated hook entries. + ''' + + for hook in self.hooks.values(): + yield hook + + +######################################################################################################################## +class hook: + ''' + This helper class abstracts the activation/deactivation of individual hooks. The class is responsible for + maintaining the various state variables requires to prevent race conditions. + ''' + + hooks = None + address = 0 + num_args = 0 + entry_hook = None + exit_hook = None + arguments = {} + exit_bps = {} + + #################################################################################################################### + def __init__ (self, address, num_args, entry_hook=None, exit_hook=None): + ''' + Initialize the object with the specified parameters. + + @type address: Long + @param address: Address of function to hook + @type num_args: Integer + @param num_args: (Optional, Def=0) Number of arguments in function to hook + @type entry_hook: Function Pointer + @param entry_hook: (Optional, def=None) Function to call on hooked API entry + @type exit_hook: Function Pointer + @param exit_hook: (Optional, def=None) Function to call on hooked API exit + ''' + + self.address = address + self.num_args = num_args + self.entry_hook = entry_hook + self.exit_hook = exit_hook + self.arguments = {} + self.exit_bps = {} + + + #################################################################################################################### + def hook (self, pydbg): + ''' + Activate the hook by setting a breakpoint on the previously specified address. Breakpoint callbacks are proxied + through an internal routine that determines and passes further needed information such as function arguments + and return value. + + @type pydbg: PyDbg Instance + @param pydbg: PyDbg Instance + ''' + + pydbg.bp_set(self.address, restore=True, handler=self.__proxy_on_entry) + + + #################################################################################################################### + def unhook (self, pydbg): + ''' + De-activate the hook by by removing the breakpoint on the previously specified address. + + @type pydbg: PyDbg Instance + @param pydbg: PyDbg Instance + ''' + + pydbg.bp_del(self.address) + + # ensure no breakpoints exist on any registered return addresses. + for address in self.exit_bps.keys(): + pydbg.bp_del(address) + + + #################################################################################################################### + def __proxy_on_entry (self, pydbg): + ''' + The breakpoint handler callback is proxied through this routine for the purpose of passing additional needed + information to the user specified hook_{entry,exit} callback. This routine also allows provides a default + return value of DBG_CONTINUE in the event that the user specified hook callback does not return a value. This + allows for further abstraction between hooking and the debugger. + + @type pydbg: PyDbg + @param pydbg: Debugger instance + + @rtype: DWORD + @return: Debugger continue status + ''' + + continue_status = None + + # retrieve and store the arguments to the hooked function. + # we categorize arguments by thread id to avoid an entry / exit matching race condition, example: + # - thread one enters API, saves arguments + # - thread two enters API, overwrites arguments + # - thread one exists API and uses arguments from thread two + tid = pydbg.dbg.dwThreadId + self.arguments[tid] = [] + + for i in range(1, self.num_args + 1): + self.arguments[tid].append(pydbg.get_arg(i)) + + # if an entry point callback was specified, call it and grab the return value. + if self.entry_hook: + continue_status = self.entry_hook(pydbg, self.arguments[tid]) + + # if an exit hook callback was specified, determine the function exit. + if self.exit_hook: + function_exit = pydbg.get_arg(0) + + # set a breakpoint on the function exit. + pydbg.bp_set(function_exit, restore=True, handler=self.__proxy_on_exit) + + # increment the break count for the exit bp. + # we track the number of breakpoints set on the exit point to avoid a hook exit race condition, ie: + # - thread one enters API sets BP on exit point + # - thread two enters API sets BP on exit point + # - thread one exits API and removes BP from exit point + # - thread two misses exit BP + self.exit_bps[function_exit] = self.exit_bps.get(function_exit, 0) + 1 + + # if a return value was not explicitly specified, default to DBG_CONTINUE. + if continue_status == None: + continue_status = DBG_CONTINUE + + return continue_status + + + #################################################################################################################### + def __proxy_on_exit (self, pydbg): + ''' + The breakpoint handler callback is proxied through this routine for the purpose of passing additional needed + information to the user specified hook_{entry,exit} callback. This routine also allows provides a default + return value of DBG_CONTINUE in the event that the user specified hook callback does not return a value. This + allows for further abstraction between hooking and the debugger. + + @type pydbg: PyDbg + @param pydbg: Debugger instance + + @rtype: DWORD + @return: Debugger continue status + ''' + + # if we are in this function, then an exit point callback was specified, call it and grab the return value. + if pydbg.dbg.dwThreadId not in self.arguments.keys(): + return + + continue_status = self.exit_hook(pydbg, self.arguments[pydbg.dbg.dwThreadId], pydbg.returned_value()) + + eip_register = pydbg.return_address() + + # reduce the break count + self.exit_bps[eip_register] -= 1 + + # if the break count is 0, remove the bp from the exit point. + if self.exit_bps[eip_register] == 0: + pydbg.bp_del(eip_register) + + # if a return value was not explicitly specified, default to DBG_CONTINUE. + if continue_status == None: + continue_status = DBG_CONTINUE + + return continue_status diff --git a/utils/injection.py b/utils/injection.py new file mode 100644 index 0000000..a770077 --- /dev/null +++ b/utils/injection.py @@ -0,0 +1,177 @@ +# +# DLL Injection/Ejection Helper +# Copyright (C) 2007 Justin Seitz +# +# $Id: injection.py 238 2010-04-05 20:40:46Z rgovostes $ +# +# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +''' +@author: Justin Seitz +@license: GNU General Public License 2.0 or later +@contact: jms@bughunter.ca +@organization: www.openrce.org +''' + +import os.path + +from ..pydbg import * +from ..defines import * +from ..my_ctypes import * + +# macos compatability. +try: + kernel32 = windll.kernel32 +except: + kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) + +######################################################################################################################## +class inject: + ''' + This class abstracts the ability to inject and eject a DLL into a remote process. + ''' + + #################################################################################################################### + def __init__ (self): + pass + + #################################################################################################################### + def inject_dll (self, dll_path, pid): + ''' + Inject a DLL of your choice into a running process. + + @type dll_name: String + @param dll_name: The path to the DLL you wish to inject + @type pid: Integer + @param pid: The process ID that you wish to inject into + + @raise pdx: An exception is raised on failure. + ''' + + dll_len = len(dll_path) + + # get a handle to the process we are injecting into. + h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid) + + # now we have to allocate enough bytes for the name and path of our DLL. + arg_address = kernel32.VirtualAllocEx(h_process, 0, dll_len, VIRTUAL_MEM, PAGE_READWRITE) + + # Write the path of the DLL into the previously allocated space. The pointer returned + written = c_int(0) + kernel32.WriteProcessMemory(h_process, arg_address, dll_path, dll_len, byref(written)) + + # resolve the address of LoadLibraryA() + h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll") + h_loadlib = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA") + + # wow we try to create the remote thread. + thread_id = c_ulong(0) + if not kernel32.CreateRemoteThread(h_process, None, 0, h_loadlib, arg_address, 0, byref(thread_id)): + # free the opened handles. + kernel32.CloseHandle(h_process) + kernel32.CloseHandle(h_kernel32) + + raise pdx("CreateRemoteThread failed, unable to inject the DLL %s into PID: %d." % (dll_path, pid), True) + + # free the opened handles. + kernel32.CloseHandle(h_process) + kernel32.CloseHandle(h_kernel32) + + + #################################################################################################################### + def eject_dll (self, dll_name, pid): + ''' + Eject a loaded DLL from a running process. + + @type dll_name: String + @param dll_name: The name of the DLL you wish to eject + @type pid: Integer + @param pid: The process ID that you want to eject a DLL from + + @raise pdx: An exception is raised on failure. + ''' + + # find the DLL and retrieve its information. + ejectee = self.get_module_info(dll_name, pid) + + if ejectee == False: + raise pdx("Couldn't eject DLL %s from PID: %d" % (dll_name, pid)) + + # open the process. + h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid) + + # resolve the address of FreeLibrary() + h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll") + h_freelib = kernel32.GetProcAddress(h_kernel32, "FreeLibrary") + + # now we try to create the remote thread hopefully freeing that DLL, the reason we loop is that + # FreeLibrary() merely decrements the reference count of the DLL we are freeing. Once the ref count + # hits 0 it will unmap the DLL from memory + count = 0 + while count <= ejectee.GlblcntUsage: + thread_id = c_ulong() + if not kernel32.CreateRemoteThread(h_process, None, 0, h_freelib, ejectee.hModule, 0, byref(thread_id)): + # free the opened handles. + kernel32.CloseHandle(h_process) + kernel32.CloseHandle(h_kernel32) + + raise pdx("CreateRemoteThread failed, couldn't run FreeLibrary()", True) + + count += 1 + + # free the opened handles. + kernel32.CloseHandle(h_process) + kernel32.CloseHandle(h_kernel32) + + + ############################################################################## + def get_module_info (self, dll_name, pid): + ''' + Helper function to retrieve the necessary information for the DLL we wish to eject. + + @type dll_name: String + @param dll_name: The name of the DLL you wish to eject + @type pid: Integer + @param pid: The process ID that you want to eject a DLL from + + @raise pdx: An exception is raised on failure. + ''' + + # we create a snapshot of the current process, this let's us dig out all kinds of useful information, including + # DLL info. We are really after the reference count so that we can decrement it enough to get rid of the DLL we + # want unmapped + current_process = MODULEENTRY32() + h_snap = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,pid) + + # check for a failure to create a valid snapshot + if h_snap == INVALID_HANDLE_VALUE: + raise pdx("CreateToolHelp32Snapshot() failed.", True) + + # we have to initiliaze the size of the MODULEENTRY32 struct or this will all fail + current_process.dwSize = sizeof(current_process) + + # check to make sure we have a valid list + if not kernel32.Module32First(h_snap, byref(current_process)): + kernel32.CloseHandle(h_snap) + raise pdx("Couldn't find a valid reference to the module %s" % dll_name, True) + + # keep looking through the loaded modules to try to find the one specified for ejection. + while current_process.szModule.lower() != dll_name.lower(): + if not kernel32.Module32Next(h_snap, byref(current_process)): + kernel32.CloseHandle(h_snap) + raise pdx("Couldn't find the DLL %s" % dll_name, True) + + # close the handle to the snapshot. + kernel32.CloseHandle(h_snap) + + # return the MODULEENTRY32 structure of our DLL. + return current_process diff --git a/utils/udraw_connector.py b/utils/udraw_connector.py new file mode 100644 index 0000000..6f4bbb0 --- /dev/null +++ b/utils/udraw_connector.py @@ -0,0 +1,268 @@ +# +# uDraw Connector +# Copyright (C) 2006 Pedram Amini +# +# $Id: udraw_connector.py 193 2007-04-05 13:30:01Z cameron $ +# +# This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Note: The majority of the uDraw functionality wrapper documentation was ripped directly from: +# +# http://www.informatik.uni-bremen.de/uDrawGraph/en/index.html +# + +''' +@author: Pedram Amini +@license: GNU General Public License 2.0 or later +@contact: pedram.amini@gmail.com +@organization: www.openrce.org +''' + +import socket + +class udraw_connector: + ''' + This class provides an abstracted interface for communicating with uDraw(Graph) when it is configured to listen on a + TCP socket in server mode. + + @todo: Debug various broken routines, abstract more of the uDraw API. + ''' + + command_handlers = {} + sock = None + + #################################################################################################################### + def __init__ (self, host="127.0.0.1", port=2542): + ''' + ''' + + self.command_handlers = {} + self.sock = None + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((host, port)) + + # receive the initial notification message. + self.sock.recv(8) + + self.log = lambda x: None + + + #################################################################################################################### + def change_element_color (self, element, id, color): + ''' + This command is used to update the attributes of nodes and edges that exist in the current graph. + ''' + + command = 'graph(change_attr([' + command += '%s("%08x",[a("COLOR","#%06x")])' % (element, id, color) + command += ']))\n' + self.send(command) + + + #################################################################################################################### + def focus_node (self, node_id, animated=True): + ''' + Scrolls the visible part of the graph visualization to the node specified by "node_id". + + @todo: This routine is buggy. Appears to only want to work when being called after a call to + change_element_color(), though the element color change will not actually work. Need to debug. + ''' + + if animated: + command = 'special(focus_node_animated("%08x"))\n' % node_id + else: + command = 'special(focus_node("%08x"))\n' % node_id + + self.send(command) + + + #################################################################################################################### + def graph_new (self, graph): + ''' + Sends a graph in term representation format to uDraw(Graph) for visualization. + ''' + + command = 'graph(new_placed(' + command += graph.render_graph_udraw() + command += '))\n' + + self.send(command) + + + #################################################################################################################### + def graph_update (self, graph): + ''' + This command can be used to update the structure of the currently loaded graph. + + @todo: This routine is not behaving appropriately, need to debug. + ''' + + command = "graph(mixed_update(" + command += graph.render_graph_udraw_update() + command += "))\n" + + self.send(command) + + + #################################################################################################################### + def layout_improve_all (self): + ''' + This command starts the layout algorithm to improve the visualization quality of the whole graph by reducing + unnecessary edge crossings and edge bends. + ''' + + command = "menu(layout(improve_all))\n" + self.send(command) + + #################################################################################################################### + def message_loop (self, arg1, arg2): + ''' + This routine should be threaded out. This routine will normally be called in the following fashion:: + + thread.start_new_thread(udraw.message_loop, (None, None)) + + The arguments to this routine are not currently used and will be ignored. + ''' + + while 1: + try: + from_server = self.sock.recv(1024) + (command, args) = self.parse(from_server) + + if self.command_handlers.has_key(command): + self.command_handlers[command](self, args) + except: + # connection severed. + break + + + #################################################################################################################### + def open_survey_view (self): + ''' + Open a survey view showing the whole graph in a reduced scale. + ''' + + self.send("menu(view(open_survey_view))\n") + + + #################################################################################################################### + def parse (self, answer): + ''' + ''' + + answer = answer.rstrip("\r\n") + self.log("raw: %s" % answer) + + # extract the answer type. + command = answer.split('(')[0] + args = None + + # if the answer contains a list, extract and convert it into a native Python list. + if answer.count("["): + args = answer[answer.index('[')+1:answer.rindex(']')] + + if len(args): + args = args.replace('"', '') + args = args.split(',') + else: + args = None + + # otherwise, if there are "arguments", grab them as a string. + elif answer.count("("): + args = answer[answer.index('(')+2:answer.index(')')-1] + + self.log("parsed command: %s" % command) + self.log("parsed args: %s" % args) + return (command, args) + + + #################################################################################################################### + def scale (self, parameter): + ''' + Sets the scale to the given parameter which is a percent value that must be from 1 to 100. + ''' + + if parameter in ["full_scale", "full"]: + parameter = "full_scale" + + elif parameter in ["fit_scale_to_window", "fit"]: + parameter = fit_scale_to_window + + elif type(parameter) is int: + parameter = "scale(%d)" % parameter + + else: + return + + self.send("menu(view(%s))\n" % scale) + + + #################################################################################################################### + def send (self, data): + ''' + ''' + + msg = "\n----- sending -------------------------------------------------------\n" + msg += data + "\n" + msg += "---------------------------------------------------------------------\n\n" + + self.log(msg) + self.sock.send(data) + + + #################################################################################################################### + def set_command_handler (self, command, callback_func): + ''' + Set a callback for the specified command. The prototype of the callback routines is:: + + func (udraw_connector, args) + + You can register a callback for any command received from the udraw server. + + @type command: String + @param command: Command string + @type callback_func: Function + @param callback_func: Function to call when specified exception code is caught. + ''' + + self.command_handlers[command] = callback_func + + + #################################################################################################################### + def window_background (self, bg): + ''' + Sets the background of the base window to the color specified by parameter bg. This is a RGB value like + "#0f331e" in the same format as used for command-line option -graphbg. + ''' + + command = 'window(background("%s"))' % bg + self.send(command) + + + #################################################################################################################### + def window_status (self, msg): + ''' + Displays a message in the right footer area of the base window. + ''' + + command = 'window(show_status("%s"))' % msg + self.send(command) + + + #################################################################################################################### + def window_title (self, msg): + ''' + Sets the title of the base window to msg. + ''' + + command = 'window(title("%s"))' % msg + self.send(command) \ No newline at end of file diff --git a/windows_h.py b/windows_h.py index 6cf9086..29b2a41 100644 --- a/windows_h.py +++ b/windows_h.py @@ -8,8 +8,10 @@ # flags 'windows.xml -s DEBUG_EVENT -s CONTEXT -s MEMORY_BASIC_INFORMATION -s LDT_ENTRY -s PROCESS_INFORMATION -s STARTUPINFO -s SYSTEM_INFO -s TOKEN_PRIVILEGES -s LUID -s HANDLE -o windows_h.py' # PEDRAM - line swap ... have to patch in our own __reduce__ definition to each ctype. -#from ctypes import * -from my_ctypes import * +from .my_ctypes import * + +import sys +is_64bits = sys.maxsize > 2**32 # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 4188 class _TOKEN_PRIVILEGES(Structure): @@ -113,8 +115,13 @@ class N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E(Structure): ('Protect', DWORD), ('Type', DWORD), ] -assert sizeof(_MEMORY_BASIC_INFORMATION) == 28, sizeof(_MEMORY_BASIC_INFORMATION) -assert alignment(_MEMORY_BASIC_INFORMATION) == 4, alignment(_MEMORY_BASIC_INFORMATION) +if is_64bits: + assert sizeof(_MEMORY_BASIC_INFORMATION) == 40, sizeof(_MEMORY_BASIC_INFORMATION) + assert alignment(_MEMORY_BASIC_INFORMATION) == 8, alignment(_MEMORY_BASIC_INFORMATION) +else: + assert sizeof(_MEMORY_BASIC_INFORMATION) == 28, sizeof(_MEMORY_BASIC_INFORMATION) + assert alignment(_MEMORY_BASIC_INFORMATION) == 4, alignment(_MEMORY_BASIC_INFORMATION) + # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1539 class _FLOATING_SAVE_AREA(Structure): pass @@ -163,6 +170,166 @@ class _FLOATING_SAVE_AREA(Structure): ] assert sizeof(_CONTEXT) == 716, sizeof(_CONTEXT) assert alignment(_CONTEXT) == 4, alignment(_CONTEXT) + +DWORD64 = c_ulonglong + + +# Define 128-bit 16-byte aligned xmm register type. +class M128A(Structure): + _pack_ = 16 + _fields_ = [ + ("Low", DWORD64), + ("High", DWORD64), + ] + +class DUMMYSTRUCTNAME(Structure): + _fields_ = [ + ("Header", M128A * 2), + ("Legacy", M128A * 8), + ("Xmm0", M128A), + ("Xmm1", M128A), + ("Xmm2", M128A), + ("Xmm3", M128A), + ("Xmm4", M128A), + ("Xmm5", M128A), + ("Xmm6", M128A), + ("Xmm7", M128A), + ("Xmm8", M128A), + ("Xmm9", M128A), + ("Xmm10", M128A), + ("Xmm11", M128A), + ("Xmm12", M128A), + ("Xmm13", M128A), + ("Xmm14", M128A), + ("Xmm15", M128A), + ] + +class _XSAVE_FORMAT32(Structure): + # align 16 + _fields_ = [ + ("ControlWord", WORD), + ("StatusWord",WORD), + ("TagWord",BYTE), + ("Reserved1",BYTE), + ("ErrorOpcode",WORD), + ("ErrorOffset",DWORD), + ("ErrorSelector",WORD), + ("Reserved2",WORD), + ("DataOffset",DWORD), + ("DataSelector",WORD), + ("Reserved3",WORD), + ("MxCsr",DWORD), + ("MxCsr_Mask",DWORD), + ("FloatRegisters",M128A*8), + + ("XmmRegisters",M128A*8), + ("Reserved4",BYTE*192), + ("StackControl", DWORD*7), # KERNEL_STACK_CONTROL structure actualy + ("Cr0NpxState",DWORD), + ] + +class _XSAVE_FORMAT64(Structure): + # align 16 + _fields_ = [ + ("ControlWord", WORD), + ("StatusWord",WORD), + ("TagWord",BYTE), + ("Reserved1",BYTE), + ("ErrorOpcode",WORD), + ("ErrorOffset",DWORD), + ("ErrorSelector",WORD), + ("Reserved2",WORD), + ("DataOffset",DWORD), + ("DataSelector",WORD), + ("Reserved3",WORD), + ("MxCsr",DWORD), + ("MxCsr_Mask",DWORD), + ("FloatRegisters",M128A*8), + + ("XmmRegisters",M128A*16), + ("Reserved4",BYTE*96), + ] + +if is_64bits: + XSAVE_FORMAT = _XSAVE_FORMAT64 +else: + XSAVE_FORMAT = _XSAVE_FORMAT32 +XMM_SAVE_AREA32 = XSAVE_FORMAT + +class DUMMYUNIONNAME(Union): + _fields_ = [ + ("FltSave", XMM_SAVE_AREA32), + ("DummyStructName", DUMMYSTRUCTNAME) + ] + +class _CONTEXT64(Structure): + _pack_ = 16 + _fields_ = [ + ("P1Home", DWORD64), + ("P2Home", DWORD64), + ("P3Home", DWORD64), + ("P4Home", DWORD64), + ("P5Home", DWORD64), + ("P6Home", DWORD64), + + ("ContextFlags", DWORD), + ("MxCsr", DWORD), + + ("SegCs", WORD), + ("SegDs", WORD), + ("SegEs", WORD), + ("SegFs", WORD), + ("SegGs", WORD), + ("SegSs", WORD), + ("EFlags", DWORD), + + ("Dr0", DWORD64), + ("Dr1", DWORD64), + ("Dr2", DWORD64), + ("Dr3", DWORD64), + ("Dr6", DWORD64), + ("Dr7", DWORD64), + + ("Rax", DWORD64), + ("Rcx", DWORD64), + ("Rdx", DWORD64), + ("Rbx", DWORD64), + ("Rsp", DWORD64), + ("Rbp", DWORD64), + ("Rsi", DWORD64), + ("Rdi", DWORD64), + ("R8", DWORD64), + ("R9", DWORD64), + ("R10", DWORD64), + ("R11", DWORD64), + ("R12", DWORD64), + ("R13", DWORD64), + ("R14", DWORD64), + ("R15", DWORD64), + ("Rip", DWORD64), + + ("DebugControl", DWORD64), + ("LastBranchToRip", DWORD64), + ("LastBranchFromRip", DWORD64), + ("LastExceptionToRip", DWORD64), + ("LastExceptionFromRip", DWORD64), + + ("DUMMYUNIONNAME", DUMMYUNIONNAME), + + ("VectorRegister", M128A * 26), + ("VectorControl", DWORD64), + + ("DebugControl", DWORD), + ("LastBranchToRip", DWORD), + ("LastBranchFromRip", DWORD), + ("LastExceptionToRip", DWORD), + ("LastExceptionFromRip", DWORD) +] +####### TODO: BROKEN +###############assert alignment(_CONTEXT64) == 16, alignment(_CONTEXT64) +assert sizeof(_CONTEXT64) == 1256, sizeof(_CONTEXT64) +CONTEXT64 = _CONTEXT64 + # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 498 class N12_SYSTEM_INFO4DOLLAR_37E(Union): pass @@ -199,8 +366,12 @@ class N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E(Structure): ('wProcessorLevel', WORD), ('wProcessorRevision', WORD), ] -assert sizeof(_SYSTEM_INFO) == 36, sizeof(_SYSTEM_INFO) -assert alignment(_SYSTEM_INFO) == 4, alignment(_SYSTEM_INFO) +if is_64bits: + assert sizeof(_SYSTEM_INFO) == 48, sizeof(_SYSTEM_INFO) + assert alignment(_SYSTEM_INFO) == 8, alignment(_SYSTEM_INFO) +else: + assert sizeof(_SYSTEM_INFO) == 36, sizeof(_SYSTEM_INFO) + assert alignment(_SYSTEM_INFO) == 4, alignment(_SYSTEM_INFO) CHAR = c_char LPSTR = POINTER(CHAR) LPBYTE = POINTER(BYTE) @@ -225,8 +396,13 @@ class N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E(Structure): ('hStdOutput', HANDLE), ('hStdError', HANDLE), ] -assert sizeof(_STARTUPINFOA) == 68, sizeof(_STARTUPINFOA) -assert alignment(_STARTUPINFOA) == 4, alignment(_STARTUPINFOA) +if is_64bits: + assert sizeof(_STARTUPINFOA) == 104, sizeof(_STARTUPINFOA) + assert alignment(_STARTUPINFOA) == 8, alignment(_STARTUPINFOA) +else: + assert sizeof(_STARTUPINFOA) == 68, sizeof(_STARTUPINFOA) + assert alignment(_STARTUPINFOA) == 4, alignment(_STARTUPINFOA) + # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 701 class N12_DEBUG_EVENT4DOLLAR_39E(Union): pass @@ -234,27 +410,52 @@ class N12_DEBUG_EVENT4DOLLAR_39E(Union): class _EXCEPTION_DEBUG_INFO(Structure): pass # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3101 -class _EXCEPTION_RECORD(Structure): +class _EXCEPTION_RECORD32(Structure): pass -_EXCEPTION_RECORD._fields_ = [ +_EXCEPTION_RECORD32._fields_ = [ # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3101 ('ExceptionCode', DWORD), ('ExceptionFlags', DWORD), - ('ExceptionRecord', POINTER(_EXCEPTION_RECORD)), + ('ExceptionRecord', POINTER(_EXCEPTION_RECORD32)), ('ExceptionAddress', PVOID), ('NumberParameters', DWORD), ('ExceptionInformation', UINT_PTR * 15), ] -assert sizeof(_EXCEPTION_RECORD) == 80, sizeof(_EXCEPTION_RECORD) -assert alignment(_EXCEPTION_RECORD) == 4, alignment(_EXCEPTION_RECORD) -EXCEPTION_RECORD = _EXCEPTION_RECORD +class _EXCEPTION_RECORD64(Structure): + pass +_EXCEPTION_RECORD64._fields_ = [ + # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3101 + ('ExceptionCode', DWORD), + ('ExceptionFlags', DWORD), + ('ExceptionRecord', POINTER(_EXCEPTION_RECORD64)), # == DWORD64 + ('ExceptionAddress', PVOID), # == DWORD64 + ('NumberParameters', DWORD), + ( 'UnusedAlignment', DWORD), + ('ExceptionInformation', DWORD64 * 15), +] + +# https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record +if is_64bits: + EXCEPTION_RECORD = _EXCEPTION_RECORD64 + assert sizeof(_EXCEPTION_RECORD64) == 152, sizeof(_EXCEPTION_RECORD64) + assert alignment(_EXCEPTION_RECORD64) == 8, alignment(_EXCEPTION_RECORD64) +else: + EXCEPTION_RECORD = _EXCEPTION_RECORD32 + assert sizeof(_EXCEPTION_RECORD32) == 80, sizeof(_EXCEPTION_RECORD32) + assert alignment(_EXCEPTION_RECORD32) == 4, alignment(_EXCEPTION_RECORD32) + + _EXCEPTION_DEBUG_INFO._fields_ = [ # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 640 ('ExceptionRecord', EXCEPTION_RECORD), ('dwFirstChance', DWORD), ] -assert sizeof(_EXCEPTION_DEBUG_INFO) == 84, sizeof(_EXCEPTION_DEBUG_INFO) -assert alignment(_EXCEPTION_DEBUG_INFO) == 4, alignment(_EXCEPTION_DEBUG_INFO) +if is_64bits: + assert sizeof(_EXCEPTION_DEBUG_INFO) == 160, sizeof(_EXCEPTION_DEBUG_INFO) + assert alignment(_EXCEPTION_DEBUG_INFO) == 8, alignment(_EXCEPTION_DEBUG_INFO) +else: + assert sizeof(_EXCEPTION_DEBUG_INFO) == 84, sizeof(_EXCEPTION_DEBUG_INFO) + assert alignment(_EXCEPTION_DEBUG_INFO) == 4, alignment(_EXCEPTION_DEBUG_INFO) EXCEPTION_DEBUG_INFO = _EXCEPTION_DEBUG_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 645 class _CREATE_THREAD_DEBUG_INFO(Structure): @@ -273,8 +474,12 @@ class _CREATE_THREAD_DEBUG_INFO(Structure): ('lpThreadLocalBase', LPVOID), ('lpStartAddress', LPTHREAD_START_ROUTINE), ] -assert sizeof(_CREATE_THREAD_DEBUG_INFO) == 12, sizeof(_CREATE_THREAD_DEBUG_INFO) -assert alignment(_CREATE_THREAD_DEBUG_INFO) == 4, alignment(_CREATE_THREAD_DEBUG_INFO) +if is_64bits: + assert sizeof(_CREATE_THREAD_DEBUG_INFO) == 24, sizeof(_CREATE_THREAD_DEBUG_INFO) + assert alignment(_CREATE_THREAD_DEBUG_INFO) == 8, alignment(_CREATE_THREAD_DEBUG_INFO) +else: + assert sizeof(_CREATE_THREAD_DEBUG_INFO) == 12, sizeof(_CREATE_THREAD_DEBUG_INFO) + assert alignment(_CREATE_THREAD_DEBUG_INFO) == 4, alignment(_CREATE_THREAD_DEBUG_INFO) CREATE_THREAD_DEBUG_INFO = _CREATE_THREAD_DEBUG_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 651 class _CREATE_PROCESS_DEBUG_INFO(Structure): @@ -292,8 +497,12 @@ class _CREATE_PROCESS_DEBUG_INFO(Structure): ('lpImageName', LPVOID), ('fUnicode', WORD), ] -assert sizeof(_CREATE_PROCESS_DEBUG_INFO) == 40, sizeof(_CREATE_PROCESS_DEBUG_INFO) -assert alignment(_CREATE_PROCESS_DEBUG_INFO) == 4, alignment(_CREATE_PROCESS_DEBUG_INFO) +if is_64bits: + assert sizeof(_CREATE_PROCESS_DEBUG_INFO) == 72, sizeof(_CREATE_PROCESS_DEBUG_INFO) + assert alignment(_CREATE_PROCESS_DEBUG_INFO) == 8, alignment(_CREATE_PROCESS_DEBUG_INFO) +else: + assert sizeof(_CREATE_PROCESS_DEBUG_INFO) == 40, sizeof(_CREATE_PROCESS_DEBUG_INFO) + assert alignment(_CREATE_PROCESS_DEBUG_INFO) == 4, alignment(_CREATE_PROCESS_DEBUG_INFO) CREATE_PROCESS_DEBUG_INFO = _CREATE_PROCESS_DEBUG_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 664 class _EXIT_THREAD_DEBUG_INFO(Structure): @@ -327,8 +536,12 @@ class _LOAD_DLL_DEBUG_INFO(Structure): ('lpImageName', LPVOID), ('fUnicode', WORD), ] -assert sizeof(_LOAD_DLL_DEBUG_INFO) == 24, sizeof(_LOAD_DLL_DEBUG_INFO) -assert alignment(_LOAD_DLL_DEBUG_INFO) == 4, alignment(_LOAD_DLL_DEBUG_INFO) +if is_64bits: + assert sizeof(_LOAD_DLL_DEBUG_INFO) == 40, sizeof(_LOAD_DLL_DEBUG_INFO) + assert alignment(_LOAD_DLL_DEBUG_INFO) == 8, alignment(_LOAD_DLL_DEBUG_INFO) +else: + assert sizeof(_LOAD_DLL_DEBUG_INFO) == 24, sizeof(_LOAD_DLL_DEBUG_INFO) + assert alignment(_LOAD_DLL_DEBUG_INFO) == 4, alignment(_LOAD_DLL_DEBUG_INFO) LOAD_DLL_DEBUG_INFO = _LOAD_DLL_DEBUG_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 681 class _UNLOAD_DLL_DEBUG_INFO(Structure): @@ -337,8 +550,12 @@ class _UNLOAD_DLL_DEBUG_INFO(Structure): # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 681 ('lpBaseOfDll', LPVOID), ] -assert sizeof(_UNLOAD_DLL_DEBUG_INFO) == 4, sizeof(_UNLOAD_DLL_DEBUG_INFO) -assert alignment(_UNLOAD_DLL_DEBUG_INFO) == 4, alignment(_UNLOAD_DLL_DEBUG_INFO) +if is_64bits: + assert sizeof(_UNLOAD_DLL_DEBUG_INFO) == 8, sizeof(_UNLOAD_DLL_DEBUG_INFO) + assert alignment(_UNLOAD_DLL_DEBUG_INFO) == 8, alignment(_UNLOAD_DLL_DEBUG_INFO) +else: + assert sizeof(_UNLOAD_DLL_DEBUG_INFO) == 4, sizeof(_UNLOAD_DLL_DEBUG_INFO) + assert alignment(_UNLOAD_DLL_DEBUG_INFO) == 4, alignment(_UNLOAD_DLL_DEBUG_INFO) UNLOAD_DLL_DEBUG_INFO = _UNLOAD_DLL_DEBUG_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 685 class _OUTPUT_DEBUG_STRING_INFO(Structure): @@ -349,8 +566,12 @@ class _OUTPUT_DEBUG_STRING_INFO(Structure): ('fUnicode', WORD), ('nDebugStringLength', WORD), ] -assert sizeof(_OUTPUT_DEBUG_STRING_INFO) == 8, sizeof(_OUTPUT_DEBUG_STRING_INFO) -assert alignment(_OUTPUT_DEBUG_STRING_INFO) == 4, alignment(_OUTPUT_DEBUG_STRING_INFO) +if is_64bits: + assert sizeof(_OUTPUT_DEBUG_STRING_INFO) == 16, sizeof(_OUTPUT_DEBUG_STRING_INFO) + assert alignment(_OUTPUT_DEBUG_STRING_INFO) == 8, alignment(_OUTPUT_DEBUG_STRING_INFO) +else: + assert sizeof(_OUTPUT_DEBUG_STRING_INFO) == 8, sizeof(_OUTPUT_DEBUG_STRING_INFO) + assert alignment(_OUTPUT_DEBUG_STRING_INFO) == 4, alignment(_OUTPUT_DEBUG_STRING_INFO) OUTPUT_DEBUG_STRING_INFO = _OUTPUT_DEBUG_STRING_INFO # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 691 class _RIP_INFO(Structure): @@ -375,8 +596,12 @@ class _RIP_INFO(Structure): ('DebugString', OUTPUT_DEBUG_STRING_INFO), ('RipInfo', RIP_INFO), ] -assert sizeof(N12_DEBUG_EVENT4DOLLAR_39E) == 84, sizeof(N12_DEBUG_EVENT4DOLLAR_39E) -assert alignment(N12_DEBUG_EVENT4DOLLAR_39E) == 4, alignment(N12_DEBUG_EVENT4DOLLAR_39E) +if is_64bits: + assert sizeof(N12_DEBUG_EVENT4DOLLAR_39E) == 160, sizeof(N12_DEBUG_EVENT4DOLLAR_39E) + assert alignment(N12_DEBUG_EVENT4DOLLAR_39E) == 8, alignment(N12_DEBUG_EVENT4DOLLAR_39E) +else: + assert sizeof(N12_DEBUG_EVENT4DOLLAR_39E) == 84, sizeof(N12_DEBUG_EVENT4DOLLAR_39E) + assert alignment(N12_DEBUG_EVENT4DOLLAR_39E) == 4, alignment(N12_DEBUG_EVENT4DOLLAR_39E) _DEBUG_EVENT._fields_ = [ # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 697 ('dwDebugEventCode', DWORD), @@ -384,8 +609,12 @@ class _RIP_INFO(Structure): ('dwThreadId', DWORD), ('u', N12_DEBUG_EVENT4DOLLAR_39E), ] -assert sizeof(_DEBUG_EVENT) == 96, sizeof(_DEBUG_EVENT) -assert alignment(_DEBUG_EVENT) == 4, alignment(_DEBUG_EVENT) +if is_64bits: + assert sizeof(_DEBUG_EVENT) == 176, sizeof(_DEBUG_EVENT) + assert alignment(_DEBUG_EVENT) == 8, alignment(_DEBUG_EVENT) +else: + assert sizeof(_DEBUG_EVENT) == 96, sizeof(_DEBUG_EVENT) + assert alignment(_DEBUG_EVENT) == 4, alignment(_DEBUG_EVENT) LONG = c_long _LUID._fields_ = [ # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 394 @@ -419,5 +648,106 @@ class _LUID_AND_ATTRIBUTES(Structure): ('dwProcessId', DWORD), ('dwThreadId', DWORD), ] -assert sizeof(_PROCESS_INFORMATION) == 16, sizeof(_PROCESS_INFORMATION) -assert alignment(_PROCESS_INFORMATION) == 4, alignment(_PROCESS_INFORMATION) +if is_64bits: + assert sizeof(_PROCESS_INFORMATION) == 24, sizeof(_PROCESS_INFORMATION) + assert alignment(_PROCESS_INFORMATION) == 8, alignment(_PROCESS_INFORMATION) +else: + assert sizeof(_PROCESS_INFORMATION) == 16, sizeof(_PROCESS_INFORMATION) + assert alignment(_PROCESS_INFORMATION) == 4, alignment(_PROCESS_INFORMATION) + +class _IMAGE_FILE_HEADER(Structure): + _fields_ = [ + ("Machine", WORD), + ("NumberOfSections", WORD), + ("TimeDateStamp", DWORD), + ("PointerToSymbolTable", DWORD), + ("NumberOfSymbols", DWORD), + ("SizeOfOptionalHeader", WORD), + ("Characteristics", WORD), + ] + +class _IMAGE_DATA_DIRECTORY(Structure): + _fields_ = [ + ("VirtualAddress", DWORD), + ("Size", DWORD), + ] + +class _IMAGE_DATA_DIRECTORY(Structure): + _fields_ = [ + ("VirtualAddress", DWORD), + ("Size", DWORD), + ] + +IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 + +class _IMAGE_OPTIONAL_HEADER(Structure): + _fields_ = [ + ("Magic", WORD), + ("MajorLinkerVersion", BYTE), + ("MinorLinkerVersion", BYTE), + ("SizeOfCode", DWORD), + ("SizeOfInitializedData", DWORD), + ("SizeOfUninitializedData", DWORD), + ("AddressOfEntryPoint", DWORD), + ("BaseOfCode", DWORD), + ("BaseOfData", DWORD), + ("ImageBase", DWORD), + ("SectionAlignment", DWORD), + ("FileAlignment", DWORD), + ("MajorOperatingSystemVersion", WORD), + ("MinorOperatingSystemVersion", WORD), + ("MajorImageVersion", WORD), + ("MinorImageVersion", WORD), + ("MajorSubsystemVersion", WORD), + ("MinorSubsystemVersion", WORD), + ("Win32VersionValue", DWORD), + ("SizeOfImage", DWORD), + ("SizeOfHeaders", DWORD), + ("CheckSum", DWORD), + ("Subsystem", WORD), + ("DllCharacteristics", WORD), + ("SizeOfStackReserve", DWORD), + ("SizeOfStackCommit", DWORD), + ("SizeOfHeapReserve", DWORD), + ("SizeOfHeapCommit", DWORD), + ("LoaderFlags", DWORD), + ("NumberOfRvaAndSizes", DWORD), + ("DataDirectory", _IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES), + ] +IMAGE_OPTIONAL_HEADER32 = _IMAGE_OPTIONAL_HEADER + +class _IMAGE_OPTIONAL_HEADER64(Structure): + _fields_ = [ + ("Magic", WORD), + ("MajorLinkerVersion", BYTE), + ("MinorLinkerVersion", BYTE), + ("SizeOfCode", DWORD), + ("SizeOfInitializedData", DWORD), + ("SizeOfUninitializedData", DWORD), + ("AddressOfEntryPoint", DWORD), + ("BaseOfCode", DWORD), + ("ImageBase", DWORD64), + ("SectionAlignment", DWORD), + ("FileAlignment", DWORD), + ("MajorOperatingSystemVersion", WORD), + ("MinorOperatingSystemVersion", WORD), + ("MajorImageVersion", WORD), + ("MinorImageVersion", WORD), + ("MajorSubsystemVersion", WORD), + ("MinorSubsystemVersion", WORD), + ("Win32VersionValue", DWORD), + ("SizeOfImage", DWORD), + ("SizeOfHeaders", DWORD), + ("CheckSum", DWORD), + ("Subsystem", WORD), + ("DllCharacteristics", WORD), + ("SizeOfStackReserve", DWORD64), + ("SizeOfStackCommit", DWORD64), + ("SizeOfHeapReserve", DWORD64), + ("SizeOfHeapCommit", DWORD64), + ("LoaderFlags", DWORD), + ("NumberOfRvaAndSizes", DWORD), + ("DataDirectory", _IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES), + ] +IMAGE_OPTIONAL_HEADER64 = _IMAGE_OPTIONAL_HEADER64 +