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 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 cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { poolmon* p = (poolmon*)info->trap->data; page_mode_t pm = drakvuf_get_page_mode(drakvuf); reg_t pool_type, size; char tag[5] = { [0 ... 4] = '\0' }; struct pooltag* s = NULL; const char* pool_type_str; access_context_t ctx; ctx.translate_mechanism = VMI_TM_PROCESS_DTB; ctx.dtb = info->regs->cr3; if (pm == VMI_PM_IA32E) { pool_type = info->regs->rcx; size = info->regs->rdx; *(reg_t*)tag = info->regs->r8; } else { vmi_lock_guard vmi_lg(drakvuf); vmi_instance_t& vmi = vmi_lg.vmi; ctx.addr = info->regs->rsp+12; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)tag) ) return 0; ctx.addr = info->regs->rsp+8; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&size) ) return 0; ctx.addr = info->regs->rsp+4; if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&pool_type) ) return 0; } s = (struct pooltag*)g_tree_lookup(p->pooltag_tree, tag); pool_type_str = pool_type<MaxPoolType ? pool_types[pool_type] : "unknown_pool_type"; switch (p->format) { case OUTPUT_CSV: printf("poolmon," FORMAT_TIMEVAL ",%" PRIu32 ",0x%" PRIx64 ",\"%s\",%" PRIi64 ",%s,%s,%" PRIu64 "", UNPACK_TIMEVAL(info->timestamp), info->vcpu, info->regs->cr3, info->proc_data.name, info->proc_data.userid, tag, pool_type_str, size); if (s) printf(",%s,%s", s->source, s->description); break; case OUTPUT_KV: printf("poolmon Time=" FORMAT_TIMEVAL ",PID=%d,PPID=%d,ProcessName=\"%s\"," "Tag=%s,Type=%s,Size=%" PRIu64, UNPACK_TIMEVAL(info->timestamp), info->proc_data.pid, info->proc_data.ppid, info->proc_data.name, tag, pool_type_str, size); if (s) printf(",Source=%s,Description=%s", s->source, s->description); break; default: case OUTPUT_DEFAULT: printf("[POOLMON] TIME:" FORMAT_TIMEVAL " VCPU:%" PRIu32 " CR3:0x%" PRIx64 ",\"%s\" %s:%" PRIi64 " %s (type: %s, size: %" PRIu64 ")", UNPACK_TIMEVAL(info->timestamp), info->vcpu, info->regs->cr3, info->proc_data.name, USERIDSTR(drakvuf), info->proc_data.userid, tag, pool_type_str, size); if (s) printf(": %s,%s", s->source, s->description); break; } printf("\n"); return 0; }
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; }