VivienneVMM 是一个通过英特尔VT-x虚拟机管理程序实现的隐形调试框架

网友投稿 1156 2022-10-12

VivienneVMM 是一个通过英特尔VT-x虚拟机管理程序实现的隐形调试框架

VivienneVMM 是一个通过英特尔VT-x虚拟机管理程序实现的隐形调试框架

VivienneVMM

VivienneVMM is a stealthy debugging framework implemented via an Intel VT-x hypervisor. The driver exposes a hardware breakpoint control interface which allows a user mode client to set and clear breakpoints. These breakpoints are invisible to the guest.

This project is an extension of the HyperPlatform framework by tandasat.

Projects

VivienneVMM

The core driver project containing the Vivienne Virtual Machine Monitor.

VivienneCL

A command line VivienneVMM client which makes use of the hardware breakpoint control interface. A simple debugger.

Tests

VivienneVMM test cases.

Implementation

Breakpoint Manager

The breakpoint manager (BPM) modifies processor debug registers to install and uninstall hardware breakpoints. Each modification is performed synchronously on all logical processors using the following protocol:

The client requests a breakpoint change via an IOCTL.BPM receives the client request and validates the input parameters.BPM issues an IPI broadcast so that all processors synchronously execute a vmcall.Each processor modifies its debug registers in VMX root mode so that a MovDr event does not occur.

Processors generate a debug exception when the execution of an instruction satisfies a hardware breakpoint condition. The breakpoint manager monitors these exceptions via a debug exception VM exit handler, and consumes exceptions caused by BPM-managed breakpoints.

Users can customize breakpoint behavior by registering a callback during breakpoint installation. Callbacks are executed in VMX root mode, in the context of the target process, whenever the breakpoint condition is met. Capture execution context is an example callback which acts as an execution hook to read register state.

Debug Register Facade

The debug register facade prevents the guest from accessing processor debug registers via a MovDr VM exit handler. This handler emulates debug register access instructions using a set of processor-specific, 'fake' debug registers.

Capture Execution Context

The capture execution context (CEC) module implements two breakpoint callbacks: capture execution context register (CECR) and capture execution context memory (CECM).

Capture Execution Context Register (CECR)

This callback samples the contents of a target register and records unique values to a user-supplied buffer.

Example

For this example, we must reverse engineer the 'player' data structure(s) of a multiplayer FPS game. Our goal is to be able to reliably obtain all player pointers for an in game 'round' from our out-of-process client.

We have identified the following function in the game's code which constantly iterates a list of player pointers:

VOIDProcessGameTick( _In_ ULONG NumberOfPlayers){ for (ULONG i = 0; i < NumberOfPlayers; ++i) { PVOID pPlayer = GetDecryptedPlayerPointer(i); SimulatePlayerTick(pPlayer); }}

Disassembly of the for loop:

.text:0000000140001032 loc_140001032:.text:0000000140001032 mov eax, [rsp+38h+PlayerIndex].text:0000000140001036 inc eax.text:0000000140001038 mov [rsp+38h+PlayerIndex], eax.text:000000014000103C.text:000000014000103C loc_14000103C:.text:000000014000103C mov eax, [rsp+38h+arg_0].text:0000000140001040 cmp [rsp+38h+PlayerIndex], eax.text:0000000140001044 jnb short loc_140001060.text:0000000140001046 mov ecx, [rsp+38h+PlayerIndex].text:000000014000104A call GetDecryptedPlayerPointer.text:000000014000104F mov [rsp+38h+pPlayer], rax ; target..text:0000000140001054 mov rcx, [rsp+38h+pPlayer].text:0000000140001059 call SimulatePlayerTick.text:000000014000105E jmp short loc_140001032

We determine that we can calculate player pointers if we are able to recreate the GetDecryptedPlayerPointer function. Now, imagine that this function is heavily obfuscated to the point that it would require an unrealistic amount of time to reverse. Normally we would dismiss this code snippet because the reward is not worth the time investment.

We can accomplish our goal without reversing GetDecryptedPlayerPointer by executing a capture register values (CECR) IOCTL for register RAX at address 0x14000104F. The CEC module satisfies this CECR request by installing an ephemeral hardware breakpoint at 0x14000104F for the target process. When the target process executes the instruction at this address, a debug exception is generated and processed by BPM. BPM then invokes the CECR callback which records every unique value in the RAX register to the IOCTL buffer. The unique values buffer is returned to the client when the breakpoint duration expires.

The following snippet is the client code for this example's CECR request:

UCHAR pBuffer[PAGE_SIZE] = {};PCEC_REGISTER_VALUES pRegisterValues = (PCEC_REGISTER_VALUES)pBuffer;BOOL status = TRUE;status = DrvCaptureRegisterValues( GetTargetProcessId(), 0, (ULONG_PTR)0x14000104F, HWBP_TYPE::Execute, HWBP_SIZE::Byte, REGISTER_RAX, 5000, pRegisterValues, sizeof(pBuffer));

Capture Execution Context Memory (CECM)

This callback samples the memory-data-typed contents of a memory address defined by a memory expression.

A memory expression can either be an absolute, virtual address or an indirect address in x64 addressing-mode form. The following are examples of valid memory expressions and their evaluations:

14000 ; effective address = [14000]rax ; effective address = [rax]rcx+rax-20 ; effective address = [rcx+rax-20]rcx+rax*8 ; effective address = [rcx+rax*8]rcx+rax*8-20 ; effective address = [rcx+rax*8-20]

For each callback invocation, the effective address is calculated, validated, and interpreted as a pointer to one of the following user-specified types:

byteworddwordqwordfloatdouble

The value obtained by dereferencing the effective pointer is the value recorded in the unique values buffer.

Example

For this example, we want to sample float values stored in xmm0 in the following instruction:

.text:0000000140001066 movss dword ptr [rsp+rax*4+30h], xmm0.text:000000014000106C ret

We can easily capture these values using the following CECM request:

UCHAR pBuffer[PAGE_SIZE] = {};CEC_MEMORY_DESCRIPTION MemoryDescription = {};PCEC_MEMORY_VALUES pMemoryValues = NULL;PMEMORY_DATA_VALUE pMemoryDataValue = NULL;BOOL status = TRUE;// Translate the memory expression and data type into a memory description.status = ParseMemoryDescriptionToken( "rsp+rax*4+30", MDT_FLOAT, &MemoryDescription);if (!status){ goto exit;}// Issue the CECM request.status = DrvCaptureMemoryValues( GetTargetProcessId(), 0, (ULONG_PTR)0x14000106C, HWBP_TYPE::Execute, HWBP_SIZE::Byte, &MemoryDescription, 5000, pMemoryValues, sizeof(pBuffer));if (!status){ goto exit;}// Log the results.for (ULONG i = 0; i < pValuesCtx->NumberOfValues; ++i){ pMemoryDataValue = (PMEMORY_DATA_VALUE)&pValuesCtx->Values[i]; printf(" %u: %f\n", i, pMemoryDataValue->Float);}

See the CECM command description in the VivienneCL README for more information.

Limitations

When the debug register facade is active, hardware breakpoints installed by the guest (into the fake debug registers) cannot be triggered. A process can detect this side effect by installing a breakpoint, triggering the breakpoint condition, and then waiting for a single-step exception that will never be delivered.

EPT Hooking Comparison

EPT hooking is a viable alternative for implementing stealth debugging which does not have the limitation(s) described above. The trade-offs are:

Maintaining EPT page mappings is arguably more complex than debug register bookkeeping.EPT hooks are slower than hardware breakpoint hooks because EPT hooks have a page-sized trigger range versus the byte, word, dword, and qword trigger range of hardware breakpoints.The target machine must use a processor that supports EPT.

Project Structure

This project uses HyperPlatform as a git subtree with prefix='VivienneVMM/HyperPlatform'. We subtree the project instead of using a git submodule because we must modify HyperPlatform files to implement VivienneVMM features. This allows us to merge HyperPlatform updates from upstream with minimal merge conflicts.

The following list of console commands are an example of how to pull HyperPlatform updates into a local VivienneVMM repository:

# Remote configuration.cd VivienneVMM/git checkout mastergit remote add upstream https://github.com/tandasat/HyperPlatform.gitgit remote set-url --push upstream DISABLED# Pulling updates.cd VivienneVMM/git fetch upstreamgit subtree pull --prefix=VivienneVMM/HyperPlatform upstream master

Related Project(s)

HyperPlatformhttps://github.com/tandasat/HyperPlatform

HyperPlatform is an Intel VT-x based hypervisor (a.k.a. virtual machine monitor) aiming to provide a thin platform for research on Windows. HyperPlatform is capable of monitoring a wide range of events, including but not limited to, access to virtual/physical memory and system registers, occurrences of interrupts and execution of certain instructions.

Notes

This project was developed and tested on Windows 7 x64 SP1.All binaries are PatchGuard safe on Windows 7.x86 is not supported and WoW64 is untested.Hardware breakpoints are restricted to user space addresses (for now).

Special Thanks

tandasatdude719

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:VSR公网对接strongSwan NAT穿越
下一篇:VPP创建接口
相关文章

 发表评论

暂时没有评论,来抢沙发吧~