A primeira coisa que o código vai fazer é obter o endereço base do kernel, para que então seja possível calcular os endereços de interesse de acordo com os offsets conhecidos.

addr_t drakvuf_get_kernel_base(drakvuf_t drakvuf)
{
    return drakvuf->kernbase;
}

O valor da variável drakvuf->kernbase é obtido através de find_kernbase(), uma função chamada por set_os_linux()

static bool find_kernbase(drakvuf_t drakvuf)
{
    if ( VMI_FAILURE == vmi_translate_ksym2v(drakvuf->vmi, "_text", &drakvuf->kernbase) )
        return 0;
 
    return !!drakvuf->kernbase;
}

TRANSITION TO LIBVMI <<

/* convert a kernel symbol into an address */
status_t
vmi_translate_ksym2v(
    vmi_instance_t vmi,
    const char *symbol,
    addr_t *vaddr)
{
    status_t status = VMI_FAILURE;
    addr_t address = 0;
 
#ifdef ENABLE_SAFETY_CHECKS
    if (!vmi || !symbol || !vaddr)
        return VMI_FAILURE;
#endif
 
// Check if we have already cached this symbol.
    status = sym_cache_get(vmi, 0, 0, symbol, &address);
// If not, search for it:
    if ( VMI_FAILURE == status ) {
        if (vmi->os_interface && vmi->os_interface->os_ksym2v) {
            addr_t _base_vaddr;
            status = vmi->os_interface->os_ksym2v(vmi, symbol, &_base_vaddr, &address);
            if ( VMI_SUCCESS == status ) {
                address = canonical_addr(address);
								// And add it to the cache
                sym_cache_set(vmi, 0, 0, symbol, address);
            }
        }
    }
 
    *vaddr = address;
    return status;
}
typedef struct os_interface {
    os_get_kernel_struct_offset_t os_get_kernel_struct_offset;
    os_get_offset_t os_get_offset;
    os_pgd_to_pid_t os_pgd_to_pid;
    os_pid_to_pgd_t os_pid_to_pgd;
    os_kernel_symbol_to_address_t os_ksym2v;
    os_user_symbol_to_rva_t os_usym2rva;
    os_address_to_symbol_t os_v2sym;
    os_address_to_symbol_kaslr_t os_v2ksym;
    os_read_unicode_struct_t os_read_unicode_struct;
    os_read_unicode_struct_pm_t os_read_unicode_struct_pm;
    os_teardown_t os_teardown;
} *os_interface_t;
os_interface->os_get_offset = linux_get_offset;
    os_interface->os_get_kernel_struct_offset = linux_get_kernel_struct_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 = NULL;
    os_interface->os_v2ksym = linux_system_map_address_to_symbol;
    os_interface->os_read_unicode_struct = NULL;
    os_interface->os_teardown = linux_teardown;
status_t
linux_symbol_to_address(
    vmi_instance_t vmi,
    const char *symbol,
    addr_t* UNUSED(__unused),
    addr_t* address)
{
    status_t ret = VMI_FAILURE;
    linux_instance_t linux_instance = vmi->os_data;
 
    if (linux_instance == NULL) {
        errprint("VMI_ERROR: OS instance not initialized\n");
        goto done;
    }
 
    if (!linux_instance->sysmap && !json_profile(vmi)) {
        errprint("VMI_WARNING: No linux sysmap and Rekall profile configured\n");
        goto done;
    }
 
    if (linux_instance->sysmap)
        ret = linux_system_map_symbol_to_address(vmi, symbol, address);
    else
        ret = json_profile_lookup(vmi, symbol, NULL, address);
 
    if ( VMI_SUCCESS == ret )
				// Linux have KASLR (ASLR for kernel lol) enabled by default 
        *address += linux_instance->kaslr_offset;
 
done:
    return ret;
}

Os simbolos no kernel são encontrados através do arquivo system.map

Mas como diabos ele usa o system.map para encontrar o endereço onde os simbolos foram carregado se o system.map apresenta os endereços sem ASLR e só /proc/kallsyms (que não é utilizado aparentemente) apresenta os endereços pós ASLR?????

https://github.com/libvmi/libvmi/issues/397

https://github.com/tklengyel/drakvuf/pull/226

FUUUUUU THIS FUCKING ISSUE HIT ME ON THE FACE BOI Y.Y

Ok eu descobri q diabos é isso… Então essa desgraça precisa de um json especificando o base address dos simbolos no kernel e_e

O system.map não é usado pq não faz sentido usar ele com o KASLR ativo e_e

Para gerar esse profiles vc pode usar o “Rekall” ou o “Dwarf” → Deixei as notas sobre eles lá no começo :p


struct linux_instance {
    char *sysmap; /**< system map file for domain's running kernel */
 
    addr_t tasks_offset; /**< task_struct->tasks */
 
    addr_t mm_offset; /**< task_struct->mm */
 
    addr_t pid_offset; /**< task_struct->pid */
 
    addr_t pgd_offset; /**< mm_struct->pgd */
 
    addr_t name_offset; /**< task_struct->comm */
 
    addr_t kaslr_offset; /**< offset generated at boot time for KASLR */
 
    addr_t init_task_fixed; /**< Rekall's location for init task, ignoring KASLR */
};
typedef struct linux_instance *linux_instance_t;
static status_t
linux_system_map_symbol_to_address(
    vmi_instance_t vmi,
    const char *symbol,
    addr_t *address)
{
    FILE *f = NULL;
    char *row = NULL;
    status_t ret = VMI_FAILURE;
 
    linux_instance_t linux_instance = vmi->os_data;
 
    if (linux_instance == NULL) {
        errprint("VMI_ERROR: OS instance not initialized\n");
        goto done;
    }
 
    if ((NULL == linux_instance->sysmap) || (strlen(linux_instance->sysmap) == 0)) {
        errprint("VMI_WARNING: No linux sysmap configured\n");
        goto done;
    }
 
    row = g_try_malloc0(MAX_ROW_LENGTH);
    if ( !row )
        goto done;
 
    if ((f = fopen(linux_instance->sysmap, "r")) == NULL) {
        fprintf(stderr,
                "ERROR: could not find System.map file after checking:\n");
        fprintf(stderr, "\t%s\n", linux_instance->sysmap);
        fprintf(stderr,
                "To fix this problem, add the correct sysmap entry to /etc/libvmi.conf\n");
        goto done;
    }
    if (get_symbol_row(f, row, symbol, 2) == VMI_FAILURE) {
        goto done;
    }
 
    if (address)
        *address = (addr_t) strtoull(row, NULL, 16);
 
    ret = VMI_SUCCESS;
 
done:
    g_free(row);
    if (f)
        fclose(f);
    return ret;
}

🌱 Back to Garden