With Lina✨ & Cyan💎
#include <iostream>
using namespace std;
int main() {
int a = 0;
void *p = &a;
cout << "&a is " << p << " nya!" << endl;
}
&a is 0x7fffc6ba5ddc nya!
This is a virtual address!
↓
0x7fffc6ba5ddc
↓
0x814745ddc
↑
This is the physical address!
40 bits for virtual address (1 TiB)
16 KiB pages → 67,108,864 pages total
8 bytes per page address
=
512 MiB page table!!!!
Translation Table Base Address Table
00: -01: Wait For Idle02: Wait 203: Doorbell04: Write 8-bit reg05: Write 32-bit reg06: Write 64-bit reg07: -08: Read 8-bit reg09: Read 32-bit reg0a: Read 64-bit reg0b: Wait 32-bit reg0c: Wait 64-bit reg0d: -0e: Store 32-bit
0f: Store 64-bit
10: Load 32-bit
11: Load 64-bit
12: ?13: ?14: ?15: Test (If)
16: Logic op
17: Add 16-bit
18: Retire19: Timestamp1a: KTrace1b: ?1c: ?1d: ?1e: Jump (Cond)
1f: ?20: ?21: ?22: Start Vertex23: Finish Vertex24: Start Fragment25: Finish Fragment26: Start Blit27: Finish Blit28: ?29: Start Compute2a: Finish Compute2b: ?2c: ?Indirect read/writes?R0 = *R1;
Multiply?R1 = 10 × R0;
But...
They forgot to blocklist that one!!!!
GPU can now read/write page tables directly!!
We can read/write stuff from the microsequence, but we can't call the uPPL...
We can't modify the firmware code directly...
Return-oriented Programming (ROP)!
g_calltwo: Call two functions...
But the first argument is a pointer to a pointer to a pointer to the function
And the second one is a pointer to 0x70 before a pointer to the function
g_store: Store a value to memory...
But it actually stores to the address you pass plus 0xa78
It's hard to demo this all working in practice...
Challenge Accepted!

| R0 | 0x0000000000000000 | R1 | 0x0000000000000000 |
| X0 | |
| X1 | |
| X2 | |
| X3 | |
| X4 | |
| X5 | |
| X6 | |
| X7 | |
| X8 | |
| X9 | |
| X10 | |
| X11 | |
| X12 | |
| X13 | |
| X14 | |
| X15 | |
| X16 | |
| X17 | |
| X18 | |
| X19 | |
| X20 | |
| X21 | |
| X22 | |
| X23 | |
| X24 | |
| X25 | |
| X26 | |
| X27 | |
| X28 | |
| X29 | |
| LR | |
| SP | |
| PC |