A boot-time interposer, not a replacement hypervisor.
HV2 establishes a small control path inside an existing virtualization stack, then exposes a compact legacy set of memory and translation operations to a trusted local research client.
HV2 is a private boot-time hypervisor interposition platform for controlled memory and execution-state research. This article separates what exists in the legacy implementation from what still needs hardening, measurement, and compatibility work, without publishing source code, signatures, offsets, or deployment instructions.
The project studies what becomes possible when memory introspection is performed below the guest operating system instead of through a driver, debugger, or in-guest agent. The goal is not to publish an operational tool. The goal is to document a disciplined private platform for controlled lab research.
HV2 establishes a small control path inside an existing virtualization stack, then exposes a compact legacy set of memory and translation operations to a trusted local research client.
Virtualization-backed isolation means kernel-level tooling is not always the lowest useful vantage point. HV2 studies memory from a layer where guest state can be observed independently.
We do not publish source code, byte signatures, structure offsets, build-specific internals, installation paths, or bypass procedures. The public value is architectural documentation and research methodology.
User-mode tooling is constrained by process boundaries. Kernel-mode tooling is powerful, but still shares the operating system's trust model, patching rules, and compromise surface. HV2 moves the observation point below that layer.
| Research position | What it can see | Primary limitation |
|---|---|---|
| User mode | Public APIs, process-visible memory, debugger-approved state. | Subject to handles, access checks, anti-debug logic, and user-mode tampering. |
| Kernel mode | Most operating-system structures, page tables, driver-visible memory. | Still inside the OS trust boundary and vulnerable to kernel-level concealment. |
| Hypervisor layer | Guest state, nested translation, physical backing behavior, VM-exit context. | Harder to initialize, debug, validate, and keep compatible across releases. |
The design depends on preserving normal behavior. HV2 recognizes a narrow command path and delegates unrelated VM exits back to the platform's original handler. Recognition is routing, not authentication; the current trust model is a single-owner, controlled lab.
HV2 uses that hardware-defined transition as a controlled command transport. Once in the hypervisor-side context, it can examine guest state and translation metadata that ordinary user-mode code cannot authoritatively access.
The payload checks the exit class and a private command marker before dispatch. If the exit is unrelated, it is forwarded to the original platform path. The marker prevents accidental routing; it does not establish caller identity or authorization.
The implementation needs the guest instruction pointer, address-space root, register context, exit metadata, and nested translation root. The way this state is recovered differs by backend.
HV2 writes the return status, advances guest execution, and avoids disturbing unrelated register or hypervisor state. This is where many unstable prototypes fail.
The legacy contract contains initialization, address-space discovery, translation, physical read/write, and virtual copy. Compactness helps auditing, but these operations remain privileged and require explicit bounds, caller trust, and negative-path validation.
The phrase "read memory" hides the real engineering work. On a virtualized x64 system, a guest virtual address must first be resolved through guest page tables, then the resulting guest physical address must be resolved through second-level translation. Correctness depends on preserving which stage failed instead of collapsing every failure into a zero address.
VA
VA -> GPA
4K / 2M / 1G
GPA -> HPA
copy
The names below are publication-safe labels, not source identifiers. A compact operation inventory is easier to audit, but size alone does not make a privileged interface safe: framing, bounds, authorization, and error semantics matter just as much.
INIT_MAP
Prepares processor-local temporary mapping state and validates the mapping path.
QUERY_ROOT
Returns the current guest address-space root for later translation work.
TRANSLATE_VA
Resolves a guest virtual address into a guest physical address under a chosen root.
READ_GPA
Copies guest physical memory into a guest-visible destination after both translation stages.
WRITE_GPA
Copies from a guest-visible source into guest physical memory; policy bounds must sit above the legacy primitive.
COPY_VA
Copies between two virtual address spaces using explicit address-space roots and page-aware chunking.
These examples are implementation-neutral hardening contracts derived from the source audit. Where the legacy path does not yet enforce an invariant consistently, the text says so. Exact instruction sequences, signatures, structure offsets, constants, and deployment details remain private.
The legacy command union is small, but the current path trusts more caller input than a hardened interface should. Version, frame size, transfer cap, checked arithmetic, and a complete-frame boundary check belong in the contract.
// target contract, not private source struct CommandFrame { version: ProtocolVersion frame_bytes: BoundedLength operation: Operation rights: CapabilityMask source: AddressDescriptor destination: AddressDescriptor transfer_bytes: BoundedLength status: HV2Status } // invariants missing from a marker-only protocol validate(frame): require complete_frame_is_mapped(frame) require supported(frame.version, frame.operation) require frame.transfer_bytes <= policy.max_transfer require checked_address_ranges(frame) require authorize(frame.rights, frame.operation)
HV2 is not a general replacement for the platform handler. The current implementation correctly forwards unrelated exits. A hardened revision must additionally separate command recognition from authorization and return an explicit error for unsupported operations.
// target control flow, not private source function on_vmexit(raw_context): exit = backend.read_exit(raw_context) if not is_recognized_command(exit, raw_context): return original_handler(raw_context) frame = backend.read_command_frame(raw_context) status = validate_authorize_dispatch(frame) backend.write_return(raw_context, status) backend.advance_guest_ip(raw_context) return resume_guest
The library pins one thread across processor groups so each logical processor installs its own temporary mapping entries. The audit also exposes two hardening needs: affinity must be restored on every failure path, and mapping-slot selection must be validated against modern topology identifiers.
// target user-mode orchestration function initialize_all_processors(): original_affinity = current_thread_affinity() try: for cpu in enumerated_logical_processors(): require pin_current_thread(cpu) require mapping_slot_is_valid(cpu) require hv2_call(INIT_MAP) == OK return OK finally: restore_affinity(original_affinity)
The legacy walker handles standard and large-page paths. The hardened contract must also validate canonical input, presence, reserved-bit constraints, requested permissions, and the physical-width assumptions used to reconstruct each result.
// target page walk, simplified function translate_guest_virtual(root, va): require canonical(va) idx = split_virtual_address(va) pml4e = read_entry(root, idx.pml4) if not pml4e.present: return PML4E_NOT_PRESENT pdpte = read_entry(pml4e.page, idx.pdpt) if not pdpte.present: return PDPTE_NOT_PRESENT if pdpte.large and entry_is_valid(pdpte): return pdpte.base + idx.offset_1g pde = read_entry(pdpte.page, idx.pd) if not pde.present: return PDE_NOT_PRESENT if pde.large and entry_is_valid(pde): return pde.base + idx.offset_2m pte = read_entry(pde.page, idx.pt) if not pte.present: return PTE_NOT_PRESENT require entry_is_valid_for_access(pte) return GPA(pte.base + idx.offset_4k)
In a virtualized system, guest physical is not the final backing address. HV2 resolves the second-stage mapping before creating a temporary hypervisor-side mapping. Each nested level needs the same presence, large-page, physical-width, and permission discipline as the guest walk.
// conceptual nested translation path function map_guest_physical(gpa, access): nroot = backend.read_nested_root() hpa = walk_nested_tables(nroot, gpa) if hpa is invalid: return INVALID_GUEST_PHYSICAL slot = choose_per_core_mapping_slot(access) map_slot(slot, hpa.page_base) invalidate_local_translation(slot) return slot.virtual_base + gpa.page_offset
Real ranges cross page boundaries. The implementation correctly limits each iteration to the remaining bytes in both mapped pages. That does not impose a total request bound; the hardened entry contract must do that before the loop starts.
// target page-safe copy loop function copy_guest_range(src_root, src_va, dst_root, dst_va, size): require size <= policy.max_transfer require ranges_do_not_overflow(src_va, dst_va, size) while size > 0: src_gpa = translate_guest_virtual(src_root, src_va) dst_gpa = translate_guest_virtual(dst_root, dst_va) src = map_guest_physical(src_gpa, READ) dst = map_guest_physical(dst_gpa, WRITE) chunk = min( bytes_left_in_page(src_va), bytes_left_in_page(dst_va), size ) copy_bytes(dst, src, chunk) src_va += chunk dst_va += chunk size -= chunk return OK
The two legacy backend trees duplicate parts of dispatch and memory logic. Their intended behavior is similar, but duplication allows fixes to land in one path only. A shared semantic test suite is the immediate requirement; a common implementation layer is the longer-term simplification.
// target backend boundary interface HV2Backend { read_exit(context) -> ExitInfo read_guest_root(context) -> AddressSpaceRoot read_guest_ip(context) -> GuestIP read_nested_root(context) -> NestedRoot read_command_frame(context) -> HV2Command write_return(context, status) advance_guest_ip(context) } // shared semantics; private state recovery stays behind the boundary dispatch(frame, backend): return operation_table[frame.op](frame, backend)
The legacy code defines granular status values, but some translation paths still collapse failure into a zero address or report success around an invalid result. The research interface needs tagged results and consistent propagation across both backends.
// target status taxonomy enum HV2Status { OK, PML4E_NOT_PRESENT, PDPTE_NOT_PRESENT, PDE_NOT_PRESENT, PTE_NOT_PRESENT, INVALID_GUEST_VIRTUAL, INVALID_GUEST_PHYSICAL, MAPPING_NOT_INITIALIZED, UNSUPPORTED_OPERATION } // invariant: fail closed, return evidence if translation_failed: frame.status = precise_failure do_not_copy()
The following examples are deliberately conceptual. They document the research workflow and threat-model value without publishing code paths that directly reproduce HV2.
A lab tool compares the guest OS view of a process or kernel object against the memory view resolved by HV2. Differences can reveal hiding, unlinking, stale structures, or tampering.
# conceptual request, not implementation code request = { operation: "translate_and_sample", address_space: observed_root, region: suspect_object_range } compare( guest_reported_state, hv2_observed_state )
Instead of asking the OS to copy between two processes, HV2 treats each side as an explicit page-table root and performs translation on both source and destination.
# conceptual request, not implementation code copy = { source_root: root_A, source_va: range_A, dest_root: root_B, dest_va: range_B, bounds: page_limited }
In an authorized lab, researchers can compare whether defensive tools detect memory changes that are visible from below the guest OS boundary.
# conceptual lab protocol baseline = hv2.snapshot(region) exercise = authorized_test_event() after = hv2.snapshot(region) report( memory_delta, defender_observation )
HV2 gives researchers a way to study how memory-integrity features, isolated secrets, and virtualization-backed policies change boot timing and page visibility.
# conceptual observation model for mode in security_modes: boot_lab(mode) observe( initialization_order, page_visibility, translation_result )
HV2 contains paths for two major x64 virtualization families. We refer to them as [Architecture-A] and [Architecture-B] to keep the article focused on methodology instead of vendor-specific replication details. They target one semantic command model, but duplicated legacy code means parity must be demonstrated by shared tests rather than inferred from similar function names.
In defensive research, an independent vantage point matters. HV2 can help compare guest-reported state against memory and translation state observed below the guest OS, inside an authorized lab environment.
Acquire the same declared region through an in-guest collector and HV2, attach address-space and translation provenance, then explain each delta. The experiment tests collector trust without assuming every difference is malicious.
Apply a predeclared, authorized memory change in an isolated guest; record the lower-layer delta and the endpoint tool's telemetry. The useful result is a coverage boundary, not a claim that one missed event defeats a product.
Compare guest-reported objects, page-table reachability, and physical remnants at controlled checkpoints. HV2 supplies an independent acquisition path; behavioral interpretation still belongs to the analysis workflow.
Capture before-and-after mappings, target regions, and crash-adjacent state during authorized exploit research. This makes postcondition analysis less dependent on a guest that may already be unstable.
Run a controlled matrix of boot policy, memory-integrity, and isolation modes; record activation, visibility, and timing outcomes. The study maps compatibility and trust boundaries without publishing bypass procedures.
These findings come from read-only inspection of the legacy implementation and its modernization plan. They are engineering evidence, not a claim that every planned target boots today. No latency, throughput, or long-run stability result is published because the repository does not contain a representative benchmark run.
| Finding | Why it matters | Status |
|---|---|---|
| Unrelated exits are forwarded | The interposer preserves the platform handler for traffic outside the recognized command path. | Observed in both legacy backends |
| The operation inventory is compact | Six legacy operations cover mapping setup, root discovery, translation, read, write, and cross-space copy. | Implemented; envelope hardening needed |
| Translation is explicitly composed | Guest virtual translation and nested guest-physical translation are separate walkers with large-page paths. | Implemented; malformed-entry corpus needed |
| Copies are page-aware | Each loop iteration is limited by both source and destination page boundaries. | Implemented; total transfer cap needed |
| Mapping state is processor-local | The client visits logical processors and initializes temporary mapping entries for each execution context. | Implemented; topology and cleanup paths need tests |
| Status propagation is incomplete | A useful error taxonomy exists, but some paths still collapse translation failure into a zero result or weak success signal. | Source-audit gap |
| Compatibility assumptions are build-sensitive | Early-boot integration and backend state recovery depend on target layouts that can drift between releases. | Legacy support present; newer targets are roadmap |
| No performance baseline exists yet | VM-exit overhead, cold/warm translation cost, copy throughput, and long-run stability remain unmeasured. | No benchmark claim |
The project is unusual, but its engineering lessons are not. They apply to debuggers, firmware agents, kernel instrumentation, device-control planes, and other software that executes beneath an ordinary application trust boundary.
An interposer should handle only traffic it can classify and validate. Unknown exits, operations, versions, and states need an explicit path, never an accidental fallthrough.
A final address is not enough. Record the guest root, page size, nested root generation, permissions, and the stage that failed so later analysis can distinguish data from acquisition error.
Mapping one page and copying an arbitrary range are different operations. Frame containment, checked range arithmetic, per-page chunking, and a total transfer cap solve different failure classes.
Initialization, affinity restoration, identifier width, processor groups, and migration behavior need tests on the machines the platform claims to support.
Source code can prove that a path exists. It cannot prove current compatibility, acceptable tail latency, or long-run stability. Those require reproducible test artifacts.
The repository defines a validation direction rather than a completed evidence set. A credible program moves from offline compatibility analysis to boot-chain verification, command correctness, adversarial translation cases, stress testing, and security-feature interaction.
Low-level hypervisor work is inherently dual-use. We describe the system at a level useful to expert readers while withholding replication details that would turn the article into an operational guide.
The legacy implementation establishes the mechanism and exposes its assumptions; it does not by itself prove current compatibility or production readiness. The next research phase focuses on runtime discovery, protocol hardening, backend parity tests, safer compatibility handling, and benchmark instrumentation.
portability
resilience
iteration speed
evidence
security research
correctness
Tell us what you are solving. We will come back with a concrete next step.
Contact Gloryck