structEntry { key_t key; Value value; bool used; };
// capacity must be power-of-two for simpler mod hvpp_hashmap_t(size_t capacity = 1024, ULONG PoolTag = 'kHpV') noexcept : m_capacity(capacity), m_pool_tag(PoolTag) { if ((m_capacity & (m_capacity - 1)) != 0) { size_t v = 1; while (v < m_capacity) v <<= 1; m_capacity = v; }
size_t bytes = sizeof(Entry) * m_capacity; m_entries = static_cast<Entry*>(ExAllocatePoolWithTag(NonPagedPoolNx, bytes, m_pool_tag)); if (m_entries) { RtlZeroMemory(m_entries, bytes); for (size_t i = 0; i < m_capacity; ++i) { m_entries[i].used = false; m_entries[i].key = 0; } } }
// insert or update; returns true on success boolinsert(key_t key, const Value& value)noexcept { if (!m_entries) returnfalse; size_t idx = hash_key(key) & (m_capacity - 1); for (size_t i = 0; i < m_capacity; ++i) { size_t j = (idx + i) & (m_capacity - 1); if (!m_entries[j].used) { m_entries[j].used = true; m_entries[j].key = key; m_entries[j].value = value; returntrue; } if (m_entries[j].used && m_entries[j].key == key) { // update existing m_entries[j].value = value; returntrue; } } returnfalse; // table full }
// find pointer to value or nullptr Value* find(key_t key)noexcept { if (!m_entries) returnnullptr; size_t idx = hash_key(key) & (m_capacity - 1); for (size_t i = 0; i < m_capacity; ++i) { size_t j = (idx + i) & (m_capacity - 1); if (!m_entries[j].used) returnnullptr; if (m_entries[j].used && m_entries[j].key == key) { return &m_entries[j].value; } } returnnullptr; }
// alternatively use [] to find or insert default inline Value& operator[](constkey_t& key) noexcept { Value* it = this->find(key); if (it) return *it;
Value default_value{}; bool ok = this->insert(key, default_value); if (!ok) { return *it; // insertion failed, return nullptr } // find must now succeed Value* it2 = this->find(key); hvpp_assert(it2); return *it2; }
// erase key, return true if existed boolerase(key_t key)noexcept { if (!m_entries) returnfalse; size_t idx = hash_key(key) & (m_capacity - 1); for (size_t i = 0; i < m_capacity; ++i) { size_t j = (idx + i) & (m_capacity - 1); if (!m_entries[j].used) returnfalse; if (m_entries[j].used && m_entries[j].key == key) { // remove and re-insert cluster following deletion to keep probe chain consistent m_entries[j].used = false; m_entries[j].key = nullptr; // reinsert cluster size_t k = (j + 1) & (m_capacity - 1); while (m_entries[k].used) { Entry tmp = m_entries[k]; m_entries[k].used = false; m_entries[k].key = nullptr; insert(tmp.key, tmp.value); k = (k + 1) & (m_capacity - 1); } returntrue; } } returnfalse; }
private: staticinlinesize_thash_key(key_t k)noexcept { // simple pointer hash uintptr_t v = static_cast<uintptr_t>(k); // mix bits (64->32 mix) v ^= (v >> 33); v *= 0xff51afd7ed558ccdULL; v ^= (v >> 33); v *= 0xc4ceb9fe1a85ec53ULL; v ^= (v >> 33); returnstatic_cast<size_t>(v); }
classept_hook_info { // // Contains information about a single EPT hook: // - original_page: The original page that is being hooked. // - read_page: The page to use when read access is attempted. // - write_page: The page to use when write access is attempted. // - execute_page: The page to use when execute access is attempted. // public: pa_t original_page; pa_t read_page; pa_t write_page; pa_t execute_page; bool enabled;
auto original_page = pa_t::from_va(vp.context().rdx_as_pointer); auto entry = data.ept_hooks[original_page.value()];
hvpp_trace("vmcall (EPT unhook)");
// // Merge the 4kb pages back to the original 2MB large page. // Note that this will also automatically set the access // rights to read_write_execute. //
// // We've changed EPT structure - mappings derived from EPT // need to be invalidated. // vmx::invept_single_context(vp.ept().ept_pointer()); break; }
voidvmexit_custom_handler::handle_ept_violation(vcpu_t& vp)noexcept { auto exit_qualification = vp.exit_qualification().ept_violation; auto guest_pa = vp.exit_guest_physical_address(); auto guest_va = vp.exit_guest_linear_address(); auto& data = user_data(vp);
auto guest_pa_aligned = PAGE_ALIGN(guest_pa.value()); auto entry = data.ept_hooks[reinterpret_cast<uint64_t>(guest_pa_aligned)];
if (!entry.enabled) { hvpp_trace("EPT violation on non-hooked page PA: 0x%p", guest_pa.value()); base_type::handle_ept_violation(vp); return; }