etc.git


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()