def gdbmi(): """ Inicializa el controlador de gdb """ controller = GdbController([DEBUGGER, KERNEL, "--interpreter=mi3"]) controller.write("target remote :1234") return controller
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 test_controller(self): """Build a simple C program, then run it with GdbController and verify the output is parsed as expected""" # Initialize object that manages gdb subprocess gdbmi = GdbController() c_hello_world_binary = self._get_c_program("hello", "pygdbmiapp.a") if USING_WINDOWS: c_hello_world_binary = c_hello_world_binary.replace("\\", "/") # Load the binary and its symbols in the gdb subprocess responses = gdbmi.write("-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1) # Verify output was parsed into a list of responses assert len(responses) != 0 response = responses[0] assert set(response.keys()) == { "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"] is None responses = gdbmi.write( ["-file-list-exec-source-files", "-break-insert main"], timeout_sec=3) assert len(responses) != 0 responses = gdbmi.write(["-exec-run", "-exec-continue"], timeout_sec=3) # Test GdbTimeoutError exception got_timeout_exception = False try: gdbmi.get_gdb_response(timeout_sec=0) except GdbTimeoutError: got_timeout_exception = True assert got_timeout_exception is True # Close gdb subprocess responses = gdbmi.exit() assert responses is None assert gdbmi.gdb_process is None # Test ValueError exception self.assertRaises(ValueError, gdbmi.write, "-file-exec-and-symbols %s" % c_hello_world_binary) # Respawn and test signal handling gdbmi.spawn_new_gdb_subprocess() responses = gdbmi.write("-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1) responses = gdbmi.write(["-break-insert main", "-exec-run"])
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)
class gdb_wrapper_x86_64(gdb_wrapper): 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) @gdb_wrapper.no_response() def get_flags(self, timeout_sec: int = DEFAULT_GDB_TIMEOUT_SEC): log = self.gdb_ctrl.write("print ${}".format(self._flags_name), timeout_sec) _, _, values = self._parse_log(log, 'console')['payload'].partition(' = ') result = {} all_flags = values.rstrip('\\n').strip('][').split() for flag_value in all_flags: flag_name, _, value = flag_value.partition('=') if value == '': value = 1 result[flag_name] = value for flag_name in self._flag_to_pos: if flag_name not in result: result[flag_name] = 0 return result
class RRController(object): 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 eval_expression(self, expr): # type: (str) -> None res = self.rr.write( "-data-evaluate-expression %s" % expr, timeout_sec=99999) print(res) def write_request(self, req, get_resp=True, **kwargs): timeout_sec = kwargs.pop('timeout_sec', 10) kwargs['read_response'] = False self.rr.write(req, timeout_sec=timeout_sec, **kwargs) resp = [] if get_resp: while True: try: resp += self.rr.get_gdb_response() except: break return resp def count_occurence(self, idx): """Count # of addr -> target in trace""" event = self.trace[idx] addr = event.addr 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.addr == addr: cnt += 1 def run_until(self, idx): event = self.trace[idx] addr = event.addr 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 test_controller(self): """Build a simple C program, then run it with GdbController and verify the output is parsed as expected""" # Initialize object that manages gdb subprocess gdbmi = GdbController() c_binary_path = self._get_c_program() # Load the binary and its symbols in the gdb subprocess responses = gdbmi.write('-file-exec-and-symbols %s' % c_binary_path, timeout_sec=1) # 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'] is None) responses = gdbmi.write( ['-file-list-exec-source-files', '-break-insert main']) assert (len(responses) != 0) responses = gdbmi.write(['-exec-run', '-exec-continue'], timeout_sec=3) found_match = False for r in responses: if r.get( 'payload', '' ) == ' leading spaces should be preserved. So should trailing spaces. ': found_match = True assert (found_match is True) # Close gdb subprocess responses = gdbmi.exit() assert (responses is None) assert (gdbmi.gdb_process is None) # Test NoGdbProcessError exception got_no_process_exception = False try: responses = gdbmi.write('-file-exec-and-symbols %s' % c_binary_path) except NoGdbProcessError: got_no_process_exception = True assert (got_no_process_exception is True)
class gdb_wrapper_avr(gdb_wrapper): def __init__(self, port: int = None, file: str = None): self.gdb_ctrl = GdbController(["avr-gdb", "-q", "--interpreter=mi"]) self._registers = {'r{}'.format(i) for i in range(32)} self._registers.update({'SP', 'PC', 'SREG'}) self._flags_name = 'SREG' self._flag_to_pos = { 'C': [0], 'Z': [1], 'N': [2], 'V': [3], 'S': [4], 'H': [5], 'T': [6], 'I': [7] } super().__init__(port, file) @gdb_wrapper.no_response() def get_flags(self, timeout_sec: int = DEFAULT_GDB_TIMEOUT_SEC): all_flags = ['C', 'Z', 'N', 'V', 'S', 'H', 'T', 'I'] log = self.gdb_ctrl.write("print ${}".format(self._flags_name), timeout_sec) _, _, values = gdb_wrapper._parse_log( log, 'console')['payload'].partition(' = ') return gdb_wrapper._parse_flags(int(values.rstrip('\\n')), all_flags)
def getBackTrack(target, pov, src_list): """ Get back track, returns the last buggy file and line number. Arg1: target binary Arg2: argument of target binary Arg3: src list which is used to filter unrelated files. """ try: gdbmi = GdbController() response = gdbmi.write('-file-exec-and-symbols %s' % target) response = gdbmi.write('run') response = gdbmi.write('bt') getLastCall(response, src_list) except Exception as e: print e
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 do_gdb(self, gdb: GdbController, pid: int): # -stack-list-locals2 # ^done, locals=[{name = "r", type = "int", value = "0"}] resp = gdb.write("-stack-list-locals 2") print(resp) locals = resp[0]['payload']['locals'] for var in locals: self.tableModelVariables.emit(pid, var['name'], var['value'])
class gdb_wrapper_arm(gdb_wrapper): 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) @gdb_wrapper.no_response() def get_flags(self, timeout_sec: int = DEFAULT_GDB_TIMEOUT_SEC): log = self.gdb_ctrl.write("print ${}".format(self._flags_name), timeout_sec) _, _, values = gdb_wrapper._parse_log( log, 'console')['payload'].partition(' = ') all_flags = [['M', 0], ['M', 1], ['M', 2], ['M', 3], ['M', 4], 'T', 'F', 'I', 'A', 'E', ['IT', 2], ['IT', 3], ['IT', 4], ['IT', 5], ['IT', 6], ['IT', 7], ['GE', 0], ['GE', 1], ['GE', 2], ['GE', 3], ['DNM', 0], ['DNM', 1], ['DNM', 2], ['DNM', 3], 'J', ['IT', 0], ['IT', 1], 'Q', 'V', 'C', 'Z', 'N'] return gdb_wrapper._parse_flags(int(values.rstrip('\\n')), all_flags)
def vul3_32(exe): slack = -254 signal = response = data_address = bss_address = None b = [] test_file = "test_file.bin" while signal != 'SIGSEGV': slack += 254 for x in range(1, 255): b.extend([x, x, x, x]) f = open(test_file, "wb") f.write(bytes(b)) f.close() gdbmi = GdbController() gdbmi.write('file ' + exe) gdbmi.write('-break-insert main') gdbmi.write('-exec-arguments ' + test_file) gdbmi.write('-exec-run') response = gdbmi.write('maint info section') if data_address is None: (data_address, bss_address) = get_addreses(response) response = gdbmi.write('-exec-continue') if response[-1]['payload']['reason'] == 'signal-received': address = response[-1]['payload']['frame']['addr'] if address[2:4] == address[4:6] or address[6:8] == address[8:]: signal = response[-1]['payload']['signal-name'] # pprint(response) address = response[-1]['payload']['frame']['addr'] length = ((int(address[8:], 16) - 1) + slack) * 4 if int(address[8:], 16) != int(address[2:4], 16): length += 4 i = 10 while int(address[i - 2:i], 16) != int(address[2:4], 16): length -= 1 i -= 2 os.remove(test_file) return length, data_address, bss_address
class gdb_runner: def __init__(self, arch): self.arch = arch # Method that runs a GDB process, gains control of it through stdin, # and attaches it to a specified QEMU process. # Arguments: # gdb_exec - full path to gdb executable. # qemu_inst - object of qemu_runner or it's descendant class. def run_and_attach(self, gdb_exec, qemu_inst): self.gdb_ctrl = GdbController([gdb_exec, "-q", "--interpreter=mi"]) return self.attach(qemu_inst) # Method that attaches a GDB process to a specified QEMU process. def attach(self, qemu_inst): resp = self.gdb_ctrl.write("file " + qemu_inst.cur_exec) resp.extend( self.gdb_ctrl.write("target extended-remote localhost:" + str(qemu_inst.dbg_port))) return resp # Method that sends a commands to GDB process via stdin. # Arguments: # command - string representing command. # Return value: # payload list of dictionaries. def write(self, command): resp = self.gdb_ctrl.write(command) if command.strip() == "q": self.gdb_ctrl = None return resp # Method that sends to GDB process following commands: # - kill the process being debugged. # - exit GDB. def exit(self): write("kill") write("q") # Method that tells if GDB process is active. def is_active(self): return self.gdb_ctrl != None
def netperf(exe): slack = -254 signal = response = data_address = bss_address = None b = [] while signal != 'SIGSEGV': slack += 254 for x in range(1, 255): b.extend([x, x, x, x]) arguments = "".join(map(chr, b)) # if(slack * 4 > 7000): gdbmi = GdbController() gdbmi.write('file vulnerable_binaries/' + exe) gdbmi.write('-break-insert main') gdbmi.write('-exec-arguments -a \"' + arguments + '\"') gdbmi.write('-exec-run') response = gdbmi.write('maint info section') if data_address is None: (data_address, bss_address) = get_addreses(response) response = gdbmi.write('-exec-continue') if response[-1]['payload']['reason'] == 'signal-received': address = response[-1]['payload']['frame']['addr'] if address[2:4] == address[4:6] or address[6:8] == address[8:]: signal = response[-1]['payload']['signal-name'] # pprint(response) address = response[-1]['payload']['frame']['addr'] length = ((int(address[8:], 16) - 1) + slack) * 4 if int(address[8:], 16) != int(address[2:4], 16): length += 4 i = 10 while int(address[i - 2:i], 16) != int(address[2:4], 16): length -= 1 i -= 2 return length, data_address, bss_address
class ProbeSession(object): def __init__(self, port, host_ip="localhost"): self.session = GdbController(gdb_path="arm-none-eabi-gdb") # Connect to the debug probe self.session.write("target remote %s:%s" % (host_ip, port)) def __del__(self): # Stop the running GDB server self.session.exit() def register_read(self, reg): reg = reg.lower() if reg not in reg_list: raise "Specified register is not valid for this target." output = self.session.write("monitor reg %s" % reg) assert (output[1]['type'] == 'target') value = reg_value.match(output[1]['payload'])['value'] return int(value, 16)
def connect_to_target(port): # open GDB in machine interface mode gdbmi = GdbController( gdb_path=args.gdb_path, gdb_args=["--nx", "--quiet", "--interpreter=mi2", args.file]) assert gdb_write_and_wait_for_result(gdbmi, '-target-select extended-remote %s' % port, 'connecting', expected_result='connected') # set options if args.connect_srst: gdbmi.write('monitor connect_srst enable', timeout_sec=TIMEOUT) if args.tpwr: gdbmi.write('monitor tpwr enable', timeout_sec=TIMEOUT) # scan for targets if not args.jtag: print("scanning using SWD...") res = gdbmi.write('monitor swdp_scan', timeout_sec=TIMEOUT) else: print("scanning using JTAG...") res = gdbmi.write('monitor jtag_scan', timeout_sec=TIMEOUT) targets = detect_targets(gdbmi, res) assert len(targets) > 0, "no targets found" print("found following targets:") for t in targets: print("\t%s" % t) print("") return gdbmi
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 try_nums(nums): numString = arrayToString(nums) gdbmi = GdbController() gdbmi.write('file bomb', timeout_sec=5) gdbmi.write('break explode_bomb', timeout_sec=5) gdbmi.write('run', timeout_sec=5) time.sleep(1.5) gdbmi.interrupt_gdb() gdbmi.write('call (void) initialize_phase6()', timeout_sec=5) response = gdbmi.write('call (void) phase6("{0}")'.format(numString), timeout_sec=5) for mess in response: if mess['message'] == 'stopped': gdbmi.exit() return numString + "is not the value." gdbmi.exit() return "! " + numString + "is the value!"
class GDBSimulator: def __init__(self) -> None: self.file = None self.gdb = GdbController() def load(self, file: str) -> None: if file == self.file: return self.file = file self.restart() return self.run(f'file {self.file}') def restart(self) -> None: self.gdb.spawn_new_gdb_subprocess() def run(self, cmd: str): return self.gdb.write(cmd)
class DebugSession(): def __init__(self, progname): self.gdbmi = GdbController() resp = self.cmd('-file-exec-and-symbols ' + progname) assert "done" in [r['message'] for r in resp] resp = self.cmd('-break-insert main') assert "done" in [r['message'] for r in resp] resp = self.cmd('-exec-run') assert "running" in [r['message'] for r in resp] print("initialized") def cmd(self, cmd): ret = self.gdbmi.write(cmd) return ret def call_func(self, funcstr): funcstr = funcstr.replace('"', '\\"') resp = self.cmd(f'-data-evaluate-expression "{funcstr}"') try: if "error" in [r['message'] for r in resp]: resp = self.cmd( f'-data-evaluate-expression "(void*){funcstr}"') except: pass result = [item['payload'] for item in resp if item['type'] == 'result'] ret = result[0]['value'] if '"' in ret: return '"' + ret.split('"')[1] + '"' else: return ret.split(' ')[0] return ret
class GDBInspector: """Wrapper around pygdbmi.gdbcontroller.GdbController.""" def __init__(self, binary, corefile='core', path=None): self._binary = os.path.join(path, binary) if path else binary self._corefile = os.path.join(path, corefile) if path else corefile self._gdb = None async def __aenter__(self): await self.connect() return self async def __aexit__(self, exc_type, exc_val, exc_tb): await self.exit() def __getattr__(self, item): return getattr(self._gdb, item) @as_future def connect(self): self._gdb = GdbController(gdb_args=self.gdb_args) @as_future def exit(self): return self._gdb.exit() @property def gdb_args(self): args = [self._binary, self._corefile] args += REQUIRED_GDB_FLAGS return args @as_future def write(self, *args, **kwargs): return self._gdb.write(*args, **kwargs) def corefile_exists(self): return os.path.isfile(self._corefile)
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 find_executable(MAKE_CMD) if not find_executable(MAKE_CMD): print( 'Could not find executable "%s". Ensure it is installed and on your $PATH.' % MAKE_CMD) exit(1) subprocess.check_output([MAKE_CMD, "-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") # noqa: F841 # gdbmi.gdb_process will be None because the gdb subprocess (and its inferior # program) will be terminated gdbmi.exit()
class CodeFrame(Gtk.VBox): location: Path toolbar: Gtk.Toolbar code: GtkSource.Buffer _gdb_callback = None _gdb_callback_arg: object _gdb: GdbController _exec_mark: GtkSource.Mark = None def __init__(self): Gtk.VBox.__init__(self) self._gdb = None self.toolbar = Gtk.Toolbar() self.btnLoad = Gtk.ToolButton(label="Load", stock_id=Gtk.STOCK_OPEN) self.btnSave = Gtk.ToolButton(label="Save", stock_id=Gtk.STOCK_SAVE) self.toolbar.add(self.btnLoad) self.toolbar.add(self.btnSave) self.toolbar.add(Gtk.SeparatorToolItem()) self.btnStart = Gtk.ToolButton(label="(Re)Start", stock_id=Gtk.STOCK_MEDIA_PLAY) self.toolbar.add(self.btnStart) self.btnStep = Gtk.ToolButton(label="s", stock_id=Gtk.STOCK_MEDIA_FORWARD) self.toolbar.add(self.btnStep) self.btnContinue = Gtk.ToolButton(label="c", stock_id=Gtk.STOCK_MEDIA_NEXT) self.toolbar.add(self.btnContinue) self.editor = GtkSource.View(monospace=True, show_line_numbers=True, show_line_marks=True, tab_width=4, auto_indent=True, insert_spaces_instead_of_tabs=True, show_right_margin=True, smart_backspace=True, highlight_current_line=True) self.code = GtkSource.Buffer(language=get_gtk_language("cpp")) self.editor.set_buffer(self.code) # register breakpoints mark_attributes = GtkSource.MarkAttributes() mark_attributes.set_icon_name(Gtk.STOCK_STOP) self.editor.set_mark_attributes(CATEGORY_BREAKPOINT, mark_attributes, 1) # register exec marker mark_attributes = GtkSource.MarkAttributes() mark_attributes.set_icon_name(Gtk.STOCK_GO_FORWARD) mark_attributes.set_background(Gdk.RGBA(0, 1, 0, 1)) self.editor.set_mark_attributes(CATEGORY_EXEC_MARKER, mark_attributes, 0) self.editor.connect("line-mark-activated", self.on_line_mark) self.pack_start(self.toolbar, expand=False, fill=False, padding=0) self.pack_start(self.editor, expand=True, fill=True, padding=2) self.btnLoad.connect("clicked", self.load_interactive) self.btnSave.connect("clicked", self.save_interactive) self.btnStart.connect("clicked", self.gdb_run) self.btnStep.connect("clicked", self.gdb_step) self.btnContinue.connect("clicked", self.gdb_continue) self._breakpoints = dict() def on_line_mark(self, view, iter, event: Gdk.Event): # print(event.get_button().button == Gdk.BUTTON_PRIMARY, # event.get_button(), Gdk.BUTTON_PRIMARY) if event.get_button().button == Gdk.BUTTON_PRIMARY: line = iter.get_line() if line in self._breakpoints: self._disable_breakpoint(iter) else: self._enable_breakpoint(iter) elif event.get_button() == Gdk.BUTTON_SECONDARY: pass # TODO Support for SYNC points def _disable_breakpoint(self, iter): mark = self._breakpoints[iter.get_line()] self.code.delete_mark(mark) del self._breakpoints[iter.get_line()] def _enable_breakpoint(self, iter): mark = self.code.create_source_mark(None, CATEGORY_BREAKPOINT, iter) mark.set_visible(True) self._breakpoints[iter.get_line()] = mark 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 _remove_all_breakpoints(self): for v in self._breakpoints.values(): self.code.delete_mark(v) del self._breakpoints self._breakpoints = dict() def gdb_step(self, *args): if self._gdb: self._gdb.write("-exec-step") self._gdb_notify_callback() def gdb_continue(self, *args): if self._gdb: self._gdb.write("-exec-continue") self._gdb_notify_callback() def _gdb_update_exec_mark(self): if self._exec_mark is not None: self.code.delete_mark(self._exec_mark) # get current line response = self._gdb.write("-stack-info-frame") line = int(response[0]['payload']['frame']['line']) print(line) self._exec_mark = self.code.create_source_mark( NAME_EXEC_MARKER, CATEGORY_EXEC_MARKER, self.code.get_iter_at_line(line - 1)) def _gdb_notify_callback(self): self._gdb_update_exec_mark() # ^done,frame={level="0",addr="0x00000000004004cb",func="main",file="left.c",fullname="/home/weigl/work/bigdb/left.c",line="17"} self._gdb_callback(self._gdb, self._gdb_callback_arg) # -stack-list-frames # ^done,stack=[frame={level="0",addr="0x0000000000400495",func="f",file="left.c",fullname="/home/weigl/work/bigdb/left.c",line="4"},frame={level="1",addr="0x00000000004004da",func="main",file="left.c",fullname="/home/weigl/work/bigdb/left.c",line="17"}] def load_interactive(self, *args): dialog = Gtk.FileChooserDialog( "Load file", self.get_toplevel(), Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) response = dialog.run() if response == Gtk.ResponseType.OK: self.load(dialog.get_filename()) elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def save_interactive(self, *args): print(self.location) if self.location: self.save() else: dialog = Gtk.FileChooserDialog( "Save file", self.get_toplevel(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE_AS, Gtk.ResponseType.OK)) response = dialog.run() if response == Gtk.ResponseType.OK: self.location = Path(dialog.get_filename()) self.save() elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def load(self, filename): with open(filename) as fh: self.code.set_text(fh.read()) self.location = Path(filename) lang = get_gtk_language(self.location.suffix[1:]) self.code.set_language(lang) def save(self): if self.location: self.location.write_text(self.code.get_text()) os.system("(cd %s; make)" % self.location.parent) def set_gdb_callback(self, func, arg=None): self._gdb_callback = func self._gdb_callback_arg = arg
def test_controller(self): """Build a simple C program, then run it with GdbController and verify the output is parsed as expected""" # Initialize object that manages gdb subprocess gdbmi = GdbController() c_hello_world_binary = self._get_c_program("hello", "pygdbmiapp.a") if USING_WINDOWS: c_hello_world_binary = c_hello_world_binary.replace("\\", "/") # Load the binary and its symbols in the gdb subprocess responses = gdbmi.write("-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1) # Verify output was parsed into a list of responses assert len(responses) != 0 response = responses[0] assert set(response.keys()) == { "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"] is None responses = gdbmi.write( ["-file-list-exec-source-files", "-break-insert main"]) assert len(responses) != 0 responses = gdbmi.write(["-exec-run", "-exec-continue"], timeout_sec=3) found_match = False print(responses) for r in responses: if (r.get( "payload", "" ) == " leading spaces should be preserved. So should trailing spaces. " ): found_match = True assert found_match is True # Test GdbTimeoutError exception got_timeout_exception = False try: gdbmi.get_gdb_response(timeout_sec=0) except GdbTimeoutError: got_timeout_exception = True assert got_timeout_exception is True # Close gdb subprocess if not USING_WINDOWS: # access denied on windows gdbmi.send_signal_to_gdb("SIGINT") gdbmi.send_signal_to_gdb(2) gdbmi.interrupt_gdb() responses = gdbmi.exit() assert responses is None assert gdbmi.gdb_process is None # Test NoGdbProcessError exception got_no_process_exception = False try: responses = gdbmi.write("-file-exec-and-symbols %s" % c_hello_world_binary) except NoGdbProcessError: got_no_process_exception = True assert got_no_process_exception is True # Respawn and test signal handling gdbmi.spawn_new_gdb_subprocess() responses = gdbmi.write("-file-exec-and-symbols %s" % c_hello_world_binary, timeout_sec=1) responses = gdbmi.write(["-break-insert main", "-exec-run"]) if not USING_WINDOWS: gdbmi.interrupt_gdb() gdbmi.send_signal_to_gdb(2) gdbmi.send_signal_to_gdb("sigTeRm") try: gdbmi.send_signal_to_gdb("sigterms") # exception must be raised assert False except ValueError: assert True responses = gdbmi.write("-exec-run") if not USING_WINDOWS: gdbmi.send_signal_to_gdb("sigstop")
class CrackMe(): 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())) def wait_for_resp(self): msgs = [] out = {} while True: resp = self.gdbmi.get_gdb_response(timeout_sec=4, raise_error_on_timeout=False) msgs += resp for m in resp: m = to_namespace(m) if m.type != 'result': continue out['result'] = m.message return msgs, out def run(self): self.gdbmi.write('run', read_response=False) return self.process_execution() def cont(self): self.gdbmi.write('continue', read_response=False) return self.process_execution() def si(self): self.gdbmi.write('si', read_response=False) return self.process_execution() def ni(self): self.gdbmi.write('ni', read_response=False) return self.process_execution() def breakpoint(self, addr): addr = filter_str(addr) self.gdbmi.write('break *' + addr, read_response=False) msgs, out = self.wait_for_resp() return out def set(self, arg): arg = filter_str(arg) self.gdbmi.write('set ' + arg, read_response=False) msgs, out = self.wait_for_resp() return out def disassemble(self, arg): arg = filter_str(arg) self.gdbmi.write('disassemble ' + arg, read_response=False) msgs, out = self.wait_for_resp() data = '' for m in msgs: m = to_namespace(m) if m.type == 'console': data += m.payload data = data.encode('latin-1').decode('unicode_escape') out['data'] = data return out def memory(self, arg): arg = filter_str(arg) self.gdbmi.write('x/' + arg, read_response=False) msgs, out = self.wait_for_resp() data = '' for m in msgs: m = to_namespace(m) if m.type == 'console': data += m.payload data = data.encode('latin-1').decode('unicode_escape') out['data'] = data return out def registers(self): self.gdbmi.write('i r', read_response=False) msgs, out = self.wait_for_resp() data = '' for m in msgs: m = to_namespace(m) if m.type == 'console': data += m.payload data = data.encode('latin-1').decode('unicode_escape') data = data.strip().split('\n') regs = {x[0]: x[1] for x in (y.split() for y in data) if len(x) >= 2} out['registers'] = regs return out def process_execution(self): run_output = '' running = True out = {} # Loop until execution stops while running: resp = self.gdbmi.get_gdb_response(timeout_sec=4, raise_error_on_timeout=False) for m in resp: m = to_namespace(m) # Console output if m.type == 'output': run_output += m.payload if m.type == 'result' and m.message == 'error': running = False out['stop_reason'] = m.payload.msg # Program stopped if m.type == 'notify': if m.message == 'stopped': running = False reason = m.payload.reason out['stop_reason'] = reason if reason == 'breakpoint-hit': out['bp_addr'] = m.payload.frame.addr out['output'] = run_output return out
class Gdb: 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 def __write(self, cmd): return self.gdbmi.write(cmd, timeout_sec=self.timeout) def file_and_exec_symbols(self, filepath): response = self.__write("-file-exec-and-symbols %s" % (filepath)) logger.info("Response: " + str(response)) self.__parse_response(response) if not self.result or self.result == "error": logger.error("GDB unable to load exec and symbols file: %s" % filepath) return logger.debug("GDB loaded exec and symbols file: %s" % filepath) def remote(self, address): response = self.__write("-target-select remote %s" % (address)) self.__parse_response(response) if not self.result or self.result == "error": logger.error("GDB unable to target remote to %s" % (address)) return logger.debug("GDB connect to remote %s" % (address)) def load(self): response = self.__write("-target-download") self.__parse_response(response) if self.result and self.result == "error": logger.error("GDB unable to do target download") return def insert_bp(self, location): logger.info("Inserting breakpoint @location: " + location) response = self.__write("-break-insert %s" % (location)) self.__parse_response(response) if not self.result or self.result == "error": return False return True def delete_bp(self, number): logger.info("Deleting breakpoint: " + number) response = self.__write("-break-delete %s" % (number)) self.__parse_response(response) def go(self): response = self.__write("-exec-continue") self.__parse_response(response) logger.info("Continue") def pause(self): #self.gdbmi.interrupt_gdb() self.__write("-exec-interrupt --all") response = self.gdbmi.get_gdb_response(timeout_sec=self.timeout) self.__parse_response(response) logger.info("Pause") def step(self): response = self.__write("-exec-step") self.__parse_response(response) logger.info("Step") def __parse_response(self, response): global vim unused = [] self.result = None self.bp_number = None self.bp_line = None for r in response: if r["type"] == "notify": if r["message"] == "stopped": p = r["payload"] if 'frame' in p: self.pc.filepath = p["frame"].get("fullname", None) self.pc.line = p["frame"].get("line", None) if self.pc.filepath and self.pc.line: # open file and move cursor on line vim.execute(":e %s" % (self.pc.filepath)) vim.execute(":%s" % (self.pc.line)) self.pc.place() if "reason" in p and p["reason"] == "signal-received": vim.echo("GDB: Segmentation fault") pass elif r["message"] == "library-loaded": libinfo = r["payload"] logger.debug("Gdb: library loaded: %s" % (libinfo["target-name"])) elif r["message"] == "library-unloaded": libinfo = r["payload"] logger.debug("Gdb: library unloaded: %s" % (libinfo["target-name"])) elif r["message"] in [ "thread-group-exited", "thread-group-started", "thread-group-added", "thread-created", "thread-exited", "running", "breakpoint-modified" ]: # TODO: treat this? pass else: unused.append(r) elif r["type"] == "log": logger.debug("GDB: %s" % (r["payload"])) elif r["type"] == "result": self.result = r["message"] if r["payload"] and "bkpt" in r["payload"]: self.bp_number = r["payload"]["bkpt"].get("number", None) self.bp_line = r["payload"]["bkpt"].get("line", None) elif r["type"] == "console": # ignore cosole output for now pass elif r["type"] == "output": if r["stream"] == "stdout": logger.info("%s" % (r["payload"])) else: unused.append(r) if unused: logger.debug("From GDB - not treated:\n" + pprint.pformat(unused))
from pygdbmi.gdbcontroller import GdbController # # Globals # DIRNAME = os.path.dirname(os.path.realpath(__file__)) RE_MATCH_ADDY = re.compile(r'0x[0-9a-fA-F]+') SORTARR_PIE_OFF = 0xdb9 # # Init GDB # gdbmi = GdbController() gdbmi.write('set cwd ./bin') gdbmi.write('-file-exec-file ./bin/amicomputable') gdbmi.write('set disassembly-flavor intel') gdbmi.write('set environment LD_PRELOAD %s/quick_sort.so' % DIRNAME) gdbmi.write('run') print("The PIE offset of the sorting function is: %s" % hex(SORTARR_PIE_OFF)) # # Search the sort array function # Cf. Writeup (SOON) # sleep(0.5) gdbmi.send_signal_to_gdb('SIGINT') gdbmi.interrupt_gdb()
def main(): parser = argparse.ArgumentParser() # Ex: python3 gdbioctl.py -v /workspace/difuze/AndroidKernels/kindle_fire_7/WORKSPACE_DIR/out2/vmlinux -f /workspace/difuze/AndroidKernels/kindle_fire_7/WORKSPACE_DIR/out/kindle7_device_ioctl.txt # Ex: python3 gdbioctl.py -v /workspace/difuze/AndroidKernels/kindle_fire_7/WORKSPACE_DIR/out2/vmlinux -f /workspace/difuze/AndroidKernels/kindle_fire_7/WORKSPACE_DIR/out/kindle7_device_ioctl.txt #parser.add_argument('-o', action='store', dest='ioctl_out', help='Destination directory where all the generated interface should be stored.') parser.add_argument( '-v', action='store', dest='vmlinux', help= 'Path of the vmlinux image. The recovered ioctls are stored in this folder.' ) parser.add_argument( '-f', action='store', dest='device_ioctl_file', help= 'The file that conations ioctl and corresponding device file names, Ex: /dev/alarm alarm_ioctl.' ) olddir = os.getcwd() parsed_args = parser.parse_args() print('%s:%s' % (parsed_args.device_ioctl_file, 5)) #Before make vmlinux, these steps should be taken. ''' for f in `find . -name Makefile`; do sed -i "s/-g /-g3 /g" $f; done for f in `find . -name Makefile`; do sed -i "s/-g$/-g3/g" $f; done With make, add this CONFIG_DEBUG_SECTION_MISMATCH=y flag to xxxdeconfig. ''' #Add flag: -fno-inline-functions-called-once os.chdir(os.path.dirname(parsed_args.vmlinux)) outdir = os.path.join(os.path.dirname(parsed_args.vmlinux), 'ioctl_finder_out') outdir2 = os.path.join(os.path.dirname(parsed_args.vmlinux), 'ioctl_preprocessed_out') if not os.path.exists(outdir): os.mkdir(outdir) if not os.path.exists(outdir2): os.mkdir(outdir2) ioctl_set = [] #ff = open('/workspace/difuze/AndroidKernels/huawei/mate9/fuben/Code_Opensource/out/ioctls', 'r') with open(parsed_args.device_ioctl_file, 'r') as ff: ioctl_set = [x.strip() for x in ff.readlines()] device_dict = {} ioctl_list = [] if ' ' in ioctl_set[0]: # Contains devname for device_ioctl in ioctl_set: device_name, ioctl_name = device_ioctl.split(' ') device_dict[ioctl_name] = device_name ioctl_list.append(ioctl_name) ioctl_set = set(ioctl_list) print(device_dict) if DEBUG: ioctl_set.clear() ioctl_set.append('main') print(ioctl_set) #for aioctl in ioctl_set: for aioctl, device_name in device_dict.items(): print('handling %s' % aioctl) ioctl_set.remove(aioctl) gdbmi = GdbController() response = gdbmi.write('file %s' % parsed_args.vmlinux) sourcefile_line_dict = get_line_file_for_ioctl_function_from_gdb( gdbmi, aioctl, allow_multi=True) item_count = 0 for sourcefile, line in sourcefile_line_dict.items(): if sourcefile == '': continue #if sourcefile[0] != '/': # sourcefile = '/workspace/difuze/dwarf/test/'+sourcefile print('%s:%d' % (sourcefile, line)) cmds_vars = handle_subprogram(gdbmi, source_name=sourcefile, decl_line=line, function_name=aioctl, depth=0, list_subprograms=[]) #print(cmds_vars) cmdstypes, restruct = handle_BEGINTYPE_ENDTYPE(gdbmi, cmds_vars) if restruct is not None: if item_count == 0: processed_filename = os.path.join(outdir2, aioctl + '.processed') txt_filename = os.path.join(outdir, aioctl + '.txt') else: processed_filename = os.path.join( outdir2, aioctl + str(item_count) + '.processed') txt_filename = os.path.join( outdir, aioctl + str(item_count) + '.txt') with open(processed_filename, 'w') as f: f.write(restruct) print(processed_filename + ':1') if cmdstypes is not None: with open(txt_filename, 'w') as f: f.write('O yeah...\n[+] Provided Function Name: %s\n' % aioctl) if device_dict == {}: f.write('Device Name: tododevname\n') else: f.write('Device Name: %s\n' % device_dict[aioctl]) f.write(assign_macros(gdbmi, cmdstypes)) f.write('Compl Preprocessed file:%s\n' % processed_filename) f.write('ALL PREPROCESSED FILES:\n') print(txt_filename + ':10') item_count += 1 gdbmi.exit() time.sleep(2) if len(ioctl_set) == 0: print("All ioctl functions are found.") else: print("%d ioctl functions are not found." % len(ioctl_set)) print(ioctl_set) os.chdir(olddir) print('Recovered interfaces are sotred in:\n%s\n%s' % (outdir, outdir2)) print("Goodbye!")
class Gdb: # Target states TARGET_STATE_UNKNOWN = 0 TARGET_STATE_STOPPED = 1 TARGET_STATE_RUNNING = 2 # Target stop reasons TARGET_STOP_REASON_UNKNOWN = 0 TARGET_STOP_REASON_SIGINT = 1 TARGET_STOP_REASON_SIGTRAP = 2 TARGET_STOP_REASON_BP = 3 TARGET_STOP_REASON_WP = 4 TARGET_STOP_REASON_WP_SCOPE = 5 TARGET_STOP_REASON_STEPPED = 6 TARGET_STOP_REASON_FN_FINISHED = 7 @staticmethod def get_logger(): return logging.getLogger('Gdb') def __init__(self, gdb=None): # Start gdb process self._logger = self.get_logger() if os.name == 'nt': self._gdbmi = GdbController(gdb_path=gdb) # self._gdbmi = GdbControllerWin(gdb_path=gdb) else: 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 self._curr_wp_val = None def _on_notify(self, rec): if rec['message'] == 'stopped': self._target_state = self.TARGET_STATE_STOPPED self._curr_frame = rec['payload']['frame'] if 'reason' in rec['payload']: if rec['payload']['reason'] == 'breakpoint-hit': self._target_stop_reason = self.TARGET_STOP_REASON_BP elif rec['payload']['reason'] == 'watchpoint-trigger': self._target_stop_reason = self.TARGET_STOP_REASON_WP self._curr_wp_val = rec['payload']['value'] elif rec['payload']['reason'] == 'watchpoint-scope': self._target_stop_reason = self.TARGET_STOP_REASON_WP_SCOPE elif rec['payload']['reason'] == 'end-stepping-range': self._target_stop_reason = self.TARGET_STOP_REASON_STEPPED elif rec['payload']['reason'] == 'function-finished': self._target_stop_reason = self.TARGET_STOP_REASON_FN_FINISHED elif rec['payload']['reason'] == 'signal-received': if rec['payload']['signal-name'] == 'SIGINT': self._target_stop_reason = self.TARGET_STOP_REASON_SIGINT elif rec['payload']['signal-name'] == 'SIGTRAP': self._target_stop_reason = self.TARGET_STOP_REASON_SIGTRAP else: self._logger.warning('Unknown signal received "%s"!', rec['payload']['signal-name']) self._target_stop_reason = self.TARGET_STOP_REASON_UNKNOWN else: self._logger.warning('Unknown target stop reason "%s"!', rec['payload']['reason']) self._target_stop_reason = self.TARGET_STOP_REASON_UNKNOWN else: self._target_stop_reason = self.TARGET_STOP_REASON_UNKNOWN elif rec['message'] == 'running': self._target_state = self.TARGET_STATE_RUNNING def _parse_mi_resp(self, new_resp, new_tgt_state): result = None result_body = None old_state = self._target_state # if any cached records go first resp = self._resp_cache + new_resp processed_recs = 0 for rec in resp: processed_recs += 1 if rec['type'] == 'log': self._logger.debug('LOG: %s', pformat(rec['payload'])) elif rec['type'] == 'console': self._logger.info('CONS: %s', pformat(rec['payload'])) elif rec['type'] == 'notify': self._logger.info('NOTIFY: %s %s', rec['message'], pformat(rec['payload'])) self._on_notify(rec) # stop upon result receiption if we do not expect target state change if self._target_state != old_state and self._target_state == new_tgt_state: self._logger.debug('new target state %d', self._target_state) break elif rec['type'] == 'result': self._logger.debug('RESULT: %s %s', rec['message'], pformat(rec['payload'])) result = rec['message'] result_body = rec['payload'] # stop upon result receiption if we do not expect target state change if not new_tgt_state: break # cache unprocessed records self._resp_cache = resp[processed_recs:] # self._logger.debug('cached recs: %s', pformat(self._resp_cache)) return result, result_body def _mi_cmd_run(self, cmd, new_tgt_state=None, tmo=5): self._logger.debug('MI->: %s', cmd) response = [] if tmo: end = time.time() + tmo try: response = self._gdbmi.write(cmd, timeout_sec=tmo) except: self._gdbmi.verify_valid_gdb_subprocess() else: while len(response) == 0: response = self._gdbmi.write(cmd, raise_error_on_timeout=False) self._logger.debug('MI<-:\n%s', pformat(response)) res, res_body = self._parse_mi_resp(response, new_tgt_state) while not res: # check for result report from GDB response = self._gdbmi.get_gdb_response( 1, raise_error_on_timeout=False) if len(response) == 0: if tmo and (time.time() >= end): raise DebuggerTargetStateTimeoutError( 'Failed to wait for completion of command "%s" / %s!' % (cmd, tmo)) else: self._logger.debug('MI<-:\n%s', pformat(response)) res, res_body = self._parse_mi_resp(response, new_tgt_state) return res, res_body def console_cmd_run(self, cmd): self._mi_cmd_run('-interpreter-exec console %s' % cmd) def target_select(self, tgt_type, tgt_params): # -target-select type parameters res, _ = self._mi_cmd_run('-target-select %s %s' % (tgt_type, tgt_params)) if res != 'connected': raise DebuggerError('Failed to connect to "%s %s"!' % (tgt_type, tgt_params)) def target_disconnect(self): # -target-disconnect self._mi_cmd_run('-target-disconnect') def target_reset(self, action='halt'): self.monitor_run('reset %s' % action) if action == 'halt': self.wait_target_state(self.TARGET_STATE_STOPPED, 5) self.console_cmd_run('flushregs') def target_download(self): raise NotImplementedError('target_download') def target_program(self, file_name, off, actions='verify', tmo=30): # actions can be any or both of 'verify reset' self.monitor_run( 'program_esp %s %s 0x%x' % (fixup_path(file_name), actions, off), tmo) def exec_file_set(self, file_path): # -file-exec-and-symbols file self._logger.debug('exec_file_set %s' % file_path) res, _ = self._mi_cmd_run('-file-exec-and-symbols %s' % fixup_path(file_path)) if res != 'done': raise DebuggerError('Failed to set program file!') def exec_interrupt(self): global OS_INT_SIG # Hack, unfortunately GDB does not react on -exec-interrupt, # so send CTRL+C to it self._logger.debug('MI->: send SIGINT') self._gdbmi.gdb_process.send_signal(OS_INT_SIG) # # -exec-interrupt [--all|--thread-group N] # res,_ = self._mi_cmd_run('-exec-interrupt --all') # if res != 'done': # raise DebuggerError('Failed to stop program!') def exec_continue(self): # -exec-continue [--reverse] [--all|--thread-group N] res, _ = self._mi_cmd_run('-exec-continue --all') if res != 'running': raise DebuggerError('Failed to continue program!') def exec_jump(self, loc): # -exec-jump location res, _ = self._mi_cmd_run('-exec-jump %s' % loc) if res != 'running': raise DebuggerError('Failed to make jump in program!') def exec_next(self): # -exec-next [--reverse] res, _ = self._mi_cmd_run('-exec-next') if res != 'running': raise DebuggerError('Failed to step over!') def exec_step(self): # -exec-step [--reverse] res, _ = self._mi_cmd_run('-exec-step') if res != 'running': raise DebuggerError('Failed to step in!') def exec_finish(self): # -exec-finish [--reverse] res, _ = self._mi_cmd_run('-exec-finish') if res != 'running': raise DebuggerError('Failed to step out!') def exec_next_insn(self): # -exec-next-instruction [--reverse] res, _ = self._mi_cmd_run('-exec-next-instruction') if res != 'running': raise DebuggerError('Failed to step insn!') def data_eval_expr(self, expr): # -data-evaluate-expression expr res, res_body = self._mi_cmd_run('-data-evaluate-expression %s' % expr) if res != 'done' or not res_body or 'value' not in res_body: raise DebuggerError('Failed to eval expression!') return res_body['value'] def get_reg(self, nm): sval = self.data_eval_expr('$%s' % nm) # for PC we get something like '0x400e0db8 <gpio_set_direction>' # TODO: use regexp to extract value fn_start = sval.find(' <') if fn_start != -1: sval = sval[:fn_start] return int(sval, 0) def get_variables_at_frame(self, thread_num=None, frame_num=0): # -stack-list-variables [ --no-frame-filters ] [ --skip-unavailable ] print-values if thread_num: cmd = '-stack-list-variables --thread %d --frame %d --all-values' % ( thread_num, frame_num) else: cmd = '-stack-list-variables --all-values' res, res_body = self._mi_cmd_run(cmd) if res != 'done' or not res_body or 'variables' not in res_body: raise DebuggerError( 'Failed to get variables @ frame %d of thread %d!' % (frame_num, thread_num)) return res_body['variables'] def get_backtrace(self): # -stack-list-frames [ --no-frame-filters low-frame high-frame ] res, res_body = self._mi_cmd_run('-stack-list-frames') if res != 'done' or not res_body or 'stack' not in res_body: raise DebuggerError('Failed to get backtrace!') return res_body['stack'] def select_frame(self, frame): # -stack-select-frame framenum res, _ = self._mi_cmd_run('-stack-select-frame %d' % frame) if res != 'done': raise DebuggerError('Failed to get backtrace!') def add_bp(self, loc, ignore_count=0, cond=''): # -break-insert [ -t ] [ -h ] [ -f ] [ -d ] [ -a ] [ -c condition ] [ -i ignore-count ] [ -p thread-id ] [ location ] cmd_args = '-i %d %s' % (ignore_count, loc) if len(cond): cmd_args = '-c "%s" %s' % (cond, cmd_args) res, res_body = self._mi_cmd_run('-break-insert %s' % cmd_args) if res != 'done' or not res_body or 'bkpt' not in res_body or 'number' not in res_body[ 'bkpt']: raise DebuggerError('Failed to insert BP!') return res_body['bkpt']['number'] def add_wp(self, exp, tp='w'): # -break-watch [ -a | -r ] expr cmd_args = '"%s"' % exp if tp == 'r': cmd_args = '-r %s' % cmd_args elif tp == 'rw': cmd_args = '-a %s' % cmd_args res, res_body = self._mi_cmd_run('-break-watch %s' % cmd_args) if res != 'done' or not res_body: raise DebuggerError('Failed to insert WP!') if tp == 'w': if 'wpt' not in res_body or 'number' not in res_body['wpt']: raise DebuggerError('Failed to insert WP!') return res_body['wpt']['number'] elif tp == 'r': if 'hw-rwpt' not in res_body or 'number' not in res_body['hw-rwpt']: raise DebuggerError('Failed to insert RWP!') return res_body['hw-rwpt']['number'] elif tp == 'rw': if 'hw-awpt' not in res_body or 'number' not in res_body['hw-awpt']: raise DebuggerError('Failed to insert AWP!') return res_body['hw-awpt']['number'] return None def delete_bp(self, bp): # -break-delete ( breakpoint )+ res, _ = self._mi_cmd_run('-break-delete %s' % bp) if res != 'done': raise DebuggerError('Failed to delete BP!') def monitor_run(self, cmd, tmo=None): res, resp = self._mi_cmd_run('mon %s' % cmd, tmo=tmo) if res != 'done': raise DebuggerError('Failed to run monitor cmd "%s"!' % cmd) return resp def wait_target_state(self, state, tmo=None): if tmo: end = time.time() + tmo while self._target_state != state: recs = [] if len(self._resp_cache): recs = self._resp_cache else: # check for target state change report from GDB recs = self._gdbmi.get_gdb_response( 1, raise_error_on_timeout=False) if tmo and len(recs) == 0 and time.time() >= end: raise DebuggerTargetStateTimeoutError( "Failed to wait for target state %d!" % state) self._parse_mi_resp(recs, state) return self._target_stop_reason def get_target_state(self): return self._target_state, self._target_stop_reason def get_current_frame(self): return self._curr_frame def get_current_wp_val(self): return self._curr_wp_val def connect(self): global OOCD_PORT self.target_select('remote', ':%d' % OOCD_PORT) def disconnect(self): self.target_disconnect() def get_thread_info(self, thread_id=None): # -thread-info [ thread-id ] if thread_id: cmd = '-thread-info %d' % thread_id else: cmd = '-thread-info' res, res_body = self._mi_cmd_run(cmd) if res != 'done' or not res_body or 'threads' not in res_body or 'current-thread-id' not in res_body: raise DebuggerError('Failed to get thread info!') return (res_body['current-thread-id'], res_body['threads']) def set_thread(self, num): res, _ = self._mi_cmd_run('-thread-select %d' % num) if res != 'done': raise DebuggerError('Failed to set thread!') return res def get_thread_ids(self): # -thread-list-ids expr res, thread_ids = self._mi_cmd_run('-thread-list-ids') if res != 'done': raise DebuggerError('Failed to eval expression!') return thread_ids def gcov_dump(self, on_the_fly=True): if on_the_fly: cmd = '%s gcov' % Oocd.current_target_name_get() else: cmd = '%s gcov dump' % Oocd.current_target_name_get() self.monitor_run(cmd, tmo=20) def sysview_start(self, file1, file2=''): self.monitor_run('%s sysview start %s %s' % (Oocd.current_target_name_get(), file1, file2)) def sysview_stop(self): self.monitor_run('%s sysview stop' % Oocd.current_target_name_get())