When to use / when not to use
- Use: TrustZone/OP-TEE class TEE analysis — Trusted App reverse engineering, command dispatch table recovery, secure storage (secure storage/device key) objects and data flow
- Use: device key/root of trust direction — DRM hardware root of trust (L1 class in [[re-drm]]), device key extraction and protection mechanisms (authorized research)
- Use: TEE interface surface — SMC calls, host-side TEE driver ioctl, client library call sequence recovery
- Use: firmware security assessment — assess the interface exposure surface, storage protection strength, and root of trust boundary of the target device's TEE implementation
- Don't use: ordinary App layer (go through [[re-mobile]]); Windows kernel/driver (go through [[re-kernel]], the TEE side is the Linux/ARM domain)
- Don't use: pure firmware extraction and unpacking (first go through [[re-fw-extract]], this skill analyzes TEE components afterward)
- Don't use: the target is only ordinary encrypted data (go through the [[re-crypto-*]] series, no need to enter this skill when there is no TEE component)
- Don't use: only need to determine "whether the device uses TEE" (no image, no call surface to analyze) — signature-level evidence is sufficient, don't enter the deep process
- Don't use: EL2 hypervisor/virtualization domain (go through [[re-hypervisor]], TEE is the EL1/EL3 domain)
- Note:Dynamic debugging inside secure world is usually infeasible(see pitfall 3) — default to static analysis + host-side observation; host-side dynamic execution follows the highest principle of [[re-analyze/platform-tips]] and is performed inside a sandbox
Tool preparation
Static analysis (image parsing/decompilation) is exempt from sandboxing; host-side dynamic (running client, hooking ioctl) follows the highest principle of [[re-analyze/platform-tips]] and goes into a sandbox; dynamic inside secure world is not done (see pitfall 3), default to static + host-side observation.
Decompilation workbench ([[re-ghidra]] / [[re-ida]], ARM64)
- [[re-ghidra]] (default): import TEE OS image and TA (.ta needs its signature header cut off first, see step 2 and pitfall 5)
- [[re-ida]]: alternative; both require ARM64 support
- Verify: able to decompile ARM64 code, locate
smcinstruction call sites andTA_InvokeCommandEntryPointclass dispatch tables
Firmware extraction toolchain ([[re-fw-extract]])
- binwalk/unblob, magic scanning, endianness determination — locate and extract the TEE OS and TA images from bootrom/boot chain/device firmware
- Verify:
binwalk --version(for installation commands see [[re-fw-extract]] "Tool preparation")
python3 (binary parsing/scripting)
- Linux:
apt install python3/dnf install python3/pacman -S python; macOS: built-in; Windows: official installer orchoco install python - Verify:
python3 --version - Purpose: signature header/ELF payload segmentation parsing, structure field annotation, decryption and recovery scripts (connects to [[re-crypto-decrypt]])
Reference implementation (OP-TEE official open source, for understanding the structure)
- The official repositories optee_os / optee_client / optee_examples (and the qemu emulation environment) provide an authoritative reference for the TEE structure and call sequences — when analyzing differences between the target and OP-TEE, check against the official source code, do not rely on empirical values
- The qemu emulation environment can run the full client → TA call chain, used to cross-check the call sequence and parameter layout; for differences between emulation and real hardware (real drivers/interrupts/storage media), real hardware prevails
frida / host-side observation ([[re-frida]])
- Host-side dynamics: hook the client library call sequence and ioctl parameters (for frida installation and sandbox principles see [[re-frida]]; dynamic execution defaults to sandbox)
- Verification:
frida --version
Procedure
Execute in order; for each step's artifacts (image location, header structure annotations, command number table) record the evidence path + sha256 (see [[re-triage]]) for citation in the report.
-
Architecture positioning (world division / SMC / TEE OS entry):
- TrustZone divides the normal world (NS) and the secure world (S); in ARMv8-A the TEE OS usually runs at secure EL1, trusted applications at secure EL0, and the EL3 monitor layer hosts the secure monitor firmware (such as ATF BL31-class boot firmware, generalized)
- SMC instructionis the channel through which the normal world enters the secure world: for AArch64 it is
smc #imm(for AArch32 it issmc); executing it from EL1/EL2 traps into the EL3 monitor layer, which then dispatches to the TEE OS; in decompilation, searching for thesmcinstruction gives the call surface locations - SMC parameter convention (SMCCC): the function ID is in w0 (including the service/calling convention identification bits), parameters are in w1-w7 (SMC32) or x1-x17 (SMC64,only SMCCC >= 1.2 extends to x1-x17; 1.0/1.1 only x1-x7), and return values start from w0 — when annotating call sites, read registers according to the convention table, do not guess according to the ordinary ABI (see pitfall 4)
- Feature reconnaissance: version strings, exported API string tables, and SMC function ID constant tables should be located by grep first — string tables often directly give the TEE OS type and version, which is faster than decompiling one by one
- Locate the TEE OS load entry: bootrom → boot firmware → TEE OS image (image header magic, load/verify code) — search along the boot chain for TEE image features (image header magic, version strings, exported API string tables); the TEE OS may also be embedded in a device firmware partition (located after extraction with [[re-fw-extract]])
- Artifacts: world/EL division diagram + TEE OS image location and load entry
-
OP-TEE structure (core and TA separation, .ta format):
- core (trusted OS kernel, secure world side system services) and TA (trusted application, secure world user space) are separated; core provides TAs with the
TEE_*internal API (cryptography, secure storage, time, etc.), and TAs rely on this API to implement business logic — before analyzing a TA, first confirm the core/TA boundary (who provides the API, who calls the API) - .ta file format(REE filesystem TA): the file = signature header + ELF payload. Signature header (
struct shdr, official core/include/signed_hdr.h): magic (0x4f545348= "OTSH"), img_type, img_size, algo, hash_size, sig_size, followed by hash and signature; img_type values: 0 plaintext signed TA / 1 bootstrap TA (with UUID+version subheader) / 2 encrypted TA / 3 subkey chained header; the encrypted variant (img_type=2) is followed by theshdr_encrypted_tasubheader (enc_algo, flags, iv_size, tag_size + iv + tag), and the ciphertext is AES-GCM - Parsing flow: locate the signature header by the file header magic → cut out the ELF payload according to img_size → parse the section table/segments with [[re-format-elf]] → annotate
ta_head(UUID, version, flags, etc.; the UUID is the TA interface identifier, corresponding to the UUID used by the host-side caller) - Extensions are not a basis: some devices distribute the TEE OS/TA with .tos/.trusted-like extensions—always determine the format by magic, not by extension when selecting a parser
- Outputs: signed header field annotations + the carved-out ELF payload + ta_head/UUID records
- core (trusted OS kernel, secure world side system services) and TA (trusted application, secure world user space) are separated; core provides TAs with the
-
TA analysis (entry / command dispatch / secure storage):
- Entry chain:
TA_CreateEntryPoint(initialization) →TA_OpenSessionEntryPoint(session establishment) →TA_InvokeCommandEntryPoint(command dispatch, switch by cmd_id)——the command dispatch table is the TA's external interface catalog, annotate each branch with the command number and behavior - Parameter model: invoke carries parameter type descriptions and at most 4
TEE_Param(value scalar pairs / memref memory references); memref points to shared memory and is the main input/output channel—annotate each command branch's parameter types and structures - Session lifecycle: the pairing of OpenSession/CloseSession with command invocations is the reconstruction target (who establishes the session, at what timing, with what initial parameters)—session establishment parameters often contain initialization key material
- Command number enumeration: number the switch branches one by one (including invalid command/default branches); cross-check with the host-side call surface (step 4) to confirm which commands are actually reachable and which are for internal use—reachable commands are the main body of the interface surface/attack surface, annotate priorities
- secure storage object operations: locate
TEE_*storage API call sites (object create/open/read/write/seek/delete-like), reconstruct object IDs (often derived from UUID + object name/index) and data flow; secure storage ciphertext ultimately lands on disk in the normal world file system (via tee-supplicant) or hardware storage (RPMB-like, for the hardware side see [[re-hw-chip]])—finding the decryption path of the ciphertext file means finding the object data - On-disk form: the REE file system side is usually a combination of an index file + data block files (object directory mapping)—find objects by index, locate ciphertext by block, then reconstruct object contents
- Outputs: command number→behavior table + parameter structure annotations + secure storage object inventory
- Entry chain:
-
Host-side call surface (ioctl / client library, squeeze from both ends):
- Linux-side TEE subsystem:
/dev/tee0(used by the client libteec) on theTEE_IOC_*ioctl (open session, invoke, close session, shared memory allocation/registration-like);/dev/teepriv0belongs to tee-supplicant (privileged daemon) and is not used for client calls—do not treat teepriv0 as a "private session" channel (see [[gotchas]]) - User-space client library call sequence (GlobalPlatform TEE Client API):
TEEC_InitializeContext→TEEC_OpenSession(with UUID and parameters) →TEEC_InvokeCommand(with command number and TEEC_Operation) →TEEC_CloseSession→TEEC_FinalizeContext - Squeeze method: read the ioctl parameters on the host side (UUID, command number, parameter layout, shared memory contents) → match them in the TA-side dispatch table (step 3)—once both sides are annotated, the TA input/output format closes; when one side is missing, infer it from the other side (see pitfall 2)
- Dynamic observation (within the sandbox): frida hook client library calls and ioctl parameters ([[re-frida]]), record session opens, command call sequences and buffer contents
- Log side:
dmesgMedium tee driver and OP-TEE initialization logs (driver version, interrupt number, supplicant status) assist fingerprinting and version determination - Supplementary observation: tee-supplicant process behavior (disk writes/peripheral access) — the REE-side landing point of the secure storage data flow
- Deliverable: host-side call sequence + TA interface comparison table (UUID/command number/parameter layout in one-to-one correspondence)
- Linux-side TEE subsystem:
-
Vendor difference handling (custom TEE, generalized):
- Difference points of a custom TEE: SMC function number scheme, TA image format (header structure/signature/encryption method), secure storage layout, exported API naming and semantics — all may differ from OP-TEE
- Approach: first fingerprint the TEE type (image header characteristics, string/exported API characteristics, SMC function number patterns) → confirm whether it is an OP-TEE lineage or a custom implementation → apply the general process layer by layer (architecture localization → format parsing → interface enumeration → call surface narrowing), using OP-TEE details only as a reference template and not as default values
- Difference handling: where structures do not match, first suspect a "custom extension" (new fields/reordering/different magic numbers), and record the difference points rather than forcing an application; when format documentation is unavailable, infer the layout backward using "field length + common values (UUID/version/size)" ([[re-proto-rev]] approach)
- Backward inference and comparison: the correspondence between command numbers and behavior can be confirmed item by item using "return code + output buffer contents" — error code semantics often expose parameter validation order and privilege boundaries
- Deliverable: TEE type fingerprint + difference point checklist
Cross-domain federation
- [[re-fw-extract]]: prerequisite for TEE OS/TA image extraction and unpacking (bootrom/boot chain/firmware partitions)
- [[re-binary-core]]: image decompilation foundation ([[re-format-elf]] parsing TA ELF, [[re-ghidra]]/[[re-ida]] workbench)
- [[re-kernel]]: host-side TEE driver analysis (ioctl dispatch, driver loading, Linux kernel side)
- [[re-mobile]]: mobile TEE integration surface (in-app TEE calls, DRM integration, interfacing with the host-side call surface)
- [[re-hw-chip]]: secure storage hardware side (RPMB-type storage, root of trust, physical protection mechanisms)
- [[re-frida]]: host-side dynamic observation (client library/ioctl instrumentation, sandbox principle)
- [[re-drm]]: device key/hardware root of trust direction (DRM L1-type root of trust inside the TEE)
- [[re-crypto-decrypt]]: decryption and recovery of encrypted TA payloads/secure storage ciphertext
- [[re-sandbox]] / [[re-analyze/platform-tips]]: highest principle of host-side dynamic execution isolation
Common pitfalls and traps
- TA is a signed (+encrypted) image; direct analysis will fail: Symptom — getting a .ta and throwing it directly into a decompiler results in import failure or all garbled text; cause — .ta = signature header + ELF payload (the encrypted variant is still AES-GCM ciphertext), and the file header is not an ELF header; countermeasure — first locate and cut out the payload by the signature header magic (e.g. "OTSH") (step 2); for the encrypted variant, first restore the decryption flow ([[re-crypto-decrypt]]) to obtain the plaintext ELF before analysis; the signature verification/decryption flow itself is also an analysis target (root of trust/keys are on the core side)
- When secure world code is unavailable, infer backward from the host side: Symptom — TEE OS/TA firmware cannot be extracted (SoC-internal ROM/protected storage), and there is no way to start dynamic analysis of the secure world; cause — the TEE OS is often built in or protected and is not distributed with the user-area firmware; countermeasure — host-side ioctl (
TEE_IOC_*) Call surface + client library call sequence records UUID/command ID/parameter layout (step 4), combined with return behavior to infer the TA interface and internal logic; when static evidence is insufficient, prioritize call-surface evidence - secure world anti-dump/monitoring mechanisms: Symptoms - attaching a debugger has no effect, memory dump cannot be obtained or its contents are abnormal, runtime behavior does not match static analysis; Cause - the TEE side has anti-debugging and integrity protection (debug port blown, security monitoring, anti-dump mechanisms, generalized); Countermeasures - do not perform dynamic debugging inside the secure world; rely mainly on static analysis + host-side observation; record behavioral differences as "TEE-side monitoring triggered" evidence and mark confidence, do not force attribution
- Misreading caused by incorrect SMC parameter conventions: Symptoms - treating SMC call sites as ordinary function calls, guessing register parameters according to the ordinary ABI, making all inferred interface structures wrong; Cause - SMC follows the SMCCC convention (function ID in w0, parameters w1-w7 / x1-x17, where SMC64 uses x1-x17 since SMCCC 1.2; return values start from w0), which differs from the ordinary ABI, and misaligned interpretation makes the entire data flow chaotic; Countermeasures - first build a table according to the public calling convention (function ID encoding, parameter register layout, return convention), then annotate the
smccall sites in the decompilation, with parameter meanings based on the convention table rather than guessing (see [[gotchas]] calling convention group) - Parsing the signature header/ta_head as an ELF header: Symptoms - parsing .ta according to the ELF header fails (e_ident does not match), or the UUID in ta_head is treated as a string table; Cause - the .ta file header is a signature header (the ELF header is inside the payload), and ta_head is the first segment structure inside the ELF rather than the file header; Countermeasures - first cut out the ELF payload according to the signature header fields (magic/img_size), then follow [[re-format-elf]]; before importing into Ghidra/IDA, use a script to strip the signature header (archive the step 2 output)
- Applying OP-TEE details to a custom TEE: Symptoms - analyzing a custom TEE according to OP-TEE header structures/command model, with all fields misaligned and interfaces not matching; Cause - custom TEE formats and conventions differ (image header, SMC function IDs, storage layout vary); Countermeasures - first perform TEE type fingerprinting (step 5), and only after confirming the system choose an analysis template; handle differences by "inferring layout from field lengths + common values (UUID/version/size)" ([[re-proto-rev]] approach), with OP-TEE structures used only as reference
- Do not force a match when UUIDs do not match: Symptoms - the UUID called from the host side cannot be found in the TA dispatch table; Cause - multiple TA versions coexist in the firmware package, or the TA image version does not match the system (UUID is an interface identifier, and version evolution changes the UUID); Countermeasures - check versions against the TA manifest/directory in the firmware package, and match UUID and command ID for each candidate TA one by one
- If secure storage ciphertext cannot be found, treat it as having no data: Symptoms - secure storage ciphertext cannot be found on the REE file system side; Cause - the storage medium is on the RPMB/hardware side, or the on-disk path differs from the default (tee-supplicant configuration); Countermeasures - first confirm the storage medium and supplicant configuration (REE FS vs RPMB); for the RPMB side, switch to the [[re-hw-chip]] approach, and do not conclude there is no data based on "cannot find it"
- For decision branches (image form/interface surface/evidence grading) see [[decision-tree]]; for format and calling convention boundaries see [[gotchas]]