You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
9.7 KiB
349 lines
9.7 KiB
#include "../common.h" |
|
|
|
static void |
|
print_inst(int instruction_offset, struct instruction *inst, int *labels, int label_count) |
|
{ |
|
if (inst->opcode > 0xF) { |
|
for (int i = 0; i < label_count; ++i) { |
|
if (instruction_offset + inst->size + inst->jump_offset == labels[i]) { |
|
printf("%s label_%d\n", OP_NAMES[inst->opcode], i); |
|
return; |
|
} |
|
} |
|
|
|
CRASH_SHOULDNOT(); |
|
} |
|
|
|
const char **regs = inst->wide ? REGS_W1 : REGS_W0; |
|
struct rm dest = inst->dst; |
|
struct rm src = inst->src; |
|
|
|
if (inst->swap) { |
|
struct rm buf = dest; |
|
dest = src; |
|
src = buf; |
|
} |
|
|
|
printf("%s ", OP_NAMES[inst->opcode]); |
|
|
|
/* Dest can not be an immediate, so dest.imm indicates if there's a displacement */ |
|
|
|
if (dest.reg1 == NO_REGISTER && dest.reg2 == NO_REGISTER && dest.disp != 0) { |
|
printf("[%d]", dest.disp); |
|
} else if (dest.reg2 == NO_REGISTER && dest.imm == 0) { |
|
printf("%s", regs[dest.reg1]); |
|
} else if (dest.reg2 != NO_REGISTER && dest.imm == 0) { |
|
printf("[%s + %s]", REGS_W1[dest.reg1], REGS_W1[dest.reg2]); |
|
} else if (dest.reg2 == NO_REGISTER && dest.imm != 0) { |
|
printf("[%s + %d]", REGS_W1[dest.reg1], dest.disp); |
|
} else if (dest.reg2 != NO_REGISTER && dest.imm != 0) { |
|
printf("[%s + %s + %d]", REGS_W1[dest.reg1], REGS_W1[dest.reg2], dest.disp); |
|
} |
|
|
|
printf(", "); |
|
|
|
if (src.reg1 == NO_REGISTER) { |
|
if (src.disp == 0) { |
|
printf("%s %d", inst->wide ? "word" : "byte", src.imm); /* only the src can be an immediate */ |
|
} else { |
|
printf("[%d]", src.disp); |
|
} |
|
} else { |
|
if (src.reg2 == NO_REGISTER && src.imm == 0) { |
|
printf("%s", regs[src.reg1]); |
|
} else if (src.reg2 != NO_REGISTER && src.imm == 0) { |
|
printf("[%s + %s]", REGS_W1[src.reg1], REGS_W1[src.reg2]); |
|
} else if (src.reg2 == NO_REGISTER && src.imm != 0) { |
|
printf("[%s + %d]", REGS_W1[src.reg1], src.disp); |
|
} else if (src.reg2 != NO_REGISTER && src.imm != 0) { |
|
printf("[%s + %s + %d]", REGS_W1[src.reg1], REGS_W1[src.reg2], src.disp); |
|
} |
|
} |
|
|
|
printf("\n"); |
|
} |
|
|
|
static int |
|
decode_rm(u8 *data, int offset, struct rm *location) |
|
{ |
|
int advance = 0; |
|
|
|
u8 b2 = data[offset + 1]; |
|
u8 mod = (b2 & 0xC0) >> 6; |
|
u8 rm_field = b2 & 0x7; |
|
|
|
if (mod == MOD_REGISTER) { |
|
location->reg1 = rm_field; |
|
location->reg2 = -1; |
|
location->disp = 0; |
|
location->imm = 0; |
|
|
|
advance = 2; |
|
} else if (mod == MOD_MEMORY) { |
|
s16 b3 = data[offset + 2]; |
|
s16 b4 = data[offset + 3]; |
|
|
|
if (rm_field == 0x6) { |
|
location->reg1 = NO_REGISTER; |
|
location->reg2 = NO_REGISTER; |
|
location->disp = (b4 << 8) | b3; |
|
location->imm = 0; |
|
advance = 4; |
|
} else { |
|
location->reg1 = MOD_PAIRS_FIRST[rm_field]; |
|
location->reg2 = MOD_PAIRS_SECOND[rm_field]; |
|
location->disp = 0; |
|
location->imm = 1; |
|
advance = 2; |
|
} |
|
} else if (mod == MOD_MEMORY_8) { |
|
s8 b3 = data[offset + 2]; |
|
|
|
location->reg1 = MOD_PAIRS_FIRST[rm_field]; |
|
location->reg2 = MOD_PAIRS_SECOND[rm_field]; |
|
location->disp = b3; |
|
location->imm = 1; |
|
|
|
advance = 3; |
|
} else if (mod == MOD_MEMORY_16) { |
|
s16 b3 = data[offset + 2]; |
|
s16 b4 = data[offset + 3]; |
|
|
|
location->reg1 = MOD_PAIRS_FIRST[rm_field]; |
|
location->reg2 = MOD_PAIRS_SECOND[rm_field]; |
|
location->disp = (b4 << 8) | b3; |
|
location->imm = 1; |
|
|
|
advance = 4; |
|
} |
|
|
|
return(advance); |
|
} |
|
|
|
static int |
|
decode_and_print_add_sub_cmp_jmp(u8 *data, u64 size, struct instruction *stream) |
|
{ |
|
printf("bits 16\n\n"); |
|
|
|
/* We still do not handle corrupt binaries */ |
|
|
|
u64 offset = 0; |
|
int stream_at = 0; |
|
|
|
while (offset < size) { |
|
int saved_offset = offset; |
|
|
|
u8 b1 = data[offset + 0]; |
|
u8 b2 = data[offset + 1]; |
|
|
|
u8 antiop = b1 & ANTIOP_MASK; |
|
u8 op = 0; |
|
|
|
if (antiop == 0x00 || antiop == 0x4) { |
|
op = (b1 & 0x38) >> 3; |
|
} else { |
|
op = (b2 & 0x38) >> 3; |
|
} |
|
|
|
if (antiop == 0x00) { |
|
u8 W_flag = b1 & 0x1; |
|
u8 D_flag = (b1 & 0x2) >> 1; |
|
u8 reg_field = (b2 & 0x38) >> 3; |
|
|
|
struct rm dest = { 0 }; |
|
struct rm src = { 0 }; |
|
|
|
dest.reg1 = reg_field; |
|
dest.reg2 = -1; |
|
dest.disp = 0; |
|
dest.imm = 0; |
|
|
|
offset += decode_rm(data, offset, &src); |
|
|
|
struct instruction inst = { |
|
.size = offset - saved_offset, |
|
.opcode = op, |
|
.dst = dest, |
|
.src = src, |
|
.wide = W_flag, |
|
.swap = !D_flag |
|
}; |
|
|
|
stream[stream_at++] = inst; |
|
|
|
continue; |
|
} else if (antiop == 0x80) { |
|
u8 W_flag = b1 & 0x1; |
|
u8 S_flag = (b1 & 0x2) >> 1; |
|
u8 D_flag = 1; /* Immediate to register-memory always has mod/rm as destination */ |
|
|
|
struct rm dest = { 0 }; |
|
struct rm src = { 0 }; |
|
|
|
offset += decode_rm(data, offset, &dest); |
|
|
|
s16 immediate = 0; |
|
|
|
if (S_flag && !W_flag) { |
|
s16 b5 = data[offset]; |
|
s16 b6 = data[offset + 1]; |
|
immediate = (b6 << 8) | b5; |
|
offset += 2; |
|
} else { |
|
s16 b5 = data[offset]; |
|
immediate = b5; |
|
offset += 1; |
|
} |
|
|
|
src.reg1 = NO_REGISTER; |
|
src.imm = immediate; |
|
|
|
struct instruction inst = { |
|
.size = offset - saved_offset, |
|
.opcode = op, |
|
.dst = dest, |
|
.src = src, |
|
.wide = W_flag, |
|
.swap = !D_flag |
|
}; |
|
|
|
stream[stream_at++] = inst; |
|
|
|
continue; |
|
} else if (antiop == 0x4) { |
|
u8 W_flag = b1 & 0x1; |
|
s16 immediate = 0; |
|
|
|
struct rm dest = { 0 }; |
|
struct rm src = { 0 }; |
|
|
|
dest.reg1 = AX; |
|
dest.reg2 = NO_REGISTER; |
|
dest.disp = 0; |
|
dest.imm = 0; |
|
|
|
if (W_flag) { |
|
s16 b3 = data[offset + 2]; |
|
immediate= (b3 << 8) | b2; |
|
offset += 3; |
|
} else { |
|
immediate = b2; |
|
offset += 2; |
|
} |
|
|
|
src.reg1 = NO_REGISTER; |
|
src.reg2 = NO_REGISTER; |
|
src.disp = 0; |
|
src.imm = immediate; |
|
|
|
struct instruction inst = { |
|
.size = offset - saved_offset, |
|
.opcode = op, |
|
.dst = dest, |
|
.src = src, |
|
.wide = W_flag, |
|
.swap = 0, |
|
}; |
|
|
|
stream[stream_at++] = inst; |
|
|
|
continue; |
|
} |
|
|
|
offset += 2; |
|
|
|
struct instruction inst = { |
|
.size = offset - saved_offset, |
|
.opcode = b1, |
|
.jump_offset = (s8) b2 |
|
}; |
|
|
|
stream[stream_at++] = inst; |
|
} |
|
|
|
return(stream_at); |
|
} |
|
|
|
static int |
|
insert_labels(struct instruction *in_stream, int instruction_count, int *labels) |
|
{ |
|
int instruction_offset = 0; |
|
int label_count = 0; |
|
|
|
for (int i = 0; i < instruction_count; ++i) { |
|
struct instruction *inst = in_stream + i; |
|
|
|
if (inst->opcode > 0xF) { |
|
/* jmp-like */ |
|
int target = instruction_offset + inst->size + inst->jump_offset; |
|
int label_exists = 0; |
|
|
|
for (int j = 0; j < label_count; ++j) { |
|
if (labels[j] == target) { |
|
label_exists = 1; |
|
break; |
|
} |
|
} |
|
|
|
if (!label_exists) { |
|
labels[label_count++] = target; |
|
} |
|
} |
|
|
|
instruction_offset += inst->size; |
|
} |
|
|
|
return(label_count); |
|
} |
|
|
|
static void |
|
print_instruction_stream(struct instruction *stream, int instruction_count, |
|
int *labels, int label_count) |
|
{ |
|
int instruction_offset = 0; |
|
|
|
for (int i = 0; i < instruction_count; ++i) { |
|
struct instruction *inst = stream + i; |
|
|
|
for (int j = 0; j < label_count; ++j) { |
|
if (labels[j] == instruction_offset) { |
|
printf("label_%d:\n", j); |
|
break; |
|
} |
|
} |
|
|
|
print_inst(instruction_offset, stream + i, labels, label_count); |
|
|
|
instruction_offset += inst->size; |
|
} |
|
} |
|
|
|
int |
|
main(int argc, char **argv) |
|
{ |
|
if (argc != 2) { |
|
fprintf(stderr, "Usage: %s binary_file\n", argv[0]); |
|
return(1); |
|
} |
|
|
|
char *filename = argv[1]; |
|
int fd = open(filename, O_RDONLY); |
|
|
|
if (fd == -1) { |
|
perror("open"); |
|
return(1); |
|
} |
|
|
|
u64 size = file_size(fd); |
|
u8 *data = read_file(fd, size); |
|
|
|
struct instruction stream[MAX_INSTRUCTIONS] = { 0 }; |
|
int labels[MAX_LABELS] = { 0 }; |
|
|
|
int instruction_count = decode_and_print_add_sub_cmp_jmp(data, size, stream); |
|
int label_count = insert_labels(stream, instruction_count, labels); |
|
|
|
print_instruction_stream(stream, instruction_count, labels, label_count); |
|
|
|
return(0); |
|
} |