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
|
"""Hunt corruption primitives whose corrupted data is CONSUMED before the canary check.
Rationale (ANALYSIS-HDCP-OVERFLOW-IS-DEFINITIVELY-DoS.md): a stack overflow is
only escalatable if a value it corrupts is loaded back BEFORE the epilogue. In
the HDCP case every post-overflow local sits BELOW the buffer, so nothing
corrupted is consumed -- which is why it is DoS regardless of a canary leak.
So the discriminator is: variable-length write to SP+D, followed by a load from
SP+off with off > D. Run headless over every module.
"""
import idapro, idc, idautils, ida_auto, re, sys
def frame_of(f,e):
a=f
while a<min(e,f+0x24):
d=idc.GetDisasm(a).replace(' ','')
m=re.match(r'SUB(?:\.W)?SP,SP,#(0x[0-9A-Fa-f]+|\d+)',d)
if m: return int(m.group(1),0)
a+=idc.get_item_size(a) or 2
return None
def var_off(dd):
s=dd.replace(' ','')
m=re.search(r'\[SP,#(0x[0-9A-Fa-f]+|\d+)\+var_([0-9A-Fa-f]+)\]',s)
if m: return int(m.group(1),0)-int(m.group(2),16)
m=re.search(r'\[SP,#(0x[0-9A-Fa-f]+|\d+)\]',s)
if m: return int(m.group(1),0)
return None
def run(path):
idapro.open_database(path, False)
ida_auto.auto_wait()
if len(list(idautils.Functions()))==0:
print(' %s: 0 functions -> forcing reanalysis'%path); idc.plan_and_wait(idc.get_inf_attr(idc.INF_MIN_EA), idc.get_inf_attr(idc.INF_MAX_EA)); ida_auto.auto_wait()
hits=[]; nf=0
for f in idautils.Functions():
nf+=1
e=idc.get_func_attr(f, idc.FUNCATTR_END)
frame=frame_of(f,e)
if not frame or frame<0x20: continue
a=f
while a<e:
d=idc.GetDisasm(a)
if ('BLX' in d or 'BL ' in d) and re.search(r'memcpy|memmove|bzero|memset',d):
dst=None; szreg=False; x=a; back=0
while x>f and back<16:
x-=2
if idc.get_item_size(x) is None: continue
dd=idc.GetDisasm(x).replace(' ','')
if dst is None:
m=re.match(r'ADD(?:\.W)?R0,SP,#(0x[0-9A-Fa-f]+|\d+)\+var_([0-9A-Fa-f]+)',dd)
if m: dst=int(m.group(1),0)-int(m.group(2),16)
if not szreg and re.match(r'MOVR(?:1|2),R\d+$',dd): szreg=True
back+=1
if dst is not None and szreg:
above=[]
x=a
while x<e:
dd=idc.GetDisasm(x)
if dd.strip().startswith('LDR'):
o=var_off(dd)
if o is not None and o>dst: above.append((x,o))
x+=idc.get_item_size(x) or 2
if above: hits.append((idc.get_func_name(f),a,dst,frame,above[:4]))
a+=idc.get_item_size(a) or 2
print(" %-14s %3d functions ; %d consumable-corruption candidate(s)"%(path.split('/')[-1],nf,len(hits)))
for nm,site,dst,frame,above in hits:
print(" %-38s write@%#x dst=SP+%#x frame=%#x above=%s"%(nm[:38],site,dst,frame,[(hex(x),hex(o)) for x,o in above]))
idapro.close_database()
for p in sys.argv[1:]: run(p)
|