Exploiting samsung galaxy s4 secure boot

Launched in April 2013, the Samsung Galaxy S4 is expected to be one of the top-selling smartphones of the year, having sold 10 million units in its first month of sales. While the majority of released models include an unlocked bootloader, which allows users to flash custom kernels and make other modifications to the software on their own devices, AT&T and Verizon branded devices ship with a locked bootloader that prevents these types of modifications. In this post, I’ll provide details on how Samsung implement this locking mechanism, and publish a vulnerability in the implementation that allows bypassing the signature checks to run custom unsigned kernels and recovery images.

Both the AT&T (SGH-I337) and Verizon (SCH-I545) models utilize the Qualcomm APQ8064T chipset. As described in my previous blog post on Motorola’s bootloader, Qualcomm leverages software-programmable fuses known as QFuses to implement a trusted boot sequence. In summary, each stage of the boot process cryptographically verifies the integrity of the subsequent stage, with the trust root originating in QFuse values. After the early boot stages bootstrap various hardware components, Samsung’s APPSBL ("Application Secondary Bootloader") is loaded and run. This bootloader differs between "locked" and "unlocked" variants of the Galaxy S4 in its enforcement of signature checks on the boot*and*recovery*partitions.

A quick glance at aboot*(adopting the name of the partition on which this bootloader resides) revealed that it is nearly identical to the open source lk*("Little Kernel") project, which undoubtedly saved me many hours of tedious reverse engineering. By locating cross-references to strings found in both lk*and aboot, I was able to quickly identify the functions that implement signature verification and booting of the Linux kernel.

The central logic to load, verify, and boot the Linux kernel and ramdisk contained in either the boot*or recovery*partitions is implemented in the boot_linux_from_mmc()*function. First, the function determines whether it is booting the main boot*partition, containing the Linux kernel and ramdisk used by the Android OS, or the recovery*partition, which contains the kernel and ramdisk used by the Android recovery subsystem. Then, the first page of the appropriate partition is read into physical memory from the eMMC flash storage:

if (!boot_into_recovery) {index = partition_get_index("boot");ptn = partition_get_offset(index);if (ptn == 0) {dprintf(CRITICAL, "ERROR: No boot partition found\n");return -1;} } else {index = partition_get_index("recovery");ptn = partition_get_offset(index);if (ptn == 0) {dprintf(CRITICAL, "ERROR: No recovery partition found\n");return -1;} } if (mmc_read(ptn + offset, (unsigned int *) buf, page_size)) {dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");return -1; }

This code is straight out of lk’s implementation. Next, after performing some sanity-checking of the boot image, which contains a custom header format, the function loads the kernel and ramdisk into memory at the addresses requested in the boot image header:

hdr = (struct boot_img_hdr *)buf; image_addr = target_get_scratch_address(); kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, page_mask); ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask) + 0x200; imagesize_actual = (page_size + kernel_actual + ramdisk_actual);memcpy(image_addr, hdr, page_size);offset = page_size; /* Load kernel */ if (mmc_read(ptn + offset, (void *)hdr->kernel_addr, kernel_actual)) {dprintf(CRITICAL, "ERROR: Cannot read kernel image\n");return -1; } memcpy(image_addr + offset, hdr->kernel_addr, kernel_actual); offset += kernel_actual; /* Load ramdisk */ if (mmc_read(ptn + offset, (void *)hdr->ramdisk_addr, ramdisk_actual)) {dprintf(CRITICAL, "ERROR: Cannot read ramdisk image\n");return -1; } memcpy(image_addr + offset, hdr->ramdisk_addr, ramdisk_actual); offset += ramdisk_actual;

This is still essentially identical to lk’s implementation, with the addition of code to copy the individual pieces of the boot image to the image_addr*location. Finally, the function performs signature verification of the entire image. If signature verification succeeds, the kernel is booted; otherwise, a tampering warning is displayed and the device fails to boot:

if (check_sig(boot_into_recovery)) {if (!is_engineering_device()){dprintf("kernel secure check fail.\n");print_console("SECURE FAIL: KERNEL");while (1){/* Display tampered screen and halt */…}} }/* Boot the Linux kernel */ …

The is_engineering_device()*function simply returns the value of a global variable that is set at an earlier stage in the boot process based on whether or not the chipset ID (an unchangeable hardware value) of the device indicates it is an engineering or production device.

就算是一辆永久单车也能让你的梦想走很远。

Exploiting samsung galaxy s4 secure boot

相关文章:

你感兴趣的文章:

标签云: