colorful rat Ratfactor.com > Dave's Repos

mez

A utility for ELF header experiments written in Zig
git clone http://ratfactor.com/repos/mez/mez.git

mez/mez.zig

Download raw file: mez.zig

1 const std = @import("std"); 2 const stdout = std.io.getStdOut().writer(); 3 4 // ANSI colors! 5 const cdone = "\x1b[0m"; 6 const red = "\x1b[31m"; 7 const green = "\x1b[32m"; 8 const yellow = "\x1b[33m"; 9 const magenta = "\x1b[35m"; 10 const cyan = "\x1b[36m"; 11 const gray = "\x1b[37m"; 12 const gray2 = "\x1b[90m"; 13 const bright_magenta = "\x1b[95m"; 14 const bright_yellow = "\x1b[93m"; 15 const bright_cyan = "\x1b[96m"; 16 const bgmagenta = "\x1b[30;105m"; 17 18 // A global slice will "point" to the data buffer 19 // so functions can access it directly. 20 var buffer: []u8 = undefined; 21 22 // Gets a bit of buffer as an integer of given type 23 fn get(addr: usize, T: anytype) T { 24 return std.mem.readIntSliceNative(T, buffer[addr..]); 25 } 26 27 // Verifies that we found what we expected at the given address. 28 fn expect(addr: usize, expected: anytype, desc: []const u8) void { 29 var val = get(addr, @TypeOf(expected)); 30 31 if (expected != val) { 32 stdout.print("\n{s}At 0x{x} expected '{x}', but found '{x}' ({s}){s}\n", .{red, addr, expected, val, desc, cdone}) 33 catch unreachable; 34 std.os.exit(1); 35 } 36 } 37 38 fn printByte(addr: usize) void { 39 stdout.print("{X:0>2} ", .{buffer[addr]}) 40 catch unreachable; 41 } 42 43 fn printByteColor(addr: usize, color: []const u8) void { 44 stdout.print("{s}{X:0>2}{s} ", .{color, buffer[addr], cdone}) 45 catch unreachable; 46 } 47 48 fn printBytes(addr: usize, count: usize) void { 49 for (0..count) |i| { 50 printByte(addr+i); 51 } 52 } 53 54 fn printBytesColor(addr: usize, count: usize, color: []const u8) void { 55 for (0..count) |i| { 56 printByteColor(addr+i, color); 57 } 58 } 59 60 fn printCharColor(addr: usize, color: []const u8) void { 61 stdout.print("{s}{c}{s} ", .{color, buffer[addr], cdone}) 62 catch unreachable; 63 } 64 65 pub fn main() !void { 66 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 67 defer arena.deinit(); 68 const allocator = arena.allocator(); 69 70 const args = try std.process.argsAlloc(allocator); 71 if(args.len < 2){ 72 std.debug.print("Usage: mez <ELF BINARY>\n", .{}); 73 std.os.exit(1); 74 } 75 76 var file = try std.fs.cwd().openFile(args[1], .{}); 77 defer file.close(); 78 79 const file_size = (try file.stat()).size; 80 buffer = try allocator.alloc(u8, file_size); 81 // var actual_buffer = try allocator.alloc(u8, file_size); 82 83 // Set global slice "pointer" 84 // buffer = actual_buffer; 85 86 try file.reader().readNoEof(buffer); 87 88 // =========================== 89 // || Print Main ELF Header || 90 // =========================== 91 try stdout.print("+-[ELF Header]------------------------------------------+\n", .{}); 92 93 // ROW 1 94 // =========================== 95 try stdout.print("| ", .{}); 96 97 printByteColor(0x00, green); 98 expect(0x00, @as(u8, 0x7F), "Magic number 0x7F"); 99 100 printCharColor(0x01, green); 101 expect(0x01, @as(u8, 'E'), "Magic number 'E'"); 102 103 printCharColor(0x02, green); 104 expect(0x02, @as(u8, 'L'), "Magic number 'L'"); 105 106 printCharColor(0x03, green); 107 expect(0x03, @as(u8, 'F'), "Magic number 'F'"); 108 109 printByte(0x04); 110 expect(0x04, @as(u8, 1), "1=32 bit arch"); 111 112 printByte(0x05); 113 expect(0x05, @as(u8, 1), "1=little endian"); 114 115 printByte(0x06); 116 expect(0x06, @as(u8, 1), "1=ELF version is current"); 117 118 printByte(0x07); 119 expect(0x07, @as(u8, 0), "0=System V ABI"); 120 121 inline for(0x08..0x10) |addr| { 122 // print padding zeroes as gray to de-emph 123 printByteColor(addr, gray); 124 expect(addr, @as(u8, 0), "Padding 0s"); 125 } 126 127 printBytes(0x10, 2); 128 expect(0x10, @as(u16, 2), "2=Executable file"); 129 try stdout.print("|\n", .{}); 130 131 // ROW 2 132 // =========================== 133 try stdout.print("| ", .{}); 134 135 printBytes(0x12, 2); 136 expect(0x12, @as(u16, 3), "3=x386 ISA (arch)"); 137 138 printBytes(0x14, 4); 139 expect(0x14, @as(u32, 1), "1=ELF version again"); 140 141 // e_entry - entry address (in memory) 142 printBytesColor(0x18, 4, bright_magenta); 143 const e_entry = get(0x18, u32); 144 145 // e_phoff - program header offset (in file) 146 printBytesColor(0x1C, 4, bright_yellow); 147 const e_phoff = get(0x1C, u32); 148 149 // e_shoff - section header offset (in file), ignore 150 printBytesColor(0x20, 4, gray); 151 try stdout.print("|\n", .{}); 152 153 // ROW 3 154 // =========================== 155 try stdout.print("| ", .{}); 156 157 // e_flags - totally ignoring 158 printBytesColor(0x24, 4, gray); 159 160 // e_hsize - don't need to display, but will check 161 printBytes(0x28, 2); 162 expect(0x28, @as(u16, 52), "header size should be 52 bytes (0x34) for 32-bit"); 163 164 // e_phentsize - the size of program header entries 165 printBytesColor(0x2A, 2, bright_yellow); 166 expect(0x2A, @as(u16, 32), "expecting program headers to be 32 bytes (0x20)"); 167 const e_phentsize = 32; 168 169 // e_phnum - the count of program header entries 170 printBytesColor(0x2C, 2, bright_yellow); 171 const e_phnum = get(0x2C, u16); 172 173 // e_shentsize - section header size, ignoring 174 printBytesColor(0x2E, 2, gray); 175 176 // e_shnum - section header count, ignoring 177 printBytesColor(0x30, 2, gray); 178 179 // e_shstrndx - section header string table offset, ignoring 180 printBytesColor(0x32, 2, gray); 181 try stdout.print("/-----+\n", .{}); 182 try stdout.print("+--+---------------------------------------------/\n", .{}); 183 184 // Main ELF Header Summary (decoded) 185 // ================================= 186 try stdout.print(" +-- Entry point address: {s}0x{X:0>8}{s}\n", .{bright_magenta, e_entry, cdone}); 187 try stdout.print(" +-- Program header file offset: {s}0x{X}{s}\n", .{bright_yellow, e_phoff, cdone}); 188 try stdout.print(" +-- Program header size: {s}{d} (0x{x}){s}\n", .{bright_yellow, e_phentsize, e_phentsize, cdone}); 189 try stdout.print(" \\-- Program header count: {s}{d}{s}\n", .{bright_yellow, e_phnum, cdone}); 190 try stdout.print(" |\n", .{}); 191 for (0..e_phnum) |ph_num| { 192 const ph_offset: usize = e_phoff + (ph_num * e_phentsize); 193 const ph_type = switch(get(ph_offset, u32)){ 194 0 => cyan ++ "PT_NULL" ++ cdone, 195 1 => bright_cyan ++ "PT_LOAD" ++ cdone, 196 2 => cyan ++ "PT_DYNAMIC" ++ cdone, 197 3 => cyan ++ "PT_INTERP" ++ cdone, 198 else => red ++ "PT_WTF (a surprise)" ++ cdone, 199 }; 200 201 try stdout.print(" +-- Program Header {d} at 0x{x}, type: {s}\n", .{ph_num, ph_offset,ph_type}); 202 } 203 204 // ========================================= 205 // || Print all LOAD-type program headers || 206 // ========================================= 207 for (0..e_phnum) |ph_num| { 208 const ph_offset: usize = e_phoff + (ph_num * e_phentsize); 209 if (get(ph_offset, u32) != 1) { 210 continue; 211 } 212 213 // Program Header Row 1 214 // ========================= 215 try stdout.print("\n+-[{s}Program Header {d}{s}]------------------------------+\n", .{bright_yellow, ph_num, cdone}); 216 try stdout.print("| ", .{}); 217 218 // p_type (decoded in summary above) 219 printBytesColor(ph_offset, 4, bright_cyan); 220 221 // p_offset - file offset of data image to load 222 printBytesColor(ph_offset + 0x04, 4, yellow); 223 const p_offset = get(ph_offset + 0x04, u32); 224 225 // p_vaddr - memory segment start address 226 printBytesColor(ph_offset + 0x08, 4, magenta); 227 const p_vaddr = get(ph_offset + 0x08, u32); 228 229 // p_paddr - physical address, ignore 230 printBytesColor(ph_offset + 0x0C, 4, gray); 231 try stdout.print("|\n", .{}); 232 233 // Program Header Row 2 234 // ========================= 235 try stdout.print("| ", .{}); 236 237 // p_filesz - data image size to load from file 238 printBytesColor(ph_offset + 0x10, 4, yellow); 239 const p_filesz = get(ph_offset + 0x10, u32); 240 241 // p_memsz - memory segment size 242 printBytesColor(ph_offset + 0x14, 4, magenta); 243 const p_memsz = get(ph_offset + 0x14, u32); 244 245 const p_vaddr_end = p_vaddr + p_memsz; 246 247 // p_flags - memory segment flags 248 printBytesColor(ph_offset + 0x18, 4, magenta); 249 const p_flags = get(ph_offset + 0x18, u32); 250 251 const rwx_decode = switch(p_flags) { 252 1 => "X", 253 2 => "W", 254 3 => "W+X", 255 4 => "R", 256 5 => "R+X", 257 6 => "R+W", 258 7 => "R+W+X", 259 else => "WTF", 260 }; 261 262 // p_align - memory segment alignment 263 printBytesColor(ph_offset + 0x1C, 4, gray); 264 try stdout.print("|\n", .{}); 265 266 // Program Header Summary (decoded) 267 // ================================ 268 try stdout.print("+--+----------------------------------------------+\n", .{}); 269 try stdout.print(" +-- File data start offset: {s}0x{x}{s}\n", .{yellow, p_offset, cdone}); 270 try stdout.print(" +-- File data bytes to load: {s}{d} (0x{x}){s}\n", .{yellow, p_filesz, p_filesz, cdone}); 271 try stdout.print(" +-- Memory segment start addr: {s}0x{x:0>8}{s}\n", .{magenta, p_vaddr, cdone}); 272 try stdout.print(" +-- Memory segment byte size: {s}{d} (0x{x}){s}\n", .{magenta, p_memsz, p_memsz, cdone}); 273 try stdout.print(" +-- Memory segment flags: {s}{s} (0x{x}){s}\n", .{magenta, rwx_decode, p_flags, cdone}); 274 275 // If we contain the entry point address, display it! 276 var need_to_print_entry = (e_entry >= p_vaddr and e_entry < p_vaddr_end); 277 if (need_to_print_entry) { 278 try stdout.print(" +-- {s}Contains entry point 0x{X:0>8}{s}\n", .{bright_magenta, e_entry, cdone}); 279 } 280 281 // Hex Dump data! 282 // ================================ 283 const dump_lines = 4; 284 const dump_columns = 12; 285 var data_start = p_offset; 286 var data_end = p_offset + p_filesz; 287 var mem_start = p_vaddr; 288 var line: usize = 0; 289 try stdout.print("from {x} to {x}...\n", .{mem_start, mem_start+p_filesz}); 290 291 while (line < dump_lines) { 292 293 var mybyte = data_start + (line * dump_columns); 294 var myaddr = mem_start + (line * dump_columns); 295 296 // We've printed it all! 297 if (mybyte >= data_end) { 298 break; 299 } 300 301 // Memory address at start of line 302 try stdout.print("0x{x:0>8} ", .{myaddr}); 303 304 // Print columns of bytes 305 try stdout.print("{s}", .{gray}); 306 for (0..dump_columns) |i| { 307 if (mybyte + i >= data_end) { 308 // Print spaces to complete row 309 try stdout.print(" ", .{}); 310 continue; 311 } 312 313 if(myaddr + i == e_entry) { 314 try stdout.print("{s}{x:0>2}{s} ", .{bgmagenta, buffer[mybyte + i], cdone}); 315 need_to_print_entry = false; 316 continue; 317 } 318 319 try stdout.print("{x:0>2} ", .{buffer[mybyte + i]}); 320 } 321 try stdout.print("{s}", .{cdone}); 322 323 // Print columns of ASCII 324 for (0..dump_columns) |i| { 325 if (mybyte + i >= data_end) { 326 break; 327 } 328 329 const m = buffer[mybyte + i]; 330 331 if(m > 32 and m < 127){ 332 try stdout.print("{s}{c}{s}", .{cyan, m, cdone}); 333 } 334 else { 335 try stdout.print(".", .{}); 336 } 337 } 338 339 line += 1; 340 341 // If that was the last line and we need to print 342 // the entry address, reset lines and set the start 343 // address so it'll start with the entry's line. 344 if (line == dump_lines and need_to_print_entry){ 345 var skip = e_entry - p_vaddr; 346 skip -= skip % dump_columns; 347 try stdout.print("\n ...Skipping to entry point...", .{}); 348 data_start = p_offset + skip; 349 mem_start = p_vaddr + skip; 350 line = 0; 351 } 352 353 try stdout.print("\n", .{}); 354 } 355 356 // Did we print it all? If not, show how much 'til end 357 const displayed_to = data_start + dump_lines * dump_columns; 358 if (displayed_to < data_end) { 359 try stdout.print(" ...{d} more bytes to load...\n", .{data_end - displayed_to}); 360 } 361 } 362 }