The Amlogic S905 System-On-Chip is an ARM processor designed for video applications. It's widely used in Android/Kodi media boxes. The SoC implements the TrustZone security extensions to run a Trusted Execution Environment (TEE) that enables DRM & other security features :





Amlogic S905 System Block Diagram





Technical documentation

#define ROMBOOT_START 0xD9040000 #define ROM_SIZE (64 * 1024) #define ROMBOOT_END (ROMBOOT_START + ROM_SIZE)

Root access over the UART

The SoC contains a Secure Boot mechanism to authenticate the TEE image before loading it in TrustZone. And the first link of this Secure Boot chain is the BootROM code, stored directly in the chip.This articles describes how to extract the BootROM code from this SoC in the Android-based Inphic Spot i7 device Amlogic released a public version of the S905 datasheet thanks to Hardkernel . However, it's heavily redacted, and most parts regarding the Secure Boot or the TrustZone have been removed. But we can still find a lot of technical information in GPL source code packages released by Amlogic For example, we can find a potential address for the BootROM code:

We start by connecting the serial port (or UART) because this interface could provide a quick & easy access to debug messages & serial console on bootloaders and Linux kernel.

Identifying the serial port on this board is quite simple since there is a port header with labels for the pinout:



UART on Inphic Spot i7 board

root@p200:/# ls -l /dev/block/platform/d0074000.emmc/ lrwxrwxrwx root root 2015 -01-01 00 :00 boot -> /dev/block/boot lrwxrwxrwx root root 2015 -01-01 00 :00 bootloader -> /dev/block/bootloader drwxr-xr-x root root 2015 -01-01 00 :00 by-num lrwxrwxrwx root root 2015 -01-01 00 :00 cache -> /dev/block/cache lrwxrwxrwx root root 2015 -01-01 00 :00 crypt -> /dev/block/crypt lrwxrwxrwx root root 2015 -01-01 00 :00 data -> /dev/block/data lrwxrwxrwx root root 2015 -01-01 00 :00 env -> /dev/block/env lrwxrwxrwx root root 2015 -01-01 00 :00 instaboot -> /dev/block/instaboot lrwxrwxrwx root root 2015 -01-01 00 :00 logo -> /dev/block/logo lrwxrwxrwx root root 2015 -01-01 00 :00 misc -> /dev/block/misc lrwxrwxrwx root root 2015 -01-01 00 :00 mmcblk0 -> /dev/block/mmcblk0 lrwxrwxrwx root root 2015 -01-01 00 :00 mmcblk0boot0 -> /dev/block/mmcblk0boot0 lrwxrwxrwx root root 2015 -01-01 00 :00 mmcblk0boot1 -> /dev/block/mmcblk0boot1 lrwxrwxrwx root root 2015 -01-01 00 :00 mmcblk0rpmb -> /dev/block/mmcblk0rpmb lrwxrwxrwx root root 2015 -01-01 00 :00 recovery -> /dev/block/recovery lrwxrwxrwx root root 2015 -01-01 00 :00 reserved -> /dev/block/reserved lrwxrwxrwx root root 2015 -01-01 00 :00 rsv -> /dev/block/rsv lrwxrwxrwx root root 2015 -01-01 00 :00 system -> /dev/block/system lrwxrwxrwx root root 2015 -01-01 00 :00 tee -> /dev/block/tee

While the tee partition (Trusted Execution Environment) turns out to be empty, the bootloader partition contains several bootloaders. But not the BootROM because it's stored in the SoC, not the flash.

(Fail at) Reading the BootROM

We connect an USB to UART adapter to this port. Once the Linux kernel boot process is finished, we have directly access to a root shell.We can start to explore the (Non-Secure side of the) system. For example, we can dump the partitions :

Since we have root permissions and a potential memory address for the BootROM, we can try to read it directly. The provided Android ROM contains a handy debugfs interface to peek & poke physical memory from user-land:



root@p200:/# echo "d0070000" >/sys/kernel/debug/aml_reg/paddr root@p200:/# cat /sys/kernel/debug/aml_reg/paddr [ 0xd0070000 ] = 0x1000254

This aml_reg driver uses the ioremap kernel function to set up an appropriate kernel page-table mapping for the requested address.





However, if we try to read the hypothetical BootROM area:

root@p200:/# echo "d9040000" >/sys/kernel/debug/aml_reg/paddr root@p200:/# cat /sys/kernel/debug/aml_reg/paddr [ 376 .546491@0 ] Unhandled fault: synchronous external abort ( 0x96000010 ) at 0xffffff80001aa000 [ 376 .549396@0 ] Internal error: : 96000010 [ #1] PREEMPT SMP [ 376 .554712@0 ] Modules linked in: dwc_otg dhd ( O ) aml_thermal ( O ) mali ( O ) aml_nftl_dev ( PO )

The kernel crashes. So either the BootROM address is wrong or this memory area is set as secure

Since we don't have other candidates for the BootROM address, let's say the BootROM area is not accessible from the Non-Secure World.

Enter the Secure World



A quick inspection of debug logs from the UART during the early phases of boot indicates that the bootloaders are based on the In theory, the Secure Boot chain prevents loading unauthorized code in the Secure World.A quick inspection of debug logs from the UART during the early phases of boot indicates that the bootloaders are based on the ARM Trusted Firmware (ATF) reference implementation.

ARM Trusted Firmware Design We will now explore some ways to get access to Secure World.

U-Boot bootloader Using the console over UART, we can interrupt the U-Boot boot sequence to access to the prompt. From here we can run arbitrary U-boot commands:

Hit any key to stop autoboot: 0

gxb_p200_v1#help

? - alias for 'help'

aml_sysrecovery- Burning with amlogic format package from partition sysrecovery

amlmmc - AMLMMC sub system

amlnf - aml nand sub-system

amlnf_test- AMLPHYNAND sub-system

autoping- do auto ping test

autoscr - run script from memory However the U-Boot bootloader (named BL33 in the ATF design) runs in Non-Secure mode as we can see in boot logs from the UART console: We will now explore some ways to get access to Secure World.Using the console over UART, we can interrupt the U-Boot boot sequence to access to the prompt. From here we can run arbitrary U-boot commands:However the U-Boot bootloader (named BL33 in the ATF design) runs in Non-Secure mode as we can see in boot logs from the UART console:

INFO: BL3-1: Preparing for EL3 exit to normal world

INFO: BL3-1: Next image address = 0x1000000

INFO: BL3-1: Next image spsr = 0x3c9



U-Boot 2015.01-ga9e9562-dirty (May 06 2016 - 03:36:02) So at this point we are already locked out of the Secure World. Next.

SMC interface Secure & Non-Secure Worlds can communicate through the ARM Secure Monitor Call (SMC). When a core executes the SMC instruction, it switches to Secure Monitor mode (exception level EL3). So at this point we are already locked out of the Secure World. Next.Secure & Non-Secure Worlds can communicate through the ARM Secure Monitor Call (SMC). When a core executes the SMC instruction, it switches to Secure Monitor mode (exception level EL3).

bootloader partition we've dumped previously. This code is highly critical for TrustZone security so we should explore it.



The open-source ATF code base in the BL31 image facilitates the analysis by reverse engineering, since we can quickly recover the ATF code structure.

Here is the list of registered services that handle SMC interrupts from Normal World:



Registered services in BL31 image

The sip_svc service is interesting because it contains several custom functions developed by Amlogic:



List of handlers in the SIP service At first glance, functions hdcp22_sec_read_reg & hdcp22_sec_write_reg look promising because they are read & write primitives for the secure memory. However, they strictly restrict access to specific memory ranges:

Function hdcp22_sec_read_reg decompiled A quick (incomplete) analysis of other functions didn't reveal any trivial flaw in parameters sanitization (arbitrary read/write bugs). Some of them are quite complex, especially the cryptographic functions, so they have not been inspected at all.



We may be able to trigger Secure World memory corruption from the Normal World if

Bypass the Secure Boot chain In the ATF design, the code that runs in EL3 is named the Boot Loader stage 3-1 (BL31). We can find this image in thepartition we've dumped previously. This code is highly critical for TrustZone security so we should explore it.The open-source ATF code base in the BL31 image facilitates the analysis by reverse engineering, since we can quickly recover the ATF code structure.Here is the list of registered services that handle SMC interrupts from Normal World:Theservice is interesting because it contains several custom functions developed by Amlogic:At first glance, functionslook promising because they are read & write primitives for the secure memory. However, they strictly restrict access to specific memory ranges:A quick (incomplete) analysis of other functions didn't reveal any trivial flaw in parameters sanitization (arbitrary read/write bugs). Some of them are quite complex, especially the cryptographic functions, so they have not been inspected at all.We may be able to trigger Secure World memory corruption from the Normal World if we find bugs in one of these functions, and then achieve privilege escalation to Secure World. However that would require some expert skills to actually exploit them . So let's explore another attack vector.

Another solution to get access to the Secure World is to break/bypass/fool/kindly ask the Secure Boot chain at one of its stage. A common attack surface of a Secure Boot chain is the loading, parsing and authentication steps of the next stage.





We don't have access to BL1 code (yet!) since it's stored in the SoC. But we have the BL2 image from the bootloader partition we have dumped previously. So we will analyze the mechanism used by BL2 to parse and authenticate the BL31 image in the hope of finding interesting flaws.



Here start the lengthy process of reverse engineering a binary without any syscall and very few strings to guide our efforts. Fortunately, the BL2 image is quite small: ~40KB. And we have some ideas to save time:



Like BL31, the BL2 image follows the ATF code logic, so reverse engineering efforts are a bit simplified: we can quickly spot main functions & structures defined in the ATF code base.





Another RE trick is to identify the memory-mapped devices accessed by functions to deduce their role.