1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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');
}
}
|