def write(self, gdbmi_cmd): """ Place gdbmi_cmd string in the output queue. Args: gdbmi_cmd: gdbmi command string. Returns: Associated gdbmi response message. """ # Build command gdbmi_cmd = '{}{}'.format(self._token, gdbmi_cmd) self._out_q.put(gdbmi_cmd) # Wait for response while True: if not self._in_q.empty(): resp = self._in_q.get() # Get PID if self.pid is None and \ resp['payload'] is not None and \ 'pid' in resp['payload']: # Set PID self.pid = resp['payload']['pid'] # Debug utils.dprint('[+] Process PID "{}"'.format(self.pid), self._verbose) # Console message if resp['type'] == 'console': self.console.append(resp) self._in_q.task_done() continue # Stopped message elif resp['message'] == 'stopped': self._stopped.append(resp) # Response message elif resp['message'] is not None and \ resp['token'] is not None and \ resp['token'] == self._token: self._token += 1 self._in_q.task_done() return resp # Release lock self._in_q.task_done()
def vmmap(self): """ Downloads and parses the inferior memory maps file. Returns: A parsed Linux maps file in list/dictionary format. """ # Get maps file maps_file = self.get_file('/proc/{}/maps'.format(self.pid), 'maps') # Debug utils.dprint('[+] Downloaded maps file "{}"'.format(maps_file), self._verbose) self._segments = utils.parse_map(maps_file) return self._segments
def run(self): """ Main thread. """ # Main listener loop while True: # Read from gdb resp = self._communicator.get_gdb_response( timeout_sec=0, raise_error_on_timeout=False) # Write to out queue if len(resp) > 0: for msg in resp: # Debug utils.dprint('[+] Read response', self._verbose) utils.dprint(msg, self._verbose) self._in_q.put(msg) # Out message to process if not self._out_q.empty(): # Get out message out_msg = self._out_q.get() # Check for kill command if out_msg != 'die': # Debug utils.dprint('[+] Dispatched command "{}"'.format(out_msg), self._verbose) # Write to gdb self._communicator.write(out_msg, read_response=False) self._out_q.task_done() # Kill thread else: # Kill gdb self._communicator.write('-gdb-exit', read_response=False) # Debug utils.dprint('[+] Killing thread', self._verbose) return
def wait(self, reason): """ Block until a stop event with specified reason is detected. Args: reason: Reason the debugger stopped. Returns: Message from the gdb stop event. """ # Stop event already found if len(self._stopped) != 0: utils.dprint('[+] Event already stopped', self._verbose) for resp in self._stopped: if resp['message'] == 'stopped' and \ 'reason' in resp['payload'] and \ resp['payload']['reason'] == reason: return resp # Read in_q while True: if not self._in_q.empty(): resp = self._in_q.get() if resp['message'] == 'stopped' and \ 'reason' in resp['payload'] and \ resp['payload']['reason'] == reason: self._in_q.task_done() return resp self._in_q.task_done()
def main(duzzle=DuzzleContext()): """ Main function for duzzle. Args: duzzle: Duzzle context object. Returns: True if execution completes expectedly. """ # Build parser parser = argparse.ArgumentParser(description= 'Remote process dumping' \ 'utility for fuzzle') parser.add_argument('address', help='GDBServer IPv4 address') parser.add_argument('port', type=int, help='GDBServer TCP port') parser.add_argument('--arch', '-a', required=True, help='Target architecture [x86_64]') parser.add_argument('--breakpoint', '-b', required=True, help='Location to insert break') parser.add_argument('--out-file', '-o', required=True, help='Output file path to write packed UZL file') parser.add_argument('--follow-child', '-f', action='store_true', help='Follow child processes') parser.add_argument('--verbose', '-v', action='store_true', help='Enable verbosity') parser.add_argument('--version', action='version', version='0.0.9') args = parser.parse_args() # Clean up args address = args.address port = args.port out_file = args.out_file follow_child = args.follow_child verbose = args.verbose # Import target architecture arch = importlib.import_module('fuzzle.duzzle.archs.{}'.format(args.arch)) # Set context architecture duzzle.set_arch(arch) # Extract breakpoint breakpoint = '' if re.search(r'^0x[0-9a-fA-F]*$', args.breakpoint): breakpoint = '*{}'.format(args.breakpoint) else: breakpoint = args.breakpoint # Debug dprint('[+] Verbosity enabled', verbose) if verbose: duzzle.set_verbose() # Initialise context duzzle.init() # Connect to remote gdb instance duzzle.connect(address=address, port=port) print('[*] Process PID "{}"'.format(duzzle.pid)) # Set breakpoint duzzle.breakpoint(breakpoint) # Follow child processes if follow_child: duzzle.follow_child() # Continue execution duzzle.run() # Wait for break point duzzle.wait(duzzle.BREAKPOINT) # Dump memory segments mem_segments = [] for segment in duzzle.vmmap(): if (segment['perms'] & pypzl.READ) != 0 and \ segment['name'] not in duzzle.kernel_segments: try: file_path = duzzle.dump_segment(segment) mem_segments.append({ 'start': segment['start'], 'end': segment['end'], 'perms': segment['perms'], 'name': segment['name'], 'data': file_path }) # Debug print('[*] Dumped {} to {} - {}'.format( segment['start'], segment['end'], segment['name'])) except Exception: print('[*] Cannot dump {}'.format(segment['start'])) # Dump user registers user_regs = duzzle.dump_registers() print('[*] Dumped user registers') # Shutdown duzzle.shutdown() # Pack ctx = pypzl.PuzzleContext(arch.ARCH) # Add memory segments for segment in mem_segments: # Debug print('[*] Packing "{}"'.format(segment['data'])) # Add memory record ctx.add_mem_rec(int(segment['start'], 16), int(segment['end'], 16), segment['perms'], read_file_bytes(segment['data']), None if not segment['name'] else \ str.encode(segment['name'])) # Add user registers ctx.add_reg_rec(arch.pack(user_regs)) # Pack data pack_data = ctx.pack() # Write to file with open(out_file, 'wb') as file: file.write(pack_data) # Clean up ctx.free() return True