aboutsummaryrefslogtreecommitdiffstats
path: root/fw/gadgets/README.md
blob: 534428b3387f1a10cb53ff1fea9f60d95e5420fc (plain) (blame)
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
# Branch-target hunting material (GCP 22.0.0.77 + tlApi library)

Produced for the control-flow work in FINDING-GCP-FE09-...md Add.89-94.

## Binaries (offset == VA - base, so you can disassemble at the right address)
| file | base VA | notes |
|---|---|---|
| `tlapi_lib_0xf00000.bin` | `0xf00000` | the 4 KB tlApi library page, **live dump from the emulator** (mapped R-X, dom 7). Code occupies `0xf00101-0xf00790`; `"$PS1"` magic + len `0x590` at `+0x10`. ARM veneer at `0xf00100`, Thumb body from `0xf00114`. |
| `gcp_22.0.0.77_code_0x400000.bin` | `0x400000` | GCP code region `0x400000-0x407000` (R-X). **`0x400000-0x400100` is SYNTHETIC ZERO PADDING** — the real content is unknown (Add.92 observed an ARM store executing at `0x400044`, so it is NOT all zero). Body proper starts at `0x400100`. |
| `gcp_22.0.0.77_image_0x400000.bin` | `0x400000` | same but the whole image (code+.data+.bss span, to `0x416100`). |

## Disassembly (linear sweep, resynced every instruction so literal pools don't desync)
`tlapi_lib.thumb.txt`, `tlapi_lib.arm.txt`, `gcp_code.thumb.txt`, `gcp_code.arm.txt`

## Gadget catalogues
`GADGETS.txt`        - control-transfer / sp-write / svc sites, per region per mode
`GADGET-CLASSES.txt` - grouped: PC-from-r4, PC-from-mem, SP-from-reg, add-sp+pop-pc, bx/blx r4|r0

## Entry state when our planted branch is taken
`UNLOAD_TA -> TA_CloseSessionEntryPoint -> gcp_tlApiFree -> gcp_call_once_init -> bx ip`
    r0 = sp = &requestBlock (tlApiFree frame)   r1 = r2 = r3 = 0
    r4 = ctx pointer (0x41d030)                 r5 = sp
    lr = 0x405617 (into tlApiFree)              Z=1, V=0  (from `movs r3,#0`)
    requestBlock: [sp]=0x22 [sp+4]=2 [sp+8..0x28)=0 [sp+0x38]=ctx ; rest = stale stack
NOTE: branching INTO an IT block clears ITSTATE, so e.g. `moveq sp,r4` at 0x40371c executes
unconditionally as `mov sp,r4` (encoding 0xa546 is a plain MOV).

## What we are looking for
A gadget that sources PC or SP from memory we can fill:
  * carvable heap: any address >= heapbase+0x7f = 0x41d07f (= ctx+0x4f) -- arbitrary bytes,
    ~256 CodecToken stamps per byte (descending trailing-byte carve, Add.91)
  * host-written ctx: FE07 puts arg18 at auth[i]+4 and arg10 (8B) at auth[i]+0x10 for 16 slots
    => ~192 bytes of CHOSEN data at ctx+0x300 + 0x50*i, no lottery needed
So the wanted shapes are `mov sp,r4 ; add sp,#>=0x300 ; pop {...,pc}` or `ldr pc,[r4,#imm>=0x50]`.
Best found so far: 0x0040371c `mov sp,r4 ; pop.w {r4,r5,r6,r7,r8,sb,sl,fp,pc}` -- pivots to ctx but
takes PC from ctx+0x20, which is NOT carve-writable (A=target-0x7f lands in the guard page), so only
blob-window values fit there and the only mapped+executable one is 0x00400000.
Dead ends: the 61 `blx r4` / `bx r4` sites (r4=ctx, heap is RW-/XN).

## BUILD SENSITIVITY (read this first)
All binaries here are **22.0.0.77** (`ta_22-0-0-77_7c426e7f`), the build running on our card.
Gadget addresses do NOT carry across builds. Measured at the two addresses used above:

| build | 0x40363a | 0x40371c |
|---|---|---|
| 22.0.0.53/.57/.60 | `f104` add.w r0,r4,#0x30 | `a14f` adr r1,#0x13c |
| 22.0.0.62/.64 | `f5b1` cmp.w r1,#0x100 | `f8cd` str.w r0,[sp,#0x79c] |
| **22.0.0.68/.71/.73/.77** | **`46a5` mov sp,r4** | **`46a5` mov sp,r4 ; pop.w {r4-r8,sb,sl,fp,pc}** |
| 22.0.4.50 | `2210` movs r2,#0x10 | `2010` movs r0,#0x10 ; str r0,[sp,#0x1c] |

So the `mov sp,r4 ; pop {...,pc}` pivot exists only on the .68-.77 line. Note the emulator harness
(`fuzz_newtas.pick_container`) defaults "gcp" to **ta_22-0-4-50**, which is how one easily ends up
disassembling the wrong image -- the same mistake recorded in FINDING Add.49 and Add.55.

## CAVEAT on the gadget catalogues
`GADGETS.txt`, `GADGET-CLASSES.txt` and the `*.txt` sweeps decode at EVERY 2-byte offset, so they
intentionally include UNINTENDED (misaligned) decodes. Those are valid ROP gadgets -- any halfword is
a legal branch target -- but they are not the intended instruction stream. Re-check any candidate
with a sequential decode before relying on it. (0x40371c is valid in both senses: `bfc r4,#0,#0xb`
at 0x403636 is 4 bytes, so 0x40363a/0x40371c are genuine instruction boundaries.)