def __init__(self, handle: HANDLE, base: DWORD): """ Given a handle and base address of the loaded DLL, determine the DLL name and size to fully initialize the system DLL object. @type handle: HANDLE @param handle: Handle to the loaded DLL @type base: DWORD @param base: Loaded address of DLL @raise PDError: An exception is raised on failure. """ self.handle = handle self.base = base self.name = None self.path = None self.pe = None self.size = 0 # calculate the file size of the file_size_hi = c_ulong(0) file_size_lo = kernel32.GetFileSize(handle, byref(file_size_hi)) self.size = (file_size_hi.value << 8) + file_size_lo # create a file mapping from the dll handle. file_map = kernel32.CreateFileMappingA(handle, 0, PAGE_READONLY, 0, 1, 0) if not file_map: raise PDError('CreateFileMappingA()', True) # Map a single byte of the dll into memory # so we can query for the file name. kernel32.MapViewOfFile.restype = POINTER(c_char) file_ptr = kernel32.MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1) if file_ptr: # Query for the filename of the mapped file. filename = create_string_buffer(2048) psapi.GetMappedFileNameA(kernel32.GetCurrentProcess(), file_ptr, byref(filename), 2048) # Store the full path. this is kind of ghetto, # but i didn't want to mess with QueryDosDevice() etc ... self.path = os.sep + filename.value.split(os.sep, 3)[3] # Store the file name. # XXX - this really shouldn't be failing. but i've seen it happen. try: self.name = filename.value[filename.value.rindex(os.sep) + 1:] except Exception: self.name = self.path kernel32.UnmapViewOfFile(file_ptr) kernel32.CloseHandle(file_map)
def pickle_recv(self): """ This routine is used for marshaling arbitrary data from the PyDbg server. We can send pretty much anything here. For example a tuple containing integers, strings, arbitrary objects and structures. Our "protocol" is a simple length-value protocol where each datagram is prefixed by a 4-byte length of the data to be received. @raise pdx: An exception is raised if the connection was severed. @rtype: Mixed @return: Whatever is received over the socket. """ try: length = int(self.sock.recv(4), 16) received = self.sock.recv(length) except: raise PDError("connection severed") return pickle.loads(received)
def __init__(self, host, port): """ Set the default client attributes. The target host and port are required. @type host: String @param host: Host address of PyDBG server (dotted quad IP address or hostname) @type port: Integer @param port: Port that the PyDBG server is listening on. @raise PDError: An exception is raised if a connection to the PyDbg server can not be established. """ self.host = host self.port = port self.pydbg = PyDBG() self.callbacks = {} try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) except: raise PDError("connection severed")
def pickle_send(self, data): """ This routine is used for marshaling arbitrary data to the PyDbg server. We can send pretty much anything here. For example a tuple containing integers, strings, arbitrary objects and structures. Our "protocol" is a simple length-value protocol where each datagram is prefixed by a 4-byte length of the data to be received. @type data: Mixed @param data: Data to marshal and transmit. Data can *pretty much* contain anything you throw at it. @raise pdx: An exception is raised if the connection was severed. """ data = pickle.dumps(data) try: self.sock.send("%04x" % len(data)) self.sock.send(data) except: raise PDError("connection severed")
def debug_event_loop(self): """ Overriden debug event handling loop. A transparent mirror here with method_missing() would not do. Our debug event loop is reduced here to a data marshaling loop with the server. If the received type from the server is a tuple then we assume a debug or exception event has occured and pass it to any registered callbacks. The callback is free to call arbitrary PyDbg routines. Upon return of the callback, a special token, **DONE**, is used to flag to the PyDbg server that we are done processing the exception and it is free to move on. """ self.pickle_send(("debug_event_loop", ())) while 1: received = self.pickle_recv() if not received: continue # if we received a "special" type, which can currently be one of: # - debugger callback event # - user callback event # - raised exception if isinstance(received, tuple): # callback type if received[0] == "callback": (msg_type, dbg, context) = received # debugger callback event if dbg and context: # propogate the convenience variables. self.dbg = dbg self.context = context self.exception_address = dbg.u.Exception.ExceptionRecord.ExceptionAddress self.write_violation = dbg.u.Exception.ExceptionRecord.ExceptionInformation[ 0] self.violation_address = dbg.u.Exception.ExceptionRecord.ExceptionInformation[ 1] exception_code = dbg.u.Exception.ExceptionRecord.ExceptionCode ret = DBG_CONTINUE if exception_code in self.callbacks: print('processing handler for 0x{:08x}'.format( exception_code)) ret = self.callbacks[exception_code](self) # user callback event. elif USER_CALLBACK_DEBUG_EVENT in self.callbacks: ret = self.callbacks[USER_CALLBACK_DEBUG_EVENT](self) # raised exception type. elif received[0] == "exception": (msg_type, exception_string) = received print(exception_string) raise PDError(exception_string) self.pickle_send(('**DONE**', ret))