Boots BL1 correctly by setting some things (twin)

This commit is contained in:
Jonathan Herrewijnen 2024-08-12 18:58:49 +02:00
parent 4bbb11908f
commit 7a80b9f5a9
5 changed files with 76 additions and 62 deletions

View File

@ -40,11 +40,15 @@ This results in the following files:
- Contains more textual information, and references to post BL2 boot, and android information - Contains more textual information, and references to post BL2 boot, and android information
- Kernel boot/BL33? - Kernel boot/BL33?
After loading the stage1 (entry.S - Frederic's exploit), we're allowed to send custom payloads to the device. The first payload that is then sent, is the debugger.
debugger debugger
======== ========
After the initial loading of the debugger, the state reported is: The initial debugger is written to ``0x2069000``, with debugger_stack and _storage at ``0x0206b000`` and ``0x0206d000`` respectively.
.. code:: bash After the initial loading of the debugger, the processor state reported is (using ghidra assistant):
.. code-block:: bash
root | DEBUG | root | DEBUG |
X0 : 0x0 | X1 : 0xffffffff | X2 : 0x20215d8 | X3 : 0x2021894 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 | X0 : 0x0 | X1 : 0xffffffff | X2 : 0x20215d8 | X3 : 0x2021894 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 |
@ -53,31 +57,9 @@ After the initial loading of the debugger, the state reported is:
X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 | X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20219b8 | SP/X31 : 0x2020ef0 X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20219b8 | SP/X31 : 0x2020ef0
With the original bl1 LR/X30 being the line register. This is the address the processor will jump to when the function is done (important to keep track off).
.. code:: bash After a cache flush, the debugger seems to be cleared as well, so the debugger is relocated to ``0x20c0000``, with _stack and _storage now at ``0x20c2000`` and ``0x20c4000`` respectively. This is done by running:
root | DEBUG |
X0 : 0x0 | X1 : 0x1 | X2 : 0x20215d8 | X3 : 0x2021894 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 |
X7 : 0x136c0008 | X8 : 0x2069000 | X9 : 0x0 | X10 : 0x2070000 | X11 : 0x0 | X12 : 0x0 | X13 : 0x0 |
X14 : 0xf | X15 : 0x20c4000 | X16 : 0x9 | X17 : 0x0 | X18 : 0x1 | X19 : 0x2000 | X20 : 0x2069000 |
X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20c0000 | SP/X31 : 0x2020ef0
With a modified bl1
.. code:: bash
root | DEBUG |
X0 : 0x0 | X1 : 0x1 | X2 : 0x20215d8 | X3 : 0x2021894 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 |
X7 : 0x136c0008 | X8 : 0x2069000 | X9 : 0x0 | X10 : 0x2070000 | X11 : 0x0 | X12 : 0x0 | X13 : 0x0 |
X14 : 0xf | X15 : 0x20c4000 | X16 : 0x9 | X17 : 0x0 | X18 : 0x1 | X19 : 0x2000 | X20 : 0x2069000 |
X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20c0000 | SP/X31 : 0x2020ef0
I relocated the debugger to ``0x20c0000`` to prevent overwriting it.
.. code-block:: python .. code-block:: python
@ -95,15 +77,20 @@ I relocated the debugger to ``0x20c0000`` to prevent overwriting it.
self.cd.relocate_debugger(0x020c7000, 0x020c0000, 0x020c4000) self.cd.relocate_debugger(0x020c7000, 0x020c0000, 0x020c4000)
relocate_debugger() relocate_debugger()
The processor state reported then is:
.. code-block:: bash
root | DEBUG |
X0 : 0x0 | X1 : 0x1 | X2 : 0x20215d8 | X3 : 0x2021894 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 |
X7 : 0x136c0008 | X8 : 0x2069000 | X9 : 0x0 | X10 : 0x2070000 | X11 : 0x0 | X12 : 0x0 | X13 : 0x0 |
X14 : 0xf | X15 : 0x20c4000 | X16 : 0x9 | X17 : 0x0 | X18 : 0x1 | X19 : 0x2000 | X20 : 0x2069000 |
X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20c0000 | SP/X31 : 0x2020ef0
bl1 bl1
=== ===
BL1 needs to be authenticated. BL1 needs to be authenticated. BL1 loads at address ``0x02024000`` and contains some form of header (ramdump). There seems to be a samsung header format, where the first 4 bytes define the entry point of the binary. In this case this entry is ``+0x10`` so we jump to ``0x02024010``. Authentication seems to be done at ``0x00012848``. Initially we thought that 0x0 indicated a verified boot state (as is plausible when reading the decompiled code in Ghidra). But after modifying BL1 in the header and contents, this value did not change.
Loads at address ``0x02024000`` and contains some form of header (ramdump).
There seems to be a samsung header format, where the first 4 bytes define the entry point of the binary.
In this case this entry is ``+0x10`` so we jump to ``0x02024010``.
.. code-block:: python .. code-block:: python
@ -123,7 +110,21 @@ In this case this entry is ``+0x10`` so we jump to ``0x02024010``.
auth_bl1(0x020c0000) auth_bl1(0x020c0000)
After authentication the bootROM jumps to it, we can execute this function with the debugger. .. figure:: images/bl1_auth_references.png
:align: center
BL1 authentication
At this point, we assumed that the authentication was succesful, and the bootROM would jump back to the debugger after loading, but this was not the case. After running this function, we were able to send a single packet, but never received a response. Indicating that the function we were executing never returned on us.
If authentication at auth_bl1 is succesful, the returns a value from a function at ``1230c``. This function does some things, but eventually jumps to a function at:
.. figure:: images/bl1_auth_follow-up_1230c.png
:align: center
BL1 authentication
After authentication the bootROM jumps to this function at, we can execute this function with the debugger.
.. code-block:: python .. code-block:: python
@ -137,10 +138,12 @@ After authentication the bootROM jumps to it, we can execute this function with
jump_fwbl1() jump_fwbl1()
BL1 is laoded at the download buffer and self copies to ``0x02024000`` and resumes execution there(``0x02024010``). BL1 is laoded at the download buffer and self copies to ``0x02024000`` and resumes execution there (``0x02024010``).
However, this does not result in a jump back to the debugger. But the ROM still receives data from the host However, this does not result in a jump back to the debugger. But the ROM still receives data from the host
! one payload allowed to be sent by the bootrom.
TODO TODO TODO TODO TODO TODO
The reason for this is the following code in bl1: The reason for this is the following code in bl1:

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

View File

@ -28,27 +28,6 @@ Frederic has also written a payload to extract the sboot.bin file from a connect
$ strings -n4 sboot.bin.1.bin $ strings -n4 sboot.bin.1.bin
was
.. list-table:: bootrom stages
:header-rows: 1
* - File
- Strings output
- Likely boot stage?
* - sboot.bin.1.bin
- Exynos BL1
- BL1
* - sboot.bin.2.bin
- BL31 %s
- BL31
* - sboot.bin.3.bin
- Unsure. Contains strings like: TOP_DIV_ACLK_MFC_600 and APOLLO_DIV_APOLLO_RUN_MONITOR
- BL2?
* - sboot.bin.4.bin
- Contains more textual information, and references to post BL2 boot, and android information
- Kernel boot/BL33?
Memory Layout Memory Layout
------------- -------------
TODO make memory layout of ROM, IMEM and some devices @JONHE TODO make memory layout of ROM, IMEM and some devices @JONHE

View File

@ -313,30 +313,36 @@ class ExynosDevice():
_initial_run_debugger() _initial_run_debugger()
_setup_debugger() _setup_debugger()
def dumb_interact(self): def dumb_interact(self, dump_imems=False):
''' '''
Room for playing around with the debugger Room for playing around with the debugger
''' '''
self.cd.arch_dbg.state.auto_sync = False self.cd.arch_dbg.state.auto_sync = False
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')
self.cd.arch_dbg.state.print_ctx() self.cd.arch_dbg.state.print_ctx()
def relocate_debugger(): def relocate_debugger():
# Seems to be cleared upon cache clearing?? # Seems to be cleared upon cache clearing??
if os.getenv("USER") == "eljakim": if os.getenv("USER") == "eljakim":
debugger_reloc = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read() debugger_reloc = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/reloc_debugger.bin", "rb").read()
else: else:
try: try:
debugger_reloc = open("../../dump/debugger.bin", "rb").read() debugger_reloc = open("../../dump/reloc_debugger.bin", "rb").read()
except Exception as e: except Exception as e:
print(f'Are you missing your debugger? Please ensure it is present in dump/debugger.bin. {e}') print(f'Are you missing your debugger? Please ensure it is present in dump/debugger.bin. {e}')
sys.exit(0) sys.exit(0)
self.cd.memwrite_region(0x020c0000, debugger_reloc) self.cd.memwrite_region(0x020c0000, debugger_reloc)
self.usb_write(b"FLSH") # Flush cache self.usb_write(b"FLSH") # Flush cache
self.cd.restore_stack_and_jump(0x020c0000) self.cd.restore_stack_and_jump(0x020c0000)
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(0x020c7000, 0x020c0000, 0x020c4000) self.cd.relocate_debugger(0x020c7000, 0x020c0000, 0x020c4000)
relocate_debugger() relocate_debugger()
logger.debug('State after relocating debugger')
self.cd.arch_dbg.state.print_ctx()
def memdump_imem(): def memdump_imem():
dumped = b"" dumped = b""
@ -349,8 +355,13 @@ class ExynosDevice():
# 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.usb_write(b"FLSH") # If wanting to modify the binary
# bl1 = bl1[:0x1C23] + b'\xaa' + bl1[0x1C24:]
self.cd.memwrite_region(0x02021800, bl1)
imem1 = memdump_imem()
AUTH_BL1 = 0x00012848 AUTH_BL1 = 0x00012848
def auth_bl1(lr=0x2069000): def auth_bl1(lr=0x2069000):
# Load the firmware # Load the firmware
@ -362,18 +373,39 @@ class ExynosDevice():
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!"
auth_bl1(0x020c0000) auth_bl1(0x020c0000)
# dump2 = memdump_imem()
# Dump memory
# imem2 = memdump_imem()
# with open("/tmp/imem1_bad.bin", "wb") as f:
# f.write(imem1)
# with open("/tmp/imem2_bad.bin", "wb") as f:
# f.write(imem2)
# Works until here TODO hijack future control flow # Works until here TODO hijack future control flow
# self.cd.memwrite_region(0x02020108, p32(0x020c0000)) # Hijack some weird function, original 0x00005790 # self.cd.memwrite_region(0x02020108, p32(0x020c0000)) # Hijack some weird function, original 0x00005790
self.cd.memwrite_region(0x02020f60, p32(0x020c0000)) self.cd.memwrite_region(0x020200e8, p32(0x020c0000)) # Overwrite line register to jump back to debugger, at function
self.cd.memwrite_region(0x020200dc, p32(0x020c0000))
def hijack_brom_weird():
print(f"LR = {hex(self.cd.arch_dbg.state.LR - 4)} X0 = {hex(self.cd.arch_dbg.state.X0)}")
self.cd.restore_stack_and_jump(0x00000314)
BOOT_BL1 = 0x00019310 BOOT_BL1 = 0x00019310
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(BOOT_BL1) self.cd.restore_stack_and_jump(BOOT_BL1)
jump_bl1(0x020c0000) jump_bl1(0x020c0000)
pass while True:
try:
resp = self.usb_read(0x200)
if self.cd.arch_dbg.state.LR == 0x02022948:
break # ROM will load next stage over USB
hijack_brom_weird()
except:
pass
# TODO load bl31
@ -432,8 +464,8 @@ class ExynosDevice():
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)
args = arg.parse_args()
args = arg.parse_args()
exynos = ExynosDevice() exynos = ExynosDevice()
if args.debug: if args.debug: