On Sat, May 7, 2022, 3:01 AM Undiscussed Horrific Abuse, One Victim of Many <gmkarl@gmail.com> wrote:
I'm not near this system and phone at this time, but that doesn't mean I can't keep learning about it.

Here's the deployment script for the bootrom phase from the amonet kamakiri source:

#!/usr/bin/env python3

import sys
import time

from common import Device
from logger import log
from load_payload import load_payload
from functions import *

import usb.core
import usb.util

import ctypes

import traceback


import struct
import os

def main(dev):

    load_payload(dev)

This uses a hack to get the 0xf00dd00d payload running on the device, roughly by uploading the stages and jumping to their addresses. The first stage involves some register and usb twiddling, and is size-limited; it may be an exploit, unsure.

    if len(sys.argv) == 2 and sys.argv[1] == "fixgpt":
        dev.emmc_switch(0)
        log("Flashing GPT")
        flash_binary(dev, "../bin/gpt-mantis.bin", 0, 34 * 0x200)

This likely replaces the partition table if requested. I don't see the gpt-mantis.bin file in the repository yet.

That size of 0x34 x 0x200 ... I've seen that before I think?


    # 1) Sanity check GPT
    log("Check GPT")
    switch_user(dev)

This switches the device to partition 0 and verifies two expected bytes in that partition.


    # 1.1) Parse gpt
    gpt = parse_gpt(dev)

def parse_gpt(dev):
    data = dev.emmc_read(0x400 // 0x200) + dev.emmc_read(0x600 // 0x200) + dev.emmc_read(0x800 // 0x200) + dev.emmc_read(0xA00 // 0x200)
    num = len(data) // 0x80
    parts = dict()
    for x in range(num):
        part = data[x * 0x80:(x + 1) * 0x80]
        part_name = part[0x38:].decode("utf-16le").rstrip("\x00")
        part_start = struct.unpack("<Q", part[0x20:0x28])[0]
        part_end = struct.unpack("<Q", part[0x28:0x30])[0]
        parts[part_name] = (part_start, part_end - part_start + 1)
    return parts

I'm guessing that partition 0 is either the partition table or the entire flash, and that the above code manually parses a GPT partition table (into a python dict of offset size pairs).

    log("gpt_parsed = {}".format(gpt))
    if "lk" not in gpt or "tee1" not in gpt or "boot" not in gpt or "recovery" not in gpt:
        raise RuntimeError("bad gpt")

    # 2) Sanity check boot0
    log("Check boot0")
    switch_boot0(dev)

This switches to partition 1 and verifies that it starts with either "EMMC_BOOT" or nul bytes.


    # 3) Sanity check rpmb
    log("Check rpmb")
    rpmb = dev.rpmb_read()
    if rpmb[0:4] != b"AMZN":
        thread = UserInputThread(msg = "rpmb looks broken; if this is expected (i.e. you're retrying the exploit) press enter, otherwise terminate with Ctrl+C")
        thread.start()
        while not thread.done:
            dev.kick_watchdog()
            time.sleep(1)

I'm not sure what rpmb is, immediately.


    # Clear preloader so, we get into bootrom without shorting, should the script stall (we flash preloader as last step)
    # 4) Downgrade preloader
    log("Clear preloader header")
    switch_boot0(dev)
    flash_data(dev, b"EMMC_BOOT" + b"\x00" * ((0x200 * 4) - 9), 0)

Places null bytes in partition 1.


    # 5) Zero out rpmb to enable downgrade
    log("Downgrade rpmb")
    dev.rpmb_write(b"\x00" * 0x100)

Whatever the rpmb is, this zeros it.

    log("Recheck rpmb")
    rpmb = dev.rpmb_read()
    if rpmb != b"\x00" * 0x100:
        dev.reboot()
        raise RuntimeError("downgrade failure, giving up")
    log("rpmb downgrade ok")
    dev.kick_watchdog()

    # 6) Downgrade tz
    log("Flash tz")
    switch_user(dev)
    flash_binary(dev, "../bin/tz.img", gpt["tee1"][0], gpt["tee1"][1] * 0x200)

Okay, it sounds like partition 0 is not the partition table, but rather partition "tz" ....

I don't yet understand why it seems to read as if the GPT table was parsed 0x400 bytes after the start of the tz partition. Could the GPT table be located at the end or inside of a partition?

Ohhhh I see -- gpt["tee1"] stores the offset of the tee1 partition. The partitions are flashed relative to the start of the emmc.


    # 7) Downgrade lk
    log("Flash lk")
    switch_user(dev)
    flash_binary(dev, "../bin/lk.bin", gpt["lk"][0], gpt["lk"][1] * 0x200)

So this code replaces two partitions with binary images. Maybe user-provided old ones, unsure.

    # 6) Install lk-payload
    log("Flash lk-payload")
    switch_boot0(dev)
    flash_binary(dev, "../lk-payload/build/payload.bin", 1024)

And here's the next payload, contents not reviewed yet. It's interesting that this is flashed at offset 1024 rather than the offset of the actual lk partition. I think is bytes rather than 0x200 sectors, not sure.

...

    # 8) Flash microloader
    log("Inject microloader")
    switch_user(dev)
    boot_hdr1 = dev.emmc_read(gpt["boot"][0]) + dev.emmc_read(gpt["boot"][0] + 1)
    boot_hdr2 = dev.emmc_read(gpt["boot"][0] + 2) + dev.emmc_read(gpt["boot"][0] + 3)
    flash_binary(dev, "../bin/microloader.bin", gpt["boot"][0], 2 * 0x200)
    if boot_hdr2[0:8] != b"ANDROID!":
        flash_data(dev, boot_hdr1, gpt["boot"][0] + 2, 2 * 0x200)

    log("Force fastboot")
    force_fastboot(dev, gpt)

    # 9) Install preloader
    log("Flash preloader")
    switch_boot0(dev)
    flash_binary(dev, "../bin/preloader.img", 0)

    # 9.1) Wait some time so data is flushed to EMMC
    time.sleep(5)

    # Reboot (to fastboot or recovery)
    log("Reboot")
    dev.reboot()


if __name__ == "__main__":

    check_modemmanager()

    dev = Device()
    dev.find_device()

    main(dev)