Example #1
0
status_t
peparse_get_image(
    vmi_instance_t vmi,
    const access_context_t *ctx,
    size_t len,
    const uint8_t * const image)
{
    if ( VMI_FAILURE == vmi_read(vmi, ctx, len, (void *)image, NULL) ) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEPARSE: failed to read PE header\n");
        return VMI_FAILURE;
    }

    if (VMI_SUCCESS != peparse_validate_pe_image(image, len)) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEPARSE: failed to validate PE header(s)\n");
        return VMI_FAILURE;
    }

    return VMI_SUCCESS;
}
Example #2
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 #3
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 #4
0
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);
}
Example #5
0
unicode_string_t *
windows_read_unicode_struct(
    vmi_instance_t vmi,
    const access_context_t *ctx)
{
    access_context_t _ctx = *ctx;
    unicode_string_t *us = 0;   // return val
    size_t struct_size = 0;
    size_t read = 0;
    addr_t buffer_va = 0;
    uint16_t buffer_len = 0;

    if (VMI_PM_IA32E == vmi_get_page_mode(vmi)) {   // 64 bit guest
        win64_unicode_string_t us64 = { 0 };
        struct_size = sizeof(us64);
        // read the UNICODE_STRING struct
        read = vmi_read(vmi, ctx, &us64, struct_size);
        if (read != struct_size) {
            dbprint(VMI_DEBUG_READ, "--%s: failed to read UNICODE_STRING\n",__FUNCTION__);
            goto out_error;
        }   // if
        buffer_va = us64.pBuffer;
        buffer_len = us64.length;
    }
    else {
        win32_unicode_string_t us32 = { 0 };
        struct_size = sizeof(us32);
        // read the UNICODE_STRING struct
        read = vmi_read(vmi, ctx, &us32, struct_size);
        if (read != struct_size) {
            dbprint(VMI_DEBUG_READ, "--%s: failed to read UNICODE_STRING\n",__FUNCTION__);
            goto out_error;
        }   // if
        buffer_va = us32.pBuffer;
        buffer_len = us32.length;
    }   // if-else

    // allocate the return value
    us = safe_malloc(sizeof(unicode_string_t));

    us->length = buffer_len;
    us->contents = safe_malloc(sizeof(uint8_t) * (buffer_len + 2));

    _ctx.addr = buffer_va;
    read = vmi_read(vmi, &_ctx, us->contents, us->length);
    if (read != us->length) {
        dbprint
            (VMI_DEBUG_READ, "--%s: failed to read UNICODE_STRING buffer\n",__FUNCTION__);
        goto out_error;
    }   // if

    // end with NULL (needed?)
    us->contents[buffer_len] = 0;
    us->contents[buffer_len + 1] = 0;

    us->encoding = "UTF-16";

    return us;

out_error:
    if (us) {
        if (us->contents) {
            free(us->contents);
        }
        free(us);
    }
    return 0;
}
Example #6
0
status_t
peparse_get_export_table(
    vmi_instance_t vmi,
    const access_context_t *ctx,
    struct export_table *et,
    addr_t *export_table_rva,
    size_t *export_table_size)
{
    // Note: this function assumes a "normal" PE where all the headers are in
    // the first page of the PE and the field DosHeader.OffsetToPE points to
    // an address in the first page.

    access_context_t _ctx = *ctx;
    addr_t export_header_rva = 0;
    size_t export_header_size = 0;

#define MAX_HEADER_BYTES 1024   // keep under 1 page
    uint8_t image[MAX_HEADER_BYTES];

    if (VMI_FAILURE == peparse_get_image(vmi, ctx, MAX_HEADER_BYTES, image)) {
        return VMI_FAILURE;
    }

    void *optional_header = NULL;
    uint16_t magic = 0;

    peparse_assign_headers(image, NULL, NULL, &magic, &optional_header, NULL, NULL);
    export_header_rva = peparse_get_idd_rva(IMAGE_DIRECTORY_ENTRY_EXPORT, &magic, optional_header, NULL, NULL);
    export_header_size = peparse_get_idd_size(IMAGE_DIRECTORY_ENTRY_EXPORT, &magic, optional_header, NULL, NULL);

    if (export_table_rva) {
        *export_table_rva=export_header_rva;
    }

    if (export_table_size) {
        *export_table_size=export_header_size;
    }

    dbprint(VMI_DEBUG_PEPARSE, "--PEParse: DLL base 0x%.16"PRIx64". Export header [RVA] 0x%.16"PRIx64". Size %" PRIu64 ".\n",
            ctx->addr, export_header_rva, export_header_size);

    _ctx.addr = ctx->addr + export_header_rva;
    if ( VMI_FAILURE == vmi_read(vmi, &_ctx,  sizeof(struct export_table), et, NULL) ) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEParse: failed to map export header\n");

        /*
         * Sometimes Windows maps the export table on page-boundaries,
         * such that the first export_flags field (which is reserved) is.
         * not actually accessible (the page is not mapped). See Issue #260.
         */
        if (!((_ctx.addr+4) & 0xfff)) {
            dbprint(VMI_DEBUG_PEPARSE, "--PEParse: export table is mapped on page boundary\n");
            _ctx.addr += 4;
            if ( VMI_FAILURE == vmi_read(vmi, &_ctx, sizeof(struct export_table)-4, (void*)((char*)et+4), NULL) ) {
                dbprint(VMI_DEBUG_PEPARSE, "--PEParse: still failed to map export header\n");
                return VMI_FAILURE;
            }

            // Manually set the reserved field to zero in this case
            et->export_flags = 0;
        } else {
            return VMI_FAILURE;
        }
    }

    /* sanity check */
    if (et->export_flags || !et->name) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEParse: bad export directory table\n");
        return VMI_FAILURE;
    }

    return VMI_SUCCESS;
}