├── Common └── WinVisorCommon.h ├── LICENSE ├── README.md ├── WinVisor.sln ├── WinVisor ├── LaunchTargetProcess.cpp ├── LogClient.cpp ├── Main.cpp ├── ParseCommandLine.cpp ├── WinVisor.h └── WinVisor.vcproj ├── WinVisorDLL ├── CreateCpuState.cpp ├── FixModuleImports.cpp ├── HandleVmExit.cpp ├── HypervisorEntryPoint.cpp ├── HypervisorUtils.cpp ├── InterruptHandler_Breakpoint.cpp ├── InterruptHandler_LegacySyscall.cpp ├── InterruptHandler_SingleStep.cpp ├── Interrupts.cpp ├── LogServer.cpp ├── Main.cpp ├── Misc.cpp ├── PageFault.cpp ├── PageTable.cpp ├── PrepareCPL0.cpp ├── PrepareCPL3.cpp ├── SyscallHook_NtTerminateProcess.cpp ├── SyscallHook_NtTerminateThread.cpp ├── SyscallNames.cpp ├── SyscallParamCount.cpp ├── SyscallProxy.cpp ├── WinHvApi.h ├── WinVisorDLL.h └── WinVisorDLL.vcproj ├── winvisor_screenshot.png └── x64 └── release ├── WinVisor.exe └── WinVisorDLL.dll /Common/WinVisorCommon.h: -------------------------------------------------------------------------------- 1 | #define LOG_PIPE_NAME "\\\\.\\pipe\\WinVisorLog" 2 | 3 | #define HOOK_ENTRY_POINT_CODE_SIZE 16 4 | 5 | #define WINVISOR_FLAG_DEBUG_LOG 0x1 6 | #define WINVISOR_FLAG_NX 0x2 7 | #define WINVISOR_FLAG_IMPORTS 0x4 8 | 9 | struct WinVisorStartDataStruct 10 | { 11 | BYTE bOrigEntryPointCode[HOOK_ENTRY_POINT_CODE_SIZE]; 12 | UINT64 qwWinVisorFlags; 13 | IMAGE_NT_HEADERS64 OrigNtHeader; 14 | }; 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 x86matthew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WinVisor 2 | 3 | ## Overview 4 | 5 | In Windows 10 (version RS4), Microsoft introduced the Windows Hypervisor Platform (WHP) API. This API exposes Microsoft's built-in hypervisor functionality to user-mode Windows applications. In 2024, I used this API to create another project: a 16-bit MS-DOS emulator called DOSVisor. This project takes the concept further, and allows Windows x64 executables to be emulated within a virtualized environment. 6 | 7 | The WHP API allows applications to create a virtual CPU, and map virtual memory from the host process directly into the guest's physical memory. The emulator uses this functionality to build a virtual environment which contains everything needed to execute a Windows user-mode process. This involves building up the memory space within the guest, including mapping the target executable and all DLL dependencies, followed by populating other internal data structures such as the `PEB`, `TEB`, `KUSER_SHARED_DATA`, etc. 8 | 9 | Mapping the EXE and DLL dependencies into memory is a simple task, but accurately maintaining internal structures, such as the PEB, is more complex. These structures are large, mostly undocumented, and their contents can vary between Windows versions. Instead of manually building up the memory layout within the virtual environment, WinVisor launches a suspended instance of the target process and clones the entire address space into the guest. The IAT and TLS data directories are temporarily removed from the PE headers in memory to stop DLL dependencies from loading and to prevent TLS callbacks from executing before reaching the entry point. The process is then resumed, allowing the usual process initialization to continue until it reaches the entry point of the target executable, at which point the hypervisor launches and takes control. 10 | 11 | As the WHP API only allows memory from the current process to be mapped into the guest, the main hypervisor logic is encapsulated within a DLL that gets injected into the target process. 12 | 13 | At the present time, the emulator simply forwards all syscalls to the host OS and logs them to the console. However, the project provides a framework to easily facilitate syscall hooks if necessary. 14 | 15 | ## Usage 16 | 17 | WinVisor has some limitations in its current form - the biggest one being that it currently only supports virtualizing a single thread. Other examples are described in further detail in the **Limitations** section below. 18 | 19 | Despite these limitations, it still works well with many executables. It has been tested successfully against built-in Windows executables such as `cmd.exe`, `ping.exe`, and even GUI applications such as `mspaint.exe` and `notepad.exe` (although these only run partially virtualized as described later). 20 | 21 | To launch WinVisor, simply execute the following command: 22 | 23 | `WinVisor.exe ` 24 | 25 | Command-line parameters can also be specified for the target application, for example: 26 | 27 | `WinVisor.exe c:\windows\system32\ping.exe 8.8.8.8` 28 | 29 | If `[ERROR] Failed to initialise Windows Hypervisor Platform API` is displayed, please ensure that `Windows Hypervisor Platform` is installed and enabled in "Windows Features". 30 | 31 | ![cmd.exe running under WinVisor](https://github.com/x86matthew/WinVisor/blob/main/winvisor_screenshot.png?raw=true) 32 | *(screenshot above shows WinVisor emulating `cmd.exe` within a virtualized environment)* 33 | 34 | ## Virtual CPU 35 | 36 | The emulator creates a virtual CPU via WHP to execute the target binary. The virtual CPU operates almost exclusively in CPL3 (user-mode), except for a small bootloader that runs at CPL0 (kernel-mode) to initialize the CPU state before execution. The initialization process involves setting up the following aspects: 37 | 38 | - Control registers (`CR0`, `CR3`, `CR4`, `XCR0`) 39 | - MSRs (`MSR_EFER`, `MSR_LSTAR`, `MSR_STAR`, `MSR_GS_BASE`) 40 | - GDT 41 | - IDT 42 | - TSS 43 | - Initial segment selectors and register values 44 | - Paging table (4-layer) 45 | 46 | Once the initial CPU state has been set up, it switches to CPL3 via a `SYSRET` instruction and begins executing the target application. 47 | 48 | The emulator handles both `SYSCALL` instructions and legacy (`INT 2E`) syscalls. To catch system calls performed via the `SYSCALL` instruction, the `MSR_LSTAR` value is set to a reserved placeholder address. This placeholder address exists in kernel space, ensuring that no conflicts occur with real user-mode memory within the process. When the virtual CPU attempts to execute the `SYSCALL` instruction, a page fault exception is generated, causing a VM exit which indicates to the host that a syscall is pending. 49 | 50 | Legacy interrupt-based syscalls are handled in a very similar way. The IDT is pre-populated with a range of placeholder handler addresses, causing a VM exit when an interrupt occurs. As the placeholder addresses are unique, the host can easily calculate which interrupt type is pending. In the case of legacy syscalls, an internal wrapper is used to proxy these calls to the same handler that is used by the `SYSCALL` instruction, before returning cleanly via `IRETQ`. 51 | 52 | ## Memory Paging 53 | 54 | As mentioned earlier, the emulator creates a child process, and all virtual memory within that process is mapped directly into the guest using the same address layout. A paging table is used to map virtual addresses to the corresponding physical pages. 55 | 56 | Instead of mapping the entire address space of the process upfront, a fixed number of physical pages are allocated for the guest. The emulator contains a very basic memory manager, and pages are mapped "on demand". When a page fault occurs, the requested page will be paged in, and execution resumes. If all page "slots" are full, the oldest entry is swapped out to make room for the new one. 57 | 58 | In addition to using a fixed number of currently-mapped pages, the emulator also uses a fixed-size page table. The size of the page table is determined by calculating the maximum possible number of tables (`PML4`, `PDPT`, `PD`, `PT`) for the amount of mapped page entries. This model results in a simple and consistent physical memory layout but comes at the cost of efficiency. In fact, the paging tables take up more space than the actual page entries. However, as the emulator functions well even with a small number of allocated pages, this level of overhead is not a major concern. 59 | 60 | ## Limitations 61 | 62 | **Single-thread only** 63 | 64 | The emulator currently only supports virtualizing a single thread. If the target executable creates additional threads, they will be executed natively. To support multiple threads, a pseudo-scheduler could be developed to handle this in the future. 65 | 66 | The Windows parallel loader is disabled to ensure all module dependencies are loaded by a single thread. 67 | 68 | **Software exceptions** 69 | 70 | Virtualized software exceptions are not currently supported. If an exception occurs, the system will call the `KiUserExceptionDispatcher` function natively within the target process as usual. 71 | 72 | **Safety issues** 73 | 74 | There are several ways to "escape" the VM, such as simply creating a new process/thread, scheduling APC calls, etc. Windows GUI-related syscalls can also make nested calls directly back into user-mode from the kernel, which would currently bypass the hypervisor layer. For this reason, GUI executables such as `notepad.exe` are only partially virtualized when run under WinVisor at this time. 75 | 76 | **Shared host memory** 77 | 78 | As the WinVisor host DLL is injected into the target process, it exists within the same virtual address space as the target executable in the guest. This means the code running within the virtual CPU is able to directly access the memory within the host hypervisor module, and could potentially corrupt it. 79 | 80 | **Non-executable guest memory** 81 | 82 | While the virtual CPU is set up to support NX, all memory regions are currently mirrored into the guest with full RWX access. 83 | 84 | ## Further Reading 85 | 86 | This project is described in further detail in the following article: https://www.elastic.co/security-labs/winvisor-hypervisor-based-emulator 87 | 88 | During development, I came across a similar project called [Simpleator](https://github.com/ionescu007/Simpleator) by Alex Ionescu. His project also utilizes the WHP API to emulate Windows x64 binaries, but is implemented in a very different way. 89 | -------------------------------------------------------------------------------- /WinVisor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinVisor", "WinVisor\WinVisor.vcproj", "{EB976AD4-A58B-4E10-B351-B4A36537FA5E}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinVisorDLL", "WinVisorDLL\WinVisorDLL.vcproj", "{BF705FB8-4B81-4092-8F99-603FE08884FD}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EB976AD4-A58B-4E10-B351-B4A36537FA5E}.Debug|x64.ActiveCfg = Debug|x64 15 | {EB976AD4-A58B-4E10-B351-B4A36537FA5E}.Debug|x64.Build.0 = Debug|x64 16 | {EB976AD4-A58B-4E10-B351-B4A36537FA5E}.Release|x64.ActiveCfg = Release|x64 17 | {EB976AD4-A58B-4E10-B351-B4A36537FA5E}.Release|x64.Build.0 = Release|x64 18 | {BF705FB8-4B81-4092-8F99-603FE08884FD}.Debug|x64.ActiveCfg = Debug|x64 19 | {BF705FB8-4B81-4092-8F99-603FE08884FD}.Debug|x64.Build.0 = Debug|x64 20 | {BF705FB8-4B81-4092-8F99-603FE08884FD}.Release|x64.ActiveCfg = Release|x64 21 | {BF705FB8-4B81-4092-8F99-603FE08884FD}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /WinVisor/LaunchTargetProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisor.h" 2 | 3 | char gszDllPath[512]; 4 | 5 | DWORD GetDllPath() 6 | { 7 | char szBaseDirectory[512]; 8 | char *pLastSlash = NULL; 9 | 10 | // get full path of current exe 11 | memset(szBaseDirectory, 0, sizeof(szBaseDirectory)); 12 | if(GetModuleFileNameA(NULL, szBaseDirectory, sizeof(szBaseDirectory) - 1) == 0) 13 | { 14 | return 1; 15 | } 16 | 17 | // terminate string at the last slash 18 | pLastSlash = strrchr(szBaseDirectory, '\\'); 19 | if(pLastSlash == NULL) 20 | { 21 | return 1; 22 | } 23 | *pLastSlash = '\0'; 24 | 25 | // append the dll name 26 | memset(gszDllPath, 0, sizeof(gszDllPath)); 27 | _snprintf(gszDllPath, sizeof(gszDllPath) - 1, "%s\\WinVisorDLL.dll", szBaseDirectory); 28 | 29 | return 0; 30 | } 31 | 32 | DWORD GetStartHypervisorExportRVA(DWORD *pdwStartHypervisorExportRVA) 33 | { 34 | HMODULE hWinVisorDLL = NULL; 35 | void *pStartHypervisorExport = NULL; 36 | DWORD dwStartHypervisorExportRVA = 0; 37 | 38 | // temporarily load WinVisor DLL 39 | hWinVisorDLL = LoadLibraryA(gszDllPath); 40 | if(hWinVisorDLL == NULL) 41 | { 42 | printf("Error: Failed to load DLL\n"); 43 | return 1; 44 | } 45 | 46 | // get StartHypervisor function address 47 | pStartHypervisorExport = GetProcAddress(hWinVisorDLL, "StartHypervisor"); 48 | if(pStartHypervisorExport == NULL) 49 | { 50 | FreeLibrary(hWinVisorDLL); 51 | return 1; 52 | } 53 | 54 | // calculate RVA 55 | dwStartHypervisorExportRVA = (DWORD)((UINT64)pStartHypervisorExport - (UINT64)hWinVisorDLL); 56 | 57 | // unload DLL 58 | FreeLibrary(hWinVisorDLL); 59 | 60 | // store RVA 61 | *pdwStartHypervisorExportRVA = dwStartHypervisorExportRVA; 62 | 63 | return 0; 64 | } 65 | 66 | DWORD PatchHypervisorSharedPage(HANDLE hProcess) 67 | { 68 | VOID *pNtQuerySystemInformation = NULL; 69 | VOID *pRemoteNtQuerySystemInformationHook = NULL; 70 | BYTE bNtQuerySystemInformationHookPrefix[] = 71 | { 72 | // cmp rcx, 0xC5 73 | 0x48, 0x81, 0xF9, 0xC5, 0x00, 0x00, 0x00, 74 | // jnz call_original 75 | 0x75, 0x06, 76 | // mov eax, 0xC0000003 77 | 0xB8, 0x03, 0x00, 0x00, 0xC0, 78 | // ret 79 | 0xC3, 80 | // call_original: 81 | // ... 82 | }; 83 | BYTE bJumpToHook[] = 84 | { 85 | // mov rax, 0x0000000000000000 86 | 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | // jmp rax 88 | 0xFF, 0xE0 89 | }; 90 | 91 | // get NtQuerySystemInformation address 92 | pNtQuerySystemInformation = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation"); 93 | if(pNtQuerySystemInformation == NULL) 94 | { 95 | return 1; 96 | } 97 | 98 | // allocate memory for NtQuerySystemInformation hook in remote process 99 | pRemoteNtQuerySystemInformationHook = VirtualAllocEx(hProcess, NULL, sizeof(bNtQuerySystemInformationHookPrefix) + SYSCALL_COPY_BYTE_COUNT, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 100 | if(pRemoteNtQuerySystemInformationHook == NULL) 101 | { 102 | return 1; 103 | } 104 | 105 | // copy hook prefix code 106 | if(WriteProcessMemory(hProcess, pRemoteNtQuerySystemInformationHook, bNtQuerySystemInformationHookPrefix, sizeof(bNtQuerySystemInformationHookPrefix), NULL) == 0) 107 | { 108 | return 1; 109 | } 110 | 111 | // append original syscall code 112 | if(WriteProcessMemory(hProcess, (BYTE*)pRemoteNtQuerySystemInformationHook + sizeof(bNtQuerySystemInformationHookPrefix), pNtQuerySystemInformation, SYSCALL_COPY_BYTE_COUNT, NULL) == 0) 113 | { 114 | return 1; 115 | } 116 | 117 | // patch NtQuerySystemInformation in remote process - jump to hook 118 | *(UINT64*)&bJumpToHook[2] = (UINT64)pRemoteNtQuerySystemInformationHook; 119 | if(WriteProcessMemory(hProcess, (BYTE*)pNtQuerySystemInformation, bJumpToHook, sizeof(bJumpToHook), NULL) == 0) 120 | { 121 | return 1; 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | DWORD DisableParallelLoader(HANDLE hProcess) 128 | { 129 | VOID *pNtOpenSection = NULL; 130 | BYTE bExpectedOrigBytes[] = 131 | { 132 | // mov r10, rcx 133 | 0x4C, 0x8B, 0xD1 134 | }; 135 | BYTE bPushRcxPopR10[] = 136 | { 137 | // push rcx 138 | 0x51, 139 | // pop r10 140 | 0x41, 0x5A 141 | }; 142 | BYTE bOrigBytes[sizeof(bExpectedOrigBytes)]; 143 | 144 | // get NtOpenSection address 145 | pNtOpenSection = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtOpenSection"); 146 | if(pNtOpenSection == NULL) 147 | { 148 | return 1; 149 | } 150 | 151 | // read original bytes from NtOpenSection 152 | if(ReadProcessMemory(hProcess, pNtOpenSection, bOrigBytes, sizeof(bOrigBytes), NULL) == 0) 153 | { 154 | return 1; 155 | } 156 | 157 | // ensure the first instruction is "mov r10, rcx" 158 | if(memcmp(bOrigBytes, bExpectedOrigBytes, sizeof(bOrigBytes)) != 0) 159 | { 160 | return 1; 161 | } 162 | 163 | // overwrite with "push rcx; pop r10". 164 | // this has the same effect as the original instruction, but will cause ntdll to set LdrpDetectDetour to 1 and therefore disable the parallel loader. 165 | if(WriteProcessMemory(hProcess, pNtOpenSection, bPushRcxPopR10, sizeof(bPushRcxPopR10), NULL) == 0) 166 | { 167 | return 1; 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | VOID *GetRemoteExeBase(HANDLE hProcess) 174 | { 175 | PROCESS_BASIC_INFORMATION ProcessBasicInfo; 176 | PEB RemotePEB; 177 | DWORD (WINAPI *pNtQueryInformationProcess)(HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength) = NULL; 178 | 179 | // get NtQueryInformationProcess address 180 | pNtQueryInformationProcess = (DWORD(WINAPI*)(HANDLE,DWORD,PVOID,ULONG,PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); 181 | if(pNtQueryInformationProcess == NULL) 182 | { 183 | return NULL; 184 | } 185 | 186 | // get PEB address 187 | memset(&ProcessBasicInfo, 0, sizeof(ProcessBasicInfo)); 188 | if(pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), NULL) != 0) 189 | { 190 | return NULL; 191 | } 192 | 193 | // read PEB data from process 194 | memset(&RemotePEB, 0, sizeof(RemotePEB)); 195 | if(ReadProcessMemory(hProcess, ProcessBasicInfo.PebBaseAddress, &RemotePEB, sizeof(RemotePEB), NULL) == 0) 196 | { 197 | return NULL; 198 | } 199 | 200 | return RemotePEB.ImageBaseAddress; 201 | } 202 | 203 | VOID *GetRemoteModuleNtHeaderAddress(HANDLE hProcess, VOID *pRemoteModuleBase) 204 | { 205 | IMAGE_DOS_HEADER RemoteDosHeader; 206 | 207 | // read DOS header 208 | memset(&RemoteDosHeader, 0, sizeof(RemoteDosHeader)); 209 | if(ReadProcessMemory(hProcess, pRemoteModuleBase, &RemoteDosHeader, sizeof(RemoteDosHeader), NULL) == 0) 210 | { 211 | return NULL; 212 | } 213 | 214 | // return NT header address 215 | return (BYTE*)pRemoteModuleBase + RemoteDosHeader.e_lfanew; 216 | } 217 | 218 | DWORD ReadRemoteModuleNtHeader(HANDLE hProcess, VOID *pRemoteModuleBase, IMAGE_NT_HEADERS64 *pRemoteNtHeader) 219 | { 220 | VOID *pRemoteNtHeaderAddress = NULL; 221 | IMAGE_NT_HEADERS64 RemoteNtHeader; 222 | 223 | // get NT header address for remote module 224 | pRemoteNtHeaderAddress = GetRemoteModuleNtHeaderAddress(hProcess, pRemoteModuleBase); 225 | if(pRemoteNtHeaderAddress == NULL) 226 | { 227 | return 1; 228 | } 229 | 230 | // read NT header 231 | memset(&RemoteNtHeader, 0, sizeof(RemoteNtHeader)); 232 | if(ReadProcessMemory(hProcess, pRemoteNtHeaderAddress, &RemoteNtHeader, sizeof(RemoteNtHeader), NULL) == 0) 233 | { 234 | return 1; 235 | } 236 | 237 | // ensure this is a 64-bit module 238 | if(RemoteNtHeader.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC || RemoteNtHeader.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) 239 | { 240 | return 1; 241 | } 242 | 243 | if(pRemoteNtHeader != NULL) 244 | { 245 | // copy headers 246 | memcpy(pRemoteNtHeader, &RemoteNtHeader, sizeof(RemoteNtHeader)); 247 | } 248 | 249 | return 0; 250 | } 251 | 252 | DWORD WriteRemoteModuleNtHeader(HANDLE hProcess, VOID *pRemoteModuleBase, IMAGE_NT_HEADERS64 *pRemoteNtHeader) 253 | { 254 | VOID *pRemoteExeNtHeaderAddress = NULL; 255 | DWORD dwOrigProtect = 0; 256 | 257 | // get NT header address for remote EXE 258 | pRemoteExeNtHeaderAddress = GetRemoteModuleNtHeaderAddress(hProcess, pRemoteModuleBase); 259 | if(pRemoteExeNtHeaderAddress == NULL) 260 | { 261 | return 1; 262 | } 263 | 264 | if(VirtualProtectEx(hProcess, pRemoteExeNtHeaderAddress, sizeof(IMAGE_NT_HEADERS64), PAGE_READWRITE, &dwOrigProtect) == 0) 265 | { 266 | return 1; 267 | } 268 | 269 | if(WriteProcessMemory(hProcess, pRemoteExeNtHeaderAddress, pRemoteNtHeader, sizeof(IMAGE_NT_HEADERS64), NULL) == 0) 270 | { 271 | return 1; 272 | } 273 | 274 | if(VirtualProtectEx(hProcess, pRemoteExeNtHeaderAddress, sizeof(IMAGE_NT_HEADERS64), dwOrigProtect, &dwOrigProtect) == 0) 275 | { 276 | return 1; 277 | } 278 | 279 | return 0; 280 | } 281 | 282 | DWORD PatchRemoteExeDataDirectories(HANDLE hProcess, VOID *pRemoteExeModuleBase) 283 | { 284 | IMAGE_NT_HEADERS64 RemoteNtHeader; 285 | 286 | // read NT header 287 | if(ReadRemoteModuleNtHeader(hProcess, pRemoteExeModuleBase, &RemoteNtHeader) != 0) 288 | { 289 | return 1; 290 | } 291 | 292 | // remove import directories 293 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0; 294 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0; 295 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; 296 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; 297 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; 298 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; 299 | 300 | // remove TLS directory 301 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress = 0; 302 | RemoteNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size = 0; 303 | 304 | // write NT header 305 | if(WriteRemoteModuleNtHeader(hProcess, pRemoteExeModuleBase, &RemoteNtHeader) != 0) 306 | { 307 | return 1; 308 | } 309 | 310 | return 0; 311 | } 312 | 313 | DWORD PatchRemoteExeEntryPoint(HANDLE hProcess, VOID *pRemoteExeModuleBase, IMAGE_NT_HEADERS64 *pOrigRemoteExeNtHeader, UINT64 qwWinVisorFlags) 314 | { 315 | VOID *pRemoteEntryPoint = NULL; 316 | VOID *pRemoteDllPath = NULL; 317 | VOID *pRemoteWinVisorStartData = NULL; 318 | VOID *pLoadWinVisorDllCode = NULL; 319 | DWORD dwStartHypervisorExportRVA = 0; 320 | WinVisorStartDataStruct WinVisorStartData; 321 | BYTE bOrigEntryPointCode[HOOK_ENTRY_POINT_CODE_SIZE]; 322 | BYTE bHookEntryPointCode[HOOK_ENTRY_POINT_CODE_SIZE] = 323 | { 324 | // mov rax, 0x0000000000000000 325 | 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 326 | // jmp rax 327 | 0xFF, 0xE0, 328 | // (padding) 329 | 0x90, 0x90, 0x90, 0x90 330 | }; 331 | BYTE bLoadWinVisorDllCode[] = 332 | { 333 | // sub rsp, 0x28 334 | 0x48, 0x83, 0xEC, 0x28, 335 | 336 | // mov rcx, 337 | 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 338 | // mov rax, 339 | 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 340 | // call rax 341 | 0xFF, 0xD0, 342 | // test rax, rax 343 | 0x48, 0x85, 0xC0, 344 | // jz 345 | 0x74, 0x26, 346 | // add rax, 347 | 0x48, 0x05, 0x00, 0x00, 0x00, 0x00, 348 | // mov rcx, 349 | 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 350 | // call rax 351 | 0xFF, 0xD0, 352 | 353 | // LoadLibraryFailed: 354 | // add rsp, 0x28 355 | 0x48, 0x83, 0xC4, 0x28, 356 | // ret 357 | 0xC3 358 | }; 359 | 360 | // get entry-point 361 | pRemoteEntryPoint = (BYTE*)pRemoteExeModuleBase + pOrigRemoteExeNtHeader->OptionalHeader.AddressOfEntryPoint; 362 | 363 | // allocate memory in remote process for winvisor dll path 364 | pRemoteDllPath = VirtualAllocEx(hProcess, NULL, sizeof(gszDllPath), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 365 | if(pRemoteDllPath == NULL) 366 | { 367 | return 1; 368 | } 369 | 370 | // allocate memory in remote process for winvisor start data 371 | pRemoteWinVisorStartData = VirtualAllocEx(hProcess, NULL, sizeof(WinVisorStartData), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 372 | if(pRemoteWinVisorStartData == NULL) 373 | { 374 | return 1; 375 | } 376 | 377 | // allocate memory in remote process for winvisor loader code 378 | pLoadWinVisorDllCode = VirtualAllocEx(hProcess, NULL, sizeof(bLoadWinVisorDllCode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 379 | if(pLoadWinVisorDllCode == NULL) 380 | { 381 | return 1; 382 | } 383 | 384 | // calculate RVA of StartHypervisor export in winvisor dll 385 | if(GetStartHypervisorExportRVA(&dwStartHypervisorExportRVA) != 0) 386 | { 387 | return 1; 388 | } 389 | 390 | // store original entry-point code (first 16 bytes) 391 | memset(bOrigEntryPointCode, 0, sizeof(bOrigEntryPointCode)); 392 | if(ReadProcessMemory(hProcess, pRemoteEntryPoint, bOrigEntryPointCode, sizeof(bOrigEntryPointCode), NULL) == 0) 393 | { 394 | return 1; 395 | } 396 | 397 | // copy full dll path to remote process 398 | if(WriteProcessMemory(hProcess, pRemoteDllPath, gszDllPath, sizeof(gszDllPath), NULL) == 0) 399 | { 400 | return 1; 401 | } 402 | 403 | // copy start data to remote process 404 | memset(&WinVisorStartData, 0, sizeof(WinVisorStartData)); 405 | memcpy(WinVisorStartData.bOrigEntryPointCode, bOrigEntryPointCode, sizeof(WinVisorStartData.bOrigEntryPointCode)); 406 | WinVisorStartData.qwWinVisorFlags = qwWinVisorFlags; 407 | memcpy(&WinVisorStartData.OrigNtHeader, pOrigRemoteExeNtHeader, sizeof(WinVisorStartData.OrigNtHeader)); 408 | if(WriteProcessMemory(hProcess, pRemoteWinVisorStartData, &WinVisorStartData, sizeof(WinVisorStartData), NULL) == 0) 409 | { 410 | return 1; 411 | } 412 | 413 | // populate values/pointers in winvisor loader code and copy to remote process 414 | *(UINT64*)&bLoadWinVisorDllCode[6] = (UINT64)pRemoteDllPath; 415 | *(UINT64*)&bLoadWinVisorDllCode[16] = (UINT64)LoadLibraryA; 416 | *(DWORD*)&bLoadWinVisorDllCode[33] = dwStartHypervisorExportRVA; 417 | *(UINT64*)&bLoadWinVisorDllCode[39] = (UINT64)pRemoteWinVisorStartData; 418 | if(WriteProcessMemory(hProcess, pLoadWinVisorDllCode, bLoadWinVisorDllCode, sizeof(bLoadWinVisorDllCode), NULL) == 0) 419 | { 420 | return 1; 421 | } 422 | 423 | // temporarily overwrite the entry-point to load the winvisor dll on startup 424 | *(UINT64*)&bHookEntryPointCode[2] = (UINT64)pLoadWinVisorDllCode; 425 | if(WriteProcessMemory(hProcess, pRemoteEntryPoint, bHookEntryPointCode, sizeof(bHookEntryPointCode), NULL) == 0) 426 | { 427 | return 1; 428 | } 429 | 430 | return 0; 431 | } 432 | 433 | DWORD AttachWinVisor(HANDLE hProcess, UINT64 qwWinVisorFlags) 434 | { 435 | VOID *pRemoteExeModuleBase = NULL; 436 | IMAGE_NT_HEADERS64 OrigRemoteExeNtHeader; 437 | 438 | // get remote EXE base address 439 | pRemoteExeModuleBase = GetRemoteExeBase(hProcess); 440 | if(pRemoteExeModuleBase == NULL) 441 | { 442 | return 1; 443 | } 444 | 445 | // store original NT headers for remote exe 446 | if(ReadRemoteModuleNtHeader(hProcess, pRemoteExeModuleBase, &OrigRemoteExeNtHeader) != 0) 447 | { 448 | printf("Error: Target is not a valid x64 executable\n"); 449 | return 1; 450 | } 451 | 452 | // hook the entry-point of the remote process - load WinVisor DLL 453 | if(PatchRemoteExeEntryPoint(hProcess, pRemoteExeModuleBase, &OrigRemoteExeNtHeader, qwWinVisorFlags) != 0) 454 | { 455 | return 1; 456 | } 457 | 458 | // temporarily remove the import and TLS data directories for the remote exe. 459 | // this prevents any DLL dependencies and TLS callbacks from executing before the hypervisor takes over. 460 | // these will be restored later, and the virtual CPU will load DLL dependencies and execute TLS callbacks manually before executing the entry-point. 461 | if(PatchRemoteExeDataDirectories(hProcess, pRemoteExeModuleBase) != 0) 462 | { 463 | return 1; 464 | } 465 | 466 | // windows 10 introduced a new shared page which is located close to KUSER_SHARED_DATA. 467 | // the exact address can be retrieved with NtQuerySystemInformation(SystemHypervisorSharedPageInformation). 468 | // this page is used by timing-related functions such as RtlQueryPerformanceCounter / RtlGetMultiTimePrecise. 469 | // the hypervisor platform api contains a bug which causes WHvRunVirtualProcessor to get stuck in an infinite loop if the guest attempts to access this shared page. 470 | // to work around this bug, NtQuerySystemInformation will be patched to return STATUS_INVALID_INFO_CLASS for SystemHypervisorSharedPageInformation requests. 471 | // this causes the code in ntdll to fall back to traditional methods. 472 | // it needs to be patched early as LdrpInitializeProcess stores the address of this page in a global variable (ntdll!RtlpHypervisorSharedUserVa). 473 | if(PatchHypervisorSharedPage(hProcess) != 0) 474 | { 475 | return 1; 476 | } 477 | 478 | // windows 10 added a parallel loader which loads DLL dependencies using multiple background threads via a thread-pool. 479 | // as this emulator currently only virtualizes a single thread, this behaviour should be disabled to ensure DLL loads are all performed by the calling thread. 480 | // it is possible to disable the parallel loader by setting PEB->ProcessParameters->LoaderThreads to 1 before resuming the process, but this value can still be 481 | // overridden by the MaxLoaderThreads IFEO value. 482 | // the windows loader also checks for inline patches within a hardcoded list of functions (LdrpCriticalLoaderFunctions), and if any patches are 483 | // detected, parallel loading is disabled for stability reasons. 484 | // this function patches the first instruction (mov r10, rcx) of a known LdrpCriticalLoaderFunctions entry (NtOpenSection) to an equivalent operation 485 | // of the same size (push rcx; pop r10), which forces the parallel loader to be disabled without affecting any other functionality. 486 | if(DisableParallelLoader(hProcess) != 0) 487 | { 488 | return 1; 489 | } 490 | 491 | return 0; 492 | } 493 | 494 | DWORD LaunchTargetProcess(char *pTargetCommandLine, UINT64 qwWinVisorFlags, DWORD *pdwPID) 495 | { 496 | STARTUPINFOA StartupInfo; 497 | PROCESS_INFORMATION ProcessInfo; 498 | 499 | printf("Launching target process: %s...\n", pTargetCommandLine); 500 | 501 | // get full WinVisor DLL path 502 | if(GetDllPath() != 0) 503 | { 504 | return 1; 505 | } 506 | 507 | // create suspended process 508 | memset(&StartupInfo, 0, sizeof(StartupInfo)); 509 | StartupInfo.cb = sizeof(StartupInfo); 510 | if(CreateProcessA(NULL, pTargetCommandLine, NULL, NULL, 0, CREATE_NEW_CONSOLE | CREATE_SUSPENDED, NULL, NULL, &StartupInfo, &ProcessInfo) == 0) 511 | { 512 | printf("Error: Failed to launch target process\n"); 513 | return 1; 514 | } 515 | 516 | // attach WinVisor to remote process 517 | if(AttachWinVisor(ProcessInfo.hProcess, qwWinVisorFlags) != 0) 518 | { 519 | printf("Error: Failed to attach WinVisor to remote process\n"); 520 | TerminateProcess(ProcessInfo.hProcess, 0); 521 | CloseHandle(ProcessInfo.hProcess); 522 | CloseHandle(ProcessInfo.hThread); 523 | return 1; 524 | } 525 | 526 | // start remote process 527 | ResumeThread(ProcessInfo.hThread); 528 | 529 | // close handles 530 | CloseHandle(ProcessInfo.hProcess); 531 | CloseHandle(ProcessInfo.hThread); 532 | 533 | // store PID 534 | *pdwPID = ProcessInfo.dwProcessId; 535 | 536 | return 0; 537 | } 538 | -------------------------------------------------------------------------------- /WinVisor/LogClient.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisor.h" 2 | 3 | DWORD StartLogClient(DWORD dwPID) 4 | { 5 | BYTE bByte = 0; 6 | DWORD dwRead = 0; 7 | HANDLE hPipe = NULL; 8 | char szPipeName[512]; 9 | 10 | // append target PID to pipe name to allow multiple instances 11 | memset(szPipeName, 0, sizeof(szPipeName)); 12 | _snprintf(szPipeName, sizeof(szPipeName) - 1, "%s_%u", LOG_PIPE_NAME, dwPID); 13 | 14 | for(;;) 15 | { 16 | // open named pipe from parent process 17 | hPipe = CreateFileA(szPipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); 18 | if(hPipe == INVALID_HANDLE_VALUE) 19 | { 20 | Sleep(100); 21 | continue; 22 | } 23 | 24 | break; 25 | } 26 | 27 | // read from pipe and print to console 28 | for(;;) 29 | { 30 | // get next character 31 | if(ReadFile(hPipe, &bByte, 1, &dwRead, NULL) == 0) 32 | { 33 | break; 34 | } 35 | 36 | if(dwRead == 0) 37 | { 38 | break; 39 | } 40 | 41 | printf("%c", bByte); 42 | } 43 | 44 | // finished 45 | CloseHandle(hPipe); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /WinVisor/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisor.h" 2 | 3 | #ifndef _WIN64 4 | #error Must be compiled as 64-bit 5 | #endif 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char *pTargetCommandLine = NULL; 10 | UINT64 qwWinVisorFlags = 0; 11 | DWORD dwPID = 0; 12 | 13 | printf("WinVisor\n"); 14 | printf(" - x86matthew\n\n"); 15 | 16 | // validate params 17 | if(argc < 2) 18 | { 19 | printf("Usage: %s [-debug] [-nx] [exe_params]\n", argv[0]); 20 | printf(" -debug : Enable debug logging\n"); 21 | printf(" -nx : Set entire EXE image to non-executable in host process\n"); 22 | printf(" -imports : Include syscall logging for initial imported modules\n"); 23 | return 1; 24 | } 25 | 26 | // parse command-line 27 | if(ParseCommandLine(argv[0], &pTargetCommandLine, &qwWinVisorFlags) != 0) 28 | { 29 | printf("Error: Invalid parameters\n"); 30 | return 1; 31 | } 32 | 33 | // launch target process and attach WinVisor 34 | if(LaunchTargetProcess(pTargetCommandLine, qwWinVisorFlags, &dwPID) != 0) 35 | { 36 | printf("Error: Failed to launch WinVisor\n"); 37 | return 1; 38 | } 39 | 40 | // read log messages 41 | if(StartLogClient(dwPID) != 0) 42 | { 43 | return 1; 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /WinVisor/ParseCommandLine.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisor.h" 2 | 3 | char *SkipWhitespace(char *pString) 4 | { 5 | char *pCurrPtr = NULL; 6 | 7 | // find first non-space character 8 | pCurrPtr = pString; 9 | for(;;) 10 | { 11 | if(*pCurrPtr != ' ') 12 | { 13 | break; 14 | } 15 | pCurrPtr++; 16 | } 17 | 18 | return pCurrPtr; 19 | } 20 | 21 | DWORD CheckCommandLineSwitch(char *pCommandLinePtr, char *pSwitchName, char **ppUpdatedCommandLinePtr) 22 | { 23 | char szTemp[64]; 24 | char *pUpdatedCommandLinePtr = NULL; 25 | 26 | // check if this is the specified command-line switch 27 | memset(szTemp, 0, sizeof(szTemp)); 28 | _snprintf(szTemp, sizeof(szTemp) - 1, "-%s ", pSwitchName); 29 | if(strncmp(pCommandLinePtr, szTemp, strlen(szTemp)) != 0) 30 | { 31 | return 1; 32 | } 33 | 34 | // update command line ptr 35 | pUpdatedCommandLinePtr = pCommandLinePtr; 36 | pUpdatedCommandLinePtr += strlen(szTemp); 37 | pUpdatedCommandLinePtr = SkipWhitespace(pUpdatedCommandLinePtr); 38 | 39 | // store ptr 40 | *ppUpdatedCommandLinePtr = pUpdatedCommandLinePtr; 41 | 42 | return 0; 43 | } 44 | 45 | DWORD ParseCommandLine(char *pFirstParam, char **ppTargetCommandLine, UINT64 *pqwWinVisorFlags) 46 | { 47 | DWORD dwIgnoreCharCount = 0; 48 | char *pTargetCommandLine = NULL; 49 | UINT64 qwWinVisorFlags = 0; 50 | 51 | // skip the first param (this exe) 52 | dwIgnoreCharCount = (DWORD)strlen(pFirstParam); 53 | pTargetCommandLine = GetCommandLineA(); 54 | if(*pTargetCommandLine == '\"') 55 | { 56 | dwIgnoreCharCount += 2; 57 | } 58 | pTargetCommandLine += dwIgnoreCharCount; 59 | 60 | // ignore leading spaces 61 | pTargetCommandLine = SkipWhitespace(pTargetCommandLine); 62 | 63 | for(;;) 64 | { 65 | // check if this is a command-line switch 66 | if(*pTargetCommandLine == '-') 67 | { 68 | // check switch type 69 | if(CheckCommandLineSwitch(pTargetCommandLine, "debug", &pTargetCommandLine) == 0) 70 | { 71 | qwWinVisorFlags |= WINVISOR_FLAG_DEBUG_LOG; 72 | } 73 | else if(CheckCommandLineSwitch(pTargetCommandLine, "nx", &pTargetCommandLine) == 0) 74 | { 75 | qwWinVisorFlags |= WINVISOR_FLAG_NX; 76 | } 77 | else if(CheckCommandLineSwitch(pTargetCommandLine, "imports", &pTargetCommandLine) == 0) 78 | { 79 | qwWinVisorFlags |= WINVISOR_FLAG_IMPORTS; 80 | } 81 | else 82 | { 83 | // unknown switch 84 | return 1; 85 | } 86 | } 87 | else 88 | { 89 | break; 90 | } 91 | } 92 | 93 | // store cmdline ptr and flags 94 | *ppTargetCommandLine = pTargetCommandLine; 95 | *pqwWinVisorFlags = qwWinVisorFlags; 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /WinVisor/WinVisor.h: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #define _CRT_SECURE_NO_DEPRECATE 3 | #include 4 | #include 5 | #include "..\Common\WinVisorCommon.h" 6 | 7 | #define ProcessBasicInformation 0 8 | 9 | #define SYSCALL_COPY_BYTE_COUNT 64 10 | 11 | struct PEB 12 | { 13 | BYTE Reserved1[2]; 14 | BYTE BeingDebugged; 15 | BYTE Reserved2[1]; 16 | PVOID Reserved3[1]; 17 | PVOID ImageBaseAddress; 18 | PVOID Ldr; 19 | PVOID ProcessParameters; 20 | PVOID Reserved4[3]; 21 | PVOID AtlThunkSListPtr; 22 | PVOID Reserved5; 23 | ULONG Reserved6; 24 | PVOID Reserved7; 25 | ULONG Reserved8; 26 | ULONG AtlThunkSListPtr32; 27 | PVOID Reserved9[45]; 28 | BYTE Reserved10[96]; 29 | PVOID PostProcessInitRoutine; 30 | BYTE Reserved11[128]; 31 | PVOID Reserved12[1]; 32 | ULONG SessionId; 33 | }; 34 | 35 | struct PROCESS_BASIC_INFORMATION 36 | { 37 | DWORD ExitStatus; 38 | PEB *PebBaseAddress; 39 | ULONG_PTR AffinityMask; 40 | DWORD BasePriority; 41 | ULONG_PTR UniqueProcessId; 42 | ULONG_PTR InheritedFromUniqueProcessId; 43 | }; 44 | 45 | extern DWORD StartLogClient(DWORD dwPID); 46 | extern DWORD ParseCommandLine(char *pFirstParam, char **ppTargetCommandLine, UINT64 *pqwWinVisorFlags); 47 | extern DWORD LaunchTargetProcess(char *pTargetCommandLine, UINT64 qwWinVisorFlags, DWORD *pdwPID); 48 | -------------------------------------------------------------------------------- /WinVisor/WinVisor.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 28 | 31 | 34 | 37 | 40 | 43 | 55 | 58 | 61 | 64 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 96 | 103 | 106 | 109 | 112 | 115 | 119 | 131 | 134 | 137 | 140 | 147 | 150 | 153 | 156 | 159 | 162 | 165 | 168 | 171 | 172 | 180 | 183 | 186 | 189 | 192 | 195 | 204 | 207 | 210 | 213 | 222 | 225 | 228 | 231 | 234 | 237 | 240 | 243 | 246 | 247 | 255 | 258 | 261 | 264 | 267 | 271 | 280 | 283 | 286 | 289 | 298 | 301 | 304 | 307 | 310 | 313 | 316 | 319 | 322 | 323 | 324 | 325 | 326 | 327 | 332 | 335 | 336 | 339 | 340 | 343 | 344 | 347 | 348 | 349 | 354 | 357 | 358 | 361 | 362 | 363 | 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /WinVisorDLL/CreateCpuState.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | CpuStateStruct *CreateCpuState(WinVisorStartDataStruct *pWinVisorStartData) 4 | { 5 | CpuStateStruct *pCpuState = NULL; 6 | 7 | // allocate cpu state object 8 | pCpuState = (CpuStateStruct*)VirtualAlloc(NULL, sizeof(CpuStateStruct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 9 | if(pCpuState == NULL) 10 | { 11 | WriteLog(LOG_ERROR, "Failed to allocate CPU state object"); 12 | return NULL; 13 | } 14 | 15 | // prepare CPL3 state 16 | if(PrepareCPL3(pCpuState, pWinVisorStartData) != 0) 17 | { 18 | WriteLog(LOG_ERROR, "Failed to prepare user-mode entry environment"); 19 | DeleteCpuState(pCpuState); 20 | return NULL; 21 | } 22 | 23 | // prepare CPL0 bootloader 24 | if(PrepareCPL0(pCpuState) != 0) 25 | { 26 | WriteLog(LOG_ERROR, "Failed to prepare bootloader"); 27 | DeleteCpuState(pCpuState); 28 | return NULL; 29 | } 30 | 31 | return pCpuState; 32 | } 33 | 34 | DWORD DeleteCpuState(CpuStateStruct *pCpuState) 35 | { 36 | if(pCpuState != NULL) 37 | { 38 | if(pCpuState->pCPL0_Stack != NULL) 39 | { 40 | // free memory 41 | VirtualFree(pCpuState->pCPL0_Stack, 0, MEM_RELEASE); 42 | } 43 | 44 | if(pCpuState->pCPL3_Stack != NULL) 45 | { 46 | // free memory 47 | VirtualFree(pCpuState->pCPL3_Stack, 0, MEM_RELEASE); 48 | } 49 | 50 | if(pCpuState->hHostThread != NULL) 51 | { 52 | // terminate thread 53 | TerminateThread(pCpuState->hHostThread, 0); 54 | CloseHandle(pCpuState->hHostThread); 55 | } 56 | 57 | if(pCpuState->hSyscallProxyReadyEvent != NULL) 58 | { 59 | // delete event object 60 | CloseHandle(pCpuState->hSyscallProxyReadyEvent); 61 | } 62 | 63 | if(pCpuState->hSyscallWaitingEvent != NULL) 64 | { 65 | // delete event object 66 | CloseHandle(pCpuState->hSyscallWaitingEvent); 67 | } 68 | 69 | if(pCpuState->hSyscallCompleteEvent != NULL) 70 | { 71 | // delete event object 72 | CloseHandle(pCpuState->hSyscallCompleteEvent); 73 | } 74 | 75 | // free main object 76 | VirtualFree(pCpuState, 0, MEM_RELEASE); 77 | } 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /WinVisorDLL/FixModuleImports.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD FixModuleImports_ProcessModule(VOID *pModuleBase, char *pModuleName, DWORD dwFirstThunkOffset) 4 | { 5 | IMAGE_THUNK_DATA64 *pCurrThunkData64 = NULL; 6 | DWORD dwCurrThunkOffset = 0; 7 | DWORD dwOrdinal = 0; 8 | IMAGE_IMPORT_BY_NAME *pImageImportByName = NULL; 9 | DWORD dwVirtualAddress = 0; 10 | HMODULE hModule = NULL; 11 | VOID *pCurrResolvedAddr = NULL; 12 | UINT64 *pImportPtr = NULL; 13 | 14 | // load target library 15 | hModule = LoadLibraryA(pModuleName); 16 | if(hModule == NULL) 17 | { 18 | WriteLog(LOG_ERROR, "Failed to load DLL: %s", pModuleName); 19 | return 1; 20 | } 21 | 22 | // process module imports 23 | dwCurrThunkOffset = dwFirstThunkOffset; 24 | for(;;) 25 | { 26 | // get current thunk ptr 27 | pCurrThunkData64 = (IMAGE_THUNK_DATA64*)((BYTE*)pModuleBase + dwCurrThunkOffset); 28 | if(pCurrThunkData64->u1.AddressOfData == 0) 29 | { 30 | // finished 31 | break; 32 | } 33 | 34 | // get virtual address of import entry 35 | dwVirtualAddress = (DWORD)((BYTE*)&pCurrThunkData64->u1.Function - (BYTE*)pModuleBase); 36 | 37 | // check import type 38 | if(pCurrThunkData64->u1.Ordinal & IMAGE_ORDINAL_FLAG64) 39 | { 40 | // resolve import by ordinal 41 | dwOrdinal = (DWORD)(pCurrThunkData64->u1.Ordinal & 0xFFFF); 42 | pCurrResolvedAddr = GetProcAddress(hModule, (char*)((SIZE_T)dwOrdinal)); 43 | if(pCurrResolvedAddr == NULL) 44 | { 45 | WriteLog(LOG_ERROR, "Failed to locate import entry: %s!#%u", pModuleName, dwOrdinal); 46 | return 1; 47 | } 48 | } 49 | else 50 | { 51 | // get imported function name 52 | pImageImportByName = (IMAGE_IMPORT_BY_NAME*)((BYTE*)pModuleBase + (DWORD)pCurrThunkData64->u1.AddressOfData); 53 | 54 | pCurrResolvedAddr = GetProcAddress(hModule, (char*)pImageImportByName->Name); 55 | if(pCurrResolvedAddr == NULL) 56 | { 57 | WriteLog(LOG_ERROR, "Failed to locate import entry: %s!#%s", pModuleName, (char*)pImageImportByName->Name); 58 | return 1; 59 | } 60 | } 61 | 62 | // update address 63 | pImportPtr = (UINT64*)((BYTE*)pModuleBase + dwVirtualAddress); 64 | if(CopyMemoryAndRestoreProtection(pImportPtr, &pCurrResolvedAddr, sizeof(pCurrResolvedAddr)) != 0) 65 | { 66 | return 1; 67 | } 68 | 69 | // update thunk offset 70 | dwCurrThunkOffset += sizeof(IMAGE_THUNK_DATA64); 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | DWORD FixModuleImports(VOID *pModuleBase) 77 | { 78 | IMAGE_NT_HEADERS64 *pImageNtHeader = NULL; 79 | IMAGE_DATA_DIRECTORY *pImportDirectory = NULL; 80 | DWORD dwCurrImportBlockOffset = 0; 81 | IMAGE_IMPORT_DESCRIPTOR *pImageImportDescriptor = NULL; 82 | char *pCurrModuleName = NULL; 83 | DWORD dwFirstThunkOffset = 0; 84 | 85 | pImageNtHeader = (IMAGE_NT_HEADERS64*)GetNtHeader((HMODULE)pModuleBase); 86 | if(pImageNtHeader == NULL) 87 | { 88 | return 1; 89 | } 90 | 91 | // check if this module contains an import directory 92 | pImportDirectory = &pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 93 | if(pImportDirectory->VirtualAddress != 0 && pImportDirectory->Size != 0) 94 | { 95 | // process import table 96 | dwCurrImportBlockOffset = pImportDirectory->VirtualAddress; 97 | for(;;) 98 | { 99 | pImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)pModuleBase + dwCurrImportBlockOffset); 100 | if(pImageImportDescriptor->Name == 0) 101 | { 102 | // finished 103 | break; 104 | } 105 | 106 | // get current module name 107 | pCurrModuleName = (char*)((BYTE*)pModuleBase + pImageImportDescriptor->Name); 108 | 109 | // process the imports for the current module 110 | dwFirstThunkOffset = pImageImportDescriptor->FirstThunk; 111 | if(FixModuleImports_ProcessModule(pModuleBase, pCurrModuleName, dwFirstThunkOffset) != 0) 112 | { 113 | return 1; 114 | } 115 | 116 | // update import block offset 117 | dwCurrImportBlockOffset += sizeof(IMAGE_IMPORT_DESCRIPTOR); 118 | } 119 | } 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /WinVisorDLL/HandleVmExit.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD HandleVmExit(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, WHV_RUN_VP_EXIT_CONTEXT *pVmExitContext) 4 | { 5 | // check vmexit reason 6 | if(pVmExitContext->ExitReason == WHvRunVpExitReasonMemoryAccess) 7 | { 8 | // invalid memory access 9 | WriteLog(LOG_ERROR, "Invalid physical memory access at 0x%p (RIP: 0x%p)", pVmExitContext->MemoryAccess.Gpa, pCpuRegisterState->RIP); 10 | return 1; 11 | } 12 | else if(pVmExitContext->ExitReason == WHvRunVpExitReasonException) 13 | { 14 | // exception 15 | if(pVmExitContext->VpException.ExceptionType == WHvX64ExceptionTypePageFault) 16 | { 17 | // page fault 18 | if(HandlePageFault(pCpuState, pCpuRegisterState, pVmExitContext->VpException.ExceptionParameter) != 0) 19 | { 20 | WriteLog(LOG_ERROR, "Failed to handle page fault (RIP: 0x%p)", pCpuRegisterState->RIP); 21 | return 1; 22 | } 23 | } 24 | else if(pVmExitContext->VpException.ExceptionType == WHvX64ExceptionTypeGeneralProtectionFault) 25 | { 26 | // general protection fault 27 | WriteLog(LOG_ERROR, "General protection fault (RIP: 0x%p)", pCpuRegisterState->RIP); 28 | return 1; 29 | } 30 | else 31 | { 32 | // unknown type 33 | WriteLog(LOG_ERROR, "Unhandled exception type: 0x%02X (RIP: 0x%p)", pVmExitContext->VpException.ExceptionType, pCpuRegisterState->RIP); 34 | return 1; 35 | } 36 | } 37 | else 38 | { 39 | // unknown vmexit reason 40 | WriteLog(LOG_ERROR, "Unhandled VmExit reason: 0x%08X (RIP: 0x%p)", pVmExitContext->ExitReason, pCpuRegisterState->RIP); 41 | return 1; 42 | } 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /WinVisorDLL/HypervisorEntryPoint.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD gdwLoadedModuleImports = 0; 4 | 5 | DWORD ExecuteTlsCallbacks(VOID *pModuleBase) 6 | { 7 | IMAGE_NT_HEADERS64 *pImageNtHeader = NULL; 8 | IMAGE_DATA_DIRECTORY *pTlsDirectory = NULL; 9 | IMAGE_TLS_DIRECTORY *pImageTlsDirectory = NULL; 10 | UINT64 *pqwCurrCallbackEntry = NULL; 11 | 12 | pImageNtHeader = (IMAGE_NT_HEADERS64*)GetNtHeader((HMODULE)pModuleBase); 13 | if(pImageNtHeader == NULL) 14 | { 15 | return 1; 16 | } 17 | 18 | // check if this module contains a TLS directory 19 | pTlsDirectory = &pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 20 | if(pTlsDirectory->VirtualAddress != 0 && pTlsDirectory->Size != 0) 21 | { 22 | pImageTlsDirectory = (IMAGE_TLS_DIRECTORY*)((BYTE*)pModuleBase + pTlsDirectory->VirtualAddress); 23 | if(pImageTlsDirectory->AddressOfCallBacks != 0) 24 | { 25 | // execute all callbacks 26 | pqwCurrCallbackEntry = (UINT64*)pImageTlsDirectory->AddressOfCallBacks; 27 | for(;;) 28 | { 29 | if(*pqwCurrCallbackEntry == 0) 30 | { 31 | // end of list 32 | break; 33 | } 34 | 35 | // execute current callback function 36 | ((PIMAGE_TLS_CALLBACK)*pqwCurrCallbackEntry)(pModuleBase, DLL_PROCESS_ATTACH, NULL); 37 | 38 | // move to next entry 39 | pqwCurrCallbackEntry++; 40 | } 41 | } 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | DWORD HypervisorEntryPoint_StartExe(VOID *pExeEntryPoint, DWORD *pdwExitCode) 48 | { 49 | DWORD dwExitCode = 0; 50 | 51 | // load EXE imports 52 | if(FixModuleImports(ghExeBase) != 0) 53 | { 54 | return 1; 55 | } 56 | 57 | // loaded imports - set flag for logging purposes 58 | gdwLoadedModuleImports = 1; 59 | 60 | // execute TLS callbacks 61 | if(ExecuteTlsCallbacks(ghExeBase) != 0) 62 | { 63 | return 1; 64 | } 65 | 66 | // execute original entry-point 67 | dwExitCode = ((DWORD(*)(VOID*))pExeEntryPoint)((VOID*)__readgsqword(0x60)); 68 | 69 | // store exit code 70 | *pdwExitCode = dwExitCode; 71 | 72 | return 0; 73 | } 74 | 75 | DWORD HypervisorEntryPoint(VOID *pExeEntryPoint) 76 | { 77 | DWORD dwExitCode = 0; 78 | 79 | // start exe 80 | HypervisorEntryPoint_StartExe(pExeEntryPoint, &dwExitCode); 81 | 82 | // ensure the current thread is terminated after the entry-point returns 83 | TerminateThread(GetCurrentThread(), dwExitCode); 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /WinVisorDLL/HypervisorUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | HRESULT (WINAPI *WHvCreatePartition)(WHV_PARTITION_HANDLE* Partition) = NULL; 4 | HRESULT (WINAPI *WHvDeletePartition)(WHV_PARTITION_HANDLE Partition) = NULL; 5 | HRESULT (WINAPI *WHvMapGpaRange)(WHV_PARTITION_HANDLE Partition, VOID* SourceAddress, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes, WHV_MAP_GPA_RANGE_FLAGS Flags) = NULL; 6 | HRESULT (WINAPI *WHvUnmapGpaRange)(WHV_PARTITION_HANDLE Partition, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes); 7 | HRESULT (WINAPI *WHvSetVirtualProcessorRegisters)(WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues) = NULL; 8 | HRESULT (WINAPI *WHvRunVirtualProcessor)(WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, VOID* ExitContext, UINT32 ExitContextSizeInBytes) = NULL; 9 | HRESULT (WINAPI *WHvSetPartitionProperty)(WHV_PARTITION_HANDLE Partition, WHV_PARTITION_PROPERTY_CODE PropertyCode, const VOID* PropertyBuffer, UINT32 PropertyBufferSizeInBytes) = NULL; 10 | HRESULT (WINAPI *WHvSetupPartition)(WHV_PARTITION_HANDLE Partition) = NULL; 11 | HRESULT (WINAPI *WHvCreateVirtualProcessor)(WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Flags) = NULL; 12 | HRESULT (WINAPI *WHvGetVirtualProcessorRegisters)(WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues) = NULL; 13 | HRESULT (WINAPI *WHvGetCapability)(WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32 *WrittenSizeInBytes) = NULL; 14 | 15 | ImportFunctionStruct gImportHypervisorPlatformFunctionList[] = 16 | { 17 | { "WHvCreatePartition", (void**)&WHvCreatePartition }, 18 | { "WHvDeletePartition", (void**)&WHvDeletePartition }, 19 | { "WHvMapGpaRange", (void**)&WHvMapGpaRange }, 20 | { "WHvUnmapGpaRange", (void**)&WHvUnmapGpaRange }, 21 | { "WHvSetVirtualProcessorRegisters", (void**)&WHvSetVirtualProcessorRegisters }, 22 | { "WHvRunVirtualProcessor", (void**)&WHvRunVirtualProcessor }, 23 | { "WHvSetPartitionProperty", (void**)&WHvSetPartitionProperty }, 24 | { "WHvSetupPartition", (void**)&WHvSetupPartition }, 25 | { "WHvCreateVirtualProcessor", (void**)&WHvCreateVirtualProcessor }, 26 | { "WHvGetVirtualProcessorRegisters", (void**)&WHvGetVirtualProcessorRegisters }, 27 | { "WHvGetCapability", (void**)&WHvGetCapability }, 28 | }; 29 | 30 | HANDLE ghPartitionHandle = NULL; 31 | 32 | DWORD HypervisorUtils_Initialise() 33 | { 34 | HMODULE hModule = NULL; 35 | void *pImportAddr = NULL; 36 | DWORD dwFunctionCount = 0; 37 | WHV_CAPABILITY HypervisorCapability; 38 | UINT32 dwHypervisorCapabilitySize = 0; 39 | 40 | // load hypervisor module 41 | hModule = LoadLibraryA("winhvplatform.dll"); 42 | if(hModule == NULL) 43 | { 44 | return 1; 45 | } 46 | 47 | // resolve imported functions 48 | dwFunctionCount = sizeof(gImportHypervisorPlatformFunctionList) / sizeof(gImportHypervisorPlatformFunctionList[0]); 49 | for(DWORD i = 0; i < dwFunctionCount; i++) 50 | { 51 | // resolve current function 52 | pImportAddr = GetProcAddress(hModule, gImportHypervisorPlatformFunctionList[i].pName); 53 | if(pImportAddr == NULL) 54 | { 55 | return 1; 56 | } 57 | 58 | // store function ptr 59 | *gImportHypervisorPlatformFunctionList[i].pFunctionPtrAddr = pImportAddr; 60 | } 61 | 62 | // ensure the hypervisor platform is enabled 63 | memset(&HypervisorCapability, 0, sizeof(HypervisorCapability)); 64 | if(WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &HypervisorCapability, sizeof(HypervisorCapability), &dwHypervisorCapabilitySize) != S_OK) 65 | { 66 | return 1; 67 | } 68 | if(HypervisorCapability.HypervisorPresent == 0) 69 | { 70 | return 1; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | DWORD HypervisorUtils_CreateEnvironment() 77 | { 78 | WHV_PARTITION_HANDLE hPartitionHandle = NULL; 79 | WHV_PARTITION_PROPERTY PartitionPropertyData; 80 | WHV_EXTENDED_VM_EXITS ExtendedVmExits; 81 | UINT64 qwExceptionExitBitmap = 0; 82 | 83 | // create hypervisor partition 84 | if(WHvCreatePartition(&hPartitionHandle) != S_OK) 85 | { 86 | return 1; 87 | } 88 | 89 | // single processor 90 | memset(&PartitionPropertyData, 0, sizeof(PartitionPropertyData)); 91 | PartitionPropertyData.ProcessorCount = 1; 92 | if(WHvSetPartitionProperty(hPartitionHandle, WHvPartitionPropertyCodeProcessorCount, &PartitionPropertyData, sizeof(PartitionPropertyData)) != S_OK) 93 | { 94 | WHvDeletePartition(hPartitionHandle); 95 | return 1; 96 | } 97 | 98 | // enable vmexit for exceptions 99 | memset(&ExtendedVmExits, 0, sizeof(ExtendedVmExits)); 100 | ExtendedVmExits.ExceptionExit = 1; 101 | if(WHvSetPartitionProperty(hPartitionHandle, WHvPartitionPropertyCodeExtendedVmExits, &ExtendedVmExits, sizeof(ExtendedVmExits)) != S_OK) 102 | { 103 | WHvDeletePartition(hPartitionHandle); 104 | return 1; 105 | } 106 | 107 | // update exception bitmap to catch page faults and general protection faults 108 | qwExceptionExitBitmap = (1 << WHvX64ExceptionTypePageFault) | (1 << WHvX64ExceptionTypeGeneralProtectionFault); 109 | if(WHvSetPartitionProperty(hPartitionHandle, WHvPartitionPropertyCodeExceptionExitBitmap, &qwExceptionExitBitmap, sizeof(qwExceptionExitBitmap)) != S_OK) 110 | { 111 | WHvDeletePartition(hPartitionHandle); 112 | return 1; 113 | } 114 | 115 | // hypervisor partition ready 116 | if(WHvSetupPartition(hPartitionHandle) != S_OK) 117 | { 118 | WHvDeletePartition(hPartitionHandle); 119 | return 1; 120 | } 121 | 122 | // create virtual CPU 123 | if(WHvCreateVirtualProcessor(hPartitionHandle, 0, 0) != S_OK) 124 | { 125 | WHvDeletePartition(hPartitionHandle); 126 | return 1; 127 | } 128 | 129 | // store handle 130 | ghPartitionHandle = hPartitionHandle; 131 | 132 | return 0; 133 | } 134 | 135 | DWORD HypervisorUtils_DeleteEnvironment() 136 | { 137 | if(ghPartitionHandle != NULL) 138 | { 139 | WHvDeletePartition(ghPartitionHandle); 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | DWORD HypervisorUtils_GetRegisterValue_U64(WHV_REGISTER_NAME RegisterName, QWORD *pqwRegisterValue) 146 | { 147 | WHV_REGISTER_VALUE RegisterValue; 148 | 149 | // get uint64 register value 150 | memset(&RegisterValue, 0, sizeof(RegisterValue)); 151 | if(WHvGetVirtualProcessorRegisters(ghPartitionHandle, 0, &RegisterName, 1, &RegisterValue) != S_OK) 152 | { 153 | return 1; 154 | } 155 | 156 | *pqwRegisterValue = RegisterValue.Reg64; 157 | 158 | return 0; 159 | } 160 | 161 | DWORD HypervisorUtils_SetRegisterValue_U64(WHV_REGISTER_NAME RegisterName, QWORD qwRegisterValue) 162 | { 163 | WHV_REGISTER_VALUE RegisterValue; 164 | 165 | // set uint64 register value 166 | memset(&RegisterValue, 0, sizeof(RegisterValue)); 167 | RegisterValue.Reg64 = qwRegisterValue; 168 | if(WHvSetVirtualProcessorRegisters(ghPartitionHandle, 0, &RegisterName, 1, &RegisterValue) != S_OK) 169 | { 170 | return 1; 171 | } 172 | 173 | return 0; 174 | } 175 | 176 | DWORD HypervisorUtils_SetRegisterValue_Segment(WHV_REGISTER_NAME RegisterName, WORD wSelector, DWORD dwCode) 177 | { 178 | WHV_REGISTER_VALUE RegisterValue; 179 | 180 | // set segment register value 181 | memset(&RegisterValue, 0, sizeof(RegisterValue)); 182 | RegisterValue.Segment.Selector = wSelector; 183 | RegisterValue.Segment.NonSystemSegment = 1; 184 | RegisterValue.Segment.DescriptorPrivilegeLevel = wSelector & 0x3; 185 | RegisterValue.Segment.Present = 1; 186 | if(dwCode == 0) 187 | { 188 | // data (write, accessed) 189 | RegisterValue.Segment.SegmentType = 0x3; 190 | } 191 | else 192 | { 193 | // code (execute, read, accessed) 194 | RegisterValue.Segment.SegmentType = 0xB; 195 | RegisterValue.Segment.Long = 1; 196 | } 197 | if(WHvSetVirtualProcessorRegisters(ghPartitionHandle, 0, &RegisterName, 1, &RegisterValue) != S_OK) 198 | { 199 | return 1; 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | DWORD HypervisorUtils_GetRegisters(CpuRegisterStateStruct *pCpuRegisterState) 206 | { 207 | CpuRegisterStateStruct CpuRegisterState; 208 | 209 | // get register values 210 | memset(&CpuRegisterState, 0, sizeof(CpuRegisterState)); 211 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRax, &CpuRegisterState.RAX); 212 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRcx, &CpuRegisterState.RCX); 213 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRdx, &CpuRegisterState.RDX); 214 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRbx, &CpuRegisterState.RBX); 215 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRsp, &CpuRegisterState.RSP); 216 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRbp, &CpuRegisterState.RBP); 217 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRsi, &CpuRegisterState.RSI); 218 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRdi, &CpuRegisterState.RDI); 219 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR8, &CpuRegisterState.R8); 220 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR9, &CpuRegisterState.R9); 221 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR10, &CpuRegisterState.R10); 222 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR11, &CpuRegisterState.R11); 223 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR12, &CpuRegisterState.R12); 224 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR13, &CpuRegisterState.R13); 225 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR14, &CpuRegisterState.R14); 226 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterR15, &CpuRegisterState.R15); 227 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRip, &CpuRegisterState.RIP); 228 | HypervisorUtils_GetRegisterValue_U64(WHvX64RegisterRflags, &CpuRegisterState.RFLAGS); 229 | memcpy(pCpuRegisterState, &CpuRegisterState, sizeof(CpuRegisterState)); 230 | 231 | return 0; 232 | } 233 | 234 | DWORD HypervisorUtils_SetRegisters(CpuRegisterStateStruct *pCpuRegisterState) 235 | { 236 | // set register values 237 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRax, pCpuRegisterState->RAX); 238 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRcx, pCpuRegisterState->RCX); 239 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRdx, pCpuRegisterState->RDX); 240 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRbx, pCpuRegisterState->RBX); 241 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRsp, pCpuRegisterState->RSP); 242 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRbp, pCpuRegisterState->RBP); 243 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRsi, pCpuRegisterState->RSI); 244 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRdi, pCpuRegisterState->RDI); 245 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR8, pCpuRegisterState->R8); 246 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR9, pCpuRegisterState->R9); 247 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR10, pCpuRegisterState->R10); 248 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR11, pCpuRegisterState->R11); 249 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR12, pCpuRegisterState->R12); 250 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR13, pCpuRegisterState->R13); 251 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR14, pCpuRegisterState->R14); 252 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterR15, pCpuRegisterState->R15); 253 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRip, pCpuRegisterState->RIP); 254 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterRflags, pCpuRegisterState->RFLAGS); 255 | 256 | return 0; 257 | } 258 | 259 | DWORD HypervisorUtils_MapGuestMemory(void *pHostVirtualAddress, void *pGuestPhysicalAddress, DWORD dwSize) 260 | { 261 | // map virtual memory region from host process into the guest 262 | if(WHvMapGpaRange(ghPartitionHandle, pHostVirtualAddress, (WHV_GUEST_PHYSICAL_ADDRESS)pGuestPhysicalAddress, dwSize, WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite | WHvMapGpaRangeFlagExecute) != S_OK) 263 | { 264 | return 1; 265 | } 266 | 267 | return 0; 268 | } 269 | 270 | DWORD HypervisorUtils_UnmapGuestMemory(void *pGuestPhysicalAddress, DWORD dwSize) 271 | { 272 | // unmap region 273 | if(WHvUnmapGpaRange(ghPartitionHandle, (WHV_GUEST_PHYSICAL_ADDRESS)pGuestPhysicalAddress, dwSize) != S_OK) 274 | { 275 | return 1; 276 | } 277 | 278 | return 0; 279 | } 280 | 281 | DWORD HypervisorUtils_ResumeExecution(WHV_RUN_VP_EXIT_CONTEXT *pVmExitContext) 282 | { 283 | // resume cpu execution until next vmexit event 284 | if(WHvRunVirtualProcessor(ghPartitionHandle, 0, pVmExitContext, sizeof(WHV_RUN_VP_EXIT_CONTEXT)) != S_OK) 285 | { 286 | return 1; 287 | } 288 | 289 | return 0; 290 | } 291 | 292 | DWORD HypervisorUtils_FlushTLB() 293 | { 294 | // reset cr3 register to force a TLB flush 295 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterCr3, PAGE_TABLE_BASE_PHYSICAL_ADDRESS); 296 | 297 | return 0; 298 | } 299 | -------------------------------------------------------------------------------- /WinVisorDLL/InterruptHandler_Breakpoint.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD InterruptHandler_Breakpoint(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState) 4 | { 5 | // software breakpoint - skip over and continue 6 | WriteLog(LOG_INFO, "Caught breakpoint"); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /WinVisorDLL/InterruptHandler_LegacySyscall.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD InterruptHandler_LegacySyscall(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState) 4 | { 5 | void *pUserStackPtr = NULL; 6 | 7 | // get user-mode RSP from kernel stack 8 | pUserStackPtr = (void*)*(UINT64*)(pCpuRegisterState->RSP + 0x18); 9 | 10 | // handle legacy syscall 11 | if(HandleGuestSyscall(pCpuState, pCpuRegisterState, pUserStackPtr) != 0) 12 | { 13 | return 1; 14 | } 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /WinVisorDLL/InterruptHandler_SingleStep.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD InterruptHandler_SingleStep(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState) 4 | { 5 | void *pUserInstructionPtr = NULL; 6 | 7 | // get user-mode RIP from kernel stack 8 | pUserInstructionPtr = (void*)*(UINT64*)(pCpuRegisterState->RSP); 9 | 10 | WriteLog(LOG_INFO, "Single-step: 0x%p", pUserInstructionPtr); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /WinVisorDLL/Interrupts.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | BYTE gbInterruptRet_ErrorCode[] = 4 | { 5 | // (some interrupts push an error code onto the stack, this must be removed before returning) 6 | // add rsp, 8 7 | 0x48, 0x83, 0xC4, 0x08, 8 | // iretq 9 | 0x48, 0xCF 10 | }; 11 | 12 | BYTE gbInterruptRet[] = 13 | { 14 | // iretq 15 | 0x48, 0xCF 16 | }; 17 | 18 | InterruptHandlerEntryStruct gInterruptHandlerList[] = 19 | { 20 | { 0x01, InterruptHandler_SingleStep, 0 }, 21 | { 0x03, InterruptHandler_Breakpoint, 0 }, 22 | { 0x2E, InterruptHandler_LegacySyscall, 0 }, 23 | }; 24 | 25 | InterruptHandlerEntryStruct *GetInterruptHandler(BYTE bInterruptIndex) 26 | { 27 | // find interrupt handler for this index 28 | for(DWORD i = 0; i < sizeof(gInterruptHandlerList) / sizeof(gInterruptHandlerList[0]); i++) 29 | { 30 | if(gInterruptHandlerList[i].bInterruptIndex == bInterruptIndex) 31 | { 32 | // found 33 | return &gInterruptHandlerList[i]; 34 | } 35 | } 36 | 37 | // not found 38 | return NULL; 39 | } 40 | 41 | BYTE *GetInterruptReturn(InterruptHandlerEntryStruct *pInterruptHandlerEntry) 42 | { 43 | if(pInterruptHandlerEntry->dwHasErrorCode != 0) 44 | { 45 | // this interrupt type has an error code - stack must be adjusted before returning 46 | return gbInterruptRet_ErrorCode; 47 | } 48 | 49 | // no error code 50 | return gbInterruptRet; 51 | } 52 | -------------------------------------------------------------------------------- /WinVisorDLL/LogServer.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD gdwStopLog = 0; 4 | DWORD gdwDebugLogEnabled = 0; 5 | HANDLE ghLogPipe = NULL; 6 | 7 | DWORD InitialiseLogServer() 8 | { 9 | char szPipeName[512]; 10 | 11 | // append PID to pipe name to allow multiple instances 12 | memset(szPipeName, 0, sizeof(szPipeName)); 13 | _snprintf(szPipeName, sizeof(szPipeName) - 1, "%s_%u", LOG_PIPE_NAME, GetCurrentProcessId()); 14 | 15 | // create logging pipe 16 | ghLogPipe = CreateNamedPipeA(szPipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 0, 0, 0, NULL); 17 | if(ghLogPipe == INVALID_HANDLE_VALUE) 18 | { 19 | return 1; 20 | } 21 | 22 | // wait for child process to connect 23 | ConnectNamedPipe(ghLogPipe, NULL); 24 | 25 | return 0; 26 | } 27 | 28 | DWORD CloseLogServer() 29 | { 30 | if(ghLogPipe != NULL) 31 | { 32 | // close log pipe 33 | CloseHandle(ghLogPipe); 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | DWORD WriteLog(DWORD dwLogType, char *pStringFormat, ...) 40 | { 41 | va_list VaList; 42 | char szFormattedString[1024]; 43 | char szFullMsg[2048]; 44 | DWORD dwWritten = 0; 45 | char *pLogType = NULL; 46 | 47 | // format string 48 | va_start(VaList, pStringFormat); 49 | memset(szFormattedString, 0, sizeof(szFormattedString)); 50 | _vsnprintf(szFormattedString, sizeof(szFormattedString) - 1, pStringFormat, VaList); 51 | va_end(VaList); 52 | 53 | // check type 54 | if(dwLogType == LOG_INFO) 55 | { 56 | pLogType = "INFO"; 57 | } 58 | else if(dwLogType == LOG_ERROR) 59 | { 60 | pLogType = "ERROR"; 61 | } 62 | else if(dwLogType == LOG_DEBUG) 63 | { 64 | if(gdwDebugLogEnabled == 0) 65 | { 66 | // debug logging disabled 67 | return 1; 68 | } 69 | 70 | pLogType = "DEBUG"; 71 | } 72 | else 73 | { 74 | return 1; 75 | } 76 | 77 | // generate full log message 78 | memset(szFullMsg, 0, sizeof(szFullMsg)); 79 | _snprintf(szFullMsg, sizeof(szFullMsg) - 1, "[%s] %s\n", pLogType, szFormattedString); 80 | 81 | if(gdwStopLog == 0) 82 | { 83 | // write to pipe 84 | WriteFile(ghLogPipe, szFullMsg, (DWORD)strlen(szFullMsg), &dwWritten, NULL); 85 | } 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /WinVisorDLL/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | #ifndef _WIN64 4 | #error Must be compiled as 64-bit 5 | #endif 6 | 7 | HMODULE ghNtdllBase = NULL; 8 | DWORD (WINAPI *pNtQueryInformationThread)(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength) = NULL; 9 | DWORD (WINAPI *pNtQuerySystemInformation)(DWORD SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) = NULL; 10 | 11 | DWORD StartHypervisor_Initialise(WinVisorStartDataStruct *pWinVisorStartData, CpuStateStruct **ppCpuState) 12 | { 13 | CpuStateStruct *pCpuState = NULL; 14 | 15 | // check if the "debug" command-line switch was specified 16 | if(pWinVisorStartData->qwWinVisorFlags & WINVISOR_FLAG_DEBUG_LOG) 17 | { 18 | gdwDebugLogEnabled = 1; 19 | } 20 | 21 | // check if the "imports" command-line switch was specified 22 | if(pWinVisorStartData->qwWinVisorFlags & WINVISOR_FLAG_IMPORTS) 23 | { 24 | gdwLogImportSyscallsEnabled = 1; 25 | } 26 | 27 | // get ntdll base 28 | ghNtdllBase = GetModuleHandleA("ntdll.dll"); 29 | 30 | // get NtQueryInformationThread ptr 31 | pNtQueryInformationThread = (DWORD(WINAPI*)(HANDLE,DWORD,PVOID,ULONG,PULONG))GetProcAddress(ghNtdllBase, "NtQueryInformationThread"); 32 | if(pNtQueryInformationThread == NULL) 33 | { 34 | return 1; 35 | } 36 | 37 | // get NtQuerySystemInformation ptr 38 | pNtQuerySystemInformation = (DWORD(WINAPI*)(DWORD,PVOID,ULONG,PULONG))GetProcAddress(ghNtdllBase, "NtQuerySystemInformation"); 39 | if(pNtQuerySystemInformation == NULL) 40 | { 41 | return 1; 42 | } 43 | 44 | // initialise log pipe 45 | if(InitialiseLogServer() != 0) 46 | { 47 | return 1; 48 | } 49 | 50 | WriteLog(LOG_INFO, "Starting..."); 51 | 52 | // initialise hypervisor platform api 53 | if(HypervisorUtils_Initialise() != 0) 54 | { 55 | WriteLog(LOG_ERROR, "Failed to initialise Windows Hypervisor Platform API"); 56 | return 1; 57 | } 58 | 59 | // initialise virtual CPU 60 | if(HypervisorUtils_CreateEnvironment() != 0) 61 | { 62 | WriteLog(LOG_ERROR, "Failed to create hypervisor environment"); 63 | return 1; 64 | } 65 | 66 | // populate list of syscall names / param counts 67 | if(CreateSyscallLists() != 0) 68 | { 69 | WriteLog(LOG_ERROR, "Failed to create syscall lists"); 70 | return 1; 71 | } 72 | 73 | // allocate page tables 74 | if(CreatePageTables() != 0) 75 | { 76 | WriteLog(LOG_ERROR, "Failed to create page tables"); 77 | return 1; 78 | } 79 | 80 | // prepare environment 81 | pCpuState = CreateCpuState(pWinVisorStartData); 82 | if(pCpuState == NULL) 83 | { 84 | WriteLog(LOG_ERROR, "Failed to create initial CPU state"); 85 | return 1; 86 | } 87 | 88 | // store cpu state object ptr 89 | *ppCpuState = pCpuState; 90 | 91 | return 0; 92 | } 93 | 94 | DWORD StartHypervisor_Cleanup(CpuStateStruct *pCpuState, DWORD dwIgnoreHypervisorEnvironment) 95 | { 96 | // clean up - all of these functions must succeed even if they haven't yet been initialised 97 | DeleteCpuState(pCpuState); 98 | DeletePageTables(); 99 | DeleteSyscallLists(); 100 | if(dwIgnoreHypervisorEnvironment == 0) 101 | { 102 | HypervisorUtils_DeleteEnvironment(); 103 | } 104 | CloseLogServer(); 105 | 106 | return 0; 107 | } 108 | 109 | extern "C" __declspec(dllexport) DWORD StartHypervisor(WinVisorStartDataStruct *pWinVisorStartData) 110 | { 111 | CpuStateStruct *pCpuState = NULL; 112 | CpuRegisterStateStruct CpuRegisterState; 113 | WHV_RUN_VP_EXIT_CONTEXT VmExitContext; 114 | 115 | // initialise hypervisor 116 | if(StartHypervisor_Initialise(pWinVisorStartData, &pCpuState) != 0) 117 | { 118 | WriteLog(LOG_ERROR, "Failed to start hypervisor"); 119 | StartHypervisor_Cleanup(NULL, 0); 120 | return 1; 121 | } 122 | 123 | // begin execution 124 | WriteLog(LOG_INFO, "Launching virtual CPU..."); 125 | for(;;) 126 | { 127 | // resume virtual CPU 128 | memset(&VmExitContext, 0, sizeof(VmExitContext)); 129 | if(HypervisorUtils_ResumeExecution(&VmExitContext) != 0) 130 | { 131 | // error 132 | break; 133 | } 134 | 135 | // caught vmexit - get register values 136 | HypervisorUtils_GetRegisters(&CpuRegisterState); 137 | 138 | // handle vmexit 139 | if(HandleVmExit(pCpuState, &CpuRegisterState, &VmExitContext) != 0) 140 | { 141 | // error (or guest process exited) 142 | break; 143 | } 144 | 145 | // update register values 146 | HypervisorUtils_SetRegisters(&CpuRegisterState); 147 | } 148 | 149 | // clean up - HypervisorUtils_DeleteEnvironment is intentionally skipped here. 150 | // the guest process may have left the CRT in an unknown state after exiting which can lead to deadlocks within the hypervisor platform module. 151 | // clean up local objects and then terminate the process immediately to prevent any potential issues. 152 | StartHypervisor_Cleanup(pCpuState, 1); 153 | TerminateProcess(GetCurrentProcess(), 0); 154 | 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /WinVisorDLL/Misc.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | IMAGE_NT_HEADERS *GetNtHeader(VOID *pModuleBase) 4 | { 5 | IMAGE_DOS_HEADER *pImageDosHeader = NULL; 6 | IMAGE_NT_HEADERS *pImageNtHeader = NULL; 7 | 8 | // get dos header 9 | pImageDosHeader = (IMAGE_DOS_HEADER*)pModuleBase; 10 | if(pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) 11 | { 12 | return NULL; 13 | } 14 | 15 | // get nt header 16 | pImageNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)pModuleBase + pImageDosHeader->e_lfanew); 17 | if(pImageNtHeader->Signature != IMAGE_NT_SIGNATURE) 18 | { 19 | return NULL; 20 | } 21 | 22 | return pImageNtHeader; 23 | } 24 | 25 | DWORD ValidateReadPointer(VOID *pAddress, SIZE_T dwLength) 26 | { 27 | BYTE *pCurrPtr = NULL; 28 | MEMORY_BASIC_INFORMATION MemoryBasicInfo; 29 | 30 | pCurrPtr = (BYTE*)pAddress; 31 | for(SIZE_T i = 0; i < dwLength; i++) 32 | { 33 | if(i == 0 || ((UINT64)pCurrPtr % PAGE_SIZE) == 0) 34 | { 35 | memset(&MemoryBasicInfo, 0, sizeof(MemoryBasicInfo)); 36 | if(VirtualQuery(pCurrPtr, &MemoryBasicInfo, sizeof(MemoryBasicInfo)) != sizeof(MemoryBasicInfo)) 37 | { 38 | return 1; 39 | } 40 | 41 | if(MemoryBasicInfo.State != MEM_COMMIT) 42 | { 43 | return 1; 44 | } 45 | 46 | if(MemoryBasicInfo.Protect & PAGE_NOACCESS) 47 | { 48 | return 1; 49 | } 50 | 51 | if(MemoryBasicInfo.Protect & PAGE_GUARD) 52 | { 53 | return 1; 54 | } 55 | } 56 | 57 | pCurrPtr++; 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | DWORD CopyMemoryAndRestoreProtection(VOID *pDestination, VOID *pSource, DWORD dwLength) 64 | { 65 | DWORD dwOrigProtect = 0; 66 | 67 | // make region writable 68 | if(VirtualProtect(pDestination, dwLength, PAGE_READWRITE, &dwOrigProtect) == 0) 69 | { 70 | return 1; 71 | } 72 | 73 | // copy data 74 | memcpy(pDestination, pSource, dwLength); 75 | 76 | // restore original protection 77 | if(VirtualProtect(pDestination, dwLength, dwOrigProtect, &dwOrigProtect) == 0) 78 | { 79 | return 1; 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | DWORD AppendString(char *pString, SIZE_T dwMaxLength, char *pAppend) 86 | { 87 | SIZE_T dwOrigLength = 0; 88 | SIZE_T dwAppendLength = 0; 89 | SIZE_T dwNewLength = 0; 90 | 91 | // get lengths 92 | dwOrigLength = strlen(pString); 93 | dwAppendLength = strlen(pAppend); 94 | 95 | // validate new length 96 | dwNewLength = dwOrigLength + dwAppendLength; 97 | if(dwNewLength > dwMaxLength) 98 | { 99 | return 1; 100 | } 101 | 102 | // append data 103 | memcpy((pString + dwOrigLength), pAppend, dwAppendLength); 104 | 105 | // add null terminator if the buffer is not full. 106 | // if the maximum length has been reached, it isn't necessary to add a null-terminator. 107 | // it is assumed that the maximum specified length is [sizeof(buffer)-1] and the final character is already a null. 108 | if(dwNewLength != dwMaxLength) 109 | { 110 | *(BYTE*)((BYTE*)pString + dwNewLength) = '\0'; 111 | } 112 | 113 | return 0; 114 | } 115 | 116 | DWORD ExecXGETBV(DWORD dwIndex, QWORD *pqwReturnValue) 117 | { 118 | VOID *pCode = NULL; 119 | UINT64 qwReturnValue = 0; 120 | BYTE bXGETBV[] = 121 | { 122 | // xgetbv 123 | 0x0F, 0x01, 0xD0, 124 | // shl rdx, 0x20 125 | 0x48, 0xC1, 0xE2, 0x20, 126 | // add rdx, rax 127 | 0x48, 0x01, 0xC2, 128 | // mov rax, rdx 129 | 0x48, 0x89, 0xD0, 130 | // ret 131 | 0xC3 132 | }; 133 | 134 | // allocate code 135 | pCode = VirtualAlloc(NULL, sizeof(bXGETBV), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 136 | if(pCode == NULL) 137 | { 138 | return 1; 139 | } 140 | 141 | // execute syscall 142 | memcpy(pCode, bXGETBV, sizeof(bXGETBV)); 143 | qwReturnValue = ((UINT64(*)(DWORD))pCode)(dwIndex); 144 | 145 | // free temporary memory 146 | VirtualFree(pCode, 0, MEM_RELEASE); 147 | 148 | // store return value 149 | *pqwReturnValue = qwReturnValue; 150 | 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /WinVisorDLL/PageFault.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD HandlePageFault_CheckSpecialAddress(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, DWORD *pdwHandled) 4 | { 5 | DWORD dwHandled = 0; 6 | BYTE bInterruptIndex = 0; 7 | InterruptHandlerEntryStruct *pInterruptHandler = NULL; 8 | 9 | // check if the page-fault occurred on a special address 10 | dwHandled = 1; 11 | if(pCpuRegisterState->RIP == SYSCALL_VIRTUAL_ADDRESS) 12 | { 13 | // syscall instruction 14 | if(HandleSyscallInstruction(pCpuState, pCpuRegisterState) != 0) 15 | { 16 | WriteLog(LOG_ERROR, "Failed to handle syscall"); 17 | return 1; 18 | } 19 | } 20 | else if(pCpuRegisterState->RIP == CPL3_ENTRY_VIRTUAL_ADDRESS) 21 | { 22 | WriteLog(LOG_INFO, "Bootloader complete, transitioning to CPL3..."); 23 | 24 | // copy CPL3 entry context 25 | memcpy((void*)pCpuRegisterState, (void*)&pCpuState->CPL3_InitialCpuRegisterState, sizeof(pCpuState->CPL3_InitialCpuRegisterState)); 26 | } 27 | else if((pCpuRegisterState->RIP >= INTERRUPT_HANDLER_VIRTUAL_ADDRESS) && (pCpuRegisterState->RIP < (INTERRUPT_HANDLER_VIRTUAL_ADDRESS + MAX_IDT_ENTRY_COUNT))) 28 | { 29 | // find interrupt handler 30 | bInterruptIndex = (BYTE)(pCpuRegisterState->RIP - INTERRUPT_HANDLER_VIRTUAL_ADDRESS); 31 | pInterruptHandler = GetInterruptHandler(bInterruptIndex); 32 | if(pInterruptHandler == NULL) 33 | { 34 | WriteLog(LOG_ERROR, "Unhandled interrupt: 0x%02X", bInterruptIndex); 35 | return 1; 36 | } 37 | 38 | // execute handler 39 | if(pInterruptHandler->pHandler(pCpuState, pCpuRegisterState) != 0) 40 | { 41 | WriteLog(LOG_ERROR, "Interrupt handler error: 0x%02X", bInterruptIndex); 42 | return 1; 43 | } 44 | 45 | // return from interrupt 46 | pCpuRegisterState->RIP = (UINT64)GetInterruptReturn(pInterruptHandler); 47 | } 48 | else 49 | { 50 | // not handled 51 | dwHandled = 0; 52 | } 53 | 54 | *pdwHandled = dwHandled; 55 | 56 | return 0; 57 | } 58 | 59 | DWORD HandlePageFault(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, UINT64 qwVirtualAddress) 60 | { 61 | DWORD dwHandled = 0; 62 | 63 | // check if this is a special (reserved) address 64 | if(HandlePageFault_CheckSpecialAddress(pCpuState, pCpuRegisterState, &dwHandled) != 0) 65 | { 66 | return 1; 67 | } 68 | 69 | // if not, treat this as a standard page-fault and attempt to page it into memory 70 | if(dwHandled == 0) 71 | { 72 | // WHvMapGpaRange allows an invalid virtual address to be mapped into the guest without any errors - it will only throw an error when it attempts to read from it later. 73 | // manually validate the target address within the current process first - this makes it easier to debug. 74 | if(ValidateReadPointer((void*)qwVirtualAddress, 1) != 0) 75 | { 76 | WriteLog(LOG_ERROR, "Attempt to access invalid virtual address: 0x%p (RIP: 0x%p)", qwVirtualAddress, pCpuRegisterState->RIP); 77 | return 1; 78 | } 79 | 80 | WriteLog(LOG_DEBUG, "Caught page fault: 0x%p (RIP: 0x%p)", qwVirtualAddress, pCpuRegisterState->RIP); 81 | 82 | // add this page to the mapped page table 83 | if(AddPagedVirtualAddress(qwVirtualAddress) != 0) 84 | { 85 | WriteLog(LOG_ERROR, "Failed to add paged virtual address: 0x%p (RIP: 0x%p)", qwVirtualAddress, pCpuRegisterState->RIP); 86 | return 1; 87 | } 88 | } 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /WinVisorDLL/PageTable.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | void *gpPageTableBase = NULL; 4 | DWORD gdwPageTableAllocSize = 0; 5 | 6 | MappedVirtualAddressStruct gPagedVirtualAddressList[MAX_MAPPED_PAGE_COUNT]; 7 | 8 | QWORD gqwCurrPagedVirtualAddressListCreationIndex = 0; 9 | 10 | DWORD GetVirtualAddressTableIndexes(UINT64 qwVirtualAddress, VirtualAddressTableIndexesStruct *pVirtualAddressTableIndexes) 11 | { 12 | // virtual address must be canonical (bits 48-63 must match bit 47) 13 | if((qwVirtualAddress >> 48) != (WORD)(0 - ((qwVirtualAddress >> 47) & 1))) 14 | { 15 | return 1; 16 | } 17 | 18 | // extract page table indexes from virtual address 19 | pVirtualAddressTableIndexes->wOffset = (WORD)(qwVirtualAddress & 0xFFF); 20 | pVirtualAddressTableIndexes->wPT = (WORD)((qwVirtualAddress >> 12) & 0x1FF); 21 | pVirtualAddressTableIndexes->wPD = (WORD)((qwVirtualAddress >> 21) & 0x1FF); 22 | pVirtualAddressTableIndexes->wPDPT = (WORD)((qwVirtualAddress >> 30) & 0x1FF); 23 | pVirtualAddressTableIndexes->wPML4 = (WORD)((qwVirtualAddress >> 39) & 0x1FF); 24 | 25 | return 0; 26 | } 27 | 28 | DWORD ResetPageTable(PageTableStruct *pPageTable) 29 | { 30 | // clear all entries within the specified page table 31 | for(DWORD i = 0; i < 512; i++) 32 | { 33 | pPageTable->qwEntries[i] = 0; 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | PageTableStruct *GetNextTableLevel(PagingStateStruct *pPagingState, PageTableStruct *pPageTable, WORD wIndex) 40 | { 41 | UINT64 qwTempPhysicalAddress = 0; 42 | PageTableStruct *pNextPageTable = NULL; 43 | 44 | // check if a child entry already exists for this index 45 | if(pPageTable->qwEntries[wIndex] != 0) 46 | { 47 | qwTempPhysicalAddress = ((pPageTable->qwEntries[wIndex] >> 12) & 0xFFFFFF) * 0x1000; 48 | pNextPageTable = (PageTableStruct*)((BYTE*)gpPageTableBase + qwTempPhysicalAddress - PAGE_TABLE_BASE_PHYSICAL_ADDRESS); 49 | } 50 | else 51 | { 52 | // create a new entry 53 | if(pPagingState->dwNextEntryIndex >= pPagingState->dwTotalEntryCount) 54 | { 55 | return NULL; 56 | } 57 | pNextPageTable = (PageTableStruct*)((BYTE*)gpPageTableBase + (pPagingState->dwNextEntryIndex * sizeof(PageTableStruct))); 58 | pPageTable->qwEntries[wIndex] = (PAGE_TABLE_BASE_PHYSICAL_ADDRESS + (pPagingState->dwNextEntryIndex * sizeof(PageTableStruct))) | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; 59 | pPagingState->dwNextEntryIndex++; 60 | ResetPageTable(pNextPageTable); 61 | } 62 | 63 | return pNextPageTable; 64 | } 65 | 66 | DWORD CreatePageTables() 67 | { 68 | // allocate page tables (PML4 + all possible tables for next 3 levels) 69 | gdwPageTableAllocSize = sizeof(PageTableStruct) + (MAX_MAPPED_PAGE_COUNT * (3 * sizeof(PageTableStruct))); 70 | gpPageTableBase = VirtualAlloc(NULL, gdwPageTableAllocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 71 | if(gpPageTableBase == NULL) 72 | { 73 | return 1; 74 | } 75 | 76 | // map page tables into the guest at a fixed physical address 77 | if(HypervisorUtils_MapGuestMemory(gpPageTableBase, (void*)PAGE_TABLE_BASE_PHYSICAL_ADDRESS, gdwPageTableAllocSize) != 0) 78 | { 79 | DeletePageTables(); 80 | return 1; 81 | } 82 | 83 | // initialise mapped address list 84 | memset(gPagedVirtualAddressList, 0, sizeof(gPagedVirtualAddressList)); 85 | for(DWORD i = 0; i < MAX_MAPPED_PAGE_COUNT; i++) 86 | { 87 | gPagedVirtualAddressList[i].dwInUse = 0; 88 | gPagedVirtualAddressList[i].qwCreationIndex = 0; 89 | gPagedVirtualAddressList[i].qwVirtualAddress = 0; 90 | gPagedVirtualAddressList[i].qwPhysicalAddress = PAGE_TABLE_BASE_PHYSICAL_ADDRESS + gdwPageTableAllocSize + (i * PAGE_SIZE); 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | DWORD DeletePageTables() 97 | { 98 | if(gpPageTableBase != NULL) 99 | { 100 | // free memory 101 | VirtualFree(gpPageTableBase, 0, MEM_RELEASE); 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | DWORD RebuildPageTables() 108 | { 109 | PageTableStruct *pPML4 = NULL; 110 | PageTableStruct *pPDPT = NULL; 111 | PageTableStruct *pPD = NULL; 112 | PageTableStruct *pPT = NULL; 113 | VirtualAddressTableIndexesStruct VirtualAddressTableIndexes; 114 | PagingStateStruct PagingState; 115 | 116 | // reset PML4 117 | pPML4 = (PageTableStruct*)gpPageTableBase; 118 | ResetPageTable(pPML4); 119 | 120 | // initialise state 121 | memset(&PagingState, 0, sizeof(PagingState)); 122 | PagingState.dwTotalEntryCount = (gdwPageTableAllocSize / 0x1000); 123 | PagingState.dwNextEntryIndex = 1; 124 | 125 | // rebuild page tables 126 | for(DWORD i = 0; i < MAX_MAPPED_PAGE_COUNT; i++) 127 | { 128 | if(gPagedVirtualAddressList[i].dwInUse == 0) 129 | { 130 | continue; 131 | } 132 | 133 | // extract table indexes from current virtual address 134 | memset(&VirtualAddressTableIndexes, 0, sizeof(VirtualAddressTableIndexes)); 135 | if(GetVirtualAddressTableIndexes(gPagedVirtualAddressList[i].qwVirtualAddress, &VirtualAddressTableIndexes) != 0) 136 | { 137 | return 1; 138 | } 139 | 140 | // navigate to the final level of the paging table 141 | pPDPT = GetNextTableLevel(&PagingState, pPML4, VirtualAddressTableIndexes.wPML4); 142 | pPD = GetNextTableLevel(&PagingState, pPDPT, VirtualAddressTableIndexes.wPDPT); 143 | pPT = GetNextTableLevel(&PagingState, pPD, VirtualAddressTableIndexes.wPD); 144 | 145 | // set mirrored page physical address 146 | pPT->qwEntries[VirtualAddressTableIndexes.wPT] = gPagedVirtualAddressList[i].qwPhysicalAddress | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER; 147 | } 148 | 149 | // flush TLB 150 | HypervisorUtils_FlushTLB(); 151 | 152 | return 0; 153 | } 154 | 155 | DWORD AddPagedVirtualAddress(UINT64 qwVirtualAddress) 156 | { 157 | QWORD qwVirtualAddressPage = 0; 158 | MappedVirtualAddressStruct *pFreeEntry = NULL; 159 | MappedVirtualAddressStruct *pOldestEntry = NULL; 160 | 161 | // round virtual address down to page base 162 | qwVirtualAddressPage = qwVirtualAddress & ~0xFFF; 163 | 164 | // check if this page is already mapped 165 | for(DWORD i = 0; i < MAX_MAPPED_PAGE_COUNT; i++) 166 | { 167 | if(gPagedVirtualAddressList[i].dwInUse == 0) 168 | { 169 | continue; 170 | } 171 | 172 | if(gPagedVirtualAddressList[i].qwVirtualAddress == qwVirtualAddressPage) 173 | { 174 | // this entry is already paged in - unknown error 175 | return 1; 176 | } 177 | } 178 | 179 | // check for a free entry in the list 180 | for(DWORD i = 0; i < MAX_MAPPED_PAGE_COUNT; i++) 181 | { 182 | if(gPagedVirtualAddressList[i].dwInUse == 0) 183 | { 184 | pFreeEntry = &gPagedVirtualAddressList[i]; 185 | break; 186 | } 187 | } 188 | 189 | if(pFreeEntry == NULL) 190 | { 191 | // list is full - find the oldest entry and remove it 192 | for(DWORD i = 0; i < MAX_MAPPED_PAGE_COUNT; i++) 193 | { 194 | if(gPagedVirtualAddressList[i].dwInUse == 0) 195 | { 196 | continue; 197 | } 198 | 199 | if(pOldestEntry == NULL) 200 | { 201 | pOldestEntry = &gPagedVirtualAddressList[i]; 202 | } 203 | else 204 | { 205 | if(gPagedVirtualAddressList[i].qwCreationIndex < pOldestEntry->qwCreationIndex) 206 | { 207 | pOldestEntry = &gPagedVirtualAddressList[i]; 208 | } 209 | } 210 | } 211 | 212 | if(pOldestEntry == NULL) 213 | { 214 | return 1; 215 | } 216 | 217 | // remove oldest entry 218 | HypervisorUtils_UnmapGuestMemory((void*)pOldestEntry->qwPhysicalAddress, PAGE_SIZE); 219 | pOldestEntry->dwInUse = 0; 220 | pFreeEntry = pOldestEntry; 221 | } 222 | 223 | // map page into guest 224 | if(HypervisorUtils_MapGuestMemory((void*)qwVirtualAddressPage, (void*)pFreeEntry->qwPhysicalAddress, PAGE_SIZE) != 0) 225 | { 226 | return 1; 227 | } 228 | 229 | // store current entry in list 230 | pFreeEntry->dwInUse = 1; 231 | pFreeEntry->qwVirtualAddress = qwVirtualAddressPage; 232 | pFreeEntry->qwCreationIndex = gqwCurrPagedVirtualAddressListCreationIndex; 233 | gqwCurrPagedVirtualAddressListCreationIndex++; 234 | 235 | // rebuild page tables 236 | if(RebuildPageTables() != 0) 237 | { 238 | return 1; 239 | } 240 | 241 | return 0; 242 | } 243 | -------------------------------------------------------------------------------- /WinVisorDLL/PrepareCPL0.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | BootloaderParamsStruct gCPL0_BootloaderParams; 4 | 5 | BYTE gbCPL0_BootloaderCode[] = 6 | { 7 | // (store BootloaderParams ptr) 8 | // mov rdi, rcx 9 | 0x48, 0x89, 0xCF, 10 | 11 | // lgdt tword ptr [rdi + 0x06] (BootloaderParams.qwGDT_Limit + BootloaderParams.qwGDT_Base) 12 | 0x0F, 0x01, 0x57, 0x06, 13 | 14 | // lidt tword ptr [rdi + 0x16] (BootloaderParams.qwIDT_Limit + BootloaderParams.qwIDT_Base) 15 | 0x0F, 0x01, 0x5F, 0x16, 16 | 17 | // (set TSS selector index) 18 | // mov ax, word ptr [rdi + 0x20] (BootloaderParams.qwTSS_Selector) 19 | 0x66, 0x8B, 0x47, 0x20, 20 | // ltr ax 21 | 0x0F, 0x00, 0xD8, 22 | 23 | // (update XCR0 value to match host - this enables AVX etc) 24 | // xor rcx, rcx 25 | 0x48, 0x31, 0xC9, 26 | // mov edx, dword ptr [rdi + 0x2C] (BootloaderParams.qwXCR0 - HIGH) 27 | 0x8B, 0x57, 0x2C, 28 | // mov eax, dword ptr [rdi + 0x28] (BootloaderParams.qwXCR0 - LOW) 29 | 0x8B, 0x47, 0x28, 30 | // xsetbv 31 | 0x0F, 0x01, 0xD1, 32 | 33 | // (enter CPL3 code) 34 | // mov ax, word ptr [rdi + 0x30] (BootloaderParams.qwCPL3_DataSelector) 35 | 0x66, 0x8B, 0x47, 0x30, 36 | // mov ds, ax 37 | 0x66, 0x8E, 0xD8, 38 | // mov es, ax 39 | 0x66, 0x8E, 0xC0, 40 | // mov gs, ax 41 | 0x66, 0x8E, 0xE8, 42 | // swapgs 43 | 0x0F, 0x01, 0xF8, 44 | // mov rcx, qword ptr [rdi + 0x40] (BootloaderParams.qwCPL3_EntryPlaceholderAddress) 45 | 0x48, 0x8B, 0x4F, 0x40, 46 | // mov r11, qword ptr [rdi + 0x38] (BootloaderParams.qwCPL3_RFLAGS) 47 | 0x4C, 0x8B, 0x5F, 0x38, 48 | // sysret 49 | 0x48, 0x0F, 0x07 50 | }; 51 | 52 | DWORD PrepareCPL0(CpuStateStruct *pCpuState) 53 | { 54 | CpuRegisterStateStruct InitialCpuRegisterState; 55 | WORD wCodeSelector = 0; 56 | UINT64 qwHandlerAddress = 0; 57 | UINT64 qwXCR0 = 0; 58 | 59 | // allocate CPL0 stack 60 | pCpuState->pCPL0_Stack = VirtualAlloc(NULL, CPL0_STACK_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 61 | if(pCpuState->pCPL0_Stack == NULL) 62 | { 63 | return 1; 64 | } 65 | 66 | // create GDT 67 | // 0 / 0x0000 (null) 68 | // 1 / 0x0008 (reserved) 69 | // 2 / 0x0010 (ring0 code) 70 | // 3 / 0x0018 (ring0 data) 71 | // 4 / 0x0020 (reserved - wow64) 72 | // 5 / 0x0028 (ring3 data) 73 | // 6 / 0x0030 (ring3 code) 74 | // 7 / 0x0038 (reserved) 75 | // 8 / 0x0040 (TSS) 76 | // 9 / 0x0048 (TSS continued) 77 | // 10 / 0x0050 (reserved - wow64) 78 | pCpuState->GDT[0] = 0; 79 | pCpuState->GDT[1] = 0; 80 | pCpuState->GDT[2] = GDT_PRESENT | GDT_DPL0 | GDT_NON_SYSTEM | GDT_CODE | GDT_CODE_READ | GDT_ACCESSED | GDT_LONG; 81 | pCpuState->GDT[3] = GDT_PRESENT | GDT_DPL0 | GDT_NON_SYSTEM | GDT_DATA | GDT_DATA_WRITE | GDT_ACCESSED; 82 | pCpuState->GDT[4] = 0; 83 | pCpuState->GDT[5] = GDT_PRESENT | GDT_DPL3 | GDT_NON_SYSTEM | GDT_DATA | GDT_DATA_WRITE | GDT_ACCESSED; 84 | pCpuState->GDT[6] = GDT_PRESENT | GDT_DPL3 | GDT_NON_SYSTEM | GDT_CODE | GDT_CODE_READ | GDT_ACCESSED | GDT_LONG; 85 | pCpuState->GDT[7] = 0; 86 | pCpuState->GDT[8] = GDT_PRESENT | GDT_TSS | GDT_DB | (sizeof(pCpuState->TSS) - 1) | (((UINT64)&pCpuState->TSS[0] & 0xFFFFFF) << 16) | ((((UINT64)&pCpuState->TSS[0] >> 24) & 0xFF) << 56); 87 | pCpuState->GDT[9] = ((UINT64)&pCpuState->TSS[0] >> 32); 88 | pCpuState->GDT[10] = 0; 89 | 90 | // create IDT - set all interrupts to placeholder address (INTERRUPT_HANDLER_VIRTUAL_ADDRESS + index) 91 | for(DWORD i = 0; i < MAX_IDT_ENTRY_COUNT; i++) 92 | { 93 | // set current entry 94 | wCodeSelector = SEGMENT_SELECTOR_CODE_CPL0; 95 | qwHandlerAddress = INTERRUPT_HANDLER_VIRTUAL_ADDRESS + i; 96 | pCpuState->IDT[i].Low = (qwHandlerAddress & 0xFFFF) | (((qwHandlerAddress >> 16) & 0xFFFF) << 48) | (wCodeSelector << 16) | IDT_INTERRUPT_GATE | IDT_DPL3 | IDT_PRESENT; 97 | pCpuState->IDT[i].High = (qwHandlerAddress >> 32); 98 | } 99 | 100 | // set TSS values (RSP0 only) 101 | memset(pCpuState->TSS, 0, sizeof(pCpuState->TSS)); 102 | *(UINT64*)&pCpuState->TSS[4] = (UINT64)pCpuState->pCPL0_Stack + CPL0_STACK_SIZE; 103 | 104 | // set control registers 105 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterCr0, CR0_PROTECTED_MODE | CR0_PAGING | CR0_COPROCESSOR_MONITORING); 106 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterCr3, PAGE_TABLE_BASE_PHYSICAL_ADDRESS); 107 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterCr4, CR4_PAE | CR4_OSFXSR | CR4_OSXSAVE); 108 | 109 | // set EFER 110 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterEfer, EFER_SYSCALL_ENABLE | EFER_LONG_MODE_ENABLE | EFER_LONG_MODE_ACTIVE | EFER_NX_ENABLE); 111 | 112 | // set STAR/LSTAR MSRs for syscalls 113 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterStar, ((UINT64)SEGMENT_SELECTOR_CODE_CPL0 << 32) | ((UINT64)(SEGMENT_SELECTOR_CODE_CPL3 - 0x10) << 48)); 114 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterLstar, SYSCALL_VIRTUAL_ADDRESS); 115 | 116 | // set KERNEL_GS_BASE to TEB base (swapgs will swap this value to user-mode later) 117 | HypervisorUtils_SetRegisterValue_U64(WHvX64RegisterKernelGsBase, (UINT64)pCpuState->pHostThreadTEB); 118 | 119 | // set CPL0 selectors 120 | HypervisorUtils_SetRegisterValue_Segment(WHvX64RegisterCs, SEGMENT_SELECTOR_CODE_CPL0, 1); 121 | HypervisorUtils_SetRegisterValue_Segment(WHvX64RegisterSs, SEGMENT_SELECTOR_DATA_CPL0, 0); 122 | 123 | // get XCR0 from host 124 | if(ExecXGETBV(0, &qwXCR0) != 0) 125 | { 126 | return 1; 127 | } 128 | 129 | // set bootloader params 130 | memset(&gCPL0_BootloaderParams, 0, sizeof(gCPL0_BootloaderParams)); 131 | gCPL0_BootloaderParams.qwGDT_Limit = (sizeof(pCpuState->GDT) - 1) << TABLE_REGISTER_LIMIT_SHIFT; 132 | gCPL0_BootloaderParams.qwGDT_Base = (UINT64)&pCpuState->GDT[0]; 133 | gCPL0_BootloaderParams.qwIDT_Limit = (sizeof(pCpuState->IDT) - 1) << TABLE_REGISTER_LIMIT_SHIFT; 134 | gCPL0_BootloaderParams.qwIDT_Base = (UINT64)&pCpuState->IDT[0]; 135 | gCPL0_BootloaderParams.qwTSS_Selector = SEGMENT_SELECTOR_TSS; 136 | gCPL0_BootloaderParams.qwCPL3_DataSelector = SEGMENT_SELECTOR_DATA_CPL3; 137 | gCPL0_BootloaderParams.qwCPL3_RFLAGS = CPL3_INITIAL_RFLAGS; 138 | gCPL0_BootloaderParams.qwCPL3_EntryPlaceholderAddress = CPL3_ENTRY_VIRTUAL_ADDRESS; 139 | gCPL0_BootloaderParams.qwXCR0 = qwXCR0; 140 | 141 | // execute CPL0 bootloader first 142 | memset(&InitialCpuRegisterState, 0, sizeof(InitialCpuRegisterState)); 143 | InitialCpuRegisterState.RFLAGS = EFLAGS_RESERVED_ALWAYS_ON; 144 | InitialCpuRegisterState.RIP = (UINT64)gbCPL0_BootloaderCode; 145 | InitialCpuRegisterState.RCX = (UINT64)&gCPL0_BootloaderParams; 146 | 147 | // set initial registers 148 | HypervisorUtils_SetRegisters(&InitialCpuRegisterState); 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /WinVisorDLL/PrepareCPL3.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | HMODULE ghExeBase = NULL; 4 | 5 | DWORD PrepareCPL3_FixExecutable(WinVisorStartDataStruct *pWinVisorStartData, VOID **ppEntryPoint) 6 | { 7 | IMAGE_NT_HEADERS64 *pExeNtHeader = NULL; 8 | VOID *pEntryPoint = NULL; 9 | IMAGE_DATA_DIRECTORY *pExeDataDirectory = NULL; 10 | IMAGE_DATA_DIRECTORY *pOrigExeDataDirectory = NULL; 11 | DWORD dwOrigProtect = 0; 12 | 13 | // get entry-point for main executable 14 | pExeNtHeader = GetNtHeader(ghExeBase); 15 | if(pExeNtHeader == NULL) 16 | { 17 | return 1; 18 | } 19 | pEntryPoint = (BYTE*)ghExeBase + pExeNtHeader->OptionalHeader.AddressOfEntryPoint; 20 | 21 | // check if the "nx" command-line switch was specified 22 | if(pWinVisorStartData->qwWinVisorFlags & WINVISOR_FLAG_NX) 23 | { 24 | // remove executable flag from all pages within the exe image 25 | if(VirtualProtect(ghExeBase, pExeNtHeader->OptionalHeader.SizeOfImage, PAGE_READWRITE, &dwOrigProtect) == 0) 26 | { 27 | return 1; 28 | } 29 | } 30 | 31 | // restore original entry-point code (this was temporarily overwritten by the WinVisor exe to load this DLL) 32 | if(CopyMemoryAndRestoreProtection(pEntryPoint, pWinVisorStartData->bOrigEntryPointCode, sizeof(pWinVisorStartData->bOrigEntryPointCode)) != 0) 33 | { 34 | return 1; 35 | } 36 | 37 | // restore import table data directory entry 38 | pExeDataDirectory = &pExeNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 39 | pOrigExeDataDirectory = &pWinVisorStartData->OrigNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 40 | if(CopyMemoryAndRestoreProtection(pExeDataDirectory, pOrigExeDataDirectory, sizeof(IMAGE_DATA_DIRECTORY)) != 0) 41 | { 42 | return 1; 43 | } 44 | 45 | // restore TLS data directory entry 46 | pExeDataDirectory = &pExeNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 47 | pOrigExeDataDirectory = &pWinVisorStartData->OrigNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; 48 | if(CopyMemoryAndRestoreProtection(pExeDataDirectory, pOrigExeDataDirectory, sizeof(IMAGE_DATA_DIRECTORY)) != 0) 49 | { 50 | return 1; 51 | } 52 | 53 | // store entry-point address 54 | *ppEntryPoint = pEntryPoint; 55 | 56 | return 0; 57 | } 58 | 59 | DWORD PrepareCPL3_GetHostThreadStackInfo(CpuStateStruct *pCpuState, VOID **ppStackAllocBase, DWORD *pdwTotalStackSize) 60 | { 61 | NT_TIB *pTIB = NULL; 62 | MEMORY_BASIC_INFORMATION MemoryBasicInfo; 63 | DWORD dwTotalStackSize = 0; 64 | 65 | // calculate total stack size 66 | pTIB = (NT_TIB*)pCpuState->pHostThreadTEB; 67 | memset(&MemoryBasicInfo, 0, sizeof(MemoryBasicInfo)); 68 | if(VirtualQuery(pTIB->StackLimit, &MemoryBasicInfo, sizeof(MemoryBasicInfo)) != sizeof(MemoryBasicInfo)) 69 | { 70 | return 1; 71 | } 72 | 73 | // calculate total stack size 74 | dwTotalStackSize = (DWORD)((UINT64)pTIB->StackBase - (UINT64)MemoryBasicInfo.AllocationBase); 75 | 76 | // store base/size 77 | *ppStackAllocBase = MemoryBasicInfo.AllocationBase; 78 | *pdwTotalStackSize = dwTotalStackSize; 79 | 80 | return 0; 81 | } 82 | 83 | DWORD PrepareCPL3_CreateHostThread(CpuStateStruct *pCpuState) 84 | { 85 | THREAD_BASIC_INFORMATION ThreadBasicInfo; 86 | 87 | // create thread for virtual CPU (entry-point will be overwritten later) 88 | pCpuState->hHostThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ExitThread, (VOID*)0, CREATE_SUSPENDED, NULL); 89 | if(pCpuState->hHostThread == NULL) 90 | { 91 | return 1; 92 | } 93 | 94 | // get TEB base 95 | memset(&ThreadBasicInfo, 0, sizeof(ThreadBasicInfo)); 96 | if(pNtQueryInformationThread(pCpuState->hHostThread, ThreadBasicInformation, &ThreadBasicInfo, sizeof(ThreadBasicInfo), NULL) != 0) 97 | { 98 | return 1; 99 | } 100 | 101 | // store TEB ptr 102 | pCpuState->pHostThreadTEB = ThreadBasicInfo.TebBaseAddress; 103 | 104 | return 0; 105 | } 106 | 107 | DWORD PrepareCPL3_StoreInitialGuestContext(CpuStateStruct *pCpuState, VOID *pEntryPoint) 108 | { 109 | CONTEXT Context; 110 | VOID *pStackAllocBase = NULL; 111 | DWORD dwTotalStackSize = 0; 112 | 113 | // get stack base/size for the new thread 114 | if(PrepareCPL3_GetHostThreadStackInfo(pCpuState, &pStackAllocBase, &dwTotalStackSize) != 0) 115 | { 116 | return 1; 117 | } 118 | 119 | // get thread context 120 | memset(&Context, 0, sizeof(Context)); 121 | Context.ContextFlags = CONTEXT_FULL; 122 | if(GetThreadContext(pCpuState->hHostThread, &Context) == 0) 123 | { 124 | return 1; 125 | } 126 | 127 | // create a second stack to allow code to be executed in real thread without interfering with guest 128 | pCpuState->pCPL3_Stack = VirtualAlloc(NULL, dwTotalStackSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 129 | if(pCpuState->pCPL3_Stack == NULL) 130 | { 131 | return 1; 132 | } 133 | 134 | // set initial CPL3 register values 135 | memset(&pCpuState->CPL3_InitialCpuRegisterState, 0, sizeof(pCpuState->CPL3_InitialCpuRegisterState)); 136 | pCpuState->CPL3_InitialCpuRegisterState.RAX = Context.Rax; 137 | pCpuState->CPL3_InitialCpuRegisterState.RCX = Context.Rcx; 138 | pCpuState->CPL3_InitialCpuRegisterState.RDX = Context.Rdx; 139 | pCpuState->CPL3_InitialCpuRegisterState.RBX = Context.Rbx; 140 | pCpuState->CPL3_InitialCpuRegisterState.RSP = Context.Rsp; 141 | pCpuState->CPL3_InitialCpuRegisterState.RBP = Context.Rbp; 142 | pCpuState->CPL3_InitialCpuRegisterState.RSI = Context.Rsi; 143 | pCpuState->CPL3_InitialCpuRegisterState.RDI = Context.Rdi; 144 | pCpuState->CPL3_InitialCpuRegisterState.R8 = Context.R8; 145 | pCpuState->CPL3_InitialCpuRegisterState.R9 = Context.R9; 146 | pCpuState->CPL3_InitialCpuRegisterState.R10 = Context.R10; 147 | pCpuState->CPL3_InitialCpuRegisterState.R11 = Context.R11; 148 | pCpuState->CPL3_InitialCpuRegisterState.R12 = Context.R12; 149 | pCpuState->CPL3_InitialCpuRegisterState.R13 = Context.R13; 150 | pCpuState->CPL3_InitialCpuRegisterState.R14 = Context.R14; 151 | pCpuState->CPL3_InitialCpuRegisterState.R15 = Context.R15; 152 | pCpuState->CPL3_InitialCpuRegisterState.RIP = Context.Rip; 153 | pCpuState->CPL3_InitialCpuRegisterState.RFLAGS = Context.EFlags | EFLAGS_RESERVED_ALWAYS_ON; 154 | 155 | // update virtual CPU entry-point: HypervisorEntryPoint(pEntryPoint) 156 | pCpuState->CPL3_InitialCpuRegisterState.RCX = (UINT64)pEntryPoint; 157 | pCpuState->CPL3_InitialCpuRegisterState.RIP = (UINT64)HypervisorEntryPoint; 158 | 159 | // update guest stack ptr 160 | pCpuState->CPL3_InitialCpuRegisterState.RSP -= (UINT64)pStackAllocBase; 161 | pCpuState->CPL3_InitialCpuRegisterState.RSP += (UINT64)pCpuState->pCPL3_Stack; 162 | 163 | return 0; 164 | } 165 | 166 | DWORD PrepareCPL3_BeginSyscallProxyThread(CpuStateStruct *pCpuState) 167 | { 168 | CONTEXT Context; 169 | 170 | // get thread context 171 | memset(&Context, 0, sizeof(Context)); 172 | Context.ContextFlags = CONTEXT_FULL; 173 | if(GetThreadContext(pCpuState->hHostThread, &Context) == 0) 174 | { 175 | return 1; 176 | } 177 | 178 | // update the entry-point of the host thread to: SyscallProxyThread(pCpuState) 179 | Context.Rcx = (UINT64)SyscallProxyThread; 180 | Context.Rdx = (UINT64)pCpuState; 181 | if(SetThreadContext(pCpuState->hHostThread, &Context) == 0) 182 | { 183 | return 1; 184 | } 185 | 186 | // create hSyscallProxyReadyEvent event object 187 | pCpuState->hSyscallProxyReadyEvent = CreateEvent(NULL, 0, 0, NULL); 188 | if(pCpuState->hSyscallProxyReadyEvent == NULL) 189 | { 190 | return 1; 191 | } 192 | 193 | // create hSyscallWaitingEvent event object 194 | pCpuState->hSyscallWaitingEvent = CreateEvent(NULL, 0, 0, NULL); 195 | if(pCpuState->hSyscallWaitingEvent == NULL) 196 | { 197 | return 1; 198 | } 199 | 200 | // create hSyscallCompleteEvent event object 201 | pCpuState->hSyscallCompleteEvent = CreateEvent(NULL, 0, 0, NULL); 202 | if(pCpuState->hSyscallCompleteEvent == NULL) 203 | { 204 | return 1; 205 | } 206 | 207 | // begin syscall proxy thread and wait for the "ready" event to be triggered 208 | ResumeThread(pCpuState->hHostThread); 209 | WaitForSingleObject(pCpuState->hSyscallProxyReadyEvent, INFINITE); 210 | 211 | return 0; 212 | } 213 | 214 | DWORD PrepareCPL3(CpuStateStruct *pCpuState, WinVisorStartDataStruct *pWinVisorStartData) 215 | { 216 | VOID *pEntryPoint = NULL; 217 | 218 | // store exe base 219 | ghExeBase = GetModuleHandleA(NULL); 220 | 221 | // fix target executable - restore original entry-point, and optionally set all pages to non-executable 222 | if(PrepareCPL3_FixExecutable(pWinVisorStartData, &pEntryPoint) != 0) 223 | { 224 | return 1; 225 | } 226 | 227 | // create suspended host thread at the original entry-point 228 | if(PrepareCPL3_CreateHostThread(pCpuState) != 0) 229 | { 230 | return 1; 231 | } 232 | 233 | // copy the initial thread state into a temporary structure which will be used by the virtual cpu later, and allocate a new stack for the guest 234 | if(PrepareCPL3_StoreInitialGuestContext(pCpuState, pEntryPoint) != 0) 235 | { 236 | return 1; 237 | } 238 | 239 | // convert the host thread into a syscall proxy thread - this will be used to forward syscalls from the guest to the host. 240 | // using the same thread ensures that the TEB and any thread-specific behaviour will remain consistent with the guest. 241 | // as mentioned above, a second stack has been allocated for the guest to prevent it from interfering with the native host thread. 242 | if(PrepareCPL3_BeginSyscallProxyThread(pCpuState) != 0) 243 | { 244 | return 1; 245 | } 246 | 247 | return 0; 248 | } 249 | -------------------------------------------------------------------------------- /WinVisorDLL/SyscallHook_NtTerminateProcess.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD GuestProcessExited(DWORD dwExitCode) 4 | { 5 | WriteLog(LOG_INFO, "** Guest process exited with code: %u **", dwExitCode); 6 | gdwStopLog = 1; 7 | 8 | return 0; 9 | } 10 | 11 | DWORD SyscallHook_NtTerminateProcess(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue) 12 | { 13 | HANDLE hProcess = NULL; 14 | DWORD dwExitCode = 0; 15 | 16 | // get params 17 | hProcess = (HANDLE)pSyscallInfo->qwParamList[0]; 18 | dwExitCode = (DWORD)pSyscallInfo->qwParamList[1]; 19 | 20 | // check if the current process is exiting 21 | if(hProcess == NULL || hProcess == GetCurrentProcess()) 22 | { 23 | GuestProcessExited(dwExitCode); 24 | return 1; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /WinVisorDLL/SyscallHook_NtTerminateThread.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD SyscallHook_NtTerminateThread(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue) 4 | { 5 | HANDLE hThread = NULL; 6 | DWORD dwExitCode = 0; 7 | 8 | // get params 9 | hThread = (HANDLE)pSyscallInfo->qwParamList[0]; 10 | dwExitCode = (DWORD)pSyscallInfo->qwParamList[1]; 11 | 12 | // check if the current thread is exiting 13 | if(hThread == NULL || hThread == GetCurrentThread()) 14 | { 15 | // current thread is exiting - treat this as a process exit because winvisor currently only supports a single thread 16 | GuestProcessExited(dwExitCode); 17 | return 1; 18 | } 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /WinVisorDLL/SyscallNames.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | SyscallNameEntryStruct *gpNtdllSyscallList = NULL; 4 | DWORD gdwNtdllSyscallCount = 0; 5 | 6 | SyscallNameEntryStruct *gpWin32uSyscallList = NULL; 7 | DWORD gdwWin32uSyscallCount = 0; 8 | 9 | int GenerateSyscallNameList_Compare(SyscallNameEntryStruct *pEntry1, SyscallNameEntryStruct *pEntry2) 10 | { 11 | // compare virtual address values 12 | if(pEntry1->dwVirtualAddress > pEntry2->dwVirtualAddress) 13 | { 14 | return 1; 15 | } 16 | else if(pEntry1->dwVirtualAddress < pEntry2->dwVirtualAddress) 17 | { 18 | return -1; 19 | } 20 | 21 | return 0; 22 | } 23 | 24 | SyscallNameEntryStruct *GenerateSyscallNameList(HMODULE hModule, char *pExportNamePrefix, DWORD *pdwSyscallCount) 25 | { 26 | IMAGE_NT_HEADERS *pImageNtHeader = NULL; 27 | IMAGE_DATA_DIRECTORY *pExportDataDirectory = NULL; 28 | IMAGE_EXPORT_DIRECTORY *pExportHeader = NULL; 29 | char *pExportName = NULL; 30 | WORD *pwAddressOfNameOrdinals = NULL; 31 | DWORD *pdwAddressOfNames = NULL; 32 | DWORD *pdwAddressOfFunctions = NULL; 33 | DWORD dwCount = 0; 34 | SyscallNameEntryStruct *pSyscallNameList = NULL; 35 | DWORD dwCurrIndex = 0; 36 | 37 | // get NT header for target module 38 | pImageNtHeader = GetNtHeader(hModule); 39 | if(pImageNtHeader == NULL) 40 | { 41 | return NULL; 42 | } 43 | 44 | // get export directory 45 | pExportDataDirectory = &pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 46 | if(pExportDataDirectory->VirtualAddress == 0 || pExportDataDirectory->Size == 0) 47 | { 48 | return NULL; 49 | } 50 | 51 | // get export directory 52 | pExportHeader = (IMAGE_EXPORT_DIRECTORY*)((BYTE*)hModule + pExportDataDirectory->VirtualAddress); 53 | if(pExportHeader == NULL) 54 | { 55 | return NULL; 56 | } 57 | 58 | // get export header virtual addresses 59 | pwAddressOfNameOrdinals = (WORD*)((BYTE*)hModule + pExportHeader->AddressOfNameOrdinals); 60 | pdwAddressOfNames = (DWORD*)((BYTE*)hModule + pExportHeader->AddressOfNames); 61 | pdwAddressOfFunctions = (DWORD*)((BYTE*)hModule + pExportHeader->AddressOfFunctions); 62 | 63 | // loop through all exports 64 | for(DWORD i = 0; i < pExportHeader->NumberOfNames; i++) 65 | { 66 | // get export name 67 | pExportName = (char*)((BYTE*)hModule + pdwAddressOfNames[i]); 68 | if(strncmp(pExportName, pExportNamePrefix, strlen(pExportNamePrefix)) != 0) 69 | { 70 | continue; 71 | } 72 | 73 | // increase count 74 | dwCount++; 75 | } 76 | 77 | // allocate list 78 | pSyscallNameList = (SyscallNameEntryStruct*)VirtualAlloc(NULL, dwCount * sizeof(SyscallNameEntryStruct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 79 | if(pSyscallNameList == NULL) 80 | { 81 | return NULL; 82 | } 83 | 84 | // copy export names to list 85 | dwCurrIndex = 0; 86 | for(DWORD i = 0; i < pExportHeader->NumberOfNames; i++) 87 | { 88 | // get export name 89 | pExportName = (char*)((BYTE*)hModule + pdwAddressOfNames[i]); 90 | if(strncmp(pExportName, pExportNamePrefix, strlen(pExportNamePrefix)) != 0) 91 | { 92 | continue; 93 | } 94 | 95 | // store syscall name 96 | memset(&pSyscallNameList[dwCurrIndex], 0, sizeof(SyscallNameEntryStruct)); 97 | strncpy(pSyscallNameList[dwCurrIndex].szName, pExportName, sizeof(pSyscallNameList[dwCurrIndex].szName) - 1); 98 | pSyscallNameList[dwCurrIndex].dwVirtualAddress = pdwAddressOfFunctions[pwAddressOfNameOrdinals[i]]; 99 | 100 | // fix syscall name (Zw - > Nt) 101 | pSyscallNameList[dwCurrIndex].szName[0] = 'N'; 102 | pSyscallNameList[dwCurrIndex].szName[1] = 't'; 103 | 104 | dwCurrIndex++; 105 | } 106 | 107 | // sort list by virtual address 108 | qsort(pSyscallNameList, dwCount, sizeof(SyscallNameEntryStruct), (int(*)(const void*,const void*))GenerateSyscallNameList_Compare); 109 | 110 | // store count 111 | *pdwSyscallCount = dwCount; 112 | 113 | return pSyscallNameList; 114 | } 115 | 116 | DWORD CreateSyscallLists() 117 | { 118 | HMODULE hWin32u = NULL; 119 | 120 | // generate syscall name list for ntdll.dll (use "Zw" prefix to filter out non-syscalls such as NtdllDefWindowProc_A) 121 | gpNtdllSyscallList = GenerateSyscallNameList(ghNtdllBase, "Zw", &gdwNtdllSyscallCount); 122 | if(gpNtdllSyscallList == NULL) 123 | { 124 | DeleteSyscallLists(); 125 | return 1; 126 | } 127 | 128 | // temporarily load win32u.dll 129 | hWin32u = LoadLibraryA("win32u.dll"); 130 | if(hWin32u == NULL) 131 | { 132 | DeleteSyscallLists(); 133 | return 1; 134 | } 135 | 136 | // generate syscall name list for win32u 137 | gpWin32uSyscallList = GenerateSyscallNameList(hWin32u, "Nt", &gdwWin32uSyscallCount); 138 | if(gpWin32uSyscallList == NULL) 139 | { 140 | FreeLibrary(hWin32u); 141 | DeleteSyscallLists(); 142 | return 1; 143 | } 144 | 145 | // unload win32u.dll 146 | FreeLibrary(hWin32u); 147 | 148 | // attempt to populate param counts via wow64 - don't check for errors here, these param counts are used for display purposes only 149 | PopulateSyscallParamCounts("ntdll.dll", gpNtdllSyscallList, gdwNtdllSyscallCount); 150 | PopulateSyscallParamCounts("win32u.dll", gpWin32uSyscallList, gdwWin32uSyscallCount); 151 | 152 | return 0; 153 | } 154 | 155 | DWORD DeleteSyscallLists() 156 | { 157 | if(gpNtdllSyscallList != NULL) 158 | { 159 | // free memory 160 | VirtualFree(gpNtdllSyscallList, 0, MEM_RELEASE); 161 | } 162 | 163 | if(gpWin32uSyscallList != NULL) 164 | { 165 | // free memory 166 | VirtualFree(gpWin32uSyscallList, 0, MEM_RELEASE); 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | char *GetSyscallName(DWORD dwSyscallIndex, DWORD *pdwParamCount) 173 | { 174 | DWORD dwTableIndex = 0; 175 | DWORD dwEntryIndex = 0; 176 | SyscallNameEntryStruct *pSyscallEntry = NULL; 177 | 178 | // extract table and entry index from syscall number 179 | dwTableIndex = (dwSyscallIndex >> 12) & 0x3; 180 | dwEntryIndex = dwSyscallIndex & 0xFFF; 181 | 182 | if(dwTableIndex == 0) 183 | { 184 | // ntdll / ntoskrnl 185 | if(dwEntryIndex >= gdwNtdllSyscallCount) 186 | { 187 | return NULL; 188 | } 189 | 190 | pSyscallEntry = &gpNtdllSyscallList[dwEntryIndex]; 191 | } 192 | else if(dwTableIndex == 1) 193 | { 194 | // win32u / win32k 195 | if(dwEntryIndex >= gdwWin32uSyscallCount) 196 | { 197 | return NULL; 198 | } 199 | 200 | pSyscallEntry = &gpWin32uSyscallList[dwEntryIndex]; 201 | } 202 | else 203 | { 204 | // invalid table index 205 | return NULL; 206 | } 207 | 208 | if(pdwParamCount != NULL) 209 | { 210 | // store param count (optional) 211 | *pdwParamCount = pSyscallEntry->dwParamCount; 212 | } 213 | 214 | return pSyscallEntry->szName; 215 | } 216 | -------------------------------------------------------------------------------- /WinVisorDLL/SyscallParamCount.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | VOID *GetProcAddress_WoW64(VOID *pImageBase, char *pExportName) 4 | { 5 | IMAGE_NT_HEADERS32 *pImageNtHeader32 = NULL; 6 | IMAGE_DATA_DIRECTORY *pExportDataDirectory = NULL; 7 | IMAGE_EXPORT_DIRECTORY *pExportHeader = NULL; 8 | char *pCurrExportName = NULL; 9 | WORD *pwAddressOfNameOrdinals = NULL; 10 | DWORD *pdwAddressOfNames = NULL; 11 | DWORD *pdwAddressOfFunctions = NULL; 12 | 13 | // get NT32 header 14 | pImageNtHeader32 = (IMAGE_NT_HEADERS32*)GetNtHeader(pImageBase); 15 | if(pImageNtHeader32 == NULL) 16 | { 17 | return NULL; 18 | } 19 | 20 | // get export directory 21 | pExportDataDirectory = &pImageNtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 22 | if(pExportDataDirectory->VirtualAddress == 0 || pExportDataDirectory->Size == 0) 23 | { 24 | return NULL; 25 | } 26 | 27 | // get export directory 28 | pExportHeader = (IMAGE_EXPORT_DIRECTORY*)((BYTE*)pImageBase + pExportDataDirectory->VirtualAddress); 29 | if(pExportHeader == NULL) 30 | { 31 | return NULL; 32 | } 33 | 34 | // get export header virtual addresses 35 | pwAddressOfNameOrdinals = (WORD*)((BYTE*)pImageBase + pExportHeader->AddressOfNameOrdinals); 36 | pdwAddressOfNames = (DWORD*)((BYTE*)pImageBase + pExportHeader->AddressOfNames); 37 | pdwAddressOfFunctions = (DWORD*)((BYTE*)pImageBase + pExportHeader->AddressOfFunctions); 38 | 39 | // loop through all exports 40 | for(DWORD i = 0; i < pExportHeader->NumberOfNames; i++) 41 | { 42 | // get export name 43 | pCurrExportName = (char*)((BYTE*)pImageBase + pdwAddressOfNames[i]); 44 | if(strcmp(pCurrExportName, pExportName) == 0) 45 | { 46 | // found 47 | return (BYTE*)pImageBase + pdwAddressOfFunctions[pwAddressOfNameOrdinals[i]]; 48 | } 49 | } 50 | 51 | // not found 52 | return NULL; 53 | } 54 | 55 | DWORD PopulateSyscallParamCounts(char *pModuleName, SyscallNameEntryStruct *pSyscallList, DWORD dwSyscallCount) 56 | { 57 | char szSysWow64DirectoryPath[512]; 58 | char szFullPath[512]; 59 | BYTE *pEndOfSyscall = NULL; 60 | DWORD dwFound = 0; 61 | DWORD dwParamCount = 0; 62 | HANDLE hFile = NULL; 63 | HANDLE hSection = NULL; 64 | VOID *pMappedImage = NULL; 65 | VOID *pFuncAddr = NULL; 66 | 67 | // set initial values 68 | for(DWORD i = 0; i < dwSyscallCount; i++) 69 | { 70 | pSyscallList[i].dwParamCount = UNKNOWN_SYSCALL_PARAM_COUNT; 71 | } 72 | 73 | // get syswow64 directory 74 | memset(szSysWow64DirectoryPath, 0, sizeof(szSysWow64DirectoryPath)); 75 | if(GetSystemWow64DirectoryA(szSysWow64DirectoryPath, sizeof(szSysWow64DirectoryPath) - 1) == 0) 76 | { 77 | return 1; 78 | } 79 | 80 | // open wow64 dll file 81 | memset(szFullPath, 0, sizeof(szFullPath)); 82 | _snprintf(szFullPath, sizeof(szFullPath) - 1, "%s\\%s", szSysWow64DirectoryPath, pModuleName); 83 | hFile = CreateFileA(szFullPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 84 | if(hFile == INVALID_HANDLE_VALUE) 85 | { 86 | return 1; 87 | } 88 | 89 | // create section 90 | hSection = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL); 91 | if(hSection == NULL) 92 | { 93 | CloseHandle(hFile); 94 | return 1; 95 | } 96 | 97 | // map section into memory 98 | pMappedImage = MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0); 99 | if(pMappedImage == NULL) 100 | { 101 | CloseHandle(hSection); 102 | CloseHandle(hFile); 103 | return 1; 104 | } 105 | 106 | // populate param counts 107 | for(DWORD i = 0; i < dwSyscallCount; i++) 108 | { 109 | // find current function in wow64 module 110 | pFuncAddr = GetProcAddress_WoW64(pMappedImage, pSyscallList[i].szName); 111 | if(pFuncAddr == NULL) 112 | { 113 | continue; 114 | } 115 | 116 | // find end of syscall (ret / ret imm16). 117 | // the wow64 code uses callee-cleaned functions (stdcall) so we can use this to calculate the param count. 118 | // in most cases, the wow64 param count will match the native 64-bit functions. 119 | dwFound = 0; 120 | for(DWORD ii = 0; ii < 64; ii++) 121 | { 122 | pEndOfSyscall = (BYTE*)pFuncAddr + ii; 123 | 124 | // call edx 125 | if(*pEndOfSyscall == 0xFF && *(pEndOfSyscall + 1) == 0xD2) 126 | { 127 | if(*(pEndOfSyscall + 2) == 0xC2 && (*(pEndOfSyscall + 3) % 4) == 0 && *(pEndOfSyscall + 4) == 0) 128 | { 129 | // ret imm16 130 | dwParamCount = *(pEndOfSyscall + 3) / 4; 131 | dwFound = 1; 132 | break; 133 | } 134 | else if(*(pEndOfSyscall + 2) == 0xC3) 135 | { 136 | // ret 137 | dwParamCount = 0; 138 | dwFound = 1; 139 | break; 140 | } 141 | } 142 | } 143 | 144 | if(dwFound == 0) 145 | { 146 | // failed to find end of syscall 147 | continue; 148 | } 149 | 150 | // store param count 151 | pSyscallList[i].dwParamCount = dwParamCount; 152 | } 153 | 154 | // clean up 155 | UnmapViewOfFile(pMappedImage); 156 | CloseHandle(hSection); 157 | CloseHandle(hFile); 158 | 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /WinVisorDLL/SyscallProxy.cpp: -------------------------------------------------------------------------------- 1 | #include "WinVisorDLL.h" 2 | 3 | DWORD gdwLogImportSyscallsEnabled = 0; 4 | 5 | SyscallHookEntryStruct gSyscallHookList[] = 6 | { 7 | { "NtTerminateThread", SyscallHook_NtTerminateThread }, 8 | { "NtTerminateProcess", SyscallHook_NtTerminateProcess }, 9 | }; 10 | 11 | BYTE gbSysRet[] = 12 | { 13 | // sysret 14 | 0x48, 0x0F, 0x07 15 | }; 16 | 17 | DWORD ExecuteSyscall(SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue) 18 | { 19 | VOID *pCode = NULL; 20 | UINT64 qwReturnValue = 0; 21 | BYTE bSyscallCode[] = 22 | { 23 | // sub rsp, 0x118 24 | 0x48, 0x81, 0xEC, 0x18, 0x01, 0x00, 0x00, 25 | // mov rax, 26 | 0x48, 0xB8, 0x28, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 27 | // mov qword ptr [rsp+0xF8], rax 28 | 0x48, 0x89, 0x84, 0x24, 0xF8, 0x00, 0x00, 0x00, 29 | // mov rax, 30 | 0x48, 0xB8, 0x27, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 31 | // mov qword ptr [rsp+0xF0], rax 32 | 0x48, 0x89, 0x84, 0x24, 0xF0, 0x00, 0x00, 0x00, 33 | // mov rax, 34 | 0x48, 0xB8, 0x26, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 35 | // mov qword ptr [rsp+0xE8], rax 36 | 0x48, 0x89, 0x84, 0x24, 0xE8, 0x00, 0x00, 0x00, 37 | // mov rax, 38 | 0x48, 0xB8, 0x25, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 39 | // mov qword ptr [rsp+0xE0], rax 40 | 0x48, 0x89, 0x84, 0x24, 0xE0, 0x00, 0x00, 0x00, 41 | // mov rax, 42 | 0x48, 0xB8, 0x24, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 43 | // mov qword ptr [rsp+0xD8], rax 44 | 0x48, 0x89, 0x84, 0x24, 0xD8, 0x00, 0x00, 0x00, 45 | // mov rax, 46 | 0x48, 0xB8, 0x23, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 47 | // mov qword ptr [rsp+0xD0], rax 48 | 0x48, 0x89, 0x84, 0x24, 0xD0, 0x00, 0x00, 0x00, 49 | // mov rax, 50 | 0x48, 0xB8, 0x22, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 51 | // mov qword ptr [rsp+0xC8], rax 52 | 0x48, 0x89, 0x84, 0x24, 0xC8, 0x00, 0x00, 0x00, 53 | // mov rax, 54 | 0x48, 0xB8, 0x21, 0x22, 0x22, 0x22, 0x04, 0x00, 0x00, 0x00, 55 | // mov qword ptr [rsp+0xC0], rax 56 | 0x48, 0x89, 0x84, 0x24, 0xC0, 0x00, 0x00, 0x00, 57 | // mov rax, 58 | 0x48, 0xB8, 0x18, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 59 | // mov qword ptr [rsp+0xB8], rax 60 | 0x48, 0x89, 0x84, 0x24, 0xB8, 0x00, 0x00, 0x00, 61 | // mov rax, 62 | 0x48, 0xB8, 0x17, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 63 | // mov qword ptr [rsp+0xB0], rax 64 | 0x48, 0x89, 0x84, 0x24, 0xB0, 0x00, 0x00, 0x00, 65 | // mov rax, 66 | 0x48, 0xB8, 0x16, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 67 | // mov qword ptr [rsp+0xA8], rax 68 | 0x48, 0x89, 0x84, 0x24, 0xA8, 0x00, 0x00, 0x00, 69 | // mov rax, 70 | 0x48, 0xB8, 0x15, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 71 | // mov qword ptr [rsp+0xA0], rax 72 | 0x48, 0x89, 0x84, 0x24, 0xA0, 0x00, 0x00, 0x00, 73 | // mov rax, 74 | 0x48, 0xB8, 0x14, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 75 | // mov qword ptr [rsp+0x98], rax 76 | 0x48, 0x89, 0x84, 0x24, 0x98, 0x00, 0x00, 0x00, 77 | // mov rax, 78 | 0x48, 0xB8, 0x13, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 79 | // mov qword ptr [rsp+0x90], rax 80 | 0x48, 0x89, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 81 | // mov rax, 82 | 0x48, 0xB8, 0x12, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 83 | // mov qword ptr [rsp+0x88], rax 84 | 0x48, 0x89, 0x84, 0x24, 0x88, 0x00, 0x00, 0x00, 85 | // mov rax, 86 | 0x48, 0xB8, 0x11, 0x11, 0x11, 0x11, 0x03, 0x00, 0x00, 0x00, 87 | // mov qword ptr [rsp+0x80], rax 88 | 0x48, 0x89, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, 89 | // mov rax, 90 | 0x48, 0xB8, 0x28, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 91 | // mov qword ptr [rsp+0x78], rax 92 | 0x48, 0x89, 0x44, 0x24, 0x78, 93 | // mov rax, 94 | 0x48, 0xB8, 0x27, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 95 | // mov qword ptr [rsp+0x70], rax 96 | 0x48, 0x89, 0x44, 0x24, 0x70, 97 | // mov rax, 98 | 0x48, 0xB8, 0x26, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 99 | // mov qword ptr [rsp+0x68], rax 100 | 0x48, 0x89, 0x44, 0x24, 0x68, 101 | // mov rax, 102 | 0x48, 0xB8, 0x25, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 103 | // mov qword ptr [rsp+0x60], rax 104 | 0x48, 0x89, 0x44, 0x24, 0x60, 105 | // mov rax, 106 | 0x48, 0xB8, 0x24, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 107 | // mov qword ptr [rsp+0x58], rax 108 | 0x48, 0x89, 0x44, 0x24, 0x58, 109 | // mov rax, 110 | 0x48, 0xB8, 0x23, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 111 | // mov qword ptr [rsp+0x50], rax 112 | 0x48, 0x89, 0x44, 0x24, 0x50, 113 | // mov rax, 114 | 0x48, 0xB8, 0x22, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 115 | // mov qword ptr [rsp+0x48], rax 116 | 0x48, 0x89, 0x44, 0x24, 0x48, 117 | // mov rax, 118 | 0x48, 0xB8, 0x21, 0x22, 0x22, 0x22, 0x02, 0x00, 0x00, 0x00, 119 | // mov qword ptr [rsp+0x40], rax 120 | 0x48, 0x89, 0x44, 0x24, 0x40, 121 | // mov rax, 122 | 0x48, 0xB8, 0x18, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 123 | // mov qword ptr [rsp+0x38], rax 124 | 0x48, 0x89, 0x44, 0x24, 0x38, 125 | // mov rax, 126 | 0x48, 0xB8, 0x17, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 127 | // mov qword ptr [rsp+0x30], rax 128 | 0x48, 0x89, 0x44, 0x24, 0x30, 129 | // mov rax, 130 | 0x48, 0xB8, 0x16, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 131 | // mov qword ptr [rsp+0x28], rax 132 | 0x48, 0x89, 0x44, 0x24, 0x28, 133 | // mov rax, 134 | 0x48, 0xB8, 0x15, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 135 | // mov qword ptr [rsp+0x20], rax 136 | 0x48, 0x89, 0x44, 0x24, 0x20, 137 | // mov r9, 138 | 0x49, 0xB9, 0x14, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 139 | // mov r8, 140 | 0x49, 0xB8, 0x13, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 141 | // mov rdx, 142 | 0x48, 0xBA, 0x12, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 143 | // mov rcx, 144 | 0x48, 0xB9, 0x11, 0x11, 0x11, 0x11, 0x01, 0x00, 0x00, 0x00, 145 | // call SYSCALL_STUB 146 | 0xE8, 0x08, 0x00, 0x00, 0x00, 147 | // add rsp, 0x118 148 | 0x48, 0x81, 0xC4, 0x18, 0x01, 0x00, 0x00, 149 | // ret 150 | 0xC3, 151 | // SYSCALL_STUB: 152 | // mov r10, rcx 153 | 0x49, 0x89, 0xCA, 154 | // mov eax, 155 | 0xB8, 0xF6, 0x00, 0x00, 0x00, 156 | // syscall 157 | 0x0F, 0x05, 158 | // ret 159 | 0xC3 160 | }; 161 | 162 | // set syscall index 163 | *(DWORD*)&bSyscallCode[532] = pSyscallInfo->dwSyscallIndex; 164 | 165 | // set syscall param values 166 | *(UINT64*)&bSyscallCode[9] = pSyscallInfo->qwParamList[31]; 167 | *(UINT64*)&bSyscallCode[27] = pSyscallInfo->qwParamList[30]; 168 | *(UINT64*)&bSyscallCode[45] = pSyscallInfo->qwParamList[29]; 169 | *(UINT64*)&bSyscallCode[63] = pSyscallInfo->qwParamList[28]; 170 | *(UINT64*)&bSyscallCode[81] = pSyscallInfo->qwParamList[27]; 171 | *(UINT64*)&bSyscallCode[99] = pSyscallInfo->qwParamList[26]; 172 | *(UINT64*)&bSyscallCode[117] = pSyscallInfo->qwParamList[25]; 173 | *(UINT64*)&bSyscallCode[135] = pSyscallInfo->qwParamList[24]; 174 | *(UINT64*)&bSyscallCode[153] = pSyscallInfo->qwParamList[23]; 175 | *(UINT64*)&bSyscallCode[171] = pSyscallInfo->qwParamList[22]; 176 | *(UINT64*)&bSyscallCode[189] = pSyscallInfo->qwParamList[21]; 177 | *(UINT64*)&bSyscallCode[207] = pSyscallInfo->qwParamList[20]; 178 | *(UINT64*)&bSyscallCode[225] = pSyscallInfo->qwParamList[19]; 179 | *(UINT64*)&bSyscallCode[243] = pSyscallInfo->qwParamList[18]; 180 | *(UINT64*)&bSyscallCode[261] = pSyscallInfo->qwParamList[17]; 181 | *(UINT64*)&bSyscallCode[279] = pSyscallInfo->qwParamList[16]; 182 | *(UINT64*)&bSyscallCode[297] = pSyscallInfo->qwParamList[15]; 183 | *(UINT64*)&bSyscallCode[312] = pSyscallInfo->qwParamList[14]; 184 | *(UINT64*)&bSyscallCode[327] = pSyscallInfo->qwParamList[13]; 185 | *(UINT64*)&bSyscallCode[342] = pSyscallInfo->qwParamList[12]; 186 | *(UINT64*)&bSyscallCode[357] = pSyscallInfo->qwParamList[11]; 187 | *(UINT64*)&bSyscallCode[372] = pSyscallInfo->qwParamList[10]; 188 | *(UINT64*)&bSyscallCode[387] = pSyscallInfo->qwParamList[9]; 189 | *(UINT64*)&bSyscallCode[402] = pSyscallInfo->qwParamList[8]; 190 | *(UINT64*)&bSyscallCode[417] = pSyscallInfo->qwParamList[7]; 191 | *(UINT64*)&bSyscallCode[432] = pSyscallInfo->qwParamList[6]; 192 | *(UINT64*)&bSyscallCode[447] = pSyscallInfo->qwParamList[5]; 193 | *(UINT64*)&bSyscallCode[462] = pSyscallInfo->qwParamList[4]; 194 | *(UINT64*)&bSyscallCode[477] = pSyscallInfo->qwParamList[3]; 195 | *(UINT64*)&bSyscallCode[487] = pSyscallInfo->qwParamList[2]; 196 | *(UINT64*)&bSyscallCode[497] = pSyscallInfo->qwParamList[1]; 197 | *(UINT64*)&bSyscallCode[507] = pSyscallInfo->qwParamList[0]; 198 | 199 | // allocate temporary memory for syscall code 200 | pCode = VirtualAlloc(NULL, sizeof(bSyscallCode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 201 | if(pCode == NULL) 202 | { 203 | return 1; 204 | } 205 | 206 | // execute syscall 207 | memcpy(pCode, bSyscallCode, sizeof(bSyscallCode)); 208 | qwReturnValue = ((UINT64(*)())pCode)(); 209 | 210 | // free temporary memory 211 | VirtualFree(pCode, 0, MEM_RELEASE); 212 | 213 | // store return value 214 | *pqwReturnValue = qwReturnValue; 215 | 216 | return 0; 217 | } 218 | 219 | DWORD WINAPI SyscallProxyThread(CpuStateStruct *pCpuState) 220 | { 221 | // syscall proxy thread ready 222 | SetEvent(pCpuState->hSyscallProxyReadyEvent); 223 | 224 | for(;;) 225 | { 226 | // wait for next syscall request 227 | WaitForSingleObject(pCpuState->hSyscallWaitingEvent, INFINITE); 228 | 229 | // execute received syscall request 230 | if(ExecuteSyscall(&pCpuState->SyscallInfo, &pCpuState->qwSyscallReturnValue) != 0) 231 | { 232 | WriteLog(LOG_ERROR, "Failed to execute syscall"); 233 | return 1; 234 | } 235 | 236 | // syscall complete 237 | SetEvent(pCpuState->hSyscallCompleteEvent); 238 | } 239 | 240 | return 0; 241 | } 242 | 243 | DWORD ForwardSyscallToHost(CpuStateStruct *pCpuState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue) 244 | { 245 | // request syscall from proxy thread 246 | memcpy(&pCpuState->SyscallInfo, pSyscallInfo, sizeof(SyscallInfoStruct)); 247 | SetEvent(pCpuState->hSyscallWaitingEvent); 248 | 249 | // wait for syscall to return 250 | WaitForSingleObject(pCpuState->hSyscallCompleteEvent, INFINITE); 251 | *pqwReturnValue = pCpuState->qwSyscallReturnValue; 252 | 253 | return 0; 254 | } 255 | 256 | DWORD CheckSyscallLoggingEnabled() 257 | { 258 | if(gdwLogImportSyscallsEnabled == 0) 259 | { 260 | // check if the EXE imports have been loaded 261 | if(gdwLoadedModuleImports == 0) 262 | { 263 | // imports not loaded yet - suppress syscall logging 264 | return 1; 265 | } 266 | } 267 | 268 | return 0; 269 | } 270 | 271 | DWORD LogSyscallStart(char *pSyscallName, DWORD dwSyscallParamCount, SyscallInfoStruct *pSyscallInfo) 272 | { 273 | char szSyscallLog[1024]; 274 | char szTemp[512]; 275 | 276 | if(CheckSyscallLoggingEnabled() != 0) 277 | { 278 | // syscall logging suppressed 279 | return 1; 280 | } 281 | 282 | // initialise log string 283 | memset(szSyscallLog, 0, sizeof(szSyscallLog)); 284 | 285 | // append syscall name 286 | memset(szTemp, 0, sizeof(szTemp)); 287 | _snprintf(szTemp, sizeof(szTemp) - 1, "Caught syscall: %s(", pSyscallName); 288 | if(AppendString(szSyscallLog, sizeof(szSyscallLog) - 1, szTemp) != 0) 289 | { 290 | return 1; 291 | } 292 | 293 | // append param values 294 | for(DWORD i = 0; i < dwSyscallParamCount; i++) 295 | { 296 | if(i != 0) 297 | { 298 | if(AppendString(szSyscallLog, sizeof(szSyscallLog) - 1, ",") != 0) 299 | { 300 | return 1; 301 | } 302 | } 303 | 304 | // append current param value 305 | memset(szTemp, 0, sizeof(szTemp)); 306 | _snprintf(szTemp, sizeof(szTemp) - 1, "0x%I64X", pSyscallInfo->qwParamList[i]); 307 | if(AppendString(szSyscallLog, sizeof(szSyscallLog) - 1, szTemp) != 0) 308 | { 309 | return 1; 310 | } 311 | } 312 | 313 | // end of param values 314 | if(AppendString(szSyscallLog, sizeof(szSyscallLog) - 1, ")") != 0) 315 | { 316 | return 1; 317 | } 318 | 319 | // write log entry 320 | WriteLog(LOG_INFO, "%s", szSyscallLog); 321 | 322 | return 0; 323 | } 324 | 325 | DWORD LogSyscallEnd(char *pSyscallName, DWORD dwSyscallParamCount, SyscallInfoStruct *pSyscallInfo, UINT64 qwReturnValue) 326 | { 327 | if(CheckSyscallLoggingEnabled() != 0) 328 | { 329 | // syscall logging suppressed 330 | return 1; 331 | } 332 | 333 | // write syscall return value 334 | WriteLog(LOG_INFO, " -> returned: 0x%I64X", qwReturnValue); 335 | 336 | return 0; 337 | } 338 | 339 | DWORD HandleGuestSyscall(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, VOID *pUserStackPtr) 340 | { 341 | DWORD dwSyscallIndex = 0; 342 | UINT64 qwCurrParamValue = 0; 343 | UINT64 qwReturnValue = 0; 344 | UINT64 *pqwStackParamPtr = NULL; 345 | SyscallInfoStruct SyscallInfo; 346 | char *pSyscallName = NULL; 347 | DWORD dwSyscallParamCount = 0; 348 | SyscallHookEntryStruct *pSyscallHookEntry = NULL; 349 | 350 | // get syscall index 351 | dwSyscallIndex = (DWORD)pCpuRegisterState->RAX; 352 | 353 | // look-up syscall name 354 | pSyscallName = GetSyscallName(dwSyscallIndex, &dwSyscallParamCount); 355 | if(pSyscallName == NULL) 356 | { 357 | WriteLog(LOG_ERROR, "Invalid syscall: 0x%X", dwSyscallIndex); 358 | return 1; 359 | } 360 | 361 | // extract syscall params from guest - copy all values regardless of dwSyscallParamCount value. 362 | // the dwSyscallParamCount value is calculated via wow64 modules so the results may not be 100% accurate - it is used for display-purposes only. 363 | memset(&SyscallInfo, 0, sizeof(SyscallInfo)); 364 | SyscallInfo.dwSyscallIndex = dwSyscallIndex; 365 | for(DWORD i = 0; i < MAX_SYSCALL_PARAM_COUNT; i++) 366 | { 367 | // get first 4 param values from registers 368 | if(i == 0) 369 | { 370 | // the syscall instruction overwrites rcx, so windows uses r10 for the first param value instead. 371 | // the legacy interrupt handler (KiSystemService) also copies r10 back into rcx so it doesn't need to be handled differently. 372 | qwCurrParamValue = pCpuRegisterState->R10; 373 | } 374 | else if(i == 1) 375 | { 376 | qwCurrParamValue = pCpuRegisterState->RDX; 377 | } 378 | else if(i == 2) 379 | { 380 | qwCurrParamValue = pCpuRegisterState->R8; 381 | } 382 | else if(i == 3) 383 | { 384 | qwCurrParamValue = pCpuRegisterState->R9; 385 | } 386 | else 387 | { 388 | // get param from stack 389 | pqwStackParamPtr = (UINT64*)((UINT64)pUserStackPtr + 0x28 + ((i - 4) * 8)); 390 | if(ValidateReadPointer(pqwStackParamPtr, 8) == 0) 391 | { 392 | qwCurrParamValue = *pqwStackParamPtr; 393 | } 394 | else 395 | { 396 | // stack ptr out of range 397 | qwCurrParamValue = 0; 398 | } 399 | } 400 | 401 | // store current param value 402 | SyscallInfo.qwParamList[i] = qwCurrParamValue; 403 | } 404 | 405 | if(dwSyscallParamCount == UNKNOWN_SYSCALL_PARAM_COUNT) 406 | { 407 | // unknown param count - use default 408 | dwSyscallParamCount = 4; 409 | } 410 | 411 | // log syscall start 412 | LogSyscallStart(pSyscallName, dwSyscallParamCount, &SyscallInfo); 413 | 414 | // check if this syscall is hooked 415 | for(DWORD i = 0; i < sizeof(gSyscallHookList) / sizeof(gSyscallHookList[0]); i++) 416 | { 417 | if(strcmp(pSyscallName, gSyscallHookList[i].pSyscallName) == 0) 418 | { 419 | pSyscallHookEntry = &gSyscallHookList[i]; 420 | break; 421 | } 422 | } 423 | 424 | if(pSyscallHookEntry != NULL) 425 | { 426 | // hooked - pass to handler 427 | if(pSyscallHookEntry->pHandler(pCpuState, pCpuRegisterState, &SyscallInfo, &qwReturnValue) != 0) 428 | { 429 | return 1; 430 | } 431 | } 432 | else 433 | { 434 | // not hooked - pass syscall directly to host via proxy thread 435 | if(ForwardSyscallToHost(pCpuState, &SyscallInfo, &qwReturnValue) != 0) 436 | { 437 | return 1; 438 | } 439 | } 440 | 441 | // log syscall end 442 | LogSyscallEnd(pSyscallName, dwSyscallParamCount, &SyscallInfo, qwReturnValue); 443 | 444 | // set return value 445 | pCpuRegisterState->RAX = qwReturnValue; 446 | 447 | // set RIP to sysret instruction 448 | pCpuRegisterState->RIP = (UINT64)gbSysRet; 449 | 450 | return 0; 451 | } 452 | 453 | DWORD HandleSyscallInstruction(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState) 454 | { 455 | // handle fast syscall - rsp still points to user-mode stack 456 | if(HandleGuestSyscall(pCpuState, pCpuRegisterState, (void*)pCpuRegisterState->RSP) != 0) 457 | { 458 | return 1; 459 | } 460 | 461 | // set RIP to sysret instruction 462 | pCpuRegisterState->RIP = (UINT64)gbSysRet; 463 | 464 | return 0; 465 | } 466 | -------------------------------------------------------------------------------- /WinVisorDLL/WinHvApi.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Module Name: 6 | 7 | WinHvApiDefs.h 8 | 9 | Abstract: 10 | 11 | This module contains the constant, type and structure definitions for 12 | the Windows Hypervisor User-Mode APIs. 13 | 14 | --*/ 15 | 16 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 17 | #pragma once 18 | #pragma warning(push) 19 | #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ 20 | #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ 21 | #endif 22 | 23 | // 24 | // Platform capabilities 25 | // 26 | typedef enum WHV_CAPABILITY_CODE 27 | { 28 | // Capabilities of the API implementation 29 | WHvCapabilityCodeHypervisorPresent = 0x00000000, 30 | WHvCapabilityCodeFeatures = 0x00000001, 31 | WHvCapabilityCodeExtendedVmExits = 0x00000002, 32 | WHvCapabilityCodeExceptionExitBitmap = 0x00000003, 33 | 34 | // Capabilities of the system's processor 35 | WHvCapabilityCodeProcessorVendor = 0x00001000, 36 | WHvCapabilityCodeProcessorFeatures = 0x00001001, 37 | WHvCapabilityCodeProcessorClFlushSize = 0x00001002 38 | } WHV_CAPABILITY_CODE; 39 | 40 | // 41 | // Return values for WhvCapabilityCodeFeatures 42 | // 43 | typedef union WHV_CAPABILITY_FEATURES 44 | { 45 | struct 46 | { 47 | UINT64 Reserved : 64; 48 | }; 49 | 50 | UINT64 AsUINT64; 51 | } WHV_CAPABILITY_FEATURES; 52 | 53 | // 54 | // Return values for WhvCapabilityCodeExtendedVmExits 55 | // 56 | typedef union WHV_EXTENDED_VM_EXITS 57 | { 58 | struct 59 | { 60 | UINT64 X64CpuidExit : 1; // RunVpExitReasonX64CPUID supported 61 | UINT64 X64MsrExit : 1; // RunVpExitX64ReasonMSRAccess supported 62 | UINT64 ExceptionExit : 1; // RunVpExitReasonException supported 63 | UINT64 Reserved : 61; 64 | }; 65 | 66 | UINT64 AsUINT64; 67 | } WHV_EXTENDED_VM_EXITS; 68 | 69 | // 70 | // Return values for WhvCapabilityCodeProcessorVendor 71 | // 72 | typedef enum WHV_PROCESSOR_VENDOR 73 | { 74 | WHvProcessorVendorAmd = 0x0000, 75 | WHvProcessorVendorIntel = 0x0001 76 | 77 | } WHV_PROCESSOR_VENDOR; 78 | 79 | // 80 | // Return values for WhvCapabilityCodeProcessorFeatures 81 | // 82 | typedef union WHV_PROCESSOR_FEATURES 83 | { 84 | struct 85 | { 86 | UINT64 Sse3Support : 1; 87 | UINT64 LahfSahfSupport : 1; 88 | UINT64 Ssse3Support : 1; 89 | UINT64 Sse4_1Support : 1; 90 | UINT64 Sse4_2Support : 1; 91 | UINT64 Sse4aSupport : 1; 92 | UINT64 XopSupport : 1; 93 | UINT64 PopCntSupport : 1; 94 | UINT64 Cmpxchg16bSupport : 1; 95 | UINT64 Altmovcr8Support : 1; 96 | UINT64 LzcntSupport : 1; 97 | UINT64 MisAlignSseSupport : 1; 98 | UINT64 MmxExtSupport : 1; 99 | UINT64 Amd3DNowSupport : 1; 100 | UINT64 ExtendedAmd3DNowSupport : 1; 101 | UINT64 Page1GbSupport : 1; 102 | UINT64 AesSupport : 1; 103 | UINT64 PclmulqdqSupport : 1; 104 | UINT64 PcidSupport : 1; 105 | UINT64 Fma4Support : 1; 106 | UINT64 F16CSupport : 1; 107 | UINT64 RdRandSupport : 1; 108 | UINT64 RdWrFsGsSupport : 1; 109 | UINT64 SmepSupport : 1; 110 | UINT64 EnhancedFastStringSupport : 1; 111 | UINT64 Bmi1Support : 1; 112 | UINT64 Bmi2Support : 1; 113 | UINT64 Reserved1 : 2; 114 | UINT64 MovbeSupport : 1; 115 | UINT64 Npiep1Support : 1; 116 | UINT64 DepX87FPUSaveSupport : 1; 117 | UINT64 RdSeedSupport : 1; 118 | UINT64 AdxSupport : 1; 119 | UINT64 IntelPrefetchSupport : 1; 120 | UINT64 SmapSupport : 1; 121 | UINT64 HleSupport : 1; 122 | UINT64 RtmSupport : 1; 123 | UINT64 RdtscpSupport : 1; 124 | UINT64 ClflushoptSupport : 1; 125 | UINT64 ClwbSupport : 1; 126 | UINT64 ShaSupport : 1; 127 | UINT64 X87PointersSavedSupport : 1; 128 | UINT64 Reserved2 : 21; 129 | }; 130 | 131 | UINT64 AsUINT64; 132 | } WHV_PROCESSOR_FEATURES; 133 | 134 | // 135 | // WHvGetCapability output buffer 136 | // 137 | typedef union WHV_CAPABILITY 138 | { 139 | BOOL HypervisorPresent; 140 | WHV_CAPABILITY_FEATURES Features; 141 | WHV_EXTENDED_VM_EXITS ExtendedVmExits; 142 | WHV_PROCESSOR_VENDOR ProcessorVendor; 143 | WHV_PROCESSOR_FEATURES ProcessorFeatures; 144 | UINT8 ProcessorClFlushSize; 145 | UINT64 ExceptionExitBitmap; 146 | } WHV_CAPABILITY; 147 | 148 | // 149 | // Partitions 150 | // 151 | 152 | typedef VOID* WHV_PARTITION_HANDLE; 153 | 154 | typedef enum WHV_PARTITION_PROPERTY_CODE 155 | { 156 | WHvPartitionPropertyCodeExtendedVmExits = 0x00000001, 157 | WHvPartitionPropertyCodeExceptionExitBitmap = 0x00000002, 158 | 159 | WHvPartitionPropertyCodeProcessorFeatures = 0x00001001, 160 | WHvPartitionPropertyCodeProcessorClFlushSize = 0x00001002, 161 | WHvPartitionPropertyCodeCpuidExitList = 0x00001003, 162 | WHvPartitionPropertyCodeCpuidResultList = 0x00001004, 163 | 164 | WHvPartitionPropertyCodeProcessorCount = 0x00001fff 165 | } WHV_PARTITION_PROPERTY_CODE; 166 | 167 | // 168 | // WHvPartitionPropertyCodeCpuidResultList input buffer list element. 169 | // 170 | typedef struct WHV_X64_CPUID_RESULT 171 | { 172 | UINT32 Function; 173 | UINT32 Reserved[3]; 174 | UINT32 Eax; 175 | UINT32 Ebx; 176 | UINT32 Ecx; 177 | UINT32 Edx; 178 | } WHV_X64_CPUID_RESULT; 179 | 180 | // 181 | // WHvPartitionPropertyCodeExceptionBitmap enumeration values. 182 | // 183 | typedef enum WHV_EXCEPTION_TYPE 184 | { 185 | WHvX64ExceptionTypeDivideErrorFault = 0x0, 186 | WHvX64ExceptionTypeDebugTrapOrFault = 0x1, 187 | WHvX64ExceptionTypeBreakpointTrap = 0x3, 188 | WHvX64ExceptionTypeOverflowTrap = 0x4, 189 | WHvX64ExceptionTypeBoundRangeFault = 0x5, 190 | WHvX64ExceptionTypeInvalidOpcodeFault = 0x6, 191 | WHvX64ExceptionTypeDeviceNotAvailableFault = 0x7, 192 | WHvX64ExceptionTypeDoubleFaultAbort = 0x8, 193 | WHvX64ExceptionTypeInvalidTaskStateSegmentFault = 0x0A, 194 | WHvX64ExceptionTypeSegmentNotPresentFault = 0x0B, 195 | WHvX64ExceptionTypeStackFault = 0x0C, 196 | WHvX64ExceptionTypeGeneralProtectionFault = 0x0D, 197 | WHvX64ExceptionTypePageFault = 0x0E, 198 | WHvX64ExceptionTypeFloatingPointErrorFault = 0x10, 199 | WHvX64ExceptionTypeAlignmentCheckFault = 0x11, 200 | WHvX64ExceptionTypeMachineCheckAbort = 0x12, 201 | WHvX64ExceptionTypeSimdFloatingPointFault = 0x13, 202 | } WHV_EXCEPTION_TYPE; 203 | 204 | // 205 | // WHvGetPartitionProperty output buffer / WHvSetPartitionProperty input buffer 206 | // 207 | typedef union WHV_PARTITION_PROPERTY 208 | { 209 | WHV_EXTENDED_VM_EXITS ExtendedVmExits; 210 | WHV_PROCESSOR_FEATURES ProcessorFeatures; 211 | UINT8 ProcessorClFlushSize; 212 | UINT32 ProcessorCount; 213 | UINT32 CpuidExitList[1]; 214 | WHV_X64_CPUID_RESULT CpuidResultList[1]; 215 | UINT64 ExceptionExitBitmap; 216 | } WHV_PARTITION_PROPERTY; 217 | 218 | // 219 | // Memory Management 220 | // 221 | 222 | // 223 | // Guest physical or virtual address 224 | // 225 | typedef UINT64 WHV_GUEST_PHYSICAL_ADDRESS; 226 | typedef UINT64 WHV_GUEST_VIRTUAL_ADDRESS; 227 | 228 | // 229 | // Flags used by WHvMapGpaRange 230 | // 231 | /*typedef enum WHV_MAP_GPA_RANGE_FLAGS 232 | { 233 | WHvMapGpaRangeFlagNone = 0x00000000, 234 | WHvMapGpaRangeFlagRead = 0x00000001, 235 | WHvMapGpaRangeFlagWrite = 0x00000002, 236 | WHvMapGpaRangeFlagExecute = 0x00000004, 237 | } WHV_MAP_GPA_RANGE_FLAGS;*/ 238 | 239 | #define WHvMapGpaRangeFlagNone 0x00000000 240 | #define WHvMapGpaRangeFlagRead 0x00000001 241 | #define WHvMapGpaRangeFlagWrite 0x00000002 242 | #define WHvMapGpaRangeFlagExecute 0x00000004 243 | #define WHV_MAP_GPA_RANGE_FLAGS DWORD 244 | //DEFINE_ENUM_FLAG_OPERATORS(WHV_MAP_GPA_RANGE_FLAGS); 245 | 246 | // 247 | // Flags used by WHvTranslateGva 248 | // 249 | typedef enum WHV_TRANSLATE_GVA_FLAGS 250 | { 251 | WHvTranslateGvaFlagNone = 0x00000000, 252 | WHvTranslateGvaFlagValidateRead = 0x00000001, 253 | WHvTranslateGvaFlagValidateWrite = 0x00000002, 254 | WHvTranslateGvaFlagValidateExecute = 0x00000004, 255 | WHvTranslateGvaFlagPrivilegeExempt = 0x00000008, 256 | WHvTranslateGvaFlagSetPageTableBits = 0x00000010 257 | } WHV_TRANSLATE_GVA_FLAGS; 258 | 259 | //DEFINE_ENUM_FLAG_OPERATORS(WHV_TRANSLATE_GVA_FLAGS); 260 | 261 | // 262 | // Result of an attempt to translate a guest virtual address 263 | // 264 | typedef enum WHV_TRANSLATE_GVA_RESULT_CODE 265 | { 266 | WHvTranslateGvaResultSuccess = 0, 267 | 268 | // Translation failures 269 | WHvTranslateGvaResultPageNotPresent = 1, 270 | WHvTranslateGvaResultPrivilegeViolation = 2, 271 | WHvTranslateGvaResultInvalidPageTableFlags = 3, 272 | 273 | // GPA access failures 274 | WHvTranslateGvaResultGpaUnmapped = 4, 275 | WHvTranslateGvaResultGpaNoReadAccess = 5, 276 | WHvTranslateGvaResultGpaNoWriteAccess = 6, 277 | WHvTranslateGvaResultGpaIllegalOverlayAccess = 7, 278 | WHvTranslateGvaResultIntercept = 8 279 | } WHV_TRANSLATE_GVA_RESULT_CODE; 280 | 281 | // 282 | // Output buffer of WHvTranslateGva 283 | // 284 | typedef struct WHV_TRANSLATE_GVA_RESULT 285 | { 286 | WHV_TRANSLATE_GVA_RESULT_CODE ResultCode; 287 | UINT32 Reserved; 288 | } WHV_TRANSLATE_GVA_RESULT; 289 | 290 | // 291 | // Virtual Processor Register Definitions 292 | // 293 | typedef enum WHV_REGISTER_NAME 294 | { 295 | // X64 General purpose registers 296 | WHvX64RegisterRax = 0x00000000, 297 | WHvX64RegisterRcx = 0x00000001, 298 | WHvX64RegisterRdx = 0x00000002, 299 | WHvX64RegisterRbx = 0x00000003, 300 | WHvX64RegisterRsp = 0x00000004, 301 | WHvX64RegisterRbp = 0x00000005, 302 | WHvX64RegisterRsi = 0x00000006, 303 | WHvX64RegisterRdi = 0x00000007, 304 | WHvX64RegisterR8 = 0x00000008, 305 | WHvX64RegisterR9 = 0x00000009, 306 | WHvX64RegisterR10 = 0x0000000A, 307 | WHvX64RegisterR11 = 0x0000000B, 308 | WHvX64RegisterR12 = 0x0000000C, 309 | WHvX64RegisterR13 = 0x0000000D, 310 | WHvX64RegisterR14 = 0x0000000E, 311 | WHvX64RegisterR15 = 0x0000000F, 312 | WHvX64RegisterRip = 0x00000010, 313 | WHvX64RegisterRflags = 0x00000011, 314 | 315 | // X64 Segment registers 316 | WHvX64RegisterEs = 0x00000012, 317 | WHvX64RegisterCs = 0x00000013, 318 | WHvX64RegisterSs = 0x00000014, 319 | WHvX64RegisterDs = 0x00000015, 320 | WHvX64RegisterFs = 0x00000016, 321 | WHvX64RegisterGs = 0x00000017, 322 | WHvX64RegisterLdtr = 0x00000018, 323 | WHvX64RegisterTr = 0x00000019, 324 | 325 | // X64 Table registers 326 | WHvX64RegisterIdtr = 0x0000001A, 327 | WHvX64RegisterGdtr = 0x0000001B, 328 | 329 | // X64 Control Registers 330 | WHvX64RegisterCr0 = 0x0000001C, 331 | WHvX64RegisterCr2 = 0x0000001D, 332 | WHvX64RegisterCr3 = 0x0000001E, 333 | WHvX64RegisterCr4 = 0x0000001F, 334 | WHvX64RegisterCr8 = 0x00000020, 335 | 336 | // X64 Debug Registers 337 | WHvX64RegisterDr0 = 0x00000021, 338 | WHvX64RegisterDr1 = 0x00000022, 339 | WHvX64RegisterDr2 = 0x00000023, 340 | WHvX64RegisterDr3 = 0x00000024, 341 | WHvX64RegisterDr6 = 0x00000025, 342 | WHvX64RegisterDr7 = 0x00000026, 343 | 344 | // X64 Floating Point and Vector Registers 345 | WHvX64RegisterXmm0 = 0x00001000, 346 | WHvX64RegisterXmm1 = 0x00001001, 347 | WHvX64RegisterXmm2 = 0x00001002, 348 | WHvX64RegisterXmm3 = 0x00001003, 349 | WHvX64RegisterXmm4 = 0x00001004, 350 | WHvX64RegisterXmm5 = 0x00001005, 351 | WHvX64RegisterXmm6 = 0x00001006, 352 | WHvX64RegisterXmm7 = 0x00001007, 353 | WHvX64RegisterXmm8 = 0x00001008, 354 | WHvX64RegisterXmm9 = 0x00001009, 355 | WHvX64RegisterXmm10 = 0x0000100A, 356 | WHvX64RegisterXmm11 = 0x0000100B, 357 | WHvX64RegisterXmm12 = 0x0000100C, 358 | WHvX64RegisterXmm13 = 0x0000100D, 359 | WHvX64RegisterXmm14 = 0x0000100E, 360 | WHvX64RegisterXmm15 = 0x0000100F, 361 | WHvX64RegisterFpMmx0 = 0x00001010, 362 | WHvX64RegisterFpMmx1 = 0x00001011, 363 | WHvX64RegisterFpMmx2 = 0x00001012, 364 | WHvX64RegisterFpMmx3 = 0x00001013, 365 | WHvX64RegisterFpMmx4 = 0x00001014, 366 | WHvX64RegisterFpMmx5 = 0x00001015, 367 | WHvX64RegisterFpMmx6 = 0x00001016, 368 | WHvX64RegisterFpMmx7 = 0x00001017, 369 | WHvX64RegisterFpControlStatus = 0x00001018, 370 | WHvX64RegisterXmmControlStatus = 0x00001019, 371 | 372 | // X64 MSRs 373 | WHvX64RegisterTsc = 0x00002000, 374 | WHvX64RegisterEfer = 0x00002001, 375 | WHvX64RegisterKernelGsBase = 0x00002002, 376 | WHvX64RegisterApicBase = 0x00002003, 377 | WHvX64RegisterPat = 0x00002004, 378 | WHvX64RegisterSysenterCs = 0x00002005, 379 | WHvX64RegisterSysenterEip = 0x00002006, 380 | WHvX64RegisterSysenterEsp = 0x00002007, 381 | WHvX64RegisterStar = 0x00002008, 382 | WHvX64RegisterLstar = 0x00002009, 383 | WHvX64RegisterCstar = 0x0000200A, 384 | WHvX64RegisterSfmask = 0x0000200B, 385 | 386 | WHvX64RegisterMsrMtrrCap = 0x0000200D, 387 | WHvX64RegisterMsrMtrrDefType = 0x0000200E, 388 | 389 | WHvX64RegisterMsrMtrrPhysBase0 = 0x00002010, 390 | WHvX64RegisterMsrMtrrPhysBase1 = 0x00002011, 391 | WHvX64RegisterMsrMtrrPhysBase2 = 0x00002012, 392 | WHvX64RegisterMsrMtrrPhysBase3 = 0x00002013, 393 | WHvX64RegisterMsrMtrrPhysBase4 = 0x00002014, 394 | WHvX64RegisterMsrMtrrPhysBase5 = 0x00002015, 395 | WHvX64RegisterMsrMtrrPhysBase6 = 0x00002016, 396 | WHvX64RegisterMsrMtrrPhysBase7 = 0x00002017, 397 | WHvX64RegisterMsrMtrrPhysBase8 = 0x00002018, 398 | WHvX64RegisterMsrMtrrPhysBase9 = 0x00002019, 399 | WHvX64RegisterMsrMtrrPhysBaseA = 0x0000201A, 400 | WHvX64RegisterMsrMtrrPhysBaseB = 0x0000201B, 401 | WHvX64RegisterMsrMtrrPhysBaseC = 0x0000201C, 402 | WHvX64RegisterMsrMtrrPhysBaseD = 0x0000201D, 403 | WHvX64RegisterMsrMtrrPhysBaseE = 0x0000201E, 404 | WHvX64RegisterMsrMtrrPhysBaseF = 0x0000201F, 405 | 406 | WHvX64RegisterMsrMtrrPhysMask0 = 0x00002040, 407 | WHvX64RegisterMsrMtrrPhysMask1 = 0x00002041, 408 | WHvX64RegisterMsrMtrrPhysMask2 = 0x00002042, 409 | WHvX64RegisterMsrMtrrPhysMask3 = 0x00002043, 410 | WHvX64RegisterMsrMtrrPhysMask4 = 0x00002044, 411 | WHvX64RegisterMsrMtrrPhysMask5 = 0x00002045, 412 | WHvX64RegisterMsrMtrrPhysMask6 = 0x00002046, 413 | WHvX64RegisterMsrMtrrPhysMask7 = 0x00002047, 414 | WHvX64RegisterMsrMtrrPhysMask8 = 0x00002048, 415 | WHvX64RegisterMsrMtrrPhysMask9 = 0x00002049, 416 | WHvX64RegisterMsrMtrrPhysMaskA = 0x0000204A, 417 | WHvX64RegisterMsrMtrrPhysMaskB = 0x0000204B, 418 | WHvX64RegisterMsrMtrrPhysMaskC = 0x0000204C, 419 | WHvX64RegisterMsrMtrrPhysMaskD = 0x0000204D, 420 | WHvX64RegisterMsrMtrrPhysMaskE = 0x0000204E, 421 | WHvX64RegisterMsrMtrrPhysMaskF = 0x0000204F, 422 | 423 | WHvX64RegisterMsrMtrrFix64k00000 = 0x00002070, 424 | WHvX64RegisterMsrMtrrFix16k80000 = 0x00002071, 425 | WHvX64RegisterMsrMtrrFix16kA0000 = 0x00002072, 426 | WHvX64RegisterMsrMtrrFix4kC0000 = 0x00002073, 427 | WHvX64RegisterMsrMtrrFix4kC8000 = 0x00002074, 428 | WHvX64RegisterMsrMtrrFix4kD0000 = 0x00002075, 429 | WHvX64RegisterMsrMtrrFix4kD8000 = 0x00002076, 430 | WHvX64RegisterMsrMtrrFix4kE0000 = 0x00002077, 431 | WHvX64RegisterMsrMtrrFix4kE8000 = 0x00002078, 432 | WHvX64RegisterMsrMtrrFix4kF0000 = 0x00002079, 433 | WHvX64RegisterMsrMtrrFix4kF8000 = 0x0000207A, 434 | 435 | WHvX64RegisterTscAux = 0x0000207B, 436 | 437 | // Interrupt / Event Registers 438 | WHvRegisterPendingInterruption = 0x80000000, 439 | WHvRegisterInterruptState = 0x80000001, 440 | WHvRegisterPendingEvent0 = 0x80000002, 441 | WHvRegisterPendingEvent1 = 0x80000003, 442 | WHvX64RegisterDeliverabilityNotifications = 0x80000004, 443 | 444 | } WHV_REGISTER_NAME; 445 | 446 | typedef union DECLSPEC_ALIGN(16) WHV_UINT128 447 | { 448 | struct 449 | { 450 | UINT64 Low64; 451 | UINT64 High64; 452 | }; 453 | 454 | UINT32 Dword[4]; 455 | } WHV_UINT128; 456 | 457 | typedef union WHV_X64_FP_REGISTER 458 | { 459 | struct 460 | { 461 | UINT64 Mantissa; 462 | UINT64 BiasedExponent:15; 463 | UINT64 Sign:1; 464 | UINT64 Reserved:48; 465 | }; 466 | 467 | WHV_UINT128 AsUINT128; 468 | } WHV_X64_FP_REGISTER; 469 | 470 | typedef union WHV_X64_FP_CONTROL_STATUS_REGISTER 471 | { 472 | struct 473 | { 474 | UINT16 FpControl; 475 | UINT16 FpStatus; 476 | UINT8 FpTag; 477 | UINT8 Reserved; 478 | UINT16 LastFpOp; 479 | union 480 | { 481 | // Long Mode 482 | UINT64 LastFpRip; 483 | 484 | // 32 Bit Mode 485 | struct 486 | { 487 | UINT32 LastFpEip; 488 | UINT16 LastFpCs; 489 | UINT16 Reserved2; 490 | }; 491 | }; 492 | }; 493 | 494 | WHV_UINT128 AsUINT128; 495 | } WHV_X64_FP_CONTROL_STATUS_REGISTER; 496 | 497 | typedef union WHV_X64_XMM_CONTROL_STATUS_REGISTER 498 | { 499 | struct 500 | { 501 | union 502 | { 503 | // Long Mode 504 | UINT64 LastFpRdp; 505 | 506 | // 32 Bit Mode 507 | struct 508 | { 509 | UINT32 LastFpDp; 510 | UINT16 LastFpDs; 511 | UINT16 Reserved; 512 | }; 513 | }; 514 | UINT32 XmmStatusControl; 515 | UINT32 XmmStatusControlMask; 516 | }; 517 | 518 | WHV_UINT128 AsUINT128; 519 | } WHV_X64_XMM_CONTROL_STATUS_REGISTER; 520 | 521 | typedef struct WHV_X64_SEGMENT_REGISTER 522 | { 523 | UINT64 Base; 524 | UINT32 Limit; 525 | UINT16 Selector; 526 | 527 | union 528 | { 529 | struct 530 | { 531 | UINT16 SegmentType:4; 532 | UINT16 NonSystemSegment:1; 533 | UINT16 DescriptorPrivilegeLevel:2; 534 | UINT16 Present:1; 535 | UINT16 Reserved:4; 536 | UINT16 Available:1; 537 | UINT16 Long:1; 538 | UINT16 Default:1; 539 | UINT16 Granularity:1; 540 | }; 541 | 542 | UINT16 Attributes; 543 | }; 544 | } WHV_X64_SEGMENT_REGISTER; 545 | 546 | typedef struct WHV_X64_TABLE_REGISTER 547 | { 548 | UINT16 Pad[3]; 549 | UINT16 Limit; 550 | UINT64 Base; 551 | } WHV_X64_TABLE_REGISTER; 552 | 553 | typedef union WHV_X64_INTERRUPT_STATE_REGISTER 554 | { 555 | struct 556 | { 557 | UINT64 InterruptShadow:1; 558 | UINT64 NmiMasked:1; 559 | UINT64 Reserved:62; 560 | }; 561 | 562 | UINT64 AsUINT64; 563 | } WHV_X64_INTERRUPT_STATE_REGISTER; 564 | 565 | typedef union WHV_X64_PENDING_INTERRUPTION_REGISTER 566 | { 567 | struct 568 | { 569 | UINT32 InterruptionPending:1; 570 | UINT32 InterruptionType:3; // WHV_X64_PENDING_INTERRUPTION_TYPE 571 | UINT32 DeliverErrorCode:1; 572 | UINT32 InstructionLength:4; 573 | UINT32 NestedEvent:1; 574 | UINT32 Reserved:6; 575 | UINT32 InterruptionVector:16; 576 | UINT32 ErrorCode; 577 | }; 578 | 579 | UINT64 AsUINT64; 580 | } WHV_X64_PENDING_INTERRUPTION_REGISTER; 581 | 582 | typedef union WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER 583 | { 584 | struct 585 | { 586 | UINT64 NmiNotification:1; 587 | UINT64 InterruptNotification:1; 588 | UINT64 InterruptPriority:4; 589 | UINT64 Reserved:58; 590 | }; 591 | 592 | UINT64 AsUINT64; 593 | } WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER; 594 | 595 | // 596 | // Register values 597 | // 598 | typedef union WHV_REGISTER_VALUE 599 | { 600 | WHV_UINT128 Reg128; 601 | UINT64 Reg64; 602 | UINT32 Reg32; 603 | UINT16 Reg16; 604 | UINT8 Reg8; 605 | WHV_X64_FP_REGISTER Fp; 606 | WHV_X64_FP_CONTROL_STATUS_REGISTER FpControlStatus; 607 | WHV_X64_XMM_CONTROL_STATUS_REGISTER XmmControlStatus; 608 | WHV_X64_SEGMENT_REGISTER Segment; 609 | WHV_X64_TABLE_REGISTER Table; 610 | WHV_X64_INTERRUPT_STATE_REGISTER InterruptState; 611 | WHV_X64_PENDING_INTERRUPTION_REGISTER PendingInterruption; 612 | WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER DeliverabilityNotifications; 613 | } WHV_REGISTER_VALUE; 614 | 615 | // 616 | // Virtual Processor Execution 617 | // 618 | 619 | // 620 | // Reason for a VM exit 621 | // 622 | typedef enum WHV_RUN_VP_EXIT_REASON 623 | { 624 | WHvRunVpExitReasonNone = 0x00000000, 625 | 626 | // Standard exits caused by operations of the virtual processor 627 | WHvRunVpExitReasonMemoryAccess = 0x00000001, 628 | WHvRunVpExitReasonX64IoPortAccess = 0x00000002, 629 | WHvRunVpExitReasonUnrecoverableException = 0x00000004, 630 | WHvRunVpExitReasonInvalidVpRegisterValue = 0x00000005, 631 | WHvRunVpExitReasonUnsupportedFeature = 0x00000006, 632 | WHvRunVpExitReasonX64InterruptWindow = 0x00000007, 633 | WHvRunVpExitReasonX64Halt = 0x00000008, 634 | 635 | // Additional exits that can be configured through partition properties 636 | WHvRunVpExitReasonX64MsrAccess = 0x00001000, 637 | WHvRunVpExitReasonX64Cpuid = 0x00001001, 638 | WHvRunVpExitReasonException = 0x00001002, 639 | 640 | // Exits caused by the host 641 | WHvRunVpExitReasonCanceled = 0x00002001, 642 | } WHV_RUN_VP_EXIT_REASON; 643 | 644 | // 645 | // Execution state of the virtual processor 646 | // 647 | typedef union WHV_X64_VP_EXECUTION_STATE 648 | { 649 | struct 650 | { 651 | UINT16 Cpl : 2; 652 | UINT16 Cr0Pe : 1; 653 | UINT16 Cr0Am : 1; 654 | UINT16 EferLma : 1; 655 | UINT16 DebugActive : 1; 656 | UINT16 InterruptionPending : 1; 657 | UINT16 Reserved0 : 5; 658 | UINT16 InterruptShadow : 1; 659 | UINT16 Reserved1 : 3; 660 | }; 661 | 662 | UINT16 AsUINT16; 663 | } WHV_X64_VP_EXECUTION_STATE; 664 | 665 | // 666 | // Execution context of a virtual processor at the time of an exit 667 | // 668 | typedef struct WHV_VP_EXIT_CONTEXT 669 | { 670 | WHV_X64_VP_EXECUTION_STATE ExecutionState; 671 | UINT8 InstructionLength : 4; 672 | UINT8 Cr8 : 4; 673 | UINT8 Reserved; 674 | UINT32 Reserved2; 675 | WHV_X64_SEGMENT_REGISTER Cs; 676 | UINT64 Rip; 677 | UINT64 Rflags; 678 | } WHV_VP_EXIT_CONTEXT; 679 | 680 | // 681 | // Context data for a VM exit caused by a memory access (WHvRunVpExitReasonMemoryAccess) 682 | // 683 | typedef enum WHV_MEMORY_ACCESS_TYPE 684 | { 685 | WHvMemoryAccessRead = 0, 686 | WHvMemoryAccessWrite = 1, 687 | WHvMemoryAccessExecute = 2 688 | } WHV_MEMORY_ACCESS_TYPE; 689 | 690 | typedef union WHV_MEMORY_ACCESS_INFO 691 | { 692 | struct { 693 | UINT32 AccessType : 2; // WHV_MEMORY_ACCESS_TYPE 694 | UINT32 GpaUnmapped : 1; 695 | UINT32 GvaValid : 1; 696 | UINT32 Reserved : 28; 697 | }; 698 | 699 | UINT32 AsUINT32; 700 | } WHV_MEMORY_ACCESS_INFO; 701 | 702 | typedef struct WHV_MEMORY_ACCESS_CONTEXT 703 | { 704 | // Context of the virtual processor 705 | UINT8 InstructionByteCount; 706 | UINT8 Reserved[3]; 707 | UINT8 InstructionBytes[16]; 708 | 709 | // Memory access info 710 | WHV_MEMORY_ACCESS_INFO AccessInfo; 711 | WHV_GUEST_PHYSICAL_ADDRESS Gpa; 712 | WHV_GUEST_VIRTUAL_ADDRESS Gva; 713 | } WHV_MEMORY_ACCESS_CONTEXT; 714 | 715 | // 716 | // Context data for an exit caused by an I/O port access (WHvRunVpExitReasonX64IOPortAccess) 717 | // 718 | typedef union WHV_X64_IO_PORT_ACCESS_INFO 719 | { 720 | struct 721 | { 722 | UINT32 IsWrite : 1; 723 | UINT32 AccessSize: 3; 724 | UINT32 StringOp : 1; 725 | UINT32 RepPrefix : 1; 726 | UINT32 Reserved : 26; 727 | }; 728 | 729 | UINT32 AsUINT32; 730 | } WHV_X64_IO_PORT_ACCESS_INFO; 731 | 732 | typedef struct WHV_X64_IO_PORT_ACCESS_CONTEXT 733 | { 734 | // Context of the virtual processor 735 | UINT8 InstructionByteCount; 736 | UINT8 Reserved[3]; 737 | UINT8 InstructionBytes[16]; 738 | 739 | // I/O port access info 740 | WHV_X64_IO_PORT_ACCESS_INFO AccessInfo; 741 | UINT16 PortNumber; 742 | UINT16 Reserved2[3]; 743 | UINT64 Rax; 744 | UINT64 Rcx; 745 | UINT64 Rsi; 746 | UINT64 Rdi; 747 | WHV_X64_SEGMENT_REGISTER Ds; 748 | WHV_X64_SEGMENT_REGISTER Es; 749 | } WHV_X64_IO_PORT_ACCESS_CONTEXT; 750 | 751 | // 752 | // Context data for an exit caused by an MSR access (WHvRunVpExitReasonX64MSRAccess) 753 | // 754 | typedef union WHV_X64_MSR_ACCESS_INFO 755 | { 756 | struct 757 | { 758 | UINT32 IsWrite : 1; 759 | UINT32 Reserved : 31; 760 | }; 761 | 762 | UINT32 AsUINT32; 763 | } WHV_X64_MSR_ACCESS_INFO; 764 | 765 | typedef struct WHV_X64_MSR_ACCESS_CONTEXT 766 | { 767 | // MSR access info 768 | WHV_X64_MSR_ACCESS_INFO AccessInfo; 769 | UINT32 MsrNumber; 770 | UINT64 Rax; 771 | UINT64 Rdx; 772 | } WHV_X64_MSR_ACCESS_CONTEXT; 773 | 774 | // 775 | // Context data for an exit caused by a CPUID call (WHvRunVpExitReasonX64CPUID) 776 | // 777 | typedef struct WHV_X64_CPUID_ACCESS_CONTEXT 778 | { 779 | // CPUID access info 780 | UINT64 Rax; 781 | UINT64 Rcx; 782 | UINT64 Rdx; 783 | UINT64 Rbx; 784 | UINT64 DefaultResultRax; 785 | UINT64 DefaultResultRcx; 786 | UINT64 DefaultResultRdx; 787 | UINT64 DefaultResultRbx; 788 | } WHV_X64_CPUID_ACCESS_CONTEXT; 789 | 790 | // 791 | // Context data for an exit caused by an exception generated by the virtual processor 792 | // (WHvRunVpExitReasonException) 793 | // 794 | typedef union WHV_VP_EXCEPTION_INFO 795 | { 796 | struct 797 | { 798 | UINT32 ErrorCodeValid : 1; 799 | UINT32 SoftwareException : 1; 800 | UINT32 Reserved : 30; 801 | }; 802 | 803 | UINT32 AsUNIT32; 804 | } WHV_VP_EXCEPTION_INFO; 805 | 806 | typedef struct WHV_VP_EXCEPTION_CONTEXT 807 | { 808 | UINT8 InstructionByteCount; 809 | UINT8 Reserved[3]; 810 | UINT8 InstructionBytes[16]; 811 | 812 | // Exception info 813 | WHV_VP_EXCEPTION_INFO ExceptionInfo; 814 | UINT8 ExceptionType; // WHV_EXCEPTION_TYPE 815 | UINT8 Reserved2[3]; 816 | UINT32 ErrorCode; 817 | UINT64 ExceptionParameter; 818 | } WHV_VP_EXCEPTION_CONTEXT; 819 | 820 | // 821 | // Context data for an exit caused by the use of an unsupported processor feature 822 | // (WHvRunVpExitReasonUnsupportedFeature) 823 | // 824 | typedef enum WHV_X64_UNSUPPORTED_FEATURE_CODE 825 | { 826 | WHvUnsupportedFeatureIntercept = 1, 827 | WHvUnsupportedFeatureTaskSwitchTss = 2 828 | } WHV_X64_UNSUPPORTED_FEATURE_CODE; 829 | 830 | typedef struct WHV_X64_UNSUPPORTED_FEATURE_CONTEXT 831 | { 832 | WHV_X64_UNSUPPORTED_FEATURE_CODE FeatureCode; 833 | UINT32 Reserved; 834 | UINT64 FeatureParameter; 835 | } WHV_X64_UNSUPPORTED_FEATURE_CONTEXT; 836 | 837 | // 838 | // Context data for an exit caused by a cancellation from the host (WHvRunVpExitReasonCanceled) 839 | // 840 | typedef enum WHV_RUN_VP_CANCEL_REASON 841 | { 842 | WhvRunVpCancelReasonUser = 0 // Execution canceled by HvCancelRunVirtualProcessor 843 | } WHV_RUN_VP_CANCEL_REASON; 844 | 845 | typedef struct WHV_RUN_VP_CANCELED_CONTEXT 846 | { 847 | WHV_RUN_VP_CANCEL_REASON CancelReason; 848 | } WHV_RUN_VP_CANCELED_CONTEXT; 849 | 850 | // 851 | // Context data for an exit caused by an interrupt delivery window cancellation from the host 852 | // (WHvRunVpExitReasonX64InterruptWindow) 853 | // 854 | typedef enum WHV_X64_PENDING_INTERRUPTION_TYPE 855 | { 856 | WHvX64PendingInterrupt = 0, 857 | WHvX64PendingNmi = 2, 858 | WHvX64PendingException = 3 859 | } WHV_X64_PENDING_INTERRUPTION_TYPE, *PWHV_X64_PENDING_INTERRUPTION_TYPE; 860 | 861 | typedef struct WHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT 862 | { 863 | WHV_X64_PENDING_INTERRUPTION_TYPE DeliverableType; 864 | } WHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT, *PWHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT; 865 | 866 | 867 | // WHvRunVirtualProcessor output buffer 868 | typedef struct WHV_RUN_VP_EXIT_CONTEXT 869 | { 870 | WHV_RUN_VP_EXIT_REASON ExitReason; 871 | UINT32 Reserved; 872 | WHV_VP_EXIT_CONTEXT VpContext; 873 | 874 | union 875 | { 876 | WHV_MEMORY_ACCESS_CONTEXT MemoryAccess; 877 | WHV_X64_IO_PORT_ACCESS_CONTEXT IoPortAccess; 878 | WHV_X64_MSR_ACCESS_CONTEXT MsrAccess; 879 | WHV_X64_CPUID_ACCESS_CONTEXT CpuidAccess; 880 | WHV_VP_EXCEPTION_CONTEXT VpException; 881 | WHV_X64_INTERRUPTION_DELIVERABLE_CONTEXT InterruptWindow; 882 | WHV_X64_UNSUPPORTED_FEATURE_CONTEXT UnsupportedFeature; 883 | WHV_RUN_VP_CANCELED_CONTEXT CancelReason; 884 | }; 885 | } WHV_RUN_VP_EXIT_CONTEXT; 886 | 887 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 888 | #pragma warning(pop) 889 | #endif 890 | 891 | -------------------------------------------------------------------------------- /WinVisorDLL/WinVisorDLL.h: -------------------------------------------------------------------------------- 1 | #define _WIN32_WINNT 0x0600 2 | #define _CRT_SECURE_NO_WARNINGS 3 | #define _CRT_SECURE_NO_DEPRECATE 4 | #include 5 | #include 6 | #include "WinHvApi.h" 7 | #include "..\Common\WinVisorCommon.h" 8 | 9 | #define ThreadBasicInformation 0 10 | 11 | #define PAGE_SIZE 0x1000 12 | 13 | #define CR0_PROTECTED_MODE 0x1 14 | #define CR0_COPROCESSOR_MONITORING 0x2 15 | #define CR0_PAGING 0x80000000 16 | 17 | #define CR4_PAE 0x20 18 | #define CR4_OSFXSR 0x200 19 | #define CR4_OSXSAVE 0x40000 20 | 21 | #define EFER_SYSCALL_ENABLE 0x1 22 | #define EFER_LONG_MODE_ENABLE 0x100 23 | #define EFER_LONG_MODE_ACTIVE 0x400 24 | #define EFER_NX_ENABLE 0x800 25 | 26 | #define EFLAGS_RESERVED_ALWAYS_ON 0x2 27 | 28 | #define PAGE_PRESENT 0x1 29 | #define PAGE_WRITABLE 0x2 30 | #define PAGE_USER 0x4 31 | 32 | #define QWORD UINT64 33 | 34 | #define SEGMENT_SELECTOR_CODE_CPL0 0x10 35 | #define SEGMENT_SELECTOR_CODE_CPL3 0x33 36 | #define SEGMENT_SELECTOR_DATA_CPL0 0x18 37 | #define SEGMENT_SELECTOR_DATA_CPL3 0x2B 38 | #define SEGMENT_SELECTOR_TSS 0x40 39 | 40 | #define PAGE_TABLE_BASE_PHYSICAL_ADDRESS 0x0 41 | 42 | #define SYSCALL_VIRTUAL_ADDRESS 0xFFFF800000000000 43 | #define CPL3_ENTRY_VIRTUAL_ADDRESS 0xFFFF900000000000 44 | #define INTERRUPT_HANDLER_VIRTUAL_ADDRESS 0xFFFFA00000000000 45 | 46 | #define CPL3_INITIAL_RFLAGS 0x202 47 | 48 | #define MAX_SYSCALL_PARAM_COUNT 32 49 | 50 | #define MAX_MAPPED_PAGE_COUNT 256 51 | 52 | #define MAX_GDT_ENTRY_COUNT 0xB 53 | #define MAX_IDT_ENTRY_COUNT 0x100 54 | #define TSS_SIZE 0x68 55 | 56 | #define GDT_PRESENT 0x800000000000 57 | #define GDT_DPL0 0x0 58 | #define GDT_DPL3 0x600000000000 59 | #define GDT_NON_SYSTEM 0x100000000000 60 | #define GDT_CODE 0x80000000000 61 | #define GDT_DATA 0x0 62 | #define GDT_CODE_READ 0x20000000000 63 | #define GDT_DATA_WRITE 0x20000000000 64 | #define GDT_ACCESSED 0x10000000000 65 | #define GDT_LONG 0x20000000000000 66 | #define GDT_TSS 0x90000000000 67 | #define GDT_DB 0x40000000000000 68 | 69 | #define IDT_INTERRUPT_GATE 0xE0000000000 70 | #define IDT_DPL3 0x600000000000 71 | #define IDT_PRESENT 0x800000000000 72 | 73 | #define CPL0_STACK_SIZE PAGE_SIZE 74 | 75 | #define LOG_INFO 0 76 | #define LOG_ERROR 1 77 | #define LOG_DEBUG 2 78 | 79 | #define UNKNOWN_SYSCALL_PARAM_COUNT 0xFFFFFFFF 80 | 81 | #define TABLE_REGISTER_LIMIT_SHIFT 48 82 | 83 | struct CLIENT_ID 84 | { 85 | HANDLE UniqueProcess; 86 | HANDLE UniqueThread; 87 | }; 88 | 89 | struct THREAD_BASIC_INFORMATION 90 | { 91 | DWORD ExitStatus; 92 | PVOID TebBaseAddress; 93 | CLIENT_ID ClientId; 94 | ULONG_PTR AffinityMask; 95 | DWORD Priority; 96 | LONG BasePriority; 97 | }; 98 | 99 | struct SyscallNameEntryStruct 100 | { 101 | char szName[128]; 102 | DWORD dwVirtualAddress; 103 | DWORD dwParamCount; 104 | }; 105 | 106 | struct MappedVirtualAddressStruct 107 | { 108 | DWORD dwInUse; 109 | QWORD qwCreationIndex; 110 | 111 | UINT64 qwVirtualAddress; 112 | UINT64 qwPhysicalAddress; 113 | }; 114 | 115 | struct PageTableStruct 116 | { 117 | UINT64 qwEntries[512]; 118 | }; 119 | 120 | struct VirtualAddressTableIndexesStruct 121 | { 122 | WORD wPML4; 123 | WORD wPDPT; 124 | WORD wPD; 125 | WORD wPT; 126 | WORD wOffset; 127 | }; 128 | 129 | struct PagingStateStruct 130 | { 131 | DWORD dwTotalEntryCount; 132 | DWORD dwNextEntryIndex; 133 | }; 134 | 135 | struct SyscallInfoStruct 136 | { 137 | DWORD dwSyscallIndex; 138 | UINT64 qwParamList[MAX_SYSCALL_PARAM_COUNT]; 139 | }; 140 | 141 | struct CpuRegisterStateStruct 142 | { 143 | QWORD RAX; 144 | QWORD RCX; 145 | QWORD RDX; 146 | QWORD RBX; 147 | QWORD RSP; 148 | QWORD RBP; 149 | QWORD RSI; 150 | QWORD RDI; 151 | QWORD R8; 152 | QWORD R9; 153 | QWORD R10; 154 | QWORD R11; 155 | QWORD R12; 156 | QWORD R13; 157 | QWORD R14; 158 | QWORD R15; 159 | QWORD RIP; 160 | QWORD RFLAGS; 161 | }; 162 | 163 | struct ImportFunctionStruct 164 | { 165 | char *pName; 166 | void **pFunctionPtrAddr; 167 | }; 168 | 169 | struct UINT128 170 | { 171 | UINT64 Low; 172 | UINT64 High; 173 | }; 174 | 175 | struct CpuStateStruct 176 | { 177 | UINT64 GDT[MAX_GDT_ENTRY_COUNT]; 178 | BYTE TSS[TSS_SIZE]; 179 | UINT128 IDT[MAX_IDT_ENTRY_COUNT]; 180 | 181 | VOID *pCPL0_Stack; 182 | VOID *pCPL3_Stack; 183 | 184 | HANDLE hHostThread; 185 | VOID *pHostThreadTEB; 186 | 187 | HANDLE hSyscallProxyReadyEvent; 188 | HANDLE hSyscallWaitingEvent; 189 | HANDLE hSyscallCompleteEvent; 190 | SyscallInfoStruct SyscallInfo; 191 | UINT64 qwSyscallReturnValue; 192 | 193 | CpuRegisterStateStruct CPL3_InitialCpuRegisterState; 194 | }; 195 | 196 | struct InterruptHandlerEntryStruct 197 | { 198 | BYTE bInterruptIndex; 199 | DWORD (*pHandler)(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState); 200 | DWORD dwHasErrorCode; 201 | }; 202 | 203 | struct SyscallHookEntryStruct 204 | { 205 | char *pSyscallName; 206 | DWORD (*pHandler)(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue); 207 | }; 208 | 209 | struct BootloaderParamsStruct 210 | { 211 | UINT64 qwGDT_Limit; 212 | UINT64 qwGDT_Base; 213 | UINT64 qwIDT_Limit; 214 | UINT64 qwIDT_Base; 215 | UINT64 qwTSS_Selector; 216 | UINT64 qwXCR0; 217 | UINT64 qwCPL3_DataSelector; 218 | UINT64 qwCPL3_RFLAGS; 219 | UINT64 qwCPL3_EntryPlaceholderAddress; 220 | }; 221 | 222 | extern DWORD InitialiseLogServer(); 223 | extern DWORD CloseLogServer(); 224 | extern DWORD WriteLog(DWORD dwLogType, char *pStringFormat, ...); 225 | extern BYTE *LoadExecutable(char *pExeFilePath); 226 | extern DWORD ExecuteSyscall(SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue); 227 | extern DWORD HypervisorUtils_Initialise(); 228 | extern DWORD HypervisorUtils_GetRegisterValue_U64(WHV_REGISTER_NAME RegisterName, QWORD *pqwRegisterValue); 229 | extern DWORD HypervisorUtils_SetRegisterValue_U64(WHV_REGISTER_NAME RegisterName, QWORD qwRegisterValue); 230 | extern DWORD HypervisorUtils_SetRegisterValue_Segment(WHV_REGISTER_NAME RegisterName, WORD wSelector, DWORD dwCode); 231 | extern DWORD HypervisorUtils_CreateEnvironment(); 232 | extern DWORD HypervisorUtils_DeleteEnvironment(); 233 | extern DWORD HypervisorUtils_ResumeExecution(WHV_RUN_VP_EXIT_CONTEXT *pVmExitContext); 234 | extern DWORD HypervisorUtils_FlushTLB(); 235 | extern DWORD HypervisorUtils_UnmapGuestMemory(void *pGuestPhysicalAddress, DWORD dwSize); 236 | extern DWORD HypervisorUtils_MapGuestMemory(void *pHostVirtualAddress, void *pGuestPhysicalAddress, DWORD dwSize); 237 | extern DWORD PrepareCPL0(CpuStateStruct *pCpuState); 238 | extern DWORD WINAPI SyscallProxyThread(CpuStateStruct *pCpuState); 239 | extern InterruptHandlerEntryStruct *GetInterruptHandler(BYTE bInterruptIndex); 240 | extern DWORD InterruptHandler_Breakpoint(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState); 241 | extern DWORD InterruptHandler_SingleStep(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState); 242 | extern BYTE *GetInterruptReturn(InterruptHandlerEntryStruct *pInterruptHandlerEntry); 243 | extern DWORD AddPagedVirtualAddress(UINT64 qwVirtualAddress); 244 | extern DWORD CreatePageTables(); 245 | extern DWORD CreateSyscallLists(); 246 | extern char *GetSyscallName(DWORD dwSyscallIndex, DWORD *pdwParamCount); 247 | extern DWORD HandleVmExit(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, WHV_RUN_VP_EXIT_CONTEXT *pVmExitContext); 248 | extern DWORD HandlePageFault(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, UINT64 qwVirtualAddress); 249 | extern DWORD HandleSyscallInstruction(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState); 250 | extern DWORD SyscallHook_NtTerminateThread(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue); 251 | extern DWORD SyscallHook_NtTerminateProcess(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue); 252 | extern DWORD ForwardSyscallToHost(CpuStateStruct *pCpuState, SyscallInfoStruct *pSyscallInfo, UINT64 *pqwReturnValue); 253 | extern DWORD InterruptHandler_LegacySyscall(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState); 254 | extern DWORD HandleGuestSyscall(CpuStateStruct *pCpuState, CpuRegisterStateStruct *pCpuRegisterState, VOID *pUserStackPtr); 255 | extern IMAGE_NT_HEADERS *GetNtHeader(VOID *pModuleBase); 256 | extern DWORD gdwStopLog; 257 | extern DWORD FixNtdllHypervisorSharedPagePtr(); 258 | extern HMODULE ghNtdllBase; 259 | extern DWORD (WINAPI *pNtQueryInformationThread)(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength); 260 | extern DWORD (WINAPI *pNtQuerySystemInformation)(DWORD SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); 261 | extern DWORD GuestProcessExited(DWORD dwExitCode); 262 | extern DWORD ValidateReadPointer(VOID *pAddress, SIZE_T dwLength); 263 | extern DWORD gdwDebugLogEnabled; 264 | extern DWORD PopulateSyscallParamCounts(char *pModuleName, SyscallNameEntryStruct *pSyscallList, DWORD dwSyscallCount); 265 | extern DWORD AppendString(char *pString, SIZE_T dwMaxLength, char *pAppend); 266 | extern DWORD HypervisorUtils_GetRegisters(CpuRegisterStateStruct *pCpuRegisterState); 267 | extern DWORD HypervisorUtils_SetRegisters(CpuRegisterStateStruct *pCpuRegisterState); 268 | extern DWORD PrepareCPL3(CpuStateStruct *pCpuState, WinVisorStartDataStruct *pWinVisorStartData); 269 | extern CpuStateStruct *CreateCpuState(WinVisorStartDataStruct *pWinVisorStartData); 270 | extern DWORD DeleteCpuState(CpuStateStruct *pCpuState); 271 | extern DWORD DeleteSyscallLists(); 272 | extern DWORD DeletePageTables(); 273 | extern DWORD ExecXGETBV(DWORD dwIndex, QWORD *pqwReturnValue); 274 | extern DWORD FixModuleImports(VOID *pModuleBase); 275 | extern DWORD HypervisorEntryPoint(VOID *pExeEntryPoint); 276 | extern HMODULE ghExeBase; 277 | extern DWORD CopyMemoryAndRestoreProtection(VOID *pDestination, VOID *pSource, DWORD dwLength); 278 | extern DWORD gdwLoadedModuleImports; 279 | extern DWORD gdwLogImportSyscallsEnabled; 280 | -------------------------------------------------------------------------------- /WinVisorDLL/WinVisorDLL.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 28 | 31 | 34 | 37 | 40 | 43 | 55 | 58 | 61 | 64 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 96 | 103 | 106 | 109 | 112 | 115 | 119 | 131 | 134 | 137 | 140 | 147 | 150 | 153 | 156 | 159 | 162 | 165 | 168 | 171 | 172 | 180 | 183 | 186 | 189 | 192 | 195 | 204 | 207 | 210 | 213 | 222 | 225 | 228 | 231 | 234 | 237 | 240 | 243 | 246 | 247 | 255 | 258 | 261 | 264 | 267 | 271 | 280 | 283 | 286 | 289 | 298 | 301 | 304 | 307 | 310 | 313 | 316 | 319 | 322 | 323 | 324 | 325 | 326 | 327 | 332 | 335 | 336 | 339 | 340 | 343 | 344 | 347 | 348 | 351 | 352 | 355 | 356 | 359 | 360 | 363 | 364 | 367 | 368 | 371 | 372 | 375 | 376 | 379 | 380 | 383 | 384 | 387 | 388 | 391 | 392 | 395 | 396 | 399 | 400 | 403 | 404 | 407 | 408 | 411 | 412 | 415 | 416 | 417 | 422 | 425 | 426 | 429 | 430 | 433 | 434 | 435 | 440 | 441 | 442 | 443 | 444 | 445 | -------------------------------------------------------------------------------- /winvisor_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x86matthew/WinVisor/e532110338d26c18aa498aff2135012858a2529b/winvisor_screenshot.png -------------------------------------------------------------------------------- /x64/release/WinVisor.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x86matthew/WinVisor/e532110338d26c18aa498aff2135012858a2529b/x64/release/WinVisor.exe -------------------------------------------------------------------------------- /x64/release/WinVisorDLL.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x86matthew/WinVisor/e532110338d26c18aa498aff2135012858a2529b/x64/release/WinVisorDLL.dll --------------------------------------------------------------------------------