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; }
event_response_t cr3_callback(vmi_instance_t vmi, vmi_event_t *event) { va_pages = vmi_get_va_pages(vmi, event->reg_event.value); GSList *loop = va_pages; while(loop) { page_info_t *page = loop->data; // Demonstrate using access_context_t access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .addr = page->vaddr, .dtb = event->reg_event.value, }; uint64_t test; if(VMI_FAILURE == vmi_read_64(vmi, &ctx, &test)) { printf("Page in virtual address space of DTB 0x%"PRIx64" unaccessible: 0x%"PRIx64".\t" "Size: 0x%"PRIx64"\n", ctx.dtb, page->vaddr, (uint64_t)page->size); } loop=loop->next; } free_va_pages(); return 0; } int main (int argc, char **argv) { vmi_instance_t vmi = NULL; status_t status = VMI_SUCCESS; struct sigaction act; char *name = NULL; va_pages = NULL; if(argc < 2) { fprintf(stderr, "Usage: events_example <name of VM>\n"); exit(1); } // Arg 1 is the VM name. name = argv[1]; /* for a clean exit */ act.sa_handler = close_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGALRM, &act, NULL); // Initialize the libvmi library. if (vmi_init(&vmi, VMI_XEN | VMI_INIT_COMPLETE | VMI_INIT_EVENTS, name) == VMI_FAILURE) { printf("Failed to init LibVMI library.\n"); if (vmi != NULL ) { vmi_destroy(vmi); } return 1; } else { printf("LibVMI init succeeded!\n"); } /* Configure an event to track when the process is running. * (The CR3 register is updated on task context switch, allowing * us to follow as various tasks are scheduled and run upon the CPU) */ SETUP_REG_EVENT(&cr3_event, CR3, VMI_REGACCESS_W, 0, cr3_callback); vmi_register_event(vmi, &cr3_event); while(!interrupted) { printf("Waiting for events...\n"); status = vmi_events_listen(vmi,500); if (status != VMI_SUCCESS) { printf("Error waiting for events, quitting...\n"); interrupted = -1; } } printf("Finished with test.\n"); free_va_pages(); // cleanup any memory associated with the libvmi instance vmi_destroy(vmi); return 0; }
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; }