From 69409d75372ce4bb0a66de4a1de0e56ed28c20b3 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:40:46 +0300 Subject: [PATCH 01/37] Complete rewrite of gdbserver --- qiling/debugger/gdb/gdb.py | 1625 +++++++++++++++++--------------- qiling/debugger/gdb/utils.py | 115 +-- qiling/debugger/gdb/xmlregs.py | 110 +++ 3 files changed, 1022 insertions(+), 828 deletions(-) create mode 100644 qiling/debugger/gdb/xmlregs.py diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 8b341666..574341a4 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -3,47 +3,69 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -# gdbserver --remote-debug 0.0.0.0:9999 /path/to binary -# documentation: according to https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol +# for watching actual protocol messages: +# server: gdbserver --remote-debug 127.0.0.1:9999 /path/to/exec +# client: gdb -q -ex "target remote 127.0.0.1:9999" +# +# also, run this command on the gdb client: +# (gdb) set debug remote 1 +# +# gdb remote protocol: +# https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html -import struct, os, socket -from binascii import unhexlify -from typing import Iterator, Literal +import os, socket, re +from logging import Logger +from typing import Iterator, Optional, Union + +from unicorn import UcError +from unicorn.unicorn_const import ( + UC_ERR_READ_UNMAPPED, UC_ERR_WRITE_UNMAPPED, UC_ERR_FETCH_UNMAPPED, + UC_ERR_READ_PROT, UC_ERR_WRITE_PROT, UC_ERR_FETCH_PROT, + UC_ERR_READ_UNALIGNED, UC_ERR_WRITE_UNALIGNED, UC_ERR_FETCH_UNALIGNED, + UC_ERR_INSN_INVALID +) from qiling import Qiling -from qiling.const import * -from qiling.utils import * +from qiling.const import QL_ARCH, QL_ENDIAN, QL_OS from qiling.debugger import QlDebugger -from qiling.arch.x86_const import reg_map_16 as x86_reg_map_16 -from qiling.arch.x86_const import reg_map_32 as x86_reg_map_32 -from qiling.arch.x86_const import reg_map_64 as x86_reg_map_64 -from qiling.arch.x86_const import reg_map_misc as x86_reg_map_misc -from qiling.arch.x86_const import reg_map_st as x86_reg_map_st -from qiling.arch.arm_const import reg_map as arm_reg_map -from qiling.arch.arm64_const import reg_map as arm64_reg_map -from qiling.arch.mips_const import reg_map as mips_reg_map -from qiling.loader.elf import AUX +from qiling.debugger.gdb import xmlregs from .utils import QlGdbUtils -GDB_SIGNAL_INT = 2 -GDB_SIGNAL_SEGV = 11 -GDB_SIGNAL_GILL = 4 -GDB_SIGNAL_STOP = 17 -GDB_SIGNAL_TRAP = 5 -GDB_SIGNAL_BUS = 10 +# gdb logging prompt +PROMPT = r'gdb>' +# default string encoding +ENCODING = 'latin' + +# define a few handy linux signals +SIGINT = 2 +SIGILL = 4 +SIGTRAP = 5 +SIGABRT = 6 +SIGBUS = 7 +SIGKILL = 9 +SIGSEGV = 11 +SIGALRM = 14 +SIGTERM = 15 +SIGCHLD = 16 +SIGCONT = 17 +SIGSTOP = 18 + +# common replies +REPLY_ACK = b'+' +REPLY_EMPTY = b'' +REPLY_OK = b'OK' + +# reply type +Reply = Union[bytes, str] + +class QlGdb(QlDebugger): + """A simple gdbserver implementation. + """ -class QlGdb(QlDebugger, object): - """docstring for Debugsession""" def __init__(self, ql: Qiling, ip: str = '127.0.01', port: int = 9999): - super(QlGdb, self).__init__(ql) - - self.ql = ql - self.last_pkt = None - self.exe_abspath = os.path.abspath(self.ql.argv[0]) - self.rootfs_abspath = os.path.abspath(self.ql.rootfs) - self.gdb = QlGdbUtils() + super().__init__(ql) if type(port) is str: port = int(port, 0) @@ -51,763 +73,866 @@ class QlGdb(QlDebugger, object): self.ip = ip self.port = port - if self.ql.baremetal: - load_address = self.ql.loader.load_address + if ql.baremetal: + load_address = ql.loader.load_address exit_point = load_address + os.path.getsize(ql.path) - elif self.ql.code: - load_address = self.ql.os.entry_point + elif ql.code: + load_address = ql.os.entry_point exit_point = load_address + len(ql.code) else: load_address = ql.loader.load_address exit_point = load_address + os.path.getsize(ql.path) - if self.ql.baremetal: - self.entry_point = self.ql.loader.entry_point - elif self.ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD) and not self.ql.code: - self.entry_point = self.ql.os.elf_entry + if ql.baremetal: + entry_point = ql.loader.entry_point + elif ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD) and not ql.code: + entry_point = ql.os.elf_entry else: - self.entry_point = self.ql.os.entry_point + entry_point = ql.os.entry_point # Only part of the binary file will be debugged. - if self.ql.entry_point is not None and self.ql.exit_point is not None: - self.entry_point = self.ql.entry_point - exit_point = self.ql.exit_point + if ql.entry_point is not None: + entry_point = ql.entry_point - self.gdb.initialize(self.ql, self.entry_point, exit_point=exit_point, mappings=[(hex(load_address))]) + if ql.exit_point is not None: + exit_point = ql.exit_point - #Setup register tables, order of tables is important - self.tables = { - QL_ARCH.A8086 : list({**x86_reg_map_16, **x86_reg_map_misc}.keys()), - QL_ARCH.X86 : list({**x86_reg_map_32, **x86_reg_map_misc, **x86_reg_map_st}.keys()), - QL_ARCH.X8664 : list({**x86_reg_map_64, **x86_reg_map_misc, **x86_reg_map_st}.keys()), - QL_ARCH.ARM : list({**arm_reg_map}.keys()), - QL_ARCH.CORTEX_M : list({**arm_reg_map}.keys()), - QL_ARCH.ARM64 : list({**arm64_reg_map}.keys()), - QL_ARCH.MIPS : list({**mips_reg_map}.keys()), + self.gdb = QlGdbUtils(ql, entry_point, exit_point) + + self.regsmap = xmlregs.load_regsmap(self.ql.arch.type) + + def run(self): + server = GdbSerialConn(self.ip, self.port, self.ql.log) + killed = False + + def __hexstr(value: int, nibbles: int = 0) -> str: + length = (nibbles or self.ql.arch.bits // 4) // 2 + byteorder = 'little' if self.ql.arch.endian == QL_ENDIAN.EL else 'big' + + return value.to_bytes(length, byteorder).hex() + + def __get_reg_value(reg: Optional[int], pos: int, nibbles: int) -> str: + # reg is either None or uc reg invalid + if reg: + value = self.ql.arch.regs.read(reg) + assert type(value) is int + + hexstr = __hexstr(value, nibbles) + else: + hexstr = 'x' * nibbles + + return hexstr + + def __set_reg_value(reg: Optional[int], pos: int, nibbles: int, hexval: str) -> None: + # reg is neither None nor uc reg invalid + if reg: + assert len(hexval) == nibbles + + val = int(hexval, 16) + + if self.ql.arch.endian == QL_ENDIAN.EL: + val = __swap_endianess(val) + + self.ql.arch.regs.write(reg, val) + + def __swap_endianess(value: int) -> int: + length = (value.bit_length() + 7) // 8 + raw = value.to_bytes(length, 'little') + + return int.from_bytes(raw, 'big') + + + def handle_exclaim(subcmd: str) -> Reply: + return REPLY_OK + + + def handle_qmark(subcmd: str) -> Reply: + # MIPS32_EL : $T051d:00e7ff7f;25:40ccfc77;#65 + # MIPS32_EB : $T051d:7fff6dc0;25:77fc4880;thread:28fa;core:0; + # ARM64 : $T051d:0*,;1f:80f6f*"ff0* ;20:c02cfdb7f* 0* ;thread:p1f9.1f9;core:0;#56 + # ARM : $T050b:0*"00;0d:e0f6ffbe;0f:8079fdb6;#ae + + response = { + QL_ARCH.X86 : ( 0x05, 0x04, 0x08 ), + QL_ARCH.X8664 : ( 0x06, 0x07, 0x10 ), + QL_ARCH.ARM : ( 0x0b, 0x0d, 0x0f ), + QL_ARCH.ARM64 : ( 0x1d, 0xf1, 0x20 ), + QL_ARCH.MIPS : ( 0x1d, 0x00, 0x25 ), + QL_ARCH.A8086 : ( 0x05, 0x04, 0x08 ), + QL_ARCH.CORTEX_M : ( 0x0b, 0x0d, 0x0f ) + } + + idhex, spid, pcid = response[self.ql.arch.type] + sp = __hexstr(self.ql.arch.regs.arch_sp) + pc = __hexstr(self.ql.arch.regs.arch_pc) + zfill = __hexstr(0) + + info = '' if self.ql.arch.type == QL_ARCH.MIPS else f':{zfill};{spid:02x}' + return f'T{SIGTRAP:02x}{idhex:02x}{info}:{sp};{pcid:02x}:{pc};' + + + def handle_c(subcmd: str) -> Reply: + try: + self.gdb.resume_emu() + except UcError as err: + sigmap = { + UC_ERR_READ_UNMAPPED : SIGSEGV, + UC_ERR_WRITE_UNMAPPED : SIGSEGV, + UC_ERR_FETCH_UNMAPPED : SIGSEGV, + UC_ERR_WRITE_PROT : SIGSEGV, + UC_ERR_READ_PROT : SIGSEGV, + UC_ERR_FETCH_PROT : SIGSEGV, + UC_ERR_READ_UNALIGNED : SIGBUS, + UC_ERR_WRITE_UNALIGNED : SIGBUS, + UC_ERR_FETCH_UNALIGNED : SIGBUS, + UC_ERR_INSN_INVALID : SIGILL + } + + # determine signal from uc error; default to SIGTERM + reply = f'S{sigmap.get(err.errno, SIGTERM):02x}' + + except KeyboardInterrupt: + # emulation was interrupted with ctrl+c + reply = f'S{SIGINT:02x}' + + else: + if self.ql.arch.regs.arch_pc == self.gdb.last_bp: + # emulation stopped because it hit a breakpoint + reply = f'S{SIGTRAP:02x}' + else: + # emulation has completed successfully + reply = f'W{self.ql.os.exit_code:02x}' + + return reply + + + def handle_g(subcmd: str) -> Reply: + # TODO: several obsolete regs cause arm to have a gap just before cpsr. the nonexistant regs + # are represented as None entries just to make sure cpsr stays at index 25. however, it is + # not clear whether its value should follow a series of 'x' (for the non-existant regs) or + # not. the original code suggests not (perhaps because fpa is not specified as an xml feature..?) + # + # non-existant regs are f0-f7 (96 bits each) and fps (32 bits). + # see: ./xml/arm/arm-fpa.xml + # see: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127 + + data = ''.join(__get_reg_value(*entry) for entry in self.regsmap) + + return data + + + def handle_G(subcmd: str) -> Reply: + data = subcmd + + for reg, pos, nibbles in self.regsmap: + if reg: + hexval = data[pos : pos + nibbles] + + if hexval != 'x' * nibbles: + val = int(hexval, 16) + + # TODO: should we swap val's endianess for big-endian targets? + self.ql.arch.regs.write(reg, val) + + return REPLY_OK + + + def handle_H(subcmd: str) -> Reply: + op = subcmd[0] + + if op in ('c', 'g'): + return REPLY_OK + + return REPLY_EMPTY + + + def handle_k(subcmd: str) -> Reply: + global killed + + killed = True + return REPLY_OK + + + def handle_m(subcmd: str) -> Reply: + """Read target memory. + """ + + addr, size = (int(p, 16) for p in subcmd.split(',')) + + try: + data = self.ql.mem.read(addr, size).hex() + except UcError: + return 'E14' + else: + return data + + + def handle_M(subcmd: str) -> Reply: + """Write target memory. + """ + + addr, data = subcmd.split(',') + size, data = data.split(':') + + addr = int(addr, 16) + data = bytes.fromhex(data) + + assert len(data) == size + + try: + self.ql.mem.write(addr, data) + except UcError: + return 'E01' + else: + return REPLY_OK + + + def handle_p(subcmd: str) -> Reply: + """Read register value by index. + """ + + idx = int(subcmd, 16) + + return __get_reg_value(*self.regsmap[idx]) + + + def handle_P(subcmd: str) -> Reply: + """Write register value by index. + """ + + idx, data = subcmd.split('=') + idx = int(idx, 16) + + if idx < len(self.regsmap): + __set_reg_value(*self.regsmap[idx], hexval=data) + + return REPLY_OK + + return 'E00' + + + def handle_Q(subcmd: str) -> Reply: + """General queries. + + @see: https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html + """ + + feature, *data = subcmd.split(':', maxsplit=1) + + supported = ( + 'DisableRandomization', + 'NonStop', + 'PassSignals', + 'ProgramSignals', + 'StartNoAckMode' + ) + + if feature == 'StartNoAckMode': + server.ack_mode = False + + return REPLY_OK if feature in supported else REPLY_EMPTY + + + def handle_D(subcmd: str) -> Reply: + """Detach. + """ + + return REPLY_OK + + + def handle_q(subcmd: str) -> Reply: + query, *data = subcmd.split(':') + + # qSupported command + # + # @see: https://sourceware.org/gdb/onlinedocs/gdb/General-Query-Packets.html#qSupported + + if query == 'Supported': + # list of supported features excluding the multithreading-related ones + common = ( + 'BreakpointCommands+', + 'ConditionalBreakpoints+', + 'ConditionalTracepoints+', + 'DisconnectedTracing+', + 'EnableDisableTracepoints+', + 'InstallInTrace+', + 'QAgent+', + 'QCatchSyscalls+', + 'QDisableRandomization+', + 'QEnvironmentHexEncoded+', + 'QEnvironmentReset+', + 'QEnvironmentUnset+', + 'QNonStop+', + 'QPassSignals+', + 'QProgramSignals+', + 'QSetWorkingDir+', + 'QStartNoAckMode+', + 'QStartupWithShell+', + 'QTBuffer:size+', + 'StaticTracepoints+', + 'TraceStateVariables+', + 'TracepointSource+', + # 'augmented-libraries-svr4-read+', + 'exec-events+', + 'fork-events+', + 'hwbreak+', + 'multiprocess+', + 'no-resumed+', + 'qXfer:auxv:read+', + 'qXfer:exec-file:read+', + 'qXfer:features:read+', + # 'qXfer:libraries-svr4:read+', + # 'qXfer:osdata:read+', + 'qXfer:siginfo:read+', + 'qXfer:siginfo:write+', + 'qXfer:statictrace:read+', + 'qXfer:threads:read+', + 'qXfer:traceframe-info:read+', + 'swbreak+', + 'tracenz+', + 'vfork-events+' + ) + + # might or might not need for multi thread + if self.ql.multithread: + features = ( + 'PacketSize=47ff', + 'FastTracepoints+', + 'QThreadEvents+', + 'Qbtrace-conf:bts:size+', + 'Qbtrace-conf:pt:size+', + 'Qbtrace:bts+', + 'Qbtrace:off+', + 'Qbtrace:pt+', + 'qXfer:btrace-conf:read+', + 'qXfer:btrace:read+', + 'vContSupported+' + ) + + else: + features = ( + 'PacketSize=3fff', + 'qXfer:spu:read+', + 'qXfer:spu:write+' + ) + + return ';'.join(common + features) + + elif query == 'Xfer': + feature, op, annex, params = data + offset, length = (int(p, 16) for p in params.split(',')) + + if feature == 'features' and op == 'read': + xfercmd_abspath = os.path.dirname(os.path.abspath(__file__)) + xml_folder = self.ql.arch.type.name.lower() + xfercmd_file = os.path.join(xfercmd_abspath, 'xml', xml_folder, annex) + + if self.ql.os.type == QL_OS.WINDOWS: + self.ql.log.info(f'{PROMPT} Qiling does not support XML for this platform yet') + content = '' + + elif not os.path.exists(xfercmd_file): + self.ql.log.info(f'{PROMPT} XML file not found: "{xfercmd_file}"') + content = '' + + else: + with open(xfercmd_file, 'r') as f: + f.seek(offset, os.SEEK_SET) + content = f.read(length) + + return f'l{content}' + + elif feature == 'threads' and op == 'read': + if not self.ql.baremetal and hasattr(self.ql.os, 'pid'): + content = '\r\n'.join(( + '', + f'', + '' + )) + + else: + content = '' + + return f'l{content}' + + elif feature == 'auxv' and op == 'read': + auxv_data = bytearray() + + if hasattr(self.ql.loader, 'auxv'): + nbytes = self.ql.arch.bits // 8 + + auxv_addr = self.ql.loader.auxv + offset + null_entry = bytes(nbytes * 2) + + # keep reading until AUXV.AT_NULL is reached + while not auxv_data.endswith(null_entry): + auxv_data.extend(self.ql.mem.read(auxv_addr, nbytes)) + auxv_addr += nbytes + + auxv_data.extend(self.ql.mem.read(auxv_addr, nbytes)) + auxv_addr += nbytes + + return b'l' + auxv_data[:length] + + elif feature == 'exec-file' and op == 'read': + return f'l{os.path.abspath(self.ql.path)}' + + elif feature == 'libraries-svr4' and op == 'read': + # TODO: this one requires information of loaded libraries which currently not provided + # by the ELF loader. until we gather that information, we cannot fulfill this request + # + # see: https://sourceware.org/gdb/current/onlinedocs/gdb/Library-List-Format-for-SVR4-Targets.html + return REPLY_EMPTY + + # if self.ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD): + # tag = 'library-list-svr4' + # xml_lib_entries = (f'' for lbnd, ubnd, _, _, path in self.ql.mem.get_mapinfo() if path) + # + # xml = '\r\n'.join((f'<{tag} version="1.0">', *xml_lib_entries, f'')) + # + # return f'l{xml}' + # else: + # return f'' + + elif feature == 'btrace-conf' and op == 'read': + return 'E.Btrace not enabled.' + + elif query == 'Attached': + return REPLY_EMPTY + + elif query == 'C': + return REPLY_EMPTY + + elif query == 'L': + return 'M001' + + elif query == 'fThreadInfo': + return 'm0' + + elif query == 'sThreadInfo': + return 'l' + + elif query == 'TStatus': + fields = ( + 'T0', + 'tnotrun:0', + 'tframes:0', + 'tcreated:0', + 'tfree:500000', + 'tsize:500000', + 'circular:0', + 'disconn:0', + 'starttime:0', + 'stoptime:0', + 'username:', + 'notes::' + ) + + return ';'.join(fields) + + elif query in ('TfV', 'TsV', 'TfP', 'TsP'): + return 'l' + + elif query == 'Symbol': + return REPLY_OK + + elif query == 'Offsets': + fields = ('Text=0', 'Data=0', 'Bss=0') + + return ';'.join(fields) + + return REPLY_EMPTY + + + def handle_v(subcmd: str) -> Reply: + if subcmd == 'MustReplyEmpty': + return REPLY_EMPTY + + elif subcmd.startswith('File'): + _, op, data = subcmd.split(':', maxsplit=2) + params = data.split(',') + + if op == 'open': + fd = -1 + + # files can be opened only where there is an os that supports filesystem + if not self.ql.interpreter and hasattr(self.ql.os, 'path'): + path, flags, mode = params + + path = bytes.fromhex(path).decode(encoding='utf-8') + flags = int(flags, 16) + mode = int(mode, 16) + + # try to guess whether this is an emulated path or real one + if path.startswith(os.path.abspath(self.ql.rootfs)): + host_path = path + else: + host_path = self.ql.os.path.virtual_to_host_path(path) + + self.ql.log.debug(f'{PROMPT} target file: {host_path}') + + if os.path.exists(host_path) and not path.startswith(r'/proc'): + fd = os.open(host_path, flags, mode) + + return f'F{fd}' + + elif op == 'pread': + fd, count, offset = (int(p, 16) for p in params) + + data = os.pread(fd, count, offset) + + return f'F{len(data):x};'.encode() + data + + elif op == 'close': + fd, *_ = params + fd = int(fd, 16) + + os.close(fd) + return 'F0' + + return REPLY_EMPTY + + elif subcmd.startswith('Kill'): + return handle_k('') + + elif subcmd.startswith('Cont'): + # remove 'Cont' prefix + data = subcmd[len('Cont'):] + + if data == '?': + # note 't' and 'r' are currently not supported + return ';'.join(('vCont', 'c', 'C', 's', 'S')) + + elif data.startswith(';'): + groups = subcmd.split(';')[1:] + + for grp in groups: + cmd, tid = grp.split(':', maxsplit=1) + + if cmd in ('c', f'C{SIGTRAP:02x}'): + return handle_c('') + + elif cmd in ('s', f'S{SIGTRAP:02x}'): + return handle_s('') + + # FIXME: not sure how to handle multiple command + # groups, so handling just the first one + break + + return REPLY_EMPTY + + + def handle_s(subcmd: str) -> Reply: + """Perform a single step. + """ + + self.gdb.resume_emu(steps=1) + + return f'S{SIGTRAP:02x}' + + + def handle_X(subcmd: str) -> Reply: + """Write data to memory. + """ + + params, data = subcmd.split(':', maxsplit=1) + addr, length = (int(p, 16) for p in params.split(',')) + + if length != len(data): + return 'E00' + + try: + if data: + self.ql.mem.write(addr, data.encode(ENCODING)) + except UcError: + return 'E01' + else: + return REPLY_OK + + + def handle_Z(subcmd: str) -> Reply: + """Insert breakpoints or watchpoints. + """ + + params, *conds = subcmd.split(';') + type, addr, kind = (int(p, 16) for p in params.split(',')) + + # type values: + # 0 = sw breakpoint + # 1 = hw breakpoint + # 2 = write watchpoint + # 3 = read watchpoint + # 4 = access watchpoint + + if type == 0: + self.gdb.bp_insert(addr) + return REPLY_OK + + return REPLY_EMPTY + + + def handle_z(subcmd: str) -> Reply: + """Remove breakpoints or watchpoints. + """ + + type, addr, kind = (int(p, 16) for p in subcmd.split(',')) + + if type == 0: + try: + self.gdb.bp_remove(addr) + except ValueError: + return 'E22' + else: + return REPLY_OK + + return REPLY_EMPTY + + + handlers = { + '!': handle_exclaim, + '?': handle_qmark, + 'c': handle_c, + 'C': handle_c, # this is intentional; not a typo + 'D': handle_D, + 'g': handle_g, + 'G': handle_G, + 'H': handle_H, + 'k': handle_k, + 'm': handle_m, + 'M': handle_M, + 'p': handle_p, + 'P': handle_P, + 'q': handle_q, + 'Q': handle_Q, + 's': handle_s, + 'v': handle_v, + 'X': handle_X, + 'Z': handle_Z, + 'z': handle_z } - def addr_to_str(self, addr: int, short: bool = False, endian: Literal['little', 'big'] = 'big') -> str: - # a hacky way to divide archbits by 2 if short, and leave it unchanged if not - nbits = self.ql.arch.bits // (int(short) + 1) + # main server loop + for packet in server.readpackets(): + if server.ack_mode: + server.send(REPLY_ACK, raw=True) - if nbits == 64: - s = f'{int.from_bytes(self.ql.pack64(addr), byteorder=endian):016x}' + cmd, subcmd = packet[0], packet[1:] + handler = handlers.get(f'{cmd:c}') - elif nbits == 32: - s = f'{int.from_bytes(self.ql.pack32(addr), byteorder=endian):08x}' + if handler: + reply = handler(subcmd.decode(ENCODING)) + server.send(reply) - elif nbits == 16: - s = f'{int.from_bytes(self.ql.pack16(addr), byteorder=endian):04x}' - - else: - raise RuntimeError - - return s - - def bin_to_escstr(self, rawbin): - rawbin_escape = "" - - def incomplete_hex_check(hexchar): - if len(hexchar) == 1: - hexchar = "0" + hexchar - return hexchar - - for a in rawbin: - - # The binary data representation uses 7d (ASCII ‘}’) as an escape character. - # Any escaped byte is transmitted as the escape character followed by the original character XORed with 0x20. - # For example, the byte 0x7d would be transmitted as the two bytes 0x7d 0x5d. The bytes 0x23 (ASCII ‘#’), 0x24 (ASCII ‘$’), and 0x7d (ASCII ‘}’) - # must always be escaped. Responses sent by the stub must also escape 0x2a (ASCII ‘*’), - # so that it is not interpreted as the start of a run-length encoded sequence (described next). - - if a in (42,35,36, 125): - a = a ^ 0x20 - a = (str(hex(a)[2:])) - a = incomplete_hex_check(a) - a = str("7d%s" % a) + if killed: + break else: - a = (str(hex(a)[2:])) - a = incomplete_hex_check(a) + self.ql.log.info(f'{PROMPT} command not supported') + server.send(REPLY_EMPTY) - rawbin_escape += a + server.close() - return unhexlify(rawbin_escape) - def setup_server(self): - self.ql.log.info("gdb> Listening on %s:%u" % (self.ip, self.port)) +class GdbSerialConn: + """Serial connection handler. + """ + + # default recieve buffer size + BUFSIZE = 4096 + + def __init__(self, ipaddr: str, port: int, logger: Logger) -> None: + """Create a new gdb serial connection handler. + + Args: + ipaddr : ip address to bind the socket to + port : port number to listen on + logger : logger instance to use + """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind((self.ip, self.port)) - sock.listen(1) - clientsocket, addr = sock.accept() + sock.bind((ipaddr, port)) + sock.listen() - self.sock = sock - self.clientsocket = clientsocket - self.netin = clientsocket.makefile('r') - self.netout = clientsocket.makefile('w') + self.log = logger + self.log.info(f'{PROMPT} listening on {ipaddr}:{port:d}') + + client, _ = sock.accept() + + self.sock = sock + self.client = client + + # ack mode should be turend on by default + self.ack_mode = True def close(self): - self.netin.close() - self.netout.close() - self.clientsocket.close() + """Close the gdb serial connection handler and release its resources. + """ + + self.client.close() self.sock.close() - def run(self): - self.setup_server() - - while self.receive() == 'Good': - pkt = self.last_pkt - self.send_raw('+') - - def handle_qmark(subcmd): - def gdbqmark_converter(arch): - """ - MIPS32_EL : gdbserver response ("$T051d:00e7ff7f;25:40ccfc77;#65") - MIPS32_EB : gdbserver response ("$T051d:7fff6dc0;25:77fc4880;thread:28fa;core:0;"); - ARM64: gdbserver response "$T051d:0*,;1f:80f6f*"ff0* ;20:c02cfdb7f* 0* ;thread:p1f9.1f9;core:0;#56"); - ARM: gdbserver $T050b:0*"00;0d:e0f6ffbe;0f:8079fdb6;#ae" - """ - adapter = { - QL_ARCH.A8086 : [ 0x05, 0x04, 0x08 ], - QL_ARCH.X86 : [ 0x05, 0x04, 0x08 ], - QL_ARCH.X8664 : [ 0x06, 0x07, 0x10 ], - QL_ARCH.MIPS : [ 0x1d, 0x00, 0x25 ], - QL_ARCH.ARM : [ 0x0b, 0x0d, 0x0f ], - QL_ARCH.CORTEX_M : [ 0x0b, 0x0d, 0x0f ], - QL_ARCH.ARM64 : [ 0x1d, 0xf1, 0x20 ] - } - return adapter.get(arch) - - idhex, spid, pcid = gdbqmark_converter(self.ql.arch.type) - sp = self.addr_to_str(self.ql.arch.regs.arch_sp) - pc = self.addr_to_str(self.ql.arch.regs.arch_pc) - nullfill = "0" * int(self.ql.arch.bits / 4) - - if self.ql.arch.type == QL_ARCH.MIPS: - self.send('T%.2x%.2x:%s;%.2x:%s;' %(GDB_SIGNAL_TRAP, idhex, sp, pcid, pc)) - else: - self.send('T%.2x%.2x:%s;%.2x:%s;%.2x:%s;' %(GDB_SIGNAL_TRAP, idhex, nullfill, spid, sp, pcid, pc)) - - - def handle_c(subcmd): - self.gdb.resume_emu(self.ql.arch.regs.arch_pc) - - if self.gdb.bp_list == [self.entry_point]: - self.send("W00") - else: - self.send(('S%.2x' % GDB_SIGNAL_TRAP)) - - - handle_C = handle_c - - - def handle_g(subcmd): - s = '' - - if self.ql.arch.type == QL_ARCH.A8086: - for reg in self.tables[QL_ARCH.A8086][:16]: - r = self.ql.arch.regs.read(reg) - tmp = self.addr_to_str(r) - s += tmp - - elif self.ql.arch.type == QL_ARCH.X86: - for reg in self.tables[QL_ARCH.X86][:16]: - r = self.ql.arch.regs.read(reg) - tmp = self.addr_to_str(r) - s += tmp - - elif self.ql.arch.type == QL_ARCH.X8664: - for reg in self.tables[QL_ARCH.X8664][:24]: - r = self.ql.arch.regs.read(reg) - if self.ql.arch.reg_bits(reg) == 64: - tmp = self.addr_to_str(r) - elif self.ql.arch.reg_bits(reg) == 32: - tmp = self.addr_to_str(r, short = True) - s += tmp - - elif self.ql.arch.type == QL_ARCH.ARM: - - - # r0-r12,sp,lr,pc,cpsr ,see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127 - for reg in self.tables[QL_ARCH.ARM][:16] + [self.tables[QL_ARCH.ARM][25]]: - # if reg is pc, make sure to take thumb mode into account - r = self.ql.arch.effective_pc if reg == "pc" else self.ql.arch.regs.read(reg) - - tmp = self.addr_to_str(r) - s += tmp - - elif self.ql.arch.type == QL_ARCH.ARM64: - for reg in self.tables[QL_ARCH.ARM64][:33]: - r = self.ql.arch.regs.read(reg) - tmp = self.addr_to_str(r) - s += tmp - - elif self.ql.arch.type == QL_ARCH.MIPS: - for reg in self.tables[QL_ARCH.MIPS][:38]: - r = self.ql.arch.regs.read(reg) - tmp = self.addr_to_str(r) - s += tmp - - self.send(s) - - - def handle_G(subcmd): - count = 0 - - if self.ql.arch.type == QL_ARCH.A8086: - for i in range(0, len(subcmd), 8): - reg_data = subcmd[i:i+7] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.A8086][count], reg_data) - count += 1 - - elif self.ql.arch.type == QL_ARCH.X86: - for i in range(0, len(subcmd), 8): - reg_data = subcmd[i:i+7] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.X86][count], reg_data) - count += 1 - - - elif self.ql.arch.type == QL_ARCH.X8664: - for i in range(0, 17*16, 16): - reg_data = subcmd[i:i+15] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.X8664][count], reg_data) - count += 1 - for j in range(17*16, 17*16+15*8, 8): - reg_data = subcmd[j:j+7] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.X8664][count], reg_data) - count += 1 - - elif self.ql.arch.type == QL_ARCH.ARM: - for i in range(0, len(subcmd), 8): - reg_data = subcmd[i:i + 7] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.ARM][count], reg_data) - count += 1 - - elif self.ql.arch.type == QL_ARCH.ARM64: - for i in range(0, len(subcmd), 16): - reg_data = subcmd[i:i+15] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.ARM64][count], reg_data) - count += 1 - - elif self.ql.arch.type == QL_ARCH.MIPS: - for i in range(0, len(subcmd), 8): - reg_data = subcmd[i:i+7] - reg_data = int(reg_data, 16) - self.ql.arch.regs.write(self.tables[QL_ARCH.MIPS][count], reg_data) - count += 1 - - self.send('OK') - - - def handle_H(subcmd): - if subcmd.startswith('g'): - self.send('OK') - if subcmd.startswith('c'): - self.send('OK') - - - def handle_m(subcmd): - addr, size = subcmd.split(',') - addr = int(addr, 16) - size = int(size, 16) - - try: - tmp = '' - for s in range(size): - mem = self.ql.mem.read(addr + s, 1) - mem = "".join( - [str("{:02x}".format(ord(c))) for c in mem.decode('latin1')]) - tmp += mem - self.send(tmp) - - except: - self.send('E14') - - - def handle_M(subcmd): - addr, data = subcmd.split(',') - size, data = data.split(':') - addr = int(addr, 16) - data = bytes.fromhex(data) - try: - self.ql.mem.write(addr, data) - self.send('OK') - except: - self.send('E01') - - - def handle_p(subcmd): - reg_index = int(subcmd, 16) - reg_value = None - try: - if self.ql.arch.type == QL_ARCH.A8086: - if reg_index <= 9: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.A8086][reg_index-1]) - else: - reg_value = 0 - reg_value = self.addr_to_str(reg_value) - - elif self.ql.arch.type == QL_ARCH.X86: - if reg_index <= 24: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.X86][reg_index-1]) - else: - reg_value = 0 - reg_value = self.addr_to_str(reg_value) - - elif self.ql.arch.type == QL_ARCH.X8664: - if reg_index <= 32: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.X8664][reg_index-1]) - else: - reg_value = 0 - if reg_index <= 17: - reg_value = self.addr_to_str(reg_value) - elif 17 < reg_index: - reg_value = self.addr_to_str(reg_value, short = True) - - elif self.ql.arch.type == QL_ARCH.ARM: - if reg_index < 26: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.ARM][reg_index - 1]) - else: - reg_value = 0 - reg_value = self.addr_to_str(reg_value) - - elif self.ql.arch.type == QL_ARCH.ARM64: - if reg_index <= 32: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.ARM64][reg_index - 1]) - else: - reg_value = 0 - reg_value = self.addr_to_str(reg_value) - - elif self.ql.arch.type == QL_ARCH.MIPS: - if reg_index <= 37: - reg_value = self.ql.arch.regs.read(self.tables[QL_ARCH.MIPS][reg_index - 1]) - else: - reg_value = 0 - reg_value = self.addr_to_str(reg_value) - - if type(reg_value) is not str: - reg_value = self.addr_to_str(reg_value) - - self.send(reg_value) - except: - self.close() - raise - - - def handle_P(subcmd): - reg_index, reg_data = subcmd.split('=') - reg_index = int(reg_index, 16) - reg_name = self.tables[self.ql.arch.type][reg_index] - - if self.ql.arch.type == QL_ARCH.A8086: - reg_data = int(reg_data, 16) - reg_data = int.from_bytes(struct.pack(' Write to register %s with %x\n" % (self.tables[self.ql.arch.type][reg_index], reg_data)) - self.send('OK') - - - def handle_Q(subcmd): - if subcmd.startswith( 'StartNoAckMode'): - self.send('OK') - - elif subcmd.startswith( 'DisableRandomization'): - self.send('OK') - - elif subcmd.startswith( 'ProgramSignals'): - self.send('OK') - - elif subcmd.startswith( 'NonStop'): - self.send('OK') - - elif subcmd.startswith('PassSignals'): - self.send('OK') - - elif subcmd.startswith('qemu'): - self.send('') - - def handle_D(subcmd): - self.send('OK') - - def handle_q(subcmd): - if subcmd.startswith('Supported:'): - # might or might not need for multi thread - if self.ql.multithread == False: - self.send("PacketSize=3fff;QPassSignals+;QProgramSignals+;QStartupWithShell+;QEnvironmentHexEncoded+;QEnvironmentReset+;QEnvironmentUnset+;QSetWorkingDir+;QCatchSyscalls+;qXfer:libraries-svr4:read+;augmented-libraries-svr4-read+;qXfer:auxv:read+;qXfer:spu:read+;qXfer:spu:write+;qXfer:siginfo:read+;qXfer:siginfo:write+;qXfer:features:read+;QStartNoAckMode+;qXfer:osdata:read+;multiprocess+;fork-events+;vfork-events+;exec-events+;QNonStop+;QDisableRandomization+;qXfer:threads:read+;ConditionalTracepoints+;TraceStateVariables+;TracepointSource+;DisconnectedTracing+;StaticTracepoints+;InstallInTrace+;qXfer:statictrace:read+;qXfer:traceframe-info:read+;EnableDisableTracepoints+;QTBuffer:size+;tracenz+;ConditionalBreakpoints+;BreakpointCommands+;QAgent+;swbreak+;hwbreak+;qXfer:exec-file:read+;no-resumed+") - else: - self.send("PacketSize=47ff;QPassSignals+;QProgramSignals+;QStartupWithShell+;QEnvironmentHexEncoded+;QEnvironmentReset+;QEnvironmentUnset+;QSetWorkingDir+;QCatchSyscalls+;qXfer:libraries-svr4:read+;augmented-libraries-svr4-read+;qXfer:auxv:read+;qXfer:siginfo:read+;qXfer:siginfo:write+;qXfer:features:read+;QStartNoAckMode+;qXfer:osdata:read+;multiprocess+;fork-events+;vfork-events+;exec-events+;QNonStop+;QDisableRandomization+;qXfer:threads:read+;ConditionalTracepoints+;TraceStateVariables+;TracepointSource+;DisconnectedTracing+;FastTracepoints+;StaticTracepoints+;InstallInTrace+;qXfer:statictrace:read+;qXfer:traceframe-info:read+;EnableDisableTracepoints+;QTBuffer:size+;tracenz+;ConditionalBreakpoints+;BreakpointCommands+;QAgent+;Qbtrace:bts+;Qbtrace-conf:bts:size+;Qbtrace:pt+;Qbtrace-conf:pt:size+;Qbtrace:off+;qXfer:btrace:read+;qXfer:btrace-conf:read+;swbreak+;hwbreak+;qXfer:exec-file:read+;vContSupported+;QThreadEvents+;no-resumed+") - elif subcmd.startswith('Xfer:features:read'): - xfercmd_file = subcmd.split(':')[3] - xfercmd_abspath = os.path.dirname(os.path.abspath(__file__)) - xml_folder = self.ql.arch.type.name.lower() - xfercmd_file = os.path.join(xfercmd_abspath,"xml",xml_folder, xfercmd_file) - - if os.path.exists(xfercmd_file) and self.ql.os.type is not QL_OS.WINDOWS: - with open(xfercmd_file, 'r') as f: - file_contents = f.read() - self.send("l%s" % file_contents) - else: - self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) - self.send("l") - - - elif subcmd.startswith('Xfer:threads:read::0,'): - if self.ql.os.type in QL_OS_NONPID or self.ql.baremetal: - self.send("l") - else: - file_contents = ("\r\n\r\n") - self.send("l" + file_contents) - - elif subcmd.startswith('Xfer:auxv:read::'): - if self.ql.code: - return - - if self.ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD): - def __read_auxv() -> Iterator[int]: - auxv_entries = ( - AUX.AT_HWCAP, - AUX.AT_PAGESZ, - AUX.AT_CLKTCK, - AUX.AT_PHDR, - AUX.AT_PHENT, - AUX.AT_PHNUM, - AUX.AT_BASE, - AUX.AT_FLAGS, - AUX.AT_ENTRY, - AUX.AT_UID, - AUX.AT_EUID, - AUX.AT_GID, - AUX.AT_EGID, - AUX.AT_SECURE, - AUX.AT_RANDOM, - AUX.AT_HWCAP2, - AUX.AT_EXECFN, - AUX.AT_PLATFORM, - AUX.AT_NULL - ) - - for e in auxv_entries: - yield e.value - yield self.ql.loader.aux_vec[e] - - annex = self.addr_to_str(0)[:-2] - sysinfo_ehdr = self.addr_to_str(0) - - auxvdata_c = unhexlify(''.join([annex, sysinfo_ehdr] + [self.addr_to_str(val) for val in __read_auxv()])) - auxvdata = self.bin_to_escstr(auxvdata_c) - else: - auxvdata = b"" - - self.send(b'l!%s' % auxvdata) - - elif subcmd.startswith('Xfer:exec-file:read:'): - self.send("l%s" % str(self.exe_abspath)) - - - elif subcmd.startswith('Xfer:libraries-svr4:read:'): - if self.ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD): - xml_addr_mapping=("") - """ - FIXME: need to find out when do we need this - """ - #for s, e, info in self.ql.map_info: - # addr_mapping += ("" %(info, e, s)) - xml_addr_mapping += ("") - self.send("l%s" % xml_addr_mapping) - else: - self.send("l") - - elif subcmd.startswith("Xfer:btrace-conf:read:"): - self.send("E.Btrace not enabled.") - - elif subcmd == "Attached": - self.send("") - - elif subcmd.startswith("C"): - self.send("") - - elif subcmd.startswith("L:"): - self.send("M001") - - elif subcmd == "fThreadInfo": - self.send("m0") - - elif subcmd == "sThreadInfo": - self.send("l") - - elif subcmd == ("TStatus"): - self.send("T0;tnotrun:0;tframes:0;tcreated:0;tfree:50*!;tsize:50*!;circular:0;disconn:0;starttime:0;stoptime:0;username:;notes::") - - elif subcmd == ("TfV"): - self.send("l") - - elif subcmd == ("TsV"): - self.send("l") - - elif subcmd == ("TfP"): - self.send("l") - - elif subcmd == ("TsP"): - self.send("l") - - - elif subcmd.startswith("Symbol"): - self.send("") - - elif subcmd.startswith("Attached"): - self.send("") - - elif subcmd == "Offsets": - self.send("Text=0;Data=0;Bss=0") - - - def handle_v(subcmd): - - if subcmd == 'MustReplyEmpty': - self.send("") - - elif subcmd.startswith('File:open'): - if self.ql.os.type == QL_OS.UEFI or self.ql.baremetal: - self.send("F-1") - return - - (file_path, flags, mode) = subcmd.split(':')[-1].split(',') - file_path = unhexlify(file_path).decode(encoding='UTF-8') - flags = int(flags, base=16) - mode = int(mode, base=16) - if file_path.startswith(self.rootfs_abspath): - file_abspath = file_path - else: - file_abspath = self.ql.os.path.transform_to_real_path(file_path) - - self.ql.log.debug("gdb> target file: %s" % (file_abspath)) - if os.path.exists(file_abspath) and not (file_path).startswith("/proc"): - fd = os.open(file_abspath, flags, mode) - self.send("F%x" % fd) - else: - self.send("F-1") - return - - elif subcmd.startswith('File:pread:'): - (fd, count, offset) = subcmd.split(':')[-1].split(',') - - fd = int(fd, base=16) - offset = int(offset, base=16) - count = int(count, base=16) - - data = os.pread(fd, count, offset) - size = len(data) - data = self.bin_to_escstr(data) - - if data: - self.send(("F%x;" % size).encode() + (data)) - else: - self.send("F0;") - - elif subcmd.startswith('File:close'): - fd = subcmd.split(':')[-1] - fd = int(fd, base=16) - os.close(fd) - self.send("F0") - - elif subcmd.startswith('Kill'): - self.send('OK') - - elif subcmd.startswith('Cont'): - self.ql.log.debug("gdb> Cont command received: %s" % subcmd) - if subcmd == 'Cont?': - self.send('vCont;c;C;t;s;S;r') - elif subcmd.startswith ("Cont;"): - subcmd = subcmd.split(';') - subcmd = subcmd[1].split(':') - if subcmd[0] in ('c', 'C05'): - handle_c(subcmd) - elif subcmd[0] in ('S', 's', 'S05'): - handle_s(subcmd) - else: - self.send("") - - - def handle_s(subcmd): - current_address = self.gdb.current_address - if current_address is None: - entry_point = self.gdb.entry_point - if entry_point is not None: - self.gdb.soft_bp = True - self.gdb.resume_emu(entry_point) - else: - self.gdb.soft_bp = True - self.gdb.resume_emu() - self.send('S%.2x' % GDB_SIGNAL_TRAP) - - - def handle_X(subcmd): - self.send('') - - - def handle_Z(subcmd): - data = subcmd - ztype = data[data.find('Z') + 1:data.find(',')] - if ztype == '0': - ztype, address, value = data.split(',') - address = int(address, 16) - try: - self.gdb.bp_insert(address) - self.send('OK') - except: - self.send('E22') - else: - self.send('E22') - - - def handle_z(subcmd): - data = subcmd.split(',') - if len(data) != 3: - self.send('E22') - try: - type = data[0] - addr = int(data[1], 16) - length = data[2] - self.gdb.bp_remove(addr, type, length) - self.send('OK') - except: - self.send('E22') - - - def handle_exclaim(subcmd): - self.send('OK') - - commands = { - '!': handle_exclaim, - '?': handle_qmark, - 'c': handle_c, - 'C': handle_C, - 'D': handle_D, - 'g': handle_g, - 'G': handle_G, - 'H': handle_H, - 'm': handle_m, - 'M': handle_M, - 'p': handle_p, - 'P': handle_P, - 'q': handle_q, - 'Q': handle_Q, - 's': handle_s, - 'v': handle_v, - 'X': handle_X, - 'Z': handle_Z, - 'z': handle_z - } - - cmd, subcmd = pkt[0], pkt[1:] - if cmd == 'k': + def readpackets(self) -> Iterator[bytes]: + """Iterate through incoming packets in an active connection until + it is terminated. + """ + + pattern = re.compile(br'^\$(?P[^#]*)#(?P[0-9a-fA-F]{2})') + buffer = bytearray() + + while True: + try: + incoming = self.client.recv(self.BUFSIZE) + except ConnectionError: break - if cmd not in commands: - self.send('') - self.ql.log.info("gdb> Command not supported: %s\n" %(cmd)) + # remote connection closed + if not incoming: + break + + buffer += incoming + + # discard incoming acks + if buffer[0:1] == REPLY_ACK: + del buffer[0] + + packet = pattern.match(buffer) + + # if there is no match, the rest of the packet might be missing + if not packet: continue - self.ql.log.debug("gdb> received: %s%s" % (cmd, subcmd)) - commands[cmd](subcmd) - self.close() + data = packet['data'] + read_csum = int(packet['checksum'], 16) + calc_csum = GdbSerialConn.checksum(data) + if read_csum != calc_csum: + raise IOError(f'checksum error: expected {calc_csum:02x} but got {read_csum:02x}') - def receive(self): - '''Receive a packet from a GDB client''' - csum = 0 - state = 'Finding SOP' - packet = '' - try: - while True: - c = self.netin.read(1) - if c == '\x03': - return 'Error: CTRL+C' + # follow gdbserver debug output format + self.log.debug(f'getpkt ("{GdbSerialConn.__printable_prefix(data).decode(ENCODING)}");') - if len(c) != 1: - return 'Error: EOF' + data = GdbSerialConn.rle_decode(data) + data = GdbSerialConn.unescape(data) - if state == 'Finding SOP': - if c == '$': - state = 'Finding EOP' - elif state == 'Finding EOP': - if c == '#': - if csum != int(self.netin.read(2), 16): - raise Exception('invalid checksum') - self.last_pkt = packet - return 'Good' - else: - packet += c - csum = (csum + ord(c)) & 0xff - else: - raise Exception('should not be here') - except: - self.close() - raise + del buffer[:packet.endpos] + yield data - def checksum(self, data): - checksum = 0 - for c in data: - if type(c) == str: - checksum += (ord(c)) - else: - checksum += c - return checksum & 0xff + def send(self, data: Reply, raw: bool = False) -> None: + """Send out a packet. - def send(self, msg): - """Send a packet to the GDB client""" - if type(msg) == str: - self.send_raw('$%s#%.2x' % (msg, self.checksum(msg))) + Args: + data : data to send out + raw : whether to encapsulate the data with standard header and + checksum or leave it raw + """ + + if type(data) is str: + data = data.encode(ENCODING) + + assert type(data) is bytes + + if raw: + packet = data else: - self.clientsocket.send(b'$%s#%.2x' % (msg, self.checksum(msg))) - self.netout.flush() + data = GdbSerialConn.escape(data) + data = GdbSerialConn.rle_encode(data) - self.ql.log.debug("gdb> send: $%s#%.2x" % (msg, self.checksum(msg))) + packet = b'$' + data + b'#' + f'{GdbSerialConn.checksum(data):02x}'.encode() - def send_raw(self, r): - self.netout.write(r) - self.netout.flush() + # follow gdbserver debug output format + self.log.debug(f'putpkt ("{GdbSerialConn.__printable_prefix(data).decode(ENCODING)}");') + + self.client.sendall(packet) + + @staticmethod + def __printable_prefix(data: bytes) -> bytes: + """Follow the gnu gdbserver debug message format which emits only the + printable prefix of a packet (either incoming or outgoing). Note that + despite of its name, it includes non-printable characters as well. + + Args: + data : packet data to scan + + Returns: a prefix of the specified data buffer + """ + + def __isascii(ch: int) -> bool: + return 0 < ch < 0x80 + + if data.isascii(): + return data + + return data[:next((i for i, ch in enumerate(data) if not __isascii(ch)), len(data))] + + @staticmethod + def escape(data: bytes) -> bytes: + """Escape data according to gdb protocol escaping rules. + """ + + def __repl(m: 're.Match[bytes]') -> bytes: + ch, *_ = m[0] + + return bytes([ord('}'), ch ^ 0x20]) + + return re.sub(br'[*#$}]', __repl, data, flags=re.DOTALL) + + @staticmethod + def unescape(data: bytes) -> bytes: + """Unescape data according to gdb protocol escaping rules. + """ + + def __repl(m: 're.Match[bytes]') -> bytes: + _, ch = m[0] + + return bytes([ch ^ 0x20]) + + return re.sub(br'}.', __repl, data, flags=re.DOTALL) + + @staticmethod + def rle_encode(data: bytes) -> bytes: + """Compact data using run-length encoding. + """ + + def __simple_rep(b: bytes, times: int) -> bytes: + return b * times + + def __runlen_rep(b: bytes, times: int) -> bytes: + return b + b'*' + bytes([times - 1 + 29]) + + def __encode_rep(b: bytes, times: int) -> bytes: + assert times > 0, 'time should be a positive value' + + if 0 < times < 4: + return __simple_rep(b, times) + + elif times == 6+1 or times == 7+1: + return __runlen_rep(b, 6) + __encode_rep(b, times - 6) + + else: + return __runlen_rep(b, times) + + def __repl(m: 're.Match[bytes]') -> bytes: + repetition = m[0] + + ch = repetition[0:1] + times = len(repetition) + + return __encode_rep(ch, times) + + return re.sub(br'(.)\1{3,96}', __repl, data, flags=re.DOTALL) + + @staticmethod + def rle_decode(data: bytes) -> bytes: + """Expand run-length encoded data. + """ + + def __repl(m: 're.Match[bytes]') -> bytes: + ch, _, times = m[0] + + return bytes([ch] * times) + + return re.sub(br'.\*.', __repl, data, flags=re.DOTALL) + + @staticmethod + def checksum(data: bytes) -> int: + return sum(data) & 0xff diff --git a/qiling/debugger/gdb/utils.py b/qiling/debugger/gdb/utils.py index 4c8a5247..30bba47e 100644 --- a/qiling/debugger/gdb/utils.py +++ b/qiling/debugger/gdb/utils.py @@ -3,117 +3,76 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +from typing import Optional + from qiling import Qiling from qiling.const import QL_ARCH +# this code is partially based on uDbg +# @see: https://github.com/iGio90/uDdbg + PROMPT = r'gdb>' class QlGdbUtils: - def __init__(self): - self.ql: Qiling - - self.current_address = 0x0 - self.current_address_size = 0x0 - self.last_bp = 0x0 - self.entry_point = None - self.exit_point = None - self.soft_bp = False - self.has_soft_bp = False - self.bp_list = [] - # self.mapping = [] - self.breakpoint_count = 0x0 - self.skip_bp_count = 0x0 - - - def initialize(self, ql: Qiling, hook_address: int, exit_point: int, mappings=[]): + def __init__(self, ql: Qiling, entry_point: int, exit_point: int): self.ql = ql - if ql.baremetal: - self.current_address = self.entry_point - else: - self.current_address = self.entry_point = ql.os.entry_point - self.exit_point = exit_point - # self.mapping = mappings + self.bp_list = [] + self.last_bp = None def __entry_point_hook(ql: Qiling): ql.hook_del(ep_hret) ql.hook_code(self.dbg_hook) + + ql.log.info(f'{PROMPT} stopped at entry point: {ql.arch.regs.arch_pc:#x}') ql.stop() - ql.log.info(f'{PROMPT} Stop at entry point: {ql.arch.regs.arch_pc:#x}') - - ep_hret = ql.hook_address(__entry_point_hook, hook_address) + # set a one-time hook to be dispatched upon reaching program entry point. + # that hook will be used to set up the breakpoint handling hook + ep_hret = ql.hook_address(__entry_point_hook, entry_point) def dbg_hook(self, ql: Qiling, address: int, size: int): - """Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg - """ + if ql.arch.type == QL_ARCH.ARM and ql.arch.is_thumb: + address += 1 - try: - if ql.arch.type == QL_ARCH.ARM: - if ql.arch.is_thumb: - address += 1 + # resuming emulation after hitting a breakpoint will re-enter this hook. + # avoid an endless hooking loop by detecting and skipping this case + if address == self.last_bp: + self.last_bp = None - # self.mapping.append([(hex(address))]) - self.current_address = address - hit_soft_bp = False + elif address in self.bp_list: + self.last_bp = address - if self.soft_bp == True: - self.soft_bp = False - hit_soft_bp = True - - # Breakpoints are always added without the LSB, even in Thumb, so they should be checked like this as well - if ((address & ~1) in self.bp_list and (address & ~1) != self.last_bp) or self.has_soft_bp == True: - if self.skip_bp_count > 0: - self.skip_bp_count -= 1 - else: - self.breakpoint_count += 1 - ql.stop() - self.last_bp = address - ql.log.info(f'{PROMPT} Breakpoint found, stop at address: {address:#x}') - - elif address == self.last_bp: - self.last_bp = 0x0 - - self.has_soft_bp = hit_soft_bp - - if self.current_address + size == self.exit_point: - ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}') - ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}') - - except KeyboardInterrupt: - ql.log.info(f'{PROMPT} Paused at {address:#x}, instruction size = {size:d}') + ql.log.info(f'{PROMPT} breakpoint hit, stopped at {address:#x}') ql.stop() - except: - raise + # # TODO: not sure what this is about + # if address + size == self.exit_point: + # ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}') + # ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}') def bp_insert(self, addr: int): if addr not in self.bp_list: self.bp_list.append(addr) - self.ql.log.info(f'{PROMPT} Breakpoint added at: {addr:#x}') + self.ql.log.info(f'{PROMPT} breakpoint added at {addr:#x}') - def bp_remove(self, addr: int, type = None, len = None): + def bp_remove(self, addr: int): self.bp_list.remove(addr) - self.ql.log.info(f'{PROMPT} Breakpoint removed at: {addr:#x}') + self.ql.log.info(f'{PROMPT} breakpoint removed from {addr:#x}') - def resume_emu(self, address: int = None, skip_bp: int = 0): - """Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg - """ + def resume_emu(self, address: Optional[int] = None, steps: int = 0): + if address is None: + address = self.ql.arch.regs.arch_pc - if address is not None: - if self.ql.arch.type == QL_ARCH.ARM: - if self.ql.arch.is_thumb: - address += 1 + if self.ql.arch.type == QL_ARCH.ARM and self.ql.arch.is_thumb: + address += 1 - self.current_address = address + op = f'stepping {steps} instructions' if steps else 'resuming' + self.ql.log.info(f'{PROMPT} {op} from {address:#x}') - self.skip_bp_count = skip_bp - - if self.exit_point is not None: - self.ql.log.info(f'{PROMPT} Resume at: {self.current_address:#x}') - self.ql.emu_start(self.current_address, self.exit_point) + self.ql.emu_start(address, self.exit_point, count=steps) diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py new file mode 100644 index 00000000..53199eaf --- /dev/null +++ b/qiling/debugger/gdb/xmlregs.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from typing import Iterator, Mapping, Optional, Sequence, Tuple +from pathlib import PurePath + +from qiling.arch.arm_const import reg_map as arm_regs +from qiling.arch.arm64_const import reg_map as arm64_regs +from qiling.arch.mips_const import reg_map as mips_regs +from qiling.arch.x86_const import reg_map_16 as x86_regs_16 +from qiling.arch.x86_const import reg_map_32 as x86_regs_32 +from qiling.arch.x86_const import reg_map_64 as x86_regs_64 +from qiling.arch.x86_const import reg_map_misc as x86_regs_misc +from qiling.arch.x86_const import reg_map_cr as x86_regs_cr +from qiling.arch.x86_const import reg_map_st as x86_regs_st +from qiling.arch.x86_const import reg_map_xmm as x86_regs_xmm +from qiling.arch.x86_const import reg_map_ymm as x86_regs_ymm + +from qiling.const import QL_ARCH + +RegEntry = Tuple[Optional[int], int, int] + +# define a local dummy function to let us reference this module +__anchor__ = lambda x: x + +def __get_xml_path(archtype: QL_ARCH) -> Tuple[str, PurePath]: + import inspect + + p = PurePath(inspect.getfile(__anchor__)) + basedir = p.parent / 'xml' / archtype.name.lower() + filename = basedir / 'target.xml' + + return str(filename), basedir + +def __walk_xml_regs(filename: str, base_url: PurePath) -> Iterator[Tuple[int, str, int]]: + from xml.etree import ElementTree, ElementInclude + + tree = ElementTree.parse(filename) + root = tree.getroot() + + # NOTE: this is needed to load xinclude hrefs relative to the main xml file. starting + # from python 3.9 ElementInclude.include has an argument for that called 'base_url'. + # this is a workaround for earlier python versions such as 3.8 + + def my_loader(base: PurePath): + def __wrapped(href: str, parse, encoding=None): + abshref = base / href + + return ElementInclude.default_loader(str(abshref), parse, encoding) + + return __wrapped + + ElementInclude.include(root, loader=my_loader(base_url)) + + regnum = -1 + + for reg in root.iter('reg'): + # if regnum is not specified, assume it follows the previous one + regnum = int(reg.get('regnum', regnum + 1)) + + name = reg.attrib['name'] + bitsize = reg.attrib['bitsize'] + + yield regnum, name, int(bitsize) + +def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: + """Initialize registers map using available target XML files. + + Args: + archtype: target architecture type + + Returns: a list representing registers data + """ + + # retreive the relevant set of registers; their order of appearance is not + # important as it is determined by the info read from the xml files + ucregs: Mapping[str, int] = { + QL_ARCH.A8086 : dict(**x86_regs_16, **x86_regs_misc, **x86_regs_cr, **x86_regs_st), + QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm), + QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm), + QL_ARCH.ARM : arm_regs, + QL_ARCH.CORTEX_M : arm_regs, + QL_ARCH.ARM64 : arm64_regs, + QL_ARCH.MIPS : mips_regs + }[archtype] + + regmap = [] + pos = 0 + + xmlpath = __get_xml_path(archtype) + + for regnum, name, bitsize in __walk_xml_regs(*xmlpath): + # regs indices might not be consecutive. + # extend regmap with null entries if needed + if len(regmap) < regnum + 1: + regmap.extend([(None, 0, 0)] * (regnum + 1 - len(regmap))) + + # reg value size in nibbles + nibbles = bitsize // 4 + + regmap[regnum] = (ucregs.get(name), pos, nibbles) + + # value position of next reg + pos += nibbles + + return regmap + +__all__ = ['RegEntry', 'load_regsmap'] From 0a37fbf63f3ff6d9600bd371fca909b86db9bd8c Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:43:41 +0300 Subject: [PATCH 02/37] Add XInclude namespace to allow correct parsing --- qiling/debugger/gdb/xml/a8086/target.xml | 7 ++++++- qiling/debugger/gdb/xml/arm/target.xml | 12 ++++++------ qiling/debugger/gdb/xml/arm64/target.xml | 8 ++++---- qiling/debugger/gdb/xml/mips/target.xml | 19 ++++++++++--------- qiling/debugger/gdb/xml/x86/target.xml | 18 +++++++++--------- qiling/debugger/gdb/xml/x8664/target.xml | 14 +++++++++++++- 6 files changed, 48 insertions(+), 30 deletions(-) diff --git a/qiling/debugger/gdb/xml/a8086/target.xml b/qiling/debugger/gdb/xml/a8086/target.xml index 71daae8c..43bc64d7 100644 --- a/qiling/debugger/gdb/xml/a8086/target.xml +++ b/qiling/debugger/gdb/xml/a8086/target.xml @@ -1 +1,6 @@ -i8086 + + + + i8086 + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/arm/target.xml b/qiling/debugger/gdb/xml/arm/target.xml index 579e84b0..89826d47 100644 --- a/qiling/debugger/gdb/xml/arm/target.xml +++ b/qiling/debugger/gdb/xml/arm/target.xml @@ -6,9 +6,9 @@ *!notice and this notice are preserved. --> - - arm - - - - + + arm + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/arm64/target.xml b/qiling/debugger/gdb/xml/arm64/target.xml index d4dbac66..b91fd952 100644 --- a/qiling/debugger/gdb/xml/arm64/target.xml +++ b/qiling/debugger/gdb/xml/arm64/target.xml @@ -7,8 +7,8 @@ *!notice and this notice are preserved. --> - - aarch64 - - + + aarch64 + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/mips/target.xml b/qiling/debugger/gdb/xml/mips/target.xml index a585dffd..0a6a1b62 100644 --- a/qiling/debugger/gdb/xml/mips/target.xml +++ b/qiling/debugger/gdb/xml/mips/target.xml @@ -6,14 +6,15 @@ *!notice and this notice are preserved. --> - - mips - GNU/Linux - - - + + mips + GNU/Linux - - * - + + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/x86/target.xml b/qiling/debugger/gdb/xml/x86/target.xml index a664320d..50270323 100644 --- a/qiling/debugger/gdb/xml/x86/target.xml +++ b/qiling/debugger/gdb/xml/x86/target.xml @@ -1,11 +1,11 @@ -i386 - GNU/Linux - - - - - - - + +i386 + GNU/Linux + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/x8664/target.xml b/qiling/debugger/gdb/xml/x8664/target.xml index d58e543e..d68705f6 100644 --- a/qiling/debugger/gdb/xml/x8664/target.xml +++ b/qiling/debugger/gdb/xml/x8664/target.xml @@ -1 +1,13 @@ -i386:x86-64GNU/Linux + + + + i386:x86-64 + GNU/Linux + + + + + + + + \ No newline at end of file From d2092bb07518cb6c79fa2cfdd57029ecc509e6fd Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:44:42 +0300 Subject: [PATCH 03/37] Reformat gdb xml files --- qiling/debugger/gdb/xml/x86/32bit-core.xml | 104 ++++++++++---------- qiling/debugger/gdb/xml/x86/32bit-mpx.xml | 64 ++++++------ qiling/debugger/gdb/xml/x86/32bit-pkeys.xml | 6 +- qiling/debugger/gdb/xml/x86/32bit-sse.xml | 84 ++++++++-------- 4 files changed, 130 insertions(+), 128 deletions(-) diff --git a/qiling/debugger/gdb/xml/x86/32bit-core.xml b/qiling/debugger/gdb/xml/x86/32bit-core.xml index 0958032a..702ae3b0 100644 --- a/qiling/debugger/gdb/xml/x86/32bit-core.xml +++ b/qiling/debugger/gdb/xml/x86/32bit-core.xml @@ -7,59 +7,59 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/x86/32bit-mpx.xml b/qiling/debugger/gdb/xml/x86/32bit-mpx.xml index 7772954d..1798dbd8 100644 --- a/qiling/debugger/gdb/xml/x86/32bit-mpx.xml +++ b/qiling/debugger/gdb/xml/x86/32bit-mpx.xml @@ -7,39 +7,41 @@ - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - + + + + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/x86/32bit-pkeys.xml b/qiling/debugger/gdb/xml/x86/32bit-pkeys.xml index 6f3c65a8..71aa7dc1 100644 --- a/qiling/debugger/gdb/xml/x86/32bit-pkeys.xml +++ b/qiling/debugger/gdb/xml/x86/32bit-pkeys.xml @@ -7,7 +7,5 @@ - - - - + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/x86/32bit-sse.xml b/qiling/debugger/gdb/xml/x86/32bit-sse.xml index c57a2d84..e28536e5 100644 --- a/qiling/debugger/gdb/xml/x86/32bit-sse.xml +++ b/qiling/debugger/gdb/xml/x86/32bit-sse.xml @@ -7,46 +7,48 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 4198033ae96c1fdef339fa70dbb76fad7c3f5755 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:46:34 +0300 Subject: [PATCH 04/37] Rename x86 eflags reg to match gdb name --- qiling/arch/x86_const.py | 2 +- qiling/debugger/qdb/arch/arch_x86.py | 4 ++-- qiling/debugger/qdb/branch_predictor/branch_predictor_x86.py | 2 +- qiling/debugger/qdb/frontend.py | 4 ++-- qiling/debugger/qdb/render/render_x86.py | 2 +- qiling/os/dos/dos.py | 4 ++-- qiling/os/macos/utils.py | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/qiling/arch/x86_const.py b/qiling/arch/x86_const.py index 798549ee..1e60479f 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -177,7 +177,7 @@ reg_map_st = { } reg_map_misc = { - "ef": UC_X86_REG_EFLAGS, + "eflags": UC_X86_REG_EFLAGS, "cs": UC_X86_REG_CS, "ss": UC_X86_REG_SS, "ds": UC_X86_REG_DS, diff --git a/qiling/debugger/qdb/arch/arch_x86.py b/qiling/debugger/qdb/arch/arch_x86.py index 4aae910c..10617cbd 100644 --- a/qiling/debugger/qdb/arch/arch_x86.py +++ b/qiling/debugger/qdb/arch/arch_x86.py @@ -21,7 +21,7 @@ class ArchX86(Arch): "eax", "ebx", "ecx", "edx", "esp", "ebp", "esi", "edi", "eip", "ss", "cs", "ds", "es", - "fs", "gs", "ef", + "fs", "gs", "eflags", ) def read_insn(self, address: int) -> bytes: @@ -34,7 +34,7 @@ class ArchX86(Arch): @staticmethod def get_flags(bits: int) -> Mapping[str, bool]: """ - get flags from ql.reg.ef + get flags from ql.reg.eflags """ return { diff --git a/qiling/debugger/qdb/branch_predictor/branch_predictor_x86.py b/qiling/debugger/qdb/branch_predictor/branch_predictor_x86.py index ce92dd9f..656c64da 100644 --- a/qiling/debugger/qdb/branch_predictor/branch_predictor_x86.py +++ b/qiling/debugger/qdb/branch_predictor/branch_predictor_x86.py @@ -91,7 +91,7 @@ class BranchPredictorX86(BranchPredictor, ArchX86): } if line.mnemonic in jump_table: - eflags = self.get_flags(self.ql.arch.regs.ef).values() + eflags = self.get_flags(self.ql.arch.regs.eflags).values() prophecy.going = jump_table.get(line.mnemonic)(*eflags) elif line.mnemonic in jump_reg_table: diff --git a/qiling/debugger/qdb/frontend.py b/qiling/debugger/qdb/frontend.py index d1f53f86..ed243446 100644 --- a/qiling/debugger/qdb/frontend.py +++ b/qiling/debugger/qdb/frontend.py @@ -389,7 +389,7 @@ class CtxManager_X86(CtxManager): "eax", "ebx", "ecx", "edx", "esp", "ebp", "esi", "edi", "eip", "ss", "cs", "ds", "es", - "fs", "gs", "ef", + "fs", "gs", "eflags", ) @context_printer("[ REGISTERS ]") def context_reg(self, saved_reg_dump): @@ -416,7 +416,7 @@ class CtxManager_X86(CtxManager): lines += line print(lines.format(*cur_regs.values())) - print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.ef)), color.END, sep="") + print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.eflags)), color.END, sep="") @context_printer("[ DISASM ]", footer=True) def context_asm(self): diff --git a/qiling/debugger/qdb/render/render_x86.py b/qiling/debugger/qdb/render/render_x86.py index 3597181e..5c43d0e5 100644 --- a/qiling/debugger/qdb/render/render_x86.py +++ b/qiling/debugger/qdb/render/render_x86.py @@ -22,7 +22,7 @@ class ContextRenderX86(ContextRender, ArchX86): cur_regs = self.dump_regs() diff_reg = self.reg_diff(cur_regs, saved_reg_dump) self.render_regs_dump(cur_regs, diff_reg=diff_reg) - print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.ef)), color.END, sep="") + print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.eflags)), color.END, sep="") @Render.divider_printer("[ DISASM ]") def context_asm(self): diff --git a/qiling/os/dos/dos.py b/qiling/os/dos/dos.py index 6fb4d330..fb7f55c1 100644 --- a/qiling/os/dos/dos.py +++ b/qiling/os/dos/dos.py @@ -58,10 +58,10 @@ class QlOsDos(QlOs): curses.endwin() def set_flag_value(self, fl: Flags, val: int) -> None: - self.ql.arch.regs.ef = self.ql.arch.regs.ef & (~fl) | (fl * val) + self.ql.arch.regs.eflags = self.ql.arch.regs.eflags & (~fl) | (fl * val) def test_flags(self, fl): - return self.ql.arch.regs.ef & fl == fl + return self.ql.arch.regs.eflags & fl == fl def set_cf(self): self.set_flag_value(Flags.CF, 0b1) diff --git a/qiling/os/macos/utils.py b/qiling/os/macos/utils.py index 351f0f0b..808fbe29 100644 --- a/qiling/os/macos/utils.py +++ b/qiling/os/macos/utils.py @@ -188,5 +188,5 @@ def page_align_end(addr, page_size): def set_eflags_cf(ql, target_cf): - ql.arch.regs.ef = ( ql.arch.regs.ef & 0xfffffffe ) | target_cf - return ql.arch.regs.ef + ql.arch.regs.eflags = ( ql.arch.regs.eflags & 0xfffffffe ) | target_cf + return ql.arch.regs.eflags From 546cbc986430dbfd291efa841ef532ca5b147bf6 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:47:31 +0300 Subject: [PATCH 05/37] Rename AUX enum --- qiling/loader/elf.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/qiling/loader/elf.py b/qiling/loader/elf.py index c6ba9573..cb62d7c9 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -29,7 +29,7 @@ from qiling.os.linux.kernel_api.kernel_api import hook_sys_open, hook_sys_read, # auxiliary vector types # see: https://man7.org/linux/man-pages/man3/getauxval.3.html -class AUX(IntEnum): +class AUXV(IntEnum): AT_NULL = 0 AT_IGNORE = 1 AT_EXECFD = 2 @@ -317,26 +317,26 @@ class QlLoaderELF(QlLoader): elf_hwcap = 0xd7b81f # setup aux vector - aux_entries = ( - (AUX.AT_PHDR, elf_phdr), - (AUX.AT_PHENT, elf_phent), - (AUX.AT_PHNUM, elf_phnum), - (AUX.AT_PAGESZ, self.ql.mem.pagesize), - (AUX.AT_BASE, interp_address), - (AUX.AT_FLAGS, 0), - (AUX.AT_ENTRY, self.elf_entry), - (AUX.AT_UID, self.ql.os.uid), - (AUX.AT_EUID, self.ql.os.euid), - (AUX.AT_GID, self.ql.os.gid), - (AUX.AT_EGID, self.ql.os.egid), - (AUX.AT_HWCAP, elf_hwcap), - (AUX.AT_CLKTCK, 100), - (AUX.AT_RANDOM, randstraddr), - (AUX.AT_HWCAP2, 0), - (AUX.AT_EXECFN, execfn), - (AUX.AT_PLATFORM, cpustraddr), - (AUX.AT_SECURE, 0), - (AUX.AT_NULL, 0) + auxv_entries = ( + (AUXV.AT_HWCAP, elf_hwcap), + (AUXV.AT_PAGESZ, self.ql.mem.pagesize), + (AUXV.AT_CLKTCK, 100), + (AUXV.AT_PHDR, elf_phdr), + (AUXV.AT_PHENT, elf_phent), + (AUXV.AT_PHNUM, elf_phnum), + (AUXV.AT_BASE, interp_address), + (AUXV.AT_FLAGS, 0), + (AUXV.AT_ENTRY, self.elf_entry), + (AUXV.AT_UID, self.ql.os.uid), + (AUXV.AT_EUID, self.ql.os.euid), + (AUXV.AT_GID, self.ql.os.gid), + (AUXV.AT_EGID, self.ql.os.egid), + (AUXV.AT_SECURE, 0), + (AUXV.AT_RANDOM, randstraddr), + (AUXV.AT_HWCAP2, 0), + (AUXV.AT_EXECFN, execfn), + (AUXV.AT_PLATFORM, cpustraddr), + (AUXV.AT_NULL, 0) ) # add all aux entries From 7258e4cc153c873e10772ed79372c9b244157b49 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:48:36 +0300 Subject: [PATCH 06/37] Have auxv hold mem address instead of dict values --- qiling/loader/elf.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qiling/loader/elf.py b/qiling/loader/elf.py index cb62d7c9..a1be0ef6 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -339,14 +339,17 @@ class QlLoaderELF(QlLoader): (AUXV.AT_NULL, 0) ) - # add all aux entries - for key, val in aux_entries: - elf_table.extend(self.ql.pack(key) + self.ql.pack(val)) + bytes_before_auxv = len(elf_table) + + # add all auxv entries + for key, val in auxv_entries: + elf_table.extend(self.ql.pack(key)) + elf_table.extend(self.ql.pack(val)) new_stack = self.ql.mem.align(new_stack - len(elf_table), 0x10) self.ql.mem.write(new_stack, bytes(elf_table)) - self.aux_vec = dict(aux_entries) + self.auxv = new_stack + bytes_before_auxv self.stack_address = new_stack self.load_address = load_address From 964fc356d5354cdde047f169d0f7b395b5862ec9 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:50:17 +0300 Subject: [PATCH 07/37] Clean up --- qiling/arch/arm_const.py | 1 - qiling/arch/x86.py | 33 --------------------------------- qiling/const.py | 5 ++--- 3 files changed, 2 insertions(+), 37 deletions(-) diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index e9339366..a58d0d1e 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -4,7 +4,6 @@ # from unicorn.arm_const import * -from enum import IntEnum reg_map = { "r0": UC_ARM_REG_R0, diff --git a/qiling/arch/x86.py b/qiling/arch/x86.py index b634849f..97850ef6 100644 --- a/qiling/arch/x86.py +++ b/qiling/arch/x86.py @@ -4,14 +4,11 @@ # from functools import cached_property -from typing import Union from unicorn import Uc, UC_ARCH_X86, UC_MODE_16, UC_MODE_32, UC_MODE_64 from capstone import Cs, CS_ARCH_X86, CS_MODE_16, CS_MODE_32, CS_MODE_64 from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_MODE_32, KS_MODE_64 -from unicorn.x86_const import UC_X86_REG_EFLAGS - from qiling.arch.arch import QlArch from qiling.arch.msr import QlMsrManager from qiling.arch.register import QlRegisterManager @@ -126,33 +123,3 @@ class QlArchX8664(QlArchIntel): @cached_property def assembler(self) -> Ks: return Ks(KS_ARCH_X86, KS_MODE_64) - - # TODO: generalize this - def __reg_bits(self, register: int) -> int: - # all regs in reg_map_misc are 16 bits except of eflags - if register == UC_X86_REG_EFLAGS: - return 32 - - regmaps = ( - (x86_const.reg_map_8, 8), - (x86_const.reg_map_16, 16), - (x86_const.reg_map_32, 32), - (x86_const.reg_map_64, 64), - (x86_const.reg_map_misc, 16), - (x86_const.reg_map_cr, 64), # 32 bits for x86 - (x86_const.reg_map_st, 32), - (x86_const.reg_map_seg_base, 64), # 32 bits for x86 - ) - - return next((rsize for rmap, rsize in regmaps if register in rmap.values()), 0) - - # note: this method was not generalized for all archs since it requires a bookkeeping - # of all registers, while it is used only by gdb and only for x86-64 - def reg_bits(self, reg: Union[str, int]) -> int: - """Get register size in bits. - """ - - if type(reg) is str: - reg = self.regs.register_mapping[reg] - - return self.__reg_bits(reg) diff --git a/qiling/const.py b/qiling/const.py index 1f97e4cb..2b38d71f 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -60,9 +60,8 @@ class QL_STOP(Flag): QL_ARCH_INTERPRETER = (QL_ARCH.EVM,) -QL_OS_NONPID = (QL_OS.DOS, QL_OS.UEFI) -QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX) -QL_OS_BAREMETAL = (QL_OS.MCU,) +QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX) +QL_OS_BAREMETAL = (QL_OS.MCU,) QL_HOOK_BLOCK = 0b0001 QL_CALL_BLOCK = 0b0010 From 01a1941af1c8fe06971824e594f56856964ce1fc Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:52:50 +0300 Subject: [PATCH 08/37] Reduce imports clutter --- qiling/const.py | 5 +++++ qiling/os/memory.py | 3 +-- qiling/os/posix/const_mapping.py | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/qiling/const.py b/qiling/const.py index 2b38d71f..d7ac9f8d 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -82,3 +82,8 @@ arch_os_map = { QL_ARCH.EVM : QL_OS.EVM, QL_ARCH.CORTEX_M : QL_OS.MCU } + +__all__ = [ + 'QL_ENDIAN', 'QL_ARCH', 'QL_OS', 'QL_VERBOSE', 'QL_DEBUGGER', 'QL_INTERCEPT', 'QL_STOP', + 'QL_ARCH_INTERPRETER', 'QL_OS_POSIX', 'QL_OS_BAREMETAL', 'QL_HOOK_BLOCK', 'QL_CALL_BLOCK' +] diff --git a/qiling/os/memory.py b/qiling/os/memory.py index 5357623e..2354f74f 100644 --- a/qiling/os/memory.py +++ b/qiling/os/memory.py @@ -4,12 +4,11 @@ # import os, re -from typing import Any, Callable, List, MutableSequence, Optional, Sequence, Tuple +from typing import Any, Callable, List, Mapping, MutableSequence, Optional, Sequence, Tuple from unicorn import UC_PROT_NONE, UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC, UC_PROT_ALL from qiling import Qiling -from qiling.const import * from qiling.exception import * # tuple: range start, range end, permissions mask, range label, is mmio? diff --git a/qiling/os/posix/const_mapping.py b/qiling/os/posix/const_mapping.py index 41036eb1..99fb31ca 100644 --- a/qiling/os/posix/const_mapping.py +++ b/qiling/os/posix/const_mapping.py @@ -3,11 +3,12 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from typing import MutableSequence -from .const import * +from typing import Mapping, MutableSequence from qiling import Qiling -from qiling.const import * +from qiling.const import QL_ARCH, QL_OS + +from .const import * def _invert_dict(d: Mapping) -> Mapping: return { v:k for k, v in d.items()} From 5f4c3dc9919608e35aa2f58817454e5fe28aeccb Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:53:49 +0300 Subject: [PATCH 09/37] Fix missing imports --- qiling/extensions/report/report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiling/extensions/report/report.py b/qiling/extensions/report/report.py index ea46aaa6..1189e698 100644 --- a/qiling/extensions/report/report.py +++ b/qiling/extensions/report/report.py @@ -12,8 +12,8 @@ class Report: def __init__(self, ql): self.filename = ql.argv self.rootfs = ql.rootfs - self.arch = list(arch_map.keys())[list(arch_map.values()).index(ql.arch.type)] - self.os = list(os_map.keys())[list(os_map.values()).index(ql.os.type)] + self.arch = ql.arch.type.name + self.os = ql.os.type.name self.env = ql.env self.strings = set() for string in ql.os.stats.strings: From baaf9cc5c7a166b4072cc73a955444051b069301 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:54:59 +0300 Subject: [PATCH 10/37] Reformat arm regs map --- qiling/arch/arm_const.py | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index a58d0d1e..d8def122 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -6,36 +6,37 @@ from unicorn.arm_const import * reg_map = { - "r0": UC_ARM_REG_R0, - "r1": UC_ARM_REG_R1, - "r2": UC_ARM_REG_R2, - "r3": UC_ARM_REG_R3, - "r4": UC_ARM_REG_R4, - "r5": UC_ARM_REG_R5, - "r6": UC_ARM_REG_R6, - "r7": UC_ARM_REG_R7, - "r8": UC_ARM_REG_R8, - "r9": UC_ARM_REG_R9, - "r10": UC_ARM_REG_R10, - "r11": UC_ARM_REG_R11, - "r12": UC_ARM_REG_R12, - "sp": UC_ARM_REG_SP, - "lr": UC_ARM_REG_LR, - "pc": UC_ARM_REG_PC, - - # CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34 - # The fp registers inbetween have become obsolete - "f0": UC_ARM_REG_INVALID, - "f1": UC_ARM_REG_INVALID, - "f2": UC_ARM_REG_INVALID, - "f3": UC_ARM_REG_INVALID, - "f4": UC_ARM_REG_INVALID, - "f5": UC_ARM_REG_INVALID, - "f6": UC_ARM_REG_INVALID, - "f7": UC_ARM_REG_INVALID, - "fps": UC_ARM_REG_INVALID, - "cpsr": UC_ARM_REG_CPSR, - "c1_c0_2": UC_ARM_REG_C1_C0_2, - "c13_c0_3": UC_ARM_REG_C13_C0_3, - "fpexc": UC_ARM_REG_FPEXC, + "r0": UC_ARM_REG_R0, + "r1": UC_ARM_REG_R1, + "r2": UC_ARM_REG_R2, + "r3": UC_ARM_REG_R3, + "r4": UC_ARM_REG_R4, + "r5": UC_ARM_REG_R5, + "r6": UC_ARM_REG_R6, + "r7": UC_ARM_REG_R7, + "r8": UC_ARM_REG_R8, + "r9": UC_ARM_REG_R9, + "r10": UC_ARM_REG_R10, + "r11": UC_ARM_REG_R11, + "r12": UC_ARM_REG_R12, + "sp": UC_ARM_REG_SP, + "lr": UC_ARM_REG_LR, + "pc": UC_ARM_REG_PC, + + # CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34 + # The fp registers inbetween have become obsolete + "f0": UC_ARM_REG_INVALID, + "f1": UC_ARM_REG_INVALID, + "f2": UC_ARM_REG_INVALID, + "f3": UC_ARM_REG_INVALID, + "f4": UC_ARM_REG_INVALID, + "f5": UC_ARM_REG_INVALID, + "f6": UC_ARM_REG_INVALID, + "f7": UC_ARM_REG_INVALID, + "fps": UC_ARM_REG_INVALID, + + "cpsr": UC_ARM_REG_CPSR, + "c1_c0_2": UC_ARM_REG_C1_C0_2, + "c13_c0_3": UC_ARM_REG_C13_C0_3, + "fpexc": UC_ARM_REG_FPEXC } From 897f2a85e7d90e9c3e605ed44512279969f24e23 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 5 May 2022 18:29:23 +0300 Subject: [PATCH 11/37] Dummy commit to re-initiate tests --- qiling/debugger/gdb/gdb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 574341a4..67d7d7d2 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -29,8 +29,7 @@ from qiling import Qiling from qiling.const import QL_ARCH, QL_ENDIAN, QL_OS from qiling.debugger import QlDebugger from qiling.debugger.gdb import xmlregs - -from .utils import QlGdbUtils +from qiling.debugger.gdb.utils import QlGdbUtils # gdb logging prompt PROMPT = r'gdb>' From d49beed1afbc73114a903d639f3ee0c3b9dfbc6e Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 5 May 2022 21:08:41 +0300 Subject: [PATCH 12/37] Add missing regs for MIPS --- qiling/arch/mips.py | 3 +- qiling/arch/mips_const.py | 70 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/qiling/arch/mips.py b/qiling/arch/mips.py index 8391becc..9368634f 100644 --- a/qiling/arch/mips.py +++ b/qiling/arch/mips.py @@ -37,7 +37,8 @@ class QlArchMIPS(QlArch): def regs(self) -> QlRegisterManager: regs_map = dict( **mips_const.reg_map, - **mips_const.reg_map_afpr128 + **mips_const.reg_map_afpr128, + **mips_const.reg_map_fpu ) pc_reg = 'pc' diff --git a/qiling/arch/mips_const.py b/qiling/arch/mips_const.py index 8dd8d48a..8e3141f4 100644 --- a/qiling/arch/mips_const.py +++ b/qiling/arch/mips_const.py @@ -6,6 +6,39 @@ from unicorn.mips_const import * reg_map = { + "r0" : UC_MIPS_REG_0, + "r1" : UC_MIPS_REG_1, + "r2" : UC_MIPS_REG_2, + "r3" : UC_MIPS_REG_3, + "r4" : UC_MIPS_REG_4, + "r5" : UC_MIPS_REG_5, + "r6" : UC_MIPS_REG_6, + "r7" : UC_MIPS_REG_7, + "r8" : UC_MIPS_REG_8, + "r9" : UC_MIPS_REG_9, + "r10" : UC_MIPS_REG_10, + "r11" : UC_MIPS_REG_11, + "r12" : UC_MIPS_REG_12, + "r13" : UC_MIPS_REG_13, + "r14" : UC_MIPS_REG_14, + "r15" : UC_MIPS_REG_15, + "r16" : UC_MIPS_REG_16, + "r17" : UC_MIPS_REG_17, + "r18" : UC_MIPS_REG_18, + "r19" : UC_MIPS_REG_19, + "r20" : UC_MIPS_REG_20, + "r21" : UC_MIPS_REG_21, + "r22" : UC_MIPS_REG_22, + "r23" : UC_MIPS_REG_23, + "r24" : UC_MIPS_REG_24, + "r25" : UC_MIPS_REG_25, + "r26" : UC_MIPS_REG_26, + "r27" : UC_MIPS_REG_27, + "r28" : UC_MIPS_REG_28, + "r29" : UC_MIPS_REG_29, + "r30" : UC_MIPS_REG_30, + "r31" : UC_MIPS_REG_31, + "zero": UC_MIPS_REG_ZERO, "at": UC_MIPS_REG_AT, "v0": UC_MIPS_REG_V0, @@ -49,4 +82,39 @@ reg_map = { reg_map_afpr128 = { "cp0_config3" : UC_MIPS_REG_CP0_CONFIG3, "cp0_userlocal": UC_MIPS_REG_CP0_USERLOCAL, -} \ No newline at end of file +} + +reg_map_fpu = { + "f0" : UC_MIPS_REG_F0, + "f1" : UC_MIPS_REG_F1, + "f2" : UC_MIPS_REG_F2, + "f3" : UC_MIPS_REG_F3, + "f4" : UC_MIPS_REG_F4, + "f5" : UC_MIPS_REG_F5, + "f6" : UC_MIPS_REG_F6, + "f7" : UC_MIPS_REG_F7, + "f8" : UC_MIPS_REG_F8, + "f9" : UC_MIPS_REG_F9, + "f10" : UC_MIPS_REG_F10, + "f11" : UC_MIPS_REG_F11, + "f12" : UC_MIPS_REG_F12, + "f13" : UC_MIPS_REG_F13, + "f14" : UC_MIPS_REG_F14, + "f15" : UC_MIPS_REG_F15, + "f16" : UC_MIPS_REG_F16, + "f17" : UC_MIPS_REG_F17, + "f18" : UC_MIPS_REG_F18, + "f19" : UC_MIPS_REG_F19, + "f20" : UC_MIPS_REG_F20, + "f21" : UC_MIPS_REG_F21, + "f22" : UC_MIPS_REG_F22, + "f23" : UC_MIPS_REG_F23, + "f24" : UC_MIPS_REG_F24, + "f25" : UC_MIPS_REG_F25, + "f26" : UC_MIPS_REG_F26, + "f27" : UC_MIPS_REG_F27, + "f28" : UC_MIPS_REG_F28, + "f29" : UC_MIPS_REG_F29, + "f30" : UC_MIPS_REG_F30, + "f31" : UC_MIPS_REG_F31 +} From 03c47b4f1f7eb751c19fc4343e9ea46d832d178e Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 5 May 2022 21:09:35 +0300 Subject: [PATCH 13/37] Fix MIPS regs gdb definition --- qiling/debugger/gdb/xmlregs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index 53199eaf..63084128 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -8,7 +8,8 @@ from pathlib import PurePath from qiling.arch.arm_const import reg_map as arm_regs from qiling.arch.arm64_const import reg_map as arm64_regs -from qiling.arch.mips_const import reg_map as mips_regs +from qiling.arch.mips_const import reg_map as mips_regs_gpr +from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu from qiling.arch.x86_const import reg_map_16 as x86_regs_16 from qiling.arch.x86_const import reg_map_32 as x86_regs_32 from qiling.arch.x86_const import reg_map_64 as x86_regs_64 @@ -83,7 +84,7 @@ def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: QL_ARCH.ARM : arm_regs, QL_ARCH.CORTEX_M : arm_regs, QL_ARCH.ARM64 : arm64_regs, - QL_ARCH.MIPS : mips_regs + QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu) }[archtype] regmap = [] From 3b9059562957e1a79e06a90db033033c0eb04595 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 5 May 2022 21:10:44 +0300 Subject: [PATCH 14/37] Make qmark handler generic rather than hardcoded --- qiling/debugger/gdb/gdb.py | 53 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 67d7d7d2..b8c53ff4 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -146,28 +146,49 @@ class QlGdb(QlDebugger): def handle_qmark(subcmd: str) -> Reply: - # MIPS32_EL : $T051d:00e7ff7f;25:40ccfc77;#65 - # MIPS32_EB : $T051d:7fff6dc0;25:77fc4880;thread:28fa;core:0; - # ARM64 : $T051d:0*,;1f:80f6f*"ff0* ;20:c02cfdb7f* 0* ;thread:p1f9.1f9;core:0;#56 - # ARM : $T050b:0*"00;0d:e0f6ffbe;0f:8079fdb6;#ae + from unicorn.x86_const import UC_X86_REG_EIP, UC_X86_REG_ESP + from unicorn.x86_const import UC_X86_REG_RIP, UC_X86_REG_RSP + from unicorn.arm_const import UC_ARM_REG_PC, UC_ARM_REG_SP + from unicorn.arm64_const import UC_ARM64_REG_PC, UC_ARM64_REG_SP + from unicorn.mips_const import UC_MIPS_REG_PC, UC_MIPS_REG_SP + + # X86 : T0505:00000000;04:c0d3ffff;08:2021fdf7;thread:p15c6.15c6;core:6 + # X8664 : T0506:0000000000000000;07:b0e2ffffff7f0000;10:0001fdf7ff7f0000;thread:p15a2.15a2;core:6; + # MIPS32_EL : T051d:00e7ff7f;25:40ccfc77; + # MIPS32_EB : T051d:7fff6dc0;25:77fc4880;thread:28fa;core:0; + # ARM64 : T051d:0000000000000000;1f:80f6ffffffff0000;20:c02cfdb7ffff0000;thread:p1f9.1f9;core:0; + # ARM : T050b:00000000;0d:e0f6ffbe;0f:8079fdb6; response = { - QL_ARCH.X86 : ( 0x05, 0x04, 0x08 ), - QL_ARCH.X8664 : ( 0x06, 0x07, 0x10 ), - QL_ARCH.ARM : ( 0x0b, 0x0d, 0x0f ), - QL_ARCH.ARM64 : ( 0x1d, 0xf1, 0x20 ), - QL_ARCH.MIPS : ( 0x1d, 0x00, 0x25 ), - QL_ARCH.A8086 : ( 0x05, 0x04, 0x08 ), - QL_ARCH.CORTEX_M : ( 0x0b, 0x0d, 0x0f ) + QL_ARCH.X86 : ( 0x05, UC_X86_REG_ESP, UC_X86_REG_EIP ), + QL_ARCH.X8664 : ( 0x06, UC_X86_REG_RSP, UC_X86_REG_RIP ), + QL_ARCH.ARM : ( 0x0b, UC_ARM_REG_SP, UC_ARM_REG_PC ), + QL_ARCH.ARM64 : ( 0x1d, UC_ARM64_REG_SP, UC_ARM64_REG_PC ), + QL_ARCH.MIPS : ( 0x1d, UC_MIPS_REG_SP, UC_MIPS_REG_PC ), + QL_ARCH.A8086 : ( 0x05, UC_X86_REG_ESP, UC_X86_REG_EIP ), + QL_ARCH.CORTEX_M : ( 0x0b, UC_ARM_REG_SP, UC_ARM_REG_PC ) } - idhex, spid, pcid = response[self.ql.arch.type] - sp = __hexstr(self.ql.arch.regs.arch_sp) - pc = __hexstr(self.ql.arch.regs.arch_pc) + idhex, sp_reg, pc_reg = response[self.ql.arch.type] + + def __get_reg_idx(ucreg: int) -> int: + """Get the index of a uc reg whithin the regsmap array. + + Returns: array index where this reg's info is stored, or -1 if not found + """ + + return next((i for i, (regnum, _, _) in enumerate(self.regsmap) if regnum == ucreg), -1) + + sp_idx = __get_reg_idx(sp_reg) + pc_idx = __get_reg_idx(pc_reg) + + sp_val = __get_reg_value(*self.regsmap[sp_idx]) + pc_val = __get_reg_value(*self.regsmap[pc_idx]) + zfill = __hexstr(0) - info = '' if self.ql.arch.type == QL_ARCH.MIPS else f':{zfill};{spid:02x}' - return f'T{SIGTRAP:02x}{idhex:02x}{info}:{sp};{pcid:02x}:{pc};' + info = '' if self.ql.arch.type == QL_ARCH.MIPS else f':{zfill};{sp_idx:02x}' + return f'T{SIGTRAP:02x}{idhex:02x}{info}:{sp_val};{pc_idx:02x}:{pc_val};' def handle_c(subcmd: str) -> Reply: From da820236717e247dd67959a3b6f6199c4ff6b5de Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 5 May 2022 21:11:21 +0300 Subject: [PATCH 15/37] Add a few gdbserver debug messages --- qiling/debugger/gdb/gdb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index b8c53ff4..e2f00bd8 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -236,9 +236,7 @@ class QlGdb(QlDebugger): # see: ./xml/arm/arm-fpa.xml # see: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127 - data = ''.join(__get_reg_value(*entry) for entry in self.regsmap) - - return data + return ''.join(__get_reg_value(*entry) for entry in self.regsmap) def handle_G(subcmd: str) -> Reply: @@ -349,6 +347,7 @@ class QlGdb(QlDebugger): if feature == 'StartNoAckMode': server.ack_mode = False + server.log.debug('[noack mode enabled]') return REPLY_OK if feature in supported else REPLY_EMPTY @@ -735,6 +734,7 @@ class QlGdb(QlDebugger): for packet in server.readpackets(): if server.ack_mode: server.send(REPLY_ACK, raw=True) + server.log.debug('[sent ack]') cmd, subcmd = packet[0], packet[1:] handler = handlers.get(f'{cmd:c}') @@ -949,7 +949,7 @@ class GdbSerialConn: def __repl(m: 're.Match[bytes]') -> bytes: ch, _, times = m[0] - return bytes([ch] * times) + return bytes([ch] * (1 + times - 29)) return re.sub(br'.\*.', __repl, data, flags=re.DOTALL) From f72bfb87d14037d6c4697f0f064be240041ed876 Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:48:40 +0300 Subject: [PATCH 16/37] Support arbitrary regs definition order --- qiling/debugger/gdb/xmlregs.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index 63084128..56d08dd2 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -87,17 +87,16 @@ def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu) }[archtype] - regmap = [] + xmlpath = __get_xml_path(archtype) + regsinfo = sorted(__walk_xml_regs(*xmlpath)) + + # pre-allocate regmap and occupy it with null entries + last_regnum = regsinfo[-1][0] + regmap: Sequence[RegEntry] = [(None, 0, 0)] * (last_regnum + 1) + pos = 0 - xmlpath = __get_xml_path(archtype) - - for regnum, name, bitsize in __walk_xml_regs(*xmlpath): - # regs indices might not be consecutive. - # extend regmap with null entries if needed - if len(regmap) < regnum + 1: - regmap.extend([(None, 0, 0)] * (regnum + 1 - len(regmap))) - + for regnum, name, bitsize in sorted(regsinfo): # reg value size in nibbles nibbles = bitsize // 4 From 2ee4ecc6d3d49cdd6a3c34f80dd7c4d6d79ef9c1 Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:50:20 +0300 Subject: [PATCH 17/37] Add ARM D* registers --- qiling/arch/arm_const.py | 36 ++++++++++++++++++++++++++++++++++ qiling/debugger/gdb/xmlregs.py | 3 ++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index d8def122..e3dfaf1b 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -40,3 +40,39 @@ reg_map = { "c13_c0_3": UC_ARM_REG_C13_C0_3, "fpexc": UC_ARM_REG_FPEXC } + +reg_vfp = { + "d0" : UC_ARM_REG_D0, + "d1" : UC_ARM_REG_D1, + "d2" : UC_ARM_REG_D2, + "d3" : UC_ARM_REG_D3, + "d4" : UC_ARM_REG_D4, + "d5" : UC_ARM_REG_D5, + "d6" : UC_ARM_REG_D6, + "d7" : UC_ARM_REG_D7, + "d8" : UC_ARM_REG_D8, + "d9" : UC_ARM_REG_D9, + "d10" : UC_ARM_REG_D10, + "d11" : UC_ARM_REG_D11, + "d12" : UC_ARM_REG_D12, + "d13" : UC_ARM_REG_D13, + "d14" : UC_ARM_REG_D14, + "d15" : UC_ARM_REG_D15, + "d16" : UC_ARM_REG_D16, + "d17" : UC_ARM_REG_D17, + "d18" : UC_ARM_REG_D18, + "d19" : UC_ARM_REG_D19, + "d20" : UC_ARM_REG_D20, + "d21" : UC_ARM_REG_D21, + "d22" : UC_ARM_REG_D22, + "d23" : UC_ARM_REG_D23, + "d24" : UC_ARM_REG_D24, + "d25" : UC_ARM_REG_D25, + "d26" : UC_ARM_REG_D26, + "d27" : UC_ARM_REG_D27, + "d28" : UC_ARM_REG_D28, + "d29" : UC_ARM_REG_D29, + "d30" : UC_ARM_REG_D30, + "d31" : UC_ARM_REG_D31, + "fpscr" : UC_ARM_REG_FPSCR +} diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index 56d08dd2..bafe03d6 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -7,6 +7,7 @@ from typing import Iterator, Mapping, Optional, Sequence, Tuple from pathlib import PurePath from qiling.arch.arm_const import reg_map as arm_regs +from qiling.arch.arm_const import reg_vfp as arm_regs_vfp from qiling.arch.arm64_const import reg_map as arm64_regs from qiling.arch.mips_const import reg_map as mips_regs_gpr from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu @@ -81,7 +82,7 @@ def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: QL_ARCH.A8086 : dict(**x86_regs_16, **x86_regs_misc, **x86_regs_cr, **x86_regs_st), QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm), QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm), - QL_ARCH.ARM : arm_regs, + QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp), QL_ARCH.CORTEX_M : arm_regs, QL_ARCH.ARM64 : arm64_regs, QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu) From e3f7db21479079edabbcb7f6099fd9b7bcaa8181 Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:53:25 +0300 Subject: [PATCH 18/37] Better handling of EB reg values --- qiling/debugger/gdb/gdb.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index e2f00bd8..da1c3421 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -110,6 +110,12 @@ class QlGdb(QlDebugger): return value.to_bytes(length, byteorder).hex() + def __unkown_reg_value(nibbles: int) -> str: + """Encode the hex string for unknown regsiter value. + """ + + return 'x' * nibbles + def __get_reg_value(reg: Optional[int], pos: int, nibbles: int) -> str: # reg is either None or uc reg invalid if reg: @@ -118,13 +124,13 @@ class QlGdb(QlDebugger): hexstr = __hexstr(value, nibbles) else: - hexstr = 'x' * nibbles + hexstr = __unkown_reg_value(nibbles) return hexstr def __set_reg_value(reg: Optional[int], pos: int, nibbles: int, hexval: str) -> None: # reg is neither None nor uc reg invalid - if reg: + if reg and hexval != __unkown_reg_value(nibbles): assert len(hexval) == nibbles val = int(hexval, 16) @@ -243,14 +249,9 @@ class QlGdb(QlDebugger): data = subcmd for reg, pos, nibbles in self.regsmap: - if reg: - hexval = data[pos : pos + nibbles] + hexval = data[pos : pos + nibbles] - if hexval != 'x' * nibbles: - val = int(hexval, 16) - - # TODO: should we swap val's endianess for big-endian targets? - self.ql.arch.regs.write(reg, val) + __set_reg_value(reg, pos, nibbles, hexval) return REPLY_OK From bf083906b40d54a9c3feec73982474660572be48 Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:53:58 +0300 Subject: [PATCH 19/37] Update explanatory comment --- qiling/debugger/gdb/gdb.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index da1c3421..5592b157 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -105,6 +105,9 @@ class QlGdb(QlDebugger): killed = False def __hexstr(value: int, nibbles: int = 0) -> str: + """Encode a value into a hex string. + """ + length = (nibbles or self.ql.arch.bits // 4) // 2 byteorder = 'little' if self.ql.arch.endian == QL_ENDIAN.EL else 'big' @@ -233,14 +236,16 @@ class QlGdb(QlDebugger): def handle_g(subcmd: str) -> Reply: - # TODO: several obsolete regs cause arm to have a gap just before cpsr. the nonexistant regs - # are represented as None entries just to make sure cpsr stays at index 25. however, it is - # not clear whether its value should follow a series of 'x' (for the non-existant regs) or - # not. the original code suggests not (perhaps because fpa is not specified as an xml feature..?) + # NOTE: in the past the 'g' reply packet for arm included the f0-f7 and fps registers between pc + # and cpsr, which placed cpsr at index (regnum) 25. as the f-registers became obsolete the cpsr + # index decreased. in order to maintain backward compatibility with older gdb versions, the gap + # between pc and cpsr that used to represent the f-registers (96 bits each + 32 bits for fps) is + # filled with unknown reg values. + # + # gdb clients that follow the xml definitions no longer need these placeholders, as registers + # indices are flexible and may be defined arbitrarily though xml. # - # non-existant regs are f0-f7 (96 bits each) and fps (32 bits). # see: ./xml/arm/arm-fpa.xml - # see: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127 return ''.join(__get_reg_value(*entry) for entry in self.regsmap) From c0a657fcc96369c2f49dd9f6f7429faa2b6b23c3 Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:54:33 +0300 Subject: [PATCH 20/37] Remove unnecessary ARM F* regs --- qiling/arch/arm_const.py | 12 ------ .../gdb/xml/arm/arm-m-profile-with-fpa.xml | 39 ------------------- 2 files changed, 51 deletions(-) delete mode 100644 qiling/debugger/gdb/xml/arm/arm-m-profile-with-fpa.xml diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index e3dfaf1b..2a268cdf 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -23,18 +23,6 @@ reg_map = { "lr": UC_ARM_REG_LR, "pc": UC_ARM_REG_PC, - # CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34 - # The fp registers inbetween have become obsolete - "f0": UC_ARM_REG_INVALID, - "f1": UC_ARM_REG_INVALID, - "f2": UC_ARM_REG_INVALID, - "f3": UC_ARM_REG_INVALID, - "f4": UC_ARM_REG_INVALID, - "f5": UC_ARM_REG_INVALID, - "f6": UC_ARM_REG_INVALID, - "f7": UC_ARM_REG_INVALID, - "fps": UC_ARM_REG_INVALID, - "cpsr": UC_ARM_REG_CPSR, "c1_c0_2": UC_ARM_REG_C1_C0_2, "c13_c0_3": UC_ARM_REG_C13_C0_3, diff --git a/qiling/debugger/gdb/xml/arm/arm-m-profile-with-fpa.xml b/qiling/debugger/gdb/xml/arm/arm-m-profile-with-fpa.xml deleted file mode 100644 index dd30abe8..00000000 --- a/qiling/debugger/gdb/xml/arm/arm-m-profile-with-fpa.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From de3afe94f58b6ed694a6a24d62288af36d42219a Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 17:55:00 +0300 Subject: [PATCH 21/37] Reformat ARM xml files --- qiling/debugger/gdb/xml/arm/arm-core.xml | 48 +++++++++---------- qiling/debugger/gdb/xml/arm/arm-m-profile.xml | 3 +- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/qiling/debugger/gdb/xml/arm/arm-core.xml b/qiling/debugger/gdb/xml/arm/arm-core.xml index a2c0a65b..5032c503 100644 --- a/qiling/debugger/gdb/xml/arm/arm-core.xml +++ b/qiling/debugger/gdb/xml/arm/arm-core.xml @@ -1,31 +1,31 @@ - + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/arm/arm-m-profile.xml b/qiling/debugger/gdb/xml/arm/arm-m-profile.xml index 5319d764..f0584a20 100644 --- a/qiling/debugger/gdb/xml/arm/arm-m-profile.xml +++ b/qiling/debugger/gdb/xml/arm/arm-m-profile.xml @@ -7,7 +7,7 @@ - + @@ -23,5 +23,6 @@ + From 8549aa7d6d67b9950d819143de15acdbcb544afe Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 18:36:07 +0300 Subject: [PATCH 22/37] Add aarch64 B*, D*, H*, Q*, S* and V* regs --- qiling/arch/arm64.py | 8 +- qiling/arch/arm64_const.py | 348 ++++++++++++++++++++++++++------- qiling/debugger/gdb/xmlregs.py | 3 +- 3 files changed, 288 insertions(+), 71 deletions(-) diff --git a/qiling/arch/arm64.py b/qiling/arch/arm64.py index 0367a818..65949669 100644 --- a/qiling/arch/arm64.py +++ b/qiling/arch/arm64.py @@ -26,7 +26,13 @@ class QlArchARM64(QlArch): def regs(self) -> QlRegisterManager: regs_map = dict( **arm64_const.reg_map, - **arm64_const.reg_map_w + **arm64_const.reg_map_b, + **arm64_const.reg_map_d, + **arm64_const.reg_map_h, + **arm64_const.reg_map_q, + **arm64_const.reg_map_s, + **arm64_const.reg_map_w, + **arm64_const.reg_map_v ) pc_reg = 'pc' diff --git a/qiling/arch/arm64_const.py b/qiling/arch/arm64_const.py index c706d10c..ee2b5516 100644 --- a/qiling/arch/arm64_const.py +++ b/qiling/arch/arm64_const.py @@ -1,79 +1,289 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # from unicorn.arm64_const import * reg_map = { - "x0": UC_ARM64_REG_X0, - "x1": UC_ARM64_REG_X1, - "x2": UC_ARM64_REG_X2, - "x3": UC_ARM64_REG_X3, - "x4": UC_ARM64_REG_X4, - "x5": UC_ARM64_REG_X5, - "x6": UC_ARM64_REG_X6, - "x7": UC_ARM64_REG_X7, - "x8": UC_ARM64_REG_X8, - "x9": UC_ARM64_REG_X9, - "x10": UC_ARM64_REG_X10, - "x11": UC_ARM64_REG_X11, - "x12": UC_ARM64_REG_X12, - "x13": UC_ARM64_REG_X13, - "x14": UC_ARM64_REG_X14, - "x15": UC_ARM64_REG_X15, - "x16": UC_ARM64_REG_X16, - "x17": UC_ARM64_REG_X17, - "x18": UC_ARM64_REG_X18, - "x19": UC_ARM64_REG_X19, - "x20": UC_ARM64_REG_X20, - "x21": UC_ARM64_REG_X21, - "x22": UC_ARM64_REG_X22, - "x23": UC_ARM64_REG_X23, - "x24": UC_ARM64_REG_X24, - "x25": UC_ARM64_REG_X25, - "x26": UC_ARM64_REG_X26, - "x27": UC_ARM64_REG_X27, - "x28": UC_ARM64_REG_X28, - "x29": UC_ARM64_REG_X29, - "x30": UC_ARM64_REG_X30, - "sp": UC_ARM64_REG_SP, - "pc": UC_ARM64_REG_PC, - "lr": UC_ARM64_REG_LR, - "cpacr_el1": UC_ARM64_REG_CPACR_EL1, - "tpidr_el0": UC_ARM64_REG_TPIDR_EL0, + "x0": UC_ARM64_REG_X0, + "x1": UC_ARM64_REG_X1, + "x2": UC_ARM64_REG_X2, + "x3": UC_ARM64_REG_X3, + "x4": UC_ARM64_REG_X4, + "x5": UC_ARM64_REG_X5, + "x6": UC_ARM64_REG_X6, + "x7": UC_ARM64_REG_X7, + "x8": UC_ARM64_REG_X8, + "x9": UC_ARM64_REG_X9, + "x10": UC_ARM64_REG_X10, + "x11": UC_ARM64_REG_X11, + "x12": UC_ARM64_REG_X12, + "x13": UC_ARM64_REG_X13, + "x14": UC_ARM64_REG_X14, + "x15": UC_ARM64_REG_X15, + "x16": UC_ARM64_REG_X16, + "x17": UC_ARM64_REG_X17, + "x18": UC_ARM64_REG_X18, + "x19": UC_ARM64_REG_X19, + "x20": UC_ARM64_REG_X20, + "x21": UC_ARM64_REG_X21, + "x22": UC_ARM64_REG_X22, + "x23": UC_ARM64_REG_X23, + "x24": UC_ARM64_REG_X24, + "x25": UC_ARM64_REG_X25, + "x26": UC_ARM64_REG_X26, + "x27": UC_ARM64_REG_X27, + "x28": UC_ARM64_REG_X28, + "x29": UC_ARM64_REG_X29, + "x30": UC_ARM64_REG_X30, + "sp": UC_ARM64_REG_SP, + "pc": UC_ARM64_REG_PC, + "lr": UC_ARM64_REG_LR, + "cpacr_el1": UC_ARM64_REG_CPACR_EL1, + "tpidr_el0": UC_ARM64_REG_TPIDR_EL0 +} + +reg_map_b = { + "b0" : UC_ARM64_REG_B0, + "b1" : UC_ARM64_REG_B1, + "b2" : UC_ARM64_REG_B2, + "b3" : UC_ARM64_REG_B3, + "b4" : UC_ARM64_REG_B4, + "b5" : UC_ARM64_REG_B5, + "b6" : UC_ARM64_REG_B6, + "b7" : UC_ARM64_REG_B7, + "b8" : UC_ARM64_REG_B8, + "b9" : UC_ARM64_REG_B9, + "b10" : UC_ARM64_REG_B10, + "b11" : UC_ARM64_REG_B11, + "b12" : UC_ARM64_REG_B12, + "b13" : UC_ARM64_REG_B13, + "b14" : UC_ARM64_REG_B14, + "b15" : UC_ARM64_REG_B15, + "b16" : UC_ARM64_REG_B16, + "b17" : UC_ARM64_REG_B17, + "b18" : UC_ARM64_REG_B18, + "b19" : UC_ARM64_REG_B19, + "b20" : UC_ARM64_REG_B20, + "b21" : UC_ARM64_REG_B21, + "b22" : UC_ARM64_REG_B22, + "b23" : UC_ARM64_REG_B23, + "b24" : UC_ARM64_REG_B24, + "b25" : UC_ARM64_REG_B25, + "b26" : UC_ARM64_REG_B26, + "b27" : UC_ARM64_REG_B27, + "b28" : UC_ARM64_REG_B28, + "b29" : UC_ARM64_REG_B29, + "b30" : UC_ARM64_REG_B30, + "b31" : UC_ARM64_REG_B31 +} + +reg_map_d = { + "d0" : UC_ARM64_REG_D0, + "d1" : UC_ARM64_REG_D1, + "d2" : UC_ARM64_REG_D2, + "d3" : UC_ARM64_REG_D3, + "d4" : UC_ARM64_REG_D4, + "d5" : UC_ARM64_REG_D5, + "d6" : UC_ARM64_REG_D6, + "d7" : UC_ARM64_REG_D7, + "d8" : UC_ARM64_REG_D8, + "d9" : UC_ARM64_REG_D9, + "d10" : UC_ARM64_REG_D10, + "d11" : UC_ARM64_REG_D11, + "d12" : UC_ARM64_REG_D12, + "d13" : UC_ARM64_REG_D13, + "d14" : UC_ARM64_REG_D14, + "d15" : UC_ARM64_REG_D15, + "d16" : UC_ARM64_REG_D16, + "d17" : UC_ARM64_REG_D17, + "d18" : UC_ARM64_REG_D18, + "d19" : UC_ARM64_REG_D19, + "d20" : UC_ARM64_REG_D20, + "d21" : UC_ARM64_REG_D21, + "d22" : UC_ARM64_REG_D22, + "d23" : UC_ARM64_REG_D23, + "d24" : UC_ARM64_REG_D24, + "d25" : UC_ARM64_REG_D25, + "d26" : UC_ARM64_REG_D26, + "d27" : UC_ARM64_REG_D27, + "d28" : UC_ARM64_REG_D28, + "d29" : UC_ARM64_REG_D29, + "d30" : UC_ARM64_REG_D30, + "d31" : UC_ARM64_REG_D31 +} + +reg_map_h = { + "h0" : UC_ARM64_REG_H0, + "h1" : UC_ARM64_REG_H1, + "h2" : UC_ARM64_REG_H2, + "h3" : UC_ARM64_REG_H3, + "h4" : UC_ARM64_REG_H4, + "h5" : UC_ARM64_REG_H5, + "h6" : UC_ARM64_REG_H6, + "h7" : UC_ARM64_REG_H7, + "h8" : UC_ARM64_REG_H8, + "h9" : UC_ARM64_REG_H9, + "h10" : UC_ARM64_REG_H10, + "h11" : UC_ARM64_REG_H11, + "h12" : UC_ARM64_REG_H12, + "h13" : UC_ARM64_REG_H13, + "h14" : UC_ARM64_REG_H14, + "h15" : UC_ARM64_REG_H15, + "h16" : UC_ARM64_REG_H16, + "h17" : UC_ARM64_REG_H17, + "h18" : UC_ARM64_REG_H18, + "h19" : UC_ARM64_REG_H19, + "h20" : UC_ARM64_REG_H20, + "h21" : UC_ARM64_REG_H21, + "h22" : UC_ARM64_REG_H22, + "h23" : UC_ARM64_REG_H23, + "h24" : UC_ARM64_REG_H24, + "h25" : UC_ARM64_REG_H25, + "h26" : UC_ARM64_REG_H26, + "h27" : UC_ARM64_REG_H27, + "h28" : UC_ARM64_REG_H28, + "h29" : UC_ARM64_REG_H29, + "h30" : UC_ARM64_REG_H30, + "h31" : UC_ARM64_REG_H31 +} + +reg_map_q = { + "q0" : UC_ARM64_REG_Q0, + "q1" : UC_ARM64_REG_Q1, + "q2" : UC_ARM64_REG_Q2, + "q3" : UC_ARM64_REG_Q3, + "q4" : UC_ARM64_REG_Q4, + "q5" : UC_ARM64_REG_Q5, + "q6" : UC_ARM64_REG_Q6, + "q7" : UC_ARM64_REG_Q7, + "q8" : UC_ARM64_REG_Q8, + "q9" : UC_ARM64_REG_Q9, + "q10" : UC_ARM64_REG_Q10, + "q11" : UC_ARM64_REG_Q11, + "q12" : UC_ARM64_REG_Q12, + "q13" : UC_ARM64_REG_Q13, + "q14" : UC_ARM64_REG_Q14, + "q15" : UC_ARM64_REG_Q15, + "q16" : UC_ARM64_REG_Q16, + "q17" : UC_ARM64_REG_Q17, + "q18" : UC_ARM64_REG_Q18, + "q19" : UC_ARM64_REG_Q19, + "q20" : UC_ARM64_REG_Q20, + "q21" : UC_ARM64_REG_Q21, + "q22" : UC_ARM64_REG_Q22, + "q23" : UC_ARM64_REG_Q23, + "q24" : UC_ARM64_REG_Q24, + "q25" : UC_ARM64_REG_Q25, + "q26" : UC_ARM64_REG_Q26, + "q27" : UC_ARM64_REG_Q27, + "q28" : UC_ARM64_REG_Q28, + "q29" : UC_ARM64_REG_Q29, + "q30" : UC_ARM64_REG_Q30, + "q31" : UC_ARM64_REG_Q31 +} + +reg_map_s = { + "s0" : UC_ARM64_REG_S0, + "s1" : UC_ARM64_REG_S1, + "s2" : UC_ARM64_REG_S2, + "s3" : UC_ARM64_REG_S3, + "s4" : UC_ARM64_REG_S4, + "s5" : UC_ARM64_REG_S5, + "s6" : UC_ARM64_REG_S6, + "s7" : UC_ARM64_REG_S7, + "s8" : UC_ARM64_REG_S8, + "s9" : UC_ARM64_REG_S9, + "s10" : UC_ARM64_REG_S10, + "s11" : UC_ARM64_REG_S11, + "s12" : UC_ARM64_REG_S12, + "s13" : UC_ARM64_REG_S13, + "s14" : UC_ARM64_REG_S14, + "s15" : UC_ARM64_REG_S15, + "s16" : UC_ARM64_REG_S16, + "s17" : UC_ARM64_REG_S17, + "s18" : UC_ARM64_REG_S18, + "s19" : UC_ARM64_REG_S19, + "s20" : UC_ARM64_REG_S20, + "s21" : UC_ARM64_REG_S21, + "s22" : UC_ARM64_REG_S22, + "s23" : UC_ARM64_REG_S23, + "s24" : UC_ARM64_REG_S24, + "s25" : UC_ARM64_REG_S25, + "s26" : UC_ARM64_REG_S26, + "s27" : UC_ARM64_REG_S27, + "s28" : UC_ARM64_REG_S28, + "s29" : UC_ARM64_REG_S29, + "s30" : UC_ARM64_REG_S30, + "s31" : UC_ARM64_REG_S31 } reg_map_w = { - "w0" : UC_ARM64_REG_W0, - "w1" : UC_ARM64_REG_W1, - "w2" : UC_ARM64_REG_W2, - "w3" : UC_ARM64_REG_W3, - "w4" : UC_ARM64_REG_W4, - "w5" : UC_ARM64_REG_W5, - "w6" : UC_ARM64_REG_W6, - "w7" : UC_ARM64_REG_W7, - "w8" : UC_ARM64_REG_W8, - "w9" : UC_ARM64_REG_W9, - "w10" : UC_ARM64_REG_W10, - "w11" : UC_ARM64_REG_W11, - "w12" : UC_ARM64_REG_W12, - "w13" : UC_ARM64_REG_W13, - "w14" : UC_ARM64_REG_W14, - "w15" : UC_ARM64_REG_W15, - "w16" : UC_ARM64_REG_W16, - "w17" : UC_ARM64_REG_W17, - "w18" : UC_ARM64_REG_W18, - "w19" : UC_ARM64_REG_W19, - "w20" : UC_ARM64_REG_W20, - "w21" : UC_ARM64_REG_W21, - "w22" : UC_ARM64_REG_W22, - "w23" : UC_ARM64_REG_W23, - "w24" : UC_ARM64_REG_W24, - "w25" : UC_ARM64_REG_W25, - "w26" : UC_ARM64_REG_W26, - "w27" : UC_ARM64_REG_W27, - "w28" : UC_ARM64_REG_W28, - "w29" : UC_ARM64_REG_W29, - "w30" : UC_ARM64_REG_W30, -} \ No newline at end of file + "w0" : UC_ARM64_REG_W0, + "w1" : UC_ARM64_REG_W1, + "w2" : UC_ARM64_REG_W2, + "w3" : UC_ARM64_REG_W3, + "w4" : UC_ARM64_REG_W4, + "w5" : UC_ARM64_REG_W5, + "w6" : UC_ARM64_REG_W6, + "w7" : UC_ARM64_REG_W7, + "w8" : UC_ARM64_REG_W8, + "w9" : UC_ARM64_REG_W9, + "w10" : UC_ARM64_REG_W10, + "w11" : UC_ARM64_REG_W11, + "w12" : UC_ARM64_REG_W12, + "w13" : UC_ARM64_REG_W13, + "w14" : UC_ARM64_REG_W14, + "w15" : UC_ARM64_REG_W15, + "w16" : UC_ARM64_REG_W16, + "w17" : UC_ARM64_REG_W17, + "w18" : UC_ARM64_REG_W18, + "w19" : UC_ARM64_REG_W19, + "w20" : UC_ARM64_REG_W20, + "w21" : UC_ARM64_REG_W21, + "w22" : UC_ARM64_REG_W22, + "w23" : UC_ARM64_REG_W23, + "w24" : UC_ARM64_REG_W24, + "w25" : UC_ARM64_REG_W25, + "w26" : UC_ARM64_REG_W26, + "w27" : UC_ARM64_REG_W27, + "w28" : UC_ARM64_REG_W28, + "w29" : UC_ARM64_REG_W29, + "w30" : UC_ARM64_REG_W30 +} + +reg_map_v = { + "v0" : UC_ARM64_REG_V0, + "v1" : UC_ARM64_REG_V1, + "v2" : UC_ARM64_REG_V2, + "v3" : UC_ARM64_REG_V3, + "v4" : UC_ARM64_REG_V4, + "v5" : UC_ARM64_REG_V5, + "v6" : UC_ARM64_REG_V6, + "v7" : UC_ARM64_REG_V7, + "v8" : UC_ARM64_REG_V8, + "v9" : UC_ARM64_REG_V9, + "v10" : UC_ARM64_REG_V10, + "v11" : UC_ARM64_REG_V11, + "v12" : UC_ARM64_REG_V12, + "v13" : UC_ARM64_REG_V13, + "v14" : UC_ARM64_REG_V14, + "v15" : UC_ARM64_REG_V15, + "v16" : UC_ARM64_REG_V16, + "v17" : UC_ARM64_REG_V17, + "v18" : UC_ARM64_REG_V18, + "v19" : UC_ARM64_REG_V19, + "v20" : UC_ARM64_REG_V20, + "v21" : UC_ARM64_REG_V21, + "v22" : UC_ARM64_REG_V22, + "v23" : UC_ARM64_REG_V23, + "v24" : UC_ARM64_REG_V24, + "v25" : UC_ARM64_REG_V25, + "v26" : UC_ARM64_REG_V26, + "v27" : UC_ARM64_REG_V27, + "v28" : UC_ARM64_REG_V28, + "v29" : UC_ARM64_REG_V29, + "v30" : UC_ARM64_REG_V30, + "v31" : UC_ARM64_REG_V31 +} diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index bafe03d6..bf2b77cf 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -9,6 +9,7 @@ from pathlib import PurePath from qiling.arch.arm_const import reg_map as arm_regs from qiling.arch.arm_const import reg_vfp as arm_regs_vfp from qiling.arch.arm64_const import reg_map as arm64_regs +from qiling.arch.arm64_const import reg_map_v as arm64_regs_v from qiling.arch.mips_const import reg_map as mips_regs_gpr from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu from qiling.arch.x86_const import reg_map_16 as x86_regs_16 @@ -84,7 +85,7 @@ def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm), QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp), QL_ARCH.CORTEX_M : arm_regs, - QL_ARCH.ARM64 : arm64_regs, + QL_ARCH.ARM64 : dict(**arm64_regs, **arm64_regs_v), QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu) }[archtype] From 0ac80ef1d4c93b897edb75d13ba44668434508ac Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 6 May 2022 18:36:21 +0300 Subject: [PATCH 23/37] Reformat ARM64 xml files --- .../debugger/gdb/xml/arm64/aarch64-core.xml | 110 ++++++------ qiling/debugger/gdb/xml/arm64/aarch64-fpu.xml | 165 +++++++++--------- qiling/debugger/gdb/xml/arm64/target.xml | 9 +- 3 files changed, 146 insertions(+), 138 deletions(-) diff --git a/qiling/debugger/gdb/xml/arm64/aarch64-core.xml b/qiling/debugger/gdb/xml/arm64/aarch64-core.xml index 5b2706e0..599d92b3 100644 --- a/qiling/debugger/gdb/xml/arm64/aarch64-core.xml +++ b/qiling/debugger/gdb/xml/arm64/aarch64-core.xml @@ -1,67 +1,67 @@ + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - * - * - * - * - * - * - * - * - * + + + + + + + + + + - * - * + + - * - * - * - * - - + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/arm64/aarch64-fpu.xml b/qiling/debugger/gdb/xml/arm64/aarch64-fpu.xml index 0b37e15f..0cdf73e5 100644 --- a/qiling/debugger/gdb/xml/arm64/aarch64-fpu.xml +++ b/qiling/debugger/gdb/xml/arm64/aarch64-fpu.xml @@ -1,86 +1,93 @@ + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> - - - - - - - - - - - - - - * - * - * - - - * - * - * - - - * - * - - - * - * - - - * - * - - - * - * - * - * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/qiling/debugger/gdb/xml/arm64/target.xml b/qiling/debugger/gdb/xml/arm64/target.xml index b91fd952..c741efa6 100644 --- a/qiling/debugger/gdb/xml/arm64/target.xml +++ b/qiling/debugger/gdb/xml/arm64/target.xml @@ -1,14 +1,15 @@ + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> aarch64 + \ No newline at end of file From a03f6ce28d0ac5dc56a475bf99bbd7529c2543b4 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 17:46:42 +0800 Subject: [PATCH 24/37] Remove nonexistent syspage.bin from MANIFEST --- MANIFEST.in | 1 - 1 file changed, 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index ead0863c..3a680e28 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,3 @@ recursive-include qiling/debugger/gdb/xml * recursive-include qiling/extensions/windows_sdk/defs * recursive-include qiling/profiles * include qiling/os/uefi/guids.csv -include qiling/os/qnx/syspage.bin From d3336291fe8df40be468b25bec46d1eb80a4b9e0 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 19:17:30 +0800 Subject: [PATCH 25/37] Use pkgutil instead of absolute paths --- MANIFEST.in | 1 + qiling/arch/evm/analysis/signatures.py | 7 +++---- qiling/debugger/gdb/gdb.py | 17 +++++++++-------- qiling/os/uefi/__init__.py | 11 ++++------- qiling/utils.py | 17 +++++++---------- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3a680e28..295f846d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ recursive-include qiling/debugger/gdb/xml * recursive-include qiling/extensions/windows_sdk/defs * recursive-include qiling/profiles * include qiling/os/uefi/guids.csv +include qiling/arch/evm/analysis/signatures.json diff --git a/qiling/arch/evm/analysis/signatures.py b/qiling/arch/evm/analysis/signatures.py index aebe55f0..321260fe 100644 --- a/qiling/arch/evm/analysis/signatures.py +++ b/qiling/arch/evm/analysis/signatures.py @@ -1,4 +1,4 @@ -import os +import pkgutil import re import logging import json @@ -92,9 +92,8 @@ def analysis_func_sign(insns:list, engine_num=1): class signatures_engine_1: @staticmethod def find_signature(sign): - path = os.path.split(os.path.realpath(__file__))[0] + '/signatures.json' - with open(path) as data_file: - data = json.load(data_file) + data = pkgutil.get_data(__package__, 'signatures.json').decode() + data = json.load(data) list_name = [name for name, hexa in data.items() if hexa == sign] diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 8b341666..6bccf5b0 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -7,6 +7,7 @@ # documentation: according to https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol import struct, os, socket +import pkgutil from binascii import unhexlify from typing import Iterator, Literal @@ -481,15 +482,15 @@ class QlGdb(QlDebugger, object): else: self.send("PacketSize=47ff;QPassSignals+;QProgramSignals+;QStartupWithShell+;QEnvironmentHexEncoded+;QEnvironmentReset+;QEnvironmentUnset+;QSetWorkingDir+;QCatchSyscalls+;qXfer:libraries-svr4:read+;augmented-libraries-svr4-read+;qXfer:auxv:read+;qXfer:siginfo:read+;qXfer:siginfo:write+;qXfer:features:read+;QStartNoAckMode+;qXfer:osdata:read+;multiprocess+;fork-events+;vfork-events+;exec-events+;QNonStop+;QDisableRandomization+;qXfer:threads:read+;ConditionalTracepoints+;TraceStateVariables+;TracepointSource+;DisconnectedTracing+;FastTracepoints+;StaticTracepoints+;InstallInTrace+;qXfer:statictrace:read+;qXfer:traceframe-info:read+;EnableDisableTracepoints+;QTBuffer:size+;tracenz+;ConditionalBreakpoints+;BreakpointCommands+;QAgent+;Qbtrace:bts+;Qbtrace-conf:bts:size+;Qbtrace:pt+;Qbtrace-conf:pt:size+;Qbtrace:off+;qXfer:btrace:read+;qXfer:btrace-conf:read+;swbreak+;hwbreak+;qXfer:exec-file:read+;vContSupported+;QThreadEvents+;no-resumed+") elif subcmd.startswith('Xfer:features:read'): - xfercmd_file = subcmd.split(':')[3] - xfercmd_abspath = os.path.dirname(os.path.abspath(__file__)) - xml_folder = self.ql.arch.type.name.lower() - xfercmd_file = os.path.join(xfercmd_abspath,"xml",xml_folder, xfercmd_file) - - if os.path.exists(xfercmd_file) and self.ql.os.type is not QL_OS.WINDOWS: - with open(xfercmd_file, 'r') as f: - file_contents = f.read() + if self.ql.os.type is not QL_OS.WINDOWS: + try: + xfercmd_file = subcmd.split(':')[3] + xml_folder = self.ql.arch.type.name.lower() + file_contents = pkgutil.get_data(__package__, f"xml/{xml_folder}/{xfercmd_file}").decode() self.send("l%s" % file_contents) + except: + self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) + self.send("l") else: self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) self.send("l") diff --git a/qiling/os/uefi/__init__.py b/qiling/os/uefi/__init__.py index 5b8faeab..aa390254 100644 --- a/qiling/os/uefi/__init__.py +++ b/qiling/os/uefi/__init__.py @@ -1,17 +1,14 @@ import csv from typing import Mapping -from os import path +import pkgutil def __init_guids_db() -> Mapping[str, str]: """Initialize GUIDs dictionary from a local database. """ - csv_path = path.dirname(path.abspath(__file__)) - csv_path = path.join(csv_path, 'guids.csv') + guids_file = pkgutil.get_data(__package__, 'guids.csv').decode() + guids_reader = csv.reader(guids_file.splitlines()) - with open(csv_path) as guids_file: - guids_reader = csv.reader(guids_file) - - return dict(tuple(entry) for entry in guids_reader) + return dict(tuple(entry) for entry in guids_reader) guids_db = __init_guids_db() diff --git a/qiling/utils.py b/qiling/utils.py index 68607fe6..b3180ed1 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -9,7 +9,7 @@ thoughout the qiling framework """ from functools import partial -import importlib, os +import importlib, pkgutil, os from configparser import ConfigParser from types import ModuleType @@ -415,19 +415,16 @@ def profile_setup(ostype: QL_OS, filename: Optional[str]): config = {} else: - qiling_home = os.path.dirname(os.path.abspath(__file__)) - os_profile = os.path.join(qiling_home, 'profiles', f'{ostype.name.lower()}.ql') - - profiles = [os_profile] - - if filename: - profiles.append(filename) - # patch 'getint' to convert integers of all bases int_converter = partial(int, base=0) config = ConfigParser(converters={'int': int_converter}) - config.read(profiles) + + os_profile = pkgutil.get_data(__package__, f'profiles/{ostype.name.lower()}.ql').decode() + config.read_string(os_profile) + + if filename: + config.read(filename) return config From 84798b19455f24c3cebce114d877e5b748ccecd9 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 19:45:07 +0800 Subject: [PATCH 26/37] Fix for nonexistent profiles --- qiling/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qiling/utils.py b/qiling/utils.py index b3180ed1..09bb68a3 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -420,8 +420,11 @@ def profile_setup(ostype: QL_OS, filename: Optional[str]): config = ConfigParser(converters={'int': int_converter}) - os_profile = pkgutil.get_data(__package__, f'profiles/{ostype.name.lower()}.ql').decode() - config.read_string(os_profile) + try: + os_profile = pkgutil.get_data(__package__, f'profiles/{ostype.name.lower()}.ql').decode() + config.read_string(os_profile) + except FileNotFoundError as e: + pass if filename: config.read(filename) From d166a984cdef163c3809847afb48f472a1ddcf42 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 19:52:48 +0800 Subject: [PATCH 27/37] Run giteesync only on main repository --- .github/workflows/giteesync.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/giteesync.yml b/.github/workflows/giteesync.yml index 4e7104ba..dbf79749 100644 --- a/.github/workflows/giteesync.yml +++ b/.github/workflows/giteesync.yml @@ -5,6 +5,7 @@ jobs: deploy: runs-on: ubuntu-latest + if: github.repository_owner == 'qilingframework' steps: - uses: actions/checkout@v2 with: From d051174810e716936f27831144c2a43ceaf9844b Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 20:14:00 +0800 Subject: [PATCH 28/37] Replace MANIFEST.in with package_data --- MANIFEST.in | 5 ----- setup.py | 7 ++++++- 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 295f846d..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -recursive-include qiling/debugger/gdb/xml * -recursive-include qiling/extensions/windows_sdk/defs * -recursive-include qiling/profiles * -include qiling/os/uefi/guids.csv -include qiling/arch/evm/analysis/signatures.json diff --git a/setup.py b/setup.py index 5496da6c..3987cc25 100644 --- a/setup.py +++ b/setup.py @@ -84,7 +84,12 @@ setup( packages=find_packages(), scripts=['qltool'], - include_package_data=True, + package_data={ + 'qiling': ['profiles/*.ql'], + 'qiling.debugger.gdb': ['xml/*/*'], + 'qiling.os.uefi': ['guids.csv'], + 'qiling.arch.evm.analysis': ['signatures.json'] + }, install_requires=requirements, extras_require=extras, ) From 719b98a61c6a996916f7982b09aac8514944616a Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 7 May 2022 20:22:39 +0800 Subject: [PATCH 29/37] Add to credits --- CREDITS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CREDITS.md b/CREDITS.md index b4e32331..9ea9f0ed 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -54,6 +54,7 @@ - danielmoos - sigeryang - bet4it +- nullableVoidPtr #### Legacy Core Developers From d05446539c0c0a89683f11ac9b38cef4975f4042 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 7 May 2022 23:31:57 +0300 Subject: [PATCH 30/37] More generic less hardcoded values --- qiling/debugger/gdb/gdb.py | 51 +++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 5592b157..6f5cc7a6 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -155,30 +155,21 @@ class QlGdb(QlDebugger): def handle_qmark(subcmd: str) -> Reply: - from unicorn.x86_const import UC_X86_REG_EIP, UC_X86_REG_ESP - from unicorn.x86_const import UC_X86_REG_RIP, UC_X86_REG_RSP - from unicorn.arm_const import UC_ARM_REG_PC, UC_ARM_REG_SP - from unicorn.arm64_const import UC_ARM64_REG_PC, UC_ARM64_REG_SP - from unicorn.mips_const import UC_MIPS_REG_PC, UC_MIPS_REG_SP + from unicorn.x86_const import UC_X86_REG_EBP + from unicorn.x86_const import UC_X86_REG_RBP + from unicorn.arm_const import UC_ARM_REG_R11 + from unicorn.arm64_const import UC_ARM64_REG_X29 + from unicorn.mips_const import UC_MIPS_REG_29 - # X86 : T0505:00000000;04:c0d3ffff;08:2021fdf7;thread:p15c6.15c6;core:6 - # X8664 : T0506:0000000000000000;07:b0e2ffffff7f0000;10:0001fdf7ff7f0000;thread:p15a2.15a2;core:6; - # MIPS32_EL : T051d:00e7ff7f;25:40ccfc77; - # MIPS32_EB : T051d:7fff6dc0;25:77fc4880;thread:28fa;core:0; - # ARM64 : T051d:0000000000000000;1f:80f6ffffffff0000;20:c02cfdb7ffff0000;thread:p1f9.1f9;core:0; - # ARM : T050b:00000000;0d:e0f6ffbe;0f:8079fdb6; - - response = { - QL_ARCH.X86 : ( 0x05, UC_X86_REG_ESP, UC_X86_REG_EIP ), - QL_ARCH.X8664 : ( 0x06, UC_X86_REG_RSP, UC_X86_REG_RIP ), - QL_ARCH.ARM : ( 0x0b, UC_ARM_REG_SP, UC_ARM_REG_PC ), - QL_ARCH.ARM64 : ( 0x1d, UC_ARM64_REG_SP, UC_ARM64_REG_PC ), - QL_ARCH.MIPS : ( 0x1d, UC_MIPS_REG_SP, UC_MIPS_REG_PC ), - QL_ARCH.A8086 : ( 0x05, UC_X86_REG_ESP, UC_X86_REG_EIP ), - QL_ARCH.CORTEX_M : ( 0x0b, UC_ARM_REG_SP, UC_ARM_REG_PC ) - } - - idhex, sp_reg, pc_reg = response[self.ql.arch.type] + arch_uc_bp = { + QL_ARCH.X86 : UC_X86_REG_EBP, + QL_ARCH.X8664 : UC_X86_REG_RBP, + QL_ARCH.ARM : UC_ARM_REG_R11, + QL_ARCH.ARM64 : UC_ARM64_REG_X29, + QL_ARCH.MIPS : UC_MIPS_REG_29, + QL_ARCH.A8086 : UC_X86_REG_EBP, + QL_ARCH.CORTEX_M : UC_ARM_REG_R11 + }[self.ql.arch.type] def __get_reg_idx(ucreg: int) -> int: """Get the index of a uc reg whithin the regsmap array. @@ -188,16 +179,20 @@ class QlGdb(QlDebugger): return next((i for i, (regnum, _, _) in enumerate(self.regsmap) if regnum == ucreg), -1) - sp_idx = __get_reg_idx(sp_reg) - pc_idx = __get_reg_idx(pc_reg) + # FIXME: a8086 should use 'esp' and 'eip' here instead of 'sp' and 'ip' set by its arch instance + bp_idx = __get_reg_idx(arch_uc_bp) + sp_idx = __get_reg_idx(self.ql.arch.regs.uc_sp) + pc_idx = __get_reg_idx(self.ql.arch.regs.uc_pc) + bp_val = __get_reg_value(*self.regsmap[bp_idx]) sp_val = __get_reg_value(*self.regsmap[sp_idx]) pc_val = __get_reg_value(*self.regsmap[pc_idx]) - zfill = __hexstr(0) + bp_info = f'{bp_idx:02x}:{bp_val};' + sp_info = f'{sp_idx:02x}:{sp_val};' + pc_info = f'{pc_idx:02x}:{pc_val};' - info = '' if self.ql.arch.type == QL_ARCH.MIPS else f':{zfill};{sp_idx:02x}' - return f'T{SIGTRAP:02x}{idhex:02x}{info}:{sp_val};{pc_idx:02x}:{pc_val};' + return f'T{SIGTRAP:02x}{"" if self.ql.arch.type == QL_ARCH.MIPS else bp_info}{sp_info}{pc_info}' def handle_c(subcmd: str) -> Reply: From b3b55110a2e8f9efa1b43f02a0b78579c276b2b5 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 7 May 2022 23:32:25 +0300 Subject: [PATCH 31/37] Fix transmission of large files --- qiling/debugger/gdb/gdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 6f5cc7a6..25dcf545 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -460,7 +460,7 @@ class QlGdb(QlDebugger): f.seek(offset, os.SEEK_SET) content = f.read(length) - return f'l{content}' + return f'{"l" if len(content) < length else "m"}{content}' elif feature == 'threads' and op == 'read': if not self.ql.baremetal and hasattr(self.ql.os, 'pid'): From c375b572b77cbfcc539e77bbd318c48b29a34ff2 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 7 May 2022 23:32:44 +0300 Subject: [PATCH 32/37] Fix a8086 regs mapping for gdb --- qiling/debugger/gdb/xmlregs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index bf2b77cf..f872ee0f 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -80,7 +80,7 @@ def load_regsmap(archtype: QL_ARCH) -> Sequence[RegEntry]: # retreive the relevant set of registers; their order of appearance is not # important as it is determined by the info read from the xml files ucregs: Mapping[str, int] = { - QL_ARCH.A8086 : dict(**x86_regs_16, **x86_regs_misc, **x86_regs_cr, **x86_regs_st), + QL_ARCH.A8086 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st), QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm), QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm), QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp), From 9bfbba1fdf637e7cb9b9fd894263613f65d60946 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 7 May 2022 23:33:14 +0300 Subject: [PATCH 33/37] Remove method that is no longer used --- qiling/arch/register.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qiling/arch/register.py b/qiling/arch/register.py index fab8f3af..368864f6 100644 --- a/qiling/arch/register.py +++ b/qiling/arch/register.py @@ -102,12 +102,6 @@ class QlRegisterManager: return self.uc.reg_write(self.uc_pc, value) - @property - def arch_pc_name(self) -> str: - """Get the architectural program counter register name. - """ - - return next(k for k, v in self.register_mapping.items() if v == self.uc_pc) @property def arch_sp(self) -> int: From 0a06dd4906abbc28a82567eddcd6300e0c56d8d6 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 8 May 2022 12:45:50 +0300 Subject: [PATCH 34/37] Simplify qmark_handler further --- qiling/debugger/gdb/gdb.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 25dcf545..5f3287fb 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -155,18 +155,23 @@ class QlGdb(QlDebugger): def handle_qmark(subcmd: str) -> Reply: + """Request status. + + @see: https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html + """ + from unicorn.x86_const import UC_X86_REG_EBP from unicorn.x86_const import UC_X86_REG_RBP from unicorn.arm_const import UC_ARM_REG_R11 from unicorn.arm64_const import UC_ARM64_REG_X29 - from unicorn.mips_const import UC_MIPS_REG_29 + from unicorn.mips_const import UC_MIPS_REG_INVALID arch_uc_bp = { QL_ARCH.X86 : UC_X86_REG_EBP, QL_ARCH.X8664 : UC_X86_REG_RBP, QL_ARCH.ARM : UC_ARM_REG_R11, QL_ARCH.ARM64 : UC_ARM64_REG_X29, - QL_ARCH.MIPS : UC_MIPS_REG_29, + QL_ARCH.MIPS : UC_MIPS_REG_INVALID, # skipped QL_ARCH.A8086 : UC_X86_REG_EBP, QL_ARCH.CORTEX_M : UC_ARM_REG_R11 }[self.ql.arch.type] @@ -179,20 +184,23 @@ class QlGdb(QlDebugger): return next((i for i, (regnum, _, _) in enumerate(self.regsmap) if regnum == ucreg), -1) + def __get_reg_info(ucreg: int) -> str: + """Retrieve register info and pack it as a pair. + """ + + regnum = __get_reg_idx(ucreg) + hexval = __get_reg_value(*self.regsmap[regnum]) + + return f'{regnum:02x}:{hexval};' + + # mips targets skip this reg info pair + bp_info = '' if self.ql.arch.type == QL_ARCH.MIPS else __get_reg_info(arch_uc_bp) + # FIXME: a8086 should use 'esp' and 'eip' here instead of 'sp' and 'ip' set by its arch instance - bp_idx = __get_reg_idx(arch_uc_bp) - sp_idx = __get_reg_idx(self.ql.arch.regs.uc_sp) - pc_idx = __get_reg_idx(self.ql.arch.regs.uc_pc) + sp_info = __get_reg_info(self.ql.arch.regs.uc_sp) + pc_info = __get_reg_info(self.ql.arch.regs.uc_pc) - bp_val = __get_reg_value(*self.regsmap[bp_idx]) - sp_val = __get_reg_value(*self.regsmap[sp_idx]) - pc_val = __get_reg_value(*self.regsmap[pc_idx]) - - bp_info = f'{bp_idx:02x}:{bp_val};' - sp_info = f'{sp_idx:02x}:{sp_val};' - pc_info = f'{pc_idx:02x}:{pc_val};' - - return f'T{SIGTRAP:02x}{"" if self.ql.arch.type == QL_ARCH.MIPS else bp_info}{sp_info}{pc_info}' + return f'T{SIGTRAP:02x}{bp_info}{sp_info}{pc_info}' def handle_c(subcmd: str) -> Reply: From f82ca0f918131b828f217973490f35010e331ba5 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 8 May 2022 12:46:41 +0300 Subject: [PATCH 35/37] Use proper value endian --- qiling/debugger/gdb/gdb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 5f3287fb..b20f8b4b 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -541,13 +541,15 @@ class QlGdb(QlDebugger): return 'l' elif query == 'TStatus': + tsize = __hexstr(0x500000) + fields = ( 'T0', 'tnotrun:0', 'tframes:0', 'tcreated:0', - 'tfree:500000', - 'tsize:500000', + f'tfree:{tsize}', + f'tsize:{tsize}', 'circular:0', 'disconn:0', 'starttime:0', From 945f32d3f90be88317b8ea94f9afe6b12e53c99e Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 8 May 2022 12:47:05 +0300 Subject: [PATCH 36/37] Document uc stepping bug --- qiling/debugger/gdb/gdb.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index b20f8b4b..a8aeceb1 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -656,6 +656,12 @@ class QlGdb(QlDebugger): """Perform a single step. """ + # BUG: a known unicorn caching issue causes it to emulate more + # steps than requestes. until that issue is fixed, single stepping + # is essentially broken. + # + # @see: https://github.com/unicorn-engine/unicorn/issues/1606 + self.gdb.resume_emu(steps=1) return f'S{SIGTRAP:02x}' From 9c6140de0b151afb1e7c4e80b4053474e16d0f43 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sun, 8 May 2022 18:44:05 +0800 Subject: [PATCH 37/37] pkgutil -> inspect + Path As per elicn's comments. TODO: If Qiling allows 3.9 standard Libs, move everything to importlib.resources.files --- qiling/arch/evm/analysis/signatures.py | 8 +++++--- qiling/debugger/gdb/gdb.py | 20 ++++++++++---------- qiling/os/uefi/__init__.py | 11 +++++++---- qiling/utils.py | 21 +++++++++++---------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/qiling/arch/evm/analysis/signatures.py b/qiling/arch/evm/analysis/signatures.py index 321260fe..e7eed509 100644 --- a/qiling/arch/evm/analysis/signatures.py +++ b/qiling/arch/evm/analysis/signatures.py @@ -1,4 +1,5 @@ -import pkgutil +import inspect +from pathlib import Path import re import logging import json @@ -92,8 +93,9 @@ def analysis_func_sign(insns:list, engine_num=1): class signatures_engine_1: @staticmethod def find_signature(sign): - data = pkgutil.get_data(__package__, 'signatures.json').decode() - data = json.load(data) + path = Path(inspect.getfile(inspect.getframe())).parent / 'signatures.json' + with path.open('r') as data_file: + data = json.load(data_file) list_name = [name for name, hexa in data.items() if hexa == sign] diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 6bccf5b0..768e2e0a 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -7,7 +7,8 @@ # documentation: according to https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol import struct, os, socket -import pkgutil +import inspect +from pathlib import Path from binascii import unhexlify from typing import Iterator, Literal @@ -482,15 +483,14 @@ class QlGdb(QlDebugger, object): else: self.send("PacketSize=47ff;QPassSignals+;QProgramSignals+;QStartupWithShell+;QEnvironmentHexEncoded+;QEnvironmentReset+;QEnvironmentUnset+;QSetWorkingDir+;QCatchSyscalls+;qXfer:libraries-svr4:read+;augmented-libraries-svr4-read+;qXfer:auxv:read+;qXfer:siginfo:read+;qXfer:siginfo:write+;qXfer:features:read+;QStartNoAckMode+;qXfer:osdata:read+;multiprocess+;fork-events+;vfork-events+;exec-events+;QNonStop+;QDisableRandomization+;qXfer:threads:read+;ConditionalTracepoints+;TraceStateVariables+;TracepointSource+;DisconnectedTracing+;FastTracepoints+;StaticTracepoints+;InstallInTrace+;qXfer:statictrace:read+;qXfer:traceframe-info:read+;EnableDisableTracepoints+;QTBuffer:size+;tracenz+;ConditionalBreakpoints+;BreakpointCommands+;QAgent+;Qbtrace:bts+;Qbtrace-conf:bts:size+;Qbtrace:pt+;Qbtrace-conf:pt:size+;Qbtrace:off+;qXfer:btrace:read+;qXfer:btrace-conf:read+;swbreak+;hwbreak+;qXfer:exec-file:read+;vContSupported+;QThreadEvents+;no-resumed+") elif subcmd.startswith('Xfer:features:read'): - if self.ql.os.type is not QL_OS.WINDOWS: - try: - xfercmd_file = subcmd.split(':')[3] - xml_folder = self.ql.arch.type.name.lower() - file_contents = pkgutil.get_data(__package__, f"xml/{xml_folder}/{xfercmd_file}").decode() - self.send("l%s" % file_contents) - except: - self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) - self.send("l") + xfercmd_file = subcmd.split(':')[3] + xfercmd_abspath = Path(inspect.getfile(inspect.currentframe())).parent + xml_folder = self.ql.arch.type.name.lower() + xfercmd_file = xfercmd_abspath / 'xml' / xml_folder / xfercmd_file + + if xfercmd_file.exists() and self.ql.os.type is not QL_OS.WINDOWS: + with xfercmd_file.open('r') as f: + file_contents = f.read() else: self.ql.log.info("gdb> Platform is not supported by xml or xml file not found: %s\n" % (xfercmd_file)) self.send("l") diff --git a/qiling/os/uefi/__init__.py b/qiling/os/uefi/__init__.py index aa390254..0e68ec3c 100644 --- a/qiling/os/uefi/__init__.py +++ b/qiling/os/uefi/__init__.py @@ -1,14 +1,17 @@ import csv from typing import Mapping -import pkgutil +import inspect +from pathlib import Path def __init_guids_db() -> Mapping[str, str]: """Initialize GUIDs dictionary from a local database. """ - guids_file = pkgutil.get_data(__package__, 'guids.csv').decode() - guids_reader = csv.reader(guids_file.splitlines()) + csv_path = Path(inspect.getfile(inspect.currentframe())).parent / 'guids.csv' - return dict(tuple(entry) for entry in guids_reader) + with csv_path.open('r') as guids_file: + guids_reader = csv.reader(guids_file) + + return dict(tuple(entry) for entry in guids_reader) guids_db = __init_guids_db() diff --git a/qiling/utils.py b/qiling/utils.py index 09bb68a3..dc370305 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -9,7 +9,8 @@ thoughout the qiling framework """ from functools import partial -import importlib, pkgutil, os +from pathlib import Path +import importlib, inspect, os from configparser import ConfigParser from types import ModuleType @@ -415,19 +416,19 @@ def profile_setup(ostype: QL_OS, filename: Optional[str]): config = {} else: + qiling_home = Path(inspect.getfile(inspect.currentframe())).parent + os_profile = qiling_home / 'profiles' / f'{ostype.name.lower()}.ql' + + profiles = [os_profile] + + if filename: + profiles.append(filename) + # patch 'getint' to convert integers of all bases int_converter = partial(int, base=0) config = ConfigParser(converters={'int': int_converter}) - - try: - os_profile = pkgutil.get_data(__package__, f'profiles/{ostype.name.lower()}.ql').decode() - config.read_string(os_profile) - except FileNotFoundError as e: - pass - - if filename: - config.read(filename) + config.read(profiles) return config