Пример #1
0
// Finds the index of the exported symbol specified - linear search
int
get_aon_index_linear(
    vmi_instance_t vmi,
    const char *symbol,
    struct export_table *et,
    const access_context_t *ctx)
{
    access_context_t _ctx = *ctx;
    uint32_t i = 0;

    for (; i < et->number_of_names; ++i) {
        _ctx.addr = ctx->addr + et->address_of_names + i * sizeof(uint32_t);
        uint32_t str_rva = 0;

        if (VMI_SUCCESS == vmi_read_32(vmi, &_ctx, &str_rva) && str_rva) {
            _ctx.addr = ctx->addr+str_rva;
            char *rva = vmi_read_str(vmi, &_ctx);

            if (NULL != rva) {
                if (strncmp(rva, symbol, strlen(rva)) == 0) {
                    free(rva);
                    return (int) i;
                }
            }
            free(rva);
        }
    }

    /* didn't find anything that matched */
    return -1;
}
Пример #2
0
/* returns a windows PE export from an RVA*/
char*
windows_rva_to_export(
    vmi_instance_t vmi,
    addr_t rva,
    const access_context_t *ctx)
{
    access_context_t _ctx = *ctx;
    struct export_table et;
    addr_t et_rva;
    size_t et_size;

    // get export table structure
    if (peparse_get_export_table(vmi, ctx, &et, &et_rva, &et_size) != VMI_SUCCESS) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEParse: failed to get export table\n");
        return NULL;
    }

    if (rva>=et_rva && rva < et_rva+et_size) {
        dbprint(VMI_DEBUG_PEPARSE, "--PEParse: symbol @ 0x%"PRIx64" is forwarded\n", ctx->addr+rva);
        return NULL;
    }


    addr_t base1 = ctx->addr + et.address_of_names;
    addr_t base2 = ctx->addr + et.address_of_name_ordinals;
    addr_t base3 = ctx->addr + et.address_of_functions;
    uint32_t i = 0;

    for (; i < et.number_of_functions; ++i) {
        uint32_t name_rva = 0;
        uint16_t ordinal = 0;
        uint32_t loc = 0;

        _ctx.addr = base2 + i * sizeof(uint16_t);
        if (VMI_FAILURE==vmi_read_16(vmi, &_ctx, &ordinal))
            continue;

        _ctx.addr = base3 + ordinal * sizeof(uint32_t);
        if (VMI_FAILURE==vmi_read_32(vmi, &_ctx, &loc))
            continue;

        if (loc==rva) {

            _ctx.addr = base1 + i * sizeof(uint32_t);
            if (i < et.number_of_names && VMI_SUCCESS==vmi_read_32(vmi, &_ctx, &name_rva) && name_rva) {
                _ctx.addr = ctx->addr + name_rva;
                return vmi_read_str(vmi, &_ctx);
            }

            dbprint(VMI_DEBUG_PEPARSE, "--PEParse: symbol @ 0x%"PRIx64" is exported by ordinal only\n", ctx->addr+rva);
            break;
        }
    }

    return NULL;
}
Пример #3
0
void
dump_exports(
    vmi_instance_t vmi,
    struct export_table *et,
    const access_context_t *ctx)
{
    access_context_t _ctx = *ctx;
    addr_t base_addr = ctx->addr;
    addr_t base1 = base_addr + et->address_of_names;
    addr_t base2 = base_addr + et->address_of_name_ordinals;
    addr_t base3 = base_addr + et->address_of_functions;
    uint32_t i = 0;

    /* print names */
    for (; i < et->number_of_names; ++i) {
        uint32_t rva = 0;
        uint16_t ordinal = 0;
        uint32_t loc = 0;
        char *str = NULL;

        _ctx.addr = base1 + i * sizeof(uint32_t);
        if (VMI_FAILURE == vmi_read_32(vmi, &_ctx, &rva))
            continue;

        if (rva) {
            _ctx.addr = base_addr + rva;
            str = vmi_read_str(vmi, &_ctx);
            if (str) {
                _ctx.addr = base2 + i * sizeof(uint16_t);
                if (VMI_FAILURE == vmi_read_16(vmi, &_ctx, &ordinal)) {
                    free(str);
                    continue;
                }

                _ctx.addr = base3 + ordinal + sizeof(uint32_t);
                if (VMI_FAILURE == vmi_read_32(vmi, &_ctx, &loc)) {
                    free(str);
                    continue;
                }

                printf("%s:%d:0x%"PRIx32"\n", str, ordinal, loc);
                free(str);
            }
        }
    }
}
Пример #4
0
// binary search function for get_aon_index_binary()
static int
find_aon_idx_bin(
    vmi_instance_t vmi,
    const char *symbol,
    addr_t aon_base_va,
    int low,
    int high,
    const access_context_t *ctx)
{
    access_context_t _ctx = *ctx;
    int mid, cmp;
    uint32_t str_rva = 0;   // RVA of curr name
    char *name = 0; // curr name

    if (high < low)
        goto not_found;

    // calc the current index ("mid")
    mid = (low + high) / 2;

    _ctx.addr = aon_base_va + mid * sizeof(uint32_t);
    if (VMI_FAILURE == vmi_read_32(vmi, &_ctx, &str_rva) || !str_rva)
        goto not_found;

    // get the curr string & compare to symbol
    _ctx.addr = ctx->addr + str_rva;
    name = vmi_read_str(vmi, &_ctx);
    if (!name)
        goto not_found;

    cmp = strcmp(symbol, name);
    free(name);

    if (cmp < 0) {  // symbol < name ==> try lower region
        return find_aon_idx_bin(vmi, symbol, aon_base_va, low, mid - 1, ctx);
    } else if (cmp > 0) { // symbol > name ==> try higher region
        return find_aon_idx_bin(vmi, symbol, aon_base_va, mid + 1, high, ctx);
    } else { // symbol == name
        return mid; // found
    }

not_found:
    return -1;
}
Пример #5
0
addr_t linux_get_current_process(drakvuf_t drakvuf, uint64_t vcpu_id)
{
    addr_t process = 0;
    vmi_instance_t vmi = drakvuf->vmi;

    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = drakvuf->regs[vcpu_id]->cr3,
        .addr = drakvuf->regs[vcpu_id]->gs_base + drakvuf->offsets[CURRENT_TASK],
    };

    if ( VMI_FAILURE == vmi_read_addr(vmi, &ctx, &process) || process < MIN_KERNEL_BOUNDARY )
    {
        /*
         * The kernel stack also has a structure called thread_info that points
         * to a task_struct but it doesn't seem to always agree with current_task.
         * However, when current_task obviously is wrong (for example during a CPUID)
         * we can fall back to it to find the correct process.
         * On most newer kernels the kernel stack size is 16K. This is just a guess
         * so for older kernels this may not work as well if the VA happens to map
         * something that resembles a kernel-address.
         * See https://www.cs.columbia.edu/~smb/classes/s06-4118/l06.pdf for more info.
         */
        ctx.addr = drakvuf->kpcr[vcpu_id] & ~STACK_SIZE_16K;
        if ( VMI_FAILURE == vmi_read_addr(vmi, &ctx, &process) || process < MIN_KERNEL_BOUNDARY )
        {
            ctx.addr = drakvuf->kpcr[vcpu_id] & ~STACK_SIZE_8K;
            if ( VMI_FAILURE == vmi_read_addr(vmi, &ctx, &process) || process < MIN_KERNEL_BOUNDARY )
                process = 0;
        }
    }

    return process;
}

/*
 * Threads are really just processes on Linux.
 */
addr_t linux_get_current_thread(drakvuf_t drakvuf, uint64_t vcpu_id)
{
    return linux_get_current_process(drakvuf, vcpu_id);
}

char* linux_get_process_name(drakvuf_t drakvuf, addr_t process_base)
{
    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_PID,
        .pid = 0,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_COMM]
    };

    return vmi_read_str(drakvuf->vmi, &ctx);
}

status_t linux_get_process_pid(drakvuf_t drakvuf, addr_t process_base, vmi_pid_t* pid )
{
    /*
     * On Linux PID is actually a thread ID, while the TGID (Thread Group-ID) is
     * what getpid() would return. Because THAT makes sense.
     */
    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_PID,
        .pid = 0,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_TGID]
    };

    return vmi_read_32(drakvuf->vmi, &ctx, (uint32_t*)pid);
}

char* linux_get_current_process_name(drakvuf_t drakvuf, uint64_t vcpu_id)
{
    addr_t process_base = linux_get_current_process(drakvuf, vcpu_id);
    if ( !process_base )
        return NULL;

    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = drakvuf->regs[vcpu_id]->cr3,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_COMM]
    };

    return vmi_read_str(drakvuf->vmi, &ctx);
}

int64_t linux_get_process_userid(drakvuf_t drakvuf, addr_t process_base)
{
    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_PID,
        .pid = 0,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_CRED]
    };

    addr_t cred;
    if ( VMI_FAILURE == vmi_read_addr(drakvuf->vmi, &ctx, &cred) )
        return -1;

    uint32_t uid;
    ctx.addr = cred + drakvuf->offsets[CRED_UID];
    if ( VMI_FAILURE == vmi_read_32(drakvuf->vmi, &ctx, &uid) )
        return -1;

    return uid;
};

int64_t linux_get_current_process_userid(drakvuf_t drakvuf, uint64_t vcpu_id)
{
    addr_t process_base = linux_get_current_process(drakvuf, vcpu_id);
    if ( !process_base )
        return -1;

    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = drakvuf->regs[vcpu_id]->cr3,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_CRED]
    };

    addr_t cred;
    if ( VMI_FAILURE == vmi_read_addr(drakvuf->vmi, &ctx, &cred) )
        return -1;

    uint32_t uid;
    ctx.addr = cred + drakvuf->offsets[CRED_UID];
    if ( VMI_FAILURE == vmi_read_32(drakvuf->vmi, &ctx, &uid) )
        return -1;

    return uid;
}

bool linux_get_current_thread_id( drakvuf_t drakvuf, uint64_t vcpu_id, uint32_t* thread_id )
{
    /*
     * On Linux PID is actually the thread ID....... ... ...
     */
    addr_t process_base = linux_get_current_process(drakvuf, vcpu_id);
    if ( !process_base )
        return false;

    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = drakvuf->regs[vcpu_id]->cr3,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_PID]
    };
    uint32_t _thread_id;

    if ( VMI_FAILURE == vmi_read_32(drakvuf->vmi, &ctx, &_thread_id) )
        return false;

    *thread_id = _thread_id;

    return true;
}

status_t linux_get_process_ppid( drakvuf_t drakvuf, addr_t process_base, vmi_pid_t* ppid )
{
    status_t ret ;
    addr_t parent_proc_base = 0 ;
    access_context_t ctx =
    {
        .translate_mechanism = VMI_TM_PROCESS_PID,
        .pid = 0,
        .addr = process_base + drakvuf->offsets[TASK_STRUCT_REALPARENT]
    };

    ret = vmi_read_addr( drakvuf->vmi, &ctx, &parent_proc_base );

    /* If we were unable to get the "proc->real_parent *" get "proc->parent *"... */
    /* Assuming a parent_proc_base == 0 is a fail... */
    if ( (ret == VMI_FAILURE ) || ! parent_proc_base )
    {
        ctx.addr = process_base + drakvuf->offsets[TASK_STRUCT_PARENT];
        ret = vmi_read_addr( drakvuf->vmi, &ctx, &parent_proc_base );
    }

    /* Get pid from parent/real_parent...*/
    if ( ( ret == VMI_SUCCESS ) && parent_proc_base )
    {
        ctx.addr = parent_proc_base + drakvuf->offsets[TASK_STRUCT_TGID];
        return vmi_read_32( drakvuf->vmi, &ctx, (uint32_t*)ppid );
    }

    return VMI_FAILURE ;
}

bool linux_get_current_process_data( drakvuf_t drakvuf, uint64_t vcpu_id, proc_data_t* proc_data )
{
    proc_data->base_addr = linux_get_current_process( drakvuf, vcpu_id );

    if ( proc_data->base_addr )
    {
        if ( linux_get_process_pid( drakvuf, proc_data->base_addr, &proc_data->pid ) == VMI_SUCCESS )
        {
            proc_data->name = linux_get_process_name( drakvuf, proc_data->base_addr );

            if ( proc_data->name )
            {
                proc_data->userid = linux_get_process_userid( drakvuf, proc_data->base_addr );
                linux_get_process_ppid( drakvuf, proc_data->base_addr, &proc_data->ppid );

                return true ;
            }
        }
    }

    return false ;
}
Пример #6
0
static status_t init_task_kaslr_test(vmi_instance_t vmi, addr_t page_vaddr) {
    status_t ret = VMI_FAILURE;
    uint32_t pid;
    addr_t init_task = page_vaddr + (vmi->init_task & VMI_BIT_MASK(0,11));
    linux_instance_t linux_instance = vmi->os_data;
    access_context_t ctx = {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = vmi->kpgd
    };

    ctx.addr = init_task + linux_instance->pid_offset;
    if ( VMI_FAILURE == vmi_read_32(vmi, &ctx, &pid) )
        return ret;

    if ( pid )
        return ret;

    ctx.addr = init_task + linux_instance->name_offset;
    char* init_task_name = vmi_read_str(vmi, &ctx);

    if ( init_task_name && !strncmp("swapper", init_task_name, 7) )
        ret = VMI_SUCCESS;

    free(init_task_name);
    return ret;
}

status_t init_kaslr(vmi_instance_t vmi) {
    /*
     * Let's check if we can translate init_task first as is.
     */
    uint32_t test;
    access_context_t ctx = {
        .translate_mechanism = VMI_TM_PROCESS_DTB,
        .dtb = vmi->kpgd,
        .addr = vmi->init_task
    };

    if ( VMI_SUCCESS == vmi_read_32(vmi, &ctx, &test) )
        return VMI_SUCCESS;

    status_t ret = VMI_FAILURE;
    linux_instance_t linux_instance = vmi->os_data;
    GSList *loop, *pages = vmi_get_va_pages(vmi, vmi->kpgd);
    loop = pages;
    while (loop) {
        page_info_t *info = loop->data;

        if ( !linux_instance->kaslr_offset ) {
            switch(vmi->page_mode) {
                case VMI_PM_AARCH64:
                case VMI_PM_IA32E:
                    if ( VMI_GET_BIT(info->vaddr, 47) )
                        ret = init_task_kaslr_test(vmi, info->vaddr);
                    break;
                default:
                    ret = init_task_kaslr_test(vmi, info->vaddr);
                    break;
            };

            if ( VMI_SUCCESS == ret ) {
                linux_instance->kaslr_offset = info->vaddr - (vmi->init_task & ~VMI_BIT_MASK(0,11));
                vmi->init_task += linux_instance->kaslr_offset;
                dbprint(VMI_DEBUG_MISC, "**calculated KASLR offset: 0x%"PRIx64"\n", linux_instance->kaslr_offset);
            }
        }

        g_free(info);
        loop = loop->next;
    }

    g_slist_free(pages);
    return ret;
}

status_t linux_init(vmi_instance_t vmi) {

    status_t rc;
    os_interface_t os_interface = NULL;

    if (vmi->config == NULL) {
        errprint("No config table found\n");
        return VMI_FAILURE;
    }

    if (vmi->os_data != NULL) {
        errprint("os data already initialized, reinitializing\n");
        free(vmi->os_data);
    }

    vmi->os_data = safe_malloc(sizeof(struct linux_instance));
    bzero(vmi->os_data, sizeof(struct linux_instance));
    linux_instance_t linux_instance = vmi->os_data;

    g_hash_table_foreach(vmi->config, (GHFunc)linux_read_config_ghashtable_entries, vmi);

    if(linux_instance->rekall_profile)
        rc = init_from_rekall_profile(vmi);
    else
        rc = linux_symbol_to_address(vmi, "init_task", NULL, &vmi->init_task);

    if (VMI_FAILURE == rc) {
        errprint("Could not get init_task from Rekall profile or System.map\n");
        goto _exit;
    }

    vmi->init_task = canonical_addr(vmi->init_task);

#if defined(ARM32) || defined(ARM64)
    rc = driver_get_vcpureg(vmi, &vmi->kpgd, TTBR1, 0);
#elif defined(I386) || defined(X86_64)
    rc = driver_get_vcpureg(vmi, &vmi->kpgd, CR3, 0);
#endif

    /*
     * The driver failed to get us a pagetable.
     * As a fall-back, try to init using heuristics.
     * This path is taken in FILE mode as well.
     */
    if (VMI_FAILURE == rc)
        if (VMI_FAILURE == linux_filemode_init(vmi))
            goto _exit;

    if ( VMI_FAILURE == init_kaslr(vmi) ) {
        dbprint(VMI_DEBUG_MISC, "**failed to determine KASLR offset\n");
        goto _exit;
    }

    dbprint(VMI_DEBUG_MISC, "**set vmi->kpgd (0x%.16"PRIx64").\n", vmi->kpgd);

    os_interface = safe_malloc(sizeof(struct os_interface));
    bzero(os_interface, sizeof(struct os_interface));
    os_interface->os_get_offset = linux_get_offset;
    os_interface->os_pid_to_pgd = linux_pid_to_pgd;
    os_interface->os_pgd_to_pid = linux_pgd_to_pid;
    os_interface->os_ksym2v = linux_symbol_to_address;
    os_interface->os_usym2rva = NULL;
    os_interface->os_v2sym = linux_system_map_address_to_symbol;
    os_interface->os_read_unicode_struct = NULL;
    os_interface->os_teardown = linux_teardown;

    vmi->os_interface = os_interface;

    return VMI_SUCCESS;

    _exit:
    free(vmi->os_data);
    vmi->os_data = NULL;
    return VMI_FAILURE;
}