/* todo: copyright/gpl stuff. */ #ifndef __i386__ #error "use it on your own risk" #endif #include #include #include #include #include #include #include #include #define PS "%-10s: " typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; static int file_len; static u8 *data, *data_start; static u32 base_data, base_code, size_code; static char *imm_chw[] = { "b", /* 08 bits */ "w", /* 16 bits */ "d", /* 32 bits */ "q", /* 64 bits */ "n" /* unsigned int. hax */ , "sn" /* signed int. hax */ }; static char *vm_reg[] = { "FLAGS", "IP", "RESERVED", "RESERVED", "RESERVED", "RESERVED", "RESERVED", "RESERVED" }; static void print_debug(const char *s) { fprintf(stderr, PS "0x%08x (0x%04x)\n", s, (u32) data, data - data_start); } static void pspace(void) { printf(" "); } static void pcomma(void) { printf(", "); } static void preg(u8 reg) { if (reg > 7) { assert(0); } printf("r%1d", reg); } static void pvmreg(u8 reg) { if (reg > 7) { assert(0); } printf("%s", vm_reg[reg]); } static void pdref(u8 x) { printf("%s", x ? "@" : ""); } #define PIMM_READ(size) \ static void pimm##size(void) \ { \ printf(size != 64 ? "%x" : "%llx", *((u##size *) data)); \ data += sizeof (u##size); \ } PIMM_READ(8); PIMM_READ(16); PIMM_READ(32); PIMM_READ(64); static void pimm(u8 mod) { switch (mod) { case 1: pimm16(); break; case 2: pimm32(); break; case 3: pimm64(); break; case 0: default: assert(0); } } static void pimmc(u8 imm_type) { if (imm_type > 5) { assert(0); } printf("%s", imm_chw[imm_type]); } static void pflags(u8 flag) { switch (flag) { case 0x0: printf("eq"); break; case 0x1: printf("lte"); break; case 0x2: printf("gte"); break; case 0x3: printf("ulte"); break; case 0x4: printf("ugte"); break; } } static const char *breakopts[] = { "Runaway program break", "Get virtual machine version", "undefined. WTF?", // "Skip"? "Debug breakpoint", "System call", "Create thunk", "Set compiler version" }; static void pbreak(void) { u8 i = *data++; pspace(); if (i > 6) { printf("unknown break!"); assert(0); } else printf("\"%s\"", breakopts[i]); } static void pinsn(void) { u8 insn = *data++; u8 opc = insn & 0x3f; switch (opc) { case 0x00: printf("BREAK"); pbreak(); break; case 0x01: case 0x03:{ u8 opindex = insn & 0x80; u8 c3264 = insn & 0x40; u8 b1 = *data++; u8 cond = b1 & 0x80; u8 flag = b1 & 0x40; u8 ebcnative = b1 & 0x20; u8 relabs = b1 & 0x10; u8 op1 = b1 & 0x7; u8 dref1 = b1 & 0x8; if (opc == 0x01) { printf("JMP%d%s", c3264 ? 64 : 32, cond ? (flag ? "cs" : "cc") : ""); } else if (opc == 0x03) { printf("CALL%d%s", c3264 ? 64 : 32, ebcnative ? "EX" : ""); } printf("%s", relabs ? "a" : ""); pspace(); if (!c3264) { // 32bit pdref(dref1); preg(op1); if (opindex) { pspace(); pimm32(); } } else { // 64bit pimm64(); } } break; case 0x02:{ u8 cond = insn & 0x80; u8 flag = insn & 0x40; printf("JMP8%s", cond ? (flag ? "cs" : "cc") : ""); pspace(); pimm8(); } break; case 0x04: printf("RET"); data++; break; case 0x05 ... 0x09:{ u8 opindex = insn & 0x80; u8 c3264 = insn & 0x40; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 op2 = (b1 & 0x70) >> 4; u8 dref2 = b1 & 0x80; printf("CMP%d", !c3264 ? 32 : 64); pflags(opc - 0x05); pspace(); preg(op1); pcomma(); pdref(dref2); preg(op2); if (opindex) { pspace(); pimm16(); } } break; case 0x0a ... 0x19:{ u8 opindex = insn & 0x80; u8 c3264 = insn & 0x40; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref1 = b1 & 0x8; u8 op2 = (b1 & 0x70) >> 4; u8 dref2 = b1 & 0x80; switch (opc) { case 0x0a: printf("NOT"); break; case 0x0b: printf("NEG"); break; case 0x0c: printf("ADD"); break; case 0x0d: printf("SUB"); break; case 0x0e: printf("MUL"); break; case 0x0f: printf("MULU"); break; case 0x10: printf("DIV"); break; case 0x11: printf("DIVU"); break; case 0x12: printf("MOD"); break; case 0x13: printf("MODU"); break; case 0x14: printf("AND"); break; case 0x15: printf("OR"); break; case 0x16: printf("XOR"); break; case 0x17: printf("SHL"); break; case 0x18: printf("SHR"); break; case 0x19: printf("ASHR"); break; default: printf("\nopcode: %x\n", opc); assert(0); } printf("%d", c3264 ? 64 : 32); pspace(); pdref(dref1); preg(op1); pcomma(); pdref(dref2); preg(op2); if (opindex) { pspace(); pimm16(); } } break; case 0x1a ... 0x1c:{ u8 opindex = insn & 0x80; u8 c3264 = insn & 0x40; u8 opmod = opc - 0x1a; // 0b, 1w, 2d u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref1 = b1 & 0x8; u8 op2 = (b1 & 0x70) >> 4; u8 dref2 = b1 & 0x80; printf("EXTND"); pimmc(opmod); printf("%d", c3264 ? 64 : 32); pspace(); pdref(dref1); preg(op1); pcomma(); pdref(dref2); preg(op2); if (opindex) { pspace(); pimm16(); } } break; case 0x2a:{ u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 op2 = (b1 & 0x70) >> 4; printf("STORESP"); pspace(); preg(op1); pcomma(); pvmreg(op2); } break; case 0x2d ... 0x31:{ u8 c3264 = insn & 0x40; u8 i1632 = !!(insn & 0x80); u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref1 = b1 & 0x8; u8 op1index = b1 & 0x10; printf("CMPI%d", !c3264 ? 32 : 64); pimmc(i1632 + 1); pflags(opc - 0x2d); pspace(); pdref(dref1); preg(op1); if (op1index) { pspace(); pimm16(); } pcomma(); if (i1632) pimm32(); else pimm16(); } break; case 0x1d ... 0x28: case 0x32 ... 0x33:{ u8 op1mod = 0; u8 op2mod = 0; switch (opc) { case 0x1d: op1mod = 0; op2mod = 1; break; // MOVbw case 0x1e: op1mod = 1; op2mod = 1; break; // MOVww case 0x1f: op1mod = 2; op2mod = 1; break; // MOVdw case 0x20: op1mod = 3; op2mod = 1; break; // MOVqw case 0x21: op1mod = 0; op2mod = 2; break; // MOVbd case 0x22: op1mod = 1; op2mod = 2; break; // MOVwd case 0x23: op1mod = 2; op2mod = 2; break; // MOVdd case 0x24: op1mod = 3; op2mod = 2; break; // MOVqd case 0x25: op1mod = 5; op2mod = 1; break; // MOVsnw case 0x26: op1mod = 5; op2mod = 2; break; // MOVsnq case 0x28: op1mod = 3; op2mod = 3; break; // MOVqq case 0x32: op1mod = 4; op2mod = 1; break; // MOVnw case 0x33: op1mod = 4; op2mod = 2; break; // MOVnd default: fprintf(stderr, "wtfopcode: %x\n", opc); assert(0); } u8 mod = (insn & 0xc0) >> 6; u8 op1index = mod & 0x2; u8 op2index = mod & 0x1; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref1 = b1 & 0x8; u8 op2 = (b1 & 0x70) >> 4; u8 dref2 = b1 & 0x80; printf("MOV"); pimmc(op1mod); pimmc(op2mod); pspace(); pdref(dref1); preg(op1); // op2mod defines index width for *both* indexes if (op1index) { pspace(); pimm(op2mod); } pcomma(); pdref(dref2); preg(op2); if (op2index) { pspace(); pimm(op2mod); } } break; case 0x37:{ u8 mod = (insn & 0xc0) >> 6; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref = b1 & 0x8; u8 width = (b1 & 0x30) >> 4; u8 op1index = b1 & 0x40; printf("MOVI"); pimmc(width); pimmc(mod); pspace(); pdref(dref); preg(op1); if (op1index) { pspace(); pimm16(); } pcomma(); pimm(mod); } break; case 0x38:{ u8 mod = (insn & 0xc0) >> 6; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref = b1 & 0x8; u8 op1index = (b1 & 0x40) >> 6; printf("MOVIn"); pimmc(mod); pspace(); pdref(dref); preg(op1); if (op1index) { pspace(); pimm16(); } pcomma(); pimm(mod); } break; case 0x39:{ u8 mod = (insn & 0xc0) >> 6; u8 b1 = *data++; u8 op1 = b1 & 0x7; u8 dref = b1 & 0x8; u8 op1index = b1 & 0x40; printf("MOVREL"); pimmc(mod); pspace(); pdref(dref); preg(op1); if (op1index) { pspace(); pimm16(); } pcomma(); pimm(mod); } break; default: fprintf(stderr, "\nunknown opcode: 0x%02x\n", opc); assert(0); } } static void pheader(void) { printf("\n%08x: ", data - data_start); } int main(int argc, const char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } int fd = open(argv[1], O_RDONLY); file_len = lseek(fd, 0, SEEK_END); data_start = data = mmap(0, file_len, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (memcmp(data, "MZ", 2) != 0) { fprintf(stderr, "wrong DOS header: %c%c\n", *data, *(data + 1)); exit(2); } else { print_debug("data"); } int found = 0; while (!found && ++data < (data + file_len)) { if (memcmp(data, "PE", 2) == 0) { print_debug("PE"); found = 1; } } if (!found) { fprintf(stderr, "no PE header found\n"); exit(3); } data += 4; if (memcmp(data, "\xbc\x0e", 2) != 0) { // read '0xebc' fprintf(stderr, "not an EBC image: 0x%02x%02x\n", *data, *(data + 1)); exit(4); } found = 0; while (!found && ++data < (data + file_len)) { if (memcmp(data, "\x0b\x01", 2) == 0) { // read '0x10b' print_debug("PE-opt"); found = 1; } } if (!found) { fprintf(stderr, "no PE-opt header found\n"); exit(3); } fprintf(stderr, PS "0x%04x\n", "magic", *((u16 *) data)); data += 2; //u16 fprintf(stderr, PS "0x%02x\n", "majorver", *((u8 *) data)); data++; //u8 fprintf(stderr, PS "0x%02x\n", "minorver", *((u8 *) data)); data++; //u8 // TODO: not sure if this is correct :/ size_code = *((u32 *) data); print_debug("size_code"); fprintf(stderr, PS "0x%08x\n", "size_code", size_code); data += 4 + 4 + 4; //u32, u32, u32 fprintf(stderr, PS "0x%08x\n", "entry_point", *((u32 *) data)); data += 4; //u32 base_code = *((u32 *) data); print_debug("base_code"); fprintf(stderr, PS "0x%08x\n", "base_code", base_code); data += 4; base_data = *((u32 *) data); print_debug("base_data"); fprintf(stderr, PS "0x%08x\n", "base_data", base_data); data += 4; //u32 fprintf(stderr, PS "0x%08x\n", "???", *((u32 *) data)); data = data_start + base_code; while (data <= (data_start + size_code)) { pheader(); pinsn(); } printf("\n"); return 0; }