Summary
This is the second part of a blog series covering my security research into Samsung’s TrustZone.
This post is a companion to my talk from Ekoparty 2017, namely the reverse engineering process of T-base. The slides and video of the talk are both available online.
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”:
ROM:00147000 tbase_extract_table DCB "t-base ",0 ; descriptor struct:
ROM:00147000 ; char name[8]
ROM:00147000 ; int offset
ROM:00147000 ; int size
ROM:00147000 ; char padding[0x10]
ROM:00147000 ;
ROM:00147000 ; real start offset: 0x132000
ROM:00147000 ;
ROM:00147000 ; Mtk: 0->0x147000 -> the Microkernel image itself -> go back 0x15000 from "t-base"
ROM:00147000 ; Image_h: 0x147000 -> 0x148000 -> so that's this
ROM:00147000 ; Rtm: 0x148000 -> RTM is the Run-Time Manager (aka S0CB)
ROM:00147000 ;
ROM:00147000 ; drcrypt: 0x167000 -> MCLF file (Crypto Driver)
ROM:00147000 ; tlproxy: 0x17A000 -> MCLF file (SFS Trustlet)
ROM:00147000 ; sth2: 0x17B000 -> MCLF file (SFS Driver)
ROM:00147000 ; mclib: 0x183000 -> tlLib (runtime library for Trustlets and Drivers)
ROM:00147008 DCD 0
ROM:0014700C DCD 0x5D000
ROM:00147010 ALIGN 0x20
ROM:00147020 aMtk DCB "mtk ",0
ROM:00147028 DCD 0
ROM:0014702C DCD 0x15000
ROM:00147030 ALIGN 0x20
ROM:00147040 aImage_h DCB "image_h",0
ROM:00147048 DCD 0x15000
ROM:0014704C DCD unk_1000
ROM:00147050 ALIGN 0x20
ROM:00147060 aRtm DCB "rtm ",0
ROM:00147068 DCD 0x16000
ROM:0014706C DCD 0x1F000
ROM:00147070 ALIGN 0x20
ROM:00147080 aDrcrypt DCB "drcrypt",0
ROM:00147088 DCD 0x35000
ROM:0014708C DCD 0x13000
ROM:00147090 ALIGN 0x20
ROM:001470A0 aTlproxy DCB "tlproxy",0
ROM:001470A8 DCD 0x48000
ROM:001470AC DCD 0x1000
ROM:001470B0 DCD 0
ROM:001470B4 DCD 0
ROM:001470B8 DCD 0
ROM:001470BC DCD 0
ROM:001470C0 aSth2 DCB "sth2 ",0
ROM:001470C8 DCD 0x49000
ROM:001470CC DCD unk_8000
ROM:001470D0 ALIGN 0x20
ROM:001470E0 aMclib DCB "mclib ",0
ROM:001470E8 DCD 0x51000
ROM:001470EC DCD unk_C000
ROM:001470F0 ALIGN 0x1000
ROM:00148000 S0CB_HDR DCB "S0CB",0 ; Rtm offset points here
ROM:00148005 DCB 0x10, 0, 0
ROM:00148008 DCD unk_C000
ROM:0014800C DCD dword_D000
ROM:00148010 DCD 0
ROM:00148014 DCD 0x11960
ROM:00148018 DCD 0xB295
ROM:0014801C DCD 0x3F
ROM:00148020 DCD 0x6A
ROM:00148024 aMccb DCB "MCCB",0
ROM:00148029 DCD 0
ROM:0014802D DCD 0
T-base SMCs
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
ROM:00133054 tbase_smc_send_VBAR ; CODE XREF: config_tbase_and_tell_aft_the_vbar+E↑p
ROM:00133054 LDR R0, =0xB2000002
ROM:00133058 MOV R1, #1
ROM:0013305C LDR R2, =0x7F00000 ; normal VBAR address
ROM:00133060 SMC #0
ROM:00133064 BX LR
ROM:00133064 ; End of function tbase_smc_send_VBAR
ROM:00133064
ROM:00133068
ROM:00133068 ; =============== S U B R O U T I N E =======================================
ROM:00133068
ROM:00133068 ; Attributes: noreturn
ROM:00133068
ROM:00133068 tbase_smc_fastcall_status_then_ret_to_nonSW
ROM:00133068 ; CODE XREF: sub_1343AA:loc_1354C8↓p
ROM:00133068 ; translate_MSMBase_to_VA+46↓p
ROM:00133068 LDR R0, =0xB2000004
ROM:0013306C MOV R1, #1
ROM:00133070 MOV R2, #1
ROM:00133074 SMC #0
ROM:00133078 LDR R0, =0x2000001
ROM:0013307C SMC #0
ROM:00133080
ROM:00133080 loc_133080 ; CODE XREF: tbase_smc_fastcall_status_then_ret_to_nonSW:loc_133080↓j
ROM:00133080 B loc_133080
ROM:00133080 ; End of function tbase_smc_fastcall_status_then_ret_to_nonSW
ROM:00133080
ROM:00133084
ROM:00133084 ; =============== S U B R O U T I N E =======================================
ROM:00133084
ROM:00133084
ROM:00133084 tbase_smc_fastcall_status ; CODE XREF: sub_1343AA+31C6↓p
ROM:00133084 LDR R0, =0xB2000004
ROM:00133088 MOV R1, #1
ROM:0013308C MOV R2, #3
ROM:00133090 SMC #0
ROM:00133094 BX LR
ROM:00133094 ; End of function tbase_smc_fastcall_status
ROM:00133094
ROM:00133098
ROM:00133098 ; =============== S U B R O U T I N E =======================================
ROM:00133098
ROM:00133098
ROM:00133098 tbase_smc_fastcall_print
ROM:00133098 MOV R2, R0
ROM:0013309C LDR R0, =0xB2000003
ROM:001330A0 MOV R1, #1
ROM:001330A4 SMC #0
ROM:001330A8 BX LR
ROM:001330A8 ; End of function tbase_smc_fastcall_print
T-base syscalls
.tbase_mem_data:07F0D86C ; ===========================================================================
.tbase_mem_data:07F0D86C
.tbase_mem_data:07F0D86C ; Segment type: Pure data
.tbase_mem_data:07F0D86C AREA .tbase_mem_data, DATA, ALIGN=0
.tbase_mem_data:07F0D86C ; ORG 0x7F0D86C
.tbase_mem_data:07F0D86C syscall_table DCD svc_0_nop+1 ; DATA XREF: invoke_syscall_from_table+40↑o
.tbase_mem_data:07F0D86C ; invoke_syscall_from_table:syscall_table_ptr↑o
.tbase_mem_data:07F0D870 DCD svc_1_init_process+1
.tbase_mem_data:07F0D874 DCD svc_2_nop+1
.tbase_mem_data:07F0D878 DCD svc_3_nop+1
.tbase_mem_data:07F0D87C DCD svc_4+1 ; Did not find this invoked anywhere in {RTM,tlLib}
.tbase_mem_data:07F0D880 DCD svc_5_start_process+1
.tbase_mem_data:07F0D884 DCD svc_exit+1
.tbase_mem_data:07F0D888 DCD svc_mmap+1
.tbase_mem_data:07F0D88C DCD svc_8_munmap+1
.tbase_mem_data:07F0D890 DCD svc_9_start_thread+1
.tbase_mem_data:07F0D894 DCD svc_A_stop_thread+1
.tbase_mem_data:07F0D898 DCD svc_B_return_0xD+1
.tbase_mem_data:07F0D89C DCD svc_C_modify_thread_registers+1
.tbase_mem_data:07F0D8A0 DCD svc_D_mprotect+1
.tbase_mem_data:07F0D8A4 DCD svc_E_resume_thread+1
.tbase_mem_data:07F0D8A8 DCD svc_F+1
.tbase_mem_data:07F0D8AC DCD svc_10_set_thread_prio+1
.tbase_mem_data:07F0D8B0 DCD svc_11_ipc+1
.tbase_mem_data:07F0D8B4 DCD svc_12_int_attach+1
.tbase_mem_data:07F0D8B8 DCD svc_13_int_detach+1
.tbase_mem_data:07F0D8BC DCD svc_14_sigwait+1
.tbase_mem_data:07F0D8C0 DCD svc_15_signal+1
.tbase_mem_data:07F0D8C4 DCD svc_16+1 ; Did not find this invoked anywhere in {RTM,tlLib}
.tbase_mem_data:07F0D8C8 DCD svc_tbase_smc_fastcall_input+1
.tbase_mem_data:07F0D8CC DCD svc_18_log_char+1
.tbase_mem_data:07F0D8D0 DCD svc_19_get_secure_timestamp+1
.tbase_mem_data:07F0D8D4 DCD svc_1A_control+1 ; includes a lot, such as:
.tbase_mem_data:07F0D8D4 ; - driver shmem map/unmap
.tbase_mem_data:07F0D8D4 ; - get/set exception info
.tbase_mem_data:07F0D8D4 ; - get MCP queue info
.tbase_mem_data:07F0D8D4 ; - get IPCH phys address values
.tbase_mem_data:07F0D8D4 ; - cache control
.tbase_mem_data:07F0D8D4 ; - virt2phys, phys2virt translation
.tbase_mem_data:07F0D8D4 ; - set custom fastcall, call custom fastcall
.tbase_mem_data:07F0D8D4 ;
.tbase_mem_data:07F0D8D4 ; Known sub-handlers:
.tbase_mem_data:07F0D8D4 ;
.tbase_mem_data:07F0D8D4 ; -0x8F: getting/setting fastcall configuration values
.tbase_mem_data:07F0D8D4 ; - 0xC: get S0CB PA
.tbase_mem_data:07F0D8D4 ; - 0xA: notify (nSW - trigger interrupt)
.tbase_mem_data:07F0D8D4 ; - 0xB: notify driver (drTriggerIntr)
.tbase_mem_data:07F0D8D4 ; - 0xD: get fc_init perm flags
.tbase_mem_data:07F0D8D4 ; - 0x1: set exception info
.tbase_mem_data:07F0D8D4 ; - 0x2: get fault info
.tbase_mem_data:07F0D8D4 ; - 0x4,0x5,0x6,0x7: get MCP queue info
.tbase_mem_data:07F0D8D4 ; (mci_buffer_addr, nq_length, mcp_queue_addr, mcp_queue_len)
.tbase_mem_data:07F0D8D4 ; - 0x9: map mcp cmd queue (in kernel)
.tbase_mem_data:07F0D8D4 ; -0x90 -> more control
.tbase_mem_data:07F0D8D4 ; - 5: read info for exception
.tbase_mem_data:07F0D8D4 ; - 7: translate VA to PA
.tbase_mem_data:07F0D8D4 ; -0x91 virt-to-phys and also virt-to-phys64
.tbase_mem_data:07F0D8D4 ; -0x92 -> I-cache clean/invalidate, D-cache clean/invalidate
.tbase_mem_data:07F0D8D4 ; -0x81:map, 0x83: unmap, 0x85:map.
.tbase_mem_data:07F0D8D4 ; - Difference in 81/85: map into TA/driver or map into RTM
.tbase_mem_data:07F0D8D4 ; -0x94 -> set custom fastcall handler
.tbase_mem_data:07F0D8D4 ; -0x95 -> SMC fastcall 5 (unk)
.tbase_mem_data:07F0D8D4 ; -0x96 -> getPhysMemType
.tbase_mem_data:07F0D8D4 ; -0x98 -> exec f
RTM IPC
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:
/** Possible message types/event types of the system. */
typedef enum {
MSG_NULL = 0, // Used for initializing state machines
/***************/
MSG_RQ = 1, /**< Request; client -> server; */
MSG_RS = 2, /**< Response; server -> client */
MSG_RD = 3, /**< Ready; server -> IPCH */
MSG_NOT = 4, /**< Notification; client -> IPCH; */
MSG_CLOSE_TRUSTLET = 5, /**< Close Trustlet; MSH -> IPCH; IPCH -> all servers */
MSG_CLOSE_TRUSTLET_ACK = 6, /**< Close Trustlet Ack; servers -> IPCH */
MSG_MAP = 7, /**< Map; driver <-> IPCH; */
MSG_ERR_NOT = 8, /**< Error Notification; EXCH/SIQH -> IPCH; */
MSG_CLOSE_DRIVER = 9, /**< Close driver; MSH -> IPCH; IPCH -> driver */
MSG_CLOSE_DRIVER_ACK = 10, /**< Close driver Ack; driver -> IPCH; IPCH -> MSH */
MSG_GET_DRIVER_VERSION = 11, /**< GetDriverVersion; client <--> IPCH */
MSG_GET_DRAPI_VERSION = 12, /**< GetDrApiVersion; driver <--> IPCH */
MSG_SET_NOTIFICATION_HANDLER = 13, // Driver <-> IPCH
MSG_DRV_NOT = 15, // Driver -> Trustlet
MSG_SET_FASTCALL_HANDLER = 16, // Driver <-> IPCH
MSG_GET_CLIENT_ROOT_AND_SP_ID = 17, // Driver <-> IPCH
MSG_CALL_FASTCALL = 25, // DRIVER -> IPCH -> Kernel
MSG_GET_CLI_UUID = 26, // Driver <-> IPCH
MSG_RQ_2 = 27, // Client -> Server
MSG_MAP_VA_DIRECT = 31, // DRIVER <-> IPCH
MSG_MAP_PY_DIRECT = 32, // DRIVER <-> IPCH
MSG_UNMAP_PY_DIRECT = 33, // DRIVER <-> IPCH
MSG_GET_MEM_TYPE = 34, // DRIVER <-> IPCH
MSG_UNMAP_VA_DIRECT = 35 // DRIVER <-> IPCH
} message_t;
tlApis
ROM:07D0BE38 tlApi_syscall_table DCD tlApiNOP+1 ; 0
ROM:07D0BE38 DCD tlApiGetVersion+1 ; 1
ROM:07D0BE38 DCD tlApiGetMobicoreVersion+1; 2
ROM:07D0BE38 DCD tlApiGetPlatformInfo+1; 3
ROM:07D0BE38 DCD tlApiExit+1 ; 4
ROM:07D0BE38 DCD tlApiLogvPrintf+1 ; 5
ROM:07D0BE38 DCD tlApiWaitNotification+1; 6
ROM:07D0BE38 DCD tlApiNotify+1 ; 7
ROM:07D0BE38 DCD tlApi_callDriver+1 ; 8
ROM:07D0BE38 DCD tlApiWrapObjectExt+1; 9
ROM:07D0BE38 DCD tlApiUnwrapObjectExt+1; 10
ROM:07D0BE38 DCD tlApiGetSuid+1 ; 11
ROM:07D0BE38 DCD tlApiSecSPICmd+1 ; 12
ROM:07D0BE38 DCD tlApiCrAbort+1 ; 13
ROM:07D0BE38 DCD tlApiRandomGenerateData+1; 14
ROM:07D0BE38 DCD tlApiGenerateKeyPair+1; 15
ROM:07D0BE38 DCD tlApiCipherInitWithData+1; 16
ROM:07D0BE38 DCD tlApiCipherUpdate+1 ; 17
ROM:07D0BE38 DCD tlApiCipherDoFinal+1; 18
ROM:07D0BE38 DCD tlApiSignatureInitWithData+1; 19
ROM:07D0BE38 DCD tlApiSignatureUpdate+1; 20
ROM:07D0BE38 DCD tlApiSignatureSign+1; 21
ROM:07D0BE38 DCD tlApiSignatureVerify+1; 22
ROM:07D0BE38 DCD tpiApiMessageDigestInitWithData+1; 23
ROM:07D0BE38 DCD tlApiMessageDigestUpdate+1; 24
ROM:07D0BE38 DCD tlApiMessageDigestDoFinal+1; 25
ROM:07D0BE38 DCD tlApiGetVirtMemType+1; 26
ROM:07D0BE38 DCD tlApiDeriveKey+1 ; 27
ROM:07D0BE38 DCD tlApiMalloc+1 ; 28
ROM:07D0BE38 DCD tlApiRealloc+1 ; 29
ROM:07D0BE38 DCD tlApiFree+1 ; 30
(...)
ROM:07D0BE38 DCD tlApiGetIDs+1 ; 43
(...)
ROM:07D0BE38 DCD tlApiRandomGenerateData_wrap+1; 83
ROM:07D0BE38 DCD tlApiCrash+1 ; 84
ROM:07D0BE38 DCD tlApiEndorse+1 ; 85
ROM:07D0BE38 DCD tlApiTuiGetScreenInfo+1; 86
ROM:07D0BE38 DCD tlApiTuiOpenSession+1; 87
ROM:07D0BE38 DCD tlApiTuiCloseSession+1; 88
ROM:07D0BE38 DCD tlApiTuiSetImage+1 ; 89
ROM:07D0BE38 DCD tlApiTuiGetTouchEvent+1; 90
ROM:07D0BE38 DCD tlApiTuiGetTouchEventsLoop+1; 91
ROM:07D0BE38 DCD tlApiDrmProcessContent+1; 92
ROM:07D0BE38 DCD tlApiDrmOpenSession+1; 93
ROM:07D0BE38 DCD tlApiDrmCloseSession+1; 94
ROM:07D0BE38 DCD tlApiDrmCheckLink+1 ; 95
ROM:07D0BE38 DCD tlApiDeriveKey_wrapper+1; 96
ROM:07D0BE38 DCD tlApiUnwrapObjectExt_wrapper+1; 97
ROM:07D0BE38 DCD tlApiGetSecureTimestamp+1; 98
drApis
ROM:07D0C114 ; int drApi_syscall_table[62]
ROM:07D0C114 drApi_syscall_table DCD drApiGetVersion+1 ; 0
ROM:07D0C114 ; DATA XREF: get_syscall_fn+30↑o
ROM:07D0C114 ; ROM:off_7D0A9F8↑o
ROM:07D0C114 DCD drApiExit+1 ; 1
ROM:07D0C114 DCD drApiMapPhys+1 ; 2
ROM:07D0C114 DCD drApiUnmap+1 ; 3
ROM:07D0C114 DCD drApiMapPhysPage4KBWithHardware+1; 4
ROM:07D0C114 DCD drApiMapClient+1 ; 5
ROM:07D0C114 DCD drApiMapClientAndParams+1; 6
ROM:07D0C114 DCD drApiAddrTranslateAndCheck+1; 7
ROM:07D0C114 DCD drApiGetTaskid+1 ; 8
ROM:07D0C114 DCD drApiTaskidGetThreadid+1; 9
ROM:07D0C114 DCD drApiGetLocalThreadId+1; 10
ROM:07D0C114 DCD drApiStartThread+1 ; 11
ROM:07D0C114 DCD drApiStopThread+1 ; 12
ROM:07D0C114 DCD drApiResumeThread+1 ; 13
ROM:07D0C114 DCD drApiThreadSleep+1 ; 14
ROM:07D0C114 DCD drApiSetThreadPriority+1; 15
ROM:07D0C114 DCD drApiIntrAttach+1 ; 16
ROM:07D0C114 DCD drApiIntrDetach+1 ; 17
ROM:07D0C114 DCD drApiWaitForIntr+1 ; 18
ROM:07D0C114 DCD drApiTriggerIntr+1 ; 19
ROM:07D0C114 DCD drApiIpcWaitForMessage+1; 20
ROM:07D0C114 DCD drApiIpcCallToIPCH+1; 21
ROM:07D0C114 DCD drApiIpcSignal+1 ; 22
ROM:07D0C114 DCD drApiIpcSigWait+1 ; 23
ROM:07D0C114 DCD drApiNotify+1 ; 24
ROM:07D0C114 DCD drApiSystemCtrl+1 ; 25
ROM:07D0C114 DCD sub_7D0A2CE+1 ; 26
ROM:07D0C114 DCD drApiVirt2Phys+1 ; 27
ROM:07D0C114 DCD drApiCacheDataClean+1; 28
ROM:07D0C114 DCD drApiCacheDataCleanAndInvalidate+1; 29
ROM:07D0C114 DCD drApiNotifyClient+1 ; 30
ROM:07D0C114 DCD drApiThreadExRegs+1 ; 31
ROM:07D0C114 DCD drApiInstallFc+1 ; 32
ROM:07D0C114 DCD drApiIpcUnknownMessage+1; 33
ROM:07D0C114 DCD drApiIpcUnknownException+1; 34
ROM:07D0C114 DCD drApiGetPhysMemType+1; 35
ROM:07D0C114 DCD drApiGetClientRootAndSpId+1; 36
ROM:07D0C114 DCD drApiCacheDataCleanRange+1; 37
ROM:07D0C114 DCD drApiCacheDataCleanAndInvalidateRange+1; 38
ROM:07D0C114 DCD drApiMapPhys64+1 ; 39
ROM:07D0C114 DCD drApiMapPhys64_2+1 ; 40
ROM:07D0C114 DCD drApiVirt2Phys64+1 ; 41
ROM:07D0C114 DCD drApiGetPhysMemType64+1; 42
ROM:07D0C114 DCD drApiUpdateNotificationThread+1; 43
ROM:07D0C114 DCD drApiRestartThread+1; 44
ROM:07D0C114 DCD drApiGetSecureTimestamp+1; 45
ROM:07D0C114 DCD drApiFastCall+1 ; 46
ROM:07D0C114 DCD drApiGetClientUuid+1; 47
ROM:07D0C114 DCD sub_7D0A2AC+1 ; 48
ROM:07D0C114 DCD drApiMapVirtBuf+1 ; 49
ROM:07D0C114 DCD drApiUnmapPhys2+1 ; 50
ROM:07D0C114 DCD drApiMapPhys2+1 ; 51
ROM:07D0C114 DCD drApiUnmapVirtBuf2+1; 52
ROM:07D0C114 DCD sub_7D07D76+1 ; 53
ROM:07D0C114 DCD sub_7D0A558+1 ; 54
ROM:07D0C114 DCD sub_7D0A574+1 ; 55
ROM:07D0C114 DCD sub_7D0A5D6+1 ; 56
ROM:07D0C114 DCD sub_7D0A738+1 ; 57
ROM:07D0C114 DCD sub_7D0A1BA+1 ; 58
ROM:07D0C114 DCD sub_7D0A1C8+1 ; 59
ROM:07D0C114 DCD sub_7D0A5F4+1 ; 60
ROM:07D0C114 DCD sub_7D0A60C+1 ; 61
Sandboxing Mechanisms
- 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