status_t vmi_get_vcpureg( vmi_instance_t vmi, reg_t *value, registers_t reg, unsigned long vcpu) { return driver_get_vcpureg(vmi, value, reg, vcpu); }
status_t windows_kernel_symbol_to_address( vmi_instance_t vmi, const char *symbol, addr_t *kernel_base_address, addr_t *address) { /* see if we have a cr3 value */ reg_t cr3 = 0; windows_instance_t windows = vmi->os_data; if (vmi->os_data == NULL) { return VMI_FAILURE; } if (vmi->kpgd) { cr3 = vmi->kpgd; } else { driver_get_vcpureg(vmi, &cr3, CR3, 0); } dbprint("--windows symbol lookup (%s)\n", symbol); if (kernel_base_address) { *kernel_base_address = windows->ntoskrnl_va; } /* check kpcr if we have a cr3 */ if ( /*cr3 && */ VMI_SUCCESS == windows_kpcr_lookup(vmi, symbol, address)) { dbprint("--got symbol from kpcr (%s --> 0x%"PRIx64").\n", symbol, *address); return VMI_SUCCESS; } dbprint("--kpcr lookup failed, trying kernel PE export table\n"); /* check exports */ if (VMI_SUCCESS == windows_export_to_rva(vmi, windows->ntoskrnl_va, 0, symbol, address)) { addr_t rva = *address; *address = windows->ntoskrnl_va + rva; dbprint("--got symbol from PE export table (%s --> 0x%.16"PRIx64").\n", symbol, *address); return VMI_SUCCESS; } dbprint("--kernel PE export table failed, nothing left to try\n"); return VMI_FAILURE; }
status_t probe_memory_layout_arm(vmi_instance_t vmi) { //Note: this will need to be a more comprehensive check when we start supporting AArch64 status_t ret = VMI_FAILURE; page_mode_t pm = VMI_PM_UNKNOWN; reg_t ttbr1; if (VMI_SUCCESS == driver_get_vcpureg(vmi, &ttbr1, TTBR1, 0)) { pm = VMI_PM_AARCH32; ret = VMI_SUCCESS; } vmi->page_mode = pm; return ret; }
/* expose virtual to physical mapping for kernel space via api call */ addr_t vmi_translate_kv2p (vmi_instance_t vmi, addr_t virt_address) { reg_t cr3 = 0; if (vmi->kpgd) { cr3 = vmi->kpgd; } else { driver_get_vcpureg(vmi, &cr3, CR3, 0); } if (!cr3) { dbprint(VMI_DEBUG_PTLOOKUP, "--early bail on v2p lookup because cr3 is zero\n"); return 0; } else { return vmi_pagetable_lookup(vmi, cr3, virt_address); } }
/* * check that this vm uses a paging method that we support * and set pm/cr3/pae/pse/lme flags optionally on the given pointers */ status_t probe_memory_layout_x86(vmi_instance_t vmi) { // To get the paging layout, the following bits are needed: // 1. CR0.PG // 2. CR4.PAE // 3. Either (a) IA32_EFER.LME, or (b) the guest's address width (32 or // 64). Not all backends allow us to read an MSR; in particular, Xen's PV // backend doessn't. status_t ret = VMI_FAILURE; page_mode_t pm = VMI_PM_UNKNOWN; uint8_t dom_addr_width = 0; // domain address width (bytes) /* pull info from registers, if we can */ reg_t cr0, cr3, cr4, efer; int pae = 0, pse = 0, lme = 0; uint8_t msr_efer_lme = 0; // LME bit in MSR_EFER /* get the control register values */ if (driver_get_vcpureg(vmi, &cr0, CR0, 0) == VMI_FAILURE) { errprint("**failed to get CR0\n"); goto _exit; } /* PG Flag --> CR0, bit 31 == 1 --> paging enabled */ if (!VMI_GET_BIT(cr0, 31)) { dbprint(VMI_DEBUG_CORE, "Paging disabled for this VM, only physical addresses supported.\n"); vmi->page_mode = VMI_PM_UNKNOWN; vmi->pae = 0; vmi->pse = 0; vmi->lme = 0; ret = VMI_SUCCESS; goto _exit; } // // Paging enabled (PG==1) // if (driver_get_vcpureg(vmi, &cr4, CR4, 0) == VMI_FAILURE) { errprint("**failed to get CR4\n"); goto _exit; } /* PSE Flag --> CR4, bit 5 */ pae = VMI_GET_BIT(cr4, 5); dbprint(VMI_DEBUG_CORE, "**set pae = %d\n", pae); /* PSE Flag --> CR4, bit 4 */ pse = VMI_GET_BIT(cr4, 4); dbprint(VMI_DEBUG_CORE, "**set pse = %d\n", pse); ret = driver_get_vcpureg(vmi, &efer, MSR_EFER, 0); if (VMI_SUCCESS == ret) { lme = VMI_GET_BIT(efer, 8); dbprint(VMI_DEBUG_CORE, "**set lme = %d\n", lme); } else { dbprint(VMI_DEBUG_CORE, "**failed to get MSR_EFER, trying method #2\n"); // does this trick work in all cases? ret = driver_get_address_width(vmi, &dom_addr_width); if (VMI_FAILURE == ret) { errprint ("Failed to get domain address width. Giving up.\n"); goto _exit; } lme = (8 == dom_addr_width); dbprint (VMI_DEBUG_CORE, "**found guest address width is %d bytes; assuming IA32_EFER.LME = %d\n", dom_addr_width, lme); } // if // Get current cr3 for sanity checking if (driver_get_vcpureg(vmi, &cr3, CR3, 0) == VMI_FAILURE) { errprint("**failed to get CR3\n"); goto _exit; } // now determine addressing mode if (0 == pae) { dbprint(VMI_DEBUG_CORE, "**32-bit paging\n"); pm = VMI_PM_LEGACY; cr3 &= 0xFFFFF000ull; } // PAE == 1; determine IA-32e or PAE else if (lme) { // PAE == 1, LME == 1 dbprint(VMI_DEBUG_CORE, "**IA-32e paging\n"); pm = VMI_PM_IA32E; cr3 &= 0xFFFFFFFFFFFFF000ull; } else { // PAE == 1, LME == 0 dbprint(VMI_DEBUG_CORE, "**PAE paging\n"); pm = VMI_PM_PAE; cr3 &= 0xFFFFFFE0; } // if-else dbprint(VMI_DEBUG_CORE, "**sanity checking cr3 = 0x%.16"PRIx64"\n", cr3); /* testing to see CR3 value */ if (!driver_is_pv(vmi) && cr3 >= vmi->max_physical_address) { // sanity check on CR3 dbprint(VMI_DEBUG_CORE, "** Note cr3 value [0x%"PRIx64"] exceeds memsize [0x%"PRIx64"]\n", cr3, vmi->size); } vmi->page_mode = pm; vmi->pae = pae; vmi->pse = pse; vmi->lme = lme; _exit: return ret; }
status_t linux_init(vmi_instance_t vmi) { status_t rc; os_interface_t os_interface = NULL; if (vmi->config == NULL) { errprint("No config table found\n"); return VMI_FAILURE; } if (vmi->os_data != NULL) { errprint("os data already initialized, reinitializing\n"); free(vmi->os_data); } vmi->os_data = safe_malloc(sizeof(struct linux_instance)); bzero(vmi->os_data, sizeof(struct linux_instance)); linux_instance_t linux_instance = vmi->os_data; g_hash_table_foreach(vmi->config, (GHFunc)linux_read_config_ghashtable_entries, vmi); #if defined(ARM) rc = driver_get_vcpureg(vmi, &vmi->kpgd, TTBR1, 0); #elif defined(I386) || defined(X86_64) rc = driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0); #endif /* * The driver failed to get us a pagetable. * As a fall-back, try to init using heuristics. * This path is taken in FILE mode as well. */ if (VMI_FAILURE == rc) if (VMI_FAILURE == linux_filemode_init(vmi)) goto _exit; dbprint(VMI_DEBUG_MISC, "**set vmi->kpgd (0x%.16"PRIx64").\n", vmi->kpgd); rc = linux_system_map_symbol_to_address(vmi, "init_task", NULL, &vmi->init_task); if (VMI_FAILURE == rc) { errprint("Could not get init_task from System.map\n"); goto _exit; } done: os_interface = safe_malloc(sizeof(struct os_interface)); bzero(os_interface, sizeof(struct os_interface)); os_interface->os_get_offset = linux_get_offset; os_interface->os_pid_to_pgd = linux_pid_to_pgd; os_interface->os_pgd_to_pid = linux_pgd_to_pid; os_interface->os_ksym2v = linux_system_map_symbol_to_address; os_interface->os_usym2rva = NULL; os_interface->os_v2sym = linux_system_map_address_to_symbol; os_interface->os_read_unicode_struct = NULL; os_interface->os_teardown = linux_teardown; vmi->os_interface = os_interface; return VMI_SUCCESS; _exit: free(vmi->os_data); vmi->os_data = NULL; return VMI_FAILURE; }
status_t linux_init(vmi_instance_t vmi) { status_t ret = VMI_FAILURE; os_interface_t os_interface = NULL; if (vmi->config == NULL) { errprint("No config table found\n"); return VMI_FAILURE; } if (vmi->os_data != NULL) { errprint("os data already initialized, reinitializing\n"); free(vmi->os_data); } vmi->os_data = safe_malloc(sizeof(struct linux_instance)); bzero(vmi->os_data, sizeof(struct linux_instance)); linux_instance_t linux_instance = vmi->os_data; g_hash_table_foreach(vmi->config, (GHFunc)linux_read_config_ghashtable_entries, vmi); addr_t boundary = 0, phys_start = 0, virt_start = 0; if(vmi->page_mode == VMI_PM_IA32E) { linux_system_map_symbol_to_address(vmi, "phys_startup_64", NULL, &phys_start); linux_system_map_symbol_to_address(vmi, "startup_64", NULL, &virt_start); } else if (vmi->page_mode == VMI_PM_LEGACY || vmi->page_mode == VMI_PM_PAE) { linux_system_map_symbol_to_address(vmi, "phys_startup_32", NULL, &phys_start); linux_system_map_symbol_to_address(vmi, "startup_32", NULL, &virt_start); } else if (vmi->page_mode == VMI_PM_UNKNOWN) { ret = linux_system_map_symbol_to_address(vmi, "phys_startup_64", NULL, &phys_start); if(VMI_SUCCESS == ret) { linux_system_map_symbol_to_address(vmi, "startup_64", NULL, &virt_start); vmi->page_mode = VMI_PM_IA32E; } else { linux_system_map_symbol_to_address(vmi, "phys_startup_32", NULL, &phys_start); linux_system_map_symbol_to_address(vmi, "startup_32", NULL, &virt_start); vmi->page_mode = VMI_PM_PAE; // it's just a guess } } if(phys_start && virt_start && phys_start < virt_start) { boundary = virt_start - phys_start; } else { // Just guess the boundary boundary = 0xc0000000UL; } linux_instance->kernel_boundary = boundary; dbprint(VMI_DEBUG_MISC, "--got kernel boundary (0x%.16"PRIx64").\n", boundary); if(VMI_FAILURE == driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0)) { if (VMI_SUCCESS == linux_system_map_symbol_to_address(vmi, "swapper_pg_dir", NULL, &vmi->kpgd)) { dbprint(VMI_DEBUG_MISC, "--got vaddr for swapper_pg_dir (0x%.16"PRIx64").\n", vmi->kpgd); //We don't know if VMI_PM_LEGACY or VMI_PM_PAE yet //so we do some heuristics below } else if (VMI_SUCCESS == linux_system_map_symbol_to_address(vmi, "init_level4_pgt", NULL, &vmi->kpgd)) { dbprint(VMI_DEBUG_MISC, "--got vaddr for init_level4_pgt (0x%.16"PRIx64").\n", vmi->kpgd); //Set page mode to VMI_PM_IA32E vmi->page_mode = VMI_PM_IA32E; } else { goto _exit; } vmi->kpgd -= boundary; } dbprint(VMI_DEBUG_MISC, "**set vmi->kpgd (0x%.16"PRIx64").\n", vmi->kpgd); // We check if the page mode is known // and if no arch interface has been setup yet we do it now if(VMI_PM_UNKNOWN == vmi->page_mode) { //Try to check 32-bit paging modes vmi->page_mode = VMI_PM_LEGACY; if(VMI_SUCCESS == arch_init(vmi)) { if(phys_start == vmi_pagetable_lookup(vmi, vmi->kpgd, virt_start)) { // PM found goto done; } } vmi->page_mode = VMI_PM_PAE; if(VMI_SUCCESS == arch_init(vmi)) { if(phys_start == vmi_pagetable_lookup(vmi, vmi->kpgd, virt_start)) { // PM found goto done; } } errprint("VMI_ERROR: Page mode is still unknown\n"); goto _exit; } if(!vmi->arch_interface) { if(VMI_FAILURE == arch_init(vmi)) { goto _exit; } } done: ret = linux_system_map_symbol_to_address(vmi, "init_task", NULL, &vmi->init_task); if (ret != VMI_SUCCESS) { errprint("Could not get init_task from System.map\n"); goto _exit; } if(!vmi_pagetable_lookup(vmi, vmi->kpgd, vmi->init_task)) { errprint("Failed to translate init_task VA using the kpgd!\n"); goto _exit; } os_interface = safe_malloc(sizeof(struct os_interface)); bzero(os_interface, sizeof(struct os_interface)); os_interface->os_get_offset = linux_get_offset; os_interface->os_pid_to_pgd = linux_pid_to_pgd; os_interface->os_pgd_to_pid = linux_pgd_to_pid; os_interface->os_ksym2v = linux_system_map_symbol_to_address; os_interface->os_usym2rva = NULL; os_interface->os_rva2sym = NULL; os_interface->os_read_unicode_struct = NULL; os_interface->os_teardown = linux_teardown; vmi->os_interface = os_interface; return VMI_SUCCESS; _exit: free(vmi->os_data); vmi->os_data = NULL; return VMI_FAILURE; }
status_t windows_init( vmi_instance_t vmi) { status_t status = VMI_FAILURE; windows_instance_t windows = NULL; os_interface_t os_interface = NULL; status_t real_kpgd_found = VMI_FAILURE; if (vmi->config == NULL) { errprint("VMI_ERROR: No config table found\n"); return VMI_FAILURE; } if (vmi->os_data != NULL) { errprint("VMI_ERROR: os data already initialized, resetting\n"); } else { vmi->os_data = safe_malloc(sizeof(struct windows_instance)); } bzero(vmi->os_data, sizeof(struct windows_instance)); windows = vmi->os_data; windows->version = VMI_OS_WINDOWS_UNKNOWN; g_hash_table_foreach(vmi->config, (GHFunc)windows_read_config_ghashtable_entries, vmi); /* Need to provide this functions so that find_page_mode will work */ os_interface = safe_malloc(sizeof(struct os_interface)); bzero(os_interface, sizeof(struct os_interface)); os_interface->os_get_offset = windows_get_offset; os_interface->os_pid_to_pgd = windows_pid_to_pgd; os_interface->os_pgd_to_pid = windows_pgd_to_pid; os_interface->os_ksym2v = windows_kernel_symbol_to_address; os_interface->os_usym2rva = windows_export_to_rva; os_interface->os_v2sym = windows_rva_to_export; os_interface->os_read_unicode_struct = windows_read_unicode_struct; os_interface->os_teardown = windows_teardown; vmi->os_interface = os_interface; if(VMI_FAILURE == check_pdbase_offset(vmi)) { goto error_exit; } /* At this point we still don't have a directory table base, * so first we try to get it via the driver (fastest way). * If the driver gets us a dtb, it will be used _only_ during the init phase, * and will be replaced by the real kpgd later. */ if(VMI_FAILURE == driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0)) { if(VMI_FAILURE == get_kpgd_method2(vmi)) { errprint("Could not get kpgd, will not be able to determine page mode\n"); goto error_exit; } else { real_kpgd_found = VMI_SUCCESS; } } if(VMI_FAILURE == init_core(vmi)) { goto error_exit; } if (VMI_PM_UNKNOWN == vmi->page_mode) { if (VMI_FAILURE == find_page_mode(vmi)) { errprint("Failed to find correct page mode.\n"); goto error_exit; } } if (VMI_SUCCESS == real_kpgd_found) { status = VMI_SUCCESS; goto done; } /* If we have a dtb via the driver we need to get the real kpgd */ if (VMI_SUCCESS == get_kpgd_method0(vmi)) { dbprint(VMI_DEBUG_MISC, "--kpgd method0 success\n"); status = VMI_SUCCESS; goto done; } if (VMI_SUCCESS == get_kpgd_method1(vmi)) { dbprint(VMI_DEBUG_MISC, "--kpgd method1 success\n"); status = VMI_SUCCESS; goto done; } if (VMI_SUCCESS == get_kpgd_method2(vmi)) { dbprint(VMI_DEBUG_MISC, "--kpgd method2 success\n"); status = VMI_SUCCESS; goto done; } vmi->kpgd = 0; errprint("Failed to find kernel page directory.\n"); goto error_exit; done: return status; error_exit: windows_teardown(vmi); return VMI_FAILURE; }
static status_t init_from_rekall_profile(vmi_instance_t vmi) { status_t ret = VMI_FAILURE; windows_instance_t windows = vmi->os_data; dbprint(VMI_DEBUG_MISC, "**Trying to init from Rekall profile\n"); reg_t kpcr = 0; addr_t kpcr_rva = 0; // try to find the kernel if we are not connecting to a file and the kernel pa/va were not already specified. if(vmi->mode != VMI_FILE && ! ( windows->ntoskrnl && windows->ntoskrnl_va ) ) { switch ( vmi->page_mode ) { case VMI_PM_IA32E: if (VMI_FAILURE == driver_get_vcpureg(vmi, &kpcr, GS_BASE, 0)) goto done; break; case VMI_PM_LEGACY: /* Fall-through */ case VMI_PM_PAE: if (VMI_FAILURE == driver_get_vcpureg(vmi, &kpcr, FS_BASE, 0)) goto done; break; default: goto done; }; if (VMI_SUCCESS == rekall_profile_symbol_to_rva(windows->rekall_profile, "KiInitialPCR", NULL, &kpcr_rva)) { if ( kpcr <= kpcr_rva || vmi->page_mode == VMI_PM_IA32E && kpcr < 0xffff800000000000 ) { dbprint(VMI_DEBUG_MISC, "**vCPU0 doesn't seem to have KiInitialPCR mapped, can't init from Rekall profile.\n"); goto done; } // If the Rekall profile has KiInitialPCR we have Win 7+ windows->ntoskrnl_va = kpcr - kpcr_rva; windows->ntoskrnl = vmi_translate_kv2p(vmi, windows->ntoskrnl_va); } else if(kpcr == 0x00000000ffdff000) { // If we are in live mode without KiInitialPCR, the KPCR has to be // at this VA (XP/Vista) and the KPCR trick [1] is still valid. // [1] http://moyix.blogspot.de/2008/04/finding-kernel-global-variables-in.html addr_t kdvb = 0, kdvb_offset = 0, kernbase_offset = 0; rekall_profile_symbol_to_rva(windows->rekall_profile, "_KPCR", "KdVersionBlock", &kdvb_offset); rekall_profile_symbol_to_rva(windows->rekall_profile, "_DBGKD_GET_VERSION64", "KernBase", &kernbase_offset); vmi_read_addr_va(vmi, kpcr+kdvb_offset, 0, &kdvb); vmi_read_addr_va(vmi, kdvb+kernbase_offset, 0, &windows->ntoskrnl_va); windows->ntoskrnl = vmi_translate_kv2p(vmi, windows->ntoskrnl_va); } else { goto done; } dbprint(VMI_DEBUG_MISC, "**KernBase PA=0x%"PRIx64"\n", windows->ntoskrnl); /* * If the CR3 value points to a pagetable that hasn't been setup yet * we need to resort to finding a valid pagetable the old fashioned way. */ if (windows->ntoskrnl_va && !windows->ntoskrnl) { windows_find_cr3(vmi); windows->ntoskrnl = vmi_translate_kv2p(vmi, windows->ntoskrnl_va); } } // This could happen if we are in file mode or for Win XP if (!windows->ntoskrnl) { windows->ntoskrnl = get_ntoskrnl_base(vmi, vmi->kpgd); // get KdVersionBlock/"_DBGKD_GET_VERSION64"->KernBase addr_t kdvb = 0, kernbase_offset = 0; rekall_profile_symbol_to_rva(windows->rekall_profile, "KdVersionBlock", NULL, &kdvb); rekall_profile_symbol_to_rva(windows->rekall_profile, "_DBGKD_GET_VERSION64", "KernBase", &kernbase_offset); dbprint(VMI_DEBUG_MISC, "**KdVersionBlock RVA 0x%lx. KernBase RVA: 0x%lx\n", kdvb, kernbase_offset); dbprint(VMI_DEBUG_MISC, "**KernBase PA=0x%"PRIx64"\n", windows->ntoskrnl); if (windows->ntoskrnl && kdvb && kernbase_offset) { vmi_read_addr_pa(vmi, windows->ntoskrnl + kdvb + kernbase_offset, &windows->ntoskrnl_va); if(!windows->ntoskrnl_va) { vmi_read_32_pa(vmi, windows->ntoskrnl + kdvb + kernbase_offset, (uint32_t*)&windows->ntoskrnl_va); } if(!windows->ntoskrnl_va) { dbprint(VMI_DEBUG_MISC, "**failed to find Windows kernel VA via KdVersionBlock\n"); goto done; } } else { dbprint(VMI_DEBUG_MISC, "**Failed to find required offsets and/or kernel base PA\n"); goto done; } } dbprint(VMI_DEBUG_MISC, "**KernBase VA=0x%"PRIx64"\n", windows->ntoskrnl_va); addr_t ntbuildnumber_rva; uint16_t ntbuildnumber = 0; // Let's do some sanity checking if (VMI_FAILURE == rekall_profile_symbol_to_rva(windows->rekall_profile, "NtBuildNumber", NULL, &ntbuildnumber_rva)) { goto done; } if (VMI_FAILURE == vmi_read_16_pa(vmi, windows->ntoskrnl + ntbuildnumber_rva, &ntbuildnumber)) { goto done; } if (ntbuild2version(ntbuildnumber) == VMI_OS_WINDOWS_UNKNOWN) { dbprint(VMI_DEBUG_MISC, "Unknown Windows NtBuildNumber: %u, the Rekall Profile may be incorrect for this Windows!\n", ntbuildnumber); } // The system map seems to be good, lets grab all the required offsets if(!windows->pdbase_offset) { if (VMI_FAILURE == rekall_profile_symbol_to_rva(windows->rekall_profile, "_KPROCESS", "DirectoryTableBase", &windows->pdbase_offset)) { goto done; } } if(!windows->tasks_offset) { if (VMI_FAILURE == rekall_profile_symbol_to_rva(windows->rekall_profile, "_EPROCESS", "ActiveProcessLinks", &windows->tasks_offset)) { goto done; } } if(!windows->pid_offset) { if (VMI_FAILURE == rekall_profile_symbol_to_rva(windows->rekall_profile, "_EPROCESS", "UniqueProcessId", &windows->pid_offset)) { goto done; } } if(!windows->pname_offset) { if (VMI_FAILURE == rekall_profile_symbol_to_rva(windows->rekall_profile, "_EPROCESS", "ImageFileName", &windows->pname_offset)) { goto done; } } ret = VMI_SUCCESS; dbprint(VMI_DEBUG_MISC, "**init from Rekall profile success\n"); done: return ret; }
static status_t init_from_sysmap(vmi_instance_t vmi) { status_t ret = VMI_FAILURE; windows_instance_t windows = vmi->os_data; dbprint(VMI_DEBUG_MISC, "**Trying to init from sysmap\n"); reg_t kpcr = 0; addr_t kpcr_rva = 0; if(vmi->mode != VMI_FILE) { if (vmi->page_mode == VMI_PM_IA32E) { if (VMI_FAILURE == driver_get_vcpureg(vmi, &kpcr, GS_BASE, 0)) { goto done; } } else if (vmi->page_mode == VMI_PM_LEGACY || vmi->page_mode == VMI_PM_PAE) { if (VMI_FAILURE == driver_get_vcpureg(vmi, &kpcr, FS_BASE, 0)) { goto done; } } if (VMI_SUCCESS == windows_system_map_symbol_to_address(vmi, "KiInitialPCR", NULL, &kpcr_rva)) { // If the sysmap has KiInitialPCR we have Win 7+ windows->ntoskrnl_va = kpcr - kpcr_rva; windows->ntoskrnl = vmi_translate_kv2p(vmi, windows->ntoskrnl_va); } else if(kpcr == 0x00000000ffdff000) { // If we are in live mode without KiInitialPCR, the KPCR has to be // at this VA (XP/Vista) and the KPCR trick [1] is still valid. // [1] http://moyix.blogspot.de/2008/04/finding-kernel-global-variables-in.html addr_t kdvb = 0, kdvb_offset = 0, kernbase_offset = 0; windows_system_map_symbol_to_address(vmi, "_KPCR", "KdVersionBlock", &kdvb_offset); windows_system_map_symbol_to_address(vmi, "_DBGKD_GET_VERSION64", "KernBase", &kernbase_offset); vmi_read_addr_va(vmi, kpcr+kdvb_offset, 0, &kdvb); vmi_read_addr_va(vmi, kdvb+kernbase_offset, 0, &windows->ntoskrnl_va); windows->ntoskrnl = vmi_translate_kv2p(vmi, windows->ntoskrnl_va); } else { goto done; } dbprint(VMI_DEBUG_MISC, "**KernBase PA=0x%"PRIx64"\n", windows->ntoskrnl); } // This could happen if we are in file mode or for Win XP if (!windows->ntoskrnl) { windows->ntoskrnl = get_ntoskrnl_base(vmi, vmi->kpgd); // get KdVersionBlock/"_DBGKD_GET_VERSION64"->KernBase addr_t kdvb = 0, kernbase_offset = 0; windows_system_map_symbol_to_address(vmi, "KdVersionBlock", NULL, &kdvb); windows_system_map_symbol_to_address(vmi, "_DBGKD_GET_VERSION64", "KernBase", &kernbase_offset); dbprint(VMI_DEBUG_MISC, "**KdVersionBlock RVA 0x%lx. KernBase RVA: 0x%lx\n", kdvb, kernbase_offset); dbprint(VMI_DEBUG_MISC, "**KernBase PA=0x%"PRIx64"\n", windows->ntoskrnl); if (windows->ntoskrnl && kdvb && kernbase_offset) { vmi_read_addr_pa(vmi, windows->ntoskrnl + kdvb + kernbase_offset, &windows->ntoskrnl_va); if(!windows->ntoskrnl_va) { vmi_read_32_pa(vmi, windows->ntoskrnl + kdvb + kernbase_offset, (uint32_t*)&windows->ntoskrnl_va); } if(!windows->ntoskrnl_va) { dbprint(VMI_DEBUG_MISC, "**failed to find Windows kernel VA via KdVersionBlock\n"); goto done; } } else { dbprint(VMI_DEBUG_MISC, "**Failed to find required offsets and/or kernel base PA\n"); goto done; } } dbprint(VMI_DEBUG_MISC, "**KernBase VA=0x%"PRIx64"\n", windows->ntoskrnl_va); addr_t ntbuildnumber_rva; uint16_t ntbuildnumber = 0; // Let's do some sanity checking if (VMI_FAILURE == windows_system_map_symbol_to_address(vmi, "NtBuildNumber", NULL, &ntbuildnumber_rva)) { goto done; } if (VMI_FAILURE == vmi_read_16_pa(vmi, windows->ntoskrnl + ntbuildnumber_rva, &ntbuildnumber)) { goto done; } if (ntbuild2version(ntbuildnumber) == VMI_OS_WINDOWS_UNKNOWN) { dbprint(VMI_DEBUG_MISC, "Unknown Windows NtBuildNumber: %u. The Rekall Profile may be incorrect for this Windows!\n", ntbuildnumber); goto done; } // The system map seems to be good, lets grab all the required offsets if(!windows->pdbase_offset) { if (VMI_FAILURE == windows_system_map_symbol_to_address(vmi, "_KPROCESS", "DirectoryTableBase", &windows->pdbase_offset)) { goto done; } } if(!windows->tasks_offset) { if (VMI_FAILURE == windows_system_map_symbol_to_address(vmi, "_EPROCESS", "ActiveProcessLinks", &windows->tasks_offset)) { goto done; } } if(!windows->pid_offset) { if (VMI_FAILURE == windows_system_map_symbol_to_address(vmi, "_EPROCESS", "UniqueProcessId", &windows->pid_offset)) { goto done; } } if(!windows->pname_offset) { if (VMI_FAILURE == windows_system_map_symbol_to_address(vmi, "_EPROCESS", "ImageFileName", &windows->pname_offset)) { goto done; } } ret = VMI_SUCCESS; dbprint(VMI_DEBUG_MISC, "**init from sysmap success\n"); done: return ret; }
status_t linux_init(vmi_instance_t vmi) { status_t ret = VMI_FAILURE; os_interface_t os_interface = NULL; if (vmi->config == NULL) { errprint("VMI_ERROR: No config table found\n"); return VMI_FAILURE; } if (vmi->os_data != NULL) { errprint("VMI_ERROR: os data already initialized, reinitializing\n"); free(vmi->os_data); } vmi->os_data = safe_malloc(sizeof(struct linux_instance)); bzero(vmi->os_data, sizeof(struct linux_instance)); g_hash_table_foreach(vmi->config, (GHFunc)linux_read_config_ghashtable_entries, vmi); if (VMI_SUCCESS == linux_system_map_symbol_to_address(vmi, "swapper_pg_dir", NULL, &vmi->kpgd)) { dbprint("--got vaddr for swapper_pg_dir (0x%.16"PRIx64").\n", vmi->kpgd); if (driver_is_pv(vmi)) { vmi->kpgd = vmi_translate_kv2p(vmi, vmi->kpgd); if (vmi_read_addr_pa(vmi, vmi->kpgd, &(vmi->kpgd)) == VMI_FAILURE) { errprint( "Failed to get physical addr for kpgd using swapper_pg_dir.\n"); } } } if (!vmi->kpgd) { ret = driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0); if (ret != VMI_SUCCESS) { errprint( "Driver does not support cr3 read and kpgd could not be set, exiting\n"); goto _exit; } } dbprint("**set vmi->kpgd (0x%.16"PRIx64").\n", vmi->kpgd); ret = linux_system_map_symbol_to_address(vmi, "init_task", NULL, &vmi->init_task); if (ret != VMI_SUCCESS) { errprint("VMI_ERROR: Could not get init_task from System.map\n"); return ret; } os_interface = safe_malloc(sizeof(struct os_interface)); bzero(os_interface, sizeof(struct os_interface)); os_interface->os_get_offset = linux_get_offset; os_interface->os_pid_to_pgd = linux_pid_to_pgd; os_interface->os_pgd_to_pid = linux_pgd_to_pid; os_interface->os_ksym2v = linux_system_map_symbol_to_address; os_interface->os_usym2rva = NULL; os_interface->os_rva2sym = NULL; os_interface->os_teardown = linux_teardown; vmi->os_interface = os_interface; _exit: return ret; }
static status_t init_task_kaslr_test(vmi_instance_t vmi, addr_t page_vaddr) { status_t ret = VMI_FAILURE; uint32_t pid; addr_t init_task = page_vaddr + (vmi->init_task & VMI_BIT_MASK(0,11)); linux_instance_t linux_instance = vmi->os_data; access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = vmi->kpgd }; ctx.addr = init_task + linux_instance->pid_offset; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, &pid) ) return ret; if ( pid ) return ret; ctx.addr = init_task + linux_instance->name_offset; char* init_task_name = vmi_read_str(vmi, &ctx); if ( init_task_name && !strncmp("swapper", init_task_name, 7) ) ret = VMI_SUCCESS; free(init_task_name); return ret; } status_t init_kaslr(vmi_instance_t vmi) { /* * Let's check if we can translate init_task first as is. */ uint32_t test; access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = vmi->kpgd, .addr = vmi->init_task }; if ( VMI_SUCCESS == vmi_read_32(vmi, &ctx, &test) ) return VMI_SUCCESS; status_t ret = VMI_FAILURE; linux_instance_t linux_instance = vmi->os_data; GSList *loop, *pages = vmi_get_va_pages(vmi, vmi->kpgd); loop = pages; while (loop) { page_info_t *info = loop->data; if ( !linux_instance->kaslr_offset ) { switch(vmi->page_mode) { case VMI_PM_AARCH64: case VMI_PM_IA32E: if ( VMI_GET_BIT(info->vaddr, 47) ) ret = init_task_kaslr_test(vmi, info->vaddr); break; default: ret = init_task_kaslr_test(vmi, info->vaddr); break; }; if ( VMI_SUCCESS == ret ) { linux_instance->kaslr_offset = info->vaddr - (vmi->init_task & ~VMI_BIT_MASK(0,11)); vmi->init_task += linux_instance->kaslr_offset; dbprint(VMI_DEBUG_MISC, "**calculated KASLR offset: 0x%"PRIx64"\n", linux_instance->kaslr_offset); } } g_free(info); loop = loop->next; } g_slist_free(pages); return ret; } status_t linux_init(vmi_instance_t vmi) { status_t rc; os_interface_t os_interface = NULL; if (vmi->config == NULL) { errprint("No config table found\n"); return VMI_FAILURE; } if (vmi->os_data != NULL) { errprint("os data already initialized, reinitializing\n"); free(vmi->os_data); } vmi->os_data = safe_malloc(sizeof(struct linux_instance)); bzero(vmi->os_data, sizeof(struct linux_instance)); linux_instance_t linux_instance = vmi->os_data; g_hash_table_foreach(vmi->config, (GHFunc)linux_read_config_ghashtable_entries, vmi); if(linux_instance->rekall_profile) rc = init_from_rekall_profile(vmi); else rc = linux_symbol_to_address(vmi, "init_task", NULL, &vmi->init_task); if (VMI_FAILURE == rc) { errprint("Could not get init_task from Rekall profile or System.map\n"); goto _exit; } vmi->init_task = canonical_addr(vmi->init_task); #if defined(ARM32) || defined(ARM64) rc = driver_get_vcpureg(vmi, &vmi->kpgd, TTBR1, 0); #elif defined(I386) || defined(X86_64) rc = driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0); #endif /* * The driver failed to get us a pagetable. * As a fall-back, try to init using heuristics. * This path is taken in FILE mode as well. */ if (VMI_FAILURE == rc) if (VMI_FAILURE == linux_filemode_init(vmi)) goto _exit; if ( VMI_FAILURE == init_kaslr(vmi) ) { dbprint(VMI_DEBUG_MISC, "**failed to determine KASLR offset\n"); goto _exit; } dbprint(VMI_DEBUG_MISC, "**set vmi->kpgd (0x%.16"PRIx64").\n", vmi->kpgd); os_interface = safe_malloc(sizeof(struct os_interface)); bzero(os_interface, sizeof(struct os_interface)); os_interface->os_get_offset = linux_get_offset; os_interface->os_pid_to_pgd = linux_pid_to_pgd; os_interface->os_pgd_to_pid = linux_pgd_to_pid; os_interface->os_ksym2v = linux_symbol_to_address; os_interface->os_usym2rva = NULL; os_interface->os_v2sym = linux_system_map_address_to_symbol; os_interface->os_read_unicode_struct = NULL; os_interface->os_teardown = linux_teardown; vmi->os_interface = os_interface; return VMI_SUCCESS; _exit: free(vmi->os_data); vmi->os_data = NULL; return VMI_FAILURE; }
status_t find_kdbg_address_instant( vmi_instance_t vmi, addr_t *kdbg_pa, addr_t *kernel_pa, addr_t *kernel_va) { dbprint(VMI_DEBUG_MISC, "**Trying find_kdbg_address_instant\n"); status_t ret = VMI_FAILURE; windows_instance_t windows = NULL; if (vmi->os_data == NULL) { goto done; } windows = vmi->os_data; // If the kernel base is unknown this approach requires the // location of the KPCR which we get from the GS/FS register, // available only on live machines. if (VMI_FILE == vmi->mode) { goto done; } // We also need the config settings for the RVAs if (!windows->kdbg_offset || !windows->kpcr_offset) { goto done; } reg_t cr3, fsgs; if (VMI_FAILURE == driver_get_vcpureg(vmi, &cr3, CR3, 0)) { goto done; } if (VMI_PM_IA32E == vmi->page_mode) { if (VMI_FAILURE == driver_get_vcpureg(vmi, &fsgs, GS_BASE, 0)) goto done; } else { if (VMI_FAILURE == driver_get_vcpureg(vmi, &fsgs, FS_BASE, 0)) goto done; } addr_t kernelbase_va = fsgs - windows->kpcr_offset; addr_t kernelbase_pa = 0; if ( VMI_FAILURE == vmi_pagetable_lookup(vmi, cr3, kernelbase_va, &kernelbase_pa) ) goto done; if ( !kernelbase_pa ) goto done; *kernel_pa = kernelbase_pa; *kernel_va = kernelbase_va; *kdbg_pa = kernelbase_pa + windows->kdbg_offset; ret = VMI_SUCCESS; done: return ret; }
status_t find_kdbg_address_faster( vmi_instance_t vmi, addr_t *kdbg_pa, addr_t *kernel_pa, addr_t *kernel_va) { dbprint(VMI_DEBUG_MISC, "**Trying find_kdbg_address_faster\n"); status_t ret = VMI_FAILURE; // This scan requires the location of the KPCR // which we get from the GS/FS register on live machines. // For file mode this needs to be further investigated. if (VMI_FILE == vmi->mode) { return ret; } void *bm = boyer_moore_init((unsigned char *)"KDBG", 4); int find_ofs = 0x10; reg_t cr3 = 0, fsgs = 0; if (VMI_FAILURE == driver_get_vcpureg(vmi, &cr3, CR3, 0)) { goto done; } switch ( vmi->page_mode ) { case VMI_PM_IA32E: if (VMI_FAILURE == driver_get_vcpureg(vmi, &fsgs, GS_BASE, 0)) goto done; break; case VMI_PM_LEGACY: /* Fall-through */ case VMI_PM_PAE: if (VMI_FAILURE == driver_get_vcpureg(vmi, &fsgs, FS_BASE, 0)) goto done; break; default: goto done; }; // We start the search from the KPCR, which has to be mapped into the kernel. // We further know that the Windows kernel is page aligned // so we are just checking if the page has a valid PE header // and if the first item in the export table is "ntoskrnl.exe". // Once the kernel is found, we find the .data section // and limit the string search for "KDBG" into that region. // start searching at the lower part from the kpcr // then switch to the upper part if needed int step = -VMI_PS_4KB; addr_t page_paddr; access_context_t ctx = { .translate_mechanism = VMI_TM_NONE, }; scan: if ( VMI_FAILURE == vmi_pagetable_lookup(vmi, cr3, fsgs, &page_paddr) ) goto done; page_paddr &= ~VMI_BIT_MASK(0,11); for (; page_paddr + step < vmi->max_physical_address; page_paddr += step) { uint8_t page[VMI_PS_4KB]; ctx.addr = page_paddr; status_t rc = peparse_get_image(vmi, &ctx, VMI_PS_4KB, page); if (VMI_FAILURE == rc) { continue; } struct pe_header *pe_header = NULL; struct dos_header *dos_header = NULL; void *optional_pe_header = NULL; uint16_t optional_header_type = 0; struct export_table et; peparse_assign_headers(page, &dos_header, &pe_header, &optional_header_type, &optional_pe_header, NULL, NULL); addr_t export_header_offset = peparse_get_idd_rva(IMAGE_DIRECTORY_ENTRY_EXPORT, &optional_header_type, optional_pe_header, NULL, NULL); if (!export_header_offset || page_paddr + export_header_offset >= vmi->max_physical_address) continue; if ( VMI_SUCCESS == vmi_read_pa(vmi, page_paddr + export_header_offset, sizeof(struct export_table), &et, NULL)) { if ( !(et.export_flags || !et.name) && page_paddr + et.name + 12 >= vmi->max_physical_address) continue; unsigned char name[13] = {0}; if ( VMI_FAILURE == vmi_read_pa(vmi, page_paddr + et.name, 12, name, NULL) ) continue; if (strcmp("ntoskrnl.exe", (const char *)name)) { continue; } } else { continue; } uint32_t c; for (c=0; c < pe_header->number_of_sections; c++) { struct section_header section; addr_t section_addr = page_paddr + dos_header->offset_to_pe + sizeof(struct pe_header) + pe_header->size_of_optional_header + c*sizeof(struct section_header); // Read the section header from memory if ( VMI_FAILURE == vmi_read_pa(vmi, section_addr, sizeof(struct section_header), (uint8_t *)§ion, NULL) ) continue; // .data check if (memcmp(section.short_name, "\x2E\x64\x61\x74\x61", 5) != 0) { continue; } uint8_t *haystack = alloca(section.size_of_raw_data); if ( VMI_FAILURE == vmi_read_pa(vmi, page_paddr + section.virtual_address, section.size_of_raw_data, haystack, NULL) ) continue; int match_offset = boyer_moore2(bm, haystack, section.size_of_raw_data); if (-1 != match_offset) { // We found the structure, but let's verify it. // The kernel is always mapped into VA at the same offset // it is found on physical memory + the kernel boundary. // Read "KernBase" from the haystack uint64_t *kernbase = (uint64_t *)&haystack[(unsigned int) match_offset + sizeof(uint64_t)]; int zeroes = __builtin_clzll(page_paddr); if ((*kernbase) << zeroes == page_paddr << zeroes) { *kernel_pa = page_paddr; *kernel_va = *kernbase; *kdbg_pa = page_paddr + section.virtual_address + (unsigned int) match_offset - find_ofs; ret = VMI_SUCCESS; dbprint(VMI_DEBUG_MISC, "--Found KdDebuggerDataBlock at PA %.16"PRIx64"\n", *kdbg_pa); goto done; } else { dbprint(VMI_DEBUG_MISC, "--WARNING: KernBase in KdDebuggerDataBlock at PA %.16"PRIx64" doesn't point back to this page.\n", page_paddr + section.virtual_address + (unsigned int) match_offset - find_ofs); } } break; } } if (step<0) { step = VMI_PS_4KB; goto scan; } done: boyer_moore_fini(bm); return ret; }
status_t find_kdbg_address_fast( vmi_instance_t vmi, addr_t *kdbg_pa, addr_t *kernel_pa, addr_t *kernel_va) { dbprint(VMI_DEBUG_MISC, "**Trying find_kdbg_address_fast\n"); status_t ret = VMI_FAILURE; reg_t cr3; if (VMI_FAILURE == driver_get_vcpureg(vmi, &cr3, CR3, 0)) { return ret; } addr_t memsize = vmi_get_max_physical_address(vmi); GSList *va_pages = vmi_get_va_pages(vmi, (addr_t)cr3); void *bm = 0; // boyer-moore internal state unsigned char haystack[VMI_PS_4KB]; int find_ofs = 0; if (VMI_PM_IA32E == vmi->page_mode) { bm = boyer_moore_init((unsigned char *)"\x00\xf8\xff\xffKDBG", 8); find_ofs = 0xc; } else { bm = boyer_moore_init((unsigned char *)"\x00\x00\x00\x00\x00\x00\x00\x00KDBG", 12); find_ofs = 0x8; } // if-else GSList *va_pages_loop = va_pages; while (va_pages_loop) { page_info_t *vap = (page_info_t *)va_pages_loop->data; // We might get pages that are greater than 4Kb // so we are just going to split them to 4Kb pages while (vap && vap->size >= VMI_PS_4KB) { vap->size -= VMI_PS_4KB; addr_t page_paddr = vap->paddr+vap->size; if (page_paddr + VMI_PS_4KB - 1 > memsize) { continue; } if ( VMI_FAILURE == vmi_read_pa(vmi, page_paddr, VMI_PS_4KB, haystack, NULL) ) continue; int match_offset = boyer_moore2(bm, haystack, VMI_PS_4KB); if (-1 != match_offset) { addr_t tmp_kva = 0, tmp_kpa = 0; addr_t tmp_kdbg = page_paddr + (unsigned int) match_offset - find_ofs; if (VMI_FAILURE == vmi_read_64_pa(vmi, tmp_kdbg + sizeof(DBGKD_DEBUG_DATA_HEADER64), &tmp_kva)) { continue; } if ( VMI_FAILURE == vmi_pagetable_lookup(vmi, cr3, tmp_kva, &tmp_kpa) ) continue; *kdbg_pa = tmp_kdbg; *kernel_va = tmp_kva; *kernel_pa = tmp_kpa; ret = VMI_SUCCESS; goto done; } } g_free(vap); va_pages_loop = va_pages_loop->next; } done: // free the rest of the list while (va_pages_loop) { g_free(va_pages_loop->data); va_pages_loop = va_pages_loop->next; } g_slist_free(va_pages); if (VMI_SUCCESS == ret) dbprint(VMI_DEBUG_MISC, "--Found KdDebuggerDataBlock at PA %.16"PRIx64"\n", *kdbg_pa); boyer_moore_fini(bm); return ret; }