--- /dev/null
+/* todo: copyright/gpl stuff. */
+
+#ifndef __i386__
+#error "use it on your own risk"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+
+#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 <pe-ebc>\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;
+}