Compare commits

...

3 Commits

Author SHA1 Message Date
Jonathan Herrewijnen
0c84503e47 minor docs update 2024-09-26 19:01:12 +02:00
Jonathan Herrewijnen
6711ceea27 updates docs to explain final sboot boot 2024-09-25 18:44:44 +02:00
Jonathan Herrewijnen
0174b2a4f7 Boots patched BL33 2024-09-24 18:50:11 +02:00
7 changed files with 95 additions and 93 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 start end name order comment X0 LR
5 0x000064e0 0x0000658c _boot_usb
6 0x020c0000 0x020c0004 _frederic_dest_ptr
7 0x000002c0 0x000002c4 _jump_bl1
8 0x02022000 0x02021800 0x02023fff 0x02023800 BL1
9 0x02024000 0x02047fff BL31
10 0x02048000 0x0206ed10 BL2
11 0x02069000 0x0206f000 Debugger

View File

@ -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

View File

@ -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

Binary file not shown.