def __init__(self, binary_path: str, trace: List[Instruction]) -> None: self.binary_path = binary_path self.trace = trace self.rr = GdbController(gdb_path=DEFAULT_RR_PATH, gdb_args=[binary_path], rr=True) self.current_index = 0
def main(verbose=True): """Build and debug an application programatically For a list of GDB MI commands, see https://www.sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html """ # Build C program subprocess.check_output(["make", "-C", SAMPLE_C_CODE_DIR, '--quiet']) # Initialize object that manages gdb subprocess gdbmi = GdbController(verbose=verbose) # Send gdb commands. Gdb machine interface commands are easier to script around, # hence the name "machine interface". # Responses are automatically printed as they are received if verbose is True. # Responses are returned after writing, by default. # Load the file responses = gdbmi.write('-file-exec-and-symbols %s' % SAMPLE_C_BINARY) # Get list of source files used to compile the binary responses = gdbmi.write('-file-list-exec-source-files') # Add breakpoint responses = gdbmi.write('-break-insert main') # Run responses = gdbmi.write('-exec-run') responses = gdbmi.write('-exec-next') responses = gdbmi.write('-exec-next') responses = gdbmi.write('-exec-continue') gdbmi.exit()
def remove_gdb_controller(self, controller: GdbController) -> List[str]: try: controller.exit() except Exception: logger.error(traceback.format_exc()) orphaned_client_ids = self.controller_to_client_ids.pop(controller, []) return orphaned_client_ids
def test_controller_buffer(self): """test that a partial response gets successfully buffered by the controller, then fully read when more data arrives""" gdbmi = GdbController() to_be_buffered = b'^done,BreakpointTable={nr_rows="1",nr_' stream = 'teststream' verbose = False response = gdbmi._get_responses_list(to_be_buffered, stream, verbose) # Nothing should have been parsed yet assert (len(response) == 0) assert (gdbmi._incomplete_output[stream] == to_be_buffered) remaining_gdb_output = b'cols="6"}\n(gdb) \n' response = gdbmi._get_responses_list(remaining_gdb_output, stream, verbose) # Should have parsed response at this point assert (len(response) == 1) r = response[0] assert (r['stream'] == 'teststream') assert (r['type'] == 'result') assert (r['payload'] == { 'BreakpointTable': { 'nr_cols': '6', 'nr_rows': '1' } })
def OnStartClick(self, event): self.gdbmi = GdbController(gdb_path=self.gdbPath, time_to_check_for_additional_output_sec=1) self.flashButton.Enable() self.gdbButton.Disable() self.binButton.Disable() self.startButton.Disable()
def __init__(self, states, binary, cda, active_state=None): # type: (StateManager, str, Any, Optional[State]) -> None # FIXME: this binary is original path master, ptsname = create_pty() self.master = master self.COMMANDS = { 'q': self.handle_query, 'g': self.read_register_all, 'G': self.write_register_all, 'H': self.set_thread, 'm': self.read_memory, 'M': self.write_memory, 'p': self.read_register, 'P': self.write_register, 'v': self.handle_long_commands, 'X': self.write_memory_bin, 'Z': self.insert_breakpoint, 'z': self.remove_breakpoint, '?': self.stop_reason, '!': self.extend_mode, } self.states = states self.active_state = active_state if active_state else states.get_major(-1) self.regs = GdbRegSpace(self.active_state) self.mem = GdbMemSpace(self.active_state, cda) self.packet_size = PAGESIZE self.libs = GdbSharedLibrary(self.active_state, self.packet_size) self.gdb = GdbController(gdb_args=['--quiet', '--nx', '--interpreter=mi2']) self.gdb.write("-target-select remote %s" % ptsname, timeout_sec=10) self.thread = threading.Thread(target=self.run) self.thread.start() self.gdb.write("-file-exec-and-symbols %s" % binary, timeout_sec=100) self.gdb.write('set stack-cache off', timeout_sec=100)
def __init__(self, port: int = None, file: str = None): self.gdb_ctrl = GdbController( ["gdb-multiarch", "-q", "--interpreter=mi"]) self.gdb_ctrl.write("set architecture arm") self._registers = {'r{}'.format(i) for i in range(13)} self._registers.update({'sp', 'lr', 'pc', 'cpsr'}) self._flags_name = 'cpsr' self._flag_to_pos = { 'M': [0, 1, 2, 3, 4], 'T': [5], 'F': [6], 'I': [7], 'A': [8], 'E': [9], 'IT': [25, 26, 10, 11, 12, 13, 14, 15], 'GE': [16, 17, 18, 19], 'DNM': [20, 21, 22, 23], 'J': [24], 'Q': [27], 'V': [28], 'C': [29], 'Z': [30], 'N': [31] } super().__init__(port, file)
def test_controller(self): """Build a simple C program, then run it with GdbController and verify the output is parsed as expected""" SAMPLE_C_CODE_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'sample_c_app') SAMPLE_C_BINARY = os.path.join(SAMPLE_C_CODE_DIR, 'a.out') # Build C program subprocess.check_output(["make", "-C", SAMPLE_C_CODE_DIR, '--quiet']) # Initialize object that manages gdb subprocess gdbmi = GdbController() # Load the binary and its symbols in the gdb subprocess responses = gdbmi.write('-file-exec-and-symbols %s' % SAMPLE_C_BINARY, timeout_sec=2) # Verify output was parsed into a list of responses assert (len(responses) != 0) response = responses[0] assert (set(response.keys()) == set( ['message', 'type', 'payload', 'stream', 'token'])) assert (response['message'] == 'thread-group-added') assert (response['type'] == 'notify') assert (response['payload'] == {'id': 'i1'}) assert (response['stream'] == 'stdout') assert (response['token'] == None) responses = gdbmi.write( ['-file-list-exec-source-files', '-break-insert main']) assert (len(responses) != 0) # Close gdb subprocess responses = gdbmi.exit() assert (responses is None) assert (gdbmi.gdb_process is None)
def __init__(self, port: int = None, file: str = None): self.gdb_ctrl = GdbController( ["gdb-multiarch", "-q", "--interpreter=mi"]) self.gdb_ctrl.write("set architecture i386:x86_64") self._registers = {'r{}'.format(i) for i in range(8, 16)} self._registers.update({ 'rax', 'rdi', 'rsi', 'rdx', 'rcx', 'rbx', 'rsp', 'rbp', 'rip', 'eflags' }) # self._registers.update({'eax', 'edi', 'esi', 'edx', 'ecx', 'ebx', 'esp', 'ebp', 'eip'}) self._flags_name = 'eflags' self._flag_to_pos = { 'CF': [0], 'PF': [2], 'AF': [4], 'ZF': [6], 'SF': [7], 'TF': [8], 'IF': [9], 'DF': [10], 'OF': [11], 'IOPL': [11, 12], 'NT': [14], 'RF': [16], 'VM': [17], 'AC': [18], 'VIF': [19], 'VIP': [20], 'ID': [21] } super().__init__(port, file)
def test_controller_buffer_randomized(self): """ The following code reads a sample gdb mi stream randomly to ensure partial output is read and that the buffer is working as expected on all streams. """ test_directory = os.path.dirname(os.path.abspath(__file__)) datafile_path = '%s/response_samples.txt' % (test_directory) gdbmi = GdbController() for stream in gdbmi._incomplete_output.keys(): responses = [] with open(datafile_path, 'rb') as f: while(True): n = random.randint(1, 100) # read random number of bytes to simulate incomplete responses gdb_mi_simulated_output = f.read(n) if gdb_mi_simulated_output == b'': break # EOF # let the controller try to parse this additional raw gdb output responses += gdbmi._get_responses_list(gdb_mi_simulated_output, stream, False) assert(len(responses) == 141) # spot check a few assert_match(responses[0], {'message': None, 'type': 'console', 'payload': u'0x00007fe2c5c58920 in __nanosleep_nocancel () at ../sysdeps/unix/syscall-template.S:81\\n', 'stream': stream}) if not USING_WINDOWS: # can't get this to pass in windows assert_match(responses[71], {'stream': stream, 'message': u'done', 'type': 'result', 'payload': None, 'token': None}) assert_match(responses[82], {'message': None, 'type': 'output', 'payload': u'The inferior program printed this! Can you still parse it?', 'stream': stream}) assert_match(responses[137], {'stream': stream, 'message': u'thread-group-exited', 'type': 'notify', 'payload': {u'exit-code': u'0', u'id': u'i1'}, 'token': None}) assert_match(responses[138], {'stream': stream, 'message': u'thread-group-started', 'type': 'notify', 'payload': {u'pid': u'48337', u'id': u'i1'}, 'token': None}) assert_match(responses[139], {'stream': stream, 'message': u'tsv-created', 'type': 'notify', 'payload': {u'name': 'trace_timestamp', u'initial': '0'}, 'token': None}) assert_match(responses[140], {'stream': stream, 'message': u'tsv-created', 'type': 'notify', 'payload': {u'name': 'trace_timestamp', u'initial': '0'}, 'token': None}) for stream in gdbmi._incomplete_output.keys(): assert(gdbmi._incomplete_output[stream] is None)
def __init__(self, gdb_path, gdb_cmds, core_filename, prog_filename, timeout_sec=DEFAULT_GDB_TIMEOUT_SEC): """ Start GDB and initialize a GdbController instance """ gdb_args = [ '--quiet', # inhibit dumping info at start-up '--nx', # inhibit window interface '--nw', # ignore .gdbinit '--interpreter=mi2', # use GDB/MI v2 '--core=%s' % core_filename ] # core file for c in gdb_cmds: if c: gdb_args += ['-ex', c] gdb_args.append(prog_filename) self.p = GdbController(gdb_path=gdb_path, gdb_args=gdb_args) self.timeout = timeout_sec # Consume initial output by issuing a dummy command self._gdbmi_run_cmd_get_responses( cmd='-data-list-register-values x pc', resp_message=None, resp_type='console', multiple=True, done_message='done', done_type='result')
def gdbmi(): """ Inicializa el controlador de gdb """ controller = GdbController([DEBUGGER, KERNEL, "--interpreter=mi3"]) controller.write("target remote :1234") return controller
def gdb_run(self, *args): if self._gdb: # TODO ask user self._gdb.exit() self._gdb = None if not self._gdb: # start new instance self._gdb = GdbController(verbose=True) self._gdb.write("-file-exec-and-symbols %s" % get_executable_path(self.location)) responses = self._gdb.write( '-file-list-exec-source-files') # ['files'] # Lines <-> PC # responses = self._gdb.write("-sym-erubbol-list-lines %s" % self.location) # Transfer breakpoints fil = self.location.name for line in self._breakpoints: self._gdb.write("-break-insert %s:%d" % (fil, 1 + line)) self._gdb.write("-break-insert main") # Read breakpoints # TODO remove breakpoints and display the correct breakpoints from gdb self._remove_all_breakpoints() response = self._gdb.write("-break-list") for breakpoint in response[0]['payload']['BreakpointTable'][ 'body']: line = int(breakpoint['line']) iter = self.code.get_iter_at_line(line - 1) self._enable_breakpoint(iter) # GDB start debugging, should stop on main self._gdb.write('-exec-run') self._gdb_notify_callback()
def test_controller_buffer(self): """test that a partial response gets successfully buffered by the controller, then fully read when more data arrives""" gdbmi = GdbController() to_be_buffered = b'^done,BreakpointTable={nr_rows="1",nr_' stream = "teststream" response = gdbmi._get_responses_list(to_be_buffered, stream) # Nothing should have been parsed yet assert len(response) == 0 assert gdbmi._incomplete_output[stream] == to_be_buffered remaining_gdb_output = b'cols="6"}\n(gdb) \n' response = gdbmi._get_responses_list(remaining_gdb_output, stream) # Should have parsed response at this point assert len(response) == 1 r = response[0] assert r["stream"] == "teststream" assert r["type"] == "result" assert r["payload"] == { "BreakpointTable": { "nr_cols": "6", "nr_rows": "1" } }
def __init__(self, gdb=None): # Start gdb process self._logger = self.get_logger() self._gdbmi = GdbController(gdb_path=gdb) self._resp_cache = [] self._target_state = self.TARGET_STATE_UNKNOWN self._target_stop_reason = self.TARGET_STOP_REASON_UNKNOWN self._curr_frame = None
def __init_gdb(self): if self.gdb is not None: return self.gdb = GdbController( time_to_check_for_additional_output_sec=self.timeout) if self.arch == "x86_64": self.gdb.write("set arch i386:x86-64:intel")
def __init__(self, gdb_path=None, log_level=None, log_stream_handler=None, log_file_handler=None, log_gdb_proc_file=None, remote_target=None, remote_address=None, remote_port=None, top_defaults=None, **kwargs): defaults = { "gdb_path": "gdb", "remote_target": True, "remote_address": "localhost", "remote_port": 3333, } self.config = defaults # type: dict if top_defaults: self.config.update(top_defaults) gdb_path = self.get_config("gdb_path", gdb_path) log_level = self.get_config("log_level", log_level) log_stream_handler = self.get_config("log_stream_handler", log_stream_handler) log_file_handler = self.get_config("log_file_handler", log_file_handler) log_gdb_proc_file = self.get_config("log_gdb_proc_file", log_gdb_proc_file) remote_target = self.get_config("remote_target", remote_target) remote_address = self.get_config("remote_address", remote_address) remote_port = self.get_config("remote_port", remote_port) # Start gdb process self.remote_target = { "use_remote": remote_target, "address": remote_address, "port": remote_port } self._logger = log.logger_init("Gdb", log_level, log_stream_handler, log_file_handler) self._gdbmi = GdbController(gdb_path=gdb_path) self._gdbmi_lock = threading.Lock() self._resp_cache = [] self._target_state = TARGET_STATE_UNKNOWN self._target_stop_reason = TARGET_STOP_REASON_UNKNOWN self._curr_frame = None self._curr_wp_val = None # gdb config self.gdb_set("mi-async", "on") if log_gdb_proc_file is not None: pardirs = os.path.dirname(log_gdb_proc_file) if pardirs: os.makedirs(pardirs, exist_ok=True) # create non-existing folders self.gdb_set("logging", "file %s" % log_gdb_proc_file) self.gdb_set("logging", "on")
def _attach(self): self.gdbmi = GdbController() init_cmds = ["set exception-verbose on", "source ~/.gdbinit", "set print element 0", "attach {}".format(str(self._pid)), "set charset ASCII"] for cmd in init_cmds: self.gdbmi.write(cmd)
def __init__(self, binary_path, trace): self.binary_path = binary_path self.trace = trace self.rr = GdbController( gdb_path=DEFAULT_RR_PATH, gdb_args=[binary_path], rr=True, ) self.current_index = 0
def start_gdb(self): """ Runs GDB and connects it to the "serial" port of the DUT. After this, the DUT expect methods can no longer be used to capture output. """ self.stop_receive() self._port_close() Utility.console_log('Starting GDB...', 'orange') self.gdb = GdbController(gdb_path=self.TOOLCHAIN_PREFIX + 'gdb') # pygdbmi logs to console by default, make it log to a file instead log_folder = self.app.get_log_folder(TEST_SUITE) pygdbmi_log_file_name = os.path.join( log_folder, 'pygdbmi_log_' + self.test_name + '.txt') pygdbmi_logger = self.gdb.logger pygdbmi_logger.setLevel(logging.DEBUG) while pygdbmi_logger.hasHandlers(): pygdbmi_logger.removeHandler(pygdbmi_logger.handlers[0]) log_handler = logging.FileHandler(pygdbmi_log_file_name) log_handler.setFormatter( logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) pygdbmi_logger.addHandler(log_handler) # Set up logging for GDB remote protocol gdb_remotelog_file_name = os.path.join( log_folder, 'gdb_remote_log_' + self.test_name + '.txt') self.gdb.write('-gdb-set remotelogfile ' + gdb_remotelog_file_name) # Load the ELF file self.gdb.write('-file-exec-and-symbols {}'.format(self.app.elf_file)) # Connect GDB to UART Utility.console_log('Connecting to GDB Stub...', 'orange') self.gdb.write('-gdb-set serial baud 115200') responses = self.gdb.write('-target-select remote ' + self.get_gdb_remote(), timeout_sec=3) # Make sure we get the 'stopped' notification stop_response = self.find_gdb_response('stopped', 'notify', responses) if not stop_response: responses = self.gdb.write('-exec-interrupt', timeout_sec=3) stop_response = self.find_gdb_response('stopped', 'notify', responses) assert stop_response frame = stop_response['payload']['frame'] if 'file' not in frame: frame['file'] = '?' if 'line' not in frame: frame['line'] = '?' Utility.console_log( 'Stopped in {func} at {addr} ({file}:{line})'.format(**frame), 'orange') # Drain remaining responses self.gdb.get_gdb_response(raise_error_on_timeout=False)
def __init__(self, options): self.options = options logging.info( " [+] Start the GDB controller and attach it to the remote target") logging.info(" [+] GDB additional timeout value is %d" % int(options.timeout)) self.gdb = GdbController( time_to_check_for_additional_output_sec=int(options.timeout)) response = self.gdb.write("target remote :1234")
class GDB: def __init__(self): self.gdbmi = None self.timeout = 10 self.command = "" self.opLog = None def gdbStart(self, gdbPath): #open log file logFile = 'copy_to_flash_' + str( datetime.now().strftime("%Y-%m-%d_%H:%M:%S")) + '.log' self.opLog = open(logFile, 'w+') #open gdb self.gdbmi = GdbController(gdb_path=gdbPath, gdb_args=None, time_to_check_for_additional_output_sec=4.5, rr=False, verbose=False) #print('started gdb') def gdbSendCommand(self, command): self.command = command if self.gdbmi is None: return False #send command and get output. response = self.gdbmi.write(command, timeout_sec=self.timeout) if len(response) == 0: return False return response def gdbClose(self): #close gdb if self.gdbmi is None: return False self.gdbmi.send_signal_to_gdb("SIGINT") self.gdbmi.send_signal_to_gdb(2) self.gdbmi.interrupt_gdb() assert self.gdbmi.exit() is None assert self.gdbmi.gdb_process is None self.opLog.close() return True def getResponseTypeMsg(self, response): TypeMsg = "" typeMsgNum = str(response).count("payload") #get exact gdb console response. for line in range(typeMsgNum): if (str(response[line]['type'])) == "console": TypeMsg += (str(response[line]['payload'])) self.opLog.write("The output of " + self.command + " :\n" + TypeMsg + "\n") return TypeMsg def gdbSendSignal(self, sig): if self.gdbmi is None: return False self.gdbmi.send_signal_to_gdb(sig)
def __init__(self, gdb_exe, target_img, symbol_list): # Initial args for gdb. gdb_args = [ target_img, # target executable. '-batch' # Causes gdb to exit when done processing initial args. ] # Append more args to the list of args. # These args print the address of symbols. for sym in symbol_list: gdb_args.append('-ex=p(&{0})'.format(sym)) # For more robust error checking, print "done" at the end, from gdb to the response list. gdb_args.append('--ex=p("done")') # Create a gdb object. gdbmi = GdbController( gdb_path=gdb_exe, gdb_args=gdb_args, verbose=False ###True ) # Lauch gdb and get the response(s). responses = gdbmi.get_gdb_response(timeout_sec=2) # validate the response based on the length and the last response being '$3 = "done"' where 3 is the len() of responses. if (len(responses) != (len(symbol_list) + 1) or responses[-1]['payload'] != '${0} = "done"'.format( len(responses))): raise Exception('Unexpected response from gdb.') del responses[ -1] # delete the last element of the responses (containing "done"). # Compose a dictionary of results, indexed by the symbol name. self.symbols = {} for eachResponse in responses: try: response_payload = eachResponse['payload'] # for a string like '$1 = (float *) 0x20000a64 <HeaterControl::htrCurrentTotal_amps>', parse for 'float *' and '0x20000a64'. reg_exp_match = re.search( '\$(\d*?) = \((.*?)\) 0x([0-9A-Fa-f]*) ', response_payload) index = int(reg_exp_match.group(1)) - 1 address = int(reg_exp_match.group(3), 16) type = reg_exp_match.group(2) ###print('{0}: {1:08X}, {2}'.format(index, address, type)) except: print('Can\'t parse {0}'.format(response_payload)) # Add this response to the dictionary. self.symbols[symbol_list[index]] = SymbolLookup.SymbolAddressType( address, type) # self.symbols should now be complete. return
def __init__(self, args=[]): self.uid = str(uuid.uuid4()) # Start gdb process gdb_args = (['--nx', '--quiet', '--interpreter=mi2'] + ['--args', './crackme'] + args) self.gdbmi = GdbController(gdb_args=gdb_args) logging.info('Starting gdb with ' + repr(self.gdbmi.get_subprocess_cmd()))
class RRController: def __init__(self, binary_path: str, trace: List[Instruction]) -> None: self.binary_path = binary_path self.trace = trace self.rr = GdbController(gdb_path=DEFAULT_RR_PATH, gdb_args=[binary_path], rr=True) self.current_index = 0 def eval_expression(self, expr: str) -> None: res = self.rr.write("-data-evaluate-expression %s" % expr, timeout_sec=99999) print(res) def write_request(self, req: str, get_resp: bool = True, **kwargs: Any) -> List[Dict[str, Any]]: timeout_sec = kwargs.pop("timeout_sec", 10) kwargs["read_response"] = False self.rr.write(req, timeout_sec=timeout_sec, **kwargs) resp = [] # type: List[Dict[str, Any]] if get_resp: while True: try: resp += self.rr.get_gdb_response() except Exception: break return resp def count_occurence(self, idx: int) -> int: """Count # of addr -> target in trace""" instruction = self.trace[idx] addr = instruction.ip cnt = 0 step = 1 if idx > self.current_index else -1 for i in range(self.current_index, idx, step): e = self.trace[i] if e.ip == addr: cnt += 1 return cnt def run_until(self, idx: int) -> None: instruction = self.trace[idx] addr = instruction.ip n = self.count_occurence(idx) cont_ins = "c" if idx > self.current_index else "reverse-cont" self.write_request("b *{}".format(hex(addr)), get_resp=False, timeout_sec=100) self.write_request("{} {}".format(cont_ins, n), get_resp=False, timeout_sec=10000) self.write_request("clear *{}".format(hex(addr)), get_resp=False, timeout_sec=100)
def init(self): self.gdbmi = GdbController() self.gdbmi.write('-exec-arguments %s %s' % self.prog_args, read_response=False) self.gdbmi.write('-file-exec-and-symbols %s' % self.binary, read_response=False) self.gdbmi.write('-break-insert %s' % self.method_name, read_response=False) self.gdbmi.write('-exec-run', read_response=False) self.gdbmi.write('-data-list-register-names', read_response=False)
def __init__(self, elf: ELF, coredump: Coredump, lib_text_addrs: Dict[str, int]) -> None: self.coredump = coredump self.elf = elf self.corefile = self.coredump.file.name self.execfile = self.elf.file.name self.gdb = GdbController(gdb_args=["--quiet", "--interpreter=mi2"]) self.lib_text_addrs = lib_text_addrs self.get_response() self.setup_gdb()
def gdbStart(self, gdbPath): #open log file logFile = 'copy_to_flash_' + str( datetime.now().strftime("%Y-%m-%d_%H:%M:%S")) + '.log' self.opLog = open(logFile, 'w+') #open gdb self.gdbmi = GdbController(gdb_path=gdbPath, gdb_args=None, time_to_check_for_additional_output_sec=4.5, rr=False, verbose=False)
def __init__(self, directory: str, filename: str, *, debug: bool = False) -> None: os.chmod(os.path.join(directory, filename), 0o755) self.gdbmi = GdbController() self.debug = debug self.execute_raw(f"cd {directory}") self.execute_raw(f"-file-exec-and-symbols {filename}") self.execute_raw("set listsize 1")
def __init__(self, gdb_path=None): if gdb_path: self.gdbmi = GdbController([gdb_path, "--interpreter=mi3"]) else: self.gdbmi = GdbController() self.pc = VimSign("", "", 1000, "dbg_pc") self.bp_number = None self.bp_line = None self.result = None self.timeout = 3