"""Minimal PE image and COFF object readers (i386 only, no dependencies).""" import struct # i386 COFF relocation types REL_I386_DIR32 = 0x06 REL_I386_DIR32NB = 0x07 REL_I386_SECTION = 0x0A REL_I386_SECREL = 0x0B REL_I386_REL32 = 0x14 IMAGE_SCN_CNT_CODE = 0x20 IMAGE_SCN_LNK_COMDAT = 0x1000 class PEImage: def __init__(self, path): with open(path, "rb") as f: self.data = f.read() d = self.data pe = struct.unpack_from("> 4) == 2 @property def is_external(self): return self.storage == 2 class CoffSection: __slots__ = ("index", "name", "data", "chars", "relocs") def __init__(self, index, name, data, chars, relocs): self.index, self.name, self.data, self.chars, self.relocs = index, name, data, chars, relocs IMAGE_COMDAT_SELECT_ANY = 2 class CoffObject: def __init__(self, path): with open(path, "rb") as f: d = f.read() self.path = path machine, nsec, _, symptr, nsym, optsize, _ = struct.unpack_from(" IMAGE_COMDAT_SELECT_* i = 0 while i < nsym: o = symptr + i * 18 raw = d[o:o + 8] value, secnum, type_, storage, naux = struct.unpack_from(" 0: # section definition self.comdat_select[secnum] = d[o + 18 + 14] i += 1 + naux self.sections = {} for s in range(nsec): o = 20 + optsize + s * 40 raw = d[o:o + 8] name = raw.rstrip(b"\0").decode("latin-1") if name.startswith("/"): off = int(name[1:]) name = d[strtab + off:d.index(b"\0", strtab + off)].decode("latin-1") size, rawptr, relptr, _, nrel, _, chars = struct.unpack_from(" 0 and sym.is_function: by_sec.setdefault(sym.section, []).append(sym) for secnum, syms in by_sec.items(): sec = self.sections[secnum] if not sec.chars & IMAGE_SCN_CNT_CODE: continue syms.sort(key=lambda s: s.value) for i, sym in enumerate(syms): end = syms[i + 1].value if i + 1 < len(syms) else len(sec.data) yield sym, sec, sym.value, end