#!/usr/bin/env python3 import os.path, struct, sys import argparse, glob, shutil, subprocess import smol.hackyelf as hackyelf import smol.linkmap as linkmap from smol.shared import * from smol.parse import * # TODO: support for hashes that aren't djb2 def readbyte(blob, off): return struct.unpack('= x.vaddr and addr < x.vaddr + x.memsz: aoff = addr - x.vaddr assert aoff < x.filesz, ".bss address!" return aoff + x.off error("E: Address %08x not in the static address range!" % addr) def get_needed_libs(elf, blob): assert elf.dyn is not None, "No DYNAMIC table present in the ELF file!" strtabs = [x.val for x in elf.dyn if x.tag == hackyelf.DT_STRTAB] assert len(strtabs) == 1, "Only one DT_STRTAB may be present in an ELF file." strtab = strtabs[0] return [readstr(blob,addr2off(elf, strtab+x.val))[0] for x in elf.dyn if x.tag == hackyelf.DT_NEEDED] def get_hashtbl(elf, blob, args): htaddr = None if args.map is not None: lmap = linkmap.parse(args.map.read()) tabs = [x for x in lmap.mmap if x.sym == '_symbols'] assert len(tabs) == 1, "One '_symbols' symbol must be present." htaddr = tabs[0].org elif elf.is32bit: txtoff = addr2off(elf, elf.entry) # scan for 'push IMM32' while blob[txtoff] != 0x68: txtoff = txtoff + 1 assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the hashtable address)" txtoff = txtoff + 1 #eprintf("Hash table offset: 0x%08x?" % txtoff) htaddr, ___ = readint(blob, txtoff) else: # 64-bit txtoff = addr2off(elf, elf.entry) # scan for 'push IMM32' # but the first one we'll encounter pushes the entrypoint addr! while blob[txtoff] != 0x68: txtoff = txtoff + 1 assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the hashtable or entrypoint address)" txtoff = txtoff + 1 # except, this is actually the value we're looking for when the binary # had been linked with -fuse-dnload-loader! so let's just check the # value htaddr, ___ = readint(blob, txtoff) #eprintf("ELF entry == 0x%08x" % elf.entry) if htaddr == elf.entry: # now we can look for the interesting address while blob[txtoff] != 0x68: txtoff = txtoff + 1 assert txtoff < len(blob), "wtf??? (can't find a push IMM32 instruction which pushes the hashtable address)" txtoff = txtoff + 1 #eprintf("Hash table offset: 0x%08x?" % txtoff) htaddr, ___ = readint(blob, txtoff) else: pass#eprintf("Hash table offset: 0x%08x?" % txtoff) assert htaddr is not None, "wtf? (no hashtable address)" #eprintf("Hash table address: 0x%08x" % htaddr) htoff = addr2off(elf, htaddr) #eprintf("Hash table offset: 0x%08x" % htoff) tbl = [] while True: hashsz = 2 if elf.is32bit and args.hash16 else 4 #eprintf("sym from 0x%08x" % htoff) #eprintf("sym end at 0x%08x, blob end at 0x%08x" % (htoff+hashsz, len(blob))) if htoff+hashsz > len(blob): #eprintf("htoff = 0x%08x, len=%08x" % (htoff, len(blob))) if len(blob) <= htoff and len(tbl) > 0: break #if elf.is32bit: if readbyte(blob, htoff)[0] == 0: break else: assert False, "AAAAA rest is %s" % repr(blob[htoff:]) #else: # if struct.unpack(' %s" % (x, v[x])) if len(noresolves) > 0: print("UNRESOLVED:") for x in noresolves: print("\t%08x" % x) return 0 def main(): parser = argparse.ArgumentParser() parser.add_argument('input', type=argparse.FileType('rb'), default=sys.stdin.buffer, help="input file") parser.add_argument('--cc', default=shutil.which('cc'), help="C compiler binary") parser.add_argument('--readelf', default=shutil.which('readelf'), help="readelf binary") parser.add_argument('--map', type=argparse.FileType('r'), help=\ "Get the address of the symbol hash table from the "+\ "linker map output instead of attempting to parse the"+\ " binary.") hashgrp = parser.add_mutually_exclusive_group() hashgrp.add_argument('-s', '--hash16', default=False, action='store_true', \ help="Use 16-bit (BSD2) hashes instead of 32-bit djb2 hashes. "+\ "Only usable for 32-bit output.") hashgrp.add_argument('-c', '--crc32c', default=False, action='store_true', \ help="Use Intel's crc32 intrinsic for hashing. Conflicts with `--hash16'.") hashgrp.add_argument('-B', '--break-on-zerobyte', default=False, action='store_true', \ help="Specify that the hash table ends on an entry with a LSByte of 0"+\ " (by default, smoldd checks the lowest 2 bytes).") args = parser.parse_args() return do_smoldd_run(args) if __name__ == '__main__': rv = main() if rv is None: pass else: try: sys.exit(int(rv)) except Exception: sys.exit(1)