1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
┌─────────────────────────────────┬───────────┬─────────────┬─────────────────┬─────┬───────────┬────────────────────────────────────────┐
│ VA range │ PA │ size │ AP │ dom │ writable? │ what it is │
├─────────────────────────────────┼───────────┼─────────────┼─────────────────┼─────┼───────────┼────────────────────────────────────────┤
│ 0x400000–0x407000 │ 0x423d000 │ 28 K │ R-X │ 6 │ ✗ │ GCP code (+ RO literal pools) │
│ 0x407000–0x417000 │ 0x4244000 │ 64 K │ RW- │ 6 │ ✓ │ .data + .bss │
│ 0x417000–0x418000 │ — │ 4 K │ — │ — │ ✗ │ guard (fault → wedges ring) │
│ 0x418000–0x41c000 │ 0x4254000 │ 16 K │ RW- │ 6 │ ✓ │ stack, SP0 = 0x41c000 │
│ 0x41c000–0x41d000 │ — │ 4 K │ — │ — │ ✗ │ guard │
│ 0x41d000–0x421000 │ 0x4258000 │ 16 K │ RW- │ 6 │ ✓ │ heap │
│ 0x421000–0x422000 │ — │ 4 K │ — │ — │ ✗ │ guard │
│ 0x422000–0x423000 │ 0x4260000 │ 4 K │ RW- │ 6 │ ✓ │ process page — contents never examined │
│ 0xf00000–0xf01000 │ 0x41c1000 │ 4 K │ R-X │ 7 │ ✗ │ tlApi library (the heap allocator) │
│ 0x04024000 +len │ host VRAM │ cmd_buf_len │ RW- │ 5 │ ✓ │ host-shared cmd buffer (ta_params) │
│ 0x200000–0x20f000 │ 0x12000 │ — │ R-X/RW-/**RWX** │ 1 │ ✗ DACR │ sysdrv — mapped but domain-blocked │
│ 0xe00000–0xeb7000 │ 0x4106000 │ — │ R-X/RW- │ 2 │ ✗ DACR │ ASD — mapped but domain-blocked │
│ 0x0–0x10000, 0x50000–0x88000, … │ — │ — │ --- (user) │ 0 │ ✗ │ sOS kernel (priv-only) │
└─────────────────────────────────┴───────────┴─────────────┴─────────────────┴─────┴───────────┴────────────────────────────────────────┘
5 writable regions total: .data/.bss, stack, heap, process page, shared cmd buffer.
2. .data — [0x407000, 0x415a26), initialized
┌───────────────────┬───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ VA range │ size │ contents │
├───────────────────┼───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 0x407000–0x408926 │ 6.3 K │ PlayReady CertificateCollection XML — device cert chain (the 6 keys cmd02 returns) │
│ 0x408926–0x415a26 │ 53 K │ 44 ENC key blobs, 0x4C0 each — by wrapping-key-id: 0x67ff5db3×32, 0x5439fea3×9, 0xfb6a4205×3. Ciphertext only │
│ 0x415a26–0x416000 │ — │ file tail, zeros │
└───────────────────┴───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
3. .bss globals — GCP's entire mutable global set is 9 cells
┌──────────┬────────────────────┬─────────────────────────────────────────────────────────────────────┐
│ VA │ referenced from │ what it is │
├──────────┼────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ 0x416000 │ 0x40011c, 0x4022cc │ runtime-globals base (gcp_runtime_globals) │
│ 0x416014 │ 0x405748 │ tlApiMalloc heap BASE cache — measured = 0x0041d000 │
│ 0x416018 │ (same pair [1]) │ heap SIZE cache │
│ 0x41601c │ 0x4042b0 │ unidentified │
│ 0x4164dc │ 0x4042ac │ FE08 __h4 scratch/guard │
│ 0x41699c │ 0x404264 │ FE08 __h3/__h4 scratch/guard │
│ 0x416e5c │ 0x40293c │ resolved tlApi library entry (→ 0xf00100); the only cell holding it │
│ 0x416e60 │ 0x404054 │ shared handle/state (cmdFE02, rpc_103B) │
│ 0x416e64 │ 0x40010c │ .bss top marker │
└──────────┴────────────────────┴─────────────────────────────────────────────────────────────────────┘
Everything else in those ~6 K is unused zeroed space — nothing reads it.
⚠️ 0x416014 was previously mislabelled "session-context pointer" (Add.80 correction). There is no ctx pointer in .bss — sOS holds it and passes it as
a1 per invoke.
4. Heap — [0x41d000, 0x421000)
```
0x41d000 ┬ IN-BAND allocator header (0x30 B) ← writable, == (ctx - 0x30)
│ +0x00/+0x04/+0x18 = 0x0041ee40 next-free / chunk end
│ +0x08 = 0x21a0 ? +0x20 = 0x5600 ? (UNEXPLAINED: 0x5600 > the 0x4000 heap)
│ +0x0c = 0x41d010 +0x10 = 0x41d000 link-ish
│ +0x1c = 0x00001e10 this chunk's size ✓
0x41d030 ┴ ctx payload (0x1e10) ───────────────────────────────────────────
```
┌────────────┬───────────────────┬────────┬─────────────────────────────────────────────────────────────────────────────────────────────┐
│ struct │ base │ stride │ fields (slot i) │
├────────────┼───────────────────┼────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤
│ drm[32] │ 0x41d030 │ 0x18 │ handle@+0, state@+4, body[16]@+8 │
│ auth[16] │ 0x41d330 │ 0x50 │ handle@+0, state@+4, body[72]@+8 │
│ crypto[64] │ 0x41d830 │ 0x58 │ handle@+0, unk04@+4, key(u64)@+8, field10@+0x10, decode_handle@+0x18, state@+0x20, rest[52] │
│ tail │ 0x41ee30 │ — │ asd_refcount, unk1E04@+4, handle_counter@+8 │
│ free heap │ 0x41ee40–0x421000 │ — │ ~8.9 K, never allocated │
└────────────┴───────────────────┴────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘
TA heap metadata layout
(heap [0x41d000, 0x421000); allocator = user-mode library 0xF00000)
```
0x41d000 ┌──────────────────────────────┐
│ ARENA DESCRIPTOR (0x10 B) │
0x41d010 ├──────────────────────────────┤ ◄── H (chunk header = ptr
− 0x20)
│ CHUNK HEADER (0x20 B) │
0x41d030 ├──────────────────────────────┤ ◄── ctx (the pointer
tlApiMalloc returned)
│ │
│ PAYLOAD: gcp_ctx (0x1e10) │
│ drm[32] auth[16] crypto[64]│
│ │
0x41ee40 ├──────────────────────────────┤ ◄── next chunk (the free
remainder)
│ CHUNK HEADER (0x20 B) │ flag=1 (free), size
0x21a0
0x41ee60 ├──────────────────────────────┤
│ free space (0x21a0) │
0x421000 └──────────────────────────────┘
```
Arena descriptor — 0x41d000, 0x10 bytes
┌───────┬──────────┬────────────┬──────────────────────────────────────┐
│ off │ abs │ measured │ role (from free()/malloc disasm) │
├───────┼──────────┼────────────┼──────────────────────────────────────┤
│ +0x00 │ 0x41d000 │ 0x0041ee40 │ list head; written str r2,[r1] when │
│ │ │ │ a coalesce empties the chain │
├───────┼──────────┼────────────┼──────────────────────────────────────┤
│ +0x04 │ 0x41d004 │ 0x0041ee40 │ free-list head; read ldr r2,[r1,#4], │
│ │ │ │ compared against H, rewritten │
│ │ │ │ [r1,#4]=r2 │
├───────┼──────────┼────────────┼──────────────────────────────────────┤
│ +0x08 │ 0x41d008 │ 0x000021a0 │ total free bytes — target of │
│ │ │ │ *(arena+8) += size+0x20 │
├───────┼──────────┼────────────┼──────────────────────────────────────┤
│ +0x0c │ 0x41d00c │ 0x0041d010 │ fallback/first-chunk pointer (read │
│ │ │ │ when the +0x04 chain hits 0) — │
│ │ │ │ points at H │
└───────┴──────────┴────────────┴──────────────────────────────────────┘
✔ Cross-check: 0x421000 − 0x41ee40 − 0x20 = 0x21a0 — exactly the +0x08
value. Confirms it's the free-byte accounting.
Chunk header — H = ptr − 0x20, 0x20 bytes
┌───────────┬───────────┬────────────┬──────────────────────┬──────────┐
│ off │ abs (ctx │ measured │ role │ evidence │
│ │ chunk) │ │ │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x00 │ 0x41d010 │ 0x0041d000 │ arena back-pointer. │ free │
│ │ │ │ r1 = [ptr−0x20], │ 0xf004dc │
│ │ │ │ then *(r1+8) += │ │
│ │ │ │ size+0x20 — │ │
│ │ │ │ dereferenced │ │
│ │ │ │ unconditionally, │ │
│ │ │ │ first │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x04 │ 0x41d014 │ 0x00000000 │ prev chunk. If ≠0 │ free │
│ │ │ │ and │ 0xf00502 │
│ │ │ │ byte[prev+0x10]≠0 → │ │
│ │ │ │ backward coalesce: │ │
│ │ │ │ *(prev+0xc) += …, │ │
│ │ │ │ *(prev+8) = next │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x08 │ 0x41d018 │ 0x0041ee40 │ next chunk. │ free │
│ │ │ │ *(next+4) = prev ← │ 0xf00514 │
│ │ │ │ the unlink write; │ │
│ │ │ │ forward coalesce │ │
│ │ │ │ reads │ │
│ │ │ │ byte[next+0x10] │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x0c │ 0x41d01c │ 0x00001e10 │ size (= the 0x1E10 │ free │
│ │ │ │ requested). Addend │ 0xf004e0 │
│ │ │ │ in both += writes │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x10 │ 0x41d020 │ 0x00 │ flag: 0 = allocated, │ free │
│ │ │ │ 1 = free. Validated │ 0xf005de │
│ │ │ │ (must be 0), set to │ / │
│ │ │ │ 1 by free │ 0xf004ee │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x11 │ 0x41d021 │ 0x56 │ MAGIC 'V'. Validated │ free │
│ │ │ │ by free and realloc; │ 0xf005da │
│ │ │ │ zeroed on coalesce │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x12..13 │ 0x41d022 │ 0x0000 │ unknown / padding │ — │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x14 │ 0x41d024 │ unread │ flags, replayed into │ realloc │
│ │ │ │ malloc by realloc │ 0xf00652 │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x18 │ 0x41d028 │ unread │ size used as memcpy │ realloc │
│ │ │ │ length by realloc │ 0xf0064a │
│ │ │ │ (min(old,new)) │ │
├───────────┼───────────┼────────────┼──────────────────────┼──────────┤
│ +0x1c │ 0x41d02c │ unread │ unknown │ — │
└───────────┴───────────┴────────────┴──────────────────────┴──────────┘
|