Unbox Your Phone — Part II.
In fact, since both of those are available, for this part of the series I didn’t quite write a “storytelling” blog post. Instead, this post only does what the slide/video format does not accomplish: a more accessible enumeration of the most important results (code snippets, declarations of reversed functions/structures). So it’s more like a wiki then a blog post.
T-base Image Extraction
To extract all the T-base firmware parts that are embedded in the Sboot image (microkernel, Run Time Manager, tlLibrary, Crypto Driver, etc), find the extract table by the string marker “t-base”:
I have identified the following T-base → ATF invocable SMCs:
- 0x1: finished processing fastcall invoked from NWd, return to NWd via ATF
- 0xB2000002: send the VBAR value to ATF (so ATF knows where the SMC handler is)
- 0xB2000003: write character (for logging messages through ATF)
- 0xB2000004: send the T-base initialization status to ATF
Note: The talk calls RTM “S0CB”, based on the magic value of its fileformat. Using the hint from the Sboot firmware, the probably more accurate name I now use for it instead is RTM (“Run Time Manager”).
This is a uniquely powerful user-space process in T-base, always started first, and tasked with starting and managing all other processes (trustlets). So similar to init on Linux. It servers three functions:
- implements MCP,
- implements the notification of trustlets when requests arrive from NWd,
- implements the IPC mechanism of T-base.
The MCP commands were known from public sources. For IPC, the first 12 IPC command definitions can be found in source code here! I used the naming convention seen here for the rest that I was able to associate functionality with:
- Kernel and RTM both maintain their own mappings of process instances to type (Trustlet/Driver) as well as the virtual address space mappings of processes.
- Kernel checks caller type for SVCs that it restricts to drivers; RTM checks caller type for IPC calls that it restricts to drivers (not a central mechanism; has to be implemented correctly one-by-one for each case, similar to access_ok() checks in Linux).
- These two together restrict the ability to map other Trustlets’ virtual memory or any physical memory (including NWd) to Drivers. Drivers are still restricted from mapping RWX memory, or mapping anything over their own code pages.
- Drivers can use SVC to get from the kernel the UUID of the Trustlet that is making the current request; this can be used to filter what clients (Trustlets) are allowed to access what functionality of a Driver.
- Drivers can use SVC to map NWd or SWd physical memory into their address space and also use SVC to register custom fastcall handlers, giving them the ability to directly executing code from SWd EL1. In effect, this makes Drivers as privileged as SWd EL1.
Exploit Mitigations (Trustlets and Drivers)
- RX code pages and RW data pages
- No boundaries between stack/bss/heap
- WSMs mapped into Trustlets and Trustlet memory mapped into Drivers are in separate memory region from the above code+data regions
- No stack canaries, no ASLR