etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
a64dis.py (3232B)
1 #!/usr/bin/env python3 2 """Minimal AArch64 static-analysis helper for stripped Android ELFs. 3 Usage: 4 a64dis.py <elf> imports # dynsym imported funcs 5 a64dis.py <elf> str <substring> # find string + its file vaddr(s) 6 a64dis.py <elf> xref <hexvaddr> # find ADRP+ADD code refs to a vaddr 7 a64dis.py <elf> dis <hexvaddr> [count] # disassemble count insns at vaddr 8 Addresses are file vaddrs (p_vaddr based), not rebased. Add Ghidra's image base to match. 9 """ 10 11 import sys, struct 12 from capstone import Cs, CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN 13 from elftools.elf.elffile import ELFFile 14 15 def load(path): 16 f=open(path,'rb'); e=ELFFile(f); return f,e 17 18 def seg_for(e,va): 19 for s in e.iter_segments(): 20 if s['p_type']=='PT_LOAD' and s['p_vaddr']<=va<s['p_vaddr']+s['p_filesz']: 21 return s 22 return None 23 24 def read_va(e,va,n): 25 s=seg_for(e,va) 26 if not s: return None 27 off=s['p_offset']+(va-s['p_vaddr']) 28 e.stream.seek(off); return e.stream.read(n) 29 30 def imports(e): 31 ds=e.get_section_by_name('.dynsym') 32 out=[] 33 for sym in ds.iter_symbols(): 34 if sym.name and sym['st_shndx']=='SHN_UNDEF': out.append(sym.name) 35 print('\n'.join(sorted(set(out)))) 36 37 def find_str(e,sub): 38 sub=sub.encode() 39 for sec in e.iter_sections(): 40 if sec['sh_type']=='SHT_NOBITS' or not sec['sh_flags']&0x2: continue 41 data=sec.data(); base=sec['sh_addr'] 42 i=0 43 while True: 44 j=data.find(sub,i) 45 if j<0: break 46 k=j 47 while k>0 and 0x20<=data[k-1]<0x7f: k-=1 48 end=data.find(b'\x00',j) 49 s=data[k:end if end>0 else j+len(sub)] 50 print(f"0x{base+k:x} {sec.name} {s[:80]!r}") 51 i=j+len(sub) 52 53 def disasm(e,va,count): 54 data=read_va(e,va,count*4) 55 md=Cs(CS_ARCH_ARM64,CS_MODE_LITTLE_ENDIAN); md.detail=False 56 for ins in md.disasm(data,va): 57 print(f"0x{ins.address:x}: {ins.mnemonic:8s} {ins.op_str}") 58 59 def xref(e,target): 60 md=Cs(CS_ARCH_ARM64,CS_MODE_LITTLE_ENDIAN) 61 for s in e.iter_segments(): 62 if s['p_type']!='PT_LOAD' or not (s['p_flags']&1): continue 63 va=s['p_vaddr']; data=s.data() 64 adrp={} # reg -> page base 65 for ins in md.disasm(data,va): 66 if ins.mnemonic=='adrp': 67 try: 68 r,imm=ins.op_str.split(', ') 69 adrp[r]=int(imm,0) 70 except: pass 71 elif ins.mnemonic in ('add','ldr') and ',' in ins.op_str: 72 parts=[p.strip() for p in ins.op_str.split(',')] 73 if len(parts)>=3 and parts[1] in adrp and parts[2].startswith('#'): 74 try: val=adrp[parts[1]]+int(parts[2][1:],0) 75 except: continue 76 if val==target: 77 print(f"0x{ins.address:x}: {ins.mnemonic} {ins.op_str} -> 0x{val:x}") 78 79 def main(): 80 path=sys.argv[1]; cmd=sys.argv[2]; f,e=load(path) 81 if cmd=='imports': imports(e) 82 elif cmd=='str': find_str(e,sys.argv[3]) 83 elif cmd=='dis': disasm(e,int(sys.argv[3],0), int(sys.argv[4]) if len(sys.argv)>4 else 40) 84 elif cmd=='xref': xref(e,int(sys.argv[3],0)) 85 else: print(__doc__) 86 87 if __name__=='__main__': main()