Ejemplo n.º 1
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;
}
Ejemplo n.º 2
0
int main (int argc, char **argv)
{
    vmi_instance_t vmi;
    unsigned char *memory = NULL;
    uint32_t offset;
    addr_t list_head = 0, next_list_entry = 0;
    addr_t current_process = 0;
    addr_t tmp_next = 0;
    char *procname = NULL;
    vmi_pid_t pid = 0;
    unsigned long tasks_offset = 0, pid_offset = 0, name_offset = 0;
    status_t status;

    /* this is the VM or file that we are looking at */
    if (argc != 2) {
        printf("Usage: %s <vmname>\n", argv[0]);
        return 1;
    } // if

    char *name = argv[1];

    /* initialize the libvmi library */
    if (vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, name) == VMI_FAILURE) {
        printf("Failed to init LibVMI library.\n");
        return 1;
    }

    /* init the offset values */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi)) {
        tasks_offset = vmi_get_offset(vmi, "linux_tasks");
        name_offset = vmi_get_offset(vmi, "linux_name");
        pid_offset = vmi_get_offset(vmi, "linux_pid");
    }
    else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)) {
        tasks_offset = vmi_get_offset(vmi, "win_tasks");
        name_offset = vmi_get_offset(vmi, "win_pname");
        pid_offset = vmi_get_offset(vmi, "win_pid");
    }

    if (0 == tasks_offset) {
        printf("Failed to find win_tasks\n");
        goto error_exit;
    }
    if (0 == pid_offset) {
        printf("Failed to find win_pid\n");
        goto error_exit;
    }
    if (0 == name_offset) {
        printf("Failed to find win_pname\n");
        goto error_exit;
    }

    /* pause the vm for consistent memory access */
    if (vmi_pause_vm(vmi) != VMI_SUCCESS) {
        printf("Failed to pause VM\n");
        goto error_exit;
    } // if

    /* demonstrate name and id accessors */
    char *name2 = vmi_get_name(vmi);

    if (VMI_FILE != vmi_get_access_mode(vmi)) {
        unsigned long id = vmi_get_vmid(vmi);

        printf("Process listing for VM %s (id=%lu)\n", name2, id);
    }
    else {
        printf("Process listing for file %s\n", name2);
    }
    free(name2);

    /* get the head of the list */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi)) {
        /* Begin at PID 0, the 'swapper' task. It's not typically shown by OS
         *  utilities, but it is indeed part of the task list and useful to
         *  display as such.
         */
        list_head = vmi_translate_ksym2v(vmi, "init_task") + tasks_offset;
    }
    else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)) {

        // find PEPROCESS PsInitialSystemProcess
        if(VMI_FAILURE == vmi_read_addr_ksym(vmi, "PsActiveProcessHead", &list_head)) {
            printf("Failed to find PsActiveProcessHead\n");
            goto error_exit;
        }
    }

    next_list_entry = list_head;

    /* walk the task list */
    do {

        current_process = next_list_entry - tasks_offset;

        /* Note: the task_struct that we are looking at has a lot of
         * information.  However, the process name and id are burried
         * nice and deep.  Instead of doing something sane like mapping
         * this data to a task_struct, I'm just jumping to the location
         * with the info that I want.  This helps to make the example
         * code cleaner, if not more fragile.  In a real app, you'd
         * want to do this a little more robust :-)  See
         * include/linux/sched.h for mode details */

        /* NOTE: _EPROCESS.UniqueProcessId is a really VOID*, but is never > 32 bits,
         * so this is safe enough for x64 Windows for example purposes */
        vmi_read_32_va(vmi, current_process + pid_offset, 0, (uint32_t*)&pid);

        procname = vmi_read_str_va(vmi, current_process + name_offset, 0);

        if (!procname) {
            printf("Failed to find procname\n");
            goto error_exit;
        }

        /* print out the process name */
        printf("[%5d] %s (struct addr:%"PRIx64")\n", pid, procname, current_process);
        if (procname) {
            free(procname);
            procname = NULL;
        }

        /* follow the next pointer */

        status = vmi_read_addr_va(vmi, next_list_entry, 0, &next_list_entry);
        if (status == VMI_FAILURE) {
            printf("Failed to read next pointer in loop at %"PRIx64"\n", next_list_entry);
            goto error_exit;
        }

    } while(next_list_entry != list_head);

error_exit:
    /* resume the vm */
    vmi_resume_vm(vmi);

    /* cleanup any memory associated with the LibVMI instance */
    vmi_destroy(vmi);

    return 0;
}
Ejemplo n.º 3
0
void list_processes(vmi_instance_t vmi, addr_t current_process,
    addr_t list_head, unsigned long tasks_offset, addr_t current_list_entry,
    addr_t next_list_entry, unsigned long pid_offset,
    vmi_pid_t pid, char* procname, unsigned long name_offset) {

    /* demonstrate name and id accessors */
    char* name2 = vmi_get_name(vmi);
    if (VMI_FILE != vmi_get_access_mode(vmi)) {
        uint64_t id = vmi_get_vmid(vmi);

        printf("Process listing for VM %s (id=%"PRIu64")\n", name2, id);
    } else {
        printf("Process listing for file %s\n", name2);
    }
    free(name2);
    /* get the head of the list */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi)) {
        /* Begin at PID 0, the 'swapper' task. It's not typically shown by OS
         *  utilities, but it is indeed part of the task list and useful to
         *  display as such.
         */
        current_process = vmi_translate_ksym2v(vmi, "init_task");
    } else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)) {
        // find PEPROCESS PsInitialSystemProcess
        vmi_read_addr_ksym(vmi, "PsInitialSystemProcess", &current_process);
    }

    /* walk the task list */
    list_head = current_process + tasks_offset;
    current_list_entry = list_head;
    status_t status = vmi_read_addr_va(vmi, current_list_entry, 0, &next_list_entry);
    if (status == VMI_FAILURE) {
        printf("Failed to read next pointer at 0x%"PRIx64" before entering loop\n",
            current_list_entry);
        goto error_exit;
    }
    printf("Next list entry is at: %"PRIx64"\n", next_list_entry);
    do {
        /* Note: the task_struct that we are looking at has a lot of
         * information.  However, the process name and id are burried
         * nice and deep.  Instead of doing something sane like mapping
         * this data to a task_struct, I'm just jumping to the location
         * with the info that I want.  This helps to make the example
         * code cleaner, if not more fragile.  In a real app, you'd
         * want to do this a little more robust :-)  See
         * include/linux/sched.h for mode details */

        /* NOTE: _EPROCESS.UniqueProcessId is a really VOID*, but is never > 32 bits,
         * so this is safe enough for x64 Windows for example purposes */
        vmi_read_32_va(vmi, current_process + pid_offset, 0, (uint32_t*)&pid);

        procname = vmi_read_str_va(vmi, current_process + name_offset, 0);

        if (!procname) {
            printf("Failed to find procname\n");
            goto error_exit;
        }

        /* print out the process name */
        printf("[%5d] %s (struct addr:%"PRIx64")\n", pid, procname, current_process);
        if (procname) {
            free(procname);
            procname = NULL;
        }

        current_list_entry = next_list_entry;
        current_process = current_list_entry - tasks_offset;

        /* follow the next pointer */

        status = vmi_read_addr_va(vmi, current_list_entry, 0, &next_list_entry);
        if (status == VMI_FAILURE) {
            printf("Failed to read next pointer in loop at %"PRIx64"\n",
                current_list_entry);
            goto error_exit;
        }

    } while (next_list_entry != list_head);
    error_exit: if (procname)
        free(procname);
}
Ejemplo n.º 4
0
extern proc_info *process_list (char *name, image_offset *img_offsets, 
                                char *config_string)
{
    vmi_instance_t vmi;
    addr_t list_head = 0, next_list_entry = 0;
    addr_t current_process_addr = 0;
    char *procname = NULL;
    status_t status;
    int image_offsets_provided = 0;

    printf("config_string: \n%s\n", config_string);

    /* initialize the libvmi library */
    if (vmi_init(&vmi, VMI_AUTO | VMI_INIT_PARTIAL, name) == VMI_FAILURE)
    {
        printf("Failed to init Libvmi Library on the first time.");
        return NULL;
    }
    if(vmi_init_complete_custom(&vmi, VMI_CONFIG_STRING, config_string) == VMI_FAILURE)
    {
        printf("Failed to init LibVMI library on the second time.\n");
        return NULL;
    }


    printf("1\n");
    /* initialize the offsets */
    if(img_offsets == NULL)
    {
        img_offsets = (image_offset*)malloc(sizeof(image_offset));

        if (get_vm_offsets(vmi, img_offsets) == VMI_FAILURE)
        {
            printf("Failed to load offsets.\n");
            vmi_destroy(vmi);
            free(img_offsets);
            return NULL;
        }
        
    }
    else
    {
        image_offsets_provided = 1;
        printf("Using provided image offsets\n");
    }

    printf("2\n");
    /* pause the vm for consistent memory access */
    if (vmi_pause_vm(vmi) != VMI_SUCCESS)
    {
        printf("Failed to pause VM\n");
        resume_vm(&vmi);
        if(!image_offsets_provided)
            free(img_offsets);
        return NULL;
    }

    /* demonstrate name and id accessors */
    char *name2 = vmi_get_name(vmi);

    if (VMI_FILE != vmi_get_access_mode(vmi))
    {
        uint64_t id = vmi_get_vmid(vmi);
        printf("Process listing for VM %s (id=%"PRIu64")\n", name2, id);
    }
    else
    {
        printf("Process listing for file %s\n", name2);
    }
    free(name2);

    /* get the head of the list */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi))
    {
        /* Begin at PID 0, the 'swapper' task. It's not typically shown by OS
         *  utilities, but it is indeed part of the task list and useful to
         *  display as such.
         */
        list_head = vmi_translate_ksym2v(vmi, "init_task") + img_offsets->tasks_offset;
    }
    else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi))
    {

        /* find PEPROCESS PsInitialSystemProcess */
        if(VMI_FAILURE == vmi_read_addr_ksym(vmi, "PsActiveProcessHead", &list_head))
        {
            printf("Failed to find PsActiveProcessHead\n");
            resume_vm(&vmi);
            if(!image_offsets_provided)
                free(img_offsets);
            return NULL;
        }
    }
    next_list_entry = list_head;

    /*
     * Run process analyse for two rounds.
     * The first round:
     *    - Collect basic process information: name, pid, etc;
     *    - Collect the initial s_time, u_time, mm for calculating
     *         cpu and memory usage;
     *    - Construct our own process-info struct (process-info linked list).
     * The second round:
     *    - Read current s_time, u_time, mm, etc. and calculate
     *         cpu and memory usage;
     *    - Finalize process-info struct;
     */

    /* The first round */
    /* walk the task list and create  */
    printf("First round.\n");
    proc_info *process_info_list_head = NULL, *previous_process_ptr = NULL, *current_process_ptr = NULL;
    int is_head = 1;
    time_t calculation_start_time, calculation_end_time;

    vmi_read_64_va(vmi, vmi_translate_ksym2v(vmi, "jiffies"), 0, &calculation_start_time);
    do {
        current_process_ptr = (proc_info*)malloc(sizeof(proc_info));
        current_process_addr = next_list_entry - img_offsets->tasks_offset;

        /* NOTE: _EPROCESS.UniqueProcessId is a really VOID*, but is never > 32 bits,
         * so this is safe enough for x64 Windows for example purposes */
        vmi_read_32_va(vmi, current_process_addr + img_offsets->pid_offset, 0, (uint32_t*)&(current_process_ptr->pid));
        current_process_ptr->name = vmi_read_str_va(vmi, current_process_addr + img_offsets->name_offset, 0);
        vmi_read_64_va(vmi, current_process_addr + img_offsets->utime_offset, 0, &(current_process_ptr->r_utime));
        vmi_read_64_va(vmi, current_process_addr + img_offsets->stime_offset, 0, &(current_process_ptr->r_stime));
        
        /* current_process_ptr->name == NULL implies reading process info is not successful */
        if (!current_process_ptr->name) {
            printf("Failed to find process name\n");
            resume_vm(&vmi);
            if(!image_offsets_provided)
                free(img_offsets);
            return NULL;
        }

        /* follow the next pointer and load the entry of next process*/
        status = vmi_read_addr_va(vmi, next_list_entry, 0, &next_list_entry);
        if (status == VMI_FAILURE) {
            printf("Failed to read next pointer in loop at %"PRIx64"\n", next_list_entry);
            resume_vm(&vmi);
            if(!image_offsets_provided)
                free(img_offsets);
            return NULL;
        }
        if(is_head){
            is_head = 0;
            process_info_list_head = current_process_ptr;
            previous_process_ptr = current_process_ptr;
        }else{
            previous_process_ptr->next = current_process_ptr;
            previous_process_ptr = current_process_ptr;
        }
    } while(next_list_entry != list_head);
    current_process_ptr->next = NULL;
    vmi_resume_vm(vmi);
    sleep(3);

    /* The second round */
    printf("Second round.\n");
    uint64_t total_memory_size = vmi_get_memsize(vmi) / 1024;                           // total_memory_size unit: KB
    int dont_read_next_process = 0;
    vmi_pause_vm(vmi);
    is_head = 1;
    int old_list_has_ended = 0;
    proc_info* old_list_ptr = process_info_list_head;
    current_process_ptr = process_info_list_head;
    previous_process_ptr = process_info_list_head;
    next_list_entry = list_head;

    vmi_pid_t proc_pid;
    uint64_t new_utime, new_stime, sum_process_time_before, sum_process_time_after;
    /* walk the task list */

    vmi_read_64_va(vmi, vmi_translate_ksym2v(vmi, "jiffies"), 0, &calculation_end_time);
    do {
        
        if (old_list_ptr == NULL)
        {
            old_list_has_ended = 1;
        }
        current_process_addr = next_list_entry - img_offsets->tasks_offset;

        /* NOTE: _EPROCESS.UniqueProcessId is a really VOID*, but is never > 32 bits,
         * so this is safe enough for x64 Windows for example purposes */
        vmi_read_32_va(vmi, current_process_addr + img_offsets->pid_offset, 0, (uint32_t*)&proc_pid);
        
        if(old_list_has_ended || proc_pid < old_list_ptr->pid)
        {
            /* new process was created between sleep time
             * and pid is smaller than current process pid
             */
            proc_info *new_process_ptr = (proc_info*)malloc(sizeof(proc_info));
            new_process_ptr->name = vmi_read_str_va(vmi, current_process_addr + img_offsets->name_offset, 0);
            new_process_ptr->type = P_NEW_PROCESS;
            vmi_read_32_va(vmi, current_process_addr + img_offsets->pid_offset, 0, (uint32_t*)&(new_process_ptr->pid));
            
            /* pointer related operations */
            if(is_head)
            {
                process_info_list_head = new_process_ptr;
                current_process_ptr = new_process_ptr;
                previous_process_ptr = new_process_ptr;
                is_head = 0;
            }
            else
            {
                current_process_ptr = new_process_ptr;
                previous_process_ptr->next = current_process_ptr;
                previous_process_ptr = current_process_ptr;
            }
        }
        else if (proc_pid > old_list_ptr->pid)
        {
            /* previous process has ended between sleep time */
            old_list_ptr->type = P_ENDED_PROCESS;
            dont_read_next_process = 1;

            if(is_head)
            {
                is_head = 0;
                old_list_ptr = old_list_ptr->next;
                continue;
            }

            current_process_ptr = old_list_ptr;
            previous_process_ptr->next = current_process_ptr;
            previous_process_ptr = current_process_ptr;
            old_list_ptr = old_list_ptr->next;
        }
        else
        {
            /* the process still exists. cpu% and mem% can be calculated*/
            current_process_ptr = old_list_ptr;
            current_process_ptr->type = P_EXIST_PROCESS;

            /* get priority and state */
            vmi_read_64_va(vmi, current_process_addr + img_offsets->state_offset, 0, (uint64_t*)&(current_process_ptr->state));
            vmi_read_32_va(vmi, current_process_addr + img_offsets->rt_priority_offset, 0, (uint32_t*)&(current_process_ptr->priority));

            /* read process cpu time */
            vmi_read_64_va(vmi, current_process_addr + img_offsets->utime_offset, 0, &new_utime);
            vmi_read_64_va(vmi, current_process_addr + img_offsets->stime_offset, 0, &new_stime);

            /* calculate cpu usage */
            sum_process_time_after = new_utime + new_stime;
            sum_process_time_before = current_process_ptr->r_stime + current_process_ptr->r_utime;
            current_process_ptr->cpu_percent = (double)(sum_process_time_after - sum_process_time_before) / (calculation_end_time - calculation_start_time) * 100.0;

            /* get total vm pages for calculating virtual memory usage */
            addr_t mm_addr = NULL;
            uint32_t total_vm_pages = 0;
            vmi_read_addr_va(vmi, current_process_addr + img_offsets->mm_offset, 0, &mm_addr);
            vmi_read_32_va(vmi, mm_addr + img_offsets->total_vm_offset,0, &total_vm_pages);
            current_process_ptr->virtual_memory_usage = total_vm_pages * PAGE_SIZE_KB;

            /* get rss count for calculating physical memory usage*/
            int i = 0;
            uint64_t rss_count = 0, temp = 0;
            for(i = 0; i < NR_MM_COUNTERS; i++)
            {
                vmi_read_64_va(vmi, mm_addr + img_offsets->rss_stat_offset + i * img_offsets->element_offset, 0, (uint64_t*)&temp);
                rss_count += temp;
            }
            current_process_ptr->physical_memory_usage = rss_count * PAGE_SIZE_KB;
            
            /* calculate memory usage (percent) */
            current_process_ptr->memory_percent = (double)rss_count * PAGE_SIZE_KB / total_memory_size * 100.0;
            
            
            /* deal with pointer */
            if(is_head)
                is_head = 0;
            else
            {    
                previous_process_ptr->next = current_process_ptr;
                previous_process_ptr = current_process_ptr;
            }
            old_list_ptr = old_list_ptr->next;
        }

        if(!dont_read_next_process)
        {
            /* follow the next pointer and load the entry of next process*/
            status = vmi_read_addr_va(vmi, next_list_entry, 0, &next_list_entry);
            if (status == VMI_FAILURE) 
            {
                printf("Failed to read next pointer in loop at %"PRIx64"\n", next_list_entry);
                resume_vm(&vmi);
                free(img_offsets);
                return NULL;
            }
        }
        else
            dont_read_next_process = 0;

    } while(next_list_entry != list_head);
    if(!old_list_has_ended)
        current_process_ptr->next = old_list_ptr;
    else if(current_process_ptr != NULL)
        current_process_ptr->next = NULL;

    if(!image_offsets_provided)
        free(img_offsets);
    vmi_resume_vm(vmi);
    vmi_destroy(vmi);

    //current_process_ptr = process_info_list_head;
    //while(current_process_ptr != NULL)
    //{
    //current_process_ptr = process_info_list_head;
    //while(current_process_ptr != NULL)
    //{
    //    printf("%d | %s | %04.2lf \n", current_process_ptr->pid, current_process_ptr->name, current_process_ptr->cpu_percent);
    //    current_process_ptr = current_process_ptr->next;
    //}

    return process_info_list_head;
}
Ejemplo n.º 5
0
int main (int argc, char **argv)
{
    vmi_instance_t vmi;
    unsigned char *memory = NULL;
    uint32_t offset;
    addr_t next_process, list_head;
    char *procname = NULL;
    int pid = 0;
    int tasks_offset, pid_offset, name_offset;
    status_t status;

    /* this is the VM or file that we are looking at */
    if (argc != 2) {
        printf ("Usage: %s <vmname>\n", argv[0]);
        return 1;
    } // if

    char *name = argv[1];

    /* initialize the libvmi library */
    if (vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, name) == VMI_FAILURE){
        printf("Failed to init LibVMI library.\n");
        goto error_exit;
    }

    /* init the offset values */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi)){
        tasks_offset = vmi_get_offset(vmi, "linux_tasks");
        name_offset = vmi_get_offset(vmi, "linux_name");
        pid_offset = vmi_get_offset(vmi, "linux_pid");
	
        /* NOTE: 
         *  name_offset is no longer hard-coded. Rather, it is now set 
         *  via libvmi.conf.
         */
    }
    else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)){
        tasks_offset = vmi_get_offset(vmi, "win_tasks");
        if (0 == tasks_offset) {
            printf("Failed to find win_tasks\n");
            goto error_exit;
        }
        name_offset = vmi_get_offset(vmi, "win_pname");
        if (0 == tasks_offset) {
            printf("Failed to find win_pname\n");
            goto error_exit;
        }
        pid_offset = vmi_get_offset(vmi, "win_pid");
        if (0 == tasks_offset) {
            printf("Failed to find win_pid\n");
            goto error_exit;
        }
    }

    /* pause the vm for consistent memory access */
    if (vmi_pause_vm(vmi) != VMI_SUCCESS) {
        printf("Failed to pause VM\n");
        goto error_exit;
    } // if

    /* demonstrate name and id accessors */
    char *name2 = vmi_get_name(vmi);
    if (VMI_FILE != vmi_get_access_mode(vmi)){
        unsigned long id = vmi_get_vmid(vmi);
        printf("Process listing for VM %s (id=%lu)\n", name2, id);
    }
    else{
        printf("Process listing for file %s\n", name2);
    }
    free(name2);

    /* get the head of the list */
    if (VMI_OS_LINUX == vmi_get_ostype(vmi)){
        addr_t init_task_va = vmi_translate_ksym2v(vmi, "init_task");
        vmi_read_addr_va(vmi, init_task_va + tasks_offset, 0, &next_process);
    }
    else if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)){

        uint32_t pdbase = 0;

        // find PEPROCESS PsInitialSystemProcess
        vmi_read_addr_ksym(vmi, "PsInitialSystemProcess", &list_head); 
        
        vmi_read_addr_va(vmi, list_head + tasks_offset, 0, &next_process);
        vmi_read_32_va(vmi, list_head + pid_offset, 0, &pid);

        vmi_read_32_va(vmi, list_head + pid_offset, 0, &pid);
        procname = vmi_read_str_va(vmi, list_head + name_offset, 0);
        if (!procname) {
            printf ("Failed to find first procname\n");
            goto error_exit;
        }

        printf("[%5d] %s\n", pid, procname);
        if (procname){
            free(procname);
            procname = NULL;
        }
    }

    list_head = next_process;

    /* walk the task list */
    while (1){

        /* follow the next pointer */
        addr_t tmp_next = 0;
        vmi_read_addr_va(vmi, next_process, 0, &tmp_next);

        /* if we are back at the list head, we are done */
        if (list_head == tmp_next){
            break;
        }

        /* print out the process name */

        /* Note: the task_struct that we are looking at has a lot of
           information.  However, the process name and id are burried
           nice and deep.  Instead of doing something sane like mapping
           this data to a task_struct, I'm just jumping to the location
           with the info that I want.  This helps to make the example
           code cleaner, if not more fragile.  In a real app, you'd
           want to do this a little more robust :-)  See
           include/linux/sched.h for mode details */
        procname = vmi_read_str_va(vmi, next_process + name_offset - tasks_offset, 0);

        if (!procname) {
            printf ("Failed to find procname\n");
        } // if

        vmi_read_32_va(vmi, next_process + pid_offset - tasks_offset, 0, &pid);

        /* trivial sanity check on data */
        if (pid >= 0 && procname){
            printf("cr3: %lx [%5d] %s\n", vmi_pid_to_dtb(vmi, pid), pid, procname);
        }
        if (procname){
            free(procname);
            procname = NULL;
        }
        next_process = tmp_next;
    }

error_exit:
    if (procname) free(procname);

    /* resume the vm */
    vmi_resume_vm(vmi);

    /* cleanup any memory associated with the LibVMI instance */
    vmi_destroy(vmi);

    return 0;
}