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 }