diff --git a/README.md b/README.md index 81dc8c56f8..d551e3ae8c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # QEMU-NYX -This repository contains Nyx's fork of Qemu. To enable Hypervisor based snapshots, Intel-PT based tracing, and Redqueen style magic byte resolution, we made various extensions to QEMU. This includes the ability to quickly reset memory and devices, ontain precise disassembly of the code running (even when code is partially swapped out / unavailable) & intel-PT decoding, instrument code running in the VM with breakpoint based hooks as well as communicating with a fuzzing frontend (e.g. based on libnyx). +This repository contains Nyx's fork of QEMU. To enable Hypervisor based snapshots, Intel-PT based tracing, and REDQUEEN style magic byte resolution, we made various extensions to QEMU. This includes the ability to quickly reset memory and devices, obtain precise disassembly of the code running (even when code is partially swapped out / unavailable) & Intel-PT decoding, instrument code running in the VM with breakpoint-based hooks as well as communicating with a fuzzing frontend (e.g. based on [libnyx](https://github.com/nyx-fuzz/libnyx)). You can find more detailed information in our main repository. @@ -20,7 +20,7 @@ If you found and fixed a bug on your own: We are very open to patches, please cr ### License -This tool is provided under **AGPL license**. +This tool is provided under **GPLv2 license**. **Free Software Hell Yeah!** diff --git a/compile_qemu_nyx.sh b/compile_qemu_nyx.sh index f09ad70682..e36fe285f8 100755 --- a/compile_qemu_nyx.sh +++ b/compile_qemu_nyx.sh @@ -81,15 +81,9 @@ compile () { echo "[!] QEMU-Nyx is ready!" } -cd libxdc git submodule init -git submodule update -cd .. - -cd capstone_v4 -git submodule init -git submodule update -cd .. +git submodule update libxdc +git submodule update capstone_v4 if [ "$#" == 0 ] ; then error diff --git a/nyx/helpers.c b/nyx/helpers.c index f1507bb18e..9f850395ca 100644 --- a/nyx/helpers.c +++ b/nyx/helpers.c @@ -18,6 +18,7 @@ void nyx_abort(char* msg){ set_abort_reason_auxiliary_buffer(GET_GLOBAL_STATE()->auxilary_buffer, msg, strlen(msg)); synchronization_lock(); + exit(1); } bool is_called_in_fuzzing_mode(const char* hypercall){ @@ -104,6 +105,10 @@ static void resize_coverage_bitmap(uint32_t new_bitmap_size){ /* pass the actual bitmap buffer size to the front-end */ GET_GLOBAL_STATE()->auxilary_buffer->capabilites.agent_coverage_bitmap_size = new_bitmap_size; + + if(new_bitmap_size & (PAGE_SIZE-1)){ + GET_GLOBAL_STATE()->shared_bitmap_size = (new_bitmap_size & ~(PAGE_SIZE-1)) + PAGE_SIZE; + } } bool apply_capabilities(CPUState *cpu){ @@ -127,6 +132,11 @@ bool apply_capabilities(CPUState *cpu){ debug_printf("GET_GLOBAL_STATE()->cap_cr3: %lx\n", GET_GLOBAL_STATE()->cap_cr3); debug_printf("--------------------------\n"); + if (GET_GLOBAL_STATE()->input_buffer_size != GET_GLOBAL_STATE()->shared_payload_buffer_size){ + resize_shared_memory(GET_GLOBAL_STATE()->input_buffer_size, &GET_GLOBAL_STATE()->shared_payload_buffer_size, NULL, GET_GLOBAL_STATE()->shared_payload_buffer_fd); + GET_GLOBAL_STATE()->shared_payload_buffer_size = GET_GLOBAL_STATE()->input_buffer_size; + } + if(GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr&0xfff){ fprintf(stderr, "[QEMU-Nyx] Error: guest's trace bitmap v_addr (0x%lx) is not page aligned!\n", GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr); return false; @@ -157,10 +167,6 @@ bool apply_capabilities(CPUState *cpu){ set_cap_agent_ijon_trace_bitmap(GET_GLOBAL_STATE()->auxilary_buffer, true); } - if (GET_GLOBAL_STATE()->input_buffer_size != GET_GLOBAL_STATE()->shared_payload_buffer_size){ - resize_shared_memory(GET_GLOBAL_STATE()->input_buffer_size, &GET_GLOBAL_STATE()->shared_payload_buffer_size, NULL, GET_GLOBAL_STATE()->shared_payload_buffer_fd); - GET_GLOBAL_STATE()->shared_payload_buffer_size = GET_GLOBAL_STATE()->input_buffer_size; - } /* pass the actual input buffer size to the front-end */ GET_GLOBAL_STATE()->auxilary_buffer->capabilites.agent_input_buffer_size = GET_GLOBAL_STATE()->shared_payload_buffer_size; diff --git a/nyx/hypercall/configuration.c b/nyx/hypercall/configuration.c index b31baf0f98..3b54415653 100644 --- a/nyx/hypercall/configuration.c +++ b/nyx/hypercall/configuration.c @@ -12,6 +12,11 @@ void handle_hypercall_kafl_get_host_config(struct kvm_run *run, CPUState *cpu, u return; } + if (GET_GLOBAL_STATE()->get_host_config_done){ + nyx_abort((char*)"KVM_EXIT_KAFL_GET_HOST_CONFIG called twice..."); + return; + } + memset((void*)&config, 0, sizeof(host_config_t)); config.host_magic = NYX_HOST_MAGIC; @@ -21,6 +26,7 @@ void handle_hypercall_kafl_get_host_config(struct kvm_run *run, CPUState *cpu, u config.payload_buffer_size = GET_GLOBAL_STATE()->shared_payload_buffer_size; write_virtual_memory(vaddr, (uint8_t*)&config, sizeof(host_config_t), cpu); + GET_GLOBAL_STATE()->get_host_config_done = true; } void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, uint64_t hypercall_arg){ @@ -31,6 +37,11 @@ void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, return; } + if (GET_GLOBAL_STATE()->set_agent_config_done){ + nyx_abort((char*)"KVM_EXIT_KAFL_SET_AGENT_CONFIG called twice..."); + return; + } + X86CPU *cpux86 = X86_CPU(cpu); CPUX86State *env = &cpux86->env; @@ -89,4 +100,5 @@ void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, fprintf(stderr, "[QEMU-Nyx] Error: %s - failed (vaddr: 0x%lx)!\n", __func__, vaddr); exit(1); } + GET_GLOBAL_STATE()->set_agent_config_done = true; } \ No newline at end of file diff --git a/nyx/hypercall/hypercall.c b/nyx/hypercall/hypercall.c index 3fae6e54c1..64703657e2 100644 --- a/nyx/hypercall/hypercall.c +++ b/nyx/hypercall/hypercall.c @@ -108,6 +108,12 @@ bool handle_hypercall_kafl_next_payload(struct kvm_run *run, CPUState *cpu, uint synchronization_lock(); } else { + + if (GET_GLOBAL_STATE()->set_agent_config_done == false){ + nyx_abort((char*)"KVM_EXIT_KAFL_SET_AGENT_CONFIG was not called..."); + return false; + } + if(!setup_snapshot_once){ //pt_reset_bitmap(); @@ -193,6 +199,11 @@ static void handle_hypercall_get_payload(struct kvm_run *run, CPUState *cpu, uin return; } + if (GET_GLOBAL_STATE()->get_host_config_done == false){ + nyx_abort((char*)"KVM_EXIT_KAFL_GET_HOST_CONFIG was not called..."); + return; + } + if(hypercall_enabled && !setup_snapshot_once){ QEMU_PT_PRINTF(CORE_PREFIX, "Payload Address:\t%lx", hypercall_arg); kvm_arch_get_registers(cpu); diff --git a/nyx/memory_access.c b/nyx/memory_access.c index e5e4bcdf30..ad6dfec8d5 100644 --- a/nyx/memory_access.c +++ b/nyx/memory_access.c @@ -167,13 +167,13 @@ bool remap_payload_slot(uint64_t phys_addr, uint32_t slot, CPUState *cpu){ uint32_t i = slot; - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); @@ -205,9 +205,8 @@ bool remap_slot(uint64_t addr, uint32_t slot, CPUState *cpu, int fd, uint64_t sh fprintf(stderr, "[QEMU-Nyx] Check if the buffer is present in the guest's memory...\n"); exit(1); } - - phys_addr = address_to_ram_offset(phys_addr); } + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); //printf("phys_addr -> %lx\n", phys_addr); @@ -216,8 +215,8 @@ bool remap_slot(uint64_t addr, uint32_t slot, CPUState *cpu, int fd, uint64_t sh QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE))); @@ -239,14 +238,14 @@ bool remap_payload_slot_protected(uint64_t phys_addr, uint32_t slot, CPUState *c uint32_t i = slot; - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); @@ -296,28 +295,28 @@ bool remap_payload_buffer(uint64_t virt_guest_addr, CPUState *cpu){ assert(phys_addr != INVALID_ADDRESS); - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); - if(munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE) == -1){ + if(munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE) == -1){ fprintf(stderr, "munmap failed!\n"); //exit(1); assert(false); } //printf("MMAP: %lx\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); - if(mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)) == MAP_FAILED){ + if(mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)) == MAP_FAILED){ fprintf(stderr, "mmap failed!\n"); //exit(1); assert(false); } - memset((block->host) + phys_addr, 0xab, 0x1000); + memset((block->host) + phys_addr_ram_offset, 0xab, 0x1000); if(GET_GLOBAL_STATE()->protect_payload_buffer){ - mprotect((block->host) + phys_addr, 0x1000, PROT_READ); + mprotect((block->host) + phys_addr_ram_offset, 0x1000, PROT_READ); } fast_reload_blacklist_page(get_fast_reload_snapshot(), phys_addr); diff --git a/nyx/state/snapshot_state.c b/nyx/state/snapshot_state.c index bf45351171..ea0ffe3560 100644 --- a/nyx/state/snapshot_state.c +++ b/nyx/state/snapshot_state.c @@ -128,6 +128,10 @@ void deserialize_state(const char* filename_prefix){ assert(apply_capabilities(qemu_get_cpu(0))); remap_payload_buffer(nyx_global_state->payload_buffer, ((CPUState *)qemu_get_cpu(0)) ); + + /* makes sure that we are allowed to enter the fuzzing loop */ + nyx_global_state->get_host_config_done = true; + nyx_global_state->set_agent_config_done = true; } else{ fprintf(stderr, "[QEMU-Nyx]: this feature is currently missing\n"); diff --git a/nyx/state/state.c b/nyx/state/state.c index 9a2c827f01..9dde7c97f1 100644 --- a/nyx/state/state.c +++ b/nyx/state/state.c @@ -107,6 +107,9 @@ void state_init_global(void){ global_state.pt_trace_mode_force = false; global_state.num_dirty_pages = 0; + + global_state.get_host_config_done = false; + global_state.set_agent_config_done = false; global_state.sharedir = sharedir_new(); diff --git a/nyx/state/state.h b/nyx/state/state.h index 3ed5645a9f..7a9367f481 100644 --- a/nyx/state/state.h +++ b/nyx/state/state.h @@ -139,6 +139,9 @@ typedef struct qemu_nyx_state_s{ uint32_t num_dirty_pages; + bool get_host_config_done; + bool set_agent_config_done; + /* capabilites */ uint8_t cap_timeout_detection; uint8_t cap_only_reload_mode;