#include "x86/msr.h" #include "x86/processor.h" #include "x86/apic-defs.h" #include "x86/apic.h" #include "x86/desc.h" #include "x86/isr.h" #include "alloc.h" #include "vmalloc.h" #include "setjmp.h" #include "usermode.h" #include "fault_test.h" #include "libcflat.h" #include #define VMWARE_BACKDOOR_PMC_HOST_TSC 0x10000 #define VMWARE_BACKDOOR_PMC_REAL_TIME 0x10001 #define VMWARE_BACKDOOR_PMC_APPARENT_TIME 0x10002 #define VMWARE_BACKDOOR_PORT 0x5658 #define VMWARE_MAGIC 0x564D5868 #define VMPORT_CMD_GETVERSION 0x0a #define VMPORT_CMD_ILLEGAL 0xfff #define VMPORT_DEFAULT_RETVAL 0xdeadbeef #define RANDOM_IO_PORT 0x1234 struct backdoor_port_result { uint64_t rax; uint64_t rbx; uint64_t rcx; uint64_t rdx; }; static bool vmware_backdoor_port_callback(struct fault_test_arg *arg) { struct backdoor_port_result *res = (struct backdoor_port_result *) arg->retval; switch (arg->arg[2]) { case VMPORT_CMD_GETVERSION: return (res->rbx == VMWARE_MAGIC); case VMPORT_CMD_ILLEGAL: return (res->rbx == VMPORT_DEFAULT_RETVAL); } return false; } static uint64_t vmware_backdoor_port(uint64_t vmport, uint64_t vmport_magic, uint64_t command) { struct backdoor_port_result *res = (struct backdoor_port_result *) malloc(sizeof(struct backdoor_port_result)); res->rax = VMPORT_DEFAULT_RETVAL; res->rbx = VMPORT_DEFAULT_RETVAL; res->rcx = VMPORT_DEFAULT_RETVAL; res->rdx = VMPORT_DEFAULT_RETVAL; asm volatile( "mov %[rax], %%rax\n\t" "mov %[rdx], %%rdx\n\t" "mov %[rcx], %%rcx\n\t" "inl %%dx, %%eax\n\t" : "+a"(res->rax), "+b"(res->rbx), "+c"(res->rcx), "+d"(res->rdx) : [rax]"m"(vmport_magic), [rdx]"m"(vmport), [rcx]"m"(command) ); return (uint64_t) res; } #define FAULT true #define NO_FAULT false #define USER_MODE true #define KERNEL_MODE false #define RDPMC_ARG(n, m, sf) {.usermode = m, \ .func = (test_fault_func) rdpmc, .fault_vector = GP_VECTOR, \ .should_fault = sf, .arg = {n, 0, 0, 0}, .callback = NULL} #define RDPMC_TEST(name, a, m, sf) FAULT_TEST("rdpmc_test: "name, \ RDPMC_ARG(a, m, sf)) #define PORT_ARG(a, b, c, m, sf) {.usermode = m, \ .func = (test_fault_func) vmware_backdoor_port, \ .fault_vector = GP_VECTOR, .should_fault = sf, .arg = {a, b, c, 0}, \ .callback = vmware_backdoor_port_callback} #define PORT_TEST(name, a, b, c, m, sf) FAULT_TEST("port_test: "name, \ PORT_ARG(a, b, c, m, sf)) struct fault_test vmware_backdoor_tests[] = { RDPMC_TEST("HOST_TSC kernel", VMWARE_BACKDOOR_PMC_HOST_TSC, KERNEL_MODE, NO_FAULT), RDPMC_TEST("REAL_TIME kernel", VMWARE_BACKDOOR_PMC_REAL_TIME, KERNEL_MODE, NO_FAULT), RDPMC_TEST("APPARENT_TIME kernel", VMWARE_BACKDOOR_PMC_APPARENT_TIME, KERNEL_MODE, NO_FAULT), RDPMC_TEST("HOST_TSC user", VMWARE_BACKDOOR_PMC_HOST_TSC, USER_MODE, NO_FAULT), RDPMC_TEST("REAL_TIME user", VMWARE_BACKDOOR_PMC_REAL_TIME, USER_MODE, NO_FAULT), RDPMC_TEST("APPARENT_TIME user", VMWARE_BACKDOOR_PMC_APPARENT_TIME, USER_MODE, NO_FAULT), RDPMC_TEST("RANDOM PMC user", 0xfff, USER_MODE, FAULT), PORT_TEST("CMD_GETVERSION user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC, VMPORT_CMD_GETVERSION, USER_MODE, NO_FAULT), PORT_TEST("CMD_GETVERSION kernel", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC, VMPORT_CMD_GETVERSION, KERNEL_MODE, NO_FAULT), PORT_TEST("CMD_ILLEGAL user", VMWARE_BACKDOOR_PORT, VMWARE_MAGIC, VMPORT_CMD_ILLEGAL, USER_MODE, NO_FAULT), PORT_TEST("RANDOM port user", RANDOM_IO_PORT, VMWARE_MAGIC, 0xfff, USER_MODE, FAULT), { NULL }, }; /* * Set TSS IO Perm to throw GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT * from User Mode */ static void set_tss_ioperm(void) { gdt_entry_t *tss_entry; tss64_t *tss; unsigned char *ioperm_bitmap; u16 tr = str(); tss_entry = get_tss_descr(); tss = (tss64_t *)get_gdt_entry_base(tss_entry); tss->iomap_base = sizeof(*tss); ioperm_bitmap = ((unsigned char *)tss+tss->iomap_base); /* We want GP on RANDOM_IO_PORT and VMWARE_BACKDOOR_PORT */ ioperm_bitmap[RANDOM_IO_PORT / 8] |= 1 << (RANDOM_IO_PORT % 8); ioperm_bitmap[VMWARE_BACKDOOR_PORT / 8] |= 1 << (VMWARE_BACKDOOR_PORT % 8); tss_entry->type &= ~DESC_BUSY; /* Update TSS */ ltr(tr); } static void check_vmware_backdoors(void) { int i; /* Disable Permissions for IO PORTS */ set_tss_ioperm(); /* Disable Permission to run rdpmc from user mode */ write_cr4(read_cr4() & ~X86_CR4_PCE); report_prefix_push("vmware_backdoors"); for (i = 0; vmware_backdoor_tests[i].name != NULL; i++) test_run(&vmware_backdoor_tests[i]); report_prefix_pop(); } int main(int ac, char **av) { setup_vm(); check_vmware_backdoors(); return report_summary(); }