draugr.git


draugr

~ single ELF file analysis tool
git clone https://github.com/djnnvx/draugr.git
log | files | refs | archive | README

printer.odin (8880B)

      1 package elf
      2 
      3 import "core:fmt"
      4 import "core:os"
      5 
      6 print_info :: proc(info: ^ELF_Info, verbose: bool) {
      7 	f := os.stdout
      8 
      9 	fmt.fprintf(f, "ELF File Information\n")
     10 	fmt.fprintf(f, "====================\n\n")
     11 
     12 	class_bits := 32
     13 	if info.is_64bit {
     14 		class_bits = 64
     15 	}
     16 	fmt.fprintf(f, "Class:     ELF%d\n", class_bits)
     17 	fmt.fprintf(f, "Type:      %s\n", type_to_str(info.header.type))
     18 	fmt.fprintf(f, "Machine:   %s\n", machine_to_str(info.header.machine))
     19 	fmt.fprintf(f, "Entry:     0x%016x\n", info.header.entry)
     20 	fmt.fprintf(f, "Flags:     0x%08x\n", info.header.flags)
     21 
     22 	pie_str := "no"
     23 	if info.is_pie {
     24 		pie_str = "yes"
     25 	}
     26 	dyn_str := "no"
     27 	if info.is_dynamic {
     28 		dyn_str = "yes"
     29 	}
     30 	fmt.fprintf(f, "PIE:       %s\n", pie_str)
     31 	fmt.fprintf(f, "Dynamic:   %s\n", dyn_str)
     32 	if info.interp_offset != 0 {
     33 		fmt.fprintf(f, "Interpreter offset: 0x%x\n", info.interp_offset)
     34 	}
     35 
     36 	if info.soname != "" {
     37 		fmt.fprintf(f, "SONAME:    %s\n", info.soname)
     38 	}
     39 	if info.rpath != "" {
     40 		fmt.fprintf(f, "RPATH:     %s\n", info.rpath)
     41 	}
     42 	if info.runpath != "" {
     43 		fmt.fprintf(f, "RUNPATH:   %s\n", info.runpath)
     44 	}
     45 	if len(info.needed) > 0 {
     46 		fmt.fprintf(f, "\nNeeded Libraries: %d\n", len(info.needed))
     47 		for lib in info.needed {
     48 			fmt.fprintf(f, "  %s\n", lib)
     49 		}
     50 	}
     51 
     52 	fmt.fprintf(f, "\n")
     53 	fmt.fprintf(f, "Program Headers: %d\n", len(info.program_hdrs))
     54 	fmt.fprintf(f, "Section Headers: %d\n", len(info.section_hdrs))
     55 	fmt.fprintf(f, "Symbols (static): %d\n", len(info.symbols))
     56 	fmt.fprintf(f, "Symbols (dynamic): %d\n", len(info.dyn_symbols))
     57 
     58 	if verbose {
     59 		if len(info.dyn_entries) > 0 {
     60 			fmt.fprintf(f, "\nDynamic Section: %d entries\n", len(info.dyn_entries))
     61 			for e in info.dyn_entries {
     62 				if name := dyn_string(info, e); name != "" {
     63 					fmt.fprintf(f, "  %-14s %s\n", dyn_tag_str(e.tag), name)
     64 				} else {
     65 					fmt.fprintf(f, "  %-14s 0x%x\n", dyn_tag_str(e.tag), e.val)
     66 				}
     67 			}
     68 		}
     69 
     70 		fmt.fprintf(f, "\n")
     71 		fmt.fprintf(f, "Detailed Sections:\n")
     72 		for i := 0; i < len(info.section_hdrs); i += 1 {
     73 			shdr := info.section_hdrs[i]
     74 			name := get_section_name(info, shdr)
     75 			if name == "" {
     76 				name = fmt.tprintf("[%d]", i)
     77 			}
     78 			fmt.fprintf(f, "  [%2d] %-20s %-12s 0x%08x 0x%06x 0x%x\n",
     79 				i, name, section_type_str(shdr.type), shdr.addr, shdr.offset, shdr.size)
     80 		}
     81 
     82 		if len(info.symbols) > 0 {
     83 			fmt.fprintf(f, "\nStatic Symbols (first 20):\n")
     84 			count := 20
     85 			if len(info.symbols) < count {
     86 				count = len(info.symbols)
     87 			}
     88 			for i := 0; i < count; i += 1 {
     89 				sym := info.symbols[i]
     90 				sym_type := symbol_type_str(sym.type)
     91 				sym_bind := symbol_bind_str(sym.bind)
     92 				fmt.fprintf(f, "  %-30s %s %-8s 0x%016x (size: %d)\n",
     93 					sym.name, sym_bind, sym_type, sym.value, sym.size)
     94 			}
     95 		}
     96 
     97 		if len(info.dyn_symbols) > 0 {
     98 			fmt.fprintf(f, "\nDynamic Symbols (first 20):\n")
     99 			count := 20
    100 			if len(info.dyn_symbols) < count {
    101 				count = len(info.dyn_symbols)
    102 			}
    103 			for i := 0; i < count; i += 1 {
    104 				sym := info.dyn_symbols[i]
    105 				sym_type := symbol_type_str(sym.type)
    106 				sym_bind := symbol_bind_str(sym.bind)
    107 				fmt.fprintf(f, "  %-30s %s %-8s 0x%016x (size: %d)\n",
    108 					sym.name, sym_bind, sym_type, sym.value, sym.size)
    109 			}
    110 		}
    111 	}
    112 }
    113 
    114 type_to_str :: proc(t: u16) -> string {
    115 	switch t {
    116 	case ET_REL:  return "REL (Relocatable)"
    117 	case ET_EXEC: return "EXEC (Executable)"
    118 	case ET_DYN:  return "DYN (Shared Object/PIE)"
    119 	case ET_CORE: return "CORE"
    120 	}
    121 	return fmt.tprintf("UNKNOWN (%d)", t)
    122 }
    123 
    124 machine_to_str :: proc(m: u16) -> string {
    125 	switch m {
    126 	case EM_SPARC:        return "SPARC"
    127 	case EM_386:          return "x86 (i386)"
    128 	case EM_68K:          return "m68k"
    129 	case EM_MIPS:         return "MIPS"
    130 	case EM_MIPS_RS3_LE:  return "MIPS RS3 LE"
    131 	case EM_PARISC:       return "PA-RISC"
    132 	case EM_PPC:          return "PowerPC"
    133 	case EM_PPC64:        return "PowerPC64"
    134 	case EM_S390:         return "S/390"
    135 	case EM_ARM:          return "ARM"
    136 	case EM_SH:           return "SuperH"
    137 	case EM_SPARCV9:      return "SPARC V9"
    138 	case EM_ARC:          return "ARC"
    139 	case EM_IA_64:        return "IA-64"
    140 	case EM_X86_64:       return "x86-64"
    141 	case EM_CRIS:         return "CRIS"
    142 	case EM_AVR:          return "AVR"
    143 	case EM_FR30:         return "FR30"
    144 	case EM_V850:         return "V850"
    145 	case EM_M32R:         return "M32R"
    146 	case EM_OPENRISC:     return "OpenRISC"
    147 	case EM_ARC_COMPACT:  return "ARCompact"
    148 	case EM_XTENSA:       return "Xtensa"
    149 	case EM_MSP430:       return "MSP430"
    150 	case EM_BLACKFIN:     return "Blackfin"
    151 	case EM_UNICORE:      return "UniCore"
    152 	case EM_TI_C6000:     return "TI C6000"
    153 	case EM_NDS32:        return "NDS32"
    154 	case EM_AARCH64:      return "AArch64"
    155 	case EM_MICROBLAZE:   return "MicroBlaze"
    156 	case EM_TILEGX:       return "TILE-Gx"
    157 	case EM_ARC_COMPACT2: return "ARCv2"
    158 	case EM_RISCV:        return "RISC-V"
    159 	case EM_BPF:          return "BPF"
    160 	case EM_CSKY:         return "C-SKY"
    161 	case EM_LOONGARCH:    return "LoongArch"
    162 	}
    163 	return fmt.tprintf("Unknown (%d)", m)
    164 }
    165 
    166 section_type_str :: proc(t: u32) -> string {
    167 	switch t {
    168 	case SHT_NULL:     return "NULL"
    169 	case SHT_PROGBITS: return "PROGBITS"
    170 	case SHT_SYMTAB:   return "SYMTAB"
    171 	case SHT_STRTAB:   return "STRTAB"
    172 	case SHT_RELA:     return "RELA"
    173 	case SHT_HASH:     return "HASH"
    174 	case SHT_DYNAMIC:  return "DYNAMIC"
    175 	case SHT_NOTE:     return "NOTE"
    176 	case SHT_NOBITS:   return "NOBITS"
    177 	case SHT_REL:      return "REL"
    178 	case SHT_DYNSYM:   return "DYNSYM"
    179 	case SHT_SHLIB:          return "SHLIB"
    180 	case SHT_INIT_ARRAY:     return "INIT_ARRAY"
    181 	case SHT_FINI_ARRAY:     return "FINI_ARRAY"
    182 	case SHT_PREINIT_ARRAY:  return "PREINIT_ARRAY"
    183 	case SHT_GROUP:          return "GROUP"
    184 	case SHT_SYMTAB_SHNDX:   return "SYMTAB SECTION INDICES"
    185 	case SHT_GNU_ATTRIBUTES: return "GNU_ATTRIBUTES"
    186 	case SHT_GNU_HASH:       return "GNU_HASH"
    187 	case SHT_GNU_VERDEF:     return "VERDEF"
    188 	case SHT_GNU_VERNEED:    return "VERNEED"
    189 	case SHT_GNU_VERSYM:     return "VERSYM"
    190 	}
    191 	return fmt.tprintf("0x%x", t)
    192 }
    193 
    194 symbol_type_str :: proc(t: u8) -> string {
    195 	switch t {
    196 	case STT_NOTYPE:  return "NOTYPE"
    197 	case STT_OBJECT:  return "OBJECT"
    198 	case STT_FUNC:    return "FUNC"
    199 	case STT_SECTION: return "SECTION"
    200 	case STT_FILE:    return "FILE"
    201 	}
    202 	return fmt.tprintf("0x%x", t)
    203 }
    204 
    205 symbol_bind_str :: proc(b: u8) -> string {
    206 	switch b {
    207 	case STB_LOCAL:  return "LOCAL"
    208 	case STB_GLOBAL: return "GLOBAL"
    209 	case STB_WEAK:   return "WEAK"
    210 	}
    211 	return fmt.tprintf("0x%x", b)
    212 }
    213 
    214 segment_type_str :: proc(t: u32) -> string {
    215 	switch t {
    216 	case PT_NULL:    return "NULL"
    217 	case PT_LOAD:    return "LOAD"
    218 	case PT_DYNAMIC: return "DYNAMIC"
    219 	case PT_INTERP:  return "INTERP"
    220 	case PT_NOTE:    return "NOTE"
    221 	case PT_SHLIB:   return "SHLIB"
    222 	case PT_PHDR:    return "PHDR"
    223 	case PT_TLS:     return "TLS"
    224 	case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"
    225 	case PT_GNU_STACK:    return "GNU_STACK"
    226 	case PT_GNU_RELRO:    return "GNU_RELRO"
    227 	case PT_GNU_PROPERTY: return "GNU_PROPERTY"
    228 	}
    229 	return fmt.tprintf("0x%x", t)
    230 }
    231 
    232 // bit 0 = X, bit 1 = W, bit 2 = R
    233 flags_to_str :: proc(flags: u64) -> string {
    234 	@static table := [8]string{"   ", "  X", " W ", " WX", "R  ", "R X", "RW ", "RWX"}
    235 	return table[flags & 7]
    236 }
    237 
    238 print_checksec :: proc(info: ^ELF_Info) {
    239 	f := os.stdout
    240 	c := checksec(info)
    241 
    242 	fmt.fprintf(f, "%-12s %s\n", "RELRO:", relro_str(c.relro))
    243 	fmt.fprintf(f, "%-12s %s\n", "Stack:", "Canary found" if c.canary else "No canary found")
    244 	fmt.fprintf(f, "%-12s %s\n", "NX:", nx_str(c.nx))
    245 	fmt.fprintf(f, "%-12s %s\n", "PIE:", pie_str(c.pie))
    246 	fmt.fprintf(f, "%-12s %s\n", "RPATH:", c.rpath if c.rpath != "" else "No RPATH")
    247 	fmt.fprintf(f, "%-12s %s\n", "RUNPATH:", c.runpath if c.runpath != "" else "No RUNPATH")
    248 	fmt.fprintf(f, "%-12s %s\n", "Symbols:", "No symbols" if c.stripped else "Symbols")
    249 	fmt.fprintf(f, "%-12s %d of %d fortifiable functions fortified", "FORTIFY:", c.fortified, c.fortifiable)
    250 	if c.partial > 0 {
    251 		fmt.fprintf(f, " (%d also imported unfortified)", c.partial)
    252 	}
    253 	fmt.fprintf(f, "\n")
    254 
    255 	if c.textrel {
    256 		fmt.fprintf(f, "%-12s DT_TEXTREL present, code segment is writable at load time\n", "TEXTREL:")
    257 	}
    258 	if c.rwx_segment {
    259 		fmt.fprintf(f, "%-12s a PT_LOAD segment is both writable and executable\n", "RWX:")
    260 	}
    261 }
    262 
    263 print_relocs :: proc(info: ^ELF_Info) {
    264 	f := os.stdout
    265 
    266 	if len(info.relocs) == 0 {
    267 		fmt.fprintf(f, "No relocations\n")
    268 		return
    269 	}
    270 
    271 	fmt.fprintf(f, "%-18s %-22s %-16s %s\n", "Offset", "Type", "Addend", "Symbol")
    272 	for r in info.relocs {
    273 		addend := fmt.tprintf("0x%x", r.addend) if r.kind == .Rela else "-"
    274 		fmt.fprintf(f, "0x%016x %-22s %-16s %s\n",
    275 			r.offset, reloc_type_str(info.header.machine, r.type), addend, r.sym_name)
    276 	}
    277 
    278 	fmt.fprintf(f, "\nPLT/GOT Map:\n")
    279 	slots := 0
    280 	for r in info.relocs {
    281 		if !is_jump_slot(info.header.machine, r.type) || r.sym_name == "" {
    282 			continue
    283 		}
    284 		slots += 1
    285 		fmt.fprintf(f, "  %-32s 0x%016x\n", r.sym_name, r.offset)
    286 	}
    287 	if slots == 0 {
    288 		fmt.fprintf(f, "  (no jump slots)\n")
    289 	}
    290 }