static event_response_t createsection_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { filedelete* f = (filedelete*)info->trap->data; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); handle_t handle = drakvuf_get_function_argument(drakvuf, info, 7); uint32_t access_mask = drakvuf_get_function_argument(drakvuf, info, 2); std::string filename; // Filter out system handles: those having high bits rised // WARNING Without this target VM could freeze or crash! if (static_cast<int64_t>(handle) < 0LL) goto done; if ( !(0x2 & access_mask) ) // SECTION_MAP_WRITE goto done; filename = get_file_name(f, drakvuf, vmi, info, handle, nullptr, nullptr); if (filename.empty()) filename = "<UNKNOWN>"; f->files[std::make_pair(info->proc_data.pid, handle)] = filename; done: drakvuf_release_vmi(drakvuf); return 0; }
/* * Intercept all handles close and filter file handles. * * The main difficulty is that this handler intercepts not only CloseHandle() * calls but returns from injected functions. To distinguish such situations * we use the regestry of processes/threads being processed. */ static event_response_t close_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { filedelete* f = (filedelete*)info->trap->data; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); addr_t handle = drakvuf_get_function_argument(drakvuf, info, 1); event_response_t response = 0; if (f->use_injector) { /* * Check if closing handle have been changed with NtWriteFile */ auto filename = f->files[std::make_pair(info->proc_data.pid, handle)]; if (filename.empty()) goto done; if ( start_readfile(drakvuf, info, vmi, handle, filename.c_str(), &response) ) goto done; } if (f->files.erase(std::make_pair(info->proc_data.pid, handle)) > 0) { // We detect the fact of closing of the previously modified file. grab_file_by_handle(f, drakvuf, vmi, info, handle); } done: drakvuf_release_vmi(drakvuf); return response; }
/* * NTSTATUS ZwSetInformationFile( * HANDLE FileHandle, * PIO_STATUS_BLOCK IoStatusBlock, * PVOID FileInformation, * ULONG Length, * FILE_INFORMATION_CLASS FileInformationClass * ); * * When FileInformationClass is FileDispositionInformation then FileInformation points to * struct _FILE_DISPOSITION_INFORMATION { * BOOLEAN DeleteFile; * } */ static event_response_t setinformation_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { filedelete* f = (filedelete*)info->trap->data; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); addr_t handle = drakvuf_get_function_argument(drakvuf, info, 1); addr_t fileinfo = drakvuf_get_function_argument(drakvuf, info, 3); uint32_t fileinfoclass = drakvuf_get_function_argument(drakvuf, info, 5); event_response_t response = 0; if (fileinfoclass == FILE_DISPOSITION_INFORMATION) { uint8_t del = 0; access_context_t ctx; ctx.translate_mechanism = VMI_TM_PROCESS_DTB; ctx.dtb = info->regs->cr3; ctx.addr = fileinfo; if ( VMI_FAILURE == vmi_read_8(vmi, &ctx, &del) ) goto done; if (del) { auto filename = get_file_name(f, drakvuf, vmi, info, handle, nullptr, nullptr); if (filename.empty()) filename = "<UNKNOWN>"; f->files[std::make_pair(info->proc_data.pid, handle)] = filename; } } done: drakvuf_release_vmi(drakvuf); return response; }
filedelete::filedelete(drakvuf_t drakvuf, const filedelete_config* c, output_format_t output) : sequence_number() { this->pm = drakvuf_get_page_mode(drakvuf); vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); this->domid = vmi_get_vmid(vmi); drakvuf_release_vmi(drakvuf); this->dump_folder = c->dump_folder; this->format = output; this->use_injector = c->filedelete_use_injector; if (!this->use_injector) { assert(sizeof(traps)/sizeof(traps[0]) > 2); register_trap(drakvuf, "NtSetInformationFile", &traps[0], setinformation_cb); register_trap(drakvuf, "NtWriteFile", &traps[1], writefile_cb); register_trap(drakvuf, "NtClose", &traps[2], close_cb); /* TODO register_trap(drakvuf, "NtDeleteFile", &traps[3], deletefile_cb); register_trap(drakvuf, "ZwDeleteFile", &traps[4], deletefile_cb); */ } else { this->queryobject_va = get_function_va(drakvuf, "ntoskrnl.exe", "ZwQueryVolumeInformationFile"); this->readfile_va = get_function_va(drakvuf, "ntoskrnl.exe", "ZwReadFile"); this->waitobject_va = get_function_va(drakvuf, "ntoskrnl.exe", "ZwWaitForSingleObject"); this->exallocatepool_va = get_function_va(drakvuf, "ntoskrnl.exe", "ExAllocatePoolWithTag"); this->exfreepool_va = get_function_va(drakvuf, "ntoskrnl.exe", "ExFreePoolWithTag"); assert(sizeof(traps)/sizeof(traps[0]) > 3); register_trap(drakvuf, "NtSetInformationFile", &traps[0], setinformation_cb); register_trap(drakvuf, "NtWriteFile", &traps[1], writefile_cb); register_trap(drakvuf, "NtClose", &traps[2], close_cb); register_trap(drakvuf, "ZwCreateSection", &traps[3], createsection_cb); } this->offsets = (size_t*)malloc(sizeof(size_t)*__OFFSET_MAX); if ( !drakvuf_get_struct_members_array_rva(drakvuf, offset_names, __OFFSET_MAX, this->offsets) ) throw -1; if ( !drakvuf_get_struct_size(drakvuf, "_CONTROL_AREA", &this->control_area_size) ) throw -1; if ( VMI_PM_LEGACY == this->pm ) this->mmpte_size = 4; else this->mmpte_size = 8; }
static event_response_t writefile_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { filedelete* f = (filedelete*)info->trap->data; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); addr_t handle = drakvuf_get_function_argument(drakvuf, info, 1); auto filename = get_file_name(f, drakvuf, vmi, info, handle, nullptr, nullptr); if (filename.empty()) filename = "<UNKNOWN>"; f->files[std::make_pair(info->proc_data.pid, handle)] = filename; drakvuf_release_vmi(drakvuf); return 0; }
static event_response_t file_name_cb(drakvuf_t drakvuf, drakvuf_trap_info_t *info) { vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); struct file_watch *watch = (struct file_watch*)info->trap->data; filetracer *f = watch->f; if (info->trap_pa == watch->file_name_buffer) { addr_t file_name = 0; uint16_t length = 0; vmi_read_addr_pa(vmi, watch->file_name_buffer, &file_name); vmi_read_16_pa(vmi, watch->file_name_length, &length); //printf("File name @ 0x%lx. Length: %u\n", file_name, length); if (file_name && length > 0 && length < VMI_PS_4KB) { char *procname = drakvuf_get_current_process_name(drakvuf, info->vcpu, info->regs); unicode_string_t str = { .contents = NULL }; str.length = length; str.encoding = "UTF-16"; str.contents = (unsigned char *)g_malloc0(length); vmi_read_va(vmi, file_name, 0, str.contents, length); unicode_string_t str2 = { .contents = NULL }; status_t rc = vmi_convert_str_encoding(&str, &str2, "UTF-8"); if (VMI_SUCCESS == rc) { switch(f->format) { case OUTPUT_CSV: printf("filetracer,%" PRIu32 ",0x%" PRIx64 ",%s,%s\n", info->vcpu, info->regs->cr3, procname, str2.contents); break; default: case OUTPUT_DEFAULT: printf("[FILETRACER] VCPU:%" PRIu32 " CR3:0x%" PRIx64 ",%s %s\n", info->vcpu, info->regs->cr3, procname, str2.contents); break; }; g_free(str2.contents); } free(str.contents); free(procname); //printf("Requesting to free writetrap @ %p\n", info->trap); info->trap->data=f; drakvuf_remove_trap(drakvuf, info->trap, free_writetrap); }
static GSList* create_trap_config(drakvuf_t drakvuf, syscalls *s, symbols_t *symbols) { GSList *ret = NULL; unsigned long i; addr_t module_list, ntoskrnl; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); if(VMI_FAILURE == vmi_read_addr_ksym(vmi, (char *)"PsLoadedModuleList", &module_list)) goto done; if( !drakvuf_get_module_base_addr(drakvuf, module_list, "ntoskrnl.exe", &ntoskrnl) ) goto done; for (i=0; i < symbols->count; i++) { const struct symbol *symbol = &symbols->symbols[i]; if (strncmp(symbol->name, "Nt", 2)) continue; //if (strcmp(symbol->name, "NtCallbackReturn")) // continue; drakvuf_trap_t *trap = (drakvuf_trap_t *)g_malloc0(sizeof(drakvuf_trap_t)); trap->breakpoint.lookup_type = LOOKUP_PID; trap->breakpoint.pid = 4; trap->breakpoint.addr_type = ADDR_VA; trap->breakpoint.addr = ntoskrnl + symbol->rva; trap->breakpoint.module = "ntoskrnl.exe"; trap->name = g_strdup(symbol->name); trap->type = BREAKPOINT; trap->cb = cb; trap->data = s; ret = g_slist_prepend(ret, trap); } done: drakvuf_release_vmi(drakvuf); return ret; }
event_response_t queryobject_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { wrapper_t* injector = (wrapper_t*)info->trap->data; auto response = 0; uint32_t thread_id = 0; if (info->regs->cr3 != injector->target_cr3) return 0; if ( !drakvuf_get_current_thread_id(drakvuf, info, &thread_id) || !injector->target_thread_id || thread_id != injector->target_thread_id ) return 0; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); if (info->regs->rax) goto handled; else { access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = injector->ntqueryobject_info.out, }; struct FILE_FS_DEVICE_INFORMATION dev_info = { 0 }; if ((VMI_FAILURE == vmi_read(vmi, &ctx, sizeof(struct FILE_FS_DEVICE_INFORMATION), &dev_info, NULL))) { PRINT_DEBUG("[FILEDELETE2] [QueryObject] Failed to read FsDeviceInformation\n"); goto err; } if (7 != dev_info.device_type) // FILE_DEVICE_DISK goto handled; injector->ntreadfile_info.bytes_read = 0UL; addr_t pool = find_pool(injector->f->pools); if (!pool) { if (inject_allocate_pool(drakvuf, info, vmi, injector)) { response = VMI_EVENT_RESPONSE_SET_REGISTERS; goto done; } } else { injector->pool = pool; if (inject_readfile(drakvuf, info, vmi, injector)) { response = VMI_EVENT_RESPONSE_SET_REGISTERS; goto done; } } } err: PRINT_DEBUG("[FILEDELETE2] [QueryObject] Error. Stop processing (CR3 0x%lx, TID %d).\n", info->regs->cr3, thread_id); handled: response = finish_readfile(drakvuf, info, vmi, false); done: drakvuf_release_vmi(drakvuf); return response; } /* * Drakvuf must be locked/unlocked in the caller */ static bool start_readfile(drakvuf_t drakvuf, drakvuf_trap_info_t* info, vmi_instance_t vmi, handle_t handle, const char* filename, event_response_t* response) { *response = VMI_EVENT_RESPONSE_NONE; filedelete* f = (filedelete*)info->trap->data; uint64_t fo_flags = 0; if (!get_file_object_flags(drakvuf, info, vmi, f, handle, &fo_flags)) return 0; bool is_synchronous = (fo_flags & FO_SYNCHRONOUS_IO); if (!is_synchronous) return 0; if ( 0 == info->proc_data.base_addr ) { PRINT_DEBUG("[FILEDELETE2] Failed to get process base on vCPU 0x%d\n", info->vcpu); return 0; } uint32_t target_thread_id = 0; if ( !drakvuf_get_current_thread_id(drakvuf, info, &target_thread_id) || !target_thread_id ) { PRINT_DEBUG("[FILEDELETE2] Failed to get Thread ID\n"); return 0; } /* * Check if process/thread is being processed. If so skip it. Add it into * regestry otherwise. */ auto thread = std::make_pair(info->regs->cr3, target_thread_id); auto thread_it = f->closing_handles.find(thread); auto map_end = f->closing_handles.end(); if (map_end != thread_it) { bool handled = thread_it->second; if (handled) { f->files.erase(std::make_pair(info->proc_data.pid, handle)); f->closing_handles.erase(thread); } return 0; } else f->closing_handles[thread] = false; /* * Real function body. * * Now we are sure this is new call to NtClose (not result of function injection) and * the Handle have been modified in NtWriteFile. So we should save it on the host. */ wrapper_t* injector = (wrapper_t*)g_malloc0(sizeof(wrapper_t)); if (!injector) return 0; injector->bp = (drakvuf_trap_t*)g_malloc0(sizeof(drakvuf_trap_t)); if (!injector->bp) { g_free(injector); return 0; } injector->f = f; injector->bp->name = info->trap->name; injector->handle = handle; injector->fo_flags = fo_flags; injector->is32bit = (f->pm != VMI_PM_IA32E); injector->target_cr3 = info->regs->cr3; injector->curr_sequence_number = -1; injector->eprocess_base = info->proc_data.base_addr; injector->target_thread_id = target_thread_id; memcpy(&injector->saved_regs, info->regs, sizeof(x86_registers_t)); if (inject_queryobject(drakvuf, info, vmi, injector)) { *response = VMI_EVENT_RESPONSE_SET_REGISTERS; return 1; } memcpy(info->regs, &injector->saved_regs, sizeof(x86_registers_t)); return 0; }
event_response_t readfile_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { wrapper_t* injector = (wrapper_t*)info->trap->data; filedelete* f = injector->f; if (info->regs->cr3 != injector->target_cr3) return 0; uint32_t thread_id = 0; if (!drakvuf_get_current_thread_id(drakvuf, info, &thread_id)) return 0; if (thread_id != injector->target_thread_id) return 0; auto response = 0; access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = injector->ntreadfile_info.io_status_block, }; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); const uint32_t status = info->regs->rax; uint32_t isb_status = 0; // `isb` is `IO_STATUS_BLOCK` size_t isb_size = 0; bool is_success = false; auto filename = f->files[std::make_pair(info->proc_data.pid, injector->handle)]; if (injector->is32bit) { struct IO_STATUS_BLOCK_32 io_status_block; if ((VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(io_status_block), &io_status_block, NULL))) goto err; isb_status = io_status_block.status; isb_size = io_status_block.info; } else { struct IO_STATUS_BLOCK_64 io_status_block; if ((VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(io_status_block), &io_status_block, NULL))) goto err; isb_status = io_status_block.status; isb_size = io_status_block.info; } if ( STATUS_SUCCESS == status && isb_size ) { if (injector->curr_sequence_number < 0) injector->curr_sequence_number = ++f->sequence_number; const int curr_sequence_number = injector->curr_sequence_number; void* buffer = g_malloc0(isb_size); ctx.addr = injector->ntreadfile_info.out; if (VMI_FAILURE == vmi_read(vmi, &ctx, isb_size, buffer, NULL)) { g_free(buffer); goto err; } bool success = save_file_chunk(f, curr_sequence_number, buffer, isb_size); g_free(buffer); if (!success) goto err; injector->ntreadfile_info.bytes_read += isb_size; if (BYTES_TO_READ == isb_size) { if (inject_readfile(drakvuf, info, vmi, injector)) { response = VMI_EVENT_RESPONSE_SET_REGISTERS; goto done; } else { goto err; } } else { auto filesize = injector->ntreadfile_info.bytes_read; print_extraction_information(f, drakvuf, info, filename.c_str(), filesize, injector->fo_flags, curr_sequence_number); save_file_metadata(f, info, curr_sequence_number, 0, filename.c_str(), filesize, injector->fo_flags); } } else if (STATUS_END_OF_FILE != status) { if (injector->ntreadfile_info.bytes_read) save_file_metadata(f, info, injector->curr_sequence_number, 0, filename.c_str(), injector->ntreadfile_info.bytes_read, injector->fo_flags, status); PRINT_DEBUG("[FILEDELETE2] [ReadFile] Failed to read %s with status 0x%lx and IO_STATUS_BLOCK = { Status 0x%x; Size 0x%lx} \n", f->files[std::make_pair(info->proc_data.pid, injector->handle)].c_str(), info->regs->rax, isb_status, isb_size); goto err; } is_success = true; goto handled; err: PRINT_DEBUG("[FILEDELETE2] [ReadFile] Error. Stop processing (CR3 0x%lx, TID %d, FileName '%s', status 0x%lx).\n", info->regs->cr3, thread_id, f->files[std::make_pair(info->proc_data.pid, injector->handle)].c_str(), info->regs->rax); handled: response = finish_readfile(drakvuf, info, vmi, is_success); done: drakvuf_release_vmi(drakvuf); return response; } event_response_t exallocatepool_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { wrapper_t* injector = (wrapper_t*)info->trap->data; auto response = 0; uint32_t thread_id = 0; vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); if (info->regs->cr3 != injector->target_cr3) goto done; if ( !drakvuf_get_current_thread_id(drakvuf, info, &thread_id) || !injector->target_thread_id || thread_id != injector->target_thread_id ) goto done; if (info->regs->rax) { injector->f->pools[info->regs->rax] = true; injector->pool = info->regs->rax; if (inject_readfile(drakvuf, info, vmi, injector)) { response = VMI_EVENT_RESPONSE_SET_REGISTERS; goto done; } else { goto err; } } err: PRINT_DEBUG("[FILEDELETE2] [ExAllocatePoolWithTag] Error. Stop processing (CR3 0x%lx, TID %d).\n", info->regs->cr3, thread_id); response = finish_readfile(drakvuf, info, vmi, false); done: drakvuf_release_vmi(drakvuf); return response; }
static addr_t place_string_on_stack_32(vmi_instance_t vmi, drakvuf_trap_info_t* info, addr_t addr, void const* str, size_t str_len) { if (!str) return 0; const uint32_t string_align = 64; const size_t len = str_len + 2;// null terminated string // the stack has to be aligned _not_ to 0x4 but to 64 // for special instructions operating on strings to work correctly // this string has to be aligned as well! addr -= len + string_align - (len % string_align); access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = addr, }; if (VMI_FAILURE == vmi_write(vmi, &ctx, len, (void*) str, NULL)) return 0; return addr; } static addr_t place_string_on_stack_64(vmi_instance_t vmi, drakvuf_trap_info_t* info, addr_t addr, void const* str, size_t str_len) { if (!str) return addr; // String length with null terminator size_t len = str_len + 2; addr_t orig_addr = addr; addr -= len; // Align string address on 32B boundary (for SSE2 instructions). addr &= ~0x1f; size_t buf_len = orig_addr - addr; void* buf = g_malloc0(buf_len); if (!buf) return 0; memcpy(buf, str, str_len); access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = addr, }; status_t status = vmi_write(vmi, &ctx, buf_len, buf, NULL); g_free(buf); return status == VMI_FAILURE ? 0 : addr; } static addr_t place_struct_on_stack_32(vmi_instance_t vmi, drakvuf_trap_info_t* info, addr_t addr, void* data, size_t size) { const uint32_t stack_align = 64; addr -= size; addr -= addr % stack_align; access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = addr, }; status_t status = vmi_write(vmi, &ctx, size, data, NULL); return status == VMI_FAILURE ? 0 : addr; } static addr_t place_struct_on_stack_64(vmi_instance_t vmi, drakvuf_trap_info_t* info, addr_t addr, void* data, size_t size) { /* According to Microsoft Doc "Building C/C++ Programs": * > The alignment of the beginning of a structure or a union is the maximum * > alignment of any individual member. */ addr -= size; addr &= ~0xf; // Align stack access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, .addr = addr, }; status_t status = vmi_write(vmi, &ctx, size, data, NULL); return status == VMI_FAILURE ? 0 : addr; } static bool setup_stack_32(vmi_instance_t vmi, drakvuf_trap_info_t* info, struct argument args[], int nb_args) { addr_t addr = info->regs->rsp; // make room for strings and structs into guest's stack for (int i = 0; i < nb_args; i++) { switch (args[i].type) { case ARGUMENT_STRING: addr = place_string_on_stack_32(vmi, info, addr, args[i].data, args[i].size); if ( !addr ) goto err; args[i].data_on_stack = addr; break; case ARGUMENT_STRUCT: addr = place_struct_on_stack_32(vmi, info, addr, args[i].data, args[i].size); if ( !addr ) goto err; args[i].data_on_stack = addr; break; case ARGUMENT_INT: args[i].data_on_stack = (uint64_t)args[i].data; break; default: goto err; } } access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, }; // write parameters into guest's stack for (int i = nb_args-1; i >= 0; i--) { addr -= 0x4; ctx.addr = addr; if (VMI_FAILURE == vmi_write_32(vmi, &ctx, (uint32_t*)&args[i].data_on_stack)) goto err; } // save the return address addr -= 0x4; ctx.addr = addr; if (VMI_FAILURE == vmi_write_32(vmi, &ctx, (uint32_t*) &info->regs->rip)) goto err; // grow the stack info->regs->rsp = addr; return 1; err: return 0; } static bool setup_stack_64(vmi_instance_t vmi, drakvuf_trap_info_t* info, struct argument args[], int nb_args) { uint64_t nul64 = 0; access_context_t ctx = { .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3, }; addr_t addr = info->regs->rsp; if ( args ) { // make room for strings and structs into guest's stack for (int i = 0; i < nb_args; i++) { switch (args[i].type) { case ARGUMENT_STRING: addr = place_string_on_stack_64(vmi, info, addr, args[i].data, args[i].size); if ( !addr ) goto err; args[i].data_on_stack = addr; break; case ARGUMENT_STRUCT: addr = place_struct_on_stack_64(vmi, info, addr, args[i].data, args[i].size); if ( !addr ) goto err; args[i].data_on_stack = addr; break; case ARGUMENT_INT: args[i].data_on_stack = (uint64_t)args[i].data; break; default: goto err; } } /* According to Microsoft Doc "Building C/C++ Programs": * > The stack will always be maintained 16-byte aligned, except within the prolog * > (for example, after the return address is pushed), and except where indicated * > in Function Types for a certain class of frame functions. * * So place one extra argument to achieve alignment just before CALL instruction. */ if (nb_args % 2) { addr -= 0x8; ctx.addr = addr; if (VMI_FAILURE == vmi_write_64(vmi, &ctx, &nul64)) goto err; } // http://www.codemachine.com/presentations/GES2010.TRoy.Slides.pdf // // First 4 parameters to functions are always passed in registers // P1=rcx, P2=rdx, P3=r8, P4=r9 // 5th parameter onwards (if any) passed via the stack // write parameters (5th onwards) into guest's stack for (int i = nb_args-1; i > 3; i--) { addr -= 0x8; ctx.addr = addr; if (VMI_FAILURE == vmi_write_64(vmi, &ctx, &(args[i].data_on_stack)) ) goto err; } switch (nb_args) { default: // p4 info->regs->r9 = args[3].data_on_stack; // fall through case 3: // p3 info->regs->r8 = args[2].data_on_stack; // fall through case 2: // p2 info->regs->rdx = args[1].data_on_stack; // fall through case 1: // p1 info->regs->rcx = args[0].data_on_stack; // fall through case 0: break; } } // allocate 0x20 "homing space" for (int i = 0; i < 4; i++) { addr -= 0x8; ctx.addr = addr; if (VMI_FAILURE == vmi_write_64(vmi, &ctx, &nul64)) goto err; } // save the return address addr -= 0x8; ctx.addr = addr; if (VMI_FAILURE == vmi_write_64(vmi, &ctx, &info->regs->rip)) goto err; // grow the stack info->regs->rsp = addr; return 1; err: return 0; } bool setup_stack_locked( drakvuf_t drakvuf, vmi_instance_t vmi, drakvuf_trap_info_t* info, struct argument args[], int nb_args) { bool is32bit = (drakvuf_get_page_mode(drakvuf) != VMI_PM_IA32E); return is32bit ? setup_stack_32(vmi, info, args, nb_args) : setup_stack_64(vmi, info, args, nb_args); } bool setup_stack( drakvuf_t drakvuf, drakvuf_trap_info_t* info, struct argument args[], int nb_args) { vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); bool success = setup_stack_locked(drakvuf, vmi, info, args, nb_args); drakvuf_release_vmi(drakvuf); return success; }
static event_response_t hook_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { bsodmon* f = static_cast<bsodmon*>(info->trap->data); vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf); access_context_t ctx; ctx.translate_mechanism = VMI_TM_PROCESS_DTB; ctx.dtb = info->regs->cr3; uint64_t code = 0; uint64_t params[4] = { 0 }; const char* bugcheck_name = "UNKNOWN_CODE" ; gchar* escaped_pname = NULL; bool is32bit = drakvuf_get_page_mode(drakvuf) != VMI_PM_IA32E; if (is32bit) { ctx.addr = info->regs->rsp + 4; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&code) ) goto done; ctx.addr = info->regs->rsp + 8; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)¶ms[0]) ) goto done; ctx.addr = info->regs->rsp + 0xc; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)¶ms[1]) ) goto done; ctx.addr = info->regs->rsp + 0x10; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)¶ms[2]) ) goto done; ctx.addr = info->regs->rsp + 0x14; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)¶ms[3]) ) goto done; } else { code = info->regs->rcx; params[0] = info->regs->rdx; params[1] = info->regs->r8; params[2] = info->regs->r9; ctx.addr = info->regs->rsp + 0x20; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)¶ms[3]) ) goto done; } if ( f->bugcheck_map.find( code ) != f->bugcheck_map.end() ) bugcheck_name = f->bugcheck_map[ code ]; switch (f->format) { case OUTPUT_CSV: printf("bsodmon," FORMAT_TIMEVAL ",%" PRIu32 ",0x%" PRIx64 ",\"%s\",%" PRIi64 ",%" PRIx64 ",\"%s\",%" PRIx64 ",%" PRIx64 ",%" PRIx64 ",%" PRIx64 "\n", UNPACK_TIMEVAL(info->timestamp), info->vcpu, info->regs->cr3, info->proc_data.name, info->proc_data.userid, code, bugcheck_name, params[0], params[1], params[2], params[3]); break; case OUTPUT_KV: printf("bsodmon Time=" FORMAT_TIMEVAL ",PID=%d,PPID=%d,ProcessName=\"%s\",BugCheckCode=%" PRIx64 ",BugCheckName=\"%s\",BugCheckParameter1=%" PRIx64 ",BugCheckParameter2=%" PRIx64 ",BugCheckParameter2=%" PRIx64 ",BugCheckParameter4=%" PRIx64 "\n", UNPACK_TIMEVAL(info->timestamp), info->proc_data.pid, info->proc_data.ppid, info->proc_data.name, code, bugcheck_name, params[0], params[1], params[2], params[3]); break; case OUTPUT_JSON: escaped_pname = drakvuf_escape_str(info->proc_data.name); printf( "{" "\"Plugin\" : \"bsodmon\"," "\"TimeStamp\" :" "\"" FORMAT_TIMEVAL "\"," "\"VCPU\": %" PRIu32 "," "\"CR3\": %" PRIu64 "," "\"ProcessName\": %s," "\"UserId\": %" PRIu64 "," "\"PID\" : %d," "\"PPID\": %d," "\"BugCheckCode\": %" PRIu64 "," "\"BugCheckName\": \"%s\"," "\"BugCheckParameter1\": %" PRIu64 "," "\"BugCheckParameter2\": %" PRIu64 "," "\"BugCheckParameter3\": %" PRIu64 "," "\"BugCheckParameter4\": %" PRIu64 "}\n", UNPACK_TIMEVAL(info->timestamp), info->vcpu, info->regs->cr3, escaped_pname, info->proc_data.userid, info->proc_data.pid, info->proc_data.ppid, code, bugcheck_name, params[0], params[1], params[2], params[3]); g_free(escaped_pname); break; default: case OUTPUT_DEFAULT: printf("[BSODMON] TIME:" FORMAT_TIMEVAL " VCPU:%" PRIu32 " CR3:0x%" PRIx64 ",\"%s\" %s:%" PRIi64 " BugCheckCode:%" PRIx64 " BugCheckName:%s BugCheckParameter1:%" PRIx64 " BugCheckParameter2:%" PRIx64 " BugCheckParameter3:%" PRIx64 " BugCheckParameter4:%" PRIx64 "\n", UNPACK_TIMEVAL(info->timestamp), info->vcpu, info->regs->cr3, info->proc_data.name, USERIDSTR(drakvuf), info->proc_data.userid, code, bugcheck_name, params[0], params[1], params[2], params[3]); break; } done: drakvuf_release_vmi(drakvuf); if ( f->abort_on_bsod ) drakvuf_interrupt( drakvuf, SIGDRAKVUFERROR); return 0; }