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
2 years ago
|
#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);
|
||
|
}
|