diff options
Diffstat (limited to 'miasm2/analysis/gdbserver.py')
| -rw-r--r-- | miasm2/analysis/gdbserver.py | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/miasm2/analysis/gdbserver.py b/miasm2/analysis/gdbserver.py new file mode 100644 index 00000000..8d0135e9 --- /dev/null +++ b/miasm2/analysis/gdbserver.py @@ -0,0 +1,425 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +import socket +import struct +import time +import logging +from StringIO import StringIO +import miasm2.analysis.debugging as debugging +from miasm2.jitter.jitload import ExceptionHandle + + +class GdbServer(object): + + "Debugguer binding for GDBServer protocol" + + general_registers_order = [] + general_registers_size = {} # RegName : Size in octet + status = "S05" + + def __init__(self, dbg, port=4455): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(('localhost', port)) + server.listen(1) + self.server = server + self.dbg = dbg + + # Communication methods + + def compute_checksum(self, data): + return chr(sum(map(ord, data)) % 256).encode("hex") + + def get_messages(self): + all_data = "" + data = self.sock.recv(4096) + all_data += data + while (len(data) == 4096 or data == ""): + if data == "": + # Avoid consuming CPU + time.sleep(0.001) + continue + data = self.sock.recv(4096) + all_data += data + + logging.debug("<- %r" % all_data) + self.recv_queue += self.parse_messages(all_data) + + def parse_messages(self, data): + buf = StringIO(data) + + msgs = [] + + while (buf.tell() < buf.len): + token = buf.read(1) + if token == "+": + continue + if token == "-": + raise NotImplementedError("Resend packet") + if token == "$": + packet_data = "" + c = buf.read(1) + while c != "#": + packet_data += c + c = buf.read(1) + checksum = buf.read(2) + if checksum != self.compute_checksum(packet_data): + raise ValueError("Incorrect checksum") + + msgs.append(packet_data) + + return msgs + + def send_string(self, s): + self.send_queue.append("O" + s.encode("hex")) + + def process_messages(self): + + while self.recv_queue: + msg = self.recv_queue.pop(0) + buf = StringIO(msg) + msg_type = buf.read(1) + + self.send_queue.append("+") + + if msg_type == "q": + if msg.startswith("qSupported"): + self.send_queue.append("PacketSize=3fff") + elif msg.startswith("qC"): + # Current thread + self.send_queue.append("") + elif msg.startswith("qAttached"): + # Not supported + self.send_queue.append("") + elif msg.startswith("qTStatus"): + # Not supported + self.send_queue.append("") + elif msg.startswith("qfThreadInfo"): + # Not supported + self.send_queue.append("") + else: + raise NotImplementedError() + + elif msg_type == "H": + # Set current thread + self.send_queue.append("OK") + + elif msg_type == "?": + # Report why the target halted + self.send_queue.append(self.status) # TRAP signal + + elif msg_type == "g": + # Report all general register values + self.send_queue.append(self.report_general_register_values()) + + elif msg_type == "p": + # Read a specific register + reg_num = int(buf.read(), 16) + self.send_queue.append(self.read_register(reg_num)) + + elif msg_type == "P": + # Set a specific register + reg_num, value = buf.read().split("=") + reg_num = int(reg_num, 16) + value = int(value.decode("hex")[::-1].encode("hex"), 16) + self.set_register(reg_num, value) + self.send_queue.append("OK") + + elif msg_type == "m": + # Read memory + addr, size = map(lambda x: int(x, 16), buf.read().split(",")) + self.send_queue.append(self.read_memory(addr, size)) + + elif msg_type == "k": + # Kill + self.sock.close() + exit(1) + + elif msg_type == "!": + # Extending debugging will be used + self.send_queue.append("OK") + + elif msg_type == "v": + if msg == "vCont?": + # Is vCont supported ? + self.send_queue.append("") + + elif msg_type == "s": + # Step + self.dbg.step() + self.send_queue.append("S05") # TRAP signal + + elif msg_type == "Z": + # Add breakpoint or watchpoint + bp_type = buf.read(1) + if bp_type == "0": + # Exec breakpoint + assert(buf.read(1) == ",") + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + + if size != 1: + raise NotImplementedError("Bigger size") + self.dbg.add_breakpoint(addr) + self.send_queue.append("OK") + + elif bp_type == "1": + # Hardware BP + assert(buf.read(1) == ",") + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + + self.dbg.add_memory_breakpoint(addr, size, + read=True, + write=True) + self.send_queue.append("OK") + + elif bp_type in ["2", "3", "4"]: + # Memory breakpoint + assert(buf.read(1) == ",") + read = bp_type in ["3", "4"] + write = bp_type in ["2", "4"] + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + + self.dbg.add_memory_breakpoint(addr, size, + read=read, + write=write) + self.send_queue.append("OK") + + else: + raise ValueError("Impossible value") + + elif msg_type == "z": + # Remove breakpoint or watchpoint + bp_type = buf.read(1) + if bp_type == "0": + # Exec breakpoint + assert(buf.read(1) == ",") + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + + if size != 1: + raise NotImplementedError("Bigger size") + dbgsoft = self.dbg.get_breakpoint_by_addr(addr) + assert(len(dbgsoft) == 1) + self.dbg.remove_breakpoint(dbgsoft[0]) + self.send_queue.append("OK") + + elif bp_type == "1": + # Hardware BP + assert(buf.read(1) == ",") + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + self.dbg.remove_memory_breakpoint_by_addr_access( + addr, read=True, write=True) + self.send_queue.append("OK") + + elif bp_type in ["2", "3", "4"]: + # Memory breakpoint + assert(buf.read(1) == ",") + read = bp_type in ["3", "4"] + write = bp_type in ["2", "4"] + addr, size = map( + lambda x: int(x, 16), buf.read().split(",")) + + self.dbg.remove_memory_breakpoint_by_addr_access( + addr, read=read, write=write) + self.send_queue.append("OK") + + else: + raise ValueError("Impossible value") + + elif msg_type == "c": + # Continue + self.status = "" + self.send_messages() + ret = self.dbg.run() + if isinstance(ret, debugging.DebugBreakpointSoft): + self.status = "S05" + self.send_queue.append("S05") # TRAP signal + elif isinstance(ret, ExceptionHandle): + if ret == ExceptionHandle.memoryBreakpoint(): + self.status = "S05" + self.send_queue.append("S05") + else: + raise NotImplementedError("Unknown Except") + else: + raise NotImplementedError() + + else: + raise NotImplementedError( + "Not implemented: message type '%s'" % msg_type) + + def send_messages(self): + for msg in self.send_queue: + if msg == "+": + data = "+" + else: + data = "$%s#%s" % (msg, self.compute_checksum(msg)) + logging.debug("-> %r" % data) + self.sock.send(data) + self.send_queue = [] + + def main_loop(self): + self.recv_queue = [] + self.send_queue = [] + + self.send_string("Test\n") + + while (self.sock): + self.get_messages() + self.process_messages() + self.send_messages() + + def run(self): + self.sock, self.address = self.server.accept() + self.main_loop() + + # Debugguer processing methods + def report_general_register_values(self): + s = "" + for i in xrange(len(self.general_registers_order)): + s += self.read_register(i) + return s + + def read_register(self, reg_num): + reg_name = self.general_registers_order[reg_num] + reg_value = self.read_register_by_name(reg_name) + size = self.general_registers_size[reg_name] + + pack_token = "" + if size == 1: + pack_token = "<B" + elif size == 2: + pack_token = "<H" + elif size == 4: + pack_token = "<I" + elif size == 8: + pack_token = "<Q" + else: + raise NotImplementedError("Unknown size") + + return struct.pack(pack_token, reg_value).encode("hex") + + def set_register(self, reg_num, value): + reg_name = self.general_registers_order[reg_num] + self.dbg.set_reg_value(reg_name, value) + + def read_register_by_name(self, reg_name): + return self.dbg.get_reg_value(reg_name) + + def read_memory(self, addr, size): + except_flag_vm = self.dbg.myjit.vm.vm_get_exception() + try: + return self.dbg.get_mem_raw(addr, size).encode("hex") + except RuntimeError: + self.dbg.myjit.vm.vm_set_exception(except_flag_vm) + return "00" * size + + +class GdbServer_x86_32(GdbServer): + + "Extend GdbServer for x86 32bits purposes" + + general_registers_order = ["EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", + "EDI", "EIP", "EFLAGS", "CS", "SS", "DS", "ES", + "FS", "GS"] + + general_registers_size = {"EAX": 4, + "ECX": 4, + "EDX": 4, + "EBX": 4, + "ESP": 4, + "EBP": 4, + "ESI": 4, + "EDI": 4, + "EIP": 4, + "EFLAGS": 2, + "CS": 2, + "SS": 2, + "DS": 2, + "ES": 2, + "FS": 2, + "GS": 2} + + register_ignore = [ + "tf", "i_f", "nt", "rf", "vm", "ac", "vif", "vip", "i_d"] + + def read_register_by_name(self, reg_name): + sup_func = super(GdbServer_x86_32, self).read_register_by_name + if reg_name == "EFLAGS": + val = 0 + eflags_args = [ + "cf", 1, "pf", 0, "af", 0, "zf", "nf", "tf", "i_f", "df", "of"] + eflags_args += ["nt", 0, "rf", "vm", "ac", "vif", "vip", "i_d"] + eflags_args += [0] * 10 + + for i, arg in enumerate(eflags_args): + if isinstance(arg, str): + if arg not in self.register_ignore: + to_add = sup_func(arg) + else: + to_add = 0 + else: + to_add = arg + + val |= (to_add << i) + return val + else: + return sup_func(reg_name) + + +class GdbServer_msp430(GdbServer): + + "Extend GdbServer for msp430 purposes" + + general_registers_order = ["PC", "SP", "SR", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", + "R15"] + + general_registers_size = {"PC": 2, + "SP": 2, + "SR": 2, + "R3": 2, + "R2": 2, + "R5": 2, + "R6": 2, + "R7": 2, + "R8": 2, + "R9": 2, + "R10": 2, + "R11": 2, + "R12": 2, + "R13": 2, + "R12": 2, + "R15": 2} + + def read_register_by_name(self, reg_name): + sup_func = super(GdbServer_msp430, self).read_register_by_name + if reg_name == "SR": + o = sup_func('res') + o <<= 1 + o |= sup_func('of') + o <<= 1 + o |= sup_func('scg1') + o <<= 1 + o |= sup_func('scg0') + o <<= 1 + o |= sup_func('osc') + o <<= 1 + o |= sup_func('cpuoff') + o <<= 1 + o |= sup_func('gie') + o <<= 1 + o |= sup_func('nf') + o <<= 1 + o |= sup_func('zf') + o <<= 1 + o |= sup_func('cf') + + return o + else: + return sup_func(reg_name) + |