Compare commits
3 Commits
1dc24198b6
...
0c84503e47
Author | SHA1 | Date | |
---|---|---|---|
|
0c84503e47 | ||
|
6711ceea27 | ||
|
0174b2a4f7 |
@ -331,4 +331,30 @@ The last stage before the kernel boots.
|
||||
.. figure:: images/bl31_debugger_memory_example.png
|
||||
:align: center
|
||||
|
||||
Boot chain with EL3 and EL1 areas
|
||||
Boot chain with EL3 and EL1 areas
|
||||
|
||||
To keep access to the debugger, but allow modifications, we need to again load the BL33, but not boot it. We need to set some registers to accomplish this. We continue our bootflow to allow the device to load BL33, and then after it is done loading, let it jump back to the debugger:
|
||||
|
||||
- Store BL33 address in X0: ``self.cd.arch_dbg.state.X0 = BL33_ptr``
|
||||
- Store BL33 link register in LR: ``self.cd.arch_dbg.state.LR = BL33_LR``
|
||||
- Set LR to debugger: ``self.cd.arch_dbg.state.LR = DEBUGGER_ADDR``
|
||||
- Jump into the function that loads BL33: ``self.cd.restore_stack_and_jump(hijacked_fun)``
|
||||
|
||||
Now we can again load our next boot stage (BL33), send it, and verify a return to the debugger.
|
||||
|
||||
At this point, we tried patching something in memory at BL33 - like we did at BL31. But this always failed. After consultation, it seemed most likely that something was checking the integrity of the next boot stages. When we modified and reverted our changes, the boot was proper. The link register to continue our boot flow was at ``0x2024eec``. We did not succeed to manually do a verification, and continue the boot flow. This function at 0x2024eec does also not return, but instead continues onwards.
|
||||
|
||||
.. figure:: images/_integrity_check_BL2-BL33.png
|
||||
:align: center
|
||||
|
||||
Possible integrity check of boot stages at BL2 and BL33.
|
||||
|
||||
The decompilation is a bit broken, but we noticed that there are multiple calls to the same function, not just at the location where BL33 was returning from. With most specific things related to BL33 already done before this function. A similar verification seemed to have been done at ``0x02024e5c``. At this address, the same function was executed as at ``0x02024eec``, so instead of jumpingo into the function at ``0x02024eec``, we jumped into the function at ``0x02024e5c``. This worked, and allowed us to patch BL33, while continuing our boot flow. I assume, that we're doing an integrity check over BL2, while booting BL33.
|
||||
|
||||
If removing the ROM/UFS Short, the phone will go into odin mode. And is visible in lsusb after booting:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
Bus 001 Device 043: ID 04e8:685d Samsung Electronics Co., Ltd GT-I9100 Phone [Galaxy S II] (Download mode)
|
||||
|
||||
|
||||
|
@ -213,3 +213,25 @@ There's an odd space at 0x14kk. With things like deadcafe:
|
||||
|
||||
1c0000000000000000000000fecaadde00000000fecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddefecaaddef
|
||||
|
||||
Week 37 - 2024
|
||||
--------------
|
||||
A few days ago, I noticed that the memory is no longer being cleared between boots. After some discussion, it seems most likely, that this is because I'm booting the device further, eventually into recovery mode. At some point, it doesn't clear the memory anymore (or it's not being cleared). This is interesting, as it allows me to see what was loaded into memory, before I start booting the device.
|
||||
|
||||
So, I've written something to dump the location of thte TTBR0_EL3 table, before booting any of BL1, but after loading the initial debugger. The read bytes are fluctuating, strongly, but there seems to be a pattern. I'm hoping that after enough boots, I'll have enough data to reconstruct the table.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pandas as pd
|
||||
blub = self.cd.memdump_region(0x02035000, 0x1000)
|
||||
try:
|
||||
df = pd.read_pickle('ttbr0_el3.pkl')
|
||||
# Concat data to existing dataframe
|
||||
df = pd.concat([df, pd.Series([blub])], ignore_index=True)
|
||||
except:
|
||||
df = pd.DataFrame()
|
||||
df['TTBR0_EL3'] = [blub]
|
||||
df.to_pickle('ttbr0_el3.pkl')
|
||||
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 685 KiB |
@ -5,7 +5,7 @@ start,end,name,order,comment,X0,LR
|
||||
0x000064e0,0x0000658c,_boot_usb,,,,
|
||||
0x020c0000,0x020c0004,_frederic_dest_ptr,,,,
|
||||
0x000002c0,0x000002c4,_jump_bl1,,,,
|
||||
0x02022000,0x02023fff,BL1,,,,
|
||||
0x02021800,0x02023800,BL1,,,,
|
||||
0x02024000,0x02047fff,BL31,,,,
|
||||
0x02048000,0x0206ed10,BL2,,,,
|
||||
0x02069000,0x0206f000,Debugger,,,,
|
||||
|
|
@ -1,63 +0,0 @@
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
root | INFO | Connection is working
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
|
||||
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
root | INFO | Connection is working
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
|
||||
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
root | INFO | Connection is working
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc42508b2f1462
|
||||
Bits: 0000000010111100010000100101000010001011001011110001010001100010
|
||||
|
||||
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
root | INFO | Connection is working
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1462
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100010
|
||||
|
||||
|
||||
TTBR0_EL3: 0xbc46508b2f1460
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100000
|
||||
root | INFO | Connection is working
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1460
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100000
|
||||
Connected device! 0x4e8 0x1234
|
||||
[+] Sent stage
|
||||
TTBR0_EL3: 0xbc46508b2f1460
|
||||
Bits: 0000000010111100010001100101000010001011001011110001010001100000
|
@ -599,7 +599,7 @@ class ExynosDevice():
|
||||
assert ttbr0 == b"\x00" * 0x8, "TTBR0_EL3 not overwritten"
|
||||
|
||||
|
||||
def test_write_execute(self, address):
|
||||
def test_write_execute(self, address, execute=True):
|
||||
"""
|
||||
At given address, test if it is possible to write and execute code, by writing a simple jump to, and jump back.
|
||||
"""
|
||||
@ -613,10 +613,11 @@ class ExynosDevice():
|
||||
|
||||
shellcode = ks.asm(shellcode, as_bytes=True)[0]
|
||||
self.cd.memwrite_region(address, shellcode)
|
||||
self.cd.jump_to(address)
|
||||
self.usb_write(b"PING")
|
||||
assert self.usb_read(0x200) == b"PONG", "Failed to jump back to debugger"
|
||||
print(f'Jumped to {hex(address)} and back')
|
||||
if execute:
|
||||
self.cd.jump_to(address)
|
||||
self.usb_write(b"PING")
|
||||
assert self.usb_read(0x200) == b"PONG", "Failed to jump back to debugger"
|
||||
print(f'Jumped to {hex(address)} and back')
|
||||
|
||||
|
||||
def debugger_boot(self):
|
||||
@ -631,6 +632,18 @@ class ExynosDevice():
|
||||
self.cd.arch_dbg.state.print_ctx()
|
||||
DEBUGGER_ADDR = 0x2069000 # 0x2069000
|
||||
|
||||
# Dump contents of TTBR0_EL3 from memory into a pandas dataframe and write it to a pickle file
|
||||
import pandas as pd
|
||||
blub = self.cd.memdump_region(0x02035000, 0x1000)
|
||||
try:
|
||||
df = pd.read_pickle('ttbr0_el3.pkl')
|
||||
# Concat data to existing dataframe
|
||||
df = pd.concat([df, pd.Series([blub])], ignore_index=True)
|
||||
except:
|
||||
df = pd.DataFrame()
|
||||
df['TTBR0_EL3'] = [blub]
|
||||
df.to_pickle('ttbr0_el3.pkl')
|
||||
|
||||
# Relocate debugger
|
||||
debugger = open("../../dump/reloc_debugger_0x11200000.bin", "rb").read()
|
||||
self.relocate_debugger(debugger=debugger, entry=0x11200000, storage=0x11203000, g_data_received=0x11204000)
|
||||
@ -654,15 +667,16 @@ class ExynosDevice():
|
||||
self.connect_device()
|
||||
|
||||
# Send boot stage 1
|
||||
self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.1.bin", "rb").read())
|
||||
bl1 = open("../S7/g930f_latest/g930f_sboot.bin.1.bin", "rb").read()
|
||||
self.send_normal_stage(bl1)
|
||||
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
|
||||
|
||||
# Setup jump and bl_auth
|
||||
AUTH_BL1 = 0x00012848 # Location of the authentication function
|
||||
def auth_bl1(lr=0x2069000):
|
||||
def auth_bl1(lr=0x2069000, x0=1, x1=1):
|
||||
# Load the firmware
|
||||
self.cd.arch_dbg.state.X0 = 1
|
||||
self.cd.arch_dbg.state.X1 = 1
|
||||
self.cd.arch_dbg.state.X0 = x0
|
||||
self.cd.arch_dbg.state.X1 = x1
|
||||
self.cd.arch_dbg.state.LR = lr #jump back to debugger when finished
|
||||
self.cd.restore_stack_and_jump(AUTH_BL1)
|
||||
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
|
||||
@ -678,6 +692,9 @@ class ExynosDevice():
|
||||
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"))
|
||||
|
||||
# Write a patch to BL1 in memory
|
||||
# self.cd.memwrite_region(0x2021800+bl1.find(b'2015'), b'2014')
|
||||
|
||||
# Jump into BL1 (sboot.bin.1.bin)
|
||||
JUMP_BL1 = 0x000002c0
|
||||
def jump_bl1(lr):
|
||||
@ -719,7 +736,10 @@ class ExynosDevice():
|
||||
TTBR0_EL3 = 0x02035600 # Zeroed out
|
||||
|
||||
# Modifies/disables setting up MMU (but is set up eventually) -> MMU says 0x0 instead of 0x1, but still little access (and proper USB recovyer boot!?)
|
||||
self.cd.memwrite_region(0x020244e8, struct.pack('>I', 0x1f0c00f1)) # Change check to always false
|
||||
self.cd.memwrite_region(0x020244e8, struct.pack('>I', 0x1f0c00f1)) # Change check to always be false
|
||||
|
||||
# DWC3 OTG update mode -> Might be useful at some point?
|
||||
# self.cd.memwrite_region(0x02021580, struct.pack('>I', 0x00000000))
|
||||
|
||||
# Jump into BL31 and execute it
|
||||
self.cd.restore_stack_and_jump(0x02024010)
|
||||
@ -743,20 +763,11 @@ class ExynosDevice():
|
||||
VBAR_EL3 = self.cd.arch_dbg.state.VBAR_EL3
|
||||
|
||||
self.test_write_execute(0x11207010)
|
||||
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
|
||||
self.cd.restore_stack_and_jump(hijacked_fun)
|
||||
time.sleep(2)
|
||||
|
||||
# ==== Stage 4 BL2 ====
|
||||
bl2 = open("../S7/g930f_latest/g930f_sboot.bin.3.bin", "rb").read()
|
||||
|
||||
# Patching
|
||||
# bl2 = len(bl2)
|
||||
# patch_len = len(b"MNGS_QUAD")
|
||||
# patch = b"Patch" + (b"\x00" * (patch_len - len(b"Patch")))
|
||||
# patch_offset = bl2.find(b"MNGS_QUAD")
|
||||
# bl2 = bl2[:patch_offset] + patch + bl2[patch_len + patch_offset:]
|
||||
# assert len(stage4) == stage4_len, "Invalid bl2 length"
|
||||
|
||||
self.send_normal_stage(bl2)
|
||||
time.sleep(2)
|
||||
self.connect_device()
|
||||
@ -773,7 +784,7 @@ class ExynosDevice():
|
||||
|
||||
# Restore bootflow
|
||||
print(self.cd.arch_dbg.state.print_ctx())
|
||||
BL33_jump = self.cd.arch_dbg.state.X0
|
||||
BL33_ptr = self.cd.arch_dbg.state.X0
|
||||
BL33_LR = self.cd.arch_dbg.state.LR
|
||||
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
|
||||
|
||||
@ -788,13 +799,19 @@ class ExynosDevice():
|
||||
self.connect_device()
|
||||
self.usb_read(0x200) # GiAs
|
||||
|
||||
# Modify something in BL33
|
||||
# print(self.cd.memdump_region(0x8f063710, 0x8))
|
||||
# self.cd.memwrite_region(0x8f063710, struct.pack('>I', 0x53616d74))
|
||||
# self.cd.memdump_region(0x8f063710, 0x8)
|
||||
|
||||
self.cd.arch_dbg.state.X0 = BL33_jump
|
||||
self.cd.restore_stack_and_jump(BL33_LR)
|
||||
# # Modify something in BL33
|
||||
print(self.cd.arch_dbg.state.print_ctx())
|
||||
print(self.cd.memdump_region(0x8f063710, 0x8))
|
||||
self.cd.memwrite_region(0x8f063710, struct.pack('>I', 0x53614d74))
|
||||
|
||||
# Modify USB Recovyer mode string to: NFI Patched BL33
|
||||
self.cd.memwrite_region(0x8f06ab10, b'\x4e\x46\x49\x20\x50\x61\x74\x63\x68\x69\x6e\x67\x20\x42\x4c\x33\x33')
|
||||
print(self.cd.memdump_region(0x8f063710, 0x8))
|
||||
|
||||
# Print state of x30/LR on screen
|
||||
self.cd.memwrite_region(0x8f01dc08, struct.pack('>I', 0x7b432c91))
|
||||
|
||||
self.cd.restore_stack_and_jump(0x02024e5c)
|
||||
|
||||
pass
|
||||
|
||||
|
BIN
source/exploit/ttbr0_el3.pkl
Normal file
BIN
source/exploit/ttbr0_el3.pkl
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user