live

ICCC / tz_iccc trustlet — TZ write primitive (Track E)

Status (2026-10-23): The Samsung ICCC secure store (tz_iccc) is fully reverse-engineered and driveable from the REE on a locked-bootloader Z Fold 6. We have a live read channel, a documented wire format, a complete namespace/field map, and a proven TZ write primitive — including one field that accepts arbitrary values both directions and two fields that are monotonic one-way locks. Nothing is flashed; nothing persists across reboots (ABL re-writes these records each boot).

What ICCC is

ICCC = Samsung "secure component status" store. ABL writes per-component structs over SCM (tzbsp_oem_write_iccc_bl_info, ..._rot_bl_info, ..._svb_bl_info, ..._kern_info, ..._bldp_info) into a trustlet tz_iccc. The trustlet lives in the QSEE world (not TEEGRIS): only /dev/qseecom exists, ro.config.iccc_version=3.0, and get_chipset_id() == 0x33. The TA is ~2 MB and has no symtab — every readable name is a __func__ string literal attributed to functions by resolving adrp+add references.

How we reach it (bypassing the HAL)

The HIDL service /vendor/bin/hw/vendor.samsung.hardware.tlc.iccc@1.0-service (iccserv) is oneshot + disabled and is not running by default. We bypass it entirely and dlopen the implementation library directly:

/vendor/lib64/vendor.samsung.hardware.tlc.iccc@1.0-impl.so

Bootstrap symbols used by our client (iccc_cli, aarch64, NDK r29):

Object layout (verified by disassembly)

create_comm_ctx (libtlc_comm_iccc.so @0x1020) allocates 0x18 bytes and calls the open method at vtable +0x28 (returns bit 0 set on success). On failure it calls the destructor at vtable +0x08.

ctx        = { comm(+0x00), send_buf(+0x08), recv_buf(+0x10) }
comm       = *(void**)ctx
vtable[1]  = +0x08   destructor  -> QSEECom_shutdown_app
vtable[4]  = +0x20   comm_request(comm, cmd)

TZ_ICCC_read_TLC (@0x5924) and TZ_ICCC_write_TLC (@0x59e0) both do exactly x0 = [ctx]; x8 = [x0]; x8 = [x8+0x20]; blr x8 — confirmed.

Wire format

send[+0x10] = cmd       (2 = write/save, 3 = read)
send[+0x14] = param
send[+0x18] = value     (write only)
comm_request(comm, cmd)
recv[+0x00] = cmd | 0x80000000
recv[+0x18] = value     (read)
recv[+0x1C] = tz return code

Buffers are 0x201c bytes. TA path /vendor/firmware_mnt/image, TA name tz_iccc.

HAL impl gates (corrected)

Calling comm_request (vtable +0x20) directly bypasses the HAL whitelist, so 0xFF200001 becomes writable at the communication layer — but the trustlet still rejects it (see below).

TA internals

Parameter namespace validator (TA @0x760)

w8 = param & 0xFFF00000, returns 1 = allowed, 0 = reject. Allowed: 0xFF000000 0xFF100000 0xFF200000 0xFF300000 0xFF400000 0xFFF00000.

Per-component layout (from the TA jump tables)

Records live on 0x100-byte boundaries inside the TZ ICCC buffer.

namespace component fields (param -> offset/size)
0xFF000000 TA 0..6 -> 0x904..0x91C/4, 7 -> 0x920/0x20
0xFF100000 KERN 0xFF100000 -> 0xA04/4 ; 0xFF100001 -> 0xA08/0x20
0xFF200000 SYS 0xFF200000 -> 0xB04/4 ; 0xFF200001 -> 0xB08/4 ; 0xFF200002 -> 0xB0C/4
0xFF300000 ROT n=0..0xD -> 0xC04,0xC08,0xC0C,0xC10, 0xC14/0x20, ..., 0xC34,0xC38 ; 0xFF30FFFF -> whole struct 0xC00/0x3C
0xFF400000 BLDP 0xFF400000 -> 0xD50/0x100 ; 0xFF400001 -> 0xD50/0x20
0xFFF00000 (none) unknown type -> -1 (no handler)

Other TA facts

Live read map (one representative boot)

ROT: [0]=0  [1]=1  [2]=0x40000  [3]=0x1A9  [0xC]=0x29A9  [0xD]=0x29A9
SYS: [0]=FFFFFFFF  [1]=0  [2]=FFFFFFFF
KERN: [0]=0
TA:  [0..5]=FFFFFFFF  [6]=0
BLDP: size mismatch (0x100)        0xFFF00000: no response

Writability matrix (rawwrite bypasses the HAL whitelist)

tzrc = 0 means the trustlet accepted the write.

field param read value write result
KERN[0] 0xFF100000 0 FULLY WRITABLE — arbitrary values both directions (tested 0, FFFFFFFF, 0x12345678). tzrc=0.
SYS[0] 0xFF200000 FFFFFFFF MONOTONIC — FFFFFFFF->0 accepted (tzrc=0), 0->FFFFFFFF rejected (tzrc=0x1D).
SYS[2] 0xFF200002 FFFFFFFF MONOTONIC (same pattern as SYS[0]).
SYS[1] 0xFF200001 0 PROTECTED — always tzrc=0x1D (HAL also denies).
ROT[0..3] 0xFF300000..03 0,1,0x40000,0x1A9 PROTECTED — tzrc=0x1D.
KERN blob 0xFF100001 (0x20 blob) PROTECTED — tzrc=0x1D.
TA[0] 0xFF000000 FFFFFFFF PROTECTED — tzrc=0x2.
TA[1] 0xFF000001 FFFFFFFF PROTECTED — tzrc=0x2.
ROT[4..0xD], TA[2..6], BLDP — — untested

tzrc codes: 0 = accepted, 0x2 = rejected (TA), 0x1D (29) = rejected (protected), 0x1C (28) = invalid param (unknown field).

Key findings

  1. Real TZ write primitive exists: KERN[0] accepts arbitrary values both directions; SYS[0]/SYS[2] are monotonic one-way locks (FFFFFFFF->0 only).
  2. The sub-index is NOT an attacker-controlled offset — offsets are fixed per field in the TA jump tables. The out-of-bounds write hypothesis is NOT supported.
  3. Values are per-boot: ABL re-writes these records each boot (via SCM), so writes do NOT persist. Within a boot, monotonic locks stick (cannot be reversed).
  4. Protected fields are gated at the TZ level (tzrc 0x1D/0x2), not just the HAL.
  5. Channel dies after ~8-11 calls per boot (cumulative comm_request count). One process per boot, ~10 calls max. Clean shutdown (vtable[1] destructor) does not extend it.

Honest assessment

These writes will not unlock the bootloader: the bit/ARB check in download mode is fuse-based (SW REV), not ICCC-based, and ABL rewrites these records each boot. The lane's value is the reverse-engineering knowledge (transport, namespaces, wire format, TA internals) and a proven monotonic TZ write primitive (KERN[0] arbitrary; SYS[0]/SYS[2] one-way locks), which may affect kernel-related attestation/Knox state within a boot.

Tooling

Credits

Research by the F956B rooting project. Site: https://getyahrootback.admindashboard.dev XDA thread: (see FULL-ROOT-ACHIEVED post). KernelSU-Next: https://github.com/KernelSU-Next/KernelSU-Next GhostLock (CVE-2026-43499) primitive: original research by the project team.