VivienneVMM is a stealthy debugging framework implemented via an Intel VT-x hypervisor. The VMM driver implements multiple breakpoint control managers which allow a user mode client to set, clear, and inspect the logs of VMM-backed breakpoints. These breakpoints are invisible to the guest.
This project is an extension of the HyperPlatform framework by tandasat.
VivienneVMM 1.0.0 released. 🎂
- Ept breakpoints: a new breakpoint type with many advantages over the now deprecated Vivienne hardware breakpoint type.
- New VivienneCL commands: GetProcessInformation, QueryEptBpInfo, SetEptBpBasic, SetEptBpRegisters, SetEptBpKeyed, DisableEptBp, ClearEptBp, PrintEptBpLogHeader, and PrintEptBpLogElements. See the VivienneCL README for more information.
- Add extended information for many VivienneCL commands. Type 'help command_name' in the VivienneCL console to see a command's extended information.
- Most VivienneCL commands now have an additional short name for ease of use.
- New config options: CFG_VIVIENNE_DEVICE_NAME, CFG_LOG_NUMBER_OF_PAGES, CFG_LOG_FLUSH_INTERVAL_MS, and CFG_LOG_APPEND_DATA_TO_EXISTING_LOG_FILE. See the config header file for more information.
- Add a CHANGELOG file.
The VivienneVMM driver contains two breakpoint control manager modules which implement their own style of breakpoint:
- Implements ept breakpoints using extended page tables.
- + Ept breakpoints are undetectable by user mode processes in the guest.
- + Supports unlimited number of ept breakpoints.
- - Only execution and data breakpoints are supported.
- ~ Data breakpoint events are processed as faults instead of traps.
- Implements hardware breakpoints by hooking the debug exception IDT vector and monitoring debug register accesses.
- + Faster than ept breakpoints.
- - Susceptible to certain anti-debug techniques by user mode processes in the guest.
The ept breakpoint manager module implements ept breakpoints using Intel VT-x extended page tables (ept) and the monitor trap flag VM execution control.
Ept breakpoints are effectively hardware breakpoints with the following differences:
- Each processor can have an unlimited number of active ept breakpoints.
- Only execution and data (read / write) breakpoints are supported.
- Data breakpoint events are processed as faults instead of traps. i.e., When a data ept breakpoint condition is triggered the VMM logs the event before the read or write occurs in the guest.
- Ept breakpoints cannot be detected by user mode processes in the guest. (See 'Warnings and Limitations')
Each ept breakpoint has a corresponding ept breakpoint log.
An ept breakpoint log contains the "breakpoint hit history" for its corresponding ept breakpoint. The contents of the log depend on the ept breakpoint log type:
Records each unique guest virtual address that triggers the breakpoint condition and its hit count. i.e., Traditional hardware breakpoint functionality. Can be used to discover addresses which read, write, and execute a target breakpoint address.
Records the general purpose register context each time the guest triggers the breakpoint condition.
Similar to General Register Context, except that guest state is only recorded when the value in the key register is not already in the log.
VivienneCL contains several commands for setting, disabling, clearing, and viewing the logs of ept breakpoints. The SetEptBp* commands install an ept breakpoint on all processors and return a log handle. This log handle is the primary input argument for the other ept breakpoint commands.
The following example uses ept breakpoint commands to recover decrypted data from a target process.
Given the following function, we want to know all possible values of the DecryptedValue variable:
VOID
EptBreakpointExample(
_In_ ULONG_PTR EncryptedValue
)
{
ULONG_PTR DecryptedValue = 0;
DecryptedValue = Decrypt(EncryptedValue);
// ...
}
Disassembly:
;
; ImageBase = 0x140000000
;
.text:0000000140001030 mov [rsp+EncryptedValue], rcx
.text:0000000140001035 sub rsp, 38h
.text:0000000140001039 mov [rsp+38h+var_18], 0
.text:0000000140001042 mov rcx, [rsp+38h+EncryptedValue] ; EncryptedValue
.text:0000000140001047 call Decrypt(unsigned __int64)
.text:0000000140001047
.text:0000000140001047 ; rax contains the decrypted value
.text:0000000140001047
.text:000000014000104C mov [rsp+38h+var_18], rax
; ...
Suppose that process "Z" is executing this function randomly. Assume that the process id of process Z is 5600 and the image base is 0x140000000. We can use the SetEptBpKeyed command to log guest state whenever a unique value is returned from the Decrypt routine at 000000014000104C:
Set the ept breakpoint with rax as the register key:
> SetEptBpKeyed 5600 e1 14000104C 10000 1 rax
Log Handle: 1
Wait a few seconds for process Z to execute the function then print the log elements.
> PrintEptBpLogElements 1
EPTBP Log #1 | PID: 5600, Address: 000000014000104C, BP: X , Status: Active, KeyedRegister RAX
NumberOfElements: 4, MaxIndex: 409
Elements:
0 rip: 000000014000104C flg: 0000000000010202
rax: 138582AFE1835301 rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFFF7F rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
1 rip: 000000014000104C flg: 0000000000010202
rax: FFF83815FA800890 rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFB123 rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
2 rip: 000000014000104C flg: 0000000000010202
rax: FFFFFFFFFFFFFFFF rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFEDED rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
3 rip: 000000014000104C flg: 0000000000010202
rax: 00103350308F80CD rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFDB89 rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
The log output above contains a breakpoint header and an array of keyed register context log elements. Each element corresponds to an instance where process Z executed the instruction at 000000014000104C with a unique value in the rax register.
From this log, we can see that process Z used four unique decrypted values while our breakpoint was active: 138582AFE1835301, FFF83815FA800890, FFFFFFFFFFFFFFFF, and 00103350308F80CD.
Now suppose that we are finished monitoring this function, but we want to continue debugging process Z. We can use the DisableEptBp command to uninstall the breakpoint while keeping the breakpoint log valid:
> DisableEptBp 1
If we print the log elements again then we see that the breakpoint is inactive:
> PrintEptBpLogElements 1
EPTBP Log #1 | PID: 5600, Address: 000000014000104C, BP: X , Status: Inactive, KeyedRegister RAX
NumberOfElements: 4, MaxIndex: 409
Elements:
0 rip: 000000014000104C flg: 0000000000010202
rax: 138582AFE1835301 rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFFF7F rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
1 rip: 000000014000104C flg: 0000000000010202
rax: FFF83815FA800890 rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFB123 rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
2 rip: 000000014000104C flg: 0000000000010202
rax: FFFFFFFFFFFFFFFF rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFEDED rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
3 rip: 000000014000104C flg: 0000000000010202
rax: 00103350308F80CD rbx: 000007FEF03369F8 rbp: 0000000000000000 rsp: 000000000012FE70
rcx: FFFFFFFFFFFFDB89 rdx: 0000000000000000 r8: 000007FFFFFDE000 r9: 000007FEF0303BD0
rdi: 0000000000368120 rsi: 0000000000000000 r10: B0003A0D060A5F66 r11: 00003A0D067C9178
r12: 0000000000000000 r13: 0000000000000000 r14: 0000000000000000 r15: 0000000000000000
Ept breakpoints can have a significant performance cost because an ept violation VM exit occurs whenever the guest accesses the target page in a manner which matches the ept breakpoint condition. e.g., If we set an execute ept breakpoint on a system call stub in ntdll.dll then the guest will VM exit each time a processor executes an instruction inside the page that contains the breakpointed address, regardless of process context. Disabling an ept breakpoint removes its performance cost.
We can use the QueryEptBpInfo command to get a list of all ept breakpoints on the system:
> QueryEptBpInfo
Ept Breakpoint Information
0 active breakpoints.
1 inactive breakpoints.
0 locked pages.
0 hooked pages.
Breakpoints:
Handle PID Address BP Status LogType #Elements MaxIndex
---------------------------------------------------------------------------------------
1 5600 000000014000104C X Inactive KeyedRegister 4 409
Finally, we can release the resources for an ept breakpoint log using the ClearEptBp command:
> ClearEptBp 1
See the VivienneCL README for more command information.
- Currently only user mode clients are supported. Kernel support is in development.
- Unhandled edge cases may reveal the presence of ept breakpoints to user mode processes in the guest. If this occurs then please create an issue on github so that we can fix it.
The hardware breakpoint manager is deprecated since the release of the ept breakpoint manager. Developers can enable the hardware breakpoint manager by modifying the config header.
Legacy documentation can be found here.
The core driver project containing the Vivienne virtual machine monitor.
A command line VivienneVMM client which makes use of the breakpoint control interfaces. A simple debugger.
VivienneVMM test cases.
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 master
git remote add upstream https://github.com/tandasat/HyperPlatform.git
git remote set-url --push upstream DISABLED
# Pulling updates.
cd VivienneVMM/
git fetch upstream
git subtree pull --prefix=VivienneVMM/HyperPlatform upstream master
- HyperPlatform
- https://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.
DdiMon is a hypervisor performing inline hooking that is invisible to a guest (ie, any code other than DdiMon) by using extended page table (EPT).
- 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.