From 69409d75372ce4bb0a66de4a1de0e56ed28c20b3 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 4 May 2022 22:40:46 +0300 Subject: [PATCH 01/30] Complete rewrite of gdbserver --- qiling/debugger/gdb/gdb.py | 1521 +++++++++++++++++--------------- qiling/debugger/gdb/utils.py | 121 +-- qiling/debugger/gdb/xmlregs.py | 110 +++ 3 files changed, 973 insertions(+), 779 deletions(-) create mode 100644 qiling/debugger/gdb/xmlregs.py diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 8b3416663..574341a46 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 os, socket, re +from logging import Logger +from typing import Iterator, Optional, Union -import struct, os, socket -from binascii import unhexlify -from typing import Iterator, Literal +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 @@ def __init__(self, ql: Qiling, ip: str = '127.0.01', port: int = 9999): 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 - - self.gdb.initialize(self.ql, self.entry_point, exit_point=exit_point, mappings=[(hex(load_address))]) - - #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()), - } + if ql.entry_point is not None: + entry_point = ql.entry_point - 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) + if ql.exit_point is not None: + exit_point = ql.exit_point - if nbits == 64: - s = f'{int.from_bytes(self.ql.pack64(addr), byteorder=endian):016x}' + self.gdb = QlGdbUtils(ql, entry_point, exit_point) - elif nbits == 32: - s = f'{int.from_bytes(self.ql.pack32(addr), byteorder=endian):08x}' + self.regsmap = xmlregs.load_regsmap(self.ql.arch.type) - elif nbits == 16: - s = f'{int.from_bytes(self.ql.pack16(addr), byteorder=endian):04x}' + def run(self): + server = GdbSerialConn(self.ip, self.port, self.ql.log) + killed = False - else: - raise RuntimeError + 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 s + return value.to_bytes(length, byteorder).hex() - def bin_to_escstr(self, rawbin): - rawbin_escape = "" + 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 - def incomplete_hex_check(hexchar): - if len(hexchar) == 1: - hexchar = "0" + hexchar - return hexchar + hexstr = __hexstr(value, nibbles) + else: + hexstr = 'x' * nibbles - for a in rawbin: + return hexstr - # 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). + 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 - 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) - else: - a = (str(hex(a)[2:])) - a = incomplete_hex_check(a) + val = int(hexval, 16) - rawbin_escape += a + if self.ql.arch.endian == QL_ENDIAN.EL: + val = __swap_endianess(val) - return unhexlify(rawbin_escape) + self.ql.arch.regs.write(reg, val) - def setup_server(self): - self.ql.log.info("gdb> Listening on %s:%u" % (self.ip, self.port)) + def __swap_endianess(value: int) -> int: + length = (value.bit_length() + 7) // 8 + raw = value.to_bytes(length, 'little') - 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() + return int.from_bytes(raw, 'big') - self.sock = sock - self.clientsocket = clientsocket - self.netin = clientsocket.makefile('r') - self.netout = clientsocket.makefile('w') - def close(self): - self.netin.close() - self.netout.close() - self.clientsocket.close() - self.sock.close() + def handle_exclaim(subcmd: str) -> Reply: + return REPLY_OK - 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") + + 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: - 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) + # emulation has completed successfully + reply = f'W{self.ql.os.exit_code:02x}' - 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') + return reply - 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) + 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 - 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) + data = ''.join(__get_reg_value(*entry) for entry in self.regsmap) - 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) + return data - 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(' 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 - self.ql.log.info("gdb> Write to register %s with %x\n" % (self.tables[self.ql.arch.type][reg_index], reg_data)) - self.send('OK') + def handle_m(subcmd: str) -> Reply: + """Read target memory. + """ - def handle_Q(subcmd): - if subcmd.startswith( 'StartNoAckMode'): - self.send('OK') + addr, size = (int(p, 16) for p in subcmd.split(',')) - elif subcmd.startswith( 'DisableRandomization'): - self.send('OK') + 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. + """ - elif subcmd.startswith( 'ProgramSignals'): - self.send('OK') + addr, data = subcmd.split(',') + size, data = data.split(':') - elif subcmd.startswith( 'NonStop'): - self.send('OK') + addr = int(addr, 16) + data = bytes.fromhex(data) - elif subcmd.startswith('PassSignals'): - self.send('OK') + assert len(data) == size - elif subcmd.startswith('qemu'): - self.send('') + 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+' + ) - def handle_D(subcmd): - self.send('OK') + return ';'.join(common + features) - 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] + 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, xfercmd_file) + 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 = '' - 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) + 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: - auxvdata = b"" + content = '' - self.send(b'l!%s' % auxvdata) + return f'l{content}' - elif subcmd.startswith('Xfer:exec-file:read:'): - self.send("l%s" % str(self.exe_abspath)) + elif feature == 'auxv' and op == 'read': + auxv_data = bytearray() + if hasattr(self.ql.loader, 'auxv'): + nbytes = self.ql.arch.bits // 8 - 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") + auxv_addr = self.ql.loader.auxv + offset + null_entry = bytes(nbytes * 2) - elif subcmd.startswith("Xfer:btrace-conf:read:"): - self.send("E.Btrace not enabled.") + # 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 - elif subcmd == "Attached": - self.send("") + auxv_data.extend(self.ql.mem.read(auxv_addr, nbytes)) + auxv_addr += nbytes - elif subcmd.startswith("C"): - self.send("") + return b'l' + auxv_data[:length] - elif subcmd.startswith("L:"): - self.send("M001") + elif feature == 'exec-file' and op == 'read': + return f'l{os.path.abspath(self.ql.path)}' - elif subcmd == "fThreadInfo": - self.send("m0") + 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 - elif subcmd == "sThreadInfo": - self.send("l") + # 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 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 feature == 'btrace-conf' and op == 'read': + return 'E.Btrace not enabled.' - elif subcmd == ("TfV"): - self.send("l") + elif query == 'Attached': + return REPLY_EMPTY - elif subcmd == ("TsV"): - self.send("l") + elif query == 'C': + return REPLY_EMPTY - elif subcmd == ("TfP"): - self.send("l") + elif query == 'L': + return 'M001' - elif subcmd == ("TsP"): - self.send("l") + elif query == 'fThreadInfo': + return 'm0' + elif query == 'sThreadInfo': + return 'l' - elif subcmd.startswith("Symbol"): - self.send("") + 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::' + ) - elif subcmd.startswith("Attached"): - self.send("") + return ';'.join(fields) - elif subcmd == "Offsets": - self.send("Text=0;Data=0;Bss=0") + elif query in ('TfV', 'TsV', 'TfP', 'TsP'): + return 'l' + elif query == 'Symbol': + return REPLY_OK - def handle_v(subcmd): + elif query == 'Offsets': + fields = ('Text=0', 'Data=0', 'Bss=0') - if subcmd == 'MustReplyEmpty': - self.send("") + return ';'.join(fields) - elif subcmd.startswith('File:open'): - if self.ql.os.type == QL_OS.UEFI or self.ql.baremetal: - self.send("F-1") - return + return REPLY_EMPTY - (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(',') + 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}' - fd = int(fd, base=16) - offset = int(offset, base=16) - count = int(count, base=16) + elif op == 'pread': + fd, count, offset = (int(p, 16) for p in params) 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;") + return f'F{len(data):x};'.encode() + data + + elif op == 'close': + fd, *_ = params + fd = int(fd, 16) - 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("") + return 'F0' + return REPLY_EMPTY - 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') + 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(',')) - def handle_z(subcmd): - data = subcmd.split(',') - if len(data) != 3: - self.send('E22') + 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: - 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 - } + 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 + } + + # main server loop + for packet in server.readpackets(): + if server.ack_mode: + server.send(REPLY_ACK, raw=True) + + cmd, subcmd = packet[0], packet[1:] + handler = handlers.get(f'{cmd:c}') + + if handler: + reply = handler(subcmd.decode(ENCODING)) + server.send(reply) + + if killed: + break + else: + self.ql.log.info(f'{PROMPT} command not supported') + server.send(REPLY_EMPTY) + + server.close() + - cmd, subcmd = pkt[0], pkt[1:] - if cmd == 'k': +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((ipaddr, port)) + sock.listen() + + 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): + """Close the gdb serial connection handler and release its resources. + """ + + self.client.close() + self.sock.close() + + 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() - - - 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' - - if len(c) != 1: - return 'Error: EOF' - - 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 - - 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, msg): - """Send a packet to the GDB client""" - if type(msg) == str: - self.send_raw('$%s#%.2x' % (msg, self.checksum(msg))) + 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}') + + # follow gdbserver debug output format + self.log.debug(f'getpkt ("{GdbSerialConn.__printable_prefix(data).decode(ENCODING)}");') + + data = GdbSerialConn.rle_decode(data) + data = GdbSerialConn.unescape(data) + + del buffer[:packet.endpos] + yield data + + def send(self, data: Reply, raw: bool = False) -> None: + """Send out a packet. + + 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) + + packet = b'$' + data + b'#' + f'{GdbSerialConn.checksum(data):02x}'.encode() + + # 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) - self.ql.log.debug("gdb> send: $%s#%.2x" % (msg, self.checksum(msg))) + return re.sub(br'.\*.', __repl, data, flags=re.DOTALL) - def send_raw(self, r): - self.netout.write(r) - self.netout.flush() + @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 4c8a5247c..30bba47e8 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.stop() - ql.log.info(f'{PROMPT} Stop at entry point: {ql.arch.regs.arch_pc:#x}') + ql.log.info(f'{PROMPT} stopped at entry point: {ql.arch.regs.arch_pc:#x}') + ql.stop() - 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 - """ - - try: - if ql.arch.type == QL_ARCH.ARM: - if ql.arch.is_thumb: - address += 1 - - # self.mapping.append([(hex(address))]) - self.current_address = address - hit_soft_bp = False - - 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}') + if ql.arch.type == QL_ARCH.ARM and 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 + + elif address in self.bp_list: + self.last_bp = address + + 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 - """ - if address is not None: - if self.ql.arch.type == QL_ARCH.ARM: - if self.ql.arch.is_thumb: - address += 1 + def resume_emu(self, address: Optional[int] = None, steps: int = 0): + if address is None: + address = self.ql.arch.regs.arch_pc - self.current_address = address + if self.ql.arch.type == QL_ARCH.ARM and self.ql.arch.is_thumb: + address += 1 - self.skip_bp_count = skip_bp + op = f'stepping {steps} instructions' if steps else 'resuming' + self.ql.log.info(f'{PROMPT} {op} from {address:#x}') - 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 000000000..53199eafa --- /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/30] 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 71daae8cd..43bc64d7f 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 579e84b04..89826d474 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 d4dbac662..b91fd952d 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 a585dffd3..0a6a1b628 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 a664320d9..502703234 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 d58e543ea..d68705f67 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/30] 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 0958032a8..702ae3b0b 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 7772954d3..1798dbd80 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 6f3c65a87..71aa7dc12 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 c57a2d846..e28536e5d 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/30] 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 798549eea..1e60479fb 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -177,7 +177,7 @@ } 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 4aae910c8..10617cbd1 100644 --- a/qiling/debugger/qdb/arch/arch_x86.py +++ b/qiling/debugger/qdb/arch/arch_x86.py @@ -21,7 +21,7 @@ def regs(self): "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 @@ def read_insn(self, address: int) -> bytes: @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 ce92dd9fd..656c64daa 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 @@ def predict(self): } 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 d1f53f864..ed243446b 100644 --- a/qiling/debugger/qdb/frontend.py +++ b/qiling/debugger/qdb/frontend.py @@ -389,7 +389,7 @@ def __init__(self, ql): "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 @@ def context_reg(self, saved_reg_dump): 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 3597181eb..5c43d0e55 100644 --- a/qiling/debugger/qdb/render/render_x86.py +++ b/qiling/debugger/qdb/render/render_x86.py @@ -22,7 +22,7 @@ def context_reg(self, saved_reg_dump): 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 6fb4d330c..fb7f55c17 100644 --- a/qiling/os/dos/dos.py +++ b/qiling/os/dos/dos.py @@ -58,10 +58,10 @@ def __del__(self): 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 351f0f0b0..808fbe29c 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/30] 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 c6ba9573f..cb62d7c96 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -29,7 +29,7 @@ # 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 @@ def __push_str(top: int, s: str) -> int: 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/30] 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 cb62d7c96..a1be0ef63 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -339,14 +339,17 @@ def __push_str(top: int, s: str) -> int: (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/30] 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 e93393665..a58d0d1ea 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 b634849fc..97850ef66 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 @@ def disassembler(self) -> Cs: @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 1f97e4cbb..2b38d71f6 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/30] 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 2b38d71f6..d7ac9f8db 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -82,3 +82,8 @@ def __casefold_enum(e: Type[T]) -> Mapping[str, T]: 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 5357623ef..2354f74f9 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 41036eb16..99fb31ca5 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/30] 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 ea46aaa6f..1189e698c 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/30] 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 a58d0d1ea..d8def1220 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/30] 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 574341a46..67d7d7d2b 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -29,8 +29,7 @@ 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/30] 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 8391becc1..9368634f2 100644 --- a/qiling/arch/mips.py +++ b/qiling/arch/mips.py @@ -37,7 +37,8 @@ def uc(self) -> Uc: 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 8dd8d48a9..8e3141f4f 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_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/30] 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 53199eafa..63084128c 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -8,7 +8,8 @@ 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/30] 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 67d7d7d2b..b8c53ff43 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -146,28 +146,49 @@ def handle_exclaim(subcmd: str) -> Reply: 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/30] 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 b8c53ff43..e2f00bd8d 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -236,9 +236,7 @@ def handle_g(subcmd: str) -> Reply: # 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 @@ def handle_Q(subcmd: str) -> Reply: 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 @@ def handle_z(subcmd: str) -> Reply: 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 @@ def rle_decode(data: bytes) -> bytes: 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/30] Support arbitrary regs definition order --- qiling/debugger/gdb/xmlregs.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index 63084128c..56d08dd28 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 = [] - pos = 0 - xmlpath = __get_xml_path(archtype) + regsinfo = sorted(__walk_xml_regs(*xmlpath)) - 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))) + # 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 + 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/30] 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 d8def1220..e3dfaf1bb 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -40,3 +40,39 @@ "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 56d08dd28..bafe03d6d 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -7,6 +7,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.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/30] 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 e2f00bd8d..da1c3421a 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -110,6 +110,12 @@ def __hexstr(value: int, nibbles: int = 0) -> str: 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 @@ def __get_reg_value(reg: Optional[int], pos: int, nibbles: int) -> str: 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 @@ 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) + hexval = data[pos : pos + nibbles] - # 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/30] 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 da1c3421a..5592b157f 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -105,6 +105,9 @@ def run(self): 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 @@ def handle_c(subcmd: str) -> 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..?) + # 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/30] 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 e3dfaf1bb..2a268cdfd 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -23,18 +23,6 @@ "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 dd30abe87..000000000 --- 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/30] 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 a2c0a65b2..5032c5039 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 5319d764e..f0584a206 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/30] 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 0367a8189..659496694 100644 --- a/qiling/arch/arm64.py +++ b/qiling/arch/arm64.py @@ -26,7 +26,13 @@ def uc(self) -> Uc: 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 c706d10c9..ee2b55167 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 bafe03d6d..bf2b77cfa 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -9,6 +9,7 @@ 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/30] 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 5b2706e03..599d92b3e 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 0b37e15fa..0cdf73e5e 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 b91fd952d..c741efa61 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 d05446539c0c0a89683f11ac9b38cef4975f4042 Mon Sep 17 00:00:00 2001 From: elicn Date: Sat, 7 May 2022 23:31:57 +0300 Subject: [PATCH 24/30] More generic less hardcoded values --- qiling/debugger/gdb/gdb.py | 53 +++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 5592b157f..6f5cc7a67 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -155,30 +155,21 @@ def handle_exclaim(subcmd: str) -> Reply: 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 - - # 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] + 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 + + 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 @@ def __get_reg_idx(ucreg: int) -> int: 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 25/30] 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 6f5cc7a67..25dcf545e 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -460,7 +460,7 @@ def handle_q(subcmd: str) -> Reply: 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 26/30] 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 bf2b77cfa..f872ee0fb 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 27/30] 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 fab8f3aff..368864f6f 100644 --- a/qiling/arch/register.py +++ b/qiling/arch/register.py @@ -102,12 +102,6 @@ def arch_pc(self, value: int) -> None: 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 28/30] Simplify qmark_handler further --- qiling/debugger/gdb/gdb.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/qiling/debugger/gdb/gdb.py b/qiling/debugger/gdb/gdb.py index 25dcf545e..5f3287fbe 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -155,18 +155,23 @@ def handle_exclaim(subcmd: str) -> Reply: 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 @@ def __get_reg_idx(ucreg: int) -> int: return next((i for i, (regnum, _, _) in enumerate(self.regsmap) if regnum == ucreg), -1) - # 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) + 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]) - 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]) + return f'{regnum:02x}:{hexval};' - bp_info = f'{bp_idx:02x}:{bp_val};' - sp_info = f'{sp_idx:02x}:{sp_val};' - pc_info = f'{pc_idx:02x}:{pc_val};' + # 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 + sp_info = __get_reg_info(self.ql.arch.regs.uc_sp) + pc_info = __get_reg_info(self.ql.arch.regs.uc_pc) - 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 29/30] 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 5f3287fbe..b20f8b4be 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -541,13 +541,15 @@ def handle_q(subcmd: str) -> Reply: 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 30/30] 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 b20f8b4be..a8aeceb18 100644 --- a/qiling/debugger/gdb/gdb.py +++ b/qiling/debugger/gdb/gdb.py @@ -656,6 +656,12 @@ def handle_s(subcmd: str) -> Reply: """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}'