About

Our goal at DFF is to reveal any threats on mobile devices, and that requires us to keep up to date with every single version of Android and iOS, including the beta and "Developer Preview" phases. Often, these are the under-the-hood, undocumented changes which have the real impact on operating system security.

iOS 17 indeed introduced such changes. Two notable ones were SPTM and TXM, two binaries included in the beta IPSW. Our previous article took a first look at them, but left much unanswered, and promised "more soon". We've been keeping busy, but haven't forgotten our readers' first for detail. As with its predecessor, this article provides a reproducible step-by-step flow which the interested reader is encouraged to follow with the disassembler of choice. And, as before, the impatient reader can just skip to the end.

.

iOS has moved a bit since our last post. We were hoping to wait for the mid-year release (to see if CL4 is put in), but Apple is moving slowly. So this analysis is on the SPTM and TXM binaries from 17.1.1 (21B91) - specifically, the iPhone 15 Pro Max. For SPTM, that's LC_SOURCE_VERSION 184.42.1.0.0. For TXM it's 80.40.3.0.0. This picks up where we left off, and corrects an inaccuracy (in the previous article's "SUBSYSTEMS" sections the numbers didn't align up, since the numbers registered appear to be for IOMMU, not actual dispatchers - more on this later here).

Second glance

Let's remember where we left off:

And pose some questions to answer here:

SPTM, revisited

Let's begin by re-examining the SPTM binary (this time, from the t8122, iPhone 15 Pro).

About that SVC #0...

Remember SVC #0? We saw the check for it in SPTM, but didn't really look where it was called. Well, looking at TXM's __TEXT_BOOT_EXEC.__bootcode we find:

fffffff017054084  d503237f      PACIBSP ; 
fffffff017054088  d4000001      SVC     #0      ; 
fffffff01705408c  d65f0fff      RETAB   ; 

Labeling this as _sptm_syscall, we can then disassemble the rest of TXM (that is, from __TEXT_EXEC, wherein we see the following:

_sptm_syscall_0x100000003:
fffffff01704a5e8  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a5ec  f2c00030      MOVK    X16, #1, LSL #32        ; X16 := 0x100000000
fffffff01704a5f0  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0x100000000
fffffff01704a5f4  f2800070      MOVK    X16, #3 ; X16 := 0x100000003             // retype?
fffffff01704a5f8  140026a3      B       0xfffffff017054084      ; _sptm_syscall
_sptm_syscall_0x100000002:
fffffff01704a5fc  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a600  f2c00030      MOVK    X16, #1, LSL #32        ; X16 := 0x100000000
fffffff01704a604  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0x100000000
fffffff01704a608  f2800050      MOVK    X16, #2 ; X16 := 0x100000002 // register_dispatch_table?
fffffff01704a60c  1400269e      B       0xfffffff017054084      ; _sptm_syscall
_sptm_syscall_0x100000004:
fffffff01704a610  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a614  f2c00030      MOVK    X16, #1, LSL #32        ; X16 := 0x100000000
fffffff01704a618  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0x100000000
fffffff01704a61c  f2800090      MOVK    X16, #4 ; X16 := 0x100000004
fffffff01704a620  14002699      B       0xfffffff017054084      ; _sptm_syscall
_sptm_syscall_0xfe00000000:
fffffff01704a624  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a628  f2c01fd0      MOVK    X16, #254, LSL #32      ; X16 := 0xfe00000000
fffffff01704a62c  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0xfe00000000
fffffff01704a630  f2800010      MOVK    X16, #0 ; X16 := 0xfe00000000
fffffff01704a634  14002694      B       0xfffffff017054084      ; _sptm_syscall
_sptm_syscall_0xfd00000000:
fffffff01704a638  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a63c  f2c01fb0      MOVK    X16, #253, LSL #32      ; X16 := 0xfd00000000
fffffff01704a640  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0xfd00000000
fffffff01704a644  f2800010      MOVK    X16, #0 ; X16 := 0xfd00000000
fffffff01704a648  1400268f      B       0xfffffff017054084      ; _sptm_syscall
_sptm_syscall_0xff00000000:
fffffff01704a64c  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704a650  f2c01ff0      MOVK    X16, #255, LSL #32      ; X16 := 0xff00000000
fffffff01704a654  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0xff00000000
fffffff01704a658  f2800010      MOVK    X16, #0 ; X16 := 0xff00000000
fffffff01704a65c  1400268a      B       0xfffffff017054084      ; _sptm_syscall

The first three calls have X16 as a value of 0x100000002, 3 and 4 (1 is missing). We can expect this pattern to be found in other clients of SPTM.

Indeed, we find the following pattern in XNU. Using disarm's gadget finding (and focusing on X16) uncovers these sequences:

DFFenders@xxxx (~) % disarm -g MOVK,MOVK,MOVK,MOVK,B /tmp/extracted/kernel.rebuilt
## '0' subsystem - Total 0-0x15 = 22 calls
fffffff02850e818(0x72e818): MOVK	X16, #0, LSL #48  ; X16 := 0x0
fffffff02850e81c(0x72e81c): MOVK	X16, #0, LSL #32  ; X16 := 0x0
fffffff02850e820(0x72e820): MOVK	X16, #0, LSL #16  ; X16 := 0x0
fffffff02850e824(0x72e824): MOVK	X16, #0 ; X16 := 0x0
..
fffffff02850e9b4(0x72e9b4): MOVK	X16, #21 ; X16 := 0x15
fffffff02850e9b8(0x72e9b8): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4
...
## '6' subsystem - Total 0-0x4 = 5 calls
fffffff02850e9c8(0x72e9c8): MOVK	X16, #0 ; X16 := 0x600000000
...
fffffff02850ea18(0x72ea18): MOVK	X16, #4 ; X16 := 0x600000004          
fffffff02850ea1c(0x72ea1c): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4
..
## '3' subsystem - Total 0-0x10 = 17 calls
fffffff02850ea2c(0x72ea2c): MOVK	X16, #0 ; X16 := 0x300000000
..
fffffff02850eb6c(0x72eb6c): MOVK	X16, #16 ; X16 := 0x300000010
fffffff02850eb70(0x72eb70): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4
..
## '5' subsystem (a bit out of order, 3 calls)
fffffff02850eb80(0x72eb80): MOVK	X16, #1 ; X16 := 0x500000001
fffffff02850eb94(0x72eb94): MOVK	X16, #2 ; X16 := 0x500000002
fffffff02850eb98(0x72eb98): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4
## '7' subsystem -  Total 0-0x12 = 13 calls

fffffff02850ebbc(0x72ebbc): MOVK	X16, #0 ; X16 := 0x700000000
fffffff02850ebc0(0x72ebc0): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4
...
fffffff02850ecac(0x72ecac): MOVK	X16, #12 ; X16 := 0x70000000c
fffffff02850ecb0(0x72ecb0): B	0xfffffff02a330cc4 ; _func_0xfffffff02a330cc4

It looks like XNU is calling a host of functions, with "families" (which we'll refer to as "subsystem IDs") of 0, 6, 3, 5 and 7. But XNU can't issue an SVC (it's already at a level higher than EL0!). So looking at the common branch point, 0xfffffff02a614e20:

DFFenders@xxxx (~) % disarm -r _func_0xfffffff02a330cc4 /tmp/extracted/kernel.rebuilt
_func_0xfffffff02a330cc4
fffffff02a614e20  d503237f      PACIBSP ; 
fffffff02a614e24  a9bf7bfd      STP     X29, X30, [X31, #-16]!  ; SP -= 16; *[SP] = [X29, X30]
fffffff02a614e28  910003fd      ADD     X29, X31, #0    ; FP = SP + 0x0 = 0x0!
        _func_0xfffffff027e8cb44();
fffffff02a614e2c  9761df46      BL      0xfffffff027e8cb44      ; 
fffffff02a614e30  910003bf      ADD     X31, X29, #0    ; SP = FP + 0x0 = 0x0!
fffffff02a614e34  a8c17bfd      LDP     X29, X30, [X31], #16    ; [X29, X0] = *[X31]; X31 += 2
fffffff02a614e38  00201420      GENTER  #0      ; 
fffffff02a614e3c  a9bf7bfd      STP     X29, X30, [X31, #-16]!  ; SP -= 16; *[SP] = [X29, X30]
fffffff02a614e40  910003fd      ADD     X29, X31, #0    ; FP = SP + 0x0 = 0x0!
        _func_0xfffffff027e8cb84(0);
fffffff02a614e44  9761df50      BL      0xfffffff027e8cb84      ; 
fffffff02a614e48  910003bf      ADD     X31, X29, #0    ; SP = FP + 0x0 = 0x0!
fffffff02a614e4c  a8c17bfd      LDP     X29, X30, [X31], #16    ; [X29, X0] = *[X31]; X31 += 2
fffffff02a614e50  d65f0fff      RETAB   ; 

So XNU makes a "system call" to SPTM by means of GENTER #0, as expected. The two functions there (0xfffffff027e8cb44 and 0xfffffff027e8cb84) appear to do before/after processing, with the latter (0xfffffff027e8cb84) calling into XNU's AST - Asynchronous Software Traps, which are often called post interrupt handling and certain kernel events.

Figuring out the subsystems

So far, then, we see that there are thus several subsystems - 0, 3, 5 and 7, along with TXM's 1, and 253/254/255 (which seem to be individual codes). Recall (from the previous article) that we had already identified several subsystems in SPTM:

DFFenders@xxxx (~/Downloads/extracted/Firmware) % disarm sptm.t8120.release.im4p.decompressed| grep 0xfffffff0070895b4
	_func_0xfffffff0070895b4(0x1,0xfffffff00705b3b8); // SART
	_func_0xfffffff0070895b4(0x2,0xfffffff00705b458); // NVMe
	_func_0xfffffff0070895b4(0x3,0xfffffff00705b4d0); // uat
	_func_0xfffffff0070895b4(0x5,0xfffffff00705b260); // t8110dart

These registrations, however, appear to be IOMMU related, and weren't the actual numbers (as could also be observed from the previous article). There is a current_iommu() routine (0xfffffff0070891c8, which is sometimes inlined), which the TLS (TPIDR_GL2 at offset 0x8), to get the active IOMMU index, from the above list. Looking into three of these IOMMUs (which are contiguous in __DATA_CONST):

fffffff00705b3b8:    0xfffffff00700876e   "SART"
fffffff00705b3c0:    0xfffffff007077da4   _func_0xfffffff007077da4  sart_bootstrap (INITIALIZER)
fffffff00705b3c8: 00 00 00 00 00 00 00 00    |........|
fffffff00705b3d0: 00 00 00 00 00 00 00 00    |........|
fffffff00705b3d8: 05 00 00 00 00 00 00 00    |........|   <-- actual subsystem number
fffffff00705b3e0:    0xfffffff0070588a0   _sart_dispatch_table
fffffff00705b3e8: 00 00 00 00 00 00 00 00    |........|
fffffff00705b3f0: 01 00 00 00 00 00 00 00    |........|
fffffff00705b3f8: 38 01 00 00 00 00 00 00    |8.......|
fffffff00705b400:    0xfffffff007008773   "VIOLATION_SART_INVALID_PT"
fffffff00705b408:    0xfffffff00700878d   "VIOLATION_SART_INVALID_PADDR"
fffffff00705b410:    0xfffffff0070087aa   "VIOLATION_SART_INVALID_N_OPS"
fffffff00705b418:    0xfffffff0070087c7   "VIOLATION_SART_INVALID_SIZE"
fffffff00705b420:    0xfffffff0070087e3   "VIOLATION_SART_INVALID_PERM"
fffffff00705b428:    0xfffffff0070087ff   "VIOLATION_SART_ILLEGAL_STATE"
fffffff00705b430:    0xfffffff00700881c   "VIOLATION_SART_NO_SPACE"
fffffff00705b438:    0xfffffff007008834   "VIOLATION_SART_ILLEGAL_MAP"
fffffff00705b440:    0xfffffff00700884f   "VIOLATION_SART_ILLEGAL_UNMAP"
fffffff00705b448:    0xfffffff00700886c   "VIOLATION_SART_CPU_RACE"
fffffff00705b450:    0xfffffff007008884   "VIOLATION_SART_INVALID_CONFIG"
---
fffffff00705b458:    0xfffffff007007cf5   "NVMe"
fffffff00705b460:    0xfffffff007076160   _func_0xfffffff007076160 
fffffff00705b468: 00 00 00 00 00 00 00 00    |........|
fffffff00705b470: 00 00 00 00 00 00 00 00    |........|
fffffff00705b478: 06 00 00 00 00 00 00 00    |........|  <-- Actual subsystem number
fffffff00705b480:    0xfffffff007058788   _nvme_dispatch_table
fffffff00705b488: 00 00 00 00 00 00 00 00    |........|
fffffff00705b490: 01 00 00 00 00 00 00 00    |........|
fffffff00705b498: 78 07 00 00 00 00 00 00    |x.......|
fffffff00705b4a0:    0xfffffff007007cfa   "VIOLATION_NVME_INVALID_QID"
fffffff00705b4a8:    0xfffffff007007d15   "VIOLATION_NVME_INVALID_CID"
fffffff00705b4b0:    0xfffffff007007d30   "VIOLATION_NVME_INVALID_PAGE_COUNT"
fffffff00705b4b8:    0xfffffff007007d52   "VIOLATION_NVME_INVALID_NVME_PAGE"
fffffff00705b4c0:    0xfffffff007007d73   "VIOLATION_NVME_ILLEGAL_CALL"
fffffff00705b4c8:    0xfffffff007007d8f   "VIOLATION_NVME_ILLEGAL_CID_STATE_TRANSITION"
---
fffffff00705b4d0:    0xfffffff007006297   "uat"
fffffff00705b4d8:    0xfffffff007071bb4   _func_0xfffffff007071bb4  UAT_bootstrap
fffffff00705b4e0:    0xfffffff007071b1c   _func_0xfffffff007071b1c  uat_retype_in
fffffff00705b4e8:    0xfffffff007071a90   _func_0xfffffff007071a90  uat_retype_out
fffffff00705b4f0: 07 00 00 00 00 00 00 00    |........|  <-- actual subsystem number
fffffff00705b4f8:    0xfffffff007058000   _uat_dispatch_table
....

You might wonder how we deduced those …_dispatch_tables above - To get those we have to look into sptm_dispatch (q.v. first article, where table was @fffffff00705b5d8):

DFFenders@xxxx (~/Firmware/Extracted) % disarm -a 0xfffffff00708a194 sptm.t8122.release.im4p
_sptm_dispatch:
fffffff00708a194  d503237f      PACIBSP ; 
fffffff00708a198  d10183ff      SUBi    X31, X31, #96   ; SP = SP - 0x60
fffffff00708a19c  a9057bfd      STP     X29, X30, [X31, #80]    ; *[SP +80] = [X29, X30]
fffffff00708a1a0  910143fd      ADD     X29, X31, #80   ; FP = SP + 0x50 = 0x50 -- !
fffffff00708a1a4  d53efb29      MRS     X9, TPIDR_GL2   ; not yet..
fffffff00708a1a8  f27b081f      TST     X0,  #0xe0              ; 
fffffff00708a1ac  540007c1      B.NE    0xfffffff00708a2a4      ; 
fffffff00708a1b0  d3609c08      LSRi    X8, X0, #0              ; 
fffffff00708a1b4  71001d1f      CMPi    W8, #7                  ; cmp to # of subsystems
fffffff00708a1b8  54000bc8      B.HI    0xfffffff00708a330      ; tooHighForASubsystem
fffffff00708a1bc  aa0803f0      MOV     X16, X8 ; X16 = X8 (0xfffffff00700e670)
fffffff00708a1c0  f1001e1f      CMPi    X16, #7 ; 
fffffff00708a1c4  9a9f9210      CSEL    X16, X16, X31, LS       ; X16 = ( ) ? 0xfffffff00700e670 : 0x50
fffffff00708a1c8  10000df1      ADR     X17, 0xfffffff00708a384 ; X17 = 0xfffffff00708a384
fffffff00708a1cc  d503201f      NOP     ; 
fffffff00708a1d0  b8b07a30      LDRSW(r)        X16, [X17, X16, LSL #2] ;  branch table @0xfffffff00708a384
fffffff00708a1d4  10000011      ADR     X17, 0xfffffff00708a1d4 ; X17 = 0xfffffff00708a1d4
fffffff00708a1d8  8b100230      ADDsr   X16, X17, X16   ; R16 = R17 + R16 = 0xfffffff00708a1e4
fffffff00708a1dc  d61f0200      BR      X16     ; (no symbol)
_case_0: (XNU)
fffffff00708a1e0  d503249f      BTI     j       ; 
fffffff00708a1e4  10e8a0e9      ADR     X9, 0xfffffff00705b600  ; _xnu_dispatch
fffffff00708a1ec  14000026      B       0xfffffff00708a284      ; (no symbol)
case 2: (unknown)
fffffff00708a1f0  d503249f      BTI     j       ; 
fffffff00708a1f4  10e8b069      ADR     X9, 0xfffffff00705b800  ; X9 = _unknown_dispatch
fffffff00708a1fc  14000022      B       0xfffffff00708a284      ; (no symbol)
case 3: (t8110dart)
fffffff00708a200  d503249f      BTI     j       ; 
fffffff00708a204  528000ca      MOVZ    W10, #6 ; X10 = 0x6
fffffff00708a208  f900052a      STRi    X10, [X9, #8]   ; *0x8 = 0x6
fffffff00708a20c  10e73ce9      ADR     X9, 0xfffffff0070589a8  ; _t8011dart_dispatch
fffffff00708a214  1400001c      B       0xfffffff00708a284      ; (no symbol)
case 4: (???)
fffffff00708a218  d503249f      BTI     j       ; 
fffffff00708a21c  528000ca      MOVZ    W10, #6 ; X10 = 0x6
fffffff00708a220  f900052a      STRi    X10, [X9, #8]   ; *0x8 = 0x6
fffffff00708a224  10e74429      ADR     X9, 0xfffffff007058aa8  ; __unknown_subsystem_4_dispatch
fffffff00708a22c  14000016      B       0xfffffff00708a284      ; (no symbol)
case 1: (TXM)
fffffff00708a230  d503249f      BTI     j      
fffffff00708a234  10e8a669      ADR     X9, 0xfffffff00705b700  ; _TXM_dispatch
fffffff00708a23c  14000012      B       0xfffffff00708a284      ; (no symbol)
case 5: (sart)
fffffff00708a240  d503249f      BTI     j       ; 
fffffff00708a244  5280004a      MOVZ    W10, #2 ; X10 = 0x2
fffffff00708a248  f900052a      STRi    X10, [X9, #8]   ; *0x8 = 0x2
fffffff00708a24c  10e732a9      ADR     X9, 0xfffffff0070588a0  ; _SART_dispatch
fffffff00708a254  1400000c      B       0xfffffff00708a284      ; (no symbol)
case 6: (nvme)
fffffff00708a258  d503249f      BTI     j       ; 
fffffff00708a25c  5280006a      MOVZ    W10, #3 ; X10 = 0x3
fffffff00708a260  f900052a      STRi    X10, [X9, #8]   ; *0x8 = 0x3
fffffff00708a264  10e72929      ADR     X9, 0xfffffff007058788  ; _NVMe_dispatch
fffffff00708a268  d503201f      NOP     ; 
fffffff00708a26c  14000006      B       0xfffffff00708a284      ; (no symbol)
case 7: (uat)
fffffff00708a270  d503249f      BTI     j       ; 
fffffff00708a274  5280008a      MOVZ    W10, #4 ; X10 = 0x4
fffffff00708a278  f900052a      STRi    X10, [X9, #8]   ; *0x8 = 0x4
fffffff00708a27c  10e6ec29      ADR     X9, 0xfffffff007058000  ; _uat_dispatch
fffffff00708a280  d503201f      NOP     ; 
fffffff00708a284  8b200d29      ADDer   X9, X9, W0, UXTB #3     ; X9 = X9 + 0x3 = 0xfffffff007058003 -- !
fffffff00708a288  f9400129      LDRi    X9, [X9]        ;       ; X9 = *(0xfffffff007058003) (9) = ??? 
fffffff00708a28c  b4000289      CBZ     X9, 0xfffffff00708a2dc  ; 
fffffff00708a290  aa0903e0      MOV     X0, X9  ; X0 = X9 (0xfffffff007058003)
fffffff00708a294  a9457bfd      LDP     X29, X30, [X31, #80]    ; [X29, X0] = *[X31]
fffffff00708a298  910183ff      ADD     X31, X31, #96   ; SP = SP + 0x60 = 0x60 -- !
....

branch_table_from_0xfffffff00708a1dc:
fffffff00708a384  0000000c      DCD     0xc     ; 
fffffff00708a388  0000005c      DCD     0x5c    ; 
fffffff00708a38c  0000001c      DCD     0x1c    ; 
fffffff00708a390  0000002c      DCD     0x2c    ; 
fffffff00708a394  00000044      DCD     0x44    ; 
fffffff00708a398  0000006c      DCD     0x6c    ; 
fffffff00708a39c  00000084      DCD     0x84    ; 
fffffff00708a3a0  0000009c      DCD     0x9c    ; 
<<< @@TODO: Explain about dispatch state machine >>>

We now have a nearly complete picture of the subsystem dispatchers:

#SubsystemDescriptor# of calls/services
0XNU0xfffffff00705b60022
1TXM0xfffffff00705b7005
2?0xfffffff00705b8003
3t8110dart0xfffffff0070589a817
4??0xfffffff007058aa8..
5SART0xfffffff0070588a03
6NVMe0xfffffff0070587885
7UAT0xfffffff00705800013

And note that the numbers for 0, 3, 5, 6 and 7 - those numbers we saw earlier are used by XNU - indeed line up, if counting from 0)!

Example: The SPTM XNU Subsystem

We can piece together the XNU subsystem of SPTM by looking through its dispatch table (we we uncovered as "case 0", above), and then matching some function call names (left in debug messages by our friends at Apple, but probably not for long), as well as correlating them from the places where they are called out in XNU. This would result in the following:

_xnu_dispatch:
fffffff00705b600:    0xfffffff007072ba0    called_from_machine_lockdown
fffffff00705b608:    0xfffffff00708a3d8    sptm_retype
fffffff00705b610:    0xfffffff00708a910   _func_0xfffffff00708a910
fffffff00705b618:    0xfffffff00708b3c4    sptm_map_table
fffffff00705b620:    0xfffffff00708bb20    sptm_unmap_table
fffffff00705b628:    0xfffffff00708ce44   _func_0xfffffff00708ce44
fffffff00705b630:    0xfffffff00708d43c   _func_0xfffffff00708d43c
fffffff00705b638:    0xfffffff00708ca48   _func_0xfffffff00708ca48
fffffff00705b640:    0xfffffff00708d4dc   _func_0xfffffff00708d4dc
fffffff00705b648:    0xfffffff00708d91c   _func_0xfffffff00708d91c
fffffff00705b650:    0xfffffff00708dc60   sptm_nest_region
fffffff00705b658:    0xfffffff00708e3a8   _func_0xfffffff00708e3a8
fffffff00705b660:    0xfffffff00708e7e4   _func_0xfffffff00708e7e4
fffffff00705b668:    0xfffffff00708e8c0   _func_0xfffffff00708e8c0
fffffff00705b670:    0xfffffff0070730b0   sptm_register_cpu
fffffff00705b678:    0xfffffff007075118   _func_0xfffffff007075118
fffffff00705b680:    0xfffffff00708ed80   sptm_sign_user_pointer
fffffff00705b688:    0xfffffff00708ee40   sptm_auth_user_pointer
fffffff00705b690:    0xfffffff00708a10c   __xnu_exc_return_handler_registration_maybe
fffffff00705b698:    0xfffffff007073818   _func_0xfffffff007073818
fffffff00705b6a0:    0xfffffff007073534   spth_slide_region
fffffff00705b6a8:    0xfffffff00708d514   _func_0xfffffff00708d514

SPTM retyping

Looking through symbols, we see quite a few references to ...page_type_retype_[in/out]:

xnu_default_retype_out
xnu_exec_retype_out
cpu_page_table_retype_out
cpu_root_table_retype_in
xnu_rozone_retype_out
t8110dart_retype_in
uat_retype_in
xnu_default_retype_out

What's interesting is that these functions have no references from elsewhere inside the code. But.. they must be called from somewhere, right? So - symbolicating accordingly, and dumping __DATA_CONST, we see what appears to be a descriptor table:

fffffff00705b920: 00 00 00 00 00 01 01 00    |........|
fffffff00705b928: FF FF FF FF FF FF FF FF    |........|
fffffff00705b930: 00 00 00 00 00 00 00 00    |........|
fffffff00705b938: 00 00 00 00 00 00 00 00    |........|
fffffff00705b940:    0xfffffff0070878bc   _sptm_retype_null_in
fffffff00705b948:    0xfffffff0070878b4   _sptm_retype_null_out
..(multiple repetitions)
..

The structure leads us to deduce the following:

TXM

XNU → XTM transitions

As described in the previous article, XNU is a client of TXM for all of its code signing/entitlement needs. This means that XNU needs a way to call out to TXM - which is technically a separate address space than itself (presumably, this is the Apple idea of an "exclave".

To get from XNU to TXM, therefore, will require involving SPTM. The sequence is as follows:

  1. XNU makes an SPTM call - via GENTER. This was explained in the previous article (0xfffffff028448e70) and is in fffffff02850e01c in the 17.1.1 kernel:
  2. fffffff02850e01c  d503237f      PACIBSP ; 
    fffffff02850e020  2a0003f0      MOV     W16, W0 ; X16 = X0 (0x0)
    fffffff02850e024  f2e00050      MOVK    X16, #2, LSL #48        ; X16 := 0x2000000000000
    fffffff02850e028  f2c00010      MOVK    X16, #0, LSL #32        ; X16 := 0x2000000000000
    fffffff02850e02c  aa0103ea      MOV     X10, X1 ; X10 = X1 (0x0)
    fffffff02850e030  a9400540      LDP     X0, X1, [X10]   ; [X0, X0] = *[X10]
    fffffff02850e034  a9410d42      LDP     X2, X3, [X10, #16]      ; [X2, X0] = *[X10]
    fffffff02850e038  a9421544      LDP     X4, X5, [X10, #32]      ; [X4, X0] = *[X10]
    fffffff02850e03c  a9431d46      LDP     X6, X7, [X10, #48]      ; [X6, X0] = *[X10]
    fffffff02850e040  00201420      GENTER  #0      ; 
    fffffff02850e044  d65f0fff      RETAB   ; 
    --
    fffffff02850e048  00201421      GENTER  #1      ; 
    fffffff02850e04c  00201422      GENTER  #2      ; 
    fffffff02850e050  14000000      HALT    #0      ; 
    --
    fffffff02850e054  00201423      GENTER  #3      ; 
    
  3. Control is transferred to SPTM (via GENTER handler), which realizes (via X16 0x20000........) this is a call from XNU proper.
  4. SPTM somehow transfers control to TXM. But how?

The TXM Side of things

Early in its initialization, TXM only signs two pointers:

fffffff01701b210  d503237f      PACIBSP ; 
fffffff01701b214  d100c3ff      SUBi    X31, X31, #48   ; SP = SP - 0x30
fffffff01701b218  a9014ff4      STP     X20, X19, [X31, #16]    ; *[SP +16] = [X20, X19]
fffffff01701b21c  a9027bfd      STP     X29, X30, [X31, #32]    ; *[SP +32] = [X29, X30]
fffffff01701b220  910083fd      ADD     X29, X31, #32   ; FP = SP + 0x20 = 0x20 -- !
fffffff01701b224  10fa8ff3      ADR     X19, 0xfffffff017010420 ; X19 = 0xfffffff017010420
fffffff01701b228  d503201f      NOP     ; 
fffffff01701b22c  52800028      MOVZ    W8, #1  ; X8 = 0x1
fffffff01701b230  39000268      STRBi   W8, [X19]       ; 
	__Do_Setup(....);
fffffff01701b234  940016b4      BL      0xfffffff017020d04      ; 
fffffff01701b238  10042d30      ADR     X16, 0xfffffff0170237dc ; X16 = 0xfffffff0170237dc
fffffff01701b23c  d503201f      NOP     ; 
fffffff01701b240  dac123f0      PACIZA  X16     ; 
fffffff01701b244  aa1003e1      MOV     X1, X16 ; X1 = X16 (0xfffffff0170237dc)
fffffff01701b248  52800000      MOVZ    W0, #0  ; X0 = 0x0
fffffff01701b24c  52800042      MOVZ    W2, #2  ; X2 = 0x2
	func_0xfffffff01704b234(0,0xfffffff0170237dc (PAC),0x2);
fffffff01701b250  9400bff9      BL      0xfffffff01704b234      ; 
fffffff01701b254  100420f0      ADR     X16, 0xfffffff017023670 ; X16 = 0xfffffff017023670
fffffff01701b258  d503201f      NOP     ; 
fffffff01701b25c  dac123f0      PACIZA  X16     ; 
fffffff01701b260  aa1003e1      MOV     X1, X16 ; X1 = X16 (0xfffffff017023670)
fffffff01701b264  52800020      MOVZ    W0, #1  ; X0 = 0x1
fffffff01701b268  52800022      MOVZ    W2, #1  ; X2 = 0x1
	func_0xfffffff01704b234(0x1,0xfffffff017023670 (PAC),0x1);
fffffff01701b26c  9400bff2      BL      0xfffffff01704b234      ; 
	_gets_build_variant_string();
fffffff01701b270  94001745      BL      0xfffffff017020f84      ; 
fffffff01701b274  f90003e0      STRi    X0, [X31]       ; *0x0 = 0x0
fffffff01701b278  50f5faa0      ADR     X0, 0xfffffff0170071ce  ; X0 = 0xfffffff0170071ce
fffffff01701b27c  d503201f      NOP     ; 
	_Log("build variant: %s");

Both pointers are provided as arguments to 0xfffffff01704b234. Where have we seen that before?

fffffff01704b234  f2e00010      MOVK    X16, #0, LSL #48        ; X16 := 0x0
fffffff01704b238  f2c00030      MOVK    X16, #1, LSL #32        ; X16 := 0x100000000
fffffff01704b23c  f2a00010      MOVK    X16, #0, LSL #16        ; X16 := 0x100000000
fffffff01704b240  f2800050      MOVK    X16, #2 ; X16 := 0x100000002

That is, in a call to SPTM! We can therefore deduce these two pointers are callbacks into TXM, and selector 0x100000002 indicates sptm_register_callback(...) or similar The first of them - 0x2 - is of particular interest. After finding a TXM Thread and ensuring stack alignment, it calls SVC #37 goes to fffffff017021e40. Wherein we shortly find ourselves in a giant switch table. The switch table is automatically reconstructed by disarm, and can be double checked thanks to ARMv8.5 Branch Target Indicators, marking the branch addresses as safe to BTI jump to.

fffffff017021f18  b8b07a30      LDRSW(r)        X16, [X17, X16, LSL #2] ;  branch table @0xfffffff0170222ec
fffffff017021f1c  10000011      ADR     X17, 0xfffffff017021f1c ; X17 = 0xfffffff017021f1c
fffffff017021f20  8b100230      ADDsr   X16, X17, X16   ; R16 = R17 + R16 = 0xfffffff017021f2c
fffffff017021f24  d61f0200      BR      X16     ; (no symbol)
..
_case 0:
fffffff017021f28  d503249f      BTI     j       ; 
	func_0xfffffff0170239b0(ARG0,ARG1,ARG2,ARG3,ARG4);
fffffff017021f2c  940006a1      BL      0xfffffff0170239b0   
..

_case_1:  // code signing related
fffffff017021f60  d503249f      BTI     j       ; 
	func_0xfffffff017022390(ARG0,ARG1,ARG2,ARG3,ARG4);
fffffff017021f64  9400010b      BL      0xfffffff017022390      ; 
fffffff017021f68  1400001c      B       0xfffffff017021fd8

..

fffffff017021fa4  d503249f      BTI     j       ; 
	func_0xfffffff0170239b0(ARG0,ARG1,ARG2,ARG3,ARG4);
fffffff017021fa8  94000682      BL      0xfffffff0170239b0      ; 
fffffff017021fac  aa0003f4      MOV     X20, X0 ; X20 = X0 (0x0)
fffffff017021fb0  52800028      MOVZ    W8, #1  ; X8 = 0x1
fffffff017021fb4  f9000c08      STRi    X8, [X0, #24]   ; *0x18 = 0x1
	_gets_build_variant_string();
fffffff017021fb8  97fffbf3      BL      0xfffffff017020f84      ;
...

fffffff0170222ec  0000000c      DCD     0xc     ;  ..017021f28
fffffff0170222f0  00000044      DCD     0x44    ;  ..17021f60
fffffff0170222f4  00000050      DCD     0x50    ; 
fffffff0170222f8  00000088      DCD     0x88    ; 
fffffff0170222fc  000000b0      DCD     0xb0    ; 
fffffff017022300  000000c8      DCD     0xc8    ; 
fffffff017022304  000000d4      DCD     0xd4    ; 
......
fffffff017022374  00000300      DCD     0x300   ;  0xfffffff01702221c, calls 0xfffffff017022c94
fffffff017022378  00000310      DCD     0x310   ;  0xfffffff01702222c, calls 0xfffffff017022cec
fffffff01702237c  0000031c      DCD     0x31c   ;  0xfffffff017022238, calls 0xfffffff017023520
fffffff017022380  0000032c      DCD     0x32c   ;  0xfffffff017022248, calls 0xfffffff01702353c
fffffff017022384  000003b4      DCD     0x3b4   ; 
fffffff017022388  0000033c      DCD     0x33c   ;  0xfffffff017022258, calls 0xfffffff0170235b4
fffffff01702238c  0000034c      DCD     0x34c   ;  0xfffffff017022268, calls 0xfffffff017022d38

At least one of the functions invoked, we can immediately tell - since it is the only argument to a format string of "build variant", we know it reports the TXM build variant (from previous listing). How do we find the rest? Let's head back to XNU.

Back to XNU

Apple released the XNU sources for iOS 17 (XNU-10002) surprisingly early. The 8792 sources already showed the PPL-client side (in bsd/kern/kern_codesigning.c, with #if PMAP_CS_PPL_MONITOR), but the 10002 sources remove that #if, instead providing many CSM_PREFIXed call-outs, and a brand new /bsd/sys/code_signing_internal.h. From the header we learn there are two possible prefixes - ppl_ (for PMAP_CS) and xnu_ (for older, non PPL devices). But what of the newer, GXF devices? Apple redacts this, because obviously if the #if for that is gone, nobody will consider disassembling the kernel, will they?

If they did, however, they'd quickly find the calls to TXM. We would start with one of the kern_codesigning.c panics:

fffffff02827d880  52804c88      MOVZ    W8, #612        ; X8 = 0x264
fffffff02827d884  d0ff7029      ADRP    X9, #-4602      ; X9 = 0xfffffff027083000
fffffff02827d888  911bd529      ADD     X9, X9, #1781   ; X9 = X9 + 0x6f5 = 0xfffffff0270836f5 -- !'kern_codesigning.c'
fffffff02827d88c  a90123e9      STP     X9, X8, [X31, #16]      ; *[SP +16] = [X9, X8]
fffffff02827d890  a90053e0      STP     X0, X20, [X31]  ; *(X31) = [X0, X20]
fffffff02827d894  d0ff7020      ADRP    X0, #-4602      ; X0 = 0xfffffff027083000
fffffff02827d898  911e2800      ADD     X0, X0, #1930   ; X0 = X0 + 0x78a = 0xfffffff02708378a -- !'unable to unregister profile from monitor: %d | %p
 @%s:%d'
	_panic("unable to unregister profile from monitor: %d | %p\n @%s:%d");

Going back from it:

fffffff02827d7f0  aa1503f4     MOV     X20, X21        ; X20 = X21 (0x0)
fffffff02827d7f4  f94012b5     LDRi    X21, [X21, #32] ; X21 = *(X21 + 0x20) = ?
fffffff02827d7f8  39406288     LDRB    W8, [X20, #96]  ; 
fffffff02827d7fc  35ffff68     CBNZ    W8, 0xfffffff02847d7e8  ; 
fffffff02827d800  f9400a80     LDRi    X0, [X20, #16]  ; X0 = *(X20 + 0x10) = ? 
        _func_0xfffffff02827f16c(?,ARG1,ARG2,ARG3,ARG4);
fffffff02827d804  9400065a     BL      0xfffffff02827f16c      ; 
fffffff02827d808  34000080     CBZ     W0, 0xfffffff02827d818  ; 
fffffff02827d80c  7100141f     CMPi    W0, #5  ; 
fffffff02827d810  54fffee0     B.EQ    0xfffffff02827d7ec      ; 
fffffff02827d814  1400001b     B       0xfffffff02827d880 

Then disassembling ...2827f16c:

DFFenders@xxxx (~) % disarm -r _func_0xfffffff02827f16c /tmp/extracted/kernel.rebuilt 
fffffff02827f16c  d503237f     PACIBSP ; 
fffffff02827f170  d10283ff     SUBi    X31, X31, #160  ; SP = SP - 0xa0
fffffff02827f174  a90757f6     STP     X22, X21, [X31, #112]   ; *[SP +112] = [X22, X21]
fffffff02827f178  a9084ff4     STP     X20, X19, [X31, #128]   ; *[SP +128] = [X20, X19]
fffffff02827f17c  a9097bfd     STP     X29, X30, [X31, #144]   ; *[SP +144] = [X29, X30]
fffffff02827f180  910243fd     ADD     X29, X31, #144  ; FP = SP + 0x90 = 0x90 -- !
fffffff02827f184  6f00e400     MOVI.2D V0, #0000000000000000   ; 
fffffff02827f188  ad0083e0     STP     Q0, Q0, [X31, #16]      ; *[SP +8] = [X0, X0]
fffffff02827f18c  f90033ff     STRi    X31, [X31, #96] ; *0x60 = 0x0
fffffff02827f190  ad0203e0     STP     Q0, Q0, [X31, #64]      ; *[SP +32] = [X0, X0]
fffffff02827f194  3d800fe0     STRi    Q0, [X31, #48]  ; *0x30 = 0x0
fffffff02827f198  52800168     MOVZ    W8, #11 ; X8 = 0xb
fffffff02827f19c  b90013e8     STRi    X8, [X31, #16]  ; *0x10 = 0xb
fffffff02827f1a0  f0ff6c68     ADRP    X8, #-4721      ; X8 = 0xfffffff02700e000
fffffff02827f1a4  fd443d00     LDRi    D0, [X8, #2168] ;   ; ~X0 = *(0xfffffff02700e878) (8) = ??? 
fffffff02827f1a8  fc01c3e0     STUR    X0, [X31, #28]  ; 
fffffff02827f1ac  f90003e0     STRi    X0, [X31]       ; *0x0 = 0x0
fffffff02827f1b0  910043e0     ADD     X0, X31, #16
	__txm_from_kernel(SP+0x10, ... );
fffffff02827f1b4  97fffc2b      BL      0xfffffff02827e260

The "selector" is stored in the arguments to the TXM kernel call (specifically, loaded into W8 (as in "#11", above), and shoved on the buffer argument to the TXM gate (as shown with selector #11, in fffffff02827f198 above). We leave it as a (lengthy) exercise to the reader to find all the selector calls. But, what of the names?

Looking at the header, we may not know the prefix, but the calls will still have the same:

DFFenders@xxxx (xnu-rel-xnu-10002.1.13) % grep CSM_P ./bsd/sys/code_signing_internal.h
#define __CSM_PREFIX(prefix, name) prefix##_##name
#define _CSM_PREFIX(prefix, name)  __CSM_PREFIX(prefix, name)
#define CSM_PREFIX(name)           _CSM_PREFIX(CODE_SIGNING_MONITOR_PREFIX, name)
void CSM_PREFIX(toggle_developer_mode)(
void CSM_PREFIX(set_compilation_service_cdhash)(
bool CSM_PREFIX(match_compilation_service_cdhash)(
void CSM_PREFIX(set_local_signing_public_key)(
uint8_t* CSM_PREFIX(get_local_signing_public_key)(void);
void* CSM_PREFIX(image4_storage_data)(
void CSM_PREFIX(image4_set_nonce)(
void CSM_PREFIX(image4_roll_nonce)(
errno_t CSM_PREFIX(image4_copy_nonce)(
errno_t CSM_PREFIX(image4_execute_object)(
errno_t CSM_PREFIX(image4_copy_object)(
const void* CSM_PREFIX(image4_get_monitor_exports)(void);
errno_t CSM_PREFIX(image4_set_release_type)(
errno_t CSM_PREFIX(image4_set_bnch_shadow)(
bool CSM_PREFIX(code_signing_enabled)(void);
vm_size_t CSM_PREFIX(managed_code_signature_size)(void);
void CSM_PREFIX(unrestrict_local_signing_cdhash)(
kern_return_t CSM_PREFIX(register_provisioning_profile)(
kern_return_t CSM_PREFIX(unregister_provisioning_profile)(
kern_return_t CSM_PREFIX(associate_provisioning_profile)(
kern_return_t CSM_PREFIX(disassociate_provisioning_profile)(
kern_return_t CSM_PREFIX(register_code_signature)(
kern_return_t CSM_PREFIX(unregister_code_signature)(
kern_return_t CSM_PREFIX(verify_code_signature)(
kern_return_t CSM_PREFIX(reconstitute_code_signature)(
kern_return_t CSM_PREFIX(associate_code_signature)(
kern_return_t CSM_PREFIX(allow_jit_region)(
kern_return_t CSM_PREFIX(associate_jit_region)(
kern_return_t CSM_PREFIX(associate_debug_region)(
kern_return_t CSM_PREFIX(address_space_debugged)(
kern_return_t CSM_PREFIX(allow_invalid_code)(
kern_return_t CSM_PREFIX(get_trust_level_kdp)(
kern_return_t CSM_PREFIX(address_space_exempt)(
kern_return_t CSM_PREFIX(fork_prepare)(
kern_return_t CSM_PREFIX(acquire_signing_identifier)(
kern_return_t CSM_PREFIX(associate_kernel_entitlements)(
kern_return_t CSM_PREFIX(resolve_kernel_entitlements)(
kern_return_t CSM_PREFIX(accelerate_entitlements)(

As another example (from the TXM side), consider one of the above functions - image4_set_release_type. This happens to be reported in one of the TXM panic messages:

_func_0xfffffff0170447ec:
fffffff0170447ec  d503237f      PACIBSP ; 
fffffff0170447f0  a9bf7bfd      STP     X29, X30, [X31, #-16]!  ; SP -= 16; *[SP] = [X29, X30]
fffffff0170447f4  910003fd      ADD     X29, X31, #0    ; FP = SP + 0x0 = 0x0 -- !
fffffff0170447f8  50e1f3e0      ADR     X0, 0xfffffff017008676  ; X0 = 0xfffffff017008676
fffffff0170447fc  d503201f      NOP     ; 
fffffff017044800  70e1f4e1      ADR     X1, 0xfffffff01700869f  ; X1 = 0xfffffff01700869f
fffffff017044804  d503201f      NOP     ; 
fffffff017044808  70e1f6e2      ADR     X2, 0xfffffff0170086e7  ; X2 = 0xfffffff0170086e7
fffffff01704480c  d503201f      NOP     ; 
fffffff017044810  52809e03      MOVZ    W3, #1264       ; X3 = 0x4f0
        _func_0xfffffff01704fa5c("release_type_p == cf4->cf4_osreleasetype","/Library/Caches/com.apple.xbs/S
ources/AppleImage4_txm/src/runtime/txm.c","img4_txm_set_release_type",0x4f0,ARG4);
fffffff017044814  94002c92      BL      0xfffffff01704fa5c      ; 

This is a "cold" path from the actual function, so going back we find its caller (and therefore, image4_set_release_type is _func_0xfffffff0170445ac. This is called from only one place - func_0xfffffff017023520, which is called from - you guessed it - that big switch table:

fffffff017022238  d503249f      BTI     j       ; 
fffffff01702223c  aa1403e0      MOV     X0, X20 ; X0 = X20 (0x0)
        _func_0xfffffff017023520(0,ARG1,ARG2,ARG3,ARG4);
fffffff017022240  940004b8      BL      0xfffffff017023520      ; 
fffffff017022244  14000027      B       0xfffffff0170222e0      ; (no symbol)

A little hex math shows that 0xfffffff017022238 - 0xfffffff017021f1c is 0x31c - which in the switch table was at entry #37 (counting from 1). Locating this in the kernel is trivial:

DFFenders@xxxx (~) % disarm /tmp/extracted/kernel.rebuilt | grep -B 10 0xfffffff02827e260  | grep \#37
fffffff02827e1dc  528004a8	MOVZ	W8, #37	; X8 = 0x25
#
# Now locate which function called us, assuming the function start is within 10 instructions before
# the address we've found
#
DFFenders@xxxx (~) % disarm /tmp/extracted/kernel.rebuilt | grep -B 10 fffffff02827e1dc  | grep ^_
_func_0xfffffff02827e1b8:

Another example can be seen by looking at entitlements validated in TXM-land. research.com.apple.license-to-operate and get-task-allow are both validated by 0xfffffff01701b080, so it makes sense they would be in TXM's implementation of allow_invalid_code. Likewise, com.apple.private.pmap.load-trust-cache is called from fffffff01701d65c, which is itself called from ffffff0170224b0 - which is at fffff017021ff0 in the dispatching branch table. Some hex math again shows us this is at 0xfffff017021ff0 - 0xfffffff017021f1c = 0xd4, or function #8.

Digging in __TEXT.__const - TXM certificates and OIDs

For your daily reminder it's not about __DATA_CONST alone, TXM's __TEXT.__const has a few certificates in it, which are easily identifiable from the DER header of "30 82..." and the textual strings therein:

And func_0xfffffff01703c85c handles certificate chain walking.

- There are a few other DER constructs embedded in __TEXT.__const, which can be extracted using some brute forcing with your favorite (or least hated) DER parsing tool of choice. For example, we see at relative offset 0x14 an array of entitlements to process:

    0:d=0  hl=4 l= 295 cons: appl [ 16 ]       
    4:d=1  hl=2 l=   1 prim: INTEGER           :01
    7:d=1  hl=4 l= 288 cons: cont [ 16 ]       
   11:d=2  hl=2 l=  56 cons: SEQUENCE          
   13:d=3  hl=2 l=  22 prim: UTF8STRING        :application-identifier
   37:d=3  hl=2 l=  30 prim: UTF8STRING        :LOCALSPKEY.swift-playgrounds-*
   69:d=2  hl=2 l=  63 cons: SEQUENCE          
   71:d=3  hl=2 l=  41 prim: UTF8STRING        :com.apple.developer.app-management-domain
  114:d=3  hl=2 l=  18 prim: UTF8STRING        :swift-playgrounds*
  134:d=2  hl=2 l=  46 cons: SEQUENCE          
  136:d=3  hl=2 l=  41 prim: UTF8STRING        :com.apple.developer.swift-playgrounds-app
  179:d=3  hl=2 l=   1 prim: BOOLEAN           :255
  182:d=2  hl=2 l=  64 cons: SEQUENCE          
  184:d=3  hl=2 l=  59 prim: UTF8STRING        :com.apple.developer.swift-playgrounds-app.development-build
  245:d=3  hl=2 l=   1 prim: UTF8STRING        :*
  248:d=2  hl=2 l=  49 cons: SEQUENCE          
  250:d=3  hl=2 l=  35 prim: UTF8STRING        :com.apple.developer.team-identifier
  287:d=3  hl=2 l=  10 prim: UTF8STRING        :LOCALSPKEY
  299:d=0  hl=2 l=  49 cons: SEQUENCE
 

- At address fffffff01700c588 we find

fffffff01700c588: 67 E6 09 6A 85 AE 67 BB    |g..j..g.|
fffffff01700c590: 72 F3 6E 3C 3A F5 4F A5    |r.n<:.O.|
fffffff01700c598: 7F 52 0E 51 8C 68 05 9B    |.R.Q.h..|
fffffff01700c5a0: AB D9 83 1F 19 CD E0 5B    |.......[|

which is the initial context of SHA-256.

- There are also quite a few encryption/hashing algorithm OID encodings embedded. For example,

fffffff01700cb88: 2A 86 48 86 F7 0D 01 01    |*.H.....|
fffffff01700cb90: 01 2A 86 48 86 F7 0D 01    |.*.H....|
fffffff01700cb98: 01 05 2A 86 48 86 F7 0D    |..*.H...|
...

encodes the OID of the RSA algorithm (OID 1.2.840.113549.1.1.1 ) followed (at ..cb91) by SHA1-RSA (1.2.840.113549.1.1.5), and others. For more on OID encodings, this reference is handy.

TXM IMG4 support

Looking through TXM's __TEXT again, we come across this interesting bit of code:

	        func_0xfffffff0170431fc(0,0,0xfffffff0170129d0,SP+0x17);
fffffff0170430a4  94000056      BL      0xfffffff0170431fc      ; 
fffffff0170430a8  7100081f      CMPi    W0, #2  ; 
fffffff0170430ac  540000a0      B.EQ    0xfffffff0170430c0      ; 
fffffff0170430b0  35000600      CBNZ    W0, 0xfffffff017043170
	..

fffffff017043170  f90003e0      STRi    X0, [X31]       ; *0x50 = 0x0
fffffff017043174  30e2b860      ADR     X0, 0xfffffff017008881  ; X0 = 0xfffffff017008881
fffffff017043178  d503201f      NOP     ; 
	func_0xfffffff017020fc0("panic: failed to retrieve research enabled bit: %d");
	

Looking at the 3rd argument, we see it is a region in __DATA_CONST:

	fffffff0170129d0: 0A 00 00 00 00 00 00 00    |........|
	fffffff0170129d8: 00 04 00 00 00 00 00 00    |........|
	fffffff0170129e0: 35 00 00 00 00 00 00 00    |5.......|
	fffffff0170129e8: 00 00 00 00 68 63 73 72    |....hcsr|
	fffffff0170129f0:    0xfffffff01700853c   "rsch"
	fffffff0170129f8:    0xfffffff017008541   "research mode"
	fffffff017012a00:    0xfffffff017008517   "bool"
	

This structure encodes the well known IMG4 manifest identifiers, and can be seen in other places as well. Specifically:
Address (in __DATA_CONST)identifierFull nameDatatype
fffffff017012728nsphpreboot splat manifest hashdigest
fffffff017012810DGSTpayload digestdigest
0xfffffff0170128f0/0xfffffff017012960ECIDunique chip identifieru64
fffffff017012880AMNMallow mix-n-matchbool
0xfffffff017012a40vnummaximum restore version cstring [version]

And the following functions get the data from the IMG4:

Other notes:

TL;DR

Putting it all together, we can (carefully) draw the following:

Moving to different binaries in 17.1.1 set back our plans to provide symbol files. Those will still be provided, so stay tuned.

@@@ Some tacky slogan here @@@




Extra stuff

  • func_0xfffffff017020fc0 is panic from TXM
  • 0xfffffff017010420 is apparently a global data structure, in which are stored (among other things) the ASID table (#16), boot-args (#520), logging (#24), and more
    entry_from_exception_vector_synchronous:
    fffffff007061b84  d500409f      MSR     PAN, #0 ;  Disable Privileged-Access-Never
    fffffff007061b88  a93f27e8      STP     X8, X9, [X31, #-16]     ; *[SP -16] = [X8, X9]
    fffffff007061b8c  d53efb28      MRS     X8, TPIDR_GL2   ; 
    fffffff007061b90  91000108      ADD     X8, X8, #0      ; X8 = X8 + 0x0 = 0x0 -- !
    fffffff007061b94  f9400d08      LDRi    X8, [X8, #24]   ; X8 = *(X8 + 0x18) = *(0x18) = 0x200001!
    fffffff007061b98  eb2863ff      SUBS    X31, X31, X8            ; 
    fffffff007061b9c  54000001      B.NE    0xfffffff007061b9c      ; 
    fffffff007061ba0  d53efaa8      MRS     X8, ESR_GL1     ; Guarded Level 1 Exception Syndrome Register
    fffffff007061ba4  d35a7d08      ubfx	X8, X8, #26, #6         ; Get bits 26-31...
    fffffff007061ba8  f100551f      CMPi    X8, #21                 ; Compare to SVC in AArch64 
    fffffff007061bac  54000080      B.EQ    0xfffffff007061bbc      ; SVC_handler
    fffffff007061bb0  f100591f      CMPi    X8, #22 ; 
    fffffff007061bb4  54000740      B.EQ    0xfffffff007061c9c      ; ??? (Not found in ARM spec!)
    fffffff007061bb8  14000046      B       0xfffffff007061cd0      ; unhandled synchronous exception panic
    SVC_handler:
    fffffff007061bbc  d53efaa8      MRS     X8, ESR_GL1             ; not yet..
    fffffff007061bc0  92403d08      AND     X8, X8, #0xffff         ; 
    fffffff007061bc4  f100011f      CMPi    X8, #0                  ; 
    fffffff007061bc8  540002e0      B.EQ    0xfffffff007061c24      ; svc_0_handler
    fffffff007061bcc  f100951f      CMPi    X8, #37                 ; 
    fffffff007061bd0  540000c0      B.EQ    0xfffffff007061be8      ; svc_37_handler
    fffffff007061bd4  f100991f      CMPi    X8, #38 ; 
    fffffff007061bd8  54000180      B.EQ    0xfffffff007061c08      ; svc_38_handler
    fffffff007061bdc  d29bd5a0      MOVZ    X0, #57005      ; X0 = 0xdead
    fffffff007061be0  d503205f      WFE     ; 
    fffffff007061be4  17ffffff      B       0xfffffff007061be0      ; (no symbol)
    svc_37_handler: (explained last article)
    ..
    svc_38_handler: (explained last article)
    ..
    ; 
    ; Remember that at this point X16 holds the subsystem/system call number:
    ; Use Unsigned BitField eXtract (UBFX) to get the top 8 bits of the higher end 32:
    ; That is the XX........
    ;
    svc_0_handler:
    fffffff007061c24  d3609e09      ubfx    X9, X16, #32, #8
    fffffff007061c28  d2801faa      MOVZ    X10, #253       ; X10 = 0xfd
    fffffff007061c2c  eb0a013f      CMPsr   X9, X10 ; 
    fffffff007061c30  54000041      B.NE    0xfffffff007061c38      ;  not_253
    fffffff007061c34  14000105      B       0xfffffff007062048      ; yes_253
    not_253:
    fffffff007061c38  d2801fca      MOVZ    X10, #254       ; X10 = 0xfe
    fffffff007061c3c  eb0a013f      CMPsr   X9, X10 ; 
    fffffff007061c40  54000261      B.NE    0xfffffff007061c8c      ;  not_253_or_254
    _yes_254:
    fffffff007061c44  d53efb28      MRS     X8, TPIDR_GL2   ; not yet..
    fffffff007061c48  91000108      ADD     X8, X8, #0      ; X8 = X8 + 0x0 = 0x0 -- !
    fffffff007061c4c  9114810a      ADD     X10, X8, #1312  ; X10 = X8 + 0x520 = 0x520 -- !
    fffffff007061c50  f9400149      LDRi    X9, [X10]       ; X9 = *(X10 + 0x0) = *(0x520) = 0xe0009c000!
    fffffff007061c54  d51ef1a9      MSR     SPRR_UPERM_EL0, X9      ; 
    fffffff007061c58  f9400549      LDRi    X9, [X10, #8]   ; X9 = *(X10 + 0x8) = *(0x528) = 0x0!
    fffffff007061c5c  d5100249      MSR     MDSCR_EL1, X9   ; 
    fffffff007061c60  f9400949      LDRi    X9, [X10, #16]  ; X9 = *(X10 + 0x10) = *(0x530) = 0x0!
    fffffff007061c64  d519f1a9      MSR     S3_1_C15_C1_5, X9       ; 
    fffffff007061c68  f9400d49      LDRi    X9, [X10, #24]  ; X9 = *(X10 + 0x18) = *(0x538) = 0x0!
    fffffff007061c6c  d51cf089      MSR     AAPL_REG_APCTL_EL1, X9  ; 
    fffffff007061c70  f9401149      LDRi    X9, [X10, #32]  ; X9 = *(X10 + 0x20) = *(0x540) = 0x4800000019!
    fffffff007061c74  d5181009      MSR     SCTLR_EL1, X9   ; 
    fffffff007061c78  d50040bf      MSR     SPSel, #0       ; 
    fffffff007061c7c  17fffd81      B       0xfffffff007061280      ; (no symbol)
    fffffff007061c80  d29bd5a0      MOVZ    X0, #57005      ; X0 = 0xdead
    fffffff007061c84  d503205f      WFE     ; 
    fffffff007061c88  17ffffff      B       0xfffffff007061c84      ; (no symbol)
    not_253_or_254:
    fffffff007061c8c  d2801fea      MOVZ    X10, #255       ; X10 = 0xff
    fffffff007061c90  eb0a013f      CMPsr   X9, X10 ; 
    fffffff007061c94  54001801      B.NE    0xfffffff007061f94      ; not_253_or_254_or_255
    fffffff007061c98  1400010c      B       0xfffffff0070620c8      ; yes_255
    fffffff007061c9c  d3609e09      LSRi    X9, X16, #0     ; 
    fffffff007061ca0  d2801faa      MOVZ    X10, #253       ; X10 = 0xfd
    fffffff007061ca4  eb0a013f      CMPsr   X9, X10 ; 
    fffffff007061ca8  54000041      B.NE    0xfffffff007061cb0      ; 
    fffffff007061cac  14000117      B       0xfffffff007062108      ; (no symbol)
    fffffff007061cb0  d2801fca      MOVZ    X10, #254       ; X10 = 0xfe
    fffffff007061cb4  eb0a013f      CMPsr   X9, X10 ; 
    fffffff007061cb8  54000041      B.NE    0xfffffff007061cc0      ; 
    fffffff007061cbc  14000046      B       0xfffffff007061dd4      ; (no symbol)
    fffffff007061cc0  d2801fea      MOVZ    X10, #255       ; X10 = 0xff
    ...
    ...
    not_253_254_or_255:   ## This is TXM's 0x1........ code entry point
    fffffff007061f94  d53efb28      MRS     X8, TPIDR_GL2   ; not yet..
    fffffff007061f98  91000108      ADD     X8, X8, #0      ; X8 = X8 + 0x0 = 0x0 -- !
    fffffff007061f9c  f9400d09      LDRi    X9, [X8, #24]   ; X9 = *(X8 + 0x18) = *(0x18) = 0x200001!
    fffffff007061fa0  9100013f      ADD     X31, X9, #0     ; SP = X9 + 0x0 = 0x200001 -- !
    fffffff007061fa4  9100e109      ADD     X9, X8, #56     ; X9 = X8 + 0x38 = 0x38 -- !
    fffffff007061fa8  a9000520      STP     X0, X1, [X9]    ; *(X9) = [X0, X1]
    fffffff007061fac  a9010d22      STP     X2, X3, [X9, #16]       ; *[? +16] = [X2, X3]
    fffffff007061fb0  a9021524      STP     X4, X5, [X9, #32]       ; *[? +32] = [X4, X5]
    fffffff007061fb4  a9031d26      STP     X6, X7, [X9, #48]       ; *[? +48] = [X6, X7]
    fffffff007061fb8  f940190a      LDRi    X10, [X8, #48]  ; X10 = *(X8 + 0x30) = *(0x30) = 0x0!
    fffffff007061fbc  f100055f      CMPi    X10, #1 ; 
    fffffff007061fc0  5400000c      B.GT    0xfffffff007061fc0      ; 
    fffffff007061fc4  f100055f      CMPi    X10, #1 ; 
    fffffff007061fc8  5400000c      B.GT    0xfffffff007061fc8      ; 
    ...