init
This commit is contained in:
134
src/disassembler.zig
Normal file
134
src/disassembler.zig
Normal file
@@ -0,0 +1,134 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const zydis = @import("zydis").zydis;
|
||||
|
||||
const log = std.log.scoped(.disassembler);
|
||||
|
||||
pub const InstructionIterator = struct {
|
||||
decoder: zydis.ZydisDecoder,
|
||||
bytes: []const u8,
|
||||
instruction: zydis.ZydisDecodedInstruction,
|
||||
operands: [zydis.ZYDIS_MAX_OPERAND_COUNT]zydis.ZydisDecodedOperand,
|
||||
|
||||
pub fn init(bytes: []const u8) InstructionIterator {
|
||||
var decoder: zydis.ZydisDecoder = undefined;
|
||||
const status = zydis.ZydisDecoderInit(
|
||||
&decoder,
|
||||
zydis.ZYDIS_MACHINE_MODE_LONG_64,
|
||||
zydis.ZYDIS_STACK_WIDTH_64,
|
||||
);
|
||||
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis decoder init failed");
|
||||
return .{
|
||||
.decoder = decoder,
|
||||
.bytes = bytes,
|
||||
.instruction = undefined,
|
||||
.operands = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next(iterator: *InstructionIterator) ?BundledInstruction {
|
||||
var status = zydis.ZydisDecoderDecodeFull(
|
||||
&iterator.decoder,
|
||||
iterator.bytes.ptr,
|
||||
iterator.bytes.len,
|
||||
&iterator.instruction,
|
||||
&iterator.operands,
|
||||
);
|
||||
var address: u64 = @intFromPtr(iterator.bytes.ptr);
|
||||
|
||||
while (!zydis.ZYAN_SUCCESS(status)) {
|
||||
// TODO: handle common padding bytes
|
||||
switch (status) {
|
||||
zydis.ZYDIS_STATUS_NO_MORE_DATA => {
|
||||
log.info("next: Got status: NO_MORE_DATA. Iterator completed.", .{});
|
||||
return null;
|
||||
},
|
||||
zydis.ZYDIS_STATUS_ILLEGAL_LOCK => log.warn("next: Got status: ILLEGAL_LOCK. " ++
|
||||
"Byte stepping, to find next valid instruction begin", .{}),
|
||||
zydis.ZYDIS_STATUS_DECODING_ERROR => log.warn("next: Got status: DECODING_ERROR. " ++
|
||||
"Byte stepping, to find next valid instruction begin", .{}),
|
||||
else => log.warn("next: Got unknown status: 0x{x}. Byte stepping, to find next " ++
|
||||
"valid instruction begin", .{status}),
|
||||
}
|
||||
// TODO: add a flag to instead return an error
|
||||
log.debug(
|
||||
"next: instruction length: {}, address: 0x{x}, bytes: 0x{x}",
|
||||
.{
|
||||
iterator.instruction.length,
|
||||
address,
|
||||
iterator.bytes[0..iterator.instruction.length],
|
||||
},
|
||||
);
|
||||
|
||||
iterator.bytes = iterator.bytes[1..];
|
||||
status = zydis.ZydisDecoderDecodeFull(
|
||||
&iterator.decoder,
|
||||
iterator.bytes.ptr,
|
||||
iterator.bytes.len,
|
||||
&iterator.instruction,
|
||||
&iterator.operands,
|
||||
);
|
||||
address = @intFromPtr(iterator.bytes.ptr);
|
||||
}
|
||||
|
||||
iterator.bytes = iterator.bytes[iterator.instruction.length..];
|
||||
return .{
|
||||
.address = address,
|
||||
.instruction = &iterator.instruction,
|
||||
.operands = iterator.operands[0..iterator.instruction.operand_count_visible],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const BundledInstruction = struct {
|
||||
address: u64,
|
||||
instruction: *const zydis.ZydisDecodedInstruction,
|
||||
operands: []const zydis.ZydisDecodedOperand,
|
||||
};
|
||||
|
||||
pub const InstructionFormatter = struct {
|
||||
formatter: zydis.ZydisFormatter,
|
||||
|
||||
pub fn init() InstructionFormatter {
|
||||
var formatter: zydis.ZydisFormatter = undefined;
|
||||
const status = zydis.ZydisFormatterInit(&formatter, zydis.ZYDIS_FORMATTER_STYLE_ATT);
|
||||
if (!zydis.ZYAN_SUCCESS(status)) @panic("Zydis formatter init failed");
|
||||
|
||||
return .{
|
||||
.formatter = formatter,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
formatter: *const InstructionFormatter,
|
||||
instruction: BundledInstruction,
|
||||
buffer: []u8,
|
||||
) []u8 {
|
||||
const status = zydis.ZydisFormatterFormatInstruction(
|
||||
&formatter.formatter,
|
||||
instruction.instruction,
|
||||
instruction.operands.ptr,
|
||||
instruction.instruction.operand_count_visible,
|
||||
buffer.ptr,
|
||||
buffer.len,
|
||||
instruction.address,
|
||||
null,
|
||||
);
|
||||
if (!zydis.ZYAN_SUCCESS(status)) {
|
||||
@panic("wow");
|
||||
}
|
||||
return mem.sliceTo(buffer, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/// Disassemble `bytes` and format them into the given buffer. Useful for error reporting or
|
||||
/// debugging purposes.
|
||||
/// This function should not be called in a tight loop as it's intentionally inefficient due tue
|
||||
/// having a simple API.
|
||||
pub fn formatBytes(bytes: []const u8, buffer: []u8) []u8 {
|
||||
var iter = InstructionIterator.init(bytes);
|
||||
|
||||
const instr = iter.next() orelse return buffer[0..0];
|
||||
const formatter = InstructionFormatter.init();
|
||||
return formatter.format(instr, buffer);
|
||||
}
|
||||
Reference in New Issue
Block a user