Recently I have reverse engineered aboot (emmc_appsboot.mbn) from ROM riva_images_V8.5.7.0.NCKCNED_20171025.0000.00_7.1_cn ( en.miui.com/thread-1026306-1-1.html )(because this is my first post and I don't have permission to post outside link, you have to add http in those url), and discovered a way to bypass bootloader lock by using several bugs in Xiaomi customized aboot.
Xiaomi's aboot is based on Qualcomm's little kernel which is open sourced and can be download at source.codeaurora.org/quic/le/kernel/lk/ , so I will use function name inside those source file in discussion below even though some of those function have been modified by Xiaomi.
Relevant function to verify and boot linux is boot_linux_from_mmc, so I'll start from here:
boot_linux_from_mmc: call boot_verifier_init()
boot_verifier_init: set device state to GREEN boot_linux_from_mmc: call verify_signed_bootimg()
verify_signed_bootimg: call boot_verify_image()
boot_verify_image: call read_der_message_length() to get length of signature
boot_verify_image: if length of signature is too large, then boot_verify_image will return false to indicate verification failure
boot_verify_image: otherwise call and return verification result of verify_image_with_sig(inlined)
verify_image_with_sig: set device state to RED if image is not signed by Xiaomi. verify_signed_bootimg: call splash_screen_mmc() to show "The system has been destroyed" if verification failed
verify_signed_bootimg: shoudown device if splash_screen_mmc() succeed, otherwise continue boot boot_linux_from_mmc: call send_rot_command()
send_rot_command: check device state, if it's YELLOW or RED, than boot will failed because it try to read embedded cert which is not initialized by Xiaomi
To successfully bypass bootloader lock we need:
1. make sure device state is GREEN so that send_rot_command won't failed, this can be achieved by making read_der_message_length return a large value to avoid calling verify_image_with_sig.
one way to do this is to append[NOTE1] image with a large length encoded in der (eg. 0x30, 0x83, 0x19, 0x89, 0x64)
2. make sure splash_screen_mmc() failed so that booting process can be continued.
this can be achieved by change the magic number in the header of splash partition from "SPLASH!!" to any other value (eg. "19890604")
Steps to bypass:
0 note that all those steps can be done offline, so no information will send to Xiaomi or anyone
0 in this tutorial I'll demonstrate how to use twrp recovery with locked bootloader
1 using test point to enter EDL mode(will void your warranty!!!)
2 unzip MiFlash, you should see QSaharaServer.exe and fh_loader.exe
3 create a sub folder called "tmp"
4 extract prog_emmc_firehose_8917_ddr.mbn & rawprogram0.xml & splash.img from riva_images_V8.5.7.0.NCKCNED_20171025.0000.00_7.1_cn and put them into "tmp"
5 append a 4k block which begins with 0x30, 0x83, 0x19, 0x89, 0x64 to twrp-3.2.1-0-riva.img then put the resulting file to "tmp" and rename it to "recovery.img"
6 change the first 8 byte in splash.img to "19890604"
7 create "hack_splash.xml" inside "tmp", then copy&paste relevant section from rawprogram0.xml to "hack_splash.xml", the resulting file should look like this:
8 create "twrp.xml" inside "tmp", then copy&paste relevant recovery section from rawprogram0.xml to "twrp.xml", the resulting file should look like this:
9 run "QSaharaServer.exe -p \\.\COM10 -s 13rog_emmc_firehose_8917_ddr.mbn -b tmp" to initialize firehose. (replace COM10 with the COM port of you phone, the same as below)
10 run "fh_loader.exe --search_path=tmp --port=\\.\COM10 --sendxml=hack_splash.xml" to flash modified splash
11 run "fh_loader.exe --search_path=tmp --port=\\.\COM10 --sendxml=twrp.xml" to flash twrp recovery
12 done
If you want flash custom ROM, you just need to append[NOTE1] boot.img
NOTE1: append should work in most case, but not always. the corrected place to write 0x30, 0x83, 0x19, 0x89, 0x64 is calculate from image header, it's defined as:
and then calculate:
imagesize_actual is the place to write
NOTE2: There may be a easier way to enter EDL considering there are so many bug(eg. uninitialized stack variable, buffer overrun, missing bound check) in Xiaomi's modification, but I haven't bothered to check since my goal is achieved.
NOTE3: I suspect other model from Xiaomi may have similar bugs that bootloader lock can be bypassed using this method, but I don't have other phones to confirm my belief.
Xiaomi's aboot is based on Qualcomm's little kernel which is open sourced and can be download at source.codeaurora.org/quic/le/kernel/lk/ , so I will use function name inside those source file in discussion below even though some of those function have been modified by Xiaomi.
Relevant function to verify and boot linux is boot_linux_from_mmc, so I'll start from here:
boot_linux_from_mmc: call boot_verifier_init()
boot_verifier_init: set device state to GREEN
verify_signed_bootimg: call boot_verify_image()
boot_verify_image: call read_der_message_length() to get length of signature
boot_verify_image: if length of signature is too large, then boot_verify_image will return false to indicate verification failure
boot_verify_image: otherwise call and return verification result of verify_image_with_sig(inlined)
verify_image_with_sig: set device state to RED if image is not signed by Xiaomi.
verify_signed_bootimg: shoudown device if splash_screen_mmc() succeed, otherwise continue boot
send_rot_command: check device state, if it's YELLOW or RED, than boot will failed because it try to read embedded cert which is not initialized by Xiaomi
To successfully bypass bootloader lock we need:
1. make sure device state is GREEN so that send_rot_command won't failed, this can be achieved by making read_der_message_length return a large value to avoid calling verify_image_with_sig.
one way to do this is to append[NOTE1] image with a large length encoded in der (eg. 0x30, 0x83, 0x19, 0x89, 0x64)
2. make sure splash_screen_mmc() failed so that booting process can be continued.
this can be achieved by change the magic number in the header of splash partition from "SPLASH!!" to any other value (eg. "19890604")
Steps to bypass:
0 note that all those steps can be done offline, so no information will send to Xiaomi or anyone
0 in this tutorial I'll demonstrate how to use twrp recovery with locked bootloader
1 using test point to enter EDL mode(will void your warranty!!!)
2 unzip MiFlash, you should see QSaharaServer.exe and fh_loader.exe
3 create a sub folder called "tmp"
4 extract prog_emmc_firehose_8917_ddr.mbn & rawprogram0.xml & splash.img from riva_images_V8.5.7.0.NCKCNED_20171025.0000.00_7.1_cn and put them into "tmp"
5 append a 4k block which begins with 0x30, 0x83, 0x19, 0x89, 0x64 to twrp-3.2.1-0-riva.img then put the resulting file to "tmp" and rename it to "recovery.img"
6 change the first 8 byte in splash.img to "19890604"
7 create "hack_splash.xml" inside "tmp", then copy&paste relevant section from rawprogram0.xml to "hack_splash.xml", the resulting file should look like this:
Code:
<?xml version="1.0" ?>
<data>
<program SECTOR_SIZE_IN_BYTES="512" file_sector_offset="0" filename="splash.img" label="splash" num_partition_sectors="40960" physical_partition_number="0" size_in_KB="20480.0" sparse="false" start_byte_hex="0x14000000" start_sector="655360" />
</data>
Code:
<?xml version="1.0" ?>
<data>
<program SECTOR_SIZE_IN_BYTES="512" file_sector_offset="0" filename="recovery.img" label="recovery" num_partition_sectors="131072" physical_partition_number="0" size_in_KB="65536.0" sparse="false" start_byte_hex="0x1c200000" start_sector="921600" />
</data>
10 run "fh_loader.exe --search_path=tmp --port=\\.\COM10 --sendxml=hack_splash.xml" to flash modified splash
11 run "fh_loader.exe --search_path=tmp --port=\\.\COM10 --sendxml=twrp.xml" to flash twrp recovery
12 done
If you want flash custom ROM, you just need to append[NOTE1] boot.img
NOTE1: append should work in most case, but not always. the corrected place to write 0x30, 0x83, 0x19, 0x89, 0x64 is calculate from image header, it's defined as:
Code:
struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned dt_size; /* device_tree in bytes */
unsigned unused; /* future expansion: should be 0 */
....
};
Code:
if (hdr->page_size && (hdr->page_size != page_size)) {
page_size = hdr->page_size;
page_mask = page_size - 1;
}
kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, page_mask);
ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);
second_actual = ROUND_TO_PAGE(hdr->second_size, page_mask);
dt_size = hdr->dt_size;
dt_actual = ROUND_TO_PAGE(dt_size, page_mask);
imagesize_actual = (page_size + kernel_actual + ramdisk_actual + second_actual + dt_actual);
NOTE2: There may be a easier way to enter EDL considering there are so many bug(eg. uninitialized stack variable, buffer overrun, missing bound check) in Xiaomi's modification, but I haven't bothered to check since my goal is achieved.
NOTE3: I suspect other model from Xiaomi may have similar bugs that bootloader lock can be bypassed using this method, but I don't have other phones to confirm my belief.
Last edited: