END_TEST /* test vmi_translate_uv2p */ //TODO figure out how to test this //START_TEST (test_libvmi_uv2p) //{ // vmi_translate_uv2p(vmi, vaddr, pid) //} //END_TEST /* test vmi_translate_ksym2v */ START_TEST (test_libvmi_ksym2v) { vmi_instance_t vmi = NULL; vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, get_testvm()); addr_t va = 0; if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)){ va = vmi_translate_ksym2v(vmi, "PsInitialSystemProcess"); } else if (VMI_OS_LINUX == vmi_get_ostype(vmi)){ va = vmi_translate_ksym2v(vmi, "init_task"); } else{ fail_unless(0, "vmi set to invalid os type"); } fail_unless(va != 0, "ksym2v translation failed"); vmi_destroy(vmi); }
int main(int argc, char **argv) { vmi_instance_t vmi; addr_t start_address; struct timeval ktv_start; struct timeval ktv_end; char *vm = argv[1]; int buf_size = atoi(argv[2]); int loops = atoi(argv[3]); int mode = atoi(argv[4]); unsigned char *buf = malloc(buf_size); int i = 0; long int diff; long int *data = malloc(loops * sizeof(long int)); int j = 0; uint32_t value = 0; if (mode != 1 && mode != 2) { printf("invalid mode\n"); return 1; } /* initialize the xen access library */ vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, vm); /* find address to work from */ start_address = vmi_translate_ksym2v(vmi, "PsInitialSystemProcess"); start_address = vmi_translate_kv2p(vmi, start_address); for (i = 0; i < loops; ++i) { if (mode == 1) { gettimeofday(&ktv_start, 0); vmi_read_pa(vmi, start_address, buf, buf_size); gettimeofday(&ktv_end, 0); } else { gettimeofday(&ktv_start, 0); for (j = 0; j < buf_size / 4; ++j) { vmi_read_32_pa(vmi, start_address + j * 4, &value); } gettimeofday(&ktv_end, 0); } print_measurement(ktv_start, ktv_end, &diff); data[i] = diff; memset(buf, 0, buf_size); sleep(1); } avg_measurement(data, loops); vmi_destroy(vmi); free(buf); return 0; }
char * vmi_read_str_ksym( vmi_instance_t vmi, char *sym) { addr_t vaddr = vmi_translate_ksym2v(vmi, sym); return vmi_read_str_va(vmi, vaddr, 0); }
END_TEST /* test vmi_translate_kv2p */ START_TEST (test_libvmi_kv2p) { vmi_instance_t vmi = NULL; vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, get_testvm()); addr_t va = 0; if (VMI_OS_WINDOWS == vmi_get_ostype(vmi)){ va = vmi_translate_ksym2v(vmi, "PsInitialSystemProcess"); } else if (VMI_OS_LINUX == vmi_get_ostype(vmi)){ va = vmi_translate_ksym2v(vmi, "init_task"); } else{ fail_unless(0, "vmi set to invalid os type"); } addr_t pa = vmi_translate_kv2p(vmi, va); fail_unless(pa != 0, "kv2p translation failed"); vmi_destroy(vmi); }
size_t vmi_read_ksym( vmi_instance_t vmi, char *sym, void *buf, size_t count) { addr_t vaddr = vmi_translate_ksym2v(vmi, sym); if (0 == vaddr) { dbprint("--%s: vmi_translate_ksym2v failed for '%s'\n", __FUNCTION__, sym); return 0; } return vmi_read_va(vmi, vaddr, 0, buf, count); }
status_t linux_init (vmi_instance_t vmi) { status_t ret = VMI_FAILURE; if (vmi->cr3){ vmi->kpgd = vmi->cr3; } else if (VMI_SUCCESS == linux_system_map_symbol_to_address(vmi, "swapper_pg_dir", &vmi->kpgd)){ dbprint("--got vaddr for swapper_pg_dir (0x%.16llx).\n", vmi->kpgd); if (driver_is_pv(vmi)){ vmi->kpgd = vmi_translate_kv2p(vmi, vmi->kpgd); if (vmi_read_addr_pa(vmi, vmi->kpgd, &(vmi->kpgd)) == VMI_FAILURE){ errprint("Failed to get physical addr for kpgd.\n"); goto _exit; } } else{ vmi->kpgd = vmi_translate_kv2p(vmi, vmi->kpgd); } } else{ errprint("swapper_pg_dir not found and CR3 not set, exiting\n"); goto _exit; } vmi->kpgd = vmi->cr3; dbprint("**set vmi->kpgd (0x%.16llx).\n", vmi->kpgd); addr_t address = vmi_translate_ksym2v(vmi, "init_task"); address += vmi->os.linux_instance.tasks_offset; if (VMI_FAILURE == vmi_read_addr_va(vmi, address, 0, &(vmi->init_task))){ errprint("Failed to get task list head 'init_task'.\n"); goto _exit; } ret = VMI_SUCCESS; _exit: return ret; }
int main(int argc, char **argv) { vmi_instance_t vmi; addr_t vaddr; struct timeval ktv_start; struct timeval ktv_end; char *vm = argv[1]; int loops = atoi(argv[2]); int i = 0; long int diff; long int *data = malloc(loops * sizeof(long int)); /* initialize the xen access library */ vmi_init(&vmi, VMI_AUTO | VMI_INIT_COMPLETE, vm); for (i = 0; i < loops; ++i) { gettimeofday(&ktv_start, 0); vaddr = vmi_translate_ksym2v(vmi, "PsGetCurrentThread"); gettimeofday(&ktv_end, 0); if (0 == vaddr) { perror("failed to lookup kernel symbol"); goto error_exit; } print_measurement(ktv_start, ktv_end, &diff); data[i] = diff; sleep(2); } avg_measurement(data, loops); error_exit: vmi_destroy(vmi); free(data); return 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; }
size_t vmi_write_ksym (vmi_instance_t vmi, char *sym, void *buf, size_t count) { addr_t vaddr = vmi_translate_ksym2v(vmi, sym); return vmi_write_va(vmi, vaddr, 0, buf, count); }
/*根据data,执行vmi函数,并发送返回值*/ int rvmi_handle_data(int fd, char *buf) { int vmiFunNum; int i=0; char delims[] = " "; char *vmiFunArg[5] = {NULL,NULL,NULL,NULL}; char returnBuf[100]; char * returnVal =NULL; vmiFunArg[i] = strtok(buf, delims ); while( vmiFunArg[i] != NULL ) { i++; vmiFunArg[i] = strtok( NULL, delims ); } vmiFunNum = atoi(vmiFunArg[0]); //printf("vmifun %d\n",vmiFunNum); switch(vmiFunNum) { case 1: break; case 2: break; case 3: { int vmiID=atoi(vmiFunArg[1]); sprintf(returnBuf, "%d\0", vmi_destroy(vmiArr[vmiID]) ); vmiFlagArr[vmiID]=0; returnVal = returnBuf; } break; case 13: { sprintf(returnBuf, "%lu\0", vmi_get_offset(vmiArr[atoi(vmiFunArg[1])], vmiFunArg[2]) ); returnVal = returnBuf; } break; case 23: { for(i=0;i<MAX_DOMU_PER_MACHINE;i++) { if(0 == vmiFlagArr[i]) break; } vmi_init(&vmiArr[i], atoi(vmiFunArg[1]), vmiFunArg[2]); vmiFlagArr[i]=1; sprintf(returnBuf,"%d\0",i); returnVal = returnBuf; } break; case 38: { uint16_t tmp; vmi_read_16_va(vmiArr[atoi(vmiFunArg[1])], atol(vmiFunArg[2]), atoi(vmiFunArg[3]), &tmp); sprintf(returnBuf, "%hu\0", tmp); returnVal = returnBuf; } break; case 40: { uint32_t tmp; vmi_read_32_va(vmiArr[atoi(vmiFunArg[1])], atol(vmiFunArg[2]), atoi(vmiFunArg[3]), &tmp); sprintf(returnBuf, "%u\0", tmp); returnVal = returnBuf; } break; case 44: { uint64_t tmp; vmi_read_64_va(vmiArr[atoi(vmiFunArg[1])], atol(vmiFunArg[2]), atoi(vmiFunArg[3]), &tmp); sprintf(returnBuf, "%lu\0", tmp); returnVal = returnBuf; } break; case 50: { addr_t tmp; vmi_read_addr_va(vmiArr[atoi(vmiFunArg[1])], atol(vmiFunArg[2]), atoi(vmiFunArg[3]), &tmp); sprintf(returnBuf, "%lu\0", tmp); returnVal = returnBuf; } break; case 55: { sprintf(returnBuf, "%s\0", vmi_read_str_va(vmiArr[atoi(vmiFunArg[1])], atol(vmiFunArg[2]), atoi(vmiFunArg[3]) )); returnVal = returnBuf; } break; case 67: { sprintf(returnBuf, "%lu\0", vmi_translate_ksym2v(vmiArr[atoi(vmiFunArg[1])], vmiFunArg[2]) ); returnVal = returnBuf; } break; default:break; } write (fd, returnVal, 100); return 1; }
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", ¤t_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); }
int main (int argc, char **argv) { vmi_instance_t vmi = NULL; status_t status = VMI_SUCCESS; struct sigaction act; reg_t lstar = 0; addr_t phys_lstar = 0; reg_t cstar = 0; addr_t phys_cstar = 0; reg_t sysenter_ip = 0; addr_t phys_sysenter_ip = 0; addr_t ia32_sysenter_target = 0; addr_t phys_ia32_sysenter_target = 0; addr_t vsyscall = 0; addr_t phys_vsyscall = 0; char *name = NULL; vmi_pid_t pid = -1; if(argc < 2){ fprintf(stderr, "Usage: events_example <name of VM> <PID of process to track {optional}>\n"); exit(1); } // Arg 1 is the VM name. name = argv[1]; // Arg 2 is the pid of the process to track. if(argc == 3) pid = (int) strtoul(argv[2], NULL, 0); /* for a clean exit */ act.sa_handler = close_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGALRM, &act, NULL); // Initialize the libvmi library. //具体哪个虚拟机 由传入的参数决定 if (vmi_init(&vmi, VMI_XEN | VMI_INIT_COMPLETE | VMI_INIT_EVENTS, name) == VMI_FAILURE){ printf("Failed to init LibVMI library.\n"); if (vmi != NULL ) { vmi_destroy(vmi); } return 1; } else{ printf("LibVMI init succeeded!\n"); } // Get the cr3 for this process. /* addr_t vmi_pid_to_dtb (vmi_instance_t vmi, vmi_pid_t pid) Given a pid, this function returns the virtual address of the directory table base for this process' address space. This value is effectively what would be in the CR3 register while this process is running. Parameters [in] vmi LibVMI instance [in] pid Desired process id to lookup Returns OrderedDict([(u'emphasis', u'pid'), ('#text', u'The directory table base virtual address for')]) */ if(pid != -1) { cr3 = vmi_pid_to_dtb(vmi, pid); //虚拟机句柄和进程id 进程基地址 printf("CR3 for process (%d) == %llx\n", pid, (unsigned long long)cr3); } // Get the value of lstar and cstar for the system. // NOTE: all vCPUs have the same value for these registers //status_t vmi_get_vcpureg (vmi_instance_t vmi, reg_t *value, registers_t reg, unsigned long vcpu) //获取特定寄存器值 vmi_get_vcpureg(vmi, &lstar, MSR_LSTAR, 0); vmi_get_vcpureg(vmi, &cstar, MSR_CSTAR, 0); vmi_get_vcpureg(vmi, &sysenter_ip, SYSENTER_EIP, 0); //在调用SYSENTER指令前,软件必须通过下面的MSR寄存器,指定0层的代码段和代码指针,0层的堆栈段和堆栈指针: printf("vcpu 0 MSR_LSTAR == %llx\n", (unsigned long long)lstar); printf("vcpu 0 MSR_CSTAR == %llx\n", (unsigned long long)cstar); printf("vcpu 0 MSR_SYSENTER_IP == %llx\n", (unsigned long long)sysenter_ip); /* addr_t vmi_translate_ksym2v (vmi_instance_t vmi, const char *symbol) Performs the translation from a kernel symbol to a virtual address. Parameters [in] vmi LibVMI instance [in] symbol Desired kernel symbol to translate Returns Virtual address, or zero on error */ ia32_sysenter_target = vmi_translate_ksym2v(vmi, "ia32_sysenter_target"); printf("ksym ia32_sysenter_target == %llx\n", (unsigned long long)ia32_sysenter_target); //??? 返回结果为0 岂不是发生了error /* Per Linux ABI, this VA represents the start of the vsyscall page * If vsyscall support is enabled (deprecated or disabled on many newer * 3.0+ kernels), it is accessible at this address in every process. */ vsyscall = 0xffffffffff600000; // Translate to a physical address. phys_lstar= vmi_translate_kv2p(vmi, lstar); printf("Physical LSTAR == %llx0\n", (unsigned long long)phys_lstar); phys_cstar= vmi_translate_kv2p(vmi, cstar); printf("Physical CSTAR == %llx\n", (unsigned long long)phys_cstar); phys_sysenter_ip= vmi_translate_kv2p(vmi, sysenter_ip); printf("Physical SYSENTER_IP == %llx\n", (unsigned long long)phys_sysenter_ip); phys_ia32_sysenter_target = vmi_translate_kv2p(vmi,ia32_sysenter_target); printf("Physical ia32_sysenter_target == %llx\n", (unsigned long long)ia32_sysenter_target); /* http://www.longene.org/forum/viewtopic.php?f=5&t=8851 在x86结构中,较新的CPU都提供了sysenter/sysexit指令,专门用于系统调用的进入和退出,由于去掉了一些不必要的检查,它的速度比以前的int指令要快。 因此在新的Linux系统中,大部分系统调用都采用了sysenter代替原来的int 80。除了一些比较慢的系统调用,如clone、execve等少数几个,因为对于这种系统调用, 进入/退出内核空间所费的时钟周期,相对于其处理时间,几乎可以忽略不计。 考虑到和glibc的对接问题,现在glibc中不直接调用int 80或者sysenter。而是采用vdso方式,在内核中生成一个页面,用于系统调用, 叫做vsyscall,在load_elf_binary时,会将其映射到用户空间,glibc直接调用其中的函数。 关于sysenter的一些资料,可以看这里http://www.cnblogs.com/yangnas/archive/2010/04/28/1723232.html */ phys_vsyscall = vmi_translate_kv2p(vmi,vsyscall); printf("Physical phys_vsyscall == %llx\n", (unsigned long long)phys_vsyscall); // Get only the page that the handler starts. printf("LSTAR Physical PFN == %llx\n", (unsigned long long)(phys_lstar >> 12)); printf("CSTAR Physical PFN == %llx\n", (unsigned long long)(phys_cstar >> 12)); printf("SYSENTER_IP Physical PFN == %llx\n", (unsigned long long)(phys_sysenter_ip >> 12)); printf("phys_vsyscall Physical PFN == %llx\n", (unsigned long long)(phys_vsyscall >> 12)); printf("phys_ia32_sysenter_target Physical PFN == %llx\n", (unsigned long long)(phys_ia32_sysenter_target >> 12)); /* Configure an event to track when the process is running. * (The CR3 register is updated on task context switch, allowing * us to follow as various tasks are scheduled and run upon the CPU) */ memset(&cr3_event, 0, sizeof(vmi_event_t)); cr3_event.type = VMI_EVENT_REGISTER; cr3_event.reg_event.reg = CR3; /* Observe only write events to the given register. * NOTE: read events are unsupported at this time. */ cr3_event.reg_event.in_access = VMI_REGACCESS_W; /* Optional (default = 0): Trigger on change * Causes events to be delivered by the hypervisor to this monitoring * program if and only if the register value differs from that previously * observed. * Usage: cr3_event.reg_event.onchange = 1; * * Optional (default = 0): Asynchronous event delivery * Causes events to be delivered by the hypervisor to this monitoring * program if and only if the register value differs from that previously * observed. * Usage: cr3_event.reg_event.async =1; */ //重要 注册事件 回调函数 if(pid == -1){ //如果没加参数 就为-1 cr3_event.callback = cr3_all_tasks_callback; vmi_register_event(vmi, &cr3_event); } else { cr3_event.callback = cr3_one_task_callback; /* This acts as a filter: if the CR3 value at time of event == the CR3 * we wish to inspect, then the callback will be invoked. Otherwise, * no action is taken. */ //只有当事件发生时,cr3的值等于我们一开始注册的进程的cr3值,才会调用回调函数 cr3_event.reg_event.equal = cr3; //保存原始的cr3的值 vmi_register_event(vmi, &cr3_event); } //设置内存事件,在cr3的事件处理函数中进行绑定 // Setup a default event for tracking memory at the syscall handler. // But don't install it; that will be done by the cr3 handler. memset(&msr_syscall_sysenter_event, 0, sizeof(vmi_event_t)); msr_syscall_sysenter_event.type = VMI_EVENT_MEMORY; msr_syscall_sysenter_event.mem_event.physical_address = phys_sysenter_ip; msr_syscall_sysenter_event.mem_event.npages = 1; msr_syscall_sysenter_event.mem_event.granularity=VMI_MEMEVENT_PAGE; memset(&kernel_sysenter_target_event, 0, sizeof(vmi_event_t)); kernel_sysenter_target_event.type = VMI_EVENT_MEMORY; kernel_sysenter_target_event.mem_event.physical_address = phys_ia32_sysenter_target; kernel_sysenter_target_event.mem_event.npages = 1; kernel_sysenter_target_event.mem_event.granularity=VMI_MEMEVENT_PAGE; memset(&kernel_vsyscall_event, 0, sizeof(vmi_event_t)); kernel_vsyscall_event.type = VMI_EVENT_MEMORY; kernel_vsyscall_event.mem_event.physical_address = phys_vsyscall; kernel_vsyscall_event.mem_event.npages = 1; kernel_vsyscall_event.mem_event.granularity=VMI_MEMEVENT_PAGE; while(!interrupted){ printf("Waiting for events...\n"); status = vmi_events_listen(vmi,500); //监听指定的虚拟机 虚拟机上绑定了特定的监听事件 if (status != VMI_SUCCESS) { printf("Error waiting for events, quitting...\n"); interrupted = -1; } } printf("Finished with test.\n"); leave: // cleanup any memory associated with the libvmi instance vmi_destroy(vmi); return 0; }
int main (int argc, char **argv) { vmi_instance_t vmi = NULL; status_t status = VMI_SUCCESS; struct sigaction act; reg_t lstar = 0; addr_t phys_lstar = 0; reg_t cstar = 0; addr_t phys_cstar = 0; reg_t sysenter_ip = 0; addr_t phys_sysenter_ip = 0; addr_t ia32_sysenter_target = 0; addr_t phys_ia32_sysenter_target = 0; addr_t vsyscall = 0; addr_t phys_vsyscall = 0; char *name = NULL; vmi_pid_t pid = -1; if(argc < 2){ fprintf(stderr, "Usage: events_example <name of VM> <PID of process to track {optional}>\n"); exit(1); } // Arg 1 is the VM name. name = argv[1]; // Arg 2 is the pid of the process to track. if(argc == 3) pid = (int) strtoul(argv[2], NULL, 0); /* for a clean exit */ act.sa_handler = close_handler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGALRM, &act, NULL); // Initialize the libvmi library. if (vmi_init(&vmi, VMI_XEN | VMI_INIT_COMPLETE | VMI_INIT_EVENTS, name) == VMI_FAILURE){ printf("Failed to init LibVMI library.\n"); if (vmi != NULL ) { vmi_destroy(vmi); } return 1; } else{ printf("LibVMI init succeeded!\n"); } // Get the cr3 for this process. if(pid != -1) { cr3 = vmi_pid_to_dtb(vmi, pid); printf("CR3 for process (%d) == %llx\n", pid, (unsigned long long)cr3); } // Get the value of lstar and cstar for the system. // NOTE: all vCPUs have the same value for these registers vmi_get_vcpureg(vmi, &lstar, MSR_LSTAR, 0); vmi_get_vcpureg(vmi, &cstar, MSR_CSTAR, 0); vmi_get_vcpureg(vmi, &sysenter_ip, SYSENTER_EIP, 0); printf("vcpu 0 MSR_LSTAR == %llx\n", (unsigned long long)lstar); printf("vcpu 0 MSR_CSTAR == %llx\n", (unsigned long long)cstar); printf("vcpu 0 MSR_SYSENTER_IP == %llx\n", (unsigned long long)sysenter_ip); ia32_sysenter_target = vmi_translate_ksym2v(vmi, "ia32_sysenter_target"); printf("ksym ia32_sysenter_target == %llx\n", (unsigned long long)ia32_sysenter_target); /* Per Linux ABI, this VA represents the start of the vsyscall page * If vsyscall support is enabled (deprecated or disabled on many newer * 3.0+ kernels), it is accessible at this address in every process. */ vsyscall = 0xffffffffff600000; // Translate to a physical address. phys_lstar= vmi_translate_kv2p(vmi, lstar); printf("Physical LSTAR == %llx\n", (unsigned long long)phys_lstar); phys_cstar= vmi_translate_kv2p(vmi, cstar); printf("Physical CSTAR == %llx\n", (unsigned long long)phys_cstar); phys_sysenter_ip= vmi_translate_kv2p(vmi, sysenter_ip); printf("Physical SYSENTER_IP == %llx\n", (unsigned long long)phys_sysenter_ip); phys_ia32_sysenter_target = vmi_translate_kv2p(vmi,ia32_sysenter_target); printf("Physical ia32_sysenter_target == %llx\n", (unsigned long long)ia32_sysenter_target); phys_vsyscall = vmi_translate_kv2p(vmi,vsyscall); printf("Physical phys_vsyscall == %llx\n", (unsigned long long)phys_vsyscall); // Get only the page that the handler starts. printf("LSTAR Physical PFN == %llx\n", (unsigned long long)(phys_lstar >> 12)); printf("CSTAR Physical PFN == %llx\n", (unsigned long long)(phys_cstar >> 12)); printf("SYSENTER_IP Physical PFN == %llx\n", (unsigned long long)(phys_sysenter_ip >> 12)); printf("phys_vsyscall Physical PFN == %llx\n", (unsigned long long)(phys_vsyscall >> 12)); printf("phys_ia32_sysenter_target Physical PFN == %llx\n", (unsigned long long)(phys_ia32_sysenter_target >> 12)); /* Configure an event to track when the process is running. * (The CR3 register is updated on task context switch, allowing * us to follow as various tasks are scheduled and run upon the CPU) */ memset(&cr3_event, 0, sizeof(vmi_event_t)); cr3_event.type = VMI_EVENT_REGISTER; cr3_event.reg_event.reg = CR3; /* Observe only write events to the given register. * NOTE: read events are unsupported at this time. */ cr3_event.reg_event.in_access = VMI_REGACCESS_W; /* Optional (default = 0): Trigger on change * Causes events to be delivered by the hypervisor to this monitoring * program if and only if the register value differs from that previously * observed. * Usage: cr3_event.reg_event.onchange = 1; * * Optional (default = 0): Asynchronous event delivery * Causes events to be delivered by the hypervisor to this monitoring * program if and only if the register value differs from that previously * observed. * Usage: cr3_event.reg_event.async =1; */ if(pid == -1){ cr3_event.callback = cr3_all_tasks_callback; vmi_register_event(vmi, &cr3_event); } else { cr3_event.callback = cr3_one_task_callback; /* This acts as a filter: if the CR3 value at time of event == the CR3 * we wish to inspect, then the callback will be invoked. Otherwise, * no action is taken. */ cr3_event.reg_event.equal = cr3; vmi_register_event(vmi, &cr3_event); } // Setup a default event for tracking memory at the syscall handler. // But don't install it; that will be done by the cr3 handler. memset(&msr_syscall_sysenter_event, 0, sizeof(vmi_event_t)); msr_syscall_sysenter_event.type = VMI_EVENT_MEMORY; msr_syscall_sysenter_event.mem_event.physical_address = phys_sysenter_ip; msr_syscall_sysenter_event.mem_event.npages = 1; msr_syscall_sysenter_event.mem_event.granularity=VMI_MEMEVENT_PAGE; memset(&kernel_sysenter_target_event, 0, sizeof(vmi_event_t)); kernel_sysenter_target_event.type = VMI_EVENT_MEMORY; kernel_sysenter_target_event.mem_event.physical_address = phys_ia32_sysenter_target; kernel_sysenter_target_event.mem_event.npages = 1; kernel_sysenter_target_event.mem_event.granularity=VMI_MEMEVENT_PAGE; memset(&kernel_vsyscall_event, 0, sizeof(vmi_event_t)); kernel_vsyscall_event.type = VMI_EVENT_MEMORY; kernel_vsyscall_event.mem_event.physical_address = phys_vsyscall; kernel_vsyscall_event.mem_event.npages = 1; kernel_vsyscall_event.mem_event.granularity=VMI_MEMEVENT_PAGE; while(!interrupted){ printf("Waiting for events...\n"); status = vmi_events_listen(vmi,500); if (status != VMI_SUCCESS) { printf("Error waiting for events, quitting...\n"); interrupted = -1; } } printf("Finished with test.\n"); leave: // cleanup any memory associated with the libvmi instance vmi_destroy(vmi); return 0; }
/////////////////////////////////////////////////////////// // Classic read functions for access to memory status_t vmi_read( vmi_instance_t vmi, const access_context_t *ctx, size_t count, void *buf, size_t *bytes_read) { status_t ret = VMI_FAILURE; unsigned char *memory = NULL; addr_t start_addr = 0; addr_t paddr = 0; addr_t pfn = 0; addr_t offset = 0; addr_t dtb = 0; size_t buf_offset = 0; #ifdef ENABLE_SAFETY_CHECKS if (NULL == vmi) { dbprint(VMI_DEBUG_READ, "--%s: vmi passed as NULL, returning without read\n", __FUNCTION__); goto done; } if (NULL == ctx) { dbprint(VMI_DEBUG_READ, "--%s: ctx passed as NULL, returning without read\n", __FUNCTION__); goto done; } if (NULL == buf) { dbprint(VMI_DEBUG_READ, "--%s: buf passed as NULL, returning without read\n", __FUNCTION__); goto done; } #endif switch (ctx->translate_mechanism) { case VMI_TM_NONE: start_addr = ctx->addr; break; case VMI_TM_KERNEL_SYMBOL: #ifdef ENABLE_SAFETY_CHECKS if (!vmi->arch_interface || !vmi->os_interface || !vmi->kpgd) goto done; #endif dtb = vmi->kpgd; if ( VMI_FAILURE == vmi_translate_ksym2v(vmi, ctx->ksym, &start_addr) ) goto done; break; case VMI_TM_PROCESS_PID: #ifdef ENABLE_SAFETY_CHECKS if (!vmi->arch_interface || !vmi->os_interface) goto done; #endif if ( !ctx->pid ) dtb = vmi->kpgd; else if (ctx->pid > 0) { if ( VMI_FAILURE == vmi_pid_to_dtb(vmi, ctx->pid, &dtb) ) goto done; } if (!dtb) goto done; start_addr = ctx->addr; break; case VMI_TM_PROCESS_DTB: #ifdef ENABLE_SAFETY_CHECKS if (!vmi->arch_interface) goto done; #endif dtb = ctx->dtb; start_addr = ctx->addr; break; default: errprint("%s error: translation mechanism is not defined.\n", __FUNCTION__); goto done; } while (count > 0) { size_t read_len = 0; if (dtb) { if (VMI_SUCCESS != vmi_pagetable_lookup_cache(vmi, dtb, start_addr + buf_offset, &paddr)) goto done; } else { paddr = start_addr + buf_offset; } /* access the memory */ pfn = paddr >> vmi->page_shift; offset = (vmi->page_size - 1) & paddr; memory = vmi_read_page(vmi, pfn); if (NULL == memory) goto done; /* determine how much we can read */ if ((offset + count) > vmi->page_size) { read_len = vmi->page_size - offset; } else { read_len = count; } /* do the read */ memcpy(((char *) buf) + (addr_t) buf_offset, memory + (addr_t) offset, read_len); /* set variables for next loop */ count -= read_len; buf_offset += read_len; } ret = VMI_SUCCESS; done: if ( bytes_read ) *bytes_read = buf_offset; return ret; }
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; }
/////////////////////////////////////////////////////////// // Classic write functions for access to memory size_t vmi_write( vmi_instance_t vmi, const access_context_t *ctx, void *buf, size_t count) { addr_t start_addr = 0; addr_t dtb = 0; addr_t paddr = 0; addr_t offset = 0; size_t buf_offset = 0; if (NULL == buf) { dbprint(VMI_DEBUG_WRITE, "--%s: buf passed as NULL, returning without write\n", __FUNCTION__); return 0; } if (NULL == ctx) { dbprint(VMI_DEBUG_WRITE, "--%s: ctx passed as NULL, returning without write\n", __FUNCTION__); return 0; } switch (ctx->translate_mechanism) { case VMI_TM_NONE: start_addr = ctx->addr; break; case VMI_TM_KERNEL_SYMBOL: if (!vmi->arch_interface || !vmi->os_interface || !vmi->kpgd) return 0; dtb = vmi->kpgd; start_addr = vmi_translate_ksym2v(vmi, ctx->ksym); break; case VMI_TM_PROCESS_PID: if (!vmi->arch_interface || !vmi->os_interface) { return 0; } if(ctx->pid) { dtb = vmi_pid_to_dtb(vmi, ctx->pid); } else { dtb = vmi->kpgd; } if (!dtb) { return 0; } start_addr = ctx->addr; break; case VMI_TM_PROCESS_DTB: if (!vmi->arch_interface) { return 0; } dtb = ctx->dtb; start_addr = ctx->addr; break; default: errprint("%s error: translation mechanism is not defined.\n", __FUNCTION__); return 0; } while (count > 0) { size_t write_len = 0; if(dtb) { if (VMI_SUCCESS != vmi_pagetable_lookup_cache(vmi, dtb, start_addr + buf_offset, &paddr)) { return buf_offset; } } else { paddr = start_addr + buf_offset; } /* determine how much we can write to this page */ offset = (vmi->page_size - 1) & paddr; if ((offset + count) > vmi->page_size) { write_len = vmi->page_size - offset; } else { write_len = count; } /* do the write */ if (VMI_FAILURE == driver_write(vmi, paddr, ((char *) buf + (addr_t) buf_offset), write_len)) { return buf_offset; } /* set variables for next loop */ count -= write_len; buf_offset += write_len; } return buf_offset; }
int main (int argc, char **argv) { // local variables vmi_instance_t vmi; int ret_val = 0; // return code for after goto struct sigaction signal_action; // process list vars vmi_pid_t pid = 0; char *procname = NULL; addr_t list_head = 0; addr_t next_list_entry = 0; addr_t current_process = 0; addr_t tmp_next = 0; status_t status; // breakpoint vars char* *sym; uint16_t *off; vmi_pid_t *bpid; addr_t *add; uint8_t *byt; addr_t lib_map_addr = 0; addr_t BN_rand_addr = 0; int bnrand_jump_offset = 0; addr_t bnrand_addr = 0; // this is the VM or file that we are looking at if (argc < 2) { printf("Usage: %s <vmname>\n", argv[0]); return 1; } char *name = argv[1]; //////////////////// // Initialization // //////////////////// // initialize the libvmi library printf("Initializing libvmi for VM \"%s\"\n", name); if (vmi_init(&vmi, VMI_XEN|VMI_INIT_COMPLETE|VMI_INIT_EVENTS, name) == VMI_FAILURE) { printf("Failed to init LibVMI library.\n"); ret_val = 2; goto error_exit; } // verify OS is Linux printf("Verifying the VM is running Linux..."); // TODO: verify that the VM is running a *supported* Linux kernel // if kernel is not one we recognize, don't run because we'll be mucking around in memory we don't understand if (VMI_OS_LINUX != vmi_get_ostype(vmi)) { // this only checks if /etc/libvmi.conf says it's "Linux" printf("\nVM is running %s, exiting...\n", vmi_get_ostype(vmi)); ret_val = 3; goto error_exit; } printf(" Yup. Good to go.\n"); // pause the vm for consistent memory access printf("Pausing the VM\n"); if (vmi_pause_vm(vmi) != VMI_SUCCESS) { printf("Failed to pause VM\n"); ret_val = 4; goto error_exit; // don't return directly, do cleanup first } tasks_offset = vmi_get_offset(vmi, "linux_tasks"); name_offset = vmi_get_offset(vmi, "linux_name"); pid_offset = vmi_get_offset(vmi, "linux_pid"); mm_offset = vmi_get_offset(vmi, "linux_mm"); // hardcoded because config_parser doesn't support dynamic config vars mmap_offset = 0x0; vm_area_file_offset = 0xa0; vm_area_next_offset = 0x10; vm_area_start_offset = 0x0; file_path_offset = 0x10; dentry_offset = 0x8; iname_offset = 0x38; //mmap_offset = vmi_get_offset(vmi, "linux_mmap"); //vm_area_file_offset = vmi_get_offset(vmi, "linux_vm_file"); //vm_area_next_offset = vmi_get_offset(vmi, "linux_vm_next"); //vm_area_start_offset = vmi_get_offset(vmi, "linux_vm_start"); //file_path_offset = vmi_get_offset(vmi, "linux_f_path"); //dentry_offset = vmi_get_offset(vmi, "linux_dentry"); //iname_offset = vmi_get_offset(vmi, "linux_d_iname"); if (0 == tasks_offset) { printf("Failed to find tasks_offset\n"); goto error_exit; } if (0 == pid_offset) { printf("Failed to find pid_offset\n"); goto error_exit; } if (0 == name_offset) { printf("Failed to find name_offset\n"); goto error_exit; } if (0 == mm_offset) { printf("Failed to find mm_offset\n"); goto error_exit; } //if (0 == mmap_offset) { // printf("Failed to find mmap_offset\n"); // goto error_exit; //} //if (0 == vm_area_file_offset) { // printf("Failed to find vm_area_file_offset\n"); // goto error_exit; //} //if (0 == vm_area_next_offset) { // printf("Failed to find vm_area_next_offset\n"); // goto error_exit; //} //if (0 == vm_area_start_offset) { // printf("Failed to find vm_area_start_offset\n"); // goto error_exit; //} //if (0 == file_path_offset) { // printf("Failed to find file_path_offset\n"); // goto error_exit; //} //if (0 == dentry_offset) { // printf("Failed to find dentry_offset\n"); // goto error_exit; //} //if (0 == iname_offset) { // printf("Failed to find iname_offset\n"); // goto error_exit; //} // Set up breakpoints breakpoints = (breakpoint_t*)calloc(MAX_BREAKPOINTS, sizeof(breakpoint_t)); // allocate space for each breakpoint, zero memory //add_breakpoint("extract_entropy_user", 155, 0, 0xe8, before_extract_buf); //add_breakpoint("extract_entropy_user", 160, 0, 0x83, after_extract_buf); // new breakpoints created below ////////////////////////// // Find apache2 process // ////////////////////////// // find pid of apache2 processes // for each process, read symbol table and find BN_rand function // at offset +13 bytes from BN_rand, we find an offset from the instruction at BN_rand+17 (probably around -700 bytes) // +570 bytes from the offset above, we find the instruction at which we want a breakpoint // so, breakpoint at BN_rand+17+[BN_rand+13]+570 // then, at callback, read r13 for address of buffer, overwrite with TODO bytes // find pid of apache2 processes list_head = vmi_translate_ksym2v(vmi, "init_task") + tasks_offset; // find init_task struct and move to first linked list entry next_list_entry = list_head; // iterator do { current_process = next_list_entry - tasks_offset; // subtract tasks_offset back off to get to head of struct vmi_read_32_va(vmi, current_process + pid_offset, 0, (uint32_t*)&pid); // get pid of this process procname = vmi_read_str_va(vmi, current_process + name_offset, 0); // get process name of this process if (strncmp(procname,"apache2",sizeof("apache2")) == 0) { printf("Finding library address in %s [pid %d]\n",procname,pid); lib_map_addr = walk_vmmap_for_lib(vmi, current_process, "libcrypto.so.1.0.2"); if (lib_map_addr == 0) { // if failed to find lib printf("Failed to find library in %s\n",procname); ret_val = 9; goto error_exit; } printf("Found library address: 0x%llx\n", lib_map_addr); // for each process, read symbol table and find BN_rand function BN_rand_addr = lib_map_addr + 0xd5a50; // static offset for BN_rand function // at offset +13 bytes from BN_rand, we find an offset from the instruction at BN_rand+17 (probably around -700 bytes) vmi_read_32_va(vmi, BN_rand_addr+13, pid, &bnrand_jump_offset); // get jump offset to bnrand function //printf("jump offset: %d\n",bnrand_jump_offset); bnrand_addr = BN_rand_addr+17+bnrand_jump_offset; // get address of bnrand function //printf("bnrand: 0x%llx\n", bnrand_addr); // +570 bytes from the offset above, we find the instruction at which we want a breakpoint add_breakpoint_addr(bnrand_addr + 570, pid, 0x31, bnrand_callback); //printf("Added breakpoint at 0x%llx\n",bnrand_addr+570); } status = vmi_read_addr_va(vmi, next_list_entry, 0, &next_list_entry); // follow linked-list->next to next element 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); for (int i = 0; i < num_breakpoints; i++) { // iterate over breakpoints and find the right addresses for them //////////////////////////////////////////// // Find memory location to put breakpoint // //////////////////////////////////////////// // assign short names (note: modifying these modifies the breakpoint struct) sym = &breakpoints[i].symbol; off = &breakpoints[i].offset; bpid = &breakpoints[i].pid; add = &breakpoints[i].addr; byt = &breakpoints[i].inst_byte; // remember that if this is not set above, it should be zeroed from calloc if (breakpoints[i].addr == 0) { // if don't have address, find symbol // find address to break on printf("Accessing System Map for %s symbol\n", *sym); *add = vmi_translate_ksym2v(vmi, *sym) + *off; printf("%s + %u is at 0x%llx\n", *sym, *off, *add); } // either verify the byte there is correct, or record which byte is there for later replacing if (*byt == 0) { // if this byte was not set, we need to get it vmi_read_8_va(vmi, *add, *bpid, byt); // read it directly into byt printf("[pid %d] Saving byte at address 0x%llx: %x\n", *bpid, *add, *byt); } else { // if the byte was set, verify that it's currently set to that value uint8_t temp_byte = 0; vmi_read_8_va(vmi, *add, *bpid, &temp_byte); // read it temporarily printf("[pid %d] Checking byte at address 0x%llx is set to %x: %x\n", *bpid, *add, *byt, temp_byte); if (*byt != temp_byte) { // uh oh, we have an error ret_val = 8; goto error_exit; } } } // end first for loop after breakpoints are constructed properly /////////////////// // Main gameplan // // // // https://groups.google.com/forum/#!topic/vmitools/jNGxM0LBEDM // Based on the google groups discussion above (which I wish I found earlier, meh), it looks like the way people trap on instructions is to: // 1) actually *modify* the memory to have the 0xcc (interrupt 3, aka breakpoint) instruction in place of the instruction it would have executed // 2) register an event on receiving the INT3 signal and receive the callback // 3) at the end of the callback, fix the memory to its original instruction, // 4) single-step one instruction forward, executing the one instruction, then getting another callback // 5) replace the previous instruction with to 0xcc, "resetting" the breakpoint, then clearing the event and continuing // // /////////////////// for (int i = 0; i < num_breakpoints; i++) { // iterate over breakpoints and insert them all // assign short names (note: modifying these modifies the breakpoint struct) add = &breakpoints[i].addr; bpid = &breakpoints[i].pid; byt = &breakpoints[i].inst_byte; // Step 1: modify memory in the VM with an INT3 instruction (0xcc) printf("[pid %d] Setting breakpoint at address 0x%llx.\n", *bpid, *add); uint8_t int3 = INT3_INST; // create temporary variable because we can't use an address to a static #defined int if (VMI_SUCCESS != vmi_write_8_va(vmi, *add, *bpid, &int3)) { printf("[pid %d] Couldn't write INT3 instruction to memory... exiting.\n", *bpid); ret_val = 5; goto error_exit; } // debug: check memory is now an INT3 instruction uint8_t temp_byte = 0; vmi_read_8_va(vmi, *add, 0, &temp_byte); printf("[pid %d] This should be an INT3 instruction (0xcc): 0x%x\n", *bpid, temp_byte); } // end second for loop after breakpoints are all inserted and callback is registered // Step 2: register an event on receiving INT3 signal printf("Creating event for callback when breakpoint is reached.\n"); memset(&rng_event, 0, sizeof(vmi_event_t)); // clear rng_event so we can set everything fresh rng_event.type = VMI_EVENT_INTERRUPT; // interrupt event -- trigger when interrupt occurs rng_event.interrupt_event.intr = INT3; // trigger on INT3 instruction rng_event.interrupt_event.reinject = 0; // swallow interrupt silently without passing it on to guest rng_event.callback = rng_int3_event_callback; // reference to our callback function printf("Registering event...\n"); if (VMI_SUCCESS == vmi_register_event(vmi, &rng_event)) {; // register the event! printf("Event Registered!\n"); } else { // uh oh, event failed printf("Problem registering event... exiting.\n"); ret_val = 6; goto error_exit; // don't return directly, do cleanup first } // resume the VM printf("Resuming the VM\n"); vmi_resume_vm(vmi); ////////////////////////////////////// // Spin and wait for event callback // ////////////////////////////////////// // for a clean exit, catch signals (from host, not VM), set "interrupted" to non-zero, exit while loop at end of main() signal_action.sa_handler = close_handler; signal_action.sa_flags = 0; sigemptyset(&signal_action.sa_mask); sigaction(SIGHUP, &signal_action, NULL); sigaction(SIGTERM, &signal_action, NULL); sigaction(SIGINT, &signal_action, NULL); sigaction(SIGALRM, &signal_action, NULL); while(!interrupted) { // until an interrupt happens printf("Waiting for events...\n"); if (VMI_SUCCESS != vmi_events_listen(vmi, 500)) { // listen for events for 500ms (no event = VMI_SUCCESS) printf("Error waiting for events... exiting.\n"); interrupted = -1; } } printf("Finished with test.\n"); ////////////////// // Exit cleanly // ////////////////// error_exit: // attempt to remove breakpoints for (int i = 0; i < num_breakpoints; i++) { // iterate over breakpoints and insert them all // assign short names (note: modifying these modifies the breakpoint struct) add = &breakpoints[i].addr; bpid = &breakpoints[i].pid; byt = &breakpoints[i].inst_byte; printf("[pid %d] Removing breakpoint %d at 0x%llx.\n", *bpid, i, *add); if (VMI_SUCCESS != vmi_write_8_va(vmi, *add, *bpid, byt)) { printf("Couldn't write to memory... exiting.\n"); ret_val = 7; } } // resume the vm printf("Resuming the VM\n"); vmi_resume_vm(vmi); // cleanup any memory associated with the LibVMI instance printf("Cleaning up\n"); vmi_destroy(vmi); return ret_val; }
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; }
int introspect_process_list (char *name) { vmi_instance_t vmi; addr_t list_head = 0, next_list_entry = 0, current_process = 0; vmi_pid_t pid = 0; char *procname = NULL; if (vmi_init(&vmi, VMI_XEN | VMI_INIT_COMPLETE, name) == VMI_FAILURE) { printf("Failed to init LibVMI library.\n"); return 1; } vmi_pause_vm(vmi); /** * get offsets of the kernel data structures * get the head of the task_struct */ switch(vmi_get_ostype(vmi)) { case VMI_OS_LINUX: tasks_offset = vmi_get_offset(vmi, "linux_tasks"); name_offset = vmi_get_offset(vmi, "linux_name"); pid_offset = vmi_get_offset(vmi, "linux_pid"); list_head = vmi_translate_ksym2v(vmi, "init_task") + tasks_offset; break; case VMI_OS_WINDOWS: tasks_offset = vmi_get_offset(vmi, "win_tasks"); name_offset = vmi_get_offset(vmi, "win_pname"); pid_offset = vmi_get_offset(vmi, "win_pid"); list_head = vmi_translate_ksym2v(vmi, "PsActiveProcessHead"); break; default: goto exit; } if (tasks_offset == 0 || pid_offset == 0 || name_offset == 0) { printf("Failed to find offsets\n"); goto exit; } next_list_entry = list_head; /** * traverse the task lists and print out each process */ do { current_process = next_list_entry - tasks_offset; 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 exit; } printf("[%5d] %s\n", pid, procname); free(procname); procname = NULL; if (vmi_read_addr_va(vmi, next_list_entry, 0, &next_list_entry) == VMI_FAILURE) { printf("Failed to read next pointer in loop at %"PRIx64"\n", next_list_entry); goto exit; } } while(next_list_entry != list_head); exit: vmi_resume_vm(vmi); vmi_destroy(vmi); return 0; }
/////////////////////////////////////////////////////////// // Classic read functions for access to memory size_t vmi_read( vmi_instance_t vmi, access_context_t *ctx, void *buf, size_t count) { unsigned char *memory = NULL; addr_t start_addr = 0; addr_t paddr = 0; addr_t pfn = 0; addr_t offset = 0; addr_t dtb = 0; size_t buf_offset = 0; if (NULL == ctx) { dbprint(VMI_DEBUG_READ, "--%s: ctx passed as NULL, returning without read\n", __FUNCTION__); return 0; } if (NULL == buf) { dbprint(VMI_DEBUG_READ, "--%s: buf passed as NULL, returning without read\n", __FUNCTION__); return 0; } switch (ctx->translate_mechanism) { case VMI_TM_NONE: start_addr = ctx->addr; break; case VMI_TM_KERNEL_SYMBOL: if (!vmi->arch_interface || !vmi->os_interface) { return 0; } dtb = vmi->kpgd; start_addr = vmi_translate_ksym2v(vmi, ctx->ksym); break; case VMI_TM_PROCESS_PID: if (!vmi->arch_interface || !vmi->os_interface) { return 0; } if(ctx->pid) { dtb = vmi_pid_to_dtb(vmi, ctx->pid); } else { dtb = vmi->kpgd; } start_addr = ctx->addr; break; case VMI_TM_PROCESS_DTB: if (!vmi->arch_interface) { return 0; } dtb = ctx->dtb; start_addr = ctx->addr; break; default: errprint("%s error: translation mechanism is not defined.\n", __FUNCTION__); return 0; } while (count > 0) { size_t read_len = 0; if(dtb) { paddr = vmi_pagetable_lookup(vmi, dtb, start_addr + buf_offset); } else { paddr = start_addr + buf_offset; } if (!paddr) { return buf_offset; } /* access the memory */ pfn = paddr >> vmi->page_shift; offset = (vmi->page_size - 1) & paddr; memory = vmi_read_page(vmi, pfn); if (NULL == memory) { return buf_offset; } /* determine how much we can read */ if ((offset + count) > vmi->page_size) { read_len = vmi->page_size - offset; } else { read_len = count; } /* do the read */ memcpy(((char *) buf) + (addr_t) buf_offset, memory + (addr_t) offset, read_len); /* set variables for next loop */ count -= read_len; buf_offset += read_len; } return buf_offset; }