import math from collections import Counter from .formats import FormatError, load_commands, parse_header, unpack ELF_SECTION_TYPES = { 0: "NULL", 1: "PROGBITS", 2: "SYMTAB", 3: "STRTAB", 4: "RELA", 5: "HASH", 6: "DYNAMIC", 7: "NOTE", 8: "NOBITS", 9: "REL", 11: "DYNSYM", 14: "INIT_ARRAY", 15: "FINI_ARRAY", 17: "GROUP", 0x6FFF4C03: "LLVM_ADDRSIG", 0x6FFFFFF6: "GNU_HASH", 0x6FFFFFFE: "VERNEED", 0x6FFFFFFF: "VERSYM", } ELF_SEGMENT_TYPES = { 0: "NULL", 1: "LOAD", 2: "DYNAMIC", 3: "INTERP", 4: "NOTE", 6: "PHDR", 7: "TLS", 0x6474E550: "GNU_EH_FRAME", 0x6474E551: "GNU_STACK", 0x6474E552: "GNU_RELRO", 0x6474E553: "GNU_PROPERTY", } MACHO_SECTION_TYPES = { 0: "regular", 1: "zerofill", 2: "cstring_literals", 6: "non_lazy_symbol_pointers", 7: "lazy_symbol_pointers", 8: "symbol_stubs", 9: "mod_init_func_pointers", 0xC: "gb_zerofill", 0x11: "thread_local_regular", 0x12: "thread_local_zerofill", 0x13: "thread_local_variables", } MACHO_ZEROFILL = {1, 0xC, 0x12} MACHO_SEGMENT_PERMS = {"__TEXT": "r-x", "__DATA": "rw-", "__DATA_CONST": "rw-", "__LINKEDIT": "r--"} def shannon_entropy(data): if not data: return 0.0 total = len(data) value = -sum(count / total * math.log2(count / total) for count in Counter(data).values()) return round(value + 0.0, 4) def perms(read, write, execute): return ("r" if read else "-") + ("w" if write else "-") + ("x" if execute else "-") def _entry(index, name, segment, kind, addr, offset, size, file_size, permissions, code, data): if file_size and offset + file_size > len(data): raise FormatError(f"section {name} runs past the end of the file") return { "index": index, "name": name, "segment": segment, "type": kind, "addr": addr, "offset": offset, "size": size, "file_size": file_size, "perms": permissions, "code": bool(code), "entropy": shannon_entropy(data[offset:offset + file_size]), } def _elf_sections(data, header): fields = header["fields"] raw = [unpack("= end or stop < 0: raise FormatError(f"string at {start:#x} is not terminated inside its table") return data[start:stop].decode("utf-8", "replace") def coff_string_table(data, fields): start = fields["pointer_to_symbol_table"] + 18 * fields["number_of_symbols"] if not fields["pointer_to_symbol_table"]: return start, start (size,) = unpack(" len(data): raise FormatError(f"program header {index} runs past the end of the file") result.append({ "name": ELF_SEGMENT_TYPES.get(kind, f"{kind:#x}"), "addr": vaddr, "size": memsz, "offset": offset, "file_size": filesz, "perms": perms(flags & 4, flags & 2, flags & 1), "max_perms": None, "entropy": shannon_entropy(data[offset:offset + filesz]), }) elif header["format"] == "macho64": for segment in macho_segments_raw(data): if segment["fileoff"] + segment["filesize"] > len(data): raise FormatError(f"segment {segment['name']} runs past the end of the file") result.append({ "name": segment["name"], "addr": segment["vmaddr"], "size": segment["vmsize"], "offset": segment["fileoff"], "file_size": segment["filesize"], "perms": _prot(segment["initprot"]), "max_perms": _prot(segment["maxprot"]), "entropy": shannon_entropy(data[segment["fileoff"]:segment["fileoff"] + segment["filesize"]]), }) return result def section_at_offset(section_list, offset): best = None for section in section_list: if section["file_size"] and section["offset"] <= offset < section["offset"] + section["file_size"]: if best is None or section["file_size"] < best["file_size"]: best = section return best def section_at_address(section_list, address): for section in section_list: if section["size"] and section["addr"] <= address < section["addr"] + section["size"]: return section return None