event_response_t after_extract_buf(vmi_instance_t vmi, vmi_event_t *event) { printf("Called after_extract_buf!\n"); // read in all the bytes at buf uint8_t buffer[EXTRACT_SIZE]; vmi_read_va(vmi, rng_buf, 0, buffer, EXTRACT_SIZE); printf("old buf: "); for (int i = 0; i < EXTRACT_SIZE; i++) { printf("%02x ",buffer[i]); } printf("\n"); // modify rng buffer! vmi_write_va(vmi, rng_buf , 0, RNG_VALUE, EXTRACT_SIZE); // read in all the bytes at buf again (sanity check) vmi_read_va(vmi, rng_buf, 0, buffer, EXTRACT_SIZE); printf("new buf: "); for (int i = 0; i < EXTRACT_SIZE; i++) { printf("%02x ",buffer[i]); } printf("\n"); return VMI_SUCCESS; }
/////////////////////////////////////////////////////////// // Easy write to virtual memory static status_t vmi_write_X_va (vmi_instance_t vmi, addr_t vaddr, int pid, void *value, int size) { size_t len_write = vmi_write_va(vmi, vaddr, pid, value, size); if (len_write == size){ return VMI_SUCCESS; } else{ return VMI_FAILURE; } }
event_response_t overwrite_buf(vmi_instance_t vmi, vmi_event_t *event) { printf("Called overwrite_buf!\n"); // local vars addr_t val_addr = 0; uint8_t buf[EXTRACT_SIZE]; // Print everything out //printf("VCPU: %d\n", event->vcpu_id); //printf("Pagetable id: %d\n", event->vmm_pagetable_id); //printf("Instruction pointer: 0x%x\n", event->interrupt_event.gla); //printf("Physical page of instruction: 0x%x\n", event->interrupt_event.gfn); //printf("Page offset: 0x%x\n", event->interrupt_event.offset); //printf("Interrupt type (1 is INT3): %d\n", event->interrupt_event.intr); ////////////////////////// // Access random number // ////////////////////////// // Print amd64 function args --> see below link for reference // https://blogs.oracle.com/eschrock/entry/debugging_://blogs.oracle.com/eschrock/entry/debugging_on_amd64_part_twoon_amd64_part_two printf("Reading RDI register (*r): 0x%llx\n", event->regs.x86->rdi); printf("Reading RSI register (*tmp): 0x%llx\n", event->regs.x86->rsi); printf("Reading RSP+0x16: 0x%llx (should be the same as RSI)\n", event->regs.x86->rsp+0x16); // val_addr is our RSP+0x16 val_addr = event->regs.x86->rsp + 0x16; // what's currently at RSP+0x16? vmi_read_va(vmi, val_addr, 0, buf, EXTRACT_SIZE); printf("old buf: "); for (int i = 0; i < EXTRACT_SIZE; i++) { printf("%02x ",buf[i]); } printf("\n"); // modify rng buffer! (should be at RSP+0x16, from static code analysis) vmi_write_va(vmi, val_addr, 0, RNG_VALUE , EXTRACT_SIZE); // what's at RSP+0x16 now? vmi_read_va(vmi, val_addr, 0, buf, EXTRACT_SIZE); printf("new buf: "); for (int i = 0; i < EXTRACT_SIZE; i++) { printf("%02x ",buf[i]); } printf("\n"); return VMI_SUCCESS; }
event_response_t bnrand_callback(vmi_instance_t vmi, vmi_event_t *event) { uint64_t nbytes; addr_t buf_addr; uint8_t* buffer; vmi_pid_t pid = vmi_dtb_to_pid(vmi, event->regs.x86->cr3); printf("Called bnrand_callback from pid %d!\n"); // look at args printf("Reading RDI register (*buf): 0x%llx\n", event->regs.x86->rdi); printf("Reading RSI register (bytes): 0x%llx\n", event->regs.x86->rsi); printf("Reading RDX register (*rnd): 0x%llx\n", event->regs.x86->rdx); // read in all the bytes at buf nbytes = event->regs.x86->rsi; buf_addr = event->regs.x86->rdi; buffer = malloc(nbytes); // allocate buffer vmi_read_va(vmi, buf_addr, pid, buffer, nbytes); printf("old buf: "); for (unsigned int i = 0; i < nbytes; i++) { printf("%02x ",buffer[i]); } printf("\n"); // modify rng buffer! vmi_write_va(vmi, buf_addr, pid, RNG_VALUE, nbytes); // read in all the bytes at buf again (sanity check) vmi_read_va(vmi, buf_addr, pid, buffer, nbytes); printf("new buf: "); for (unsigned int i = 0; i < nbytes; i++) { printf("%02x ",buffer[i]); } printf("\n"); return VMI_SUCCESS; }
void hijack_thread(struct injector *injector, vmi_instance_t vmi, unsigned int vcpu, vmi_pid_t pid) { printf("Ready to hijack thread of PID %u on vCPU %u!\n", pid, vcpu); addr_t cpa = sym2va(vmi, pid, "kernel32.dll", "CreateProcessA"); printf("CPA @ 0x%lx\n", cpa); reg_t fsgs, rbp, rsp, rip, rcx, rdx, rax, r8, r9; addr_t stack_base, stack_limit; vmi_get_vcpureg(vmi, &rsp, RSP, vcpu); vmi_get_vcpureg(vmi, &rip, RIP, vcpu); vmi_get_vcpureg(vmi, &rax, RAX, vcpu); vmi_get_vcpureg(vmi, &rcx, RCX, vcpu); vmi_get_vcpureg(vmi, &rdx, RDX, vcpu); vmi_get_vcpureg(vmi, &r8, R8, vcpu); vmi_get_vcpureg(vmi, &r9, R9, vcpu); if (injector->pm == VMI_PM_LEGACY || injector->pm == VMI_PM_PAE) { vmi_get_vcpureg(vmi, &fsgs, FS_BASE, vcpu); vmi_get_vcpureg(vmi, &rbp, RBP, vcpu); printf("FS: 0x%lx RBP: 0x%lx", fsgs, rbp); vmi_read_addr_va(vmi, fsgs + 0x4, pid, &stack_base); vmi_read_addr_va(vmi, fsgs + 0x8, pid, &stack_limit); } else { vmi_get_vcpureg(vmi, &fsgs, GS_BASE, vcpu); printf("GS: 0x%lx ", fsgs); vmi_read_addr_va(vmi, fsgs + 0x8, pid, &stack_base); vmi_read_addr_va(vmi, fsgs + 0x10, pid, &stack_limit); } printf("RSP: 0x%lx. RIP: 0x%lx. RCX: 0x%lx\n", rsp, rip, rcx); printf("Stack base: 0x%lx. Limit: 0x%lx\n", stack_base, stack_limit); //Push input arguments on the stack //CreateProcess(NULL, TARGETPROC, // NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, pi)) uint64_t nul64 = 0; uint32_t nul32 = 0; uint8_t nul8 = 0; size_t len = strlen(injector->target_proc); addr_t addr = rsp; injector->saved_rsp = rsp; injector->saved_rip = rip; injector->saved_rax = rax; injector->saved_rdx = rdx; injector->saved_rcx = rcx; injector->saved_r8 = r8; injector->saved_r9 = r9; if (injector->pm == VMI_PM_LEGACY || injector->pm == VMI_PM_PAE) { addr -= 0x4; // the stack has to be alligned to 0x4 // and we need a bit of extra buffer before the string for \0 // we just going to null out that extra space fully vmi_write_32_va(vmi, addr, pid, &nul32); // this string has to be aligned as well! addr -= len + 0x4 - (len % 0x4); addr_t str_addr = addr; vmi_write_va(vmi, addr, pid, (void*) injector->target_proc, len); // add null termination vmi_write_8_va(vmi, addr + len, pid, &nul8); printf("%s @ 0x%lx.\n", injector->target_proc, str_addr); //struct startup_info_32 si = {.wShowWindow = SW_SHOWDEFAULT }; struct startup_info_32 si; memset(&si, 0, sizeof(struct startup_info_32)); struct process_information_32 pi; memset(&pi, 0, sizeof(struct process_information_32)); addr -= sizeof(struct process_information_32); injector->process_info = addr; vmi_write_va(vmi, addr, pid, &pi, sizeof(struct process_information_32)); printf("pip @ 0x%lx\n", addr); addr -= sizeof(struct startup_info_32); addr_t sip = addr; vmi_write_va(vmi, addr, pid, &si, sizeof(struct startup_info_32)); printf("sip @ 0x%lx\n", addr); //p10 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, (uint32_t *) &injector->process_info); //p9 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, (uint32_t *) &sip); //p8 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p7 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p6 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p5 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p4 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p3 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); //p2 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, (uint32_t *) &str_addr); //p1 addr -= 0x4; vmi_write_32_va(vmi, addr, pid, &nul32); // save the return address (RIP) addr -= 0x4; vmi_write_32_va(vmi, addr, pid, (uint32_t *) &rip); } else { addr -= 0x8; // the stack has to be alligned to 0x8 // and we need a bit of extra buffer before the string for \0 // we just going to null out that extra space fully vmi_write_64_va(vmi, addr, pid, &nul64); // this string has to be aligned as well! addr -= len + 0x8 - (len % 0x8); addr_t str_addr = addr; vmi_write_va(vmi, addr, pid, (void*) injector->target_proc, len); // add null termination vmi_write_8_va(vmi, addr + len, pid, &nul8); printf("%s @ 0x%lx.\n", injector->target_proc, str_addr); struct startup_info_64 si; memset(&si, 0, sizeof(struct startup_info_64)); struct process_information_64 pi; memset(&pi, 0, sizeof(struct process_information_64)); addr -= sizeof(struct process_information_64); injector->process_info = addr; vmi_write_va(vmi, addr, pid, &pi, sizeof(struct process_information_64)); printf("pip @ 0x%lx\n", addr); addr -= sizeof(struct startup_info_64); addr_t sip = addr; vmi_write_va(vmi, addr, pid, &si, sizeof(struct startup_info_64)); printf("sip @ 0x%lx\n", addr); //http://www.codemachine.com/presentations/GES2010.TRoy.Slides.pdf // //First 4 parameters to functions are always passed in registers //P1=rcx, P2=rdx, P3=r8, P4=r9 //5th parameter onwards (if any) passed via the stack //p10 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &injector->process_info); //p9 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &sip); //p8 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); //p7 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); //p6 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); //p5 addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); // allocate 0x20 "homing space" addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &nul64); //p1 vmi_set_vcpureg(vmi, 0, RCX, vcpu); //p2 vmi_set_vcpureg(vmi, str_addr, RDX, vcpu); //p3 vmi_set_vcpureg(vmi, 0, R8, vcpu); //p4 vmi_set_vcpureg(vmi, 0, R9, vcpu); // save the return address (RIP) addr -= 0x8; vmi_write_64_va(vmi, addr, pid, &rip); } printf("Return address @ 0x%lx -> 0x%lx. Setting RSP: 0x%lx.\n", addr, rip, addr); // Grow the stack and switch execution vmi_set_vcpureg(vmi, addr, RSP, vcpu); vmi_set_vcpureg(vmi, cpa, RIP, vcpu); printf("Done with hijack routine\n"); }
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); }