summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-01-11 21:31:44 -0300
committerGabriel Schneider <[email protected]>2026-01-11 21:31:44 -0300
commit264a66ac5327c85434d0df0b1821ddb61f1ce2ae (patch)
treedeeefae67f1494ee12ed141184dc28743df6a0fb /src
downloadcodenomicon-264a66ac5327c85434d0df0b1821ddb61f1ce2ae.tar.gz
codenomicon-264a66ac5327c85434d0df0b1821ddb61f1ce2ae.zip
initial commit
Diffstat (limited to 'src')
-rw-r--r--src/elfo-pretty.zig181
1 files changed, 181 insertions, 0 deletions
diff --git a/src/elfo-pretty.zig b/src/elfo-pretty.zig
new file mode 100644
index 0000000..d4710ad
--- /dev/null
+++ b/src/elfo-pretty.zig
@@ -0,0 +1,181 @@
+const std = @import("std");
+const cs = @import("capstone");
+
+pub fn main() !void {
+ var args = std.process.args();
+ _ = args.skip(); // skip argv[0]
+
+ const bw = std.debug.lockStderrWriter(&.{});
+ defer std.debug.unlockStderrWriter();
+ const ttyconf = std.io.tty.detectConfig(.stderr());
+
+ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
+ const allocator = gpa.allocator();
+ try printElf(allocator, args.next() orelse "./study-samples/split", bw, ttyconf);
+}
+
+pub fn printElf(
+ allocator: std.mem.Allocator,
+ path: []const u8,
+ bw: *std.Io.Writer,
+ ttyconf: std.io.tty.Config,
+) !void {
+ const f = try std.fs.cwd().openFile(path, .{ .mode = .read_only });
+ var buffer = try allocator.alloc(u8, 1024 * 100);
+ // const buffer = try allocator.alloc(u8, 1024 * 10000000);
+ var reader = f.reader(buffer);
+ const header = try std.elf.Header.read(&reader.interface);
+
+ var handle: usize = undefined;
+ std.debug.assert(cs.cs_open(cs.CS_ARCH_X86, cs.CS_MODE_64, @ptrCast(&handle)) == cs.CS_ERR_OK);
+ std.debug.print("capstone handle {x}\n", .{handle});
+
+ const shstr = blk: {
+ var section_it = header.iterateSectionHeaders(&reader);
+ var section_idx: u32 = 0;
+ while (try section_it.next()) |s| {
+ defer section_idx += 1;
+ if (section_idx == header.shstrndx) {
+ std.debug.assert(s.sh_type == std.elf.SHT_STRTAB);
+ break :blk s;
+ }
+ }
+ break :blk null;
+ };
+ // during the program's runtime, how will be this information accessed?
+
+ const elf_strtab_slice = blk: {
+ try reader.seekTo(shstr.?.sh_offset);
+ const slice = try reader.interface.take(shstr.?.sh_size);
+ const owned_slice = try allocator.alloc(u8, slice.len);
+ @memcpy(owned_slice, slice);
+ break :blk owned_slice;
+ };
+
+ var strs: std.ArrayList([]const u8) = try .initCapacity(allocator, 8);
+ {
+ var str_it = std.mem.splitScalar(u8, elf_strtab_slice, 0);
+ while (str_it.next()) |str| {
+ const owned_str = try allocator.alloc(u8, str.len);
+ @memcpy(owned_str, str);
+ try strs.append(allocator, owned_str);
+ }
+ }
+
+ var sections: std.ArrayList(std.elf.Elf64_Shdr) = try .initCapacity(allocator, 8);
+ {
+ var section_it = header.iterateSectionHeaders(&reader);
+ while (try section_it.next()) |section| {
+ try sections.append(allocator, section);
+ }
+ std.mem.sort(std.elf.Elf64_Shdr, sections.items, {}, struct {
+ pub fn inner(_: void, x: std.elf.Elf64_Shdr, y: std.elf.Elf64_Shdr) bool {
+ // NOTE: use the running mem or the static elf mem?
+ // return x.sh_offset < y.sh_offset;
+ return x.sh_addr < y.sh_addr;
+ }
+ }.inner);
+ }
+
+ for (sections.items) |section| {
+ if (section.sh_size > 0 and section.sh_addr > 0) {
+ try ttyconf.setColor(bw, .bright_green);
+ try bw.print("\n{s}", .{std.mem.sliceTo(elf_strtab_slice[section.sh_name..], 0)});
+ try ttyconf.setColor(bw, .reset);
+ try ttyconf.setColor(bw, .dim);
+ try bw.print(" -- {x}-{x}\n", .{
+ section.sh_addr,
+ section.sh_addr + section.sh_size,
+ // section,
+ });
+ try ttyconf.setColor(bw, .reset);
+ try reader.seekTo(section.sh_offset);
+
+ if (buffer.len < section.sh_size) {
+ buffer = try allocator.realloc(buffer, section.sh_size);
+ reader = f.reader(buffer);
+ }
+ // FIXME: this is buggy
+ const section_slice = try reader.interface.take(section.sh_size);
+ if (section.sh_type == std.elf.SHT_PROGBITS) {
+ const instrs: []cs.cs_insn = blk: {
+ var insn: [*]cs.cs_insn = undefined;
+ const count = cs.cs_disasm(handle, section_slice.ptr, section_slice.len, section.sh_addr, 0, @ptrCast(&insn));
+ break :blk insn[0..count];
+ };
+
+ try dumpInstr(bw, ttyconf, instrs);
+ } else {
+ std.debug.print("section pointer {x}\n", .{@intFromPtr(section_slice.ptr)});
+ try dumpHexFallible(u64, bw, ttyconf, section_slice, section.sh_addr);
+ }
+ }
+ }
+}
+
+fn dumpInstr(
+ bw: *std.Io.Writer,
+ ttyconf: std.io.tty.Config,
+ instrs: []cs.cs_insn,
+) !void {
+ for (instrs) |instr| {
+ try ttyconf.setColor(bw, .dim);
+ try bw.print("{x:0>[1]} ", .{ instr.address, @sizeOf(usize) * 2 });
+ try ttyconf.setColor(bw, .reset);
+ try bw.print("{s} {s}\n", .{ instr.mnemonic, instr.op_str });
+ }
+}
+
+/// Prints a hexadecimal view of the bytes, returning any error that occurs.
+pub fn dumpHexFallible(_: type, bw: *std.Io.Writer, ttyconf: std.io.tty.Config, bytes: []const u8, offset: u64) !void {
+ // @breakpoint();
+ const nbytes = 16;
+ var chunks = std.mem.window(u8, @ptrCast(@alignCast(bytes)), nbytes, nbytes);
+ while (chunks.next()) |window| {
+ // 1. Print the address.
+ const address = ((0x10 * (std.math.divCeil(usize, chunks.index orelse bytes.len, nbytes) catch unreachable)) - 0x10) + offset;
+ try ttyconf.setColor(bw, .dim);
+ // We print the address in lowercase and the bytes in uppercase hexadecimal to distinguish them more.
+ // Also, make sure all lines are aligned by padding the address.
+ try bw.print("{x:0>[1]} ", .{ address, @sizeOf(usize) * 2 });
+ try ttyconf.setColor(bw, .reset);
+
+ // 2. Print the bytes.
+ for (window, 0..) |byte, index| {
+ try bw.print("{X:0>2} ", .{byte});
+ if (index == 7) try bw.writeByte(' ');
+ }
+ try bw.writeByte(' ');
+ if (window.len < 16) {
+ var missing_columns = (16 - window.len) * 3;
+ if (window.len < 8) missing_columns += 1;
+ try bw.splatByteAll(' ', missing_columns);
+ }
+
+ const window_bytes: []const u8 = @ptrCast(@alignCast(window));
+
+ // 3. Print the characters.
+ for (window_bytes) |byte| {
+ if (std.ascii.isPrint(byte)) {
+ try bw.writeByte(byte);
+ } else {
+ // Related: https://github.com/ziglang/zig/issues/7600
+ if (ttyconf == .windows_api) {
+ try bw.writeByte('.');
+ continue;
+ }
+
+ // Let's print some common control codes as graphical Unicode symbols.
+ // We don't want to do this for all control codes because most control codes apart from
+ // the ones that Zig has escape sequences for are likely not very useful to print as symbols.
+ switch (byte) {
+ '\n' => try bw.writeAll("␊"),
+ '\r' => try bw.writeAll("␍"),
+ '\t' => try bw.writeAll("␉"),
+ else => try bw.writeByte('.'),
+ }
+ }
+ }
+ try bw.writeByte('\n');
+ }
+}