From 24b535bc7e77df2da9371669f489d54c49ee8dc5 Mon Sep 17 00:00:00 2001 From: Eljakim Herrewijnen Date: Tue, 23 Apr 2024 23:04:16 +0200 Subject: [PATCH] started work on s922 --- source/bootrom_emulator/.vscode/launch.json | 7 +- source/bootrom_emulator/Readme.md | 2 +- source/bootrom_emulator/amlogic_rom_db.py | 20 + source/bootrom_emulator/bin/S922X_bootrom.bin | Bin 0 -> 65536 bytes source/bootrom_emulator/emulator.py | 347 +++++++++++++++++ source/bootrom_emulator/fuzzer.py | 360 +----------------- 6 files changed, 377 insertions(+), 359 deletions(-) create mode 100644 source/bootrom_emulator/amlogic_rom_db.py create mode 100644 source/bootrom_emulator/bin/S922X_bootrom.bin create mode 100644 source/bootrom_emulator/emulator.py diff --git a/source/bootrom_emulator/.vscode/launch.json b/source/bootrom_emulator/.vscode/launch.json index 6146163..463b9d1 100644 --- a/source/bootrom_emulator/.vscode/launch.json +++ b/source/bootrom_emulator/.vscode/launch.json @@ -8,18 +8,17 @@ "name": "Run Emulator", "type": "python", "request": "launch", - "program": "fuzzer.py", + "program": "emulator.py", "console": "integratedTerminal", "justMyCode": false }, { - "name": "Run Emulator", + "name": "Run Fuzzer", "type": "python", "request": "launch", "program": "fuzzer.py", "console": "integratedTerminal", "justMyCode": false, - "" - }, + } ] } \ No newline at end of file diff --git a/source/bootrom_emulator/Readme.md b/source/bootrom_emulator/Readme.md index 2328ea4..bd36312 100644 --- a/source/bootrom_emulator/Readme.md +++ b/source/bootrom_emulator/Readme.md @@ -14,7 +14,7 @@ Save the container ## Usage ```bash -afl-fuzz -U -m none -o /tmp/output_fuzz -i input/ python3 fuzzer.py +afl-fuzz -U -m none -o /tmp/output_fuzz -i input/ python3 fuzzer.py ``` debug diff --git a/source/bootrom_emulator/amlogic_rom_db.py b/source/bootrom_emulator/amlogic_rom_db.py new file mode 100644 index 0000000..5d7653b --- /dev/null +++ b/source/bootrom_emulator/amlogic_rom_db.py @@ -0,0 +1,20 @@ +TEST_OFFSET = 0xfffa0000 + 0x8000 +TEST_REQ_BUFFER = TEST_OFFSET + 0x800 +TEST_CONTEXT_BUFFER = TEST_OFFSET + 0x9000 + +amlogic_rom_db = { + "S905X3" : { + "ENTRY_POINT" : 0xffff0000, + "STACK_ADDRESS" : 0xfffe3800, + "FASTBOOT_CMD_HANDLER" : 0xffff9758, + "fastboot_response" : 0xffff8c04, + "usb_ep_queue" : 0xffff8998 + }, + "S922" : { + "ENTRY_POINT" : 0xffff0000, + "STACK_ADDRESS" : 0xfffe3800, + "FASTBOOT_CMD_HANDLER" : 0xffff815c, + "fastboot_response" : 0xffff7740, + "usb_ep_queue" : 0xffff7474 + }, +} \ No newline at end of file diff --git a/source/bootrom_emulator/bin/S922X_bootrom.bin b/source/bootrom_emulator/bin/S922X_bootrom.bin new file mode 100644 index 0000000000000000000000000000000000000000..76a2d7180aa17b5b3503563222c852428b522665 GIT binary patch literal 65536 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 None: + ''' + Amlogic bootrom emulator. + ''' + super().__init__(True) + + self.logger = setup_logger("GSCEmulator") + self.logger.setLevel(logging.DEBUG) + self.debug = debug + + self.bootrom = open(file, 'rb').read() + self.uc = Uc(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN) + self.sc = ShellcodeCrafterARM64(None, None) + self.device_offsets = amlogic_rom_db[device] + self.setup_memory() + self.setup_registers() + self.setup_hooks() + self.setup_devices() + self.apply_patches() + self.debug = False + self.afl_input = [] + self.em = None # By default GA not used + self.wait_for_usb = False # USB not ready + + self.I2C_inject_buffer = b"" + + self.pc_trace = [] + self.enable_trace = False + + def write(self, address, data): + self.uc.mem_write(address, data) + + def read(self, address, size): + return bytes(self.uc.mem_read(address, size)) + + def apply_patches(self): + self.write(self.device_offsets['fastboot_response'], self.sc.ret_ins) + self.write(self.device_offsets['usb_ep_queue'], self.sc.ret_ins) + + def setup_memory(self): + #Bootrom + self.uc.mem_map(ENTRY_POINT, len(self.bootrom), UC_PROT_EXEC | UC_PROT_READ) + self.write(ENTRY_POINT, self.bootrom) + + # Timer 0xff800000|0xff800fff|rti + self.uc.mem_map(0xff800000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # SRAM 0xfffa0000|0xfffe7fff | + self.uc.mem_map(0xfffa0000, 0x48000, UC_PROT_READ | UC_PROT_WRITE) + + # ISA 0xffd0f000|0xffd0ffff|isa + self.uc.mem_map(0xffd0f000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # eFuse 0xff630000|0xff631fff|efuse + self.uc.mem_map(0xff630000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + self.uc.mem_write(0xff630000, open(get_local_file("bin/efuse_ff630000_ff632000.bin"), 'rb').read()) + + # UART 0xff803000|0xff803fff|uart and add hook + self.uc.mem_map(0xff803000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # ?? 0xffd08000|0xffd08fff|assist + self.uc.mem_map(0xffd08000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # 0xff634400|0xff6347ff|periphs_reg + self.uc.mem_map(0xff634400, 0x400, UC_PROT_READ | UC_PROT_WRITE) + + + # Reset? 0xffd01000|0xffd01fff|reset + self.uc.mem_map(0xffd01000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # RAM for USB? ff63c000 + self.uc.mem_map(0xff63c000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) + + # UNKNOWN + self.uc.mem_map(0xffe09000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + + # DMA 0xff63e000|0xff63ffff|dma + self.uc.mem_map(0xff63e000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + + # 0xff63a000|0xff63bfff|usbphy21 + self.uc.mem_map(0xff63a000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + + # USB1 0xff400000|0xff4fffff|usb1 + self.uc.mem_map(0xff400000, 0x100000, UC_PROT_READ | UC_PROT_WRITE) #1MB + + # UNKNOWN 0xffd14000|0xffd14fff|spifc + self.uc.mem_map(0xffd14000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) #1MB + + # Flash 0xf6000000|0xf9ffffff|flash #TODO add flash device for fuzzing? + self.uc.mem_map(0xf6000000, 0xf9ffffff + 1 - 0xf6000000, UC_PROT_READ | UC_PROT_WRITE) + + # Emmc 0xffe07000|0xffe08fff|emmcC TODO add eMMC device for fuzzing? + self.uc.mem_map(0xffe07000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + # 0xffe05000|0xffe06fff|emmcB + self.uc.mem_map(0xffe05000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + # FFE03000 FFE04FFF emmcA + self.uc.mem_map(0xFFE03000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) + + def write_ptr(self, at, ptr): + return self.uc.mem_write(at, p32(ptr)) + + def read_ptr(self, at): + return u32(self.uc.mem_read(at, 4)) + + def setup_registers(self): + self.pc = ENTRY_POINT + self.sp = STACK_ADDRESS + + def setup_hooks(self): + if self.debug: + self.uc.hook_add(UC_HOOK_CODE, self.hook_code) + self.uc.hook_add(UC_HOOK_INSN_INVALID, self.hook_invalid_ins) + self.uc.hook_add(UC_HOOK_INTR, self.hook_exception) + self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff3f0000, 0xff30cffe + 1) + + #Add hook usb device + self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff63a000, 0xff63bfff + 1) + self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff400000, 0xff4fffff + 1) + + def setup_devices(self): + self.devices = [ + # USBDevice(self), + UARTDevice(self), + TimerDevice(self), + GPIODevice(self), + EfuseDevice(self), + USBPHYDevice(self), + USB1Device(self), + ] + + def hook_devices(self, handle, access, address, size, value, user_data): + if address == 0xff630044 and access == UC_MEM_WRITE: + pass + for device in self.devices: + if address >= device.BASE and address < device.BASE + device.SIZE: + if access == UC_MEM_READ: + device.read(address, size) + else: + device.write(address, size, value) + return True + return True + + def get_device(self, name): + for dev in self.devices: + if dev.NAME == name: + return dev + return None + + def run(self): + while True: + self.uc.emu_start(self.pc, self.pc + 4) + + def run_until_usb(self): + info("Run until USB") + try: + while True: + self.uc.emu_start(self.pc, self.pc + 4) + if self.wait_for_usb: + return + except Exception as e: + print(bytes(self.get_device("UART").get_rx()).decode(errors="ignore")) + self.print_ctx() + print(str(e)) + if e.errno == UC_ERR_READ_UNMAPPED or e.errno == UC_ERR_WRITE_UNMAPPED: + pass + # print(find_memory()) + exit(0) + + @property + def pc(self): + return self.uc.reg_read(UC_ARM64_REG_PC) + + @pc.setter + def pc(self, value): + self.uc.reg_write(UC_ARM64_REG_PC, value) + + @property + def sp(self): + return self.uc.reg_read(UC_ARM64_REG_SP) + + @sp.setter + def sp(self, value): + self.uc.reg_write(UC_ARM64_REG_SP, value) + + @property + def X0(self): + return self.uc.reg_read(UC_ARM64_REG_X0) + + @X0.setter + def X0(self, value): + self.uc.reg_write(UC_ARM64_REG_X0, value) + + @property + def X1(self): + return self.uc.reg_read(UC_ARM64_REG_X1) + + @X1.setter + def X1(self, value): + self.uc.reg_write(UC_ARM64_REG_X1, value) + + def pc_in_debug(self): + # for start, end in debug_functions: + # if self.pc >= start and self.pc <= end: + # return True + return False + + def dump_stack(self, n_offset = 0, size = 0x200): + hexdump(bytes(self.uc.mem_read(self.sp - n_offset, size))) + + def add_breakpoint(self, at): + def _breakpoint(uc : Uc, address, size, em : Amlogic_Emulator): + em.print_ctx(print) + pass + self.uc.hook_add(UC_HOOK_CODE, _breakpoint, self, at, at + 1) + + def hook_code(self, uc : Uc, address, size, user_data): + ''' + Hook startup code to continue boot process + ''' + if self.enable_trace: + self.pc_trace.append(hex(self.pc)) + # Reset expects value in DRAM + if self.pc == 0xffff00b0: + self.write_ptr(0xff800228, uc.reg_read(UC_ARM64_REG_X1)) + if self.pc == 0xffff62dc: + uc.reg_write(UC_ARM64_REG_W21, uc.reg_read(UC_ARM64_REG_W2)) + + # Hooks for I2c ========== + if self.pc == 0xffff569c: + # Address for HDMI i2c is 0xa4 + # self.logger.debug(f"i2c reading from address: {hex(self.uc.reg_read(UC_ARM64_REG_W0))}") + self.get_device("GPIO").I2C_Address = self.uc.reg_read(UC_ARM64_REG_W0) + if self.pc == 0xffff56e8: + # self.logger.info(f"Data from i2c address={hex(self.get_device('GPIO').I2C_Address)} value={hex(self.uc.reg_read(UC_ARM64_REG_W0))}") + pass + #Dirty hook to inject I2C data, instead of implementing a full I2CDevice + if self.pc == 0xffff5760: + self.I2C_inject_buffer = b"boot@USB\x00" + self.i2c_offset = 0 + if len(self.I2C_inject_buffer) > 0 and self.pc == 0xffff56d8: + self.uc.reg_write(UC_ARM64_REG_W20, self.I2C_inject_buffer[self.i2c_offset]) + self.i2c_offset += 1 + + # Add hook for injecting i2c boot string: + if self.pc == 0xffff5790: + self.logger.info(f"I2C boot string: {self.uc.mem_read(self.sp + 0x10, 8).decode(errors='ignore')}") + # ==================== + if self.get_device("UART").has_rx(): + print(bytes(self.get_device("UART").get_rx()).decode(errors="ignore"), end="") + + if self.pc_in_debug(): + self.print_ctx() + # breakpoint() + + # USB is here + if self.pc == 0xffff9d18: + self.wait_for_usb = True + uc.emu_stop() + pass + + def hook_invalid_ins(self, handle, intno, user_data): + pass + + def read_string(self, at): + if at == 0: + return b'' + s = b'' + while 1: + b = self.uc.mem_read(at, 1) + at += 12 + if b == b'\0': + return s + s += b + return s + + def hook_exception(self, handle : Uc, intno, user_data): + if intno == 1: + handle.reg_write(UC_ARM64_REG_PC, handle.reg_read(UC_ARM64_REG_PC) + 4) + return True + else: + # Invalid instruction + pass + + def place_fastboot_command(self, input): + assert len(input) <= 0x200, "Too much data!" + + # Setup fake request in X1 + self.X1 = TEST_OFFSET + # Set request length to 0x200 + self.write_ptr(TEST_OFFSET + 8, 0x200) + + # Write input buffer + self.write_ptr(TEST_OFFSET, TEST_REQ_BUFFER) + self.write(TEST_REQ_BUFFER, input) + + # Setup context + self.write_ptr(self.X1 + 0x28 + 0x4, 64) + + def run_fastboot_cmd(self): + def _hook_usb_ep_que(uc : Uc, address, size, em : Amlogic_Emulator): + # em.uc.emu_stop() + return True + # self.uc.hook_add(UC_HOOK_CODE, _hook_usb_ep_que, self, 0xffff8998, 0xffff8998 + 1) + + def _hook_fastboot_tx_write(uc :Uc, address, size, em : Amlogic_Emulator): + if em.debug: + hexdump(em.read(em.X0, em.X1)) + # em.uc.emu_stop() + return True + self.uc.hook_add(UC_HOOK_CODE, _hook_fastboot_tx_write, self, self.device_offsets['fastboot_response'], self.device_offsets['fastboot_response'] + 1) + + self.pc = FASTBOOT_CMD_HANDLER + self.sp = STACK_ADDRESS + + # Run + if self.debug: + self.enable_trace = True + self.uc.emu_start(self.pc, 0) + return 0 + +if __name__ == "__main__": + device = "S905X3" + + emulator = Amlogic_Emulator() \ No newline at end of file diff --git a/source/bootrom_emulator/fuzzer.py b/source/bootrom_emulator/fuzzer.py index fe81ac8..e18228c 100644 --- a/source/bootrom_emulator/fuzzer.py +++ b/source/bootrom_emulator/fuzzer.py @@ -1,14 +1,4 @@ -from unicorn import * -from unicorn.arm64_const import * -from ghidra_assistant.utils.utils import * -from ghidra_assistant.utils.archs.arm64.arm64_emulator import ARM64UC_Emulator -from ghidra_assistant.utils.archs.arm64.asm_arm64 import ShellcodeCrafterARM64 - -# Fix paths -import sys, pathlib -from add_memory_maps import * -from tools import * -from amlogic_devices import * +from emulator import * import unicornafl import argparse @@ -25,349 +15,9 @@ debug_functions = [ # (0xffff9bc4, 0xffff9d6c), # Fastboot # (0xffff66d8, 0xffff6754), ] - -def get_local_file(name): - return pathlib.Path(pathlib.Path(__file__).parent.resolve() / name) - -class Amlogic_Emulator(ARM64UC_Emulator): - def __init__(self, file = "bin/BootROM_s905x3.bin", debug=False) -> None: - ''' - Amlogic bootrom emulator. - ''' - super().__init__(True) - - self.logger = setup_logger("GSCEmulator") - self.logger.setLevel(logging.DEBUG) - self.debug = debug - - self.bootrom = open(file, 'rb').read() - self.uc = Uc(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN) - self.sc = ShellcodeCrafterARM64(None, None) - self.setup_memory() - self.setup_registers() - self.setup_hooks() - self.setup_devices() - self.apply_patches() - self.debug = False - self.afl_input = [] - self.em = None # By default GA not used - self.wait_for_usb = False # USB not ready - - self.I2C_inject_buffer = b"" - - self.pc_trace = [] - self.enable_trace = False - - def write(self, address, data): - self.uc.mem_write(address, data) - - def read(self, address, size): - return bytes(self.uc.mem_read(address, size)) - - def apply_patches(self): - self.write(0xffff8c04, self.sc.ret_ins) - self.write(0xffff8998, self.sc.ret_ins) - - def setup_memory(self): - #Bootrom - self.uc.mem_map(ENTRY_POINT, len(self.bootrom), UC_PROT_EXEC | UC_PROT_READ) - self.write(ENTRY_POINT, self.bootrom) - - # Timer 0xff800000|0xff800fff|rti - self.uc.mem_map(0xff800000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # SRAM 0xfffa0000|0xfffe7fff | - self.uc.mem_map(0xfffa0000, 0x48000, UC_PROT_READ | UC_PROT_WRITE) - - # ISA 0xffd0f000|0xffd0ffff|isa - self.uc.mem_map(0xffd0f000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # eFuse 0xff630000|0xff631fff|efuse - self.uc.mem_map(0xff630000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - self.uc.mem_write(0xff630000, open(get_local_file("efuse_ff630000_ff632000.bin"), 'rb').read()) - - # UART 0xff803000|0xff803fff|uart and add hook - self.uc.mem_map(0xff803000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # ?? 0xffd08000|0xffd08fff|assist - self.uc.mem_map(0xffd08000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # 0xff634400|0xff6347ff|periphs_reg - self.uc.mem_map(0xff634400, 0x400, UC_PROT_READ | UC_PROT_WRITE) - - - # Reset? 0xffd01000|0xffd01fff|reset - self.uc.mem_map(0xffd01000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # RAM for USB? ff63c000 - self.uc.mem_map(0xff63c000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) - - # UNKNOWN - self.uc.mem_map(0xffe09000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - - # DMA 0xff63e000|0xff63ffff|dma - self.uc.mem_map(0xff63e000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - - # 0xff63a000|0xff63bfff|usbphy21 - self.uc.mem_map(0xff63a000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - - # USB1 0xff400000|0xff4fffff|usb1 - self.uc.mem_map(0xff400000, 0x100000, UC_PROT_READ | UC_PROT_WRITE) #1MB - - # UNKNOWN 0xffd14000|0xffd14fff|spifc - self.uc.mem_map(0xffd14000, 0x1000, UC_PROT_READ | UC_PROT_WRITE) #1MB - - # Flash 0xf6000000|0xf9ffffff|flash #TODO add flash device for fuzzing? - self.uc.mem_map(0xf6000000, 0xf9ffffff + 1 - 0xf6000000, UC_PROT_READ | UC_PROT_WRITE) - - # Emmc 0xffe07000|0xffe08fff|emmcC TODO add eMMC device for fuzzing? - self.uc.mem_map(0xffe07000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - # 0xffe05000|0xffe06fff|emmcB - self.uc.mem_map(0xffe05000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - # FFE03000 FFE04FFF emmcA - self.uc.mem_map(0xFFE03000, 0x2000, UC_PROT_READ | UC_PROT_WRITE) - - - - def write_ptr(self, at, ptr): - return self.uc.mem_write(at, p32(ptr)) - - def read_ptr(self, at): - return u32(self.uc.mem_read(at, 4)) - - def setup_registers(self): - self.pc = ENTRY_POINT - self.sp = STACK_ADDRESS - - def setup_hooks(self): - if self.debug: - self.uc.hook_add(UC_HOOK_CODE, self.hook_code) - self.uc.hook_add(UC_HOOK_INSN_INVALID, self.hook_invalid_ins) - self.uc.hook_add(UC_HOOK_INTR, self.hook_exception) - self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff3f0000, 0xff30cffe + 1) - - #Add hook usb device - self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff63a000, 0xff63bfff + 1) - self.uc.hook_add(UC_HOOK_MEM_READ|UC_HOOK_MEM_WRITE, self.hook_devices, None, 0xff400000, 0xff4fffff + 1) - - def setup_devices(self): - self.devices = [ - # USBDevice(self), - UARTDevice(self), - TimerDevice(self), - GPIODevice(self), - EfuseDevice(self), - USBPHYDevice(self), - USB1Device(self), - ] - - def hook_devices(self, handle, access, address, size, value, user_data): - if address == 0xff630044 and access == UC_MEM_WRITE: - pass - for device in self.devices: - if address >= device.BASE and address < device.BASE + device.SIZE: - if access == UC_MEM_READ: - device.read(address, size) - else: - device.write(address, size, value) - return True - return True - - # def print_ctx(self): - # if self.get_device("UART").has_rx(): - # print(bytes(self.get_device("UART").get_rx()).decode(errors="ignore")) - # print(f"PC: {hex(self.pc)} | X0: {hex(self.uc.reg_read(UC_ARM64_REG_X0))} | X1: {hex(self.uc.reg_read(UC_ARM64_REG_X1))} | X2: {hex(self.uc.reg_read(UC_ARM64_REG_X2))}") - # print(f"X3: {hex(self.uc.reg_read(UC_ARM64_REG_X3))} | X4: {hex(self.uc.reg_read(UC_ARM64_REG_X4))} | X5: {hex(self.uc.reg_read(UC_ARM64_REG_X5))} | X6: {hex(self.uc.reg_read(UC_ARM64_REG_X6))}") - # print(f"X7: {hex(self.uc.reg_read(UC_ARM64_REG_X7))} | X8: {hex(self.uc.reg_read(UC_ARM64_REG_X8))} | X9: {hex(self.uc.reg_read(UC_ARM64_REG_X9))} | X10: {hex(self.uc.reg_read(UC_ARM64_REG_X10))}") - - # # Sync ghidra if available - # if self.em: - # pass - # # self.em.ghidra.cursor = self.pc - - - def get_device(self, name): - for dev in self.devices: - if dev.NAME == name: - return dev - return None - - def run(self): - while True: - self.uc.emu_start(self.pc, self.pc + 4) - - def run_until_usb(self): - info("Run until USB") - try: - while True: - self.uc.emu_start(self.pc, self.pc + 4) - if self.wait_for_usb: - return - except Exception as e: - print(bytes(self.get_device("UART").get_rx()).decode(errors="ignore")) - self.print_ctx() - print(str(e)) - if e.errno == UC_ERR_READ_UNMAPPED or e.errno == UC_ERR_WRITE_UNMAPPED: - pass - # print(find_memory()) - exit(0) - - @property - def pc(self): - return self.uc.reg_read(UC_ARM64_REG_PC) - - @pc.setter - def pc(self, value): - self.uc.reg_write(UC_ARM64_REG_PC, value) - - @property - def sp(self): - return self.uc.reg_read(UC_ARM64_REG_SP) - - @sp.setter - def sp(self, value): - self.uc.reg_write(UC_ARM64_REG_SP, value) - - @property - def X0(self): - return self.uc.reg_read(UC_ARM64_REG_X0) - - @X0.setter - def X0(self, value): - self.uc.reg_write(UC_ARM64_REG_X0, value) - - @property - def X1(self): - return self.uc.reg_read(UC_ARM64_REG_X1) - - @X1.setter - def X1(self, value): - self.uc.reg_write(UC_ARM64_REG_X1, value) - - def pc_in_debug(self): - # return False - for start, end in debug_functions: - if self.pc >= start and self.pc <= end: - return True - return False - - def dump_stack(self, n_offset = 0, size = 0x200): - hexdump(bytes(self.uc.mem_read(self.sp - n_offset, size))) - - def add_breakpoint(self, at): - def _breakpoint(uc : Uc, address, size, em : Amlogic_Emulator): - em.print_ctx(print) - pass - self.uc.hook_add(UC_HOOK_CODE, _breakpoint, self, at, at + 1) - - def hook_code(self, uc : Uc, address, size, user_data): - ''' - Hook startup code to continue boot process - ''' - if self.enable_trace: - self.pc_trace.append(hex(self.pc)) - # Reset expects value in DRAM - if self.pc == 0xffff00b0: - self.write_ptr(0xff800228, uc.reg_read(UC_ARM64_REG_X1)) - if self.pc == 0xffff62dc: - uc.reg_write(UC_ARM64_REG_W21, uc.reg_read(UC_ARM64_REG_W2)) - - # Hooks for I2c ========== - if self.pc == 0xffff569c: - # Address for HDMI i2c is 0xa4 - # self.logger.debug(f"i2c reading from address: {hex(self.uc.reg_read(UC_ARM64_REG_W0))}") - self.get_device("GPIO").I2C_Address = self.uc.reg_read(UC_ARM64_REG_W0) - if self.pc == 0xffff56e8: - # self.logger.info(f"Data from i2c address={hex(self.get_device('GPIO').I2C_Address)} value={hex(self.uc.reg_read(UC_ARM64_REG_W0))}") - pass - #Dirty hook to inject I2C data, instead of implementing a full I2CDevice - if self.pc == 0xffff5760: - self.I2C_inject_buffer = b"boot@USB\x00" - self.i2c_offset = 0 - if len(self.I2C_inject_buffer) > 0 and self.pc == 0xffff56d8: - self.uc.reg_write(UC_ARM64_REG_W20, self.I2C_inject_buffer[self.i2c_offset]) - self.i2c_offset += 1 - - # Add hook for injecting i2c boot string: - if self.pc == 0xffff5790: - self.logger.info(f"I2C boot string: {self.uc.mem_read(self.sp + 0x10, 8).decode(errors='ignore')}") - # ==================== - if self.get_device("UART").has_rx(): - print(bytes(self.get_device("UART").get_rx()).decode(errors="ignore"), end="") - - if self.pc_in_debug(): - self.print_ctx() - # breakpoint() - - # USB is here - if self.pc == 0xffff9d18: - self.wait_for_usb = True - uc.emu_stop() - pass - - def hook_invalid_ins(self, handle, intno, user_data): - pass - - def read_string(self, at): - if at == 0: - return b'' - s = b'' - while 1: - b = self.uc.mem_read(at, 1) - at += 12 - if b == b'\0': - return s - s += b - return s - - def hook_exception(self, handle : Uc, intno, user_data): - if intno == 1: - handle.reg_write(UC_ARM64_REG_PC, handle.reg_read(UC_ARM64_REG_PC) + 4) - return True - else: - # Invalid instruction - pass - - def place_fastboot_command(self, input): - assert len(input) <= 0x200, "Too much data!" - - # Setup fake request in X1 - self.X1 = TEST_OFFSET - # Set request length to 0x200 - self.write_ptr(TEST_OFFSET + 8, 0x200) - - # Write input buffer - self.write_ptr(TEST_OFFSET, TEST_REQ_BUFFER) - self.write(TEST_REQ_BUFFER, input) - - # Setup context - self.write_ptr(self.X1 + 0x28 + 0x4, 64) - - def run_fastboot_cmd(self): - def _hook_usb_ep_que(uc : Uc, address, size, em : Amlogic_Emulator): - # em.uc.emu_stop() - return True - # self.uc.hook_add(UC_HOOK_CODE, _hook_usb_ep_que, self, 0xffff8998, 0xffff8998 + 1) - - def _hook_fastboot_tx_write(uc :Uc, address, size, em : Amlogic_Emulator): - if em.debug: - hexdump(em.read(em.X0, em.X1)) - # em.uc.emu_stop() - return True - self.uc.hook_add(UC_HOOK_CODE, _hook_fastboot_tx_write, self, 0xffff8c04, 0xffff8c04 + 1) - - self.pc = FASTBOOT_CMD_HANDLER - self.sp = STACK_ADDRESS - - # Run - if self.debug: - self.enable_trace = True - self.uc.emu_start(self.pc, 0) - # self.pc = FASTBOOT_CMD_HANDLER - return 0 - -def test_fb_cmd(cmd=b'getvar:version'): - emulator = Amlogic_Emulator() + +def test_fb_cmd(cmd=b'getvar:version', device="S905X3"): + emulator = Amlogic_Emulator(device=device) emulator.debug = True emulator.place_fastboot_command(cmd) res = emulator.run_fastboot_cmd() @@ -395,6 +45,8 @@ def afl_fuzzer(): if __name__ == "__main__": args = argparse.ArgumentParser("Amlogic BootROM Fuzzer") + # test_fb_cmd(device="S905X3") + test_fb_cmd(device="S922") # afl_fuzzer() # exit(0) args.add_argument("--input", "-i", help="Input file for crash", default=None)