class frankensteinCLI(internalblue.cli.InternalBlueCLI):
    def __init__(self, main_args):
        super().__init__(main_args)

        if self.internalblue.fw.FW_NAME == "CYW20735B1":
            self.internalblue.patchRom(0x3d32e, b"\x70\x47\x70\x47")
        elif self.internalblue.fw.FW_NAME == "CYW20819A1":
            self.internalblue.patchRom(0x2330e, b"\x70\x47\x70\x47")
        self.internalblue.registerHciCallback(self.debug_hci_callback)
        self.internalblue.registerHciCallback(self.map_memory_hci_callback)
        self.internalblue.registerHciCallback(self.xmit_state_hci_callback)

    """
        Print info messages emitted from the firmware
    """

    def debug_hci_callback(self, record):
        hcipkt = record[0]
        if not issubclass(hcipkt.__class__, hci.HCI_Event):
            return

        #We use event code 0xfe for info text messages
        #Buffer messages until a newline is found
        if hcipkt.event_code == 0xfe:
            self.msg += hcipkt.data.decode("utf-8")
            while "\n" in self.msg:
                msg_split = self.msg.split("\n")
                self.logger.info("Firmware says: %s" % msg_split[0])
                self.msg = "\n".join(msg_split[1:])

        #We use event code 0xfd for data
        if hcipkt.event_code == 0xfd:
            self.logger.info("Firmware says: %s" % hexlify(hcipkt.data))

        return

    msg = ""
    """
        Loads Sections from ELF file to firmware
        Loads symbols into global dict
        Executes entry point of ELF on behalf
    """

    def load_ELF(self, fname):
        "Loads an ELF file to device and executes entry point"

        try:
            fd = open(fname, "rb")
            self.elf = elffile.ELFFile(fd)
        except:
            traceback.print_exc()
            self.logger.warn("Could not parse ELF file...")
            return False

        if "_fini" in symbols:
            fini = symbols["_fini"]
            if yesno("Found _fini of already installed patch at 0x%x. Execute?"
                     % fini):
                self.launchRam(fini - 1)

        #load sections
        for i in range(self.elf.num_sections()):
            section = self.elf.get_section(i)
            if section.header["sh_flags"] & SH_FLAGS.SHF_ALLOC != 0:
                addr = section.header["sh_addr"]
                name = section.name

                #NOBITS sections contains no data in file
                #Will be initialized with zero
                if section.header["sh_type"] == "SHT_NOBITS":
                    data = b"\x00" * section.header["sh_size"]
                else:
                    data = section.data()

                self.logger.info("Loading %s @ 0x%x - 0x%x (%d bytes)" %
                                 (name, addr, addr + len(data), len(data)))
                #write section data to device
                self.writeMem(addr, data)

        #load symbols
        n = 0
        for i in range(self.elf.num_sections()):
            section = self.elf.get_section(i)
            if section.header.sh_type == 'SHT_SYMTAB':
                for symbol in section.iter_symbols():
                    if symbol.name != "" and "$" not in symbol.name:
                        symbols[symbol.name] = symbol.entry["st_value"]
                        n += 1
        self.logger.info("Loaded %d symbols" % n)

        return self.elf.header.e_entry

    """
    Command implementation
    """
    description = "Loads an ELF file to device and executes entry point"
    loadelf_parser = argparse.ArgumentParser(description=description)
    loadelf_parser.add_argument("fname", help="ELF file to load")

    @cmd2.with_argparser(loadelf_parser)
    def do_loadelf(self, args):
        if args == None:
            return False

        if not os.path.exists(args.fname):
            self.logger.warn("Could not find file %s" % args.fname)
            return False

        entry = self.load_ELF(args.fname)

        #execute entrypoint
        if entry and entry != 0:
            if yesno("Found nonzero entry point 0x%x. Execute?" % entry):
                self.launchRam(entry - 1)

        return False

    """
    Map memory events
    """

    def map_memory_hci_callback(self, record):
        hcipkt = record[0]
        if not issubclass(hcipkt.__class__, hci.HCI_Event):
            return

        #State report
        if hcipkt.event_code == 0xfa:
            addr = struct.unpack("I", hcipkt.data[:4])[0]
            if addr == 0xffffffff:
                self.expected_addr = -1
                self.segment_start = -1

            #addr is mapped
            elif addr & 1 == 0:
                if self.expected_addr != addr:
                    if self.expected_addr != -1:
                        print("Found Map 0x%x - 0x%x" %
                              (self.segment_start, self.expected_addr))

                    self.segment_start = addr
                self.expected_addr = addr + 256
                self.last_addr = addr

            #Not mapped
            else:
                self.last_addr = addr

            if self.watchdog:
                self.watchdog.cancel()
            self.watchdog = Timer(1, self.watchdog_handle)
            self.watchdog.start()

    def watchdog_handle(self):
        self.logger.warn("Firmware died at address 0x%x while mapping memory" %
                         (self.last_addr & 0xfffffffe))

    """
    Command implementation
    """
    description = "Loads an ELF file to device and executes entry point"
    mapmemory_parser = argparse.ArgumentParser(description=description)
    mapmemory_parser.add_argument("start",
                                  help="Start address for mapping",
                                  type=internalblue.cli.auto_int)
    watchdog = None

    @cmd2.with_argparser(mapmemory_parser)
    def do_mapmemory(self, args):
        if args == None:
            return False

        patch = "projects/%s/gen/map_memory.patch" % self.internalblue.fw.FW_NAME
        if not os.path.exists(patch):
            self.logger.warn("Could not find file %s" % patch)
            return False

        entry = self.load_ELF(patch)

        start = struct.pack("I", args.start)
        self.last_addr = args.start
        self.writeMem(symbols["map_memory_start"], start)

        self.launchRam(entry - 1)

        return False

    """
    Receive state dump
    """

    def xmit_state_hci_callback(self, record):
        hcipkt = record[0]
        if not issubclass(hcipkt.__class__, hci.HCI_Event):
            return

        #State report
        if hcipkt.event_code == 0xfc:
            saved_regs, cont = struct.unpack("II", hcipkt.data[:8])
            if cont != 0:
                self.logger.info(
                    "Receiving firmware state: regs@0x%x cont@0x%x" %
                    (saved_regs, cont))
                self.segment_data = []
                self.segments = {}
                self.succsess = True
                self.saved_regs = saved_regs
                self.cont = cont

            else:
                if not self.succsess:
                    return
                self.logger.info("Received fuill firmware state")

                groupName = datetime.now().strftime(
                    "internalBlue_%m.%d.%Y_%H.%M.%S")
                self.project = Project("projects/" +
                                       self.internalblue.fw.FW_NAME)
                self.project.group_add(groupName)
                self.project.group_deactivate_all()
                self.project.group_set_active(groupName, True)
                self.project.save()
                self.project.add_symbol(groupName, "cont", self.cont | 1)
                self.project.add_symbol(groupName, "get_int",
                                        symbols["get_int"])
                self.project.add_symbol(groupName, "set_int",
                                        symbols["set_int"])
                self.project.add_symbol(groupName, "saved_regs",
                                        self.saved_regs)
                for segment_addr in self.segments:
                    self.project.add_segment(
                        groupName, "", segment_addr,
                        b"".join(self.segments[segment_addr]))

                self.project.save()

        if hcipkt.event_code == 0xfb:
            segment_addr, size, current = struct.unpack(
                "III", hcipkt.data[:12])
            self.segment_data += [hcipkt.data[12:]]

            #Check if we have missed an HCI event
            if segment_addr + len(self.segment_data) * 128 != current + 128:
                if self.succsess:
                    print(hex(segment_addr), hex(len(self.segment_data) * 128),
                          hex(current + 128))
                    self.logger.info("Failed to receive state")
                self.succsess = False

            #Fully received memory dumo
            if len(self.segment_data) * 128 == size:
                self.logger.info("Received segment 0x%x - 0x%x" %
                                 (segment_addr, segment_addr + size))
                self.segments[segment_addr] = self.segment_data
                self.segment_data = []

    """
    Command implementation
    """
    description = "Sets a hook on a function, emmits an executable state and add the dump to frankenstein"
    xmitstate_parser = argparse.ArgumentParser(description=description)
    xmitstate_parser.add_argument("target",
                                  help="Target function",
                                  type=internalblue.cli.auto_int)

    @cmd2.with_argparser(xmitstate_parser)
    def do_xmitstate(self, args):
        if not args:
            return False

        patch = "projects/%s/gen/xmit_state.patch" % self.internalblue.fw.FW_NAME
        print(patch)
        if not os.path.exists(patch):
            self.logger.warn("Could not find file %s" % patch)
            return False

        entry = self.load_ELF(patch)
        if entry == False:
            self.logger.warn("Failed to load patch ELF %s" % patch)
            return False

        target = struct.pack("I", args.target | 1)
        self.writeMem(symbols["xmit_state_target"], target)

        self.launchRam(entry - 1)

        return False
Esempio n. 2
0
class CmdXmitState(CmdLoadELF):
    keywords = ["xmitstate"]
    description = "Sets a hook on a function, emmits an executable state and add the dump to frankenstein"

    parser = argparse.ArgumentParser(prog=keywords[0], description=description)

    parser.add_argument("target", help="Target function", type=auto_int)

    """
    Receive state dump
    """
    def xmit_state_hci_callback(self, record):
        hcipkt = record[0]
        if not issubclass(hcipkt.__class__, hci.HCI_Event):
            return

        #State report
        if hcipkt.event_code == 0xfc:
            saved_regs, cont = struct.unpack("II", hcipkt.data[:8])
            if cont != 0:
                log.info("Receiving firmware state: regs@0x%x cont@0x%x" % (saved_regs, cont))
                self.segment_data = []
                self.segments = {}
                self.succsess = True
                self.saved_regs = saved_regs
                self.cont = cont

            else:
                if not self.succsess:
                    return
                log.info("Received fuill firmware state")

                groupName = datetime.now().strftime("internalBlue_%m.%d.%Y_%H.%M.%S")
                self.project = Project("projects/"+self.internalblue.fw.FW_NAME)
                self.project.add_group(groupName)
                self.project.deactivate_all_groups()
                self.project.set_active_group(groupName, True)
                self.project.save()
                self.project.add_symbol(groupName, "cont", self.cont|1)
                self.project.add_symbol(groupName, "get_int", symbols["get_int"])
                self.project.add_symbol(groupName, "set_int", symbols["set_int"])
                self.project.add_symbol(groupName, "saved_regs", self.saved_regs)
                for segment_addr in self.segments:
                    self.project.add_segment(groupName, "", segment_addr, "".join(self.segments[segment_addr]))

                self.project.save()

        if hcipkt.event_code == 0xfb:
            segment_addr,size,current = struct.unpack("III", hcipkt.data[:12])
            self.segment_data += [hcipkt.data[12:]]

            #Check if we have missed an HCI event
            if segment_addr + len(self.segment_data)*128 != current + 128:
                if self.succsess:
                    print( hex(segment_addr), hex(len(self.segment_data)*128), hex( current + 128))
                    log.info("Failed to receive state")
                self.succsess = False
                
            #Fully received memory dumo
            if len(self.segment_data)*128 == size:
                log.info("Received segment 0x%x - 0x%x" % (segment_addr, segment_addr+size))
                self.segments[segment_addr] = self.segment_data
                self.segment_data = []

    """
    Command implementation
    """
    def work(self):
        args = self.getArgs()
        if not args:
            return True

        # Initialize callbacks for xmitstate
        global CmdXmitStateInitialized

        if not CmdXmitStateInitialized:

            # disable uart_SetRTSMode if we know its location
            if self.internalblue.fw.FW_NAME == "CYW20735B1":
                self.internalblue.patchRom(0x3d32e, b"\x70\x47\x70\x47")
            elif self.internalblue.fw.FW_NAME == "CYW20819A1":
                self.internalblue.patchRom(0x2330e, b"\x70\x47\x70\x47")

            # and now let's enable the callbacks
            self.internalblue.registerHciCallback(self.debug_hci_callback)
            self.internalblue.registerHciCallback(self.xmit_state_hci_callback)
            CmdXmitStateInitialized = True


        patch = "projects/%s/gen/xmit_state.patch" % self.internalblue.fw.FW_NAME
        print(patch)
        if not os.path.exists(patch):
            log.warn("Could not find file %s" % patch)
            return False

        entry = self.load_ELF(patch)
        if entry == False:
            log.warn("Failed to load patch ELF %s" % patch)
            return False

        target = struct.pack("I", args.target | 1)
        self.writeMem(symbols["xmit_state_target"], target)

        self.launchRam(entry-1)

        return entry != False