Example #1
0
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;
}
Example #2
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;
}
Example #3
0
/*
 * 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;
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
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);
        }
Example #7
0
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;
}
Example #8
0
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;
}
Example #9
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;
}
Example #10
0
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;
}
Example #11
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*)&params[0]) )
            goto done;

        ctx.addr = info->regs->rsp + 0xc;
        if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&params[1]) )
            goto done;

        ctx.addr = info->regs->rsp + 0x10;
        if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&params[2]) )
            goto done;

        ctx.addr = info->regs->rsp + 0x14;
        if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, (uint32_t*)&params[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*)&params[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;
}