├── README.md ├── pydasm.pyd ├── memory_snapshot_block.py ├── memory_snapshot_context.py ├── __init__.py ├── breakpoint.py ├── hardware_breakpoint.py ├── pdx.py ├── memory_breakpoint.py ├── my_ctypes.py ├── system_dll.py ├── defines.py ├── pydbg_client.py ├── windows_h.py └── pydbg.py /README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pydasm.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenRCE/pydbg/HEAD/pydasm.pyd -------------------------------------------------------------------------------- /memory_snapshot_block.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: memory_snapshot_block.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | class memory_snapshot_block: 26 | ''' 27 | Memory block object, used in memory snapshots. 28 | ''' 29 | 30 | mbi = None 31 | data = None 32 | 33 | #################################################################################################################### 34 | def __init__ (self, mbi=None, data=None): 35 | ''' 36 | @type mbi: MEMORY_BASIC_INFORMATION 37 | @param mbi: MEMORY_BASIC_INFORMATION of memory block 38 | @type data: Raw Bytes 39 | @param data: Raw bytes stored in memory block at time of snapshot 40 | ''' 41 | 42 | self.mbi = mbi 43 | self.data = data 44 | -------------------------------------------------------------------------------- /memory_snapshot_context.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: memory_snapshot_context.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | class memory_snapshot_context: 26 | ''' 27 | Thread context object, used in memory snapshots. 28 | ''' 29 | 30 | thread_id = None 31 | context = None 32 | 33 | #################################################################################################################### 34 | def __init__ (self, thread_id=None, context=None): 35 | ''' 36 | @type thread_id: Integer 37 | @param thread_id: Thread ID 38 | @type context: CONTEXT 39 | @param context: Context of thread specified by ID at time of snapshot 40 | ''' 41 | 42 | self.thread_id = thread_id 43 | self.context = context -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: __init__.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | __all__ = \ 26 | [ 27 | "breakpoint", 28 | "defines", 29 | "hardware_breakpoint", 30 | "memory_breakpoint", 31 | "memory_snapshot_block", 32 | "memory_snapshot_context", 33 | "pdx", 34 | "pydbg", 35 | "pydbg_client", 36 | "system_dll", 37 | "windows_h", 38 | ] 39 | 40 | from breakpoint import * 41 | from defines import * 42 | from hardware_breakpoint import * 43 | from memory_breakpoint import * 44 | from memory_snapshot_block import * 45 | from memory_snapshot_context import * 46 | from pdx import * 47 | from pydbg import * 48 | from pydbg_client import * 49 | from system_dll import * 50 | from windows_h import * -------------------------------------------------------------------------------- /breakpoint.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: breakpoint.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | class breakpoint: 26 | ''' 27 | Soft breakpoint object. 28 | ''' 29 | 30 | address = None 31 | original_byte = None 32 | description = None 33 | restore = None 34 | handler = None 35 | 36 | #################################################################################################################### 37 | def __init__ (self, address=None, original_byte=None, description="", restore=True, handler=None): 38 | ''' 39 | @type address: DWORD 40 | @param address: Address of breakpoint 41 | @type original_byte: Byte 42 | @param original_byte: Original byte stored at breakpoint address 43 | @type description: String 44 | @param description: (Optional) Description of breakpoint 45 | @type restore: Boolean 46 | @param restore: (Optional, def=True) Flag controlling whether or not to restore the breakpoint 47 | @type handler: Function Pointer 48 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 49 | ''' 50 | 51 | self.address = address 52 | self.original_byte = original_byte 53 | self.description = description 54 | self.restore = restore 55 | self.handler = handler -------------------------------------------------------------------------------- /hardware_breakpoint.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: hardware_breakpoint.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | class hardware_breakpoint: 26 | ''' 27 | Hardware breakpoint object. 28 | ''' 29 | 30 | address = None 31 | length = None 32 | condition = None 33 | description = None 34 | restore = None 35 | slot = None 36 | handler = None 37 | 38 | #################################################################################################################### 39 | def __init__ (self, address=None, length=0, condition="", description="", restore=True, slot=None, handler=None): 40 | ''' 41 | 42 | @type address: DWORD 43 | @param address: Address to set hardware breakpoint at 44 | @type length: Integer (1, 2 or 4) 45 | @param length: Size of hardware breakpoint (byte, word or dword) 46 | @type condition: Integer (HW_ACCESS, HW_WRITE, HW_EXECUTE) 47 | @param condition: Condition to set the hardware breakpoint to activate on 48 | @type description: String 49 | @param description: (Optional) Description of breakpoint 50 | @type restore: Boolean 51 | @param restore: (Optional, def=True) Flag controlling whether or not to restore the breakpoint 52 | @type slot: Integer (0-3) 53 | @param slot: (Optional, Def=None) Debug register slot this hardware breakpoint sits in. 54 | @type handler: Function Pointer 55 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 56 | ''' 57 | 58 | self.address = address 59 | self.length = length 60 | self.condition = condition 61 | self.description = description 62 | self.restore = restore 63 | self.slot = slot 64 | self.handler = handler -------------------------------------------------------------------------------- /pdx.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: pdx.py 238 2010-04-05 20:40:46Z rgovostes $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | import os.path 26 | 27 | from my_ctypes import * 28 | from defines import * 29 | 30 | # macos compatability. 31 | try: 32 | kernel32 = windll.kernel32 33 | except: 34 | kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) 35 | 36 | class pdx (Exception): 37 | ''' 38 | This class is used internally for raising custom exceptions and includes support for automated Windows error message 39 | resolution and formatting. For example, to raise a generic error you can use:: 40 | 41 | raise pdx("Badness occured.") 42 | 43 | To raise a Windows API error you can use:: 44 | 45 | raise pdx("SomeWindowsApi()", True) 46 | ''' 47 | 48 | message = None 49 | error_code = None 50 | 51 | #################################################################################################################### 52 | def __init__ (self, message, win32_exception=False): 53 | ''' 54 | ''' 55 | 56 | self.message = message 57 | self.error_code = None 58 | 59 | if win32_exception: 60 | self.error_msg = c_char_p() 61 | self.error_code = kernel32.GetLastError() 62 | 63 | kernel32.FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 64 | None, 65 | self.error_code, 66 | 0x00000400, # MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) 67 | byref(self.error_msg), 68 | 0, 69 | None) 70 | 71 | 72 | #################################################################################################################### 73 | def __str__ (self): 74 | if self.error_code != None: 75 | return "[%d] %s: %s" % (self.error_code, self.message, self.error_msg.value) 76 | else: 77 | return self.message -------------------------------------------------------------------------------- /memory_breakpoint.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: memory_breakpoint.py 194 2007-04-05 15:31:53Z cameron $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | import random 26 | 27 | class memory_breakpoint: 28 | ''' 29 | Memory breakpoint object. 30 | ''' 31 | 32 | address = None 33 | size = None 34 | mbi = None 35 | description = None 36 | handler = None 37 | 38 | read_count = 0 # number of times the target buffer was read from 39 | split_count = 0 # number of times this breakpoint was split 40 | copy_depth = 0 # degrees of separation from original buffer 41 | id = 0 # unique breakpoint identifier 42 | on_stack = False # is this memory breakpoint on a stack buffer? 43 | 44 | #################################################################################################################### 45 | def __init__ (self, address=None, size=None, mbi=None, description="", handler=None): 46 | ''' 47 | @type address: DWORD 48 | @param address: Address of breakpoint 49 | @type size: Integer 50 | @param size: Size of buffer we want to break on 51 | @type mbi: MEMORY_BASIC_INFORMATION 52 | @param mbi: MEMORY_BASIC_INFORMATION of page containing buffer we want to break on 53 | @type description: String 54 | @param description: (Optional) Description of breakpoint 55 | @type handler: Function Pointer 56 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 57 | ''' 58 | 59 | self.address = address 60 | self.size = size 61 | self.mbi = mbi 62 | self.description = description 63 | self.handler = handler 64 | 65 | self.id = random.randint(0, 0xFFFFFFFF) # unique breakpoint identifier 66 | self.read_count = 0 # number of times the target buffer was read from 67 | self.split_count = 0 # number of times this breakpoint was split 68 | self.copy_depth = 0 # degrees of separation from original buffer 69 | self.on_stack = False # is this memory breakpoint on a stack buffer? -------------------------------------------------------------------------------- /my_ctypes.py: -------------------------------------------------------------------------------- 1 | #!c:\python\python.exe 2 | 3 | # 4 | # PyDBG 5 | # Copyright (C) 2006 Pedram Amini 6 | # 7 | # $Id: my_ctypes.py 236 2010-03-05 18:16:17Z pedram.amini $ 8 | # 9 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 10 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 11 | # version. 12 | # 13 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 14 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 17 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | # 19 | # 20 | # Many thanks to Thomas Heller, for both his efforts in developing the c_types library and for his assistance with 21 | # getting c_types to cooperate with cPickle. 22 | # 23 | 24 | ''' 25 | @author: Pedram Amini 26 | @license: GNU General Public License 2.0 or later 27 | @contact: pedram.amini@gmail.com 28 | @organization: www.openrce.org 29 | ''' 30 | 31 | from ctypes import * 32 | 33 | 34 | ######################################################################################################################## 35 | def _construct (typ, raw_bytes): 36 | ''' 37 | This routine is called by _reduce for unmarshaling purposes. The object type is used to instantiate a new object of 38 | the desired type. The raw bytes are then directly written into the address space of the newly instantiated object 39 | via the ctypes routines memmove() and addressof(). 40 | 41 | @type typ: Class object 42 | @param typ: The class type of the object we are unmarshaling. 43 | @type raw_bytes: Raw bytes (in a string buffer) 44 | @param raw_bytes: The raw bytes 45 | 46 | @rtype: Mixed 47 | @return: Unmarshaled object. 48 | ''' 49 | 50 | obj = typ.__new__(typ) 51 | memmove(addressof(obj), raw_bytes, len(raw_bytes)) 52 | 53 | return obj 54 | 55 | 56 | ######################################################################################################################## 57 | def _reduce (self): 58 | ''' 59 | cPickle will allow an object to marshal itself if the __reduce__ function is defined. Because cPickle is unable to 60 | handle ctype primitives or the objects derived on those primitives we define this function that later is assigned 61 | as the __reduce__ method for all types. This method relies on _construct for unmarshaling. As per the Python docs 62 | __reduce__ must return a tuple where the first element is "A callable object that will be called to create the 63 | initial version of the object. The next element of the tuple will provide arguments for this callable...". In this 64 | case we pass the object class and the raw data bytes of the object to _construct. 65 | 66 | @rtype: Tuple 67 | @return: Tuple, as specified per the cPickle __reduce__ Python docs. 68 | ''' 69 | 70 | return (_construct, (self.__class__, str(buffer(self)))) 71 | 72 | 73 | ######################################################################################################################## 74 | c_types = (Structure, c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, \ 75 | c_ulonglong, c_float, c_double, c_char_p, c_wchar_p, c_void_p) 76 | 77 | 78 | # for each ctype we need to marshal, set it's __reduce__ routine. 79 | #for typ in c_types: 80 | # typ.__reduce__ = _reduce 81 | -------------------------------------------------------------------------------- /system_dll.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: system_dll.py 238 2010-04-05 20:40:46Z rgovostes $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | 18 | ''' 19 | @author: Pedram Amini 20 | @license: GNU General Public License 2.0 or later 21 | @contact: pedram.amini@gmail.com 22 | @organization: www.openrce.org 23 | ''' 24 | 25 | import os.path 26 | 27 | from my_ctypes import * 28 | from defines import * 29 | from windows_h import * 30 | 31 | # macos compatability. 32 | try: 33 | kernel32 = windll.kernel32 34 | psapi = windll.psapi 35 | except: 36 | kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) 37 | psapi = kernel32 38 | 39 | from pdx import * 40 | 41 | import os 42 | 43 | class system_dll: 44 | ''' 45 | System DLL descriptor object, used to keep track of loaded system DLLs and locations. 46 | 47 | @todo: Add PE parsing support. 48 | ''' 49 | 50 | handle = None 51 | base = None 52 | name = None 53 | path = None 54 | pe = None 55 | size = 0 56 | 57 | #################################################################################################################### 58 | def __init__ (self, handle, base): 59 | ''' 60 | Given a handle and base address of the loaded DLL, determine the DLL name and size to fully initialize the 61 | system DLL object. 62 | 63 | @type handle: HANDLE 64 | @param handle: Handle to the loaded DLL 65 | @type base: DWORD 66 | @param base: Loaded address of DLL 67 | 68 | @raise pdx: An exception is raised on failure. 69 | ''' 70 | 71 | self.handle = handle 72 | self.base = base 73 | self.name = None 74 | self.path = None 75 | self.pe = None 76 | self.size = 0 77 | 78 | # calculate the file size of the 79 | file_size_hi = c_ulong(0) 80 | file_size_lo = 0 81 | file_size_lo = kernel32.GetFileSize(handle, byref(file_size_hi)) 82 | self.size = (file_size_hi.value << 8) + file_size_lo 83 | 84 | # create a file mapping from the dll handle. 85 | file_map = kernel32.CreateFileMappingA(handle, 0, PAGE_READONLY, 0, 1, 0) 86 | 87 | if file_map: 88 | # map a single byte of the dll into memory so we can query for the file name. 89 | kernel32.MapViewOfFile.restype = POINTER(c_char) 90 | file_ptr = kernel32.MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1) 91 | 92 | if file_ptr: 93 | # query for the filename of the mapped file. 94 | filename = create_string_buffer(2048) 95 | psapi.GetMappedFileNameA(kernel32.GetCurrentProcess(), file_ptr, byref(filename), 2048) 96 | 97 | # store the full path. this is kind of ghetto, but i didn't want to mess with QueryDosDevice() etc ... 98 | self.path = os.sep + filename.value.split(os.sep, 3)[3] 99 | 100 | # store the file name. 101 | # XXX - this really shouldn't be failing. but i've seen it happen. 102 | try: 103 | self.name = filename.value[filename.value.rindex(os.sep)+1:] 104 | except: 105 | self.name = self.path 106 | 107 | kernel32.UnmapViewOfFile(file_ptr) 108 | 109 | kernel32.CloseHandle(file_map) 110 | 111 | 112 | #################################################################################################################### 113 | def __del__ (self): 114 | ''' 115 | Close the handle. 116 | ''' 117 | 118 | kernel32.CloseHandle(self.handle) -------------------------------------------------------------------------------- /defines.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyDBG 3 | # Copyright (C) 2006 Pedram Amini 4 | # 5 | # $Id: defines.py 224 2007-10-12 19:51:45Z aportnoy $ 6 | # 7 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 8 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 9 | # version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 12 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 15 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | # 17 | # 18 | # windows_h.py was generated with: 19 | # 20 | # c:\Python\Lib\site-packages\ctypes\wrap 21 | # c:\python\python h2xml.py windows.h -o windows.xml -q -c 22 | # c:\python\python xml2py.py windows.xml -s DEBUG_EVENT -s CONTEXT -s MEMORY_BASIC_INFORMATION -s LDT_ENTRY \ 23 | # -s PROCESS_INFORMATION -s STARTUPINFO -s SYSTEM_INFO -o windows_h.py 24 | # 25 | # Then the import of ctypes was changed at the top of the file to utilize my_ctypes, which adds the necessary changes 26 | # to support the pickle-ing of our defined data structures and ctype primitives. 27 | # 28 | 29 | ''' 30 | @author: Pedram Amini 31 | @license: GNU General Public License 2.0 or later 32 | @contact: pedram.amini@gmail.com 33 | @organization: www.openrce.org 34 | ''' 35 | 36 | from my_ctypes import * 37 | from windows_h import * 38 | 39 | ### 40 | ### manually declare entities from Tlhelp32.h since i was unable to import using h2xml.py. 41 | ### 42 | 43 | TH32CS_SNAPHEAPLIST = 0x00000001 44 | TH32CS_SNAPPROCESS = 0x00000002 45 | TH32CS_SNAPTHREAD = 0x00000004 46 | TH32CS_SNAPMODULE = 0x00000008 47 | TH32CS_INHERIT = 0x80000000 48 | TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE) 49 | 50 | class THREADENTRY32(Structure): 51 | _fields_ = [ 52 | ('dwSize', DWORD), 53 | ('cntUsage', DWORD), 54 | ('th32ThreadID', DWORD), 55 | ('th32OwnerProcessID', DWORD), 56 | ('tpBasePri', DWORD), 57 | ('tpDeltaPri', DWORD), 58 | ('dwFlags', DWORD), 59 | ] 60 | 61 | class PROCESSENTRY32(Structure): 62 | _fields_ = [ 63 | ('dwSize', DWORD), 64 | ('cntUsage', DWORD), 65 | ('th32ProcessID', DWORD), 66 | ('th32DefaultHeapID', DWORD), 67 | ('th32ModuleID', DWORD), 68 | ('cntThreads', DWORD), 69 | ('th32ParentProcessID', DWORD), 70 | ('pcPriClassBase', DWORD), 71 | ('dwFlags', DWORD), 72 | ('szExeFile', CHAR * 260), 73 | ] 74 | 75 | class MODULEENTRY32(Structure): 76 | _fields_ = [ 77 | ("dwSize", DWORD), 78 | ("th32ModuleID", DWORD), 79 | ("th32ProcessID", DWORD), 80 | ("GlblcntUsage", DWORD), 81 | ("ProccntUsage", DWORD), 82 | ("modBaseAddr", DWORD), 83 | ("modBaseSize", DWORD), 84 | ("hModule", DWORD), 85 | ("szModule", CHAR * 256), 86 | ("szExePath", CHAR * 260), 87 | ] 88 | 89 | class _MIB_TCPROW_OWNER_PID(Structure): 90 | _fields_ = [ 91 | ("dwState", DWORD), 92 | ("dwLocalAddr", DWORD), 93 | ("dwLocalPort", DWORD), 94 | ("dwRemoteAddr", DWORD), 95 | ("dwRemotePort", DWORD), 96 | ("dwOwningPid", DWORD), 97 | ] 98 | 99 | class MIB_TCPTABLE_OWNER_PID(Structure): 100 | _fields_ = [ 101 | ("dwNumEntries", DWORD), 102 | ("table", _MIB_TCPROW_OWNER_PID * 512) 103 | ] 104 | 105 | 106 | class _MIB_UDPROW_OWNER_PID(Structure): 107 | _fields_ = [ 108 | ("dwLocalAddr", DWORD), 109 | ("dwLocalPort", DWORD), 110 | ("dwOwningPid", DWORD) 111 | ] 112 | 113 | class MIB_UDPTABLE_OWNER_PID(Structure): 114 | _fields_ = [ 115 | ("dwNumEntries", DWORD), 116 | ("table", _MIB_UDPROW_OWNER_PID * 512) 117 | ] 118 | 119 | 120 | ### 121 | ### manually declare various structures as needed. 122 | ### 123 | 124 | class SYSDBG_MSR(Structure): 125 | _fields_ = [ 126 | ("Address", c_ulong), 127 | ("Data", c_ulonglong), 128 | ] 129 | 130 | ### 131 | ### manually declare various #define's as needed. 132 | ### 133 | 134 | # debug event codes. 135 | EXCEPTION_DEBUG_EVENT = 0x00000001 136 | CREATE_THREAD_DEBUG_EVENT = 0x00000002 137 | CREATE_PROCESS_DEBUG_EVENT = 0x00000003 138 | EXIT_THREAD_DEBUG_EVENT = 0x00000004 139 | EXIT_PROCESS_DEBUG_EVENT = 0x00000005 140 | LOAD_DLL_DEBUG_EVENT = 0x00000006 141 | UNLOAD_DLL_DEBUG_EVENT = 0x00000007 142 | OUTPUT_DEBUG_STRING_EVENT = 0x00000008 143 | RIP_EVENT = 0x00000009 144 | USER_CALLBACK_DEBUG_EVENT = 0xDEADBEEF # added for callback support in debug event loop. 145 | 146 | # debug exception codes. 147 | EXCEPTION_ACCESS_VIOLATION = 0xC0000005 148 | EXCEPTION_BREAKPOINT = 0x80000003 149 | EXCEPTION_GUARD_PAGE = 0x80000001 150 | EXCEPTION_SINGLE_STEP = 0x80000004 151 | 152 | # hw breakpoint conditions 153 | HW_ACCESS = 0x00000003 154 | HW_EXECUTE = 0x00000000 155 | HW_WRITE = 0x00000001 156 | 157 | CONTEXT_CONTROL = 0x00010001 158 | CONTEXT_FULL = 0x00010007 159 | CONTEXT_DEBUG_REGISTERS = 0x00010010 160 | CREATE_NEW_CONSOLE = 0x00000010 161 | DBG_CONTINUE = 0x00010002 162 | DBG_EXCEPTION_NOT_HANDLED = 0x80010001 163 | DBG_EXCEPTION_HANDLED = 0x00010001 164 | DEBUG_PROCESS = 0x00000001 165 | DEBUG_ONLY_THIS_PROCESS = 0x00000002 166 | EFLAGS_RF = 0x00010000 167 | EFLAGS_TRAP = 0x00000100 168 | ERROR_NO_MORE_FILES = 0x00000012 169 | FILE_MAP_READ = 0x00000004 170 | FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 171 | FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 172 | INVALID_HANDLE_VALUE = 0xFFFFFFFF 173 | MEM_COMMIT = 0x00001000 174 | MEM_DECOMMIT = 0x00004000 175 | MEM_IMAGE = 0x01000000 176 | MEM_RELEASE = 0x00008000 177 | PAGE_NOACCESS = 0x00000001 178 | PAGE_READONLY = 0x00000002 179 | PAGE_READWRITE = 0x00000004 180 | PAGE_WRITECOPY = 0x00000008 181 | PAGE_EXECUTE = 0x00000010 182 | PAGE_EXECUTE_READ = 0x00000020 183 | PAGE_EXECUTE_READWRITE = 0x00000040 184 | PAGE_EXECUTE_WRITECOPY = 0x00000080 185 | PAGE_GUARD = 0x00000100 186 | PAGE_NOCACHE = 0x00000200 187 | PAGE_WRITECOMBINE = 0x00000400 188 | PROCESS_ALL_ACCESS = 0x001F0FFF 189 | SE_PRIVILEGE_ENABLED = 0x00000002 190 | SW_SHOW = 0x00000005 191 | THREAD_ALL_ACCESS = 0x001F03FF 192 | TOKEN_ADJUST_PRIVILEGES = 0x00000020 193 | UDP_TABLE_OWNER_PID = 0x00000001 194 | VIRTUAL_MEM = 0x00003000 195 | 196 | # for NtSystemDebugControl() 197 | SysDbgReadMsr = 16 198 | SysDbgWriteMsr = 17 199 | 200 | # for mapping TCP ports and PIDs 201 | AF_INET = 0x00000002 202 | AF_INET6 = 0x00000017 203 | MIB_TCP_STATE_LISTEN = 0x00000002 204 | TCP_TABLE_OWNER_PID_ALL = 0x00000005 205 | -------------------------------------------------------------------------------- /pydbg_client.py: -------------------------------------------------------------------------------- 1 | #!c:\python\python.exe 2 | 3 | # 4 | # PyDBG 5 | # Copyright (C) 2006 Pedram Amini 6 | # 7 | # $Id: pydbg_client.py 194 2007-04-05 15:31:53Z cameron $ 8 | # 9 | 10 | ''' 11 | @author: Pedram Amini 12 | @contact: pedram.amini@gmail.com 13 | @organization: www.openrce.org 14 | ''' 15 | 16 | import socket 17 | import cPickle 18 | 19 | from pydbg import * 20 | from defines import * 21 | from pdx import * 22 | 23 | class pydbg_client: 24 | ''' 25 | This class defines the client portion of the decoupled client/server PyDBG debugger. The class was designed to be 26 | completely transparent to the end user, requiring only the simple change from:: 27 | 28 | dbg = pydbg() 29 | 30 | to:: 31 | 32 | dbg = pydbg_client(host, port) 33 | 34 | Command line options can be used to control which instantiation is used thereby allowing for any PyDBG driver to be 35 | used locally or remotely. 36 | ''' 37 | 38 | host = None 39 | port = None 40 | callbacks = {} 41 | pydbg = None 42 | 43 | #################################################################################################################### 44 | def __init__ (self, host, port): 45 | ''' 46 | Set the default client attributes. The target host and port are required. 47 | 48 | @type host: String 49 | @param host: Host address of PyDBG server (dotted quad IP address or hostname) 50 | @type port: Integer 51 | @param port: Port that the PyDBG server is listening on. 52 | 53 | @raise pdx: An exception is raised if a connection to the PyDbg server can not be established. 54 | ''' 55 | 56 | self.host = host 57 | self.port = port 58 | self.pydbg = pydbg() 59 | self.callbacks = {} 60 | 61 | try: 62 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 63 | self.sock.connect((host, port)) 64 | except: 65 | raise pdx("connection severed") 66 | 67 | 68 | #################################################################################################################### 69 | def __getattr__ (self, method_name): 70 | ''' 71 | This routine is called by default when a requested attribute (or method) is accessed that has no definition. 72 | Unfortunately __getattr__ only passes the requested method name and not the arguments. So we extend the 73 | functionality with a little lambda magic to the routine method_missing(). Which is actually how Ruby handles 74 | missing methods by default ... with arguments. Now we are just as cool as Ruby. 75 | 76 | @type method_name: String 77 | @param method_name: The name of the requested and undefined attribute (or method in our case). 78 | 79 | @rtype: Lambda 80 | @return: Lambda magic passing control (and in turn the arguments we want) to self.method_missing(). 81 | ''' 82 | 83 | return lambda *args, **kwargs: self.method_missing(method_name, *args, **kwargs) 84 | 85 | 86 | #################################################################################################################### 87 | def debug_event_loop (self): 88 | ''' 89 | Overriden debug event handling loop. A transparent mirror here with method_missing() would not do. Our debug 90 | event loop is reduced here to a data marshaling loop with the server. If the received type from the server is a 91 | tuple then we assume a debug or exception event has occured and pass it to any registered callbacks. The 92 | callback is free to call arbitrary PyDbg routines. Upon return of the callback, a special token, **DONE**, is 93 | used to flag to the PyDbg server that we are done processing the exception and it is free to move on. 94 | ''' 95 | 96 | self.pickle_send(("debug_event_loop", ())) 97 | 98 | while 1: 99 | received = self.pickle_recv() 100 | 101 | if not received: 102 | continue 103 | 104 | # if we received a "special" type, which can currently be one of: 105 | # - debugger callback event 106 | # - user callback event 107 | # - raised exception 108 | 109 | if type(received) == tuple: 110 | #### callback type. 111 | if received[0] == "callback": 112 | (msg_type, dbg, context) = received 113 | 114 | ## debugger callback event. 115 | if dbg and context: 116 | # propogate the convenience variables. 117 | self.dbg = dbg 118 | self.context = context 119 | self.exception_address = dbg.u.Exception.ExceptionRecord.ExceptionAddress 120 | self.write_violation = dbg.u.Exception.ExceptionRecord.ExceptionInformation[0] 121 | self.violation_address = dbg.u.Exception.ExceptionRecord.ExceptionInformation[1] 122 | 123 | exception_code = dbg.u.Exception.ExceptionRecord.ExceptionCode 124 | ret = DBG_CONTINUE 125 | 126 | if self.callbacks.has_key(exception_code): 127 | print "processing handler for %08x" % exception_code 128 | ret = self.callbacks[exception_code](self) 129 | 130 | ## user callback event. 131 | else: 132 | if self.callbacks.has_key(USER_CALLBACK_DEBUG_EVENT): 133 | ret = self.callbacks[USER_CALLBACK_DEBUG_EVENT](self) 134 | 135 | #### raised exception type. 136 | elif received[0] == "exception": 137 | (msg_type, exception_string) = received 138 | print exception_string 139 | raise pdx(exception_string) 140 | 141 | self.pickle_send(("**DONE**", ret)) 142 | 143 | 144 | #################################################################################################################### 145 | def method_missing (self, method_name, *args, **kwargs): 146 | ''' 147 | See the notes for __getattr__ for related notes. This method is called, in the Ruby fashion, with the method 148 | name and arguments for any requested but undefined class method. We utilize this method to transparently wrap 149 | requested PyDBG routines, transmit the method name and arguments to the server, then grab and return the methods 150 | return value. This transparency allows us to modify pydbg.py freely without having to add support for newly 151 | created methods to pydbg_client.py. Methods that require "special" attention and can not simply be mirrored are 152 | individually overridden and handled separately. 153 | 154 | @type method_name: String 155 | @param method_name: The name of the requested and undefined attribute (or method in our case). 156 | @type *args: Tuple 157 | @param *args: Tuple of arguments. 158 | @type **kwargs Dictionary 159 | @param **kwargs: Dictioanry of arguments. 160 | 161 | @rtype: Mixed 162 | @return: Return value of the mirrored method. 163 | ''' 164 | 165 | # some functions shouldn't go over the network. 166 | # XXX - there may be more routines we have to add to this list. 167 | if method_name in ("hex_dump", "flip_endian", "flip_endian_dword"): 168 | exec("method_pointer = self.pydbg.%s" % method_name) 169 | ret = method_pointer(*args, **kwargs) 170 | else: 171 | self.pickle_send((method_name, (args, kwargs))) 172 | ret = self.pickle_recv() 173 | 174 | if ret == "**SELF**": 175 | return self 176 | else: 177 | return ret 178 | 179 | 180 | #################################################################################################################### 181 | def pickle_recv (self): 182 | ''' 183 | This routine is used for marshaling arbitrary data from the PyDbg server. We can send pretty much anything here. 184 | For example a tuple containing integers, strings, arbitrary objects and structures. Our "protocol" is a simple 185 | length-value protocol where each datagram is prefixed by a 4-byte length of the data to be received. 186 | 187 | @raise pdx: An exception is raised if the connection was severed. 188 | @rtype: Mixed 189 | @return: Whatever is received over the socket. 190 | ''' 191 | 192 | try: 193 | length = long(self.sock.recv(4), 16) 194 | received = self.sock.recv(length) 195 | except: 196 | raise pdx("connection severed") 197 | 198 | return cPickle.loads(received) 199 | 200 | 201 | #################################################################################################################### 202 | def pickle_send (self, data): 203 | ''' 204 | This routine is used for marshaling arbitrary data to the PyDbg server. We can send pretty much anything here. 205 | For example a tuple containing integers, strings, arbitrary objects and structures. Our "protocol" is a simple 206 | length-value protocol where each datagram is prefixed by a 4-byte length of the data to be received. 207 | 208 | @type data: Mixed 209 | @param data: Data to marshal and transmit. Data can *pretty much* contain anything you throw at it. 210 | 211 | @raise pdx: An exception is raised if the connection was severed. 212 | ''' 213 | 214 | data = cPickle.dumps(data) 215 | 216 | try: 217 | self.sock.send("%04x" % len(data)) 218 | self.sock.send(data) 219 | except: 220 | raise pdx("connection severed") 221 | 222 | 223 | #################################################################################################################### 224 | def run (self): 225 | ''' 226 | Alias for debug_event_loop(). 227 | 228 | @see: debug_event_loop() 229 | ''' 230 | 231 | self.debug_event_loop() 232 | 233 | 234 | #################################################################################################################### 235 | def set_callback (self, exception_code, callback_func): 236 | ''' 237 | Overriden callback setting routing. A transparent mirror here with method_missing() would not do. We save the 238 | requested callback address / exception code pair and then tell the PyDbg server about it. For more information 239 | see the documentation of pydbg.set_callback(). 240 | 241 | @type exception_code: Long 242 | @param exception_code: Exception code to establish a callback for 243 | @type callback_func: Function 244 | @param callback_func: Function to call when specified exception code is caught. 245 | ''' 246 | 247 | self.callbacks[exception_code] = callback_func 248 | 249 | self.pickle_send(("set_callback", exception_code)) 250 | 251 | return self.pickle_recv() 252 | -------------------------------------------------------------------------------- /windows_h.py: -------------------------------------------------------------------------------- 1 | # generated by 'xml2py' 2 | 3 | # 4 | # $Id: windows_h.py 194 2007-04-05 15:31:53Z cameron $ 5 | # 6 | 7 | 8 | # 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' 9 | 10 | # PEDRAM - line swap ... have to patch in our own __reduce__ definition to each ctype. 11 | #from ctypes import * 12 | from my_ctypes import * 13 | 14 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 4188 15 | class _TOKEN_PRIVILEGES(Structure): 16 | pass 17 | TOKEN_PRIVILEGES = _TOKEN_PRIVILEGES 18 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 3774 19 | class _STARTUPINFOA(Structure): 20 | pass 21 | STARTUPINFOA = _STARTUPINFOA 22 | STARTUPINFO = STARTUPINFOA 23 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1661 24 | class _LDT_ENTRY(Structure): 25 | pass 26 | LDT_ENTRY = _LDT_ENTRY 27 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 4534 28 | class _MEMORY_BASIC_INFORMATION(Structure): 29 | pass 30 | MEMORY_BASIC_INFORMATION = _MEMORY_BASIC_INFORMATION 31 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 697 32 | class _DEBUG_EVENT(Structure): 33 | pass 34 | DEBUG_EVENT = _DEBUG_EVENT 35 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1563 36 | class _CONTEXT(Structure): 37 | pass 38 | CONTEXT = _CONTEXT 39 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 497 40 | class _SYSTEM_INFO(Structure): 41 | pass 42 | SYSTEM_INFO = _SYSTEM_INFO 43 | HANDLE = c_void_p 44 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 229 45 | class _PROCESS_INFORMATION(Structure): 46 | pass 47 | PROCESS_INFORMATION = _PROCESS_INFORMATION 48 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 394 49 | class _LUID(Structure): 50 | pass 51 | LUID = _LUID 52 | WORD = c_ushort 53 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1664 54 | class N10_LDT_ENTRY3DOLLAR_4E(Union): 55 | pass 56 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1665 57 | class N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E(Structure): 58 | pass 59 | BYTE = c_ubyte 60 | N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E._fields_ = [ 61 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1665 62 | ('BaseMid', BYTE), 63 | ('Flags1', BYTE), 64 | ('Flags2', BYTE), 65 | ('BaseHi', BYTE), 66 | ] 67 | assert sizeof(N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E) == 4, sizeof(N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E) 68 | assert alignment(N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E) == 1, alignment(N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E) 69 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1671 70 | class N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E(Structure): 71 | pass 72 | DWORD = c_ulong 73 | N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E._fields_ = [ 74 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1671 75 | ('BaseMid', DWORD, 8), 76 | ('Type', DWORD, 5), 77 | ('Dpl', DWORD, 2), 78 | ('Pres', DWORD, 1), 79 | ('LimitHi', DWORD, 4), 80 | ('Sys', DWORD, 1), 81 | ('Reserved_0', DWORD, 1), 82 | ('Default_Big', DWORD, 1), 83 | ('Granularity', DWORD, 1), 84 | ('BaseHi', DWORD, 8), 85 | ] 86 | assert sizeof(N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E) == 4, sizeof(N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E) 87 | assert alignment(N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E) == 4, alignment(N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E) 88 | N10_LDT_ENTRY3DOLLAR_4E._fields_ = [ 89 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1664 90 | ('Bytes', N10_LDT_ENTRY3DOLLAR_43DOLLAR_5E), 91 | ('Bits', N10_LDT_ENTRY3DOLLAR_43DOLLAR_6E), 92 | ] 93 | assert sizeof(N10_LDT_ENTRY3DOLLAR_4E) == 4, sizeof(N10_LDT_ENTRY3DOLLAR_4E) 94 | assert alignment(N10_LDT_ENTRY3DOLLAR_4E) == 4, alignment(N10_LDT_ENTRY3DOLLAR_4E) 95 | _LDT_ENTRY._fields_ = [ 96 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1661 97 | ('LimitLow', WORD), 98 | ('BaseLow', WORD), 99 | ('HighWord', N10_LDT_ENTRY3DOLLAR_4E), 100 | ] 101 | assert sizeof(_LDT_ENTRY) == 8, sizeof(_LDT_ENTRY) 102 | assert alignment(_LDT_ENTRY) == 4, alignment(_LDT_ENTRY) 103 | PVOID = c_void_p 104 | UINT_PTR = c_ulong 105 | SIZE_T = UINT_PTR 106 | _MEMORY_BASIC_INFORMATION._fields_ = [ 107 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 4534 108 | ('BaseAddress', PVOID), 109 | ('AllocationBase', PVOID), 110 | ('AllocationProtect', DWORD), 111 | ('RegionSize', SIZE_T), 112 | ('State', DWORD), 113 | ('Protect', DWORD), 114 | ('Type', DWORD), 115 | ] 116 | assert sizeof(_MEMORY_BASIC_INFORMATION) == 28, sizeof(_MEMORY_BASIC_INFORMATION) 117 | assert alignment(_MEMORY_BASIC_INFORMATION) == 4, alignment(_MEMORY_BASIC_INFORMATION) 118 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1539 119 | class _FLOATING_SAVE_AREA(Structure): 120 | pass 121 | _FLOATING_SAVE_AREA._fields_ = [ 122 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1539 123 | ('ControlWord', DWORD), 124 | ('StatusWord', DWORD), 125 | ('TagWord', DWORD), 126 | ('ErrorOffset', DWORD), 127 | ('ErrorSelector', DWORD), 128 | ('DataOffset', DWORD), 129 | ('DataSelector', DWORD), 130 | ('RegisterArea', BYTE * 80), 131 | ('Cr0NpxState', DWORD), 132 | ] 133 | assert sizeof(_FLOATING_SAVE_AREA) == 112, sizeof(_FLOATING_SAVE_AREA) 134 | assert alignment(_FLOATING_SAVE_AREA) == 4, alignment(_FLOATING_SAVE_AREA) 135 | FLOATING_SAVE_AREA = _FLOATING_SAVE_AREA 136 | _CONTEXT._fields_ = [ 137 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 1563 138 | ('ContextFlags', DWORD), 139 | ('Dr0', DWORD), 140 | ('Dr1', DWORD), 141 | ('Dr2', DWORD), 142 | ('Dr3', DWORD), 143 | ('Dr6', DWORD), 144 | ('Dr7', DWORD), 145 | ('FloatSave', FLOATING_SAVE_AREA), 146 | ('SegGs', DWORD), 147 | ('SegFs', DWORD), 148 | ('SegEs', DWORD), 149 | ('SegDs', DWORD), 150 | ('Edi', DWORD), 151 | ('Esi', DWORD), 152 | ('Ebx', DWORD), 153 | ('Edx', DWORD), 154 | ('Ecx', DWORD), 155 | ('Eax', DWORD), 156 | ('Ebp', DWORD), 157 | ('Eip', DWORD), 158 | ('SegCs', DWORD), 159 | ('EFlags', DWORD), 160 | ('Esp', DWORD), 161 | ('SegSs', DWORD), 162 | ('ExtendedRegisters', BYTE * 512), 163 | ] 164 | assert sizeof(_CONTEXT) == 716, sizeof(_CONTEXT) 165 | assert alignment(_CONTEXT) == 4, alignment(_CONTEXT) 166 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 498 167 | class N12_SYSTEM_INFO4DOLLAR_37E(Union): 168 | pass 169 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 500 170 | class N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E(Structure): 171 | pass 172 | N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E._fields_ = [ 173 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 500 174 | ('wProcessorArchitecture', WORD), 175 | ('wReserved', WORD), 176 | ] 177 | assert sizeof(N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E) == 4, sizeof(N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E) 178 | assert alignment(N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E) == 2, alignment(N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E) 179 | N12_SYSTEM_INFO4DOLLAR_37E._fields_ = [ 180 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 498 181 | ('dwOemId', DWORD), 182 | # Unnamed field renamed to '_' 183 | ('_', N12_SYSTEM_INFO4DOLLAR_374DOLLAR_38E), 184 | ] 185 | assert sizeof(N12_SYSTEM_INFO4DOLLAR_37E) == 4, sizeof(N12_SYSTEM_INFO4DOLLAR_37E) 186 | assert alignment(N12_SYSTEM_INFO4DOLLAR_37E) == 4, alignment(N12_SYSTEM_INFO4DOLLAR_37E) 187 | LPVOID = c_void_p 188 | _SYSTEM_INFO._fields_ = [ 189 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 497 190 | # Unnamed field renamed to '_' 191 | ('_', N12_SYSTEM_INFO4DOLLAR_37E), 192 | ('dwPageSize', DWORD), 193 | ('lpMinimumApplicationAddress', LPVOID), 194 | ('lpMaximumApplicationAddress', LPVOID), 195 | ('dwActiveProcessorMask', DWORD), 196 | ('dwNumberOfProcessors', DWORD), 197 | ('dwProcessorType', DWORD), 198 | ('dwAllocationGranularity', DWORD), 199 | ('wProcessorLevel', WORD), 200 | ('wProcessorRevision', WORD), 201 | ] 202 | assert sizeof(_SYSTEM_INFO) == 36, sizeof(_SYSTEM_INFO) 203 | assert alignment(_SYSTEM_INFO) == 4, alignment(_SYSTEM_INFO) 204 | CHAR = c_char 205 | LPSTR = POINTER(CHAR) 206 | LPBYTE = POINTER(BYTE) 207 | _STARTUPINFOA._fields_ = [ 208 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 3774 209 | ('cb', DWORD), 210 | ('lpReserved', LPSTR), 211 | ('lpDesktop', LPSTR), 212 | ('lpTitle', LPSTR), 213 | ('dwX', DWORD), 214 | ('dwY', DWORD), 215 | ('dwXSize', DWORD), 216 | ('dwYSize', DWORD), 217 | ('dwXCountChars', DWORD), 218 | ('dwYCountChars', DWORD), 219 | ('dwFillAttribute', DWORD), 220 | ('dwFlags', DWORD), 221 | ('wShowWindow', WORD), 222 | ('cbReserved2', WORD), 223 | ('lpReserved2', LPBYTE), 224 | ('hStdInput', HANDLE), 225 | ('hStdOutput', HANDLE), 226 | ('hStdError', HANDLE), 227 | ] 228 | assert sizeof(_STARTUPINFOA) == 68, sizeof(_STARTUPINFOA) 229 | assert alignment(_STARTUPINFOA) == 4, alignment(_STARTUPINFOA) 230 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 701 231 | class N12_DEBUG_EVENT4DOLLAR_39E(Union): 232 | pass 233 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 640 234 | class _EXCEPTION_DEBUG_INFO(Structure): 235 | pass 236 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3101 237 | class _EXCEPTION_RECORD(Structure): 238 | pass 239 | _EXCEPTION_RECORD._fields_ = [ 240 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3101 241 | ('ExceptionCode', DWORD), 242 | ('ExceptionFlags', DWORD), 243 | ('ExceptionRecord', POINTER(_EXCEPTION_RECORD)), 244 | ('ExceptionAddress', PVOID), 245 | ('NumberParameters', DWORD), 246 | ('ExceptionInformation', UINT_PTR * 15), 247 | ] 248 | assert sizeof(_EXCEPTION_RECORD) == 80, sizeof(_EXCEPTION_RECORD) 249 | assert alignment(_EXCEPTION_RECORD) == 4, alignment(_EXCEPTION_RECORD) 250 | EXCEPTION_RECORD = _EXCEPTION_RECORD 251 | _EXCEPTION_DEBUG_INFO._fields_ = [ 252 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 640 253 | ('ExceptionRecord', EXCEPTION_RECORD), 254 | ('dwFirstChance', DWORD), 255 | ] 256 | assert sizeof(_EXCEPTION_DEBUG_INFO) == 84, sizeof(_EXCEPTION_DEBUG_INFO) 257 | assert alignment(_EXCEPTION_DEBUG_INFO) == 4, alignment(_EXCEPTION_DEBUG_INFO) 258 | EXCEPTION_DEBUG_INFO = _EXCEPTION_DEBUG_INFO 259 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 645 260 | class _CREATE_THREAD_DEBUG_INFO(Structure): 261 | pass 262 | 263 | # macos compatability. 264 | try: 265 | PTHREAD_START_ROUTINE = WINFUNCTYPE(DWORD, c_void_p) 266 | except: 267 | PTHREAD_START_ROUTINE = CFUNCTYPE(DWORD, c_void_p) 268 | 269 | LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE 270 | _CREATE_THREAD_DEBUG_INFO._fields_ = [ 271 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 645 272 | ('hThread', HANDLE), 273 | ('lpThreadLocalBase', LPVOID), 274 | ('lpStartAddress', LPTHREAD_START_ROUTINE), 275 | ] 276 | assert sizeof(_CREATE_THREAD_DEBUG_INFO) == 12, sizeof(_CREATE_THREAD_DEBUG_INFO) 277 | assert alignment(_CREATE_THREAD_DEBUG_INFO) == 4, alignment(_CREATE_THREAD_DEBUG_INFO) 278 | CREATE_THREAD_DEBUG_INFO = _CREATE_THREAD_DEBUG_INFO 279 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 651 280 | class _CREATE_PROCESS_DEBUG_INFO(Structure): 281 | pass 282 | _CREATE_PROCESS_DEBUG_INFO._fields_ = [ 283 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 651 284 | ('hFile', HANDLE), 285 | ('hProcess', HANDLE), 286 | ('hThread', HANDLE), 287 | ('lpBaseOfImage', LPVOID), 288 | ('dwDebugInfoFileOffset', DWORD), 289 | ('nDebugInfoSize', DWORD), 290 | ('lpThreadLocalBase', LPVOID), 291 | ('lpStartAddress', LPTHREAD_START_ROUTINE), 292 | ('lpImageName', LPVOID), 293 | ('fUnicode', WORD), 294 | ] 295 | assert sizeof(_CREATE_PROCESS_DEBUG_INFO) == 40, sizeof(_CREATE_PROCESS_DEBUG_INFO) 296 | assert alignment(_CREATE_PROCESS_DEBUG_INFO) == 4, alignment(_CREATE_PROCESS_DEBUG_INFO) 297 | CREATE_PROCESS_DEBUG_INFO = _CREATE_PROCESS_DEBUG_INFO 298 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 664 299 | class _EXIT_THREAD_DEBUG_INFO(Structure): 300 | pass 301 | _EXIT_THREAD_DEBUG_INFO._fields_ = [ 302 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 664 303 | ('dwExitCode', DWORD), 304 | ] 305 | assert sizeof(_EXIT_THREAD_DEBUG_INFO) == 4, sizeof(_EXIT_THREAD_DEBUG_INFO) 306 | assert alignment(_EXIT_THREAD_DEBUG_INFO) == 4, alignment(_EXIT_THREAD_DEBUG_INFO) 307 | EXIT_THREAD_DEBUG_INFO = _EXIT_THREAD_DEBUG_INFO 308 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 668 309 | class _EXIT_PROCESS_DEBUG_INFO(Structure): 310 | pass 311 | _EXIT_PROCESS_DEBUG_INFO._fields_ = [ 312 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 668 313 | ('dwExitCode', DWORD), 314 | ] 315 | assert sizeof(_EXIT_PROCESS_DEBUG_INFO) == 4, sizeof(_EXIT_PROCESS_DEBUG_INFO) 316 | assert alignment(_EXIT_PROCESS_DEBUG_INFO) == 4, alignment(_EXIT_PROCESS_DEBUG_INFO) 317 | EXIT_PROCESS_DEBUG_INFO = _EXIT_PROCESS_DEBUG_INFO 318 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 672 319 | class _LOAD_DLL_DEBUG_INFO(Structure): 320 | pass 321 | _LOAD_DLL_DEBUG_INFO._fields_ = [ 322 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 672 323 | ('hFile', HANDLE), 324 | ('lpBaseOfDll', LPVOID), 325 | ('dwDebugInfoFileOffset', DWORD), 326 | ('nDebugInfoSize', DWORD), 327 | ('lpImageName', LPVOID), 328 | ('fUnicode', WORD), 329 | ] 330 | assert sizeof(_LOAD_DLL_DEBUG_INFO) == 24, sizeof(_LOAD_DLL_DEBUG_INFO) 331 | assert alignment(_LOAD_DLL_DEBUG_INFO) == 4, alignment(_LOAD_DLL_DEBUG_INFO) 332 | LOAD_DLL_DEBUG_INFO = _LOAD_DLL_DEBUG_INFO 333 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 681 334 | class _UNLOAD_DLL_DEBUG_INFO(Structure): 335 | pass 336 | _UNLOAD_DLL_DEBUG_INFO._fields_ = [ 337 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 681 338 | ('lpBaseOfDll', LPVOID), 339 | ] 340 | assert sizeof(_UNLOAD_DLL_DEBUG_INFO) == 4, sizeof(_UNLOAD_DLL_DEBUG_INFO) 341 | assert alignment(_UNLOAD_DLL_DEBUG_INFO) == 4, alignment(_UNLOAD_DLL_DEBUG_INFO) 342 | UNLOAD_DLL_DEBUG_INFO = _UNLOAD_DLL_DEBUG_INFO 343 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 685 344 | class _OUTPUT_DEBUG_STRING_INFO(Structure): 345 | pass 346 | _OUTPUT_DEBUG_STRING_INFO._fields_ = [ 347 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 685 348 | ('lpDebugStringData', LPSTR), 349 | ('fUnicode', WORD), 350 | ('nDebugStringLength', WORD), 351 | ] 352 | assert sizeof(_OUTPUT_DEBUG_STRING_INFO) == 8, sizeof(_OUTPUT_DEBUG_STRING_INFO) 353 | assert alignment(_OUTPUT_DEBUG_STRING_INFO) == 4, alignment(_OUTPUT_DEBUG_STRING_INFO) 354 | OUTPUT_DEBUG_STRING_INFO = _OUTPUT_DEBUG_STRING_INFO 355 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 691 356 | class _RIP_INFO(Structure): 357 | pass 358 | _RIP_INFO._fields_ = [ 359 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 691 360 | ('dwError', DWORD), 361 | ('dwType', DWORD), 362 | ] 363 | assert sizeof(_RIP_INFO) == 8, sizeof(_RIP_INFO) 364 | assert alignment(_RIP_INFO) == 4, alignment(_RIP_INFO) 365 | RIP_INFO = _RIP_INFO 366 | N12_DEBUG_EVENT4DOLLAR_39E._fields_ = [ 367 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 701 368 | ('Exception', EXCEPTION_DEBUG_INFO), 369 | ('CreateThread', CREATE_THREAD_DEBUG_INFO), 370 | ('CreateProcessInfo', CREATE_PROCESS_DEBUG_INFO), 371 | ('ExitThread', EXIT_THREAD_DEBUG_INFO), 372 | ('ExitProcess', EXIT_PROCESS_DEBUG_INFO), 373 | ('LoadDll', LOAD_DLL_DEBUG_INFO), 374 | ('UnloadDll', UNLOAD_DLL_DEBUG_INFO), 375 | ('DebugString', OUTPUT_DEBUG_STRING_INFO), 376 | ('RipInfo', RIP_INFO), 377 | ] 378 | assert sizeof(N12_DEBUG_EVENT4DOLLAR_39E) == 84, sizeof(N12_DEBUG_EVENT4DOLLAR_39E) 379 | assert alignment(N12_DEBUG_EVENT4DOLLAR_39E) == 4, alignment(N12_DEBUG_EVENT4DOLLAR_39E) 380 | _DEBUG_EVENT._fields_ = [ 381 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 697 382 | ('dwDebugEventCode', DWORD), 383 | ('dwProcessId', DWORD), 384 | ('dwThreadId', DWORD), 385 | ('u', N12_DEBUG_EVENT4DOLLAR_39E), 386 | ] 387 | assert sizeof(_DEBUG_EVENT) == 96, sizeof(_DEBUG_EVENT) 388 | assert alignment(_DEBUG_EVENT) == 4, alignment(_DEBUG_EVENT) 389 | LONG = c_long 390 | _LUID._fields_ = [ 391 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 394 392 | ('LowPart', DWORD), 393 | ('HighPart', LONG), 394 | ] 395 | assert sizeof(_LUID) == 8, sizeof(_LUID) 396 | assert alignment(_LUID) == 4, alignment(_LUID) 397 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3241 398 | class _LUID_AND_ATTRIBUTES(Structure): 399 | pass 400 | _LUID_AND_ATTRIBUTES._fields_ = [ 401 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 3241 402 | ('Luid', LUID), 403 | ('Attributes', DWORD), 404 | ] 405 | assert sizeof(_LUID_AND_ATTRIBUTES) == 12, sizeof(_LUID_AND_ATTRIBUTES) 406 | assert alignment(_LUID_AND_ATTRIBUTES) == 4, alignment(_LUID_AND_ATTRIBUTES) 407 | LUID_AND_ATTRIBUTES = _LUID_AND_ATTRIBUTES 408 | _TOKEN_PRIVILEGES._fields_ = [ 409 | # C:/PROGRA~1/gccxml/bin/Vc6/Include/winnt.h 4188 410 | ('PrivilegeCount', DWORD), 411 | ('Privileges', LUID_AND_ATTRIBUTES * 1), 412 | ] 413 | assert sizeof(_TOKEN_PRIVILEGES) == 16, sizeof(_TOKEN_PRIVILEGES) 414 | assert alignment(_TOKEN_PRIVILEGES) == 4, alignment(_TOKEN_PRIVILEGES) 415 | _PROCESS_INFORMATION._fields_ = [ 416 | # C:/PROGRA~1/MICROS~2/VC98/Include/winbase.h 229 417 | ('hProcess', HANDLE), 418 | ('hThread', HANDLE), 419 | ('dwProcessId', DWORD), 420 | ('dwThreadId', DWORD), 421 | ] 422 | assert sizeof(_PROCESS_INFORMATION) == 16, sizeof(_PROCESS_INFORMATION) 423 | assert alignment(_PROCESS_INFORMATION) == 4, alignment(_PROCESS_INFORMATION) 424 | -------------------------------------------------------------------------------- /pydbg.py: -------------------------------------------------------------------------------- 1 | #!c:\python\python.exe 2 | 3 | # 4 | # PyDBG 5 | # Copyright (C) 2006 Pedram Amini 6 | # 7 | # $Id: pydbg.py 253 2011-01-24 19:13:57Z my.name.is.sober $ 8 | # 9 | # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 10 | # License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later 11 | # version. 12 | # 13 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 14 | # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along with this program; if not, write to the Free 17 | # Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | # 19 | 20 | ''' 21 | @author: Pedram Amini 22 | @license: GNU General Public License 2.0 or later 23 | @contact: pedram.amini@gmail.com 24 | @organization: www.openrce.org 25 | ''' 26 | 27 | import os.path 28 | import sys 29 | import copy 30 | import signal 31 | import struct 32 | import pydasm 33 | import socket 34 | 35 | from my_ctypes import * 36 | from defines import * 37 | from windows_h import * 38 | 39 | # macos compatability. 40 | try: 41 | kernel32 = windll.kernel32 42 | advapi32 = windll.advapi32 43 | ntdll = windll.ntdll 44 | iphlpapi = windll.iphlpapi 45 | except: 46 | kernel32 = CDLL(os.path.join(os.path.dirname(__file__), "libmacdll.dylib")) 47 | advapi32 = kernel32 48 | 49 | from breakpoint import * 50 | from hardware_breakpoint import * 51 | from memory_breakpoint import * 52 | from memory_snapshot_block import * 53 | from memory_snapshot_context import * 54 | from pdx import * 55 | from system_dll import * 56 | 57 | class pydbg: 58 | ''' 59 | This class implements standard low leven functionality including: 60 | - The load() / attach() routines. 61 | - The main debug event loop. 62 | - Convenience wrappers for commonly used Windows API. 63 | - Single step toggling routine. 64 | - Win32 error handler wrapped around PDX. 65 | - Base exception / event handler routines which are meant to be overridden. 66 | 67 | Higher level functionality is also implemented including: 68 | - Register manipulation. 69 | - Soft (INT 3) breakpoints. 70 | - Memory breakpoints (page permissions). 71 | - Hardware breakpoints. 72 | - Exception / event handling call backs. 73 | - Pydasm (libdasm) disassembly wrapper. 74 | - Process memory snapshotting and restoring. 75 | - Endian manipulation routines. 76 | - Debugger hiding. 77 | - Function resolution. 78 | - "Intelligent" memory derefencing. 79 | - Stack/SEH unwinding. 80 | - Etc... 81 | ''' 82 | 83 | STRING_EXPLORATON_BUF_SIZE = 256 84 | STRING_EXPLORATION_MIN_LENGTH = 2 85 | 86 | #################################################################################################################### 87 | def __init__ (self, ff=True, cs=False): 88 | ''' 89 | Set the default attributes. See the source if you want to modify the default creation values. 90 | 91 | @type ff: Boolean 92 | @param ff: (Optional, Def=True) Flag controlling whether or not pydbg attaches to forked processes 93 | @type cs: Boolean 94 | @param cs: (Optional, Def=False) Flag controlling whether or not pydbg is in client/server (socket) mode 95 | ''' 96 | 97 | # private variables, internal use only: 98 | self._restore_breakpoint = None # breakpoint to restore 99 | self._guarded_pages = set() # specific pages we set PAGE_GUARD on 100 | self._guards_active = True # flag specifying whether or not guard pages are active 101 | 102 | self.page_size = 0 # memory page size (dynamically resolved at run-time) 103 | self.pid = 0 # debuggee's process id 104 | self.h_process = None # debuggee's process handle 105 | self.h_thread = None # handle to current debuggee thread 106 | self.debugger_active = True # flag controlling the main debugger event handling loop 107 | self.follow_forks = ff # flag controlling whether or not pydbg attaches to forked processes 108 | self.client_server = cs # flag controlling whether or not pydbg is in client/server mode 109 | self.callbacks = {} # exception callback handler dictionary 110 | self.system_dlls = [] # list of loaded system dlls 111 | self.dirty = False # flag specifying that the memory space of the debuggee was modified 112 | self.system_break = None # the address at which initial and forced breakpoints occur at 113 | self.peb = None # process environment block address 114 | self.tebs = {} # dictionary of thread IDs to thread environment block addresses 115 | 116 | # internal variables specific to the last triggered exception. 117 | self.context = None # thread context of offending thread 118 | self.dbg = None # DEBUG_EVENT 119 | self.exception_address = None # from dbg.u.Exception.ExceptionRecord.ExceptionAddress 120 | self.write_violation = None # from dbg.u.Exception.ExceptionRecord.ExceptionInformation[0] 121 | self.violation_address = None # from dbg.u.Exception.ExceptionRecord.ExceptionInformation[1] 122 | self.exception_code = None # from dbg.u.Exception.ExceptionRecord.ExceptionCode 123 | 124 | self.breakpoints = {} # internal breakpoint dictionary, keyed by address 125 | self.memory_breakpoints = {} # internal memory breakpoint dictionary, keyed by base address 126 | self.hardware_breakpoints = {} # internal hardware breakpoint array, indexed by slot (0-3 inclusive) 127 | self.memory_snapshot_blocks = [] # list of memory blocks at time of memory snapshot 128 | self.memory_snapshot_contexts = [] # list of threads contexts at time of memory snapshot 129 | 130 | self.first_breakpoint = True # this flag gets disabled once the windows initial break is handled 131 | self.memory_breakpoint_hit = 0 # address of hit memory breakpoint or zero on miss 132 | # designates whether or not the violation was in reaction to a memory 133 | # breakpoint hit or other unrelated event. 134 | self.hardware_breakpoint_hit = None # hardware breakpoint on hit or None on miss 135 | # designates whether or not the single step event was in reaction to 136 | # a hardware breakpoint hit or other unrelated event. 137 | 138 | self.instruction = None # pydasm instruction object, propagated by self.disasm() 139 | self.mnemonic = None # pydasm decoded instruction mnemonic, propagated by self.disasm() 140 | self.op1 = None # pydasm decoded 1st operand, propagated by self.disasm() 141 | self.op2 = None # pydasm decoded 2nd operand, propagated by self.disasm() 142 | self.op3 = None # pydasm decoded 3rd operand, propagated by self.disasm() 143 | 144 | # control debug/error logging. 145 | self._log = lambda msg: None #sys.stderr.write("PDBG_LOG> " + msg + "\n") 146 | self._err = lambda msg: sys.stderr.write("PDBG_ERR> " + msg + "\n") 147 | 148 | # determine the system page size. 149 | system_info = SYSTEM_INFO() 150 | kernel32.GetSystemInfo(byref(system_info)) 151 | self.page_size = system_info.dwPageSize 152 | 153 | # determine the system DbgBreakPoint address. this is the address at which initial and forced breaks happen. 154 | # XXX - need to look into fixing this for pydbg client/server. 155 | self.system_break = self.func_resolve("ntdll.dll", "DbgBreakPoint") 156 | 157 | self._log("system page size is %d" % self.page_size) 158 | 159 | 160 | #################################################################################################################### 161 | def addr_to_dll (self, address): 162 | ''' 163 | Return the system DLL that contains the address specified. 164 | 165 | @type address: DWORD 166 | @param address: Address to search system DLL ranges for 167 | 168 | @rtype: system_dll 169 | @return: System DLL that contains the address specified or None if not found. 170 | ''' 171 | 172 | for dll in self.system_dlls: 173 | if dll.base < address < dll.base + dll.size: 174 | return dll 175 | 176 | return None 177 | 178 | 179 | #################################################################################################################### 180 | def addr_to_module (self, address): 181 | ''' 182 | Return the MODULEENTRY32 structure for the module that contains the address specified. 183 | 184 | @type address: DWORD 185 | @param address: Address to search loaded module ranges for 186 | 187 | @rtype: MODULEENTRY32 188 | @return: MODULEENTRY32 strucutre that contains the address specified or None if not found. 189 | ''' 190 | 191 | found = None 192 | 193 | for module in self.iterate_modules(): 194 | if module.modBaseAddr < address < module.modBaseAddr + module.modBaseSize: 195 | # we have to make a copy of the 'module' since it is an iterator and will be blown away. 196 | # the reason we can't "break" out of the loop is because there will be a handle leak. 197 | # and we can't use enumerate_modules() because we need the entire module structure. 198 | # so there... 199 | found = copy.copy(module) 200 | 201 | return found 202 | 203 | 204 | #################################################################################################################### 205 | def attach (self, pid): 206 | ''' 207 | Attach to the specified process by PID. Saves a process handle in self.h_process and prevents debuggee from 208 | exiting on debugger quit. 209 | 210 | @type pid: Integer 211 | @param pid: Process ID to attach to 212 | 213 | @raise pdx: An exception is raised on failure. 214 | @rtype: pydbg 215 | @return: Self 216 | ''' 217 | 218 | self._log("attaching to pid %d" % pid) 219 | 220 | # obtain necessary debug privileges. 221 | self.get_debug_privileges() 222 | 223 | self.pid = pid 224 | self.open_process(pid) 225 | 226 | self.debug_active_process(pid) 227 | 228 | # allow detaching on systems that support it. 229 | try: 230 | self.debug_set_process_kill_on_exit(False) 231 | except: 232 | pass 233 | 234 | # enumerate the TEBs and add them to the internal dictionary. 235 | for thread_id in self.enumerate_threads(): 236 | thread_handle = self.open_thread(thread_id) 237 | thread_context = self.get_thread_context(thread_handle) 238 | selector_entry = LDT_ENTRY() 239 | 240 | if not kernel32.GetThreadSelectorEntry(thread_handle, thread_context.SegFs, byref(selector_entry)): 241 | self.win32_error("GetThreadSelectorEntry()") 242 | 243 | self.close_handle(thread_handle) 244 | 245 | teb = selector_entry.BaseLow 246 | teb += (selector_entry.HighWord.Bits.BaseMid << 16) + (selector_entry.HighWord.Bits.BaseHi << 24) 247 | 248 | # add this TEB to the internal dictionary. 249 | self.tebs[thread_id] = teb 250 | 251 | # if the PEB has not been set yet, do so now. 252 | if not self.peb: 253 | self.peb = self.read_process_memory(teb + 0x30, 4) 254 | self.peb = struct.unpack("= address and address_to_check <= address + size: 512 | return address 513 | 514 | return False 515 | 516 | 517 | #################################################################################################################### 518 | def bp_set (self, address, description="", restore=True, handler=None): 519 | ''' 520 | Sets a breakpoint at the designated address. Register an EXCEPTION_BREAKPOINT callback handler to catch 521 | breakpoint events. If a list of addresses is submitted to this routine then the entire list of new breakpoints 522 | get the same description and restore. The optional "handler" parameter can be used to identify a function to 523 | specifically handle the specified bp, as opposed to the generic bp callback handler. The prototype of the 524 | callback routines is:: 525 | 526 | func (pydbg) 527 | return DBG_CONTINUE # or other continue status 528 | 529 | @see: bp_is_ours(), bp_del(), bp_del_all() 530 | 531 | @type address: DWORD or List 532 | @param address: Address or list of addresses to set breakpoint at 533 | @type description: String 534 | @param description: (Optional) Description to associate with this breakpoint 535 | @type restore: Bool 536 | @param restore: (Optional, def=True) Flag controlling whether or not to restore the breakpoint 537 | @type handler: Function Pointer 538 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 539 | 540 | @raise pdx: An exception is raised on failure. 541 | @rtype: pydbg 542 | @return: Self 543 | ''' 544 | 545 | # if a list of addresses to set breakpoints on from was supplied 546 | if type(address) is list: 547 | # pass each lone address to ourself (each one gets the same description / restore flag). 548 | for addr in address: 549 | self.bp_set(addr, description, restore, handler) 550 | 551 | return self.ret_self() 552 | 553 | self._log("bp_set(0x%08x)" % address) 554 | 555 | # ensure a breakpoint doesn't already exist at the target address. 556 | if not self.breakpoints.has_key(address): 557 | try: 558 | # save the original byte at the requested breakpoint address. 559 | original_byte = self.read_process_memory(address, 1) 560 | 561 | # write an int3 into the target process space. 562 | self.write_process_memory(address, "\xCC") 563 | self.set_attr("dirty", True) 564 | 565 | # add the breakpoint to the internal list. 566 | self.breakpoints[address] = breakpoint(address, original_byte, description, restore, handler) 567 | except: 568 | raise pdx("Failed setting breakpoint at %08x" % address) 569 | 570 | return self.ret_self() 571 | 572 | 573 | #################################################################################################################### 574 | def bp_set_hw (self, address, length, condition, description="", restore=True, handler=None): 575 | ''' 576 | Sets a hardware breakpoint at the designated address. Register an EXCEPTION_SINGLE_STEP callback handler to 577 | catch hardware breakpoint events. Setting hardware breakpoints requires the internal h_thread handle be set. 578 | This means that you can not set one outside the context of an debug event handler. If you want to set a hardware 579 | breakpoint as soon as you attach to or load a process, do so in the first chance breakpoint handler. 580 | 581 | For more information regarding the Intel x86 debug registers and hardware breakpoints see:: 582 | 583 | http://pdos.csail.mit.edu/6.828/2005/readings/ia32/IA32-3.pdf 584 | Section 15.2 585 | 586 | Alternatively, you can register a custom handler to handle hits on the specific hw breakpoint slot. 587 | 588 | *Warning: Setting hardware breakpoints during the first system breakpoint will be removed upon process 589 | continue. A better approach is to set a software breakpoint that when hit will set your hardware breakpoints. 590 | 591 | @note: Hardware breakpoints are handled globally throughout the entire process and not a single specific thread. 592 | @see: bp_del_hw(), bp_del_hw_all() 593 | 594 | @type address: DWORD 595 | @param address: Address to set hardware breakpoint at 596 | @type length: Integer (1, 2 or 4) 597 | @param length: Size of hardware breakpoint in bytes (byte, word or dword) 598 | @type condition: Integer (HW_ACCESS, HW_WRITE, HW_EXECUTE) 599 | @param condition: Condition to set the hardware breakpoint to activate on 600 | @type description: String 601 | @param description: (Optional) Description of breakpoint 602 | @type restore: Boolean 603 | @param restore: (Optional, def=True) Flag controlling whether or not to restore the breakpoint 604 | @type handler: Function Pointer 605 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 606 | 607 | @raise pdx: An exception is raised on failure. 608 | @rtype: pydbg 609 | @return: Self 610 | ''' 611 | 612 | self._log("bp_set_hw(%08x, %d, %s)" % (address, length, condition)) 613 | 614 | # instantiate a new hardware breakpoint object for the new bp to create. 615 | hw_bp = hardware_breakpoint(address, length, condition, description, restore, handler=handler) 616 | 617 | if length not in (1, 2, 4): 618 | raise pdx("invalid hw breakpoint length: %d." % length) 619 | 620 | # length -= 1 because the following codes are used for determining length: 621 | # 00 - 1 byte length 622 | # 01 - 2 byte length 623 | # 10 - undefined 624 | # 11 - 4 byte length 625 | length -= 1 626 | 627 | # condition table: 628 | # 00 - break on instruction execution only 629 | # 01 - break on data writes only 630 | # 10 - undefined 631 | # 11 - break on data reads or writes but not instruction fetches 632 | if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE): 633 | raise pdx("invalid hw breakpoint condition: %d" % condition) 634 | 635 | # check for any available hardware breakpoint slots. there doesn't appear to be any difference between local 636 | # and global as far as we are concerned on windows. 637 | # 638 | # bits 0, 2, 4, 6 for local (L0 - L3) 639 | # bits 1, 3, 5, 7 for global (G0 - G3) 640 | # 641 | # we could programatically search for an open slot in a given thread context with the following code: 642 | # 643 | # available = None 644 | # for slot in xrange(4): 645 | # if context.Dr7 & (1 << (slot * 2)) == 0: 646 | # available = slot 647 | # break 648 | # 649 | # but since we are doing global hardware breakpoints, we rely on ourself for tracking open slots. 650 | 651 | if not self.hardware_breakpoints.has_key(0): 652 | available = 0 653 | elif not self.hardware_breakpoints.has_key(1): 654 | available = 1 655 | elif not self.hardware_breakpoints.has_key(2): 656 | available = 2 657 | elif not self.hardware_breakpoints.has_key(3): 658 | available = 3 659 | else: 660 | raise pdx("no hw breakpoint slots available.") 661 | 662 | # activate the hardware breakpoint for all active threads. 663 | for thread_id in self.enumerate_threads(): 664 | context = self.get_thread_context(thread_id=thread_id) 665 | 666 | # mark available debug register as active (L0 - L3). 667 | context.Dr7 |= 1 << (available * 2) 668 | 669 | # save our breakpoint address to the available hw bp slot. 670 | if available == 0: context.Dr0 = address 671 | elif available == 1: context.Dr1 = address 672 | elif available == 2: context.Dr2 = address 673 | elif available == 3: context.Dr3 = address 674 | 675 | # set the condition (RW0 - RW3) field for the appropriate slot (bits 16/17, 20/21, 24,25, 28/29) 676 | context.Dr7 |= condition << ((available * 4) + 16) 677 | 678 | # set the length (LEN0-LEN3) field for the appropriate slot (bits 18/19, 22/23, 26/27, 30/31) 679 | context.Dr7 |= length << ((available * 4) + 18) 680 | 681 | # set the thread context. 682 | self.set_thread_context(context, thread_id=thread_id) 683 | 684 | # update the internal hardware breakpoint array at the used slot index. 685 | hw_bp.slot = available 686 | self.hardware_breakpoints[available] = hw_bp 687 | 688 | return self.ret_self() 689 | 690 | 691 | #################################################################################################################### 692 | def bp_set_mem (self, address, size, description="", handler=None): 693 | ''' 694 | Sets a memory breakpoint at the target address. This is implemented by changing the permissions of the page 695 | containing the address to PAGE_GUARD. To catch memory breakpoints you have to register the EXCEPTION_GUARD_PAGE 696 | callback. Within the callback handler check the internal pydbg variable self.memory_breakpoint_hit to 697 | determine if the violation was a result of a direct memory breakpoint hit or some unrelated event. 698 | Alternatively, you can register a custom handler to handle the memory breakpoint. Memory breakpoints are 699 | automatically restored via the internal single step handler. To remove a memory breakpoint, you must explicitly 700 | call bp_del_mem(). 701 | 702 | @see: bp_is_ours_mem(), bp_del_mem(), bp_del_mem_all() 703 | 704 | @type address: DWORD 705 | @param address: Starting address of the buffer to break on 706 | @type size: Integer 707 | @param size: Size of the buffer to break on 708 | @type description: String 709 | @param description: (Optional) Description to associate with this breakpoint 710 | @type handler: Function Pointer 711 | @param handler: (Optional, def=None) Optional handler to call for this bp instead of the default handler 712 | 713 | @raise pdx: An exception is raised on failure. 714 | @rtype: pydbg 715 | @return: Self 716 | ''' 717 | 718 | self._log("bp_set_mem() buffer range is %08x - %08x" % (address, address + size)) 719 | 720 | # ensure the target address doesn't already sit in a memory breakpoint range: 721 | if self.bp_is_ours_mem(address): 722 | self._log("a memory breakpoint spanning %08x already exists" % address) 723 | return self.ret_self() 724 | 725 | # determine the base address of the page containing the starting point of our buffer. 726 | try: 727 | mbi = self.virtual_query(address) 728 | except: 729 | raise pdx("bp_set_mem(): failed querying address: %08x" % address) 730 | 731 | self._log("buffer starting at %08x sits on page starting at %08x" % (address, mbi.BaseAddress)) 732 | 733 | # individually change the page permissions for each page our buffer spans. 734 | # why do we individually set the page permissions of each page as opposed to a range of pages? because empirical 735 | # testing shows that when you set a PAGE_GUARD on a range of pages, if any of those pages are accessed, then 736 | # the PAGE_GUARD attribute is dropped for the entire range of pages that was originally modified. this is 737 | # undesirable for our purposes when it comes to the ease of restoring hit memory breakpoints. 738 | current_page = mbi.BaseAddress 739 | 740 | while current_page <= address + size: 741 | self._log("changing page permissions on %08x" % current_page) 742 | 743 | # keep track of explicitly guarded pages, to differentiate from pages guarded by the debuggee / OS. 744 | self._guarded_pages.add(current_page) 745 | self.virtual_protect(current_page, 1, mbi.Protect | PAGE_GUARD) 746 | 747 | current_page += self.page_size 748 | 749 | # add the breakpoint to the internal list. 750 | self.memory_breakpoints[address] = memory_breakpoint(address, size, mbi, description, handler) 751 | 752 | return self.ret_self() 753 | 754 | 755 | #################################################################################################################### 756 | def close_handle (self, handle): 757 | ''' 758 | Convenience wraper around kernel32.CloseHandle() 759 | 760 | @type handle: Handle 761 | @param handle: Handle to close 762 | 763 | @rtype: Bool 764 | @return: Return value from CloseHandle(). 765 | ''' 766 | 767 | return kernel32.CloseHandle(handle) 768 | 769 | 770 | #################################################################################################################### 771 | def dbg_print_all_debug_registers (self): 772 | ''' 773 | *** DEBUG ROUTINE *** 774 | 775 | This is a debugging routine that was used when debugging hardware breakpoints. It was too useful to be removed 776 | from the release code. 777 | ''' 778 | 779 | # ensure we have an up to date context for the current thread. 780 | context = self.get_thread_context(self.h_thread) 781 | 782 | print "eip = %08x" % context.Eip 783 | print "Dr0 = %08x" % context.Dr0 784 | print "Dr1 = %08x" % context.Dr1 785 | print "Dr2 = %08x" % context.Dr2 786 | print "Dr3 = %08x" % context.Dr3 787 | print "Dr7 = %s" % self.to_binary(context.Dr7) 788 | print " 10987654321098765432109876543210" 789 | print " 332222222222111111111" 790 | 791 | 792 | #################################################################################################################### 793 | def dbg_print_all_guarded_pages (self): 794 | ''' 795 | *** DEBUG ROUTINE *** 796 | 797 | This is a debugging routine that was used when debugging memory breakpoints. It was too useful to be removed 798 | from the release code. 799 | ''' 800 | 801 | cursor = 0 802 | 803 | # scan through the entire memory range. 804 | while cursor < 0xFFFFFFFF: 805 | try: 806 | mbi = self.virtual_query(cursor) 807 | except: 808 | break 809 | 810 | if mbi.Protect & PAGE_GUARD: 811 | address = mbi.BaseAddress 812 | print "PAGE GUARD on %08x" % mbi.BaseAddress 813 | 814 | while 1: 815 | address += self.page_size 816 | tmp_mbi = self.virtual_query(address) 817 | 818 | if not tmp_mbi.Protect & PAGE_GUARD: 819 | break 820 | 821 | print "PAGE GUARD on %08x" % address 822 | 823 | cursor += mbi.RegionSize 824 | 825 | 826 | #################################################################################################################### 827 | def debug_active_process (self, pid): 828 | ''' 829 | Convenience wrapper around GetLastError() and FormatMessage(). Returns the error code and formatted message 830 | associated with the last error. You probably do not want to call this directly, rather look at attach(). 831 | 832 | @type pid: Integer 833 | @param pid: Process ID to attach to 834 | 835 | @raise pdx: An exception is raised on failure. 836 | ''' 837 | 838 | if not kernel32.DebugActiveProcess(pid): 839 | raise pdx("DebugActiveProcess(%d)" % pid, True) 840 | 841 | 842 | #################################################################################################################### 843 | def debug_event_iteration (self): 844 | ''' 845 | Check for and process a debug event. 846 | ''' 847 | 848 | continue_status = DBG_CONTINUE 849 | dbg = DEBUG_EVENT() 850 | 851 | # wait for a debug event. 852 | if kernel32.WaitForDebugEvent(byref(dbg), 100): 853 | # grab various information with regards to the current exception. 854 | self.h_thread = self.open_thread(dbg.dwThreadId) 855 | self.context = self.get_thread_context(self.h_thread) 856 | self.dbg = dbg 857 | self.exception_address = dbg.u.Exception.ExceptionRecord.ExceptionAddress 858 | self.write_violation = dbg.u.Exception.ExceptionRecord.ExceptionInformation[0] 859 | self.violation_address = dbg.u.Exception.ExceptionRecord.ExceptionInformation[1] 860 | self.exception_code = dbg.u.Exception.ExceptionRecord.ExceptionCode 861 | 862 | if dbg.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT: 863 | continue_status = self.event_handler_create_process() 864 | 865 | elif dbg.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT: 866 | continue_status = self.event_handler_create_thread() 867 | 868 | elif dbg.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT: 869 | continue_status = self.event_handler_exit_process() 870 | 871 | elif dbg.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT: 872 | continue_status = self.event_handler_exit_thread() 873 | 874 | elif dbg.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT: 875 | continue_status = self.event_handler_load_dll() 876 | 877 | elif dbg.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT: 878 | continue_status = self.event_handler_unload_dll() 879 | 880 | # an exception was caught. 881 | elif dbg.dwDebugEventCode == EXCEPTION_DEBUG_EVENT: 882 | ec = dbg.u.Exception.ExceptionRecord.ExceptionCode 883 | 884 | self._log("debug_event_loop() exception: %08x" % ec) 885 | 886 | # call the internal handler for the exception event that just occured. 887 | if ec == EXCEPTION_ACCESS_VIOLATION: 888 | continue_status = self.exception_handler_access_violation() 889 | elif ec == EXCEPTION_BREAKPOINT: 890 | continue_status = self.exception_handler_breakpoint() 891 | elif ec == EXCEPTION_GUARD_PAGE: 892 | continue_status = self.exception_handler_guard_page() 893 | elif ec == EXCEPTION_SINGLE_STEP: 894 | continue_status = self.exception_handler_single_step() 895 | # generic callback support. 896 | elif self.callbacks.has_key(ec): 897 | continue_status = self.callbacks[ec](self) 898 | # unhandled exception. 899 | else: 900 | self._log("TID:%04x caused an unhandled exception (%08x) at %08x" % (self.dbg.dwThreadId, ec, self.exception_address)) 901 | continue_status = DBG_EXCEPTION_NOT_HANDLED 902 | 903 | # if the memory space of the debuggee was tainted, flush the instruction cache. 904 | # from MSDN: Applications should call FlushInstructionCache if they generate or modify code in memory. 905 | # The CPU cannot detect the change, and may execute the old code it cached. 906 | if self.dirty: 907 | kernel32.FlushInstructionCache(self.h_process, 0, 0) 908 | 909 | # close the opened thread handle and resume executing the thread that triggered the debug event. 910 | self.close_handle(self.h_thread) 911 | kernel32.ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, continue_status) 912 | 913 | 914 | #################################################################################################################### 915 | def debug_event_loop (self): 916 | ''' 917 | Enter the infinite debug event handling loop. This is the main loop of the debugger and is responsible for 918 | catching debug events and exceptions and dispatching them appropriately. This routine will check for and call 919 | the USER_CALLBACK_DEBUG_EVENT callback on each loop iteration. run() is an alias for this routine. 920 | 921 | @see: run() 922 | 923 | @raise pdx: An exception is raised on any exceptional conditions, such as debugger being interrupted or 924 | debuggee quiting. 925 | ''' 926 | 927 | while self.debugger_active: 928 | # don't let the user interrupt us in the midst of handling a debug event. 929 | try: 930 | def_sigint_handler = None 931 | def_sigint_handler = signal.signal(signal.SIGINT, self.sigint_handler) 932 | except: 933 | pass 934 | 935 | # if a user callback was specified, call it. 936 | if self.callbacks.has_key(USER_CALLBACK_DEBUG_EVENT): 937 | # user callbacks do not / should not access debugger or contextual information. 938 | self.dbg = self.context = None 939 | self.callbacks[USER_CALLBACK_DEBUG_EVENT](self) 940 | 941 | # iterate through a debug event. 942 | self.debug_event_iteration() 943 | 944 | # resume keyboard interruptability. 945 | if def_sigint_handler: 946 | signal.signal(signal.SIGINT, def_sigint_handler) 947 | 948 | # close the global process handle. 949 | self.close_handle(self.h_process) 950 | 951 | 952 | #################################################################################################################### 953 | def debug_set_process_kill_on_exit (self, kill_on_exit): 954 | ''' 955 | Convenience wrapper around DebugSetProcessKillOnExit(). 956 | 957 | @type kill_on_exit: Bool 958 | @param kill_on_exit: True to kill the process on debugger exit, False to let debuggee continue running. 959 | 960 | @raise pdx: An exception is raised on failure. 961 | ''' 962 | 963 | if not kernel32.DebugSetProcessKillOnExit(kill_on_exit): 964 | raise pdx("DebugActiveProcess(%s)" % kill_on_exit, True) 965 | 966 | 967 | #################################################################################################################### 968 | def detach (self): 969 | ''' 970 | Detach from debuggee. 971 | 972 | @raise pdx: An exception is raised on failure. 973 | @rtype: pydbg 974 | @return: Self 975 | ''' 976 | 977 | self._log("detaching from debuggee") 978 | 979 | # remove all software, memory and hardware breakpoints. 980 | self.bp_del_all() 981 | self.bp_del_mem_all() 982 | self.bp_del_hw_all() 983 | 984 | # try to detach from the target process if the API is available on the current platform. 985 | kernel32.DebugActiveProcessStop(self.pid) 986 | 987 | self.set_debugger_active(False) 988 | return self.ret_self() 989 | 990 | 991 | #################################################################################################################### 992 | def disasm (self, address): 993 | ''' 994 | Pydasm disassemble utility function wrapper. Stores the pydasm decoded instruction in self.instruction. 995 | 996 | @type address: DWORD 997 | @param address: Address to disassemble at 998 | 999 | @rtype: String 1000 | @return: Disassembled string. 1001 | ''' 1002 | 1003 | try: 1004 | data = self.read_process_memory(address, 32) 1005 | except: 1006 | return "Unable to disassemble at %08x" % address 1007 | 1008 | # update our internal member variables. 1009 | self.instruction = pydasm.get_instruction(data, pydasm.MODE_32) 1010 | 1011 | if not self.instruction: 1012 | self.mnemonic = "[UNKNOWN]" 1013 | self.op1 = "" 1014 | self.op2 = "" 1015 | self.op3 = "" 1016 | 1017 | return "[UNKNOWN]" 1018 | else: 1019 | self.mnemonic = pydasm.get_mnemonic_string(self.instruction, pydasm.FORMAT_INTEL) 1020 | self.op1 = pydasm.get_operand_string(self.instruction, 0, pydasm.FORMAT_INTEL, address) 1021 | self.op2 = pydasm.get_operand_string(self.instruction, 1, pydasm.FORMAT_INTEL, address) 1022 | self.op3 = pydasm.get_operand_string(self.instruction, 2, pydasm.FORMAT_INTEL, address) 1023 | 1024 | # the rstrip() is for removing extraneous trailing whitespace that libdasm sometimes leaves. 1025 | return pydasm.get_instruction_string(self.instruction, pydasm.FORMAT_INTEL, address).rstrip(" ") 1026 | 1027 | 1028 | #################################################################################################################### 1029 | def disasm_around (self, address, num_inst=5): 1030 | ''' 1031 | Given a specified address this routine will return the list of 5 instructions before and after the instruction 1032 | at address (including the instruction at address, so 11 instructions in total). This is accomplished by grabbing 1033 | a larger chunk of data around the address than what is predicted as necessary and then disassembling forward. 1034 | If during the forward disassembly the requested address lines up with the start of an instruction, then the 1035 | assumption is made that the forward disassembly self corrected itself and the instruction set is returned. If 1036 | we are unable to align with the original address, then we modify our data slice and try again until we do. 1037 | 1038 | @type address: DWORD 1039 | @param address: Address to disassemble around 1040 | @type num_inst: Integer 1041 | @param num_inst: (Optional, Def=5) Number of instructions to disassemble up/down from address 1042 | 1043 | @rtype: List 1044 | @return: List of tuples (address, disassembly) of instructions around the specified address. 1045 | ''' 1046 | 1047 | if num_inst == 0: 1048 | return [(address, self.disasm(address))] 1049 | 1050 | if num_inst < 0 or not int == type(num_inst): 1051 | self._err("disasm_around called with an invalid window size. reurning error value") 1052 | return [(address, "invalid window size supplied")] 1053 | 1054 | # grab a safe window size of bytes. 1055 | window_size = (num_inst * 64) / 5 1056 | 1057 | # grab a window of bytes before and after the requested address. 1058 | try: 1059 | data = self.read_process_memory(address - window_size, window_size * 2) 1060 | except: 1061 | return [(address, "Unable to disassemble")] 1062 | 1063 | # the rstrip() is for removing extraneous trailing whitespace that libdasm sometimes leaves. 1064 | i = pydasm.get_instruction(data[window_size:], pydasm.MODE_32) 1065 | disassembly = pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, address).rstrip(" ") 1066 | complete = False 1067 | start_byte = 0 1068 | 1069 | # loop until we retrieve a set of instructions that align to the requested address. 1070 | while not complete: 1071 | instructions = [] 1072 | slice = data[start_byte:] 1073 | offset = 0 1074 | 1075 | # step through the bytes in the data slice. 1076 | while offset < len(slice): 1077 | i = pydasm.get_instruction(slice[offset:], pydasm.MODE_32) 1078 | 1079 | if not i: 1080 | break 1081 | 1082 | # calculate the actual address of the instruction at the current offset and grab the disassembly 1083 | addr = address - window_size + start_byte + offset 1084 | inst = pydasm.get_instruction_string(i, pydasm.FORMAT_INTEL, addr).rstrip(" ") 1085 | 1086 | # add the address / instruction pair to our list of tuples. 1087 | instructions.append((addr, inst)) 1088 | 1089 | # increment the offset into the data slice by the length of the current instruction. 1090 | offset += i.length 1091 | 1092 | # we're done processing a data slice. 1093 | # step through each addres / instruction tuple in our instruction list looking for an instruction alignment 1094 | # match. we do the match on address and the original disassembled instruction. 1095 | index_of_address = 0 1096 | for (addr, inst) in instructions: 1097 | if addr == address and inst == disassembly: 1098 | complete = True 1099 | break 1100 | 1101 | index_of_address += 1 1102 | 1103 | start_byte += 1 1104 | 1105 | return instructions[index_of_address-num_inst:index_of_address+num_inst+1] 1106 | 1107 | 1108 | #################################################################################################################### 1109 | def dump_context (self, context=None, stack_depth=5, print_dots=True): 1110 | ''' 1111 | Return an informational block of text describing the CPU context of the current thread. Information includes: 1112 | - Disassembly at current EIP 1113 | - Register values in hex, decimal and "smart" dereferenced 1114 | - ESP, ESP+4, ESP+8 ... values in hex, decimal and "smart" dereferenced 1115 | 1116 | @see: dump_context_list() 1117 | 1118 | @type context: Context 1119 | @param context: (Optional) Current thread context to examine 1120 | @type stack_depth: Integer 1121 | @param stack_depth: (Optional, def:5) Number of dwords to dereference off of the stack (not including ESP) 1122 | @type print_dots: Bool 1123 | @param print_dots: (Optional, def:True) Controls suppression of dot in place of non-printable 1124 | 1125 | @rtype: String 1126 | @return: Information about current thread context. 1127 | ''' 1128 | 1129 | # if the optional current thread context was not supplied, grab it for the current thread. 1130 | if not context: 1131 | context = self.context 1132 | 1133 | context_list = self.dump_context_list(context, stack_depth, print_dots) 1134 | 1135 | context_dump = "CONTEXT DUMP\n" 1136 | context_dump += " EIP: %08x %s\n" % (context.Eip, context_list["eip"]) 1137 | context_dump += " EAX: %08x (%10d) -> %s\n" % (context.Eax, context.Eax, context_list["eax"]) 1138 | context_dump += " EBX: %08x (%10d) -> %s\n" % (context.Ebx, context.Ebx, context_list["ebx"]) 1139 | context_dump += " ECX: %08x (%10d) -> %s\n" % (context.Ecx, context.Ecx, context_list["ecx"]) 1140 | context_dump += " EDX: %08x (%10d) -> %s\n" % (context.Edx, context.Edx, context_list["edx"]) 1141 | context_dump += " EDI: %08x (%10d) -> %s\n" % (context.Edi, context.Edi, context_list["edi"]) 1142 | context_dump += " ESI: %08x (%10d) -> %s\n" % (context.Esi, context.Esi, context_list["esi"]) 1143 | context_dump += " EBP: %08x (%10d) -> %s\n" % (context.Ebp, context.Ebp, context_list["ebp"]) 1144 | context_dump += " ESP: %08x (%10d) -> %s\n" % (context.Esp, context.Esp, context_list["esp"]) 1145 | 1146 | for offset in xrange(0, stack_depth + 1): 1147 | context_dump += " +%02x: %08x (%10d) -> %s\n" % \ 1148 | ( \ 1149 | offset * 4, \ 1150 | context_list["esp+%02x"%(offset*4)]["value"], \ 1151 | context_list["esp+%02x"%(offset*4)]["value"], \ 1152 | context_list["esp+%02x"%(offset*4)]["desc"] \ 1153 | ) 1154 | 1155 | return context_dump 1156 | 1157 | 1158 | #################################################################################################################### 1159 | def dump_context_list (self, context=None, stack_depth=5, print_dots=True, hex_dump=False): 1160 | ''' 1161 | Return an informational list of items describing the CPU context of the current thread. Information includes: 1162 | - Disassembly at current EIP 1163 | - Register values in hex, decimal and "smart" dereferenced 1164 | - ESP, ESP+4, ESP+8 ... values in hex, decimal and "smart" dereferenced 1165 | 1166 | @see: dump_context() 1167 | 1168 | @type context: Context 1169 | @param context: (Optional) Current thread context to examine 1170 | @type stack_depth: Integer 1171 | @param stack_depth: (Optional, def:5) Number of dwords to dereference off of the stack (not including ESP) 1172 | @type print_dots: Bool 1173 | @param print_dots: (Optional, def:True) Controls suppression of dot in place of non-printable 1174 | @type hex_dump: Bool 1175 | @param hex_dump: (Optional, def=False) Return a hex dump in the absense of string detection 1176 | 1177 | @rtype: Dictionary 1178 | @return: Dictionary of information about current thread context. 1179 | ''' 1180 | 1181 | # if the optional current thread context was not supplied, grab it for the current thread. 1182 | if not context: 1183 | context = self.context 1184 | 1185 | context_list = {} 1186 | 1187 | context_list["eip"] = self.disasm(context.Eip) 1188 | context_list["eax"] = self.smart_dereference(context.Eax, print_dots, hex_dump) 1189 | context_list["ebx"] = self.smart_dereference(context.Ebx, print_dots, hex_dump) 1190 | context_list["ecx"] = self.smart_dereference(context.Ecx, print_dots, hex_dump) 1191 | context_list["edx"] = self.smart_dereference(context.Edx, print_dots, hex_dump) 1192 | context_list["edi"] = self.smart_dereference(context.Edi, print_dots, hex_dump) 1193 | context_list["esi"] = self.smart_dereference(context.Esi, print_dots, hex_dump) 1194 | context_list["ebp"] = self.smart_dereference(context.Ebp, print_dots, hex_dump) 1195 | context_list["esp"] = self.smart_dereference(context.Esp, print_dots, hex_dump) 1196 | 1197 | for offset in xrange(0, stack_depth + 1): 1198 | try: 1199 | esp = self.flip_endian_dword(self.read_process_memory(context.Esp + offset * 4, 4)) 1200 | 1201 | context_list["esp+%02x"%(offset*4)] = {} 1202 | context_list["esp+%02x"%(offset*4)]["value"] = esp 1203 | context_list["esp+%02x"%(offset*4)]["desc"] = self.smart_dereference(esp, print_dots, hex_dump) 1204 | except: 1205 | context_list["esp+%02x"%(offset*4)] = {} 1206 | context_list["esp+%02x"%(offset*4)]["value"] = 0 1207 | context_list["esp+%02x"%(offset*4)]["desc"] = "[INVALID]" 1208 | 1209 | return context_list 1210 | 1211 | 1212 | ##################################################################################################################### 1213 | def enumerate_modules (self): 1214 | ''' 1215 | Using the CreateToolhelp32Snapshot() API enumerate and return the list of module name / base address tuples that 1216 | belong to the debuggee 1217 | 1218 | @see: iterate_modules() 1219 | 1220 | @rtype: List 1221 | @return: List of module name / base address tuples. 1222 | ''' 1223 | 1224 | self._log("enumerate_modules()") 1225 | 1226 | module = MODULEENTRY32() 1227 | module_list = [] 1228 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.pid) 1229 | 1230 | if snapshot == INVALID_HANDLE_VALUE: 1231 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, %d" % self.pid, True) 1232 | 1233 | # we *must* set the size of the structure prior to using it, otherwise Module32First() will fail. 1234 | module.dwSize = sizeof(module) 1235 | 1236 | found_mod = kernel32.Module32First(snapshot, byref(module)) 1237 | 1238 | while found_mod: 1239 | module_list.append((module.szModule, module.modBaseAddr)) 1240 | found_mod = kernel32.Module32Next(snapshot, byref(module)) 1241 | 1242 | self.close_handle(snapshot) 1243 | return module_list 1244 | 1245 | 1246 | #################################################################################################################### 1247 | def enumerate_processes (self): 1248 | ''' 1249 | Using the CreateToolhelp32Snapshot() API enumerate all system processes returning a list of pid / process name 1250 | tuples. 1251 | 1252 | @see: iterate_processes() 1253 | 1254 | @rtype: List 1255 | @return: List of pid / process name tuples. 1256 | 1257 | Example:: 1258 | 1259 | for (pid, name) in pydbg.enumerate_processes(): 1260 | if name == "test.exe": 1261 | break 1262 | 1263 | pydbg.attach(pid) 1264 | ''' 1265 | 1266 | self._log("enumerate_processes()") 1267 | 1268 | pe = PROCESSENTRY32() 1269 | process_list = [] 1270 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 1271 | 1272 | if snapshot == INVALID_HANDLE_VALUE: 1273 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0", True) 1274 | 1275 | # we *must* set the size of the structure prior to using it, otherwise Process32First() will fail. 1276 | pe.dwSize = sizeof(PROCESSENTRY32) 1277 | 1278 | found_proc = kernel32.Process32First(snapshot, byref(pe)) 1279 | 1280 | while found_proc: 1281 | process_list.append((pe.th32ProcessID, pe.szExeFile)) 1282 | found_proc = kernel32.Process32Next(snapshot, byref(pe)) 1283 | 1284 | self.close_handle(snapshot) 1285 | return process_list 1286 | 1287 | 1288 | #################################################################################################################### 1289 | def enumerate_threads (self): 1290 | ''' 1291 | Using the CreateToolhelp32Snapshot() API enumerate all system threads returning a list of thread IDs that 1292 | belong to the debuggee. 1293 | 1294 | @see: iterate_threads() 1295 | 1296 | @rtype: List 1297 | @return: List of thread IDs belonging to the debuggee. 1298 | 1299 | Example:: 1300 | for thread_id in self.enumerate_threads(): 1301 | context = self.get_thread_context(None, thread_id) 1302 | ''' 1303 | 1304 | self._log("enumerate_threads()") 1305 | 1306 | thread_entry = THREADENTRY32() 1307 | debuggee_threads = [] 1308 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid) 1309 | 1310 | if snapshot == INVALID_HANDLE_VALUE: 1311 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, %d" % self.pid, True) 1312 | 1313 | # we *must* set the size of the structure prior to using it, otherwise Thread32First() will fail. 1314 | thread_entry.dwSize = sizeof(thread_entry) 1315 | 1316 | success = kernel32.Thread32First(snapshot, byref(thread_entry)) 1317 | 1318 | while success: 1319 | if thread_entry.th32OwnerProcessID == self.pid: 1320 | debuggee_threads.append(thread_entry.th32ThreadID) 1321 | 1322 | success = kernel32.Thread32Next(snapshot, byref(thread_entry)) 1323 | 1324 | self.close_handle(snapshot) 1325 | return debuggee_threads 1326 | 1327 | 1328 | #################################################################################################################### 1329 | def event_handler_create_process (self): 1330 | ''' 1331 | This is the default CREATE_PROCESS_DEBUG_EVENT handler. 1332 | 1333 | @rtype: DWORD 1334 | @return: Debug event continue status. 1335 | ''' 1336 | 1337 | self._log("event_handler_create_process()") 1338 | 1339 | # don't need this. 1340 | self.close_handle(self.dbg.u.CreateProcessInfo.hFile) 1341 | 1342 | if not self.follow_forks: 1343 | return DBG_CONTINUE 1344 | 1345 | if self.callbacks.has_key(CREATE_PROCESS_DEBUG_EVENT): 1346 | return self.callbacks[CREATE_PROCESS_DEBUG_EVENT](self) 1347 | else: 1348 | return DBG_CONTINUE 1349 | 1350 | 1351 | #################################################################################################################### 1352 | def event_handler_create_thread (self): 1353 | ''' 1354 | This is the default CREATE_THREAD_DEBUG_EVENT handler. 1355 | 1356 | @rtype: DWORD 1357 | @return: Debug event continue status. 1358 | ''' 1359 | 1360 | self._log("event_handler_create_thread(%d)" % self.dbg.dwThreadId) 1361 | 1362 | # resolve the newly created threads TEB and add it to the internal dictionary. 1363 | thread_id = self.dbg.dwThreadId 1364 | thread_handle = self.dbg.u.CreateThread.hThread 1365 | thread_context = self.get_thread_context(thread_handle) 1366 | selector_entry = LDT_ENTRY() 1367 | 1368 | if not kernel32.GetThreadSelectorEntry(thread_handle, thread_context.SegFs, byref(selector_entry)): 1369 | self.win32_error("GetThreadSelectorEntry()") 1370 | 1371 | teb = selector_entry.BaseLow 1372 | teb += (selector_entry.HighWord.Bits.BaseMid << 16) + (selector_entry.HighWord.Bits.BaseHi << 24) 1373 | 1374 | # add this TEB to the internal dictionary. 1375 | self.tebs[thread_id] = teb 1376 | 1377 | # apply any existing hardware breakpoints to this new thread. 1378 | for slot, hw_bp in self.hardware_breakpoints.items(): 1379 | # mark available debug register as active (L0 - L3). 1380 | thread_context.Dr7 |= 1 << (slot * 2) 1381 | 1382 | # save our breakpoint address to the available hw bp slot. 1383 | if slot == 0: thread_context.Dr0 = hw_bp.address 1384 | elif slot == 1: thread_context.Dr1 = hw_bp.address 1385 | elif slot == 2: thread_context.Dr2 = hw_bp.address 1386 | elif slot == 3: thread_context.Dr3 = hw_bp.address 1387 | 1388 | # set the condition (RW0 - RW3) field for the appropriate slot (bits 16/17, 20/21, 24,25, 28/29) 1389 | thread_context.Dr7 |= hw_bp.condition << ((slot * 4) + 16) 1390 | 1391 | # set the length (LEN0-LEN3) field for the appropriate slot (bits 18/19, 22/23, 26/27, 30/31) 1392 | thread_context.Dr7 |= hw_bp.length << ((slot * 4) + 18) 1393 | 1394 | # set the thread context. 1395 | self.set_thread_context(thread_context, thread_id=thread_id) 1396 | 1397 | # pass control to user defined callback. 1398 | if self.callbacks.has_key(CREATE_THREAD_DEBUG_EVENT): 1399 | return self.callbacks[CREATE_THREAD_DEBUG_EVENT](self) 1400 | else: 1401 | return DBG_CONTINUE 1402 | 1403 | 1404 | #################################################################################################################### 1405 | def event_handler_exit_process (self): 1406 | ''' 1407 | This is the default EXIT_PROCESS_DEBUG_EVENT handler. 1408 | 1409 | @raise pdx: An exception is raised to denote process exit. 1410 | ''' 1411 | 1412 | self.set_debugger_active(False) 1413 | 1414 | if self.callbacks.has_key(EXIT_PROCESS_DEBUG_EVENT): 1415 | return self.callbacks[EXIT_PROCESS_DEBUG_EVENT](self) 1416 | else: 1417 | return DBG_CONTINUE 1418 | 1419 | 1420 | #################################################################################################################### 1421 | def event_handler_exit_thread (self): 1422 | ''' 1423 | This is the default EXIT_THREAD_DEBUG_EVENT handler. 1424 | 1425 | @rtype: DWORD 1426 | @return: Debug event continue status. 1427 | ''' 1428 | 1429 | # before we remove the TEB entry from our internal list, let's give the user a chance to do something with it. 1430 | if self.callbacks.has_key(EXIT_THREAD_DEBUG_EVENT): 1431 | continue_status = self.callbacks[EXIT_THREAD_DEBUG_EVENT](self) 1432 | else: 1433 | continue_status = DBG_CONTINUE 1434 | 1435 | # remove the TEB entry for the exiting thread id. 1436 | if self.tebs.has_key(self.dbg.dwThreadId): 1437 | del(self.tebs[self.dbg.dwThreadId]) 1438 | 1439 | return continue_status 1440 | 1441 | 1442 | #################################################################################################################### 1443 | def event_handler_load_dll (self): 1444 | ''' 1445 | This is the default LOAD_DLL_DEBUG_EVENT handler. You can access the last loaded dll in your callback handler 1446 | with the following example code:: 1447 | 1448 | last_dll = pydbg.get_system_dll(-1) 1449 | print "loading:%s from %s into:%08x size:%d" % (last_dll.name, last_dll.path, last_dll.base, last_dll.size) 1450 | 1451 | The get_system_dll() routine is preferred over directly accessing the internal data structure for proper and 1452 | transparent client/server support. 1453 | 1454 | @rtype: DWORD 1455 | @return: Debug event continue status. 1456 | ''' 1457 | 1458 | dll = system_dll(self.dbg.u.LoadDll.hFile, self.dbg.u.LoadDll.lpBaseOfDll) 1459 | self.system_dlls.append(dll) 1460 | 1461 | if self.callbacks.has_key(LOAD_DLL_DEBUG_EVENT): 1462 | return self.callbacks[LOAD_DLL_DEBUG_EVENT](self) 1463 | else: 1464 | return DBG_CONTINUE 1465 | 1466 | 1467 | #################################################################################################################### 1468 | def event_handler_unload_dll (self): 1469 | ''' 1470 | This is the default UNLOAD_DLL_DEBUG_EVENT handler. 1471 | 1472 | @rtype: DWORD 1473 | @return: Debug event continue status. 1474 | ''' 1475 | 1476 | base = self.dbg.u.UnloadDll.lpBaseOfDll 1477 | unloading = None 1478 | 1479 | for system_dll in self.system_dlls: 1480 | if system_dll.base == base: 1481 | unloading = system_dll 1482 | break 1483 | 1484 | # before we remove the system dll from our internal list, let's give the user a chance to do something with it. 1485 | if self.callbacks.has_key(UNLOAD_DLL_DEBUG_EVENT): 1486 | continue_status = self.callbacks[UNLOAD_DLL_DEBUG_EVENT](self) 1487 | else: 1488 | continue_status = DBG_CONTINUE 1489 | 1490 | if not unloading: 1491 | #raise pdx("Unable to locate DLL that is being unloaded from %08x" % base, False) 1492 | pass 1493 | else: 1494 | # close the open file handle to the system dll being unloaded. 1495 | self.close_handle(unloading.handle) 1496 | 1497 | # remove the system dll from the internal list. 1498 | self.system_dlls.remove(unloading) 1499 | del(unloading) 1500 | 1501 | return continue_status 1502 | 1503 | 1504 | #################################################################################################################### 1505 | def exception_handler_access_violation (self): 1506 | ''' 1507 | This is the default EXCEPTION_ACCESS_VIOLATION handler. Responsible for handling the access violation and 1508 | passing control to the registered user callback handler. 1509 | 1510 | @attention: If you catch an access violaton and wish to terminate the process, you *must* still return 1511 | DBG_CONTINUE to avoid a deadlock. 1512 | 1513 | @rtype: DWORD 1514 | @return: Debug event continue status. 1515 | ''' 1516 | 1517 | if self.callbacks.has_key(EXCEPTION_ACCESS_VIOLATION): 1518 | return self.callbacks[EXCEPTION_ACCESS_VIOLATION](self) 1519 | else: 1520 | return DBG_EXCEPTION_NOT_HANDLED 1521 | 1522 | 1523 | #################################################################################################################### 1524 | def exception_handler_breakpoint (self): 1525 | ''' 1526 | This is the default EXCEPTION_BREAKPOINT handler, responsible for transparently restoring soft breakpoints 1527 | and passing control to the registered user callback handler. 1528 | 1529 | @rtype: DWORD 1530 | @return: Debug event continue status. 1531 | ''' 1532 | 1533 | self._log("pydbg.exception_handler_breakpoint() at %08x from thread id %d" % (self.exception_address, self.dbg.dwThreadId)) 1534 | 1535 | # breakpoints we did not set. 1536 | if not self.bp_is_ours(self.exception_address): 1537 | # system breakpoints. 1538 | if self.exception_address == self.system_break: 1539 | # pass control to user registered call back. 1540 | if self.callbacks.has_key(EXCEPTION_BREAKPOINT): 1541 | continue_status = self.callbacks[EXCEPTION_BREAKPOINT](self) 1542 | else: 1543 | continue_status = DBG_CONTINUE 1544 | 1545 | if self.first_breakpoint: 1546 | self._log("first windows driven system breakpoint at %08x" % self.exception_address) 1547 | self.first_breakpoint = False 1548 | 1549 | # ignore all other breakpoints we didn't explicitly set. 1550 | else: 1551 | self._log("breakpoint not ours %08x" % self.exception_address) 1552 | continue_status = DBG_EXCEPTION_NOT_HANDLED 1553 | 1554 | # breakpoints we did set. 1555 | else: 1556 | # restore the original byte at the breakpoint address. 1557 | self._log("restoring original byte at %08x" % self.exception_address) 1558 | self.write_process_memory(self.exception_address, self.breakpoints[self.exception_address].original_byte) 1559 | self.set_attr("dirty", True) 1560 | 1561 | # before we can continue, we have to correct the value of EIP. the reason for this is that the 1-byte INT 3 1562 | # we inserted causes EIP to "slide" + 1 into the original instruction and must be reset. 1563 | self.set_register("EIP", self.exception_address) 1564 | self.context.Eip -= 1 1565 | 1566 | # if there is a specific handler registered for this bp, pass control to it. 1567 | if self.breakpoints[self.exception_address].handler: 1568 | self._log("calling user handler") 1569 | continue_status = self.breakpoints[self.exception_address].handler(self) 1570 | 1571 | # pass control to default user registered call back handler, if it is specified. 1572 | elif self.callbacks.has_key(EXCEPTION_BREAKPOINT): 1573 | continue_status = self.callbacks[EXCEPTION_BREAKPOINT](self) 1574 | 1575 | else: 1576 | continue_status = DBG_CONTINUE 1577 | 1578 | # if the breakpoint still exists, ie: the user didn't erase it during the callback, and the breakpoint is 1579 | # flagged for restore, then tell the single step handler about it. furthermore, check if the debugger is 1580 | # still active, that way we don't try and single step if the user requested a detach. 1581 | if self.get_attr("debugger_active") and self.breakpoints.has_key(self.exception_address): 1582 | if self.breakpoints[self.exception_address].restore: 1583 | self._restore_breakpoint = self.breakpoints[self.exception_address] 1584 | self.single_step(True) 1585 | 1586 | self.bp_del(self.exception_address) 1587 | 1588 | return continue_status 1589 | 1590 | 1591 | #################################################################################################################### 1592 | def exception_handler_guard_page (self): 1593 | ''' 1594 | This is the default EXCEPTION_GUARD_PAGE handler, responsible for transparently restoring memory breakpoints 1595 | passing control to the registered user callback handler. 1596 | 1597 | @rtype: DWORD 1598 | @return: Debug event continue status. 1599 | ''' 1600 | 1601 | self._log("pydbg.exception_handler_guard_page()") 1602 | 1603 | # determine the base address of the page where the offending reference resides. 1604 | mbi = self.virtual_query(self.violation_address) 1605 | 1606 | # if the hit is on a page we did not explicitly GUARD, then pass the violation to the debuggee. 1607 | if mbi.BaseAddress not in self._guarded_pages: 1608 | return DBG_EXCEPTION_NOT_HANDLED 1609 | 1610 | # determine if the hit was within a monitored buffer, or simply on the same page. 1611 | self.memory_breakpoint_hit = self.bp_is_ours_mem(self.violation_address) 1612 | 1613 | # grab the actual memory breakpoint object, for the hit breakpoint. 1614 | if self.memory_breakpoint_hit: 1615 | self._log("direct hit on memory breakpoint at %08x" % self.memory_breakpoint_hit) 1616 | 1617 | if self.write_violation: 1618 | self._log("write violation from %08x on %08x of mem bp" % (self.exception_address, self.violation_address)) 1619 | else: 1620 | self._log("read violation from %08x on %08x of mem bp" % (self.exception_address, self.violation_address)) 1621 | 1622 | # if there is a specific handler registered for this bp, pass control to it. 1623 | if self.memory_breakpoint_hit and self.memory_breakpoints[self.memory_breakpoint_hit].handler: 1624 | continue_status = self.memory_breakpoints[self.memory_breakpoint_hit].handler(self) 1625 | 1626 | # pass control to default user registered call back handler, if it is specified. 1627 | elif self.callbacks.has_key(EXCEPTION_GUARD_PAGE): 1628 | continue_status = self.callbacks[EXCEPTION_GUARD_PAGE](self) 1629 | 1630 | else: 1631 | continue_status = DBG_CONTINUE 1632 | 1633 | # if the hit page is still in our list of explicitly guarded pages, ie: the user didn't erase it during the 1634 | # callback, then tell the single step handler about it. furthermore, check if the debugger is still active, 1635 | # that way we don't try and single step if the user requested a detach. 1636 | if self.get_attr("debugger_active") and mbi.BaseAddress in self._guarded_pages: 1637 | self._restore_breakpoint = memory_breakpoint(None, None, mbi, None) 1638 | self.single_step(True) 1639 | 1640 | return continue_status 1641 | 1642 | 1643 | #################################################################################################################### 1644 | def exception_handler_single_step (self): 1645 | ''' 1646 | This is the default EXCEPTION_SINGLE_STEP handler, responsible for transparently restoring breakpoints and 1647 | passing control to the registered user callback handler. 1648 | 1649 | @rtype: DWORD 1650 | @return: Debug event continue status. 1651 | ''' 1652 | 1653 | self._log("pydbg.exception_handler_single_step()") 1654 | 1655 | # if there is a breakpoint to restore. 1656 | if self._restore_breakpoint: 1657 | bp = self._restore_breakpoint 1658 | 1659 | # restore a soft breakpoint. 1660 | if isinstance(bp, breakpoint): 1661 | self._log("restoring breakpoint at 0x%08x" % bp.address) 1662 | self.bp_set(bp.address, bp.description, bp.restore, bp.handler) 1663 | 1664 | # restore PAGE_GUARD for a memory breakpoint (make sure guards are not temporarily suspended). 1665 | elif isinstance(bp, memory_breakpoint) and self._guards_active: 1666 | self._log("restoring %08x +PAGE_GUARD on page based @ %08x" % (bp.mbi.Protect, bp.mbi.BaseAddress)) 1667 | self.virtual_protect(bp.mbi.BaseAddress, 1, bp.mbi.Protect | PAGE_GUARD) 1668 | 1669 | # restore a hardware breakpoint. 1670 | elif isinstance(bp, hardware_breakpoint): 1671 | self._log("restoring hardware breakpoint on %08x" % bp.address) 1672 | self.bp_set_hw(bp.address, bp.length, bp.condition, bp.description, bp.restore, bp.handler) 1673 | 1674 | # determine if this single step event occured in reaction to a hardware breakpoint and grab the hit breakpoint. 1675 | # according to the Intel docs, we should be able to check for the BS flag in Dr6. but it appears that windows 1676 | # isn't properly propogating that flag down to us. 1677 | if self.context.Dr6 & 0x1 and self.hardware_breakpoints.has_key(0): 1678 | self.hardware_breakpoint_hit = self.hardware_breakpoints[0] 1679 | 1680 | elif self.context.Dr6 & 0x2 and self.hardware_breakpoints.has_key(1): 1681 | self.hardware_breakpoint_hit = self.hardware_breakpoints[1] 1682 | 1683 | elif self.context.Dr6 & 0x4 and self.hardware_breakpoints.has_key(2): 1684 | self.hardware_breakpoint_hit = self.hardware_breakpoints[2] 1685 | 1686 | elif self.context.Dr6 & 0x8 and self.hardware_breakpoints.has_key(3): 1687 | self.hardware_breakpoint_hit = self.hardware_breakpoints[3] 1688 | 1689 | # if we are dealing with a hardware breakpoint and there is a specific handler registered, pass control to it. 1690 | if self.hardware_breakpoint_hit and self.hardware_breakpoint_hit.handler: 1691 | continue_status = self.hardware_breakpoint_hit.handler(self) 1692 | 1693 | # pass control to default user registered call back handler, if it is specified. 1694 | elif self.callbacks.has_key(EXCEPTION_SINGLE_STEP): 1695 | continue_status = self.callbacks[EXCEPTION_SINGLE_STEP](self) 1696 | 1697 | # if we single stepped to handle a breakpoint restore. 1698 | elif self._restore_breakpoint: 1699 | continue_status = DBG_CONTINUE 1700 | 1701 | # macos compatability. 1702 | # need to clear TRAP flag for MacOS. this doesn't hurt Windows aside from a negligible speed hit. 1703 | context = self.get_thread_context(self.h_thread) 1704 | context.EFlags &= ~EFLAGS_TRAP 1705 | self.set_thread_context(context) 1706 | 1707 | else: 1708 | continue_status = DBG_EXCEPTION_NOT_HANDLED 1709 | 1710 | # if we are handling a hardware breakpoint hit and it still exists, ie: the user didn't erase it during the 1711 | # callback, and the breakpoint is flagged for restore, then tell the single step handler about it. furthermore, 1712 | # check if the debugger is still active, that way we don't try and single step if the user requested a detach. 1713 | if self.hardware_breakpoint_hit != None and self.get_attr("debugger_active"): 1714 | slot = self.hardware_breakpoint_hit.slot 1715 | 1716 | if self.hardware_breakpoints.has_key(slot): 1717 | curr = self.hardware_breakpoints[slot] 1718 | prev = self.hardware_breakpoint_hit 1719 | 1720 | if curr.address == prev.address: 1721 | if prev.restore: 1722 | self._restore_breakpoint = prev 1723 | self.single_step(True) 1724 | 1725 | self.bp_del_hw(slot=prev.slot) 1726 | 1727 | # reset the hardware breakpoint hit flag and restore breakpoint variable. 1728 | self.hardware_breakpoint_hit = None 1729 | self._restore_breakpoint = None 1730 | 1731 | return continue_status 1732 | 1733 | 1734 | #################################################################################################################### 1735 | def func_resolve (self, dll, function): 1736 | ''' 1737 | Utility function that resolves the address of a given module / function name pair under the context of the 1738 | debugger. 1739 | 1740 | @see: func_resolve_debuggee() 1741 | 1742 | @type dll: String 1743 | @param dll: Name of the DLL (case-insensitive) 1744 | @type function: String 1745 | @param function: Name of the function to resolve (case-sensitive) 1746 | 1747 | @rtype: DWORD 1748 | @return: Address 1749 | ''' 1750 | 1751 | handle = kernel32.LoadLibraryA(dll) 1752 | address = kernel32.GetProcAddress(handle, function) 1753 | 1754 | kernel32.FreeLibrary(handle) 1755 | 1756 | return address 1757 | 1758 | 1759 | #################################################################################################################### 1760 | def func_resolve_debuggee (self, dll_name, func_name): 1761 | ''' 1762 | Utility function that resolves the address of a given module / function name pair under the context of the 1763 | debuggee. Note: Be weary of calling this function from within a LOAD_DLL handler as the module is not yet 1764 | fully loaded and therefore the snapshot will not include it. 1765 | 1766 | @author: Otto Ebeling 1767 | @see: func_resolve() 1768 | @todo: Add support for followed imports. 1769 | 1770 | @type dll_name: String 1771 | @param dll_name: Name of the DLL (case-insensitive, ex:ws2_32.dll) 1772 | @type func_name: String 1773 | @param func_name: Name of the function to resolve (case-sensitive) 1774 | 1775 | @rtype: DWORD 1776 | @return: Address of the symbol in the target process address space if it can be resolved, None otherwise 1777 | ''' 1778 | 1779 | dll_name = dll_name.lower() 1780 | 1781 | # we can't make the assumption that all DLL names end in .dll, for example Quicktime libs end in .qtx / .qts 1782 | # so instead of this old line: 1783 | # if not dll_name.endswith(".dll"): 1784 | # we'll check for the presence of a dot and will add .dll as a conveneince. 1785 | if not dll_name.count("."): 1786 | dll_name += ".dll" 1787 | 1788 | for module in self.iterate_modules(): 1789 | if module.szModule.lower() == dll_name: 1790 | base_address = module.modBaseAddr 1791 | dos_header = self.read_process_memory(base_address, 0x40) 1792 | 1793 | # check validity of DOS header. 1794 | if len(dos_header) != 0x40 or dos_header[:2] != "MZ": 1795 | continue 1796 | 1797 | e_lfanew = struct.unpack(" func_name: 1832 | high = middle - 1 1833 | else: 1834 | # MSFT documentation is misleading - see http://www.bitsum.com/pedocerrors.htm 1835 | bin_ordinal = self.read_process_memory(base_address + address_of_ordinals + middle * 2, 2) 1836 | ordinal = struct.unpack(" 126: 1867 | break 1868 | 1869 | discovered += char 1870 | 1871 | if len(discovered) < self.STRING_EXPLORATION_MIN_LENGTH: 1872 | return False 1873 | 1874 | return discovered 1875 | 1876 | 1877 | 1878 | #################################################################################################################### 1879 | def get_arg (self, index, context=None): 1880 | ''' 1881 | Given a thread context, this convenience routine will retrieve the function argument at the specified index. 1882 | The return address of the function can be retrieved by specifying an index of 0. This routine should be called 1883 | from breakpoint handlers at the top of a function. 1884 | 1885 | @type index: Integer 1886 | @param index: Data to explore for printable ascii string 1887 | @type context: Context 1888 | @param context: (Optional) Current thread context to examine 1889 | 1890 | @rtype: DWORD 1891 | @return: Value of specified argument. 1892 | ''' 1893 | 1894 | # if the optional current thread context was not supplied, grab it for the current thread. 1895 | if not context: 1896 | context = self.context 1897 | 1898 | arg_val = self.read_process_memory(context.Esp + index * 4, 4) 1899 | arg_val = self.flip_endian_dword(arg_val) 1900 | 1901 | return arg_val 1902 | 1903 | 1904 | #################################################################################################################### 1905 | def get_attr (self, attribute): 1906 | ''' 1907 | Return the value for the specified class attribute. This routine should be used over directly accessing class 1908 | member variables for transparent support across local vs. client/server debugger clients. 1909 | 1910 | @see: set_attr() 1911 | 1912 | @type attribute: String 1913 | @param attribute: Name of attribute to return. 1914 | 1915 | @rtype: Mixed 1916 | @return: Requested attribute or None if not found. 1917 | ''' 1918 | 1919 | if not hasattr(self, attribute): 1920 | return None 1921 | 1922 | return getattr(self, attribute) 1923 | 1924 | 1925 | #################################################################################################################### 1926 | def get_debug_privileges (self): 1927 | ''' 1928 | Obtain necessary privileges for debugging. 1929 | 1930 | @raise pdx: An exception is raised on failure. 1931 | ''' 1932 | 1933 | h_token = HANDLE() 1934 | luid = LUID() 1935 | token_state = TOKEN_PRIVILEGES() 1936 | 1937 | self._log("get_debug_privileges()") 1938 | 1939 | if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, byref(h_token)): 1940 | raise pdx("OpenProcessToken()", True) 1941 | 1942 | if not advapi32.LookupPrivilegeValueA(0, "seDebugPrivilege", byref(luid)): 1943 | raise pdx("LookupPrivilegeValue()", True) 1944 | 1945 | token_state.PrivilegeCount = 1 1946 | token_state.Privileges[0].Luid = luid 1947 | token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED 1948 | 1949 | if not advapi32.AdjustTokenPrivileges(h_token, 0, byref(token_state), 0, 0, 0): 1950 | raise pdx("AdjustTokenPrivileges()", True) 1951 | 1952 | 1953 | #################################################################################################################### 1954 | def get_instruction (self, address): 1955 | ''' 1956 | Pydasm disassemble utility function wrapper. Returns the pydasm decoded instruction in self.instruction. 1957 | 1958 | @type address: DWORD 1959 | @param address: Address to disassemble at 1960 | 1961 | @rtype: pydasm instruction 1962 | @return: pydasm instruction 1963 | ''' 1964 | 1965 | try: 1966 | data = self.read_process_memory(address, 32) 1967 | except: 1968 | return "Unable to disassemble at %08x" % address 1969 | 1970 | return pydasm.get_instruction(data, pydasm.MODE_32) 1971 | 1972 | 1973 | #################################################################################################################### 1974 | def get_printable_string (self, data, print_dots=True): 1975 | ''' 1976 | description 1977 | 1978 | @type data: Raw 1979 | @param data: Data to explore for printable ascii string 1980 | @type print_dots: Bool 1981 | @param print_dots: (Optional, def:True) Controls suppression of dot in place of non-printable 1982 | 1983 | @rtype: String 1984 | @return: False on failure, discovered printable chars in string otherwise. 1985 | ''' 1986 | 1987 | discovered = "" 1988 | 1989 | for char in data: 1990 | if ord(char) >= 32 and ord(char) <= 126: 1991 | discovered += char 1992 | elif print_dots: 1993 | discovered += "." 1994 | 1995 | return discovered 1996 | 1997 | 1998 | #################################################################################################################### 1999 | def get_register (self, register): 2000 | ''' 2001 | Get the value of a register in the debuggee within the context of the self.h_thread. 2002 | 2003 | @type register: Register 2004 | @param register: One of EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP, EIP 2005 | 2006 | @raise pdx: An exception is raised on failure. 2007 | @rtype: DWORD 2008 | @return: Value of specified register. 2009 | ''' 2010 | 2011 | self._log("getting %s in thread id %d" % (register, self.dbg.dwThreadId)) 2012 | 2013 | register = register.upper() 2014 | if register not in ("EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "ESP", "EBP", "EIP"): 2015 | raise pdx("invalid register specified") 2016 | 2017 | # ensure we have an up to date thread context. 2018 | context = self.get_thread_context(self.h_thread) 2019 | 2020 | if register == "EAX": return context.Eax 2021 | elif register == "EBX": return context.Ebx 2022 | elif register == "ECX": return context.Ecx 2023 | elif register == "EDX": return context.Edx 2024 | elif register == "ESI": return context.Esi 2025 | elif register == "EDI": return context.Edi 2026 | elif register == "ESP": return context.Esp 2027 | elif register == "EBP": return context.Ebp 2028 | elif register == "EIP": return context.Eip 2029 | 2030 | # this shouldn't ever really be reached. 2031 | return 0 2032 | 2033 | 2034 | #################################################################################################################### 2035 | def get_system_dll (self, idx): 2036 | ''' 2037 | Return the system DLL at the specified index. If the debugger is in client / server mode, remove the PE 2038 | structure (we do not want to send that mammoth over the wire). 2039 | 2040 | @type idx: Integer 2041 | @param idx: Index into self.system_dlls[] to retrieve DLL from. 2042 | 2043 | @rtype: Mixed 2044 | @return: Requested attribute or None if not found. 2045 | ''' 2046 | 2047 | self._log("get_system_dll()") 2048 | 2049 | try: 2050 | dll = self.system_dlls[idx] 2051 | except: 2052 | # index out of range. 2053 | return None 2054 | 2055 | dll.pe = None 2056 | return dll 2057 | 2058 | 2059 | #################################################################################################################### 2060 | def get_thread_context (self, thread_handle=None, thread_id=0): 2061 | ''' 2062 | Convenience wrapper around GetThreadContext(). Can obtain a thread context via a handle or thread id. 2063 | 2064 | @type thread_handle: HANDLE 2065 | @param thread_handle: (Optional) Handle of thread to get context of 2066 | @type thread_id: Integer 2067 | @param thread_id: (Optional) ID of thread to get context of 2068 | 2069 | @raise pdx: An exception is raised on failure. 2070 | @rtype: CONTEXT 2071 | @return: Thread CONTEXT on success. 2072 | ''' 2073 | 2074 | context = CONTEXT() 2075 | context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS 2076 | 2077 | # if a thread handle was not specified, get one from the thread id. 2078 | if not thread_handle: 2079 | h_thread = self.open_thread(thread_id) 2080 | else: 2081 | h_thread = thread_handle 2082 | 2083 | if not kernel32.GetThreadContext(h_thread, byref(context)): 2084 | raise pdx("GetThreadContext()", True) 2085 | 2086 | # if we had to resolve the thread handle, close it. 2087 | if not thread_handle: 2088 | self.close_handle(h_thread) 2089 | 2090 | return context 2091 | 2092 | 2093 | #################################################################################################################### 2094 | def get_unicode_string (self, data): 2095 | ''' 2096 | description 2097 | 2098 | @type data: Raw 2099 | @param data: Data to explore for printable unicode string 2100 | 2101 | @rtype: String 2102 | @return: False on failure, ascii-converted unicode string on discovered string. 2103 | ''' 2104 | 2105 | discovered = "" 2106 | every_other = True 2107 | 2108 | for char in data: 2109 | if every_other: 2110 | # if we've hit a non printable char, break 2111 | if ord(char) < 32 or ord(char) > 126: 2112 | break 2113 | 2114 | discovered += char 2115 | 2116 | every_other = not every_other 2117 | 2118 | if len(discovered) < self.STRING_EXPLORATION_MIN_LENGTH: 2119 | return False 2120 | 2121 | return discovered 2122 | 2123 | 2124 | #################################################################################################################### 2125 | def hex_dump (self, data, addr=0, prefix=""): 2126 | ''' 2127 | Utility function that converts data into hex dump format. 2128 | 2129 | @type data: Raw Bytes 2130 | @param data: Raw bytes to view in hex dump 2131 | @type addr: DWORD 2132 | @param addr: (Optional, def=0) Address to start hex offset display from 2133 | @type prefix: String (Optional, def="") 2134 | @param prefix: String to prefix each line of hex dump with. 2135 | 2136 | @rtype: String 2137 | @return: Hex dump of data. 2138 | ''' 2139 | 2140 | dump = prefix 2141 | slice = "" 2142 | 2143 | for byte in data: 2144 | if addr % 16 == 0: 2145 | dump += " " 2146 | 2147 | for char in slice: 2148 | if ord(char) >= 32 and ord(char) <= 126: 2149 | dump += char 2150 | else: 2151 | dump += "." 2152 | 2153 | dump += "\n%s%04x: " % (prefix, addr) 2154 | slice = "" 2155 | 2156 | dump += "%02x " % ord(byte) 2157 | slice += byte 2158 | addr += 1 2159 | 2160 | remainder = addr % 16 2161 | 2162 | if remainder != 0: 2163 | dump += " " * (16 - remainder) + " " 2164 | 2165 | for char in slice: 2166 | if ord(char) >= 32 and ord(char) <= 126: 2167 | dump += char 2168 | else: 2169 | dump += "." 2170 | 2171 | return dump + "\n" 2172 | 2173 | 2174 | #################################################################################################################### 2175 | def hide_debugger (self): 2176 | ''' 2177 | Hide the presence of the debugger. This routine requires an active context and therefore can not be called 2178 | immediately after a load() for example. Call it from the first chance breakpoint handler. This routine hides 2179 | the debugger in the following ways: 2180 | 2181 | - Modifies the PEB flag that IsDebuggerPresent() checks for. 2182 | 2183 | @raise pdx: An exception is raised if we are unable to hide the debugger for various reasons. 2184 | ''' 2185 | 2186 | selector_entry = LDT_ENTRY() 2187 | 2188 | # a current thread context is required. 2189 | if not self.context: 2190 | raise pdx("hide_debugger(): a thread context is required. Call me from a breakpoint handler.") 2191 | 2192 | if not kernel32.GetThreadSelectorEntry(self.h_thread, self.context.SegFs, byref(selector_entry)): 2193 | self.win32_error("GetThreadSelectorEntry()") 2194 | 2195 | fs_base = selector_entry.BaseLow 2196 | fs_base += (selector_entry.HighWord.Bits.BaseMid << 16) + (selector_entry.HighWord.Bits.BaseHi << 24) 2197 | 2198 | # http://openrce.org/reference_library/files/reference/Windows Memory Layout, User-Kernel Address Spaces.pdf 2199 | # find the peb. 2200 | peb = self.read_process_memory(fs_base + 0x30, 4) 2201 | peb = self.flip_endian_dword(peb) 2202 | 2203 | # zero out the flag. (3rd byte) 2204 | self.write_process_memory(peb+2, "\x00", 1) 2205 | 2206 | return self.ret_self() 2207 | 2208 | 2209 | #################################################################################################################### 2210 | def is_address_on_stack (self, address, context=None): 2211 | ''' 2212 | Utility function to determine if the specified address exists on the current thread stack or not. 2213 | 2214 | @type address: DWORD 2215 | @param address: Address to check 2216 | @type context: Context 2217 | @param context: (Optional) Current thread context to examine 2218 | 2219 | @rtype: Bool 2220 | @return: True if address lies in current threads stack range, False otherwise. 2221 | ''' 2222 | 2223 | # if the optional current thread context was not supplied, grab it for the current thread. 2224 | if not context: 2225 | context = self.context 2226 | 2227 | (stack_top, stack_bottom) = self.stack_range(context) 2228 | 2229 | if address >= stack_bottom and address <= stack_top: 2230 | return True 2231 | 2232 | return False 2233 | 2234 | 2235 | ##################################################################################################################### 2236 | def iterate_modules (self): 2237 | ''' 2238 | A simple iterator function that can be used to iterate through all modules the target process has mapped in its 2239 | address space. Yielded objects are of type MODULEENTRY32. 2240 | 2241 | @author: Otto Ebeling 2242 | @see: enumerate_modules() 2243 | @warning: break-ing out of loops over this routine will cause a handle leak. 2244 | 2245 | @rtype: MODULEENTRY32 2246 | @return: Iterated module entries. 2247 | ''' 2248 | 2249 | self._log("iterate_modules()") 2250 | 2251 | current_entry = MODULEENTRY32() 2252 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.pid) 2253 | 2254 | if snapshot == INVALID_HANDLE_VALUE: 2255 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, %d" % self.pid, True) 2256 | 2257 | # we *must* set the size of the structure prior to using it, otherwise Module32First() will fail. 2258 | current_entry.dwSize = sizeof(current_entry) 2259 | 2260 | if not kernel32.Module32First(snapshot, byref(current_entry)): 2261 | return 2262 | 2263 | while 1: 2264 | yield current_entry 2265 | 2266 | if not kernel32.Module32Next(snapshot, byref(current_entry)): 2267 | break 2268 | 2269 | # if the above loop is "broken" out of, then this handle leaks. 2270 | self.close_handle(snapshot) 2271 | 2272 | 2273 | ##################################################################################################################### 2274 | def iterate_processes (self): 2275 | ''' 2276 | A simple iterator function that can be used to iterate through all running processes. Yielded objects are of 2277 | type PROCESSENTRY32. 2278 | 2279 | @see: enumerate_processes() 2280 | @warning: break-ing out of loops over this routine will cause a handle leak. 2281 | 2282 | @rtype: PROCESSENTRY32 2283 | @return: Iterated process entries. 2284 | ''' 2285 | 2286 | self._log("iterate_processes()") 2287 | 2288 | pe = PROCESSENTRY32() 2289 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 2290 | 2291 | if snapshot == INVALID_HANDLE_VALUE: 2292 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0", True) 2293 | 2294 | # we *must* set the size of the structure prior to using it, otherwise Process32First() will fail. 2295 | pe.dwSize = sizeof(PROCESSENTRY32) 2296 | 2297 | if not kernel32.Process32First(snapshot, byref(pe)): 2298 | return 2299 | 2300 | while 1: 2301 | yield pe 2302 | 2303 | if not kernel32.Process32Next(snapshot, byref(pe)): 2304 | break 2305 | 2306 | # if the above loop is "broken" out of, then this handle leaks. 2307 | self.close_handle(snapshot) 2308 | 2309 | 2310 | ##################################################################################################################### 2311 | def iterate_threads (self): 2312 | ''' 2313 | A simple iterator function that can be used to iterate through all running processes. Yielded objects are of 2314 | type THREADENTRY32. 2315 | 2316 | @see: enumerate_threads() 2317 | @warning: break-ing out of loops over this routine will cause a handle leak. 2318 | 2319 | @rtype: THREADENTRY32 2320 | @return: Iterated process entries. 2321 | ''' 2322 | 2323 | self._log("iterate_threads()") 2324 | 2325 | thread_entry = THREADENTRY32() 2326 | snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid) 2327 | 2328 | if snapshot == INVALID_HANDLE_VALUE: 2329 | raise pdx("CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, %d" % self.pid, True) 2330 | 2331 | # we *must* set the size of the structure prior to using it, otherwise Thread32First() will fail. 2332 | thread_entry.dwSize = sizeof(thread_entry) 2333 | 2334 | if not kernel32.Thread32First(snapshot, byref(thread_entry)): 2335 | return 2336 | 2337 | while 1: 2338 | if thread_entry.th32OwnerProcessID == self.pid: 2339 | yield thread_entry 2340 | 2341 | if not kernel32.Thread32Next(snapshot, byref(thread_entry)): 2342 | break 2343 | 2344 | # if the above loop is "broken" out of, then this handle leaks. 2345 | self.close_handle(snapshot) 2346 | 2347 | 2348 | #################################################################################################################### 2349 | def flip_endian (self, dword): 2350 | ''' 2351 | Utility function to flip the endianess a given DWORD into raw bytes. 2352 | 2353 | @type dword: DWORD 2354 | @param dowrd: DWORD whose endianess to flip 2355 | 2356 | @rtype: Raw Bytes 2357 | @return: Converted DWORD in raw bytes. 2358 | ''' 2359 | 2360 | byte1 = chr(dword % 256) 2361 | dword = dword >> 8 2362 | byte2 = chr(dword % 256) 2363 | dword = dword >> 8 2364 | byte3 = chr(dword % 256) 2365 | dword = dword >> 8 2366 | byte4 = chr(dword % 256) 2367 | 2368 | return "%c%c%c%c" % (byte1, byte2, byte3, byte4) 2369 | 2370 | 2371 | #################################################################################################################### 2372 | def flip_endian_dword (self, bytes): 2373 | ''' 2374 | Utility function to flip the endianess of a given set of raw bytes into a DWORD. 2375 | 2376 | @type bytes: Raw Bytes 2377 | @param bytes: Raw bytes whose endianess to flip 2378 | 2379 | @rtype: DWORD 2380 | @return: Converted DWORD. 2381 | ''' 2382 | 2383 | return struct.unpack(" %s" % (explored_string, location) 3213 | else: 3214 | return "%s (%s)" % (explored_string, location) 3215 | 3216 | 3217 | #################################################################################################################### 3218 | def stack_range (self, context=None): 3219 | ''' 3220 | Determine the stack range (top and bottom) of the current or specified thread. The desired information is 3221 | located at offsets 4 and 8 from the Thread Environment Block (TEB), which in turn is pointed to by fs:0. 3222 | 3223 | @type context: Context 3224 | @param context: (Optional) Current thread context to examine 3225 | 3226 | @rtype: Mixed 3227 | @return: List containing (stack_top, stack_bottom) on success, False otherwise. 3228 | ''' 3229 | 3230 | selector_entry = LDT_ENTRY() 3231 | 3232 | # if the optional current thread context was not supplied, grab it for the current thread. 3233 | if not context: 3234 | context = self.context 3235 | 3236 | if not kernel32.GetThreadSelectorEntry(self.h_thread, context.SegFs, byref(selector_entry)): 3237 | self.win32_error("GetThreadSelectorEntry()") 3238 | 3239 | fs_base = selector_entry.BaseLow 3240 | fs_base += (selector_entry.HighWord.Bits.BaseMid << 16) + (selector_entry.HighWord.Bits.BaseHi << 24) 3241 | 3242 | # determine the top and bottom of the debuggee's stack. 3243 | stack_top = self.read_process_memory(fs_base + 4, 4) 3244 | stack_bottom = self.read_process_memory(fs_base + 8, 4) 3245 | 3246 | stack_top = self.flip_endian_dword(stack_top) 3247 | stack_bottom = self.flip_endian_dword(stack_bottom) 3248 | 3249 | return (stack_top, stack_bottom) 3250 | 3251 | 3252 | #################################################################################################################### 3253 | def stack_unwind (self, context=None): 3254 | ''' 3255 | Unwind the stack to the best of our ability. This function is really only useful if called when EBP is actually 3256 | used as a frame pointer. If it is otherwise being used as a general purpose register then stack unwinding will 3257 | fail immediately. 3258 | 3259 | @type context: Context 3260 | @param context: (Optional) Current thread context to examine 3261 | 3262 | @rtype: List 3263 | @return: The current call stack ordered from most recent call backwards. 3264 | ''' 3265 | 3266 | self._log("stack_unwind()") 3267 | 3268 | selector_entry = LDT_ENTRY() 3269 | call_stack = [] 3270 | 3271 | # if the optional current thread context was not supplied, grab it for the current thread. 3272 | if not context: 3273 | context = self.context 3274 | 3275 | # determine the stack top / bottom. 3276 | (stack_top, stack_bottom) = self.stack_range(context) 3277 | 3278 | this_frame = context.Ebp 3279 | 3280 | while this_frame > stack_bottom and this_frame < stack_top: 3281 | # stack frame sanity check: must be DWORD boundary aligned. 3282 | if this_frame & 3: 3283 | break 3284 | 3285 | try: 3286 | ret_addr = self.read_process_memory(this_frame + 4, 4) 3287 | ret_addr = self.flip_endian_dword(ret_addr) 3288 | except: 3289 | break 3290 | 3291 | # return address sanity check: return address must live on an executable page. 3292 | try: 3293 | mbi = self.virtual_query(ret_addr) 3294 | except: 3295 | break 3296 | 3297 | if mbi.Protect not in (PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY): 3298 | break 3299 | 3300 | # add the return address to the call stack. 3301 | call_stack.append(ret_addr) 3302 | 3303 | # follow the frame pointer to the next frame. 3304 | try: 3305 | next_frame = self.read_process_memory(this_frame, 4) 3306 | next_frame = self.flip_endian_dword(next_frame) 3307 | except: 3308 | break 3309 | 3310 | # stack frame sanity check: new frame must be at a higher address then the previous frame. 3311 | if next_frame <= this_frame: 3312 | break 3313 | 3314 | this_frame = next_frame 3315 | 3316 | return call_stack 3317 | 3318 | 3319 | #################################################################################################################### 3320 | def suspend_all_threads (self): 3321 | ''' 3322 | Suspend all process threads. 3323 | 3324 | @see: resume_all_threads() 3325 | 3326 | @raise pdx: An exception is raised on failure. 3327 | @rtype: pydbg 3328 | @return: Self 3329 | ''' 3330 | 3331 | for thread_id in self.enumerate_threads(): 3332 | self.suspend_thread(thread_id) 3333 | 3334 | return self.ret_self() 3335 | 3336 | 3337 | #################################################################################################################### 3338 | def suspend_thread (self, thread_id): 3339 | ''' 3340 | Suspend the specified thread. 3341 | 3342 | @type thread_id: DWORD 3343 | @param thread_id: ID of thread to suspend 3344 | 3345 | @raise pdx: An exception is raised on failure. 3346 | @rtype: pydbg 3347 | @return: Self 3348 | ''' 3349 | 3350 | self._log("suspending thread: %08x" % thread_id) 3351 | 3352 | thread_handle = self.open_thread(thread_id) 3353 | 3354 | if kernel32.SuspendThread(thread_handle) == -1: 3355 | raise pdx("SuspendThread()", True) 3356 | 3357 | self.close_handle(thread_handle) 3358 | 3359 | return self.ret_self() 3360 | 3361 | 3362 | #################################################################################################################### 3363 | def terminate_process (self, exit_code=0, method="terminateprocess"): 3364 | ''' 3365 | Terminate the debuggee using the specified method. 3366 | 3367 | "terminateprocess": Terminate the debuggee by calling TerminateProcess(debuggee_handle). 3368 | "exitprocess": Terminate the debuggee by setting its current EIP to ExitProcess(). 3369 | 3370 | @type exit_code: Integer 3371 | @param exit_code: (Optional, def=0) Exit code 3372 | @type method: String 3373 | @param method: (Optonal, def="terminateprocess") Termination method. See __doc__ for more info. 3374 | 3375 | @raise pdx: An exception is raised on failure. 3376 | ''' 3377 | 3378 | if method.lower().startswith("exitprocess"): 3379 | self.context.Eip = self.func_resolve_debuggee("kernel32", "ExitProcess") 3380 | self.set_thread_context(self.context) 3381 | 3382 | # fall back to "terminateprocess". 3383 | else: 3384 | if not kernel32.TerminateProcess(self.h_process, exit_code): 3385 | raise pdx("TerminateProcess(%d)" % exit_code, True) 3386 | 3387 | 3388 | #################################################################################################################### 3389 | def to_binary (self, number, bit_count=32): 3390 | ''' 3391 | Convert a number into a binary string. This is an ugly one liner that I ripped off of some site. 3392 | 3393 | @see: to_decimal() 3394 | 3395 | @type number: Integer 3396 | @param number: Number to convert to binary string. 3397 | @type bit_count: Integer 3398 | @param bit_count: (Optional, Def=32) Number of bits to include in output string. 3399 | 3400 | @rtype: String 3401 | @return: Specified integer as a binary string 3402 | ''' 3403 | 3404 | return "".join(map(lambda x:str((number >> x) & 1), range(bit_count -1, -1, -1))) 3405 | 3406 | 3407 | #################################################################################################################### 3408 | def to_decimal (self, binary): 3409 | ''' 3410 | Convert a binary string into a decimal number. 3411 | 3412 | @see: to_binary() 3413 | 3414 | @type binary: String 3415 | @param binary: Binary string to convert to decimal 3416 | 3417 | @rtype: Integer 3418 | @return: Specified binary string as an integer 3419 | ''' 3420 | 3421 | # this is an ugly one liner that I ripped off of some site. 3422 | #return sum(map(lambda x: int(binary[x]) and 2**(len(binary) - x - 1), range(len(binary)-1, -1, -1))) 3423 | 3424 | # this is much cleaner (thanks cody) 3425 | return int(binary, 2) 3426 | 3427 | 3428 | #################################################################################################################### 3429 | def virtual_alloc (self, address, size, alloc_type, protection): 3430 | ''' 3431 | Convenience wrapper around VirtualAllocEx() 3432 | 3433 | @type address: DWORD 3434 | @param address: Desired starting address of region to allocate, can be None 3435 | @type size: Integer 3436 | @param size: Size of memory region to allocate, in bytes 3437 | @type alloc_type: DWORD 3438 | @param alloc_type: The type of memory allocation (most often MEM_COMMIT) 3439 | @type protection: DWORD 3440 | @param protection: Memory protection to apply to the specified region 3441 | 3442 | @raise pdx: An exception is raised on failure. 3443 | @rtype: DWORD 3444 | @return: Base address of the allocated region of pages. 3445 | ''' 3446 | 3447 | if address: 3448 | self._log("VirtualAllocEx(%08x, %d, %08x, %08x)" % (address, size, alloc_type, protection)) 3449 | else: 3450 | self._log("VirtualAllocEx(NULL, %d, %08x, %08x)" % (size, alloc_type, protection)) 3451 | 3452 | allocated_address = kernel32.VirtualAllocEx(self.h_process, address, size, alloc_type, protection) 3453 | 3454 | if not allocated_address: 3455 | raise pdx("VirtualAllocEx(%08x, %d, %08x, %08x)" % (address, size, alloc_type, protection), True) 3456 | 3457 | return allocated_address 3458 | 3459 | 3460 | #################################################################################################################### 3461 | def virtual_free (self, address, size, free_type): 3462 | ''' 3463 | Convenience wrapper around VirtualFreeEx() 3464 | 3465 | @type address: DWORD 3466 | @param address: Pointer to the starting address of the region of memory to be freed 3467 | @type size: Integer 3468 | @param size: Size of memory region to free, in bytes 3469 | @type free_type: DWORD 3470 | @param free_type: The type of free operation 3471 | 3472 | @raise pdx: An exception is raised on failure. 3473 | ''' 3474 | 3475 | self._log("VirtualFreeEx(%08x, %d, %08x)" % (address, size, free_type)) 3476 | 3477 | if not kernel32.VirtualFreeEx(self.h_process, address, size, free_type): 3478 | raise pdx("VirtualFreeEx(%08x, %d, %08x)" % (address, size, free_type), True) 3479 | 3480 | 3481 | #################################################################################################################### 3482 | def virtual_protect (self, base_address, size, protection): 3483 | ''' 3484 | Convenience wrapper around VirtualProtectEx() 3485 | 3486 | @type base_address: DWORD 3487 | @param base_address: Base address of region of pages whose access protection attributes are to be changed 3488 | @type size: Integer 3489 | @param size: Size of the region whose access protection attributes are to be changed 3490 | @type protection: DWORD 3491 | @param protection: Memory protection to apply to the specified region 3492 | 3493 | @raise pdx: An exception is raised on failure. 3494 | @rtype: DWORD 3495 | @return: Previous access protection. 3496 | ''' 3497 | 3498 | #self._log("VirtualProtectEx( , 0x%08x, %d, %08x, ,)" % (base_address, size, protection)) 3499 | 3500 | old_protect = c_ulong(0) 3501 | 3502 | if not kernel32.VirtualProtectEx(self.h_process, base_address, size, protection, byref(old_protect)): 3503 | raise pdx("VirtualProtectEx(%08x, %d, %08x)" % (base_address, size, protection), True) 3504 | 3505 | return old_protect.value 3506 | 3507 | 3508 | #################################################################################################################### 3509 | def virtual_query (self, address): 3510 | ''' 3511 | Convenience wrapper around VirtualQueryEx(). 3512 | 3513 | @type address: DWORD 3514 | @param address: Address to query 3515 | 3516 | @raise pdx: An exception is raised on failure. 3517 | 3518 | @rtype: MEMORY_BASIC_INFORMATION 3519 | @return: MEMORY_BASIC_INFORMATION 3520 | ''' 3521 | 3522 | mbi = MEMORY_BASIC_INFORMATION() 3523 | 3524 | if kernel32.VirtualQueryEx(self.h_process, address, byref(mbi), sizeof(mbi)) < sizeof(mbi): 3525 | raise pdx("VirtualQueryEx(%08x)" % address, True) 3526 | 3527 | return mbi 3528 | 3529 | 3530 | #################################################################################################################### 3531 | def win32_error (self, prefix=None): 3532 | ''' 3533 | Convenience wrapper around GetLastError() and FormatMessage(). Raises an exception with the relevant error code 3534 | and formatted message. 3535 | 3536 | @type prefix: String 3537 | @param prefix: (Optional) String to prefix error message with. 3538 | 3539 | @raise pdx: An exception is always raised by this routine. 3540 | ''' 3541 | 3542 | error = c_char_p() 3543 | error_code = kernel32.GetLastError() 3544 | 3545 | kernel32.FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 3546 | None, 3547 | error_code, 3548 | 0x00000400, # MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) 3549 | byref(error), 3550 | 0, 3551 | None) 3552 | if prefix: 3553 | error_message = "%s: %s" % (prefix, error.value) 3554 | else: 3555 | error_message = "GetLastError(): %s" % error.value 3556 | 3557 | raise pdx(error_message, error_code) 3558 | 3559 | 3560 | #################################################################################################################### 3561 | def write (self, address, data, length=0): 3562 | ''' 3563 | Alias to write_process_memory(). 3564 | 3565 | @see: write_process_memory 3566 | ''' 3567 | 3568 | return self.write_process_memory(address, data, length) 3569 | 3570 | 3571 | #################################################################################################################### 3572 | def write_msr (self, address, data): 3573 | ''' 3574 | Write data to the specified MSR address. 3575 | 3576 | @see: read_msr 3577 | 3578 | @type address: DWORD 3579 | @param address: MSR address to write to. 3580 | @type data: QWORD 3581 | @param data: Data to write to MSR address. 3582 | 3583 | @rtype: tuple 3584 | @return: (read status, msr structure) 3585 | ''' 3586 | 3587 | msr = SYSDBG_MSR() 3588 | msr.Address = address 3589 | msr.Data = data 3590 | 3591 | status = ntdll.NtSystemDebugControl(SysDbgWriteMsr, 3592 | byref(msr), 3593 | sizeof(SYSDBG_MSR), 3594 | 0, 3595 | 0, 3596 | 0) 3597 | 3598 | return status 3599 | 3600 | 3601 | #################################################################################################################### 3602 | def write_process_memory (self, address, data, length=0): 3603 | ''' 3604 | Write to the debuggee process space. Convenience wrapper around WriteProcessMemory(). This routine will 3605 | continuously attempt to write the data requested until it is complete. 3606 | 3607 | @type address: DWORD 3608 | @param address: Address to write to 3609 | @type data: Raw Bytes 3610 | @param data: Data to write 3611 | @type length: DWORD 3612 | @param length: (Optional, Def:len(data)) Length of data, in bytes, to write 3613 | 3614 | @raise pdx: An exception is raised on failure. 3615 | ''' 3616 | 3617 | count = c_ulong(0) 3618 | 3619 | # if the optional data length parameter was omitted, calculate the length ourselves. 3620 | if not length: 3621 | length = len(data) 3622 | 3623 | # ensure we can write to the requested memory space. 3624 | _address = address 3625 | _length = length 3626 | try: 3627 | old_protect = self.virtual_protect(_address, _length, PAGE_EXECUTE_READWRITE) 3628 | except: 3629 | pass 3630 | 3631 | while length: 3632 | c_data = c_char_p(data[count.value:]) 3633 | 3634 | if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length, byref(count)): 3635 | raise pdx("WriteProcessMemory(%08x, ..., %d)" % (address, length), True) 3636 | 3637 | length -= count.value 3638 | address += count.value 3639 | 3640 | # restore the original page permissions on the target memory region. 3641 | try: 3642 | self.virtual_protect(_address, _length, old_protect) 3643 | except: 3644 | pass 3645 | --------------------------------------------------------------------------------