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 }