bool kernel_debug_search(vmi_instance_t vmi, struct cv_info_pdb70 *pdb_header) { addr_t i; for(i=0; i < max_mem; i += PAGE_SIZE) { uint8_t pe[VMI_PS_4KB]; if ( VMI_FAILURE == vmi_read_pa(vmi, i, VMI_PS_4KB, pe, NULL) ) continue; uint32_t c; for (c=0;c<VMI_PS_4KB-PDB_FILENAME_LENGTH;c++) { if(!strncmp((char*)&pe[c], "ntkrnlmp.pdb", PDB_FILENAME_LENGTH) || !strncmp((char*)&pe[c], "ntoskrnl.pdb", PDB_FILENAME_LENGTH) || !strncmp((char*)&pe[c], "ntkrnlpa.pdb", PDB_FILENAME_LENGTH) || !strncmp((char*)&pe[c], "ntkrpamp.pdb", PDB_FILENAME_LENGTH) ) { if ( VMI_FAILURE == vmi_read_pa(vmi, i+c - 2*sizeof(uint32_t) - sizeof(struct guid), sizeof(struct cv_info_pdb70)+PDB_FILENAME_LENGTH, pdb_header,NULL) ) continue; if ( pdb_header->cv_signature != RSDS) continue; else return 1; } } } return 0; }
int find_pname_offset( vmi_instance_t vmi, check_magic_func check) { addr_t block_pa = 0; addr_t offset = 0; uint32_t value = 0; void *bm = 0; bm = boyer_moore_init((unsigned char *)"Idle", 4); #define BLOCK_SIZE 1024 * 1024 * 1 unsigned char block_buffer[BLOCK_SIZE]; if (NULL == check) { check = get_check_magic_func(vmi); } for (block_pa = 4096; block_pa + BLOCK_SIZE < vmi->max_physical_address; block_pa += BLOCK_SIZE) { if ( VMI_FAILURE == vmi_read_pa(vmi, block_pa, BLOCK_SIZE, block_buffer, NULL) ) { continue; } for (offset = 0; offset < BLOCK_SIZE; offset += 8) { memcpy(&value, block_buffer + offset, 4); if (check(value)) { // look for specific magic # dbprint (VMI_DEBUG_MISC, "--%s: found magic value 0x%.8"PRIx32" @ offset 0x%.8"PRIx64"\n", __FUNCTION__, value, block_pa + offset); unsigned char haystack[0x500]; if ( VMI_FAILURE == vmi_read_pa(vmi, block_pa + offset, 0x500, haystack, NULL) ) { continue; } int i = boyer_moore2(bm, haystack, 0x500); if (-1 == i) { continue; } else { vmi->init_task = block_pa + offset; dbprint (VMI_DEBUG_MISC, "--%s: found Idle process at 0x%.8"PRIx64" + 0x%x\n", __FUNCTION__, block_pa + offset, i); boyer_moore_fini(bm); return i; } } } } boyer_moore_fini(bm); return 0; }
addr_t get_ntoskrnl_base( vmi_instance_t vmi, addr_t page_paddr) { uint8_t page[VMI_PS_4KB]; addr_t ret = 0; access_context_t ctx = { .translate_mechanism = VMI_TM_NONE, .addr = page_paddr }; for(; ctx.addr + VMI_PS_4KB < vmi->max_physical_address; ctx.addr += VMI_PS_4KB) { uint8_t page[VMI_PS_4KB]; if(VMI_FAILURE == peparse_get_image(vmi, &ctx, VMI_PS_4KB, page)) 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 || ctx.addr + export_header_offset >= vmi->max_physical_address) continue; uint32_t nbytes = vmi_read_pa(vmi, ctx.addr + export_header_offset, &et, sizeof(struct export_table)); if(nbytes == sizeof(struct export_table) && !(et.export_flags || !et.name) ) { if(ctx.addr + et.name + 12 >= vmi->max_physical_address) { continue; } unsigned char name[13] = {0}; vmi_read_pa(vmi, ctx.addr + et.name, name, 12); if(!strcmp("ntoskrnl.exe", (char*)name)) { ret = ctx.addr; break; } } else { continue; } } return ret; }
addr_t get_ntoskrnl_base( vmi_instance_t vmi, addr_t page_paddr) { uint8_t page[VMI_PS_4KB]; addr_t ret = 0; for(; page_paddr + VMI_PS_4KB < vmi->size; page_paddr += VMI_PS_4KB) { uint8_t page[VMI_PS_4KB]; status_t rc = peparse_get_image_phys(vmi, page_paddr, 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->size) continue; uint32_t nbytes = vmi_read_pa(vmi, page_paddr + export_header_offset, &et, sizeof(struct export_table)); if(nbytes == sizeof(struct export_table) && !(et.export_flags || !et.name) ) { if(page_paddr + et.name + 12 > vmi->size) { continue; } unsigned char name[13] = {0}; vmi_read_pa(vmi, page_paddr + et.name, name, 12); if(!strcmp("ntoskrnl.exe", (char*)name)) { ret = page_paddr; break; } } else { continue; } } return ret; }
status_t check_sections(vmi_instance_t vmi, addr_t image_base_p, uint8_t *pe) { struct pe_header *pe_header = NULL; struct dos_header *dos_header = NULL; uint16_t optional_header_type = 0; peparse_assign_headers(pe, &dos_header, &pe_header, &optional_header_type, NULL, NULL, NULL); uint32_t c; for(c=0; c < pe_header->number_of_sections; c++) { struct section_header section; addr_t section_addr = image_base_p + dos_header->offset_to_pe + sizeof(struct pe_header) + pe_header->size_of_optional_header + c*sizeof(struct section_header); // Read the section from memory if ( VMI_FAILURE == vmi_read_pa(vmi, section_addr, sizeof(struct section_header), (uint8_t *)§ion, NULL) ) return VMI_FAILURE; //printf("S: %s\n", section.short_name); // The character array is not null terminated, so only print the first 8 characters! if ( !strncmp(section.short_name, "INITKDBG", 8) ) return VMI_SUCCESS; } return VMI_FAILURE; }
char * windows_get_eprocess_name( vmi_instance_t vmi, addr_t paddr) { size_t name_length = 16; //TODO verify that this is correct for all versions windows_instance_t windows = vmi->os_data; if (windows == NULL) { return NULL; } addr_t name_paddr = paddr + windows->pname_offset; char *name = (char *) g_malloc0(name_length); if ( !name ) return NULL; if ( VMI_FAILURE == vmi_read_pa(vmi, name_paddr, name_length, name, NULL)) { return name; } else { free(name); return NULL; } }
static addr_t find_kdversionblock_address( vmi_instance_t vmi) { addr_t kdvb_address = 0; addr_t paddr = 0; unsigned char buf[12]; for (paddr = 0; paddr < vmi_get_memsize(vmi); paddr += 4) { if (12 == vmi_read_pa(vmi, paddr, buf, 12)) { if (VMI_PM_IA32E == vmi->page_mode) { if (memcmp(buf, "\x00\xf8\xff\xffKDBG", 8) == 0) { kdvb_address = paddr - 0xc; break; } } else { if (memcmp (buf, "\x00\x00\x00\x00\x00\x00\x00\x00KDBG", 12) == 0) { kdvb_address = paddr - 0x8; break; } } } } return kdvb_address; }
void print_pe_header(vmi_instance_t vmi, addr_t image_base_p, uint8_t *pe) { struct pe_header *pe_header = NULL; struct dos_header *dos_header = NULL; uint16_t optional_header_type = 0; peparse_assign_headers(pe, &dos_header, &pe_header, &optional_header_type, NULL, NULL, NULL); printf("\tSignature: %u.\n", pe_header->signature); printf("\tMachine: %u.\n", pe_header->machine); printf("\t# of sections: %u.\n", pe_header->number_of_sections); printf("\t# of symbols: %u.\n", pe_header->number_of_symbols); printf("\tTimestamp: %u.\n", pe_header->time_date_stamp); printf("\tCharacteristics: %u.\n", pe_header->characteristics); printf("\tOptional header size: %u.\n", pe_header->size_of_optional_header); printf("\tOptional header type: 0x%x\n", optional_header_type); uint32_t c; for(c=0; c < pe_header->number_of_sections; c++) { struct section_header section; addr_t section_addr = image_base_p + dos_header->offset_to_pe + sizeof(struct pe_header) + pe_header->size_of_optional_header + c*sizeof(struct section_header); // Read the section from memory vmi_read_pa(vmi, section_addr, (uint8_t *)§ion, sizeof(struct section_header)); // The character array is not null terminated, so only print the first 8 characters! printf("\tSection %u: %.8s\n", c+1, section.short_name); } }
status_t is_WINDOWS_KERNEL(vmi_instance_t vmi, addr_t base_p, uint8_t *pe) { status_t ret = VMI_FAILURE; void *optional_pe_header = NULL; uint16_t optional_header_type = 0; struct export_table et; peparse_assign_headers(pe, NULL, NULL, &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); // The kernel's export table is continuously allocated on the PA level with the PE header // This trick may not work for other PE headers (though may work for some drivers) uint32_t nbytes = vmi_read_pa(vmi, base_p + export_header_offset, &et, sizeof(struct export_table)); if(nbytes == sizeof(struct export_table) && !(et.export_flags || !et.name) ) { char *name = vmi_read_str_pa(vmi, base_p + et.name); if(name) { if(strcmp("ntoskrnl.exe", name)==0) ret = VMI_SUCCESS; free(name); } } return ret; }
status_t peparse_get_image_phys( vmi_instance_t vmi, addr_t base_paddr, size_t len, const uint8_t * const image) { uint32_t nbytes = vmi_read_pa(vmi, base_paddr, (void *)image, len); if(nbytes != len) { dbprint(VMI_DEBUG_MISC, "--PEPARSE: failed to read a continuous PE header\n"); return VMI_FAILURE; } if(VMI_SUCCESS != peparse_validate_pe_image(image, len)) { dbprint(VMI_DEBUG_MISC, "--PEPARSE: failed to validate a continuous PE header\n"); if(vmi->init_mode & VMI_INIT_COMPLETE) { dbprint(VMI_DEBUG_MISC, "--PEPARSE: You might want to use peparse_get_image_virt here!\n"); } return VMI_FAILURE; } return VMI_SUCCESS; }
static int vmifs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { (void) fi; if(strcmp(path, mem_path) != 0) return -ENOENT; unsigned long memsize = vmi_get_memsize(vmi); if(offset < memsize && size) { if(offset + size > memsize) size = memsize-offset; uint8_t *buffer = g_malloc0(sizeof(uint8_t)*size); size_t size_read = vmi_read_pa(vmi, offset, buffer, size); if(size != size_read) { g_free(buffer); } else { memcpy(buf, buffer, size); g_free(buffer); } } else { return 0; } return size; }
int main(int argc, char **argv) { vmi_instance_t vmi; addr_t start_address; struct timeval ktv_start; struct timeval ktv_end; char *vm = argv[1]; int buf_size = atoi(argv[2]); int loops = atoi(argv[3]); int mode = atoi(argv[4]); unsigned char *buf = malloc(buf_size); int i = 0; long int diff; long int *data = malloc(loops * sizeof(long int)); int j = 0; uint32_t value = 0; if (mode != 1 && mode != 2) { printf("invalid mode\n"); return 1; } /* initialize the xen access library */ vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, vm); /* find address to work from */ start_address = vmi_translate_ksym2v(vmi, "PsInitialSystemProcess"); start_address = vmi_translate_kv2p(vmi, start_address); for (i = 0; i < loops; ++i) { if (mode == 1) { gettimeofday(&ktv_start, 0); vmi_read_pa(vmi, start_address, buf, buf_size); gettimeofday(&ktv_end, 0); } else { gettimeofday(&ktv_start, 0); for (j = 0; j < buf_size / 4; ++j) { vmi_read_32_pa(vmi, start_address + j * 4, &value); } gettimeofday(&ktv_end, 0); } print_measurement(ktv_start, ktv_end, &diff); data[i] = diff; memset(buf, 0, buf_size); sleep(1); } avg_measurement(data, loops); vmi_destroy(vmi); free(buf); return 0; }
static addr_t find_kdversionblock_address_fast( vmi_instance_t vmi) { // Note: this function has several limitations: // -the KD version block signature cannot cross block (frame) boundaries // -reading PA 0 fails; hope the KD version block is not in frame 0 // // Todo: // -support matching across frames (can this happen in windows?) addr_t kdvb_address = 0; addr_t block_pa = 0; addr_t memsize = vmi_get_memsize(vmi); size_t read = 0; void *bm = 0; // boyer-moore internal state int find_ofs = 0; #define BLOCK_SIZE 1024 * 1024 * 1 unsigned char haystack[BLOCK_SIZE]; if (VMI_PM_IA32E == vmi->page_mode) { bm = boyer_moore_init("\x00\xf8\xff\xffKDBG", 8); find_ofs = 0xc; } else { bm = boyer_moore_init("\x00\x00\x00\x00\x00\x00\x00\x00KDBG", 12); find_ofs = 0x8; } // if-else for (block_pa = 4096; block_pa < memsize; block_pa += BLOCK_SIZE) { read = vmi_read_pa(vmi, block_pa, haystack, BLOCK_SIZE); if (BLOCK_SIZE != read) { continue; } int match_offset = boyer_moore2(bm, haystack, BLOCK_SIZE); if (-1 != match_offset) { kdvb_address = block_pa + (unsigned int) match_offset - find_ofs; break; } // if } // outer for if (kdvb_address) dbprint("--Found KD version block at PA %.16"PRIx64"\n", kdvb_address); boyer_moore_fini(bm); return kdvb_address; }
char * vmi_read_str_pa( vmi_instance_t vmi, addr_t paddr) { char *rtnval = NULL; size_t chunk_size = vmi->page_size - ((vmi->page_size - 1) & paddr); char *buf = (char *) safe_malloc(chunk_size); // read in chunk of data if (chunk_size != vmi_read_pa(vmi, paddr, buf, chunk_size)) { goto exit; } // look for \0 character, expand as needed size_t len = strnlen(buf, chunk_size); size_t buf_size = chunk_size; while (len == buf_size) { size_t offset = buf_size; buf_size += chunk_size; buf = realloc(buf, buf_size); if (chunk_size != vmi_read_pa(vmi, paddr + offset, buf + offset, chunk_size)) { goto exit; } len = strnlen(buf, buf_size); } rtnval = (char *) safe_malloc(len + 1); memcpy(rtnval, buf, len); rtnval[len] = '\0'; exit: free(buf); return rtnval; }
/////////////////////////////////////////////////////////// // Easy access to physical memory static status_t vmi_read_X_pa( vmi_instance_t vmi, addr_t paddr, void *value, int size) { size_t len_read = vmi_read_pa(vmi, paddr, value, size); if (len_read == size) { return VMI_SUCCESS; } else { return VMI_FAILURE; } }
char * windows_get_eprocess_name( vmi_instance_t vmi, addr_t paddr) { int name_length = 16; //TODO verify that this is correct for all versions addr_t name_paddr = paddr + vmi->os.windows_instance.pname_offset; char *name = (char *) safe_malloc(name_length); if (name_length == vmi_read_pa(vmi, name_paddr, name, name_length)) { return name; } else { free(name); return NULL; } }
static addr_t find_process_by_name( vmi_instance_t vmi, check_magic_func check, addr_t start_address, const char *name) { dbprint(VMI_DEBUG_MISC, "--searching for process by name: %s\n", name); addr_t block_pa = 0; addr_t offset = 0; uint32_t value = 0; size_t read = 0; unsigned char block_buffer[VMI_PS_4KB]; if (NULL == check) { check = get_check_magic_func(vmi); } for (block_pa = start_address; block_pa + VMI_PS_4KB < vmi->max_physical_address; block_pa += VMI_PS_4KB) { read = vmi_read_pa(vmi, block_pa, block_buffer, VMI_PS_4KB); if (VMI_PS_4KB != read) { continue; } for (offset = 0; offset < VMI_PS_4KB; offset += 8) { memcpy(&value, block_buffer + offset, 4); if (check(value)) { // look for specific magic # char *procname = windows_get_eprocess_name(vmi, block_pa + offset); if (procname) { if (strncmp(procname, name, 50) == 0) { free(procname); return block_pa + offset; } free(procname); } } } } return 0; }
static addr_t find_process_by_name( vmi_instance_t vmi, check_magic_func check, addr_t start_address, const char *name) { addr_t block_pa = 0; addr_t offset = 0; uint32_t value = 0; size_t read = 0; #define BLOCK_SIZE 1024 * 1024 * 1 unsigned char block_buffer[BLOCK_SIZE]; if (NULL == check) { check = get_check_magic_func(vmi); } for (block_pa = start_address; block_pa < vmi->size; block_pa += BLOCK_SIZE) { read = vmi_read_pa(vmi, block_pa, block_buffer, BLOCK_SIZE); if (BLOCK_SIZE != read) { continue; } for (offset = 0; offset < BLOCK_SIZE; offset += 8) { memcpy(&value, block_buffer + offset, 4); if (check(value)) { // look for specific magic # char *procname = windows_get_eprocess_name(vmi, block_pa + offset); if (procname) { if (strncmp(procname, name, 50) == 0) { free(procname); return block_pa + offset; } free(procname); } } } } return 0; }
status_t is_WINDOWS_KERNEL(vmi_instance_t vmi, addr_t base_p, uint8_t *pe) { status_t ret = VMI_FAILURE; void *optional_pe_header = NULL; uint16_t optional_header_type = 0; struct export_table et; peparse_assign_headers(pe, NULL, NULL, &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); // The kernel's export table is continuously allocated on the PA level with the PE header // This trick may not work for other PE headers (though may work for some drivers) if ( base_p + export_header_offset < base_p + VMI_PS_4KB ) { if( VMI_SUCCESS == vmi_read_pa(vmi, base_p + export_header_offset, sizeof(struct export_table), &et, NULL) && !(et.export_flags || !et.name)) { char *name = vmi_read_str_pa(vmi, base_p + et.name); if(name) { if(strcmp("ntoskrnl.exe", name)==0) ret = VMI_SUCCESS; free(name); } } } // The export header may be stripped from the kernel so check section names. // This is commonly the case with Windows 10. if ( ret == VMI_FAILURE ) { ret = check_sections(vmi, base_p, pe); } return ret; }
END_TEST /* test vmi_get_dgpma */ // we use vmi_read_pa() to verify vmi_get_dgpma() START_TEST (test_vmi_get_dgpma) { vmi_instance_t vmi = NULL; vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, get_testvm()); vmi_shm_snapshot_create(vmi); addr_t pa = 0x1000; // just because vmi_read_page() deny to fetch frame 0. size_t count = 4096; unsigned long max_size = vmi_get_max_physical_address(vmi); void *buf_readpa = malloc(count); void *buf_dgpma = NULL; for (; pa + count <= max_size; pa += count) { size_t read_pa = vmi_read_pa(vmi, pa, buf_readpa, count); size_t read_dgpma = vmi_get_dgpma(vmi, pa, &buf_dgpma, count); if (read_pa == 0 && read_dgpma == 0) { continue; } fail_unless(read_pa == read_dgpma, "vmi_get_dgpma(0x%"PRIx64 ") read size %d dosn't conform to %d of vmi_read_pa()", pa, read_dgpma, read_pa); int cmp = memcmp(buf_readpa, buf_dgpma, read_pa); fail_unless(0 == cmp, "vmi_get_dgpma(0x%"PRIx64 ") contents dosn't conform to vmi_read_pa()", pa); } free(buf_readpa); vmi_shm_snapshot_destroy(vmi); vmi_destroy(vmi); }
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; }
int find_pname_offset( vmi_instance_t vmi, check_magic_func check) { addr_t block_pa = 0; addr_t offset = 0; uint32_t value = 0; size_t read = 0; void *bm = 0; bm = boyer_moore_init("Idle", 4); #define BLOCK_SIZE 1024 * 1024 * 1 unsigned char block_buffer[BLOCK_SIZE]; if (NULL == check) { check = get_check_magic_func(vmi); } for (block_pa = 4096; block_pa < vmi->size; block_pa += BLOCK_SIZE) { read = vmi_read_pa(vmi, block_pa, block_buffer, BLOCK_SIZE); if (BLOCK_SIZE != read) { continue; } for (offset = 0; offset < BLOCK_SIZE; offset += 8) { memcpy(&value, block_buffer + offset, 4); if (check(value)) { // look for specific magic # dbprint ("--%s: found magic value 0x%.8"PRIx32" @ offset 0x%.8"PRIx64"\n", __FUNCTION__, value, block_pa + offset); unsigned char haystack[0x500]; read = vmi_read_pa(vmi, block_pa + offset, haystack, 0x500); if (0x500 != read) { continue; } int i = boyer_moore2(bm, haystack, 0x500); if (-1 == i) { continue; } else { vmi->init_task = block_pa + offset + vmi->os.windows_instance.tasks_offset; dbprint ("--%s: found Idle process at 0x%.8"PRIx64" + 0x%x\n", __FUNCTION__, block_pa + offset, i); boyer_moore_fini(bm); return i; } } } } boyer_moore_fini(bm); return 0; }
void print_guid(vmi_instance_t vmi, addr_t kernel_base_p, uint8_t* pe) { uint16_t major_os_version; uint16_t minor_os_version; uint32_t size_of_image; struct pe_header *pe_header = NULL; uint16_t optional_header_type = 0; struct optional_header_pe32 *oh32 = NULL; struct optional_header_pe32plus *oh32plus = NULL; peparse_assign_headers(pe, NULL, &pe_header, &optional_header_type, NULL, &oh32, &oh32plus); addr_t debug_offset = peparse_get_idd_rva(IMAGE_DIRECTORY_ENTRY_DEBUG, NULL, NULL, oh32, oh32plus); if(optional_header_type == IMAGE_PE32_MAGIC) { major_os_version=oh32->major_os_version; minor_os_version=oh32->minor_os_version; size_of_image=oh32->size_of_image; } else if(optional_header_type == IMAGE_PE32_PLUS_MAGIC) { major_os_version=oh32plus->major_os_version; minor_os_version=oh32plus->minor_os_version; size_of_image=oh32plus->size_of_image; } struct image_debug_directory debug_directory; vmi_read_pa(vmi, kernel_base_p + debug_offset, (uint8_t *)&debug_directory, sizeof(struct image_debug_directory)); if(debug_directory.type == IMAGE_DEBUG_TYPE_MISC) { printf("This operating system uses .dbg instead of .pdb\n"); if(major_os_version == 5 && minor_os_version == 0) { printf("GUID: %.8x%.8x\n",pe_header->time_date_stamp,size_of_image); } return; } else if(debug_directory.type != IMAGE_DEBUG_TYPE_CODEVIEW) { printf("The header is not in CodeView format, unable to deal with that!\n"); return; } struct cv_info_pdb70 *pdb_header = malloc(debug_directory.size_of_data); vmi_read_pa(vmi, kernel_base_p + debug_directory.address_of_raw_data, pdb_header, debug_directory.size_of_data); // The PDB header has to be PDB 7.0 // http://www.debuginfo.com/articles/debuginfomatch.html if(pdb_header->cv_signature != RSDS) { printf("The CodeView debug information has to be in PDB 7.0 for the kernel!\n"); return; } printf("\tGUID: "); printf("%.8x", pdb_header->signature.data1); printf("%.4x", pdb_header->signature.data2); printf("%.4x", pdb_header->signature.data3); int c; for(c=0;c<8;c++) printf("%.2x", pdb_header->signature.data4[c]); printf("%.1x", pdb_header->age & 0xf); printf("\n"); printf("\tKernel filename: %s\n", pdb_header->pdb_file_name); free(pdb_header); }
GSList* get_va_pages_ia32e(vmi_instance_t vmi, addr_t dtb) { GSList *ret = NULL; uint8_t entry_size = 0x8; #define IA32E_ENTRIES_PER_PAGE 0x200 // 0x1000/0x8 uint64_t *pml4_page = malloc(VMI_PS_4KB); addr_t pml4e_location = dtb & VMI_BIT_MASK(12,51); if (VMI_PS_4KB != vmi_read_pa(vmi, pml4e_location, pml4_page, VMI_PS_4KB)) { free(pml4_page); return ret; } uint64_t *pdpt_page = malloc(VMI_PS_4KB); uint64_t *pgd_page = malloc(VMI_PS_4KB); uint64_t *pt_page = malloc(VMI_PS_4KB); uint64_t pml4e_index; for(pml4e_index = 0; pml4e_index < IA32E_ENTRIES_PER_PAGE; pml4e_index++, pml4e_location += entry_size) { uint64_t pml4e_value = pml4_page[pml4e_index]; if(!ENTRY_PRESENT(vmi->os_type, pml4e_value)) { continue; } uint64_t pdpte_location = pml4e_value & VMI_BIT_MASK(12,51); if (VMI_PS_4KB != vmi_read_pa(vmi, pdpte_location, pdpt_page, VMI_PS_4KB)) { continue; } uint64_t pdpte_index; for(pdpte_index = 0; pdpte_index < IA32E_ENTRIES_PER_PAGE; pdpte_index++, pdpte_location++) { uint64_t pdpte_value = pdpt_page[pdpte_index]; if(!ENTRY_PRESENT(vmi->os_type, pdpte_value)) { continue; } if(PAGE_SIZE(pdpte_value)) { page_info_t *info = g_malloc0(sizeof(page_info_t)); info->vaddr = (pml4e_index << 39) | (pdpte_index << 30); info->paddr = get_gigpage_ia32e(info->vaddr, pdpte_value); info->size = VMI_PS_1GB; info->x86_ia32e.pml4e_location = pml4e_location; info->x86_ia32e.pml4e_value = pml4e_value; info->x86_ia32e.pdpte_location = pdpte_location; info->x86_ia32e.pdpte_value = pdpte_value; ret = g_slist_prepend(ret, info); continue; } uint64_t pgd_location = pdpte_value & VMI_BIT_MASK(12,51); if (VMI_PS_4KB != vmi_read_pa(vmi, pgd_location, pgd_page, VMI_PS_4KB)) { continue; } uint64_t pgde_index; for(pgde_index = 0; pgde_index < IA32E_ENTRIES_PER_PAGE; pgde_index++, pgd_location += entry_size) { uint64_t pgd_value = pgd_page[pgde_index]; if(ENTRY_PRESENT(vmi->os_type, pgd_value)) { if(PAGE_SIZE(pgd_value)) { page_info_t *info = g_malloc0(sizeof(page_info_t)); info->vaddr = (pml4e_index << 39) | (pdpte_index << 30) | (pgde_index << 21); info->paddr = get_2megpage_ia32e(info->vaddr, pgd_value); info->size = VMI_PS_2MB; info->x86_ia32e.pml4e_location = pml4e_location; info->x86_ia32e.pml4e_value = pml4e_value; info->x86_ia32e.pdpte_location = pdpte_location; info->x86_ia32e.pdpte_value = pdpte_value; info->x86_ia32e.pgd_location = pgd_location; info->x86_ia32e.pgd_value = pgd_value; ret = g_slist_prepend(ret, info); continue; } uint64_t pte_location = (pgd_value & VMI_BIT_MASK(12,51)); if (VMI_PS_4KB != vmi_read_pa(vmi, pte_location, pt_page, VMI_PS_4KB)) { continue; } uint64_t pte_index; for(pte_index = 0; pte_index < IA32E_ENTRIES_PER_PAGE; pte_index++, pte_location += entry_size) { uint64_t pte_value = pt_page[pte_index]; if(ENTRY_PRESENT(vmi->os_type, pte_value)) { page_info_t *info = g_malloc0(sizeof(page_info_t)); info->vaddr = (pml4e_index << 39) | (pdpte_index << 30) | (pgde_index << 21) | (pte_index << 12); info->paddr = get_paddr_ia32e(info->vaddr, pte_value); info->size = VMI_PS_4KB; info->x86_ia32e.pml4e_location = pml4e_location; info->x86_ia32e.pml4e_value = pml4e_value; info->x86_ia32e.pdpte_location = pdpte_location; info->x86_ia32e.pdpte_value = pdpte_value; info->x86_ia32e.pgd_location = pgd_location; info->x86_ia32e.pgd_value = pgd_value; info->x86_ia32e.pte_location = pte_location; info->x86_ia32e.pte_value = pte_value; ret = g_slist_prepend(ret, info); continue; } } } } } } free(pt_page); free(pgd_page); free(pdpt_page); free(pml4_page); return ret; }
static void extract_ca_file(filedelete* f, drakvuf_t drakvuf, const drakvuf_trap_info_t* info, vmi_instance_t vmi, addr_t control_area, access_context_t* ctx, const char* filename, uint64_t fo_flags) { addr_t subsection = control_area + f->control_area_size; addr_t segment = 0; addr_t test = 0; addr_t test2 = 0; size_t filesize = 0; /* Check whether subsection points back to the control area */ ctx->addr = control_area + f->offsets[CONTROL_AREA_SEGMENT]; if ( VMI_FAILURE == vmi_read_addr(vmi, ctx, &segment) ) return; ctx->addr = segment + f->offsets[SEGMENT_CONTROLAREA]; if ( VMI_FAILURE == vmi_read_addr(vmi, ctx, &test) || test != control_area ) return; ctx->addr = segment + f->offsets[SEGMENT_SIZEOFSEGMENT]; if ( VMI_FAILURE == vmi_read_64(vmi, ctx, &test) ) return; ctx->addr = segment + f->offsets[SEGMENT_TOTALNUMBEROFPTES]; if ( VMI_FAILURE == vmi_read_32(vmi, ctx, (uint32_t*)&test2) ) return; if ( test != (test2 * 4096) ) return; const int curr_sequence_number = ++f->sequence_number; char* file = NULL; if ( asprintf(&file, "%s/file.%06d.mm", f->dump_folder, curr_sequence_number) < 0 ) return; FILE* fp = fopen(file, "w"); free(file); if (!fp) return; while (subsection) { /* Check whether subsection points back to the control area */ ctx->addr = subsection + f->offsets[SUBSECTION_CONTROLAREA]; if ( VMI_FAILURE == vmi_read_addr(vmi, ctx, &test) || test != control_area ) break; addr_t base = 0; addr_t start = 0; uint32_t ptes = 0; ctx->addr = subsection + f->offsets[SUBSECTION_SUBSECTIONBASE]; if ( VMI_FAILURE == vmi_read_addr(vmi, ctx, &base) ) break; ctx->addr = subsection + f->offsets[SUBSECTION_PTESINSUBSECTION]; if ( VMI_FAILURE == vmi_read_32(vmi, ctx, &ptes) ) break; ctx->addr = subsection + f->offsets[SUBSECTION_STARTINGSECTOR]; if ( VMI_FAILURE == vmi_read_32(vmi, ctx, (uint32_t*)&start) ) break; /* * The offset into the file is stored implicitely * based on the PTE's location within the Subsection. */ addr_t subsection_offset = start * 0x200; addr_t ptecount; for (ptecount=0; ptecount < ptes; ptecount++) { addr_t pteoffset = base + f->mmpte_size * ptecount; addr_t fileoffset = subsection_offset + ptecount * 0x1000; addr_t pte = 0; ctx->addr = pteoffset; if ( VMI_FAILURE == vmi_read(vmi, ctx, f->mmpte_size, &pte, NULL) ) break; if ( ENTRY_PRESENT(1, pte) ) { uint8_t page[4096]; if ( VMI_FAILURE == vmi_read_pa(vmi, VMI_BIT_MASK(12,48) & pte, 4096, &page, NULL) ) continue; if ( !fseek ( fp, fileoffset, SEEK_SET ) ) { if ( fwrite(page, 4096, 1, fp) ) filesize = MAX(filesize, fileoffset + 4096); } } } ctx->addr = subsection + f->offsets[SUBSECTION_NEXTSUBSECTION]; if ( !vmi_read_addr(vmi, ctx, &subsection) ) break; } fclose(fp); print_extraction_information(f, drakvuf, info, filename, filesize, fo_flags, curr_sequence_number); save_file_metadata(f, info, curr_sequence_number, control_area, filename, filesize, fo_flags); }
int main( int argc, char **argv) { vmi_instance_t vmi; char *filename = NULL; FILE *f = NULL; unsigned char memory[PAGE_SIZE]; unsigned char zeros[PAGE_SIZE]; memset(zeros, 0, PAGE_SIZE); uint32_t offset = 0; addr_t address = 0; /* this is the VM or file that we are looking at */ char *name = argv[1]; /* this is the file name to write the memory image to */ filename = strndup(argv[2], 50); /* initialize the libvmi library */ if (vmi_init(&vmi, VMI_AUTO | VMI_INIT_PARTIAL, name) == VMI_FAILURE) { printf("Failed to init LibVMI library.\n"); goto error_exit; } /* open the file for writing */ if ((f = fopen(filename, "w+")) == NULL) { printf("failed to open file for writing.\n"); goto error_exit; } while (address < vmi_get_memsize(vmi)) { /* write memory to file */ if (PAGE_SIZE == vmi_read_pa(vmi, address, memory, PAGE_SIZE)) { /* memory mapped, just write to file */ size_t written = fwrite(memory, 1, PAGE_SIZE, f); if (written != PAGE_SIZE) { printf("failed to write memory to file.\n"); goto error_exit; } } else { /* memory not mapped, write zeros to maintain offset */ size_t written = fwrite(zeros, 1, PAGE_SIZE, f); if (written != PAGE_SIZE) { printf("failed to write zeros to file.\n"); goto error_exit; } } /* move on to the next page */ address += PAGE_SIZE; } error_exit: if (f) fclose(f); /* cleanup any memory associated with the libvmi instance */ vmi_destroy(vmi); return 0; }
status_t find_kdbg_address( vmi_instance_t vmi, addr_t *kdbg_pa, addr_t *kernel_va) { dbprint(VMI_DEBUG_MISC, "**Trying find_kdbg_address\n"); status_t ret = VMI_FAILURE; *kdbg_pa = 0; addr_t paddr = 0; unsigned char haystack[VMI_PS_4KB]; addr_t memsize = vmi_get_max_physical_address(vmi); void *bm64 = boyer_moore_init((unsigned char *)"\x00\xf8\xff\xffKDBG", 8); void *bm32 = boyer_moore_init((unsigned char *)"\x00\x00\x00\x00\x00\x00\x00\x00KDBG", 12); uint32_t find_ofs_64 = 0xc, find_ofs_32 = 0x8, find_ofs = 0; for (; paddr<memsize; paddr+=VMI_PS_4KB) { find_ofs = 0; if (VMI_FAILURE == vmi_read_pa(vmi, paddr, VMI_PS_4KB, &haystack, NULL)) continue; int match_offset = boyer_moore2(bm64, haystack, VMI_PS_4KB); if (-1 != match_offset) { find_ofs = find_ofs_64; } else { match_offset = boyer_moore2(bm32, haystack, VMI_PS_4KB); } if (-1 != match_offset) { if (!find_ofs) { find_ofs = find_ofs_32; } // Read "KernBase" from the haystack long unsigned int kernbase_offset = 0; kdbg_symbol_offset("KernBase", &kernbase_offset); if ( match_offset - find_ofs + kernbase_offset + sizeof(uint64_t) >= VMI_PS_4KB ) continue; memcpy(kernel_va, &haystack[(unsigned int) match_offset - find_ofs + kernbase_offset], sizeof(uint64_t)); *kdbg_pa = paddr + (unsigned int) match_offset - find_ofs; ret = VMI_SUCCESS; break; } } dbprint(VMI_DEBUG_MISC, "--Found KdDebuggerDataBlock at PA %.16"PRIx64"\n", *kdbg_pa); boyer_moore_fini(bm32); boyer_moore_fini(bm64); 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; }
void print_guid(vmi_instance_t vmi, addr_t kernel_base_p, uint8_t* pe) { uint32_t size_of_image; bool debug_directory_valid = 0; struct pe_header *pe_header = NULL; uint16_t optional_header_type = 0; struct optional_header_pe32 *oh32 = NULL; struct optional_header_pe32plus *oh32plus = NULL; peparse_assign_headers(pe, NULL, &pe_header, &optional_header_type, NULL, &oh32, &oh32plus); addr_t debug_offset = peparse_get_idd_rva(IMAGE_DIRECTORY_ENTRY_DEBUG, NULL, NULL, oh32, oh32plus); switch(optional_header_type) { case IMAGE_PE32_MAGIC: size_of_image=oh32->size_of_image; break; case IMAGE_PE32_PLUS_MAGIC: size_of_image=oh32plus->size_of_image; break; default: return; } struct image_debug_directory debug_directory = { 0 }; struct cv_info_pdb70 *pdb_header = g_malloc0(sizeof(struct cv_info_pdb70)+PDB_FILENAME_LENGTH+1); if ( VMI_FAILURE == vmi_read_pa(vmi, kernel_base_p + debug_offset, sizeof(struct image_debug_directory), (uint8_t *)&debug_directory, NULL) ) return; printf("\tPE GUID: %.8x%.5x\n",pe_header->time_date_stamp,size_of_image); switch(debug_directory.type) { case IMAGE_DEBUG_TYPE_CODEVIEW: // OK debug_directory_valid = 1; break; case IMAGE_DEBUG_TYPE_MISC: printf("This operating system uses .dbg instead of .pdb\n"); return; default: //printf("The debug directory header is not in CodeView format, will do a brute-force search!\n"); break; } if (debug_directory_valid) { if(debug_directory.size_of_data > VMI_PS_4KB/4) { // Normal size of the debug directory on Windows 7 for example is 0x25 bytes. printf("The size of the debug directory is huge, something might be wrong.\n"); goto done; } if ( VMI_FAILURE == vmi_read_pa(vmi, kernel_base_p + debug_directory.address_of_raw_data, sizeof(struct cv_info_pdb70)+PDB_FILENAME_LENGTH, pdb_header, NULL) ) goto done; // The PDB header has to be PDB 7.0 // http://www.debuginfo.com/articles/debuginfomatch.html if(RSDS != pdb_header->cv_signature) { printf("The CodeView debug information has to be in PDB 7.0 for the kernel!\n"); goto done; } } else { if(!kernel_debug_search(vmi, pdb_header)) goto done; } printf("\tPDB GUID: "); printf("%.8x", pdb_header->signature.data1); printf("%.4x", pdb_header->signature.data2); printf("%.4x", pdb_header->signature.data3); int c; for(c=0;c<8;c++) printf("%.2x", pdb_header->signature.data4[c]); printf("%.1x", pdb_header->age & 0xf); printf("\n"); printf("\tKernel filename: %s\n", (char*)pdb_header->pdb_file_name); if(!strcmp("ntoskrnl.pdb", (char*)pdb_header->pdb_file_name)) { printf("\tSingle-processor without PAE\n"); } else if(!strcmp("ntkrnlmp.pdb", (char*)pdb_header->pdb_file_name)) { printf("\tMulti-processor without PAE\n"); } else if(!strcmp("ntkrnlpa.pdb", (char*)pdb_header->pdb_file_name)) { printf("\tSingle-processor with PAE (version 5.0 and higher)\n"); } else if(!strcmp("ntkrpamp.pdb", (char*)pdb_header->pdb_file_name)) { printf("\tMulti-processor with PAE (version 5.0 and higher)\n"); } done: free(pdb_header); }