"""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 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 af 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 xdst: 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)