├── .gitignore ├── OptionalFeatures.png ├── README.md ├── debug.PNG ├── emulator ├── debug.cpp ├── emulator.cpp ├── emulator.vcxproj ├── loader.cpp ├── memory.cpp ├── partition.cpp ├── processor.cpp ├── sem.h └── syscalls.cpp ├── inc ├── semdef.h ├── semmsg.h └── semprov.h ├── monitor.PNG ├── monitor ├── mon.h ├── monitor.cpp ├── monitor.vcxproj └── ui.cpp ├── provider ├── ntemu.h ├── provider.cpp ├── provider.def ├── provider.h └── provider.vcxproj ├── registers.PNG ├── simpleator.sln └── testapp ├── testapp.cpp └── testapp.vcxproj /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /OptionalFeatures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionescu007/Simpleator/bbed7c2bc97fc5595fd6ff9adc78d8f556fa67f7/OptionalFeatures.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simpleator 2 | 3 | [![Build Status](https://travis-ci.org/joemccann/dillinger.svg?branch=master)](https://travis-ci.org/joemccann/dillinger) 4 | 5 | Simpleator ("Simple-ator") is an innovative Windows-centric x64 user-mode application emulator that leverages several new features that were added in Windows 10 Spring Update (1803), also called "Redstone 4", with additional improvements that were made in Windows 10 October Update (1809), aka "Redstone 5". 6 | 7 | Namely, Simpleator relies on: 8 | * The Hyper-V Platform API (WHVP) which now allows compatible applications to leverage the Hyper-V hypervisor in order to create and manage "Exo" Partitions, providing a similar API as KVM on Linux. 9 | * Changes to the Memory Manager API which resulted in the creation of the VirtualAlloc2 and MapViewOfFile3 APIs that allow constraining alignment, minimum, and maximum address ranges, without custom hooks or hacks. 10 | * Improvements to WHVP in 1809, specifically the ability to partially unmap guest physical address (GPA) ranges. 11 | 12 | It is meant as a Proof-of-Concept on how simpler and faster sandboxed detonation environments could be built, as well as even more resource-limited containers that could run serverless workloads (AWS Lambdas / Azure Functions) without requiring a guest operating system. 13 | 14 | ## Building 15 | 16 | Simpleator can be built with Visual Studio 2017 and the latest Windows SDK (1809). Note that older SDKs cannot be used, as they do not support the newer WHVP definitions, and that Simpleator itself only supports 64-bit Windows 10 systems running builds 17763 or above (Redstone 5 / 1809). 17 | 18 | ## Screenshots 19 | 20 | The main Monitor Window which traces the system calls, shown here displaying the console output from the test guest application: 21 | 22 | ![Monitor](https://raw.githubusercontent.com/ionescu007/Simpleator/master/monitor.PNG) 23 | 24 | The Register Window, which can be used when there's an assertion/issue with the emulator (the UI thread will freeze, hence the "not responding" message): 25 | 26 | ![Registers](https://raw.githubusercontent.com/ionescu007/Simpleator/master/registers.PNG) 27 | 28 | And finally, if enabling the `FLG_SHOW_LDR_SNAPS` flag in the PEB, the Debug Window shows calls to `DbgPrint` from the loader (otherwise, any other DbgPrint calls would show up regardless): 29 | 30 | ![Debug](https://raw.githubusercontent.com/ionescu007/Simpleator/master/debug.PNG) 31 | 32 | ## Motivation 33 | 34 | tbd tbd add links 35 | 36 | Developers have been writing, and leveraging, emulation technologies for decades, so why write yet another emulator? 37 | 38 | First, the introduction of an actual virtualization API in the heart of Windows is an under-publicized dramatic (in a positive way) shift to the previous closed nature of the Hyper-V Platform. While there were undocumented APIs and IOCTLs through the Virtualization Infrastructure Device (VID) library, a supported and stable Win32 layer is a welcomed improvement. Already, QEMU now supports using WHVP for its acceleration, and VirtualBox 6.0 will likely ship with this support as well (it is already implemented in the repository). Only VMWare stands alone and defiant. On this topic, learning how to leverage this new API isn't necessarily an easy topic, so I wanted to learn -- and share with others -- how these new interfaces work. 39 | 40 | Second, when looking at emulation technologies, there are usually three modern driving forces for its use: 41 | * The ability to emulate full operating systems, for purposes of testing, development, education, and compatibility or accessibility. 42 | * The ability to over-subscribe a machine, such as for providing cloud/container-type services 43 | * The ability to safely 'detonate' potentially malicious code and study its behavior 44 | 45 | My main interest was to look at the third bullet -- which so far has been achieved with full system emulation, with some custom implementations that use over-subscription models, but still bringing lots of complexity -- a case in point being most Antivirus Emulators, such as the one implemented in Windows Defender (see some great research [here] and [here]). Furthermore, researchers familiar with Qilin have probably already seen the multitude of simple Python bindings that easily build upon it in order to quickly 'spin-up' a Windows process using an over-subscription model by leveraging QEMU as a full system emulator but yet without a primary OS image. 46 | 47 | I decided to pursue another avenue -- a sort of 'user-mode Windows' implementation, where the only binaries mapped in the guest address space would be the host's OS loader (Ntdll.dll) and the target binary, and where a 256 GB address space would be provided that would have native 1:1 access between guest virtual mappings and host virtual mappings, in a 'sandboxed' process (NOTE: I have not yet implemented the AppContainer-based sandboxing). As long as the emulator would provide the basic kernel-constructed data structures for the loader and system DLLs, the host could run at native speeds, with only privileged Ring transitions causing exits. 48 | 49 | Then, for simplicity, a System Call Provider intercepts the system calls that are being made by the guest VM, and can operate in one of three ways: 50 | 51 | * Forward the call natively to the host operating system 52 | * Adjust certain parameters and/or modify the behavior of the call in order to satisfy the needs of the emulation environment (including perhaps blocking or not implementing the call) 53 | * Similar to the point above, constrain the call from a security point of view, such that the guest code is not attempting to operate on system-call-accessible state that belongs to the host 54 | 55 | Depending on where the needs lie between performance, complexity, compatibility and security, less than 500 lines of code are needed to implement enough of bullets 1 and 2 above to get a simple test application to load, display a "Hello World" message, and exit, with lots of potential security issues in handling its system calls. A doubling of the codebase could probably realistically mitigate most of the security issues in the system calls (minus actual vulnerabilities in the host OS kernel -- which a sandbox could mitigate against). 56 | 57 | But even at 1000 lines of code, since all of the system calls are ultimately natively sent to the operating system, Simpleator behaves more like a 'seccomp' implementation on top of a cgroup on Linux, rather than the much more complex emulators that we see today. 58 | 59 | Finally, it's worth pointing out that there's renewed interest in the cloud computing/containerization space to minimize the resources needed for running workloads such as Amazon Lambdas or Azure Functions, which are serverless pieces of code that run in containers, which still require spinning up an entire guest operating system. With a stricter control of the security boundaries that Simpleator provides, one could imagine the ability to run the JVM or .NET Core as a dedicated application without requiring a full guest OS. 60 | 61 | ## Basic Design 62 | 63 | tbd tbd 64 | 65 | There are 3 main interesting parts (to me) about how Simpleator achieves a unique guest execution environment that makes it much simpler to run Windows applications: 66 | 67 | * The creation of a PEB and TEB with the same kind of data that the kernel's `MiCreatePebOrTeb` functions would set up, but with specific flags to make it easier to run under the guest environment, including 68 | 1) Running as a secure process todo: Flag 69 | 2) Running as a protected process todo: Flag 70 | 3) Disabling IFEO todo: Flag 71 | 72 | * Creating a 1:1 mapping between guest and host addresses, and leveraging the new "address requirements" features to lock down allocations to that range. Note that at the moment, Simpleator maps the authentic `KUSER_SHARED_DATA` region at `0x7FFE0000` which means that the passage of time is 'seen' by the guest VM thanks to the updating of the `SystemTime` and `InterruptTime` fields that are kept up to date by the host. Isolating this region would require a periodic timer to emulate updating this value. 73 | 74 | * Mapping the authentic `Ntdll.dll` image and leveraging the host OS system calls to natively execute most of the loading process, providing access to `INT 2E`, `SYSCALL` and `INT 2C` ring transitions. 75 | 76 | 77 | 78 | Additionally, from a modularity basis, Simpleator is composed of three binaries: 79 | 80 | * `Simpleator.exe` which implements the Debug Monitor. This component is responsible for displaying the UI for the Monitor, Debug, and Register windows, hosting a named pipe which allows the emulator to communicate with it, and loading the emulator with an appropriate environment (today, this means setting up the 256 GB address space reservation, in the future, this would also mean the sandbox). 81 | * `Provider.dll` which implements the System Call Provider for Windows 10 1809 (RS5) and Windows 10 1903 (19H1), the current builds supported. 82 | * `Emulator.exe` which implements the actual WHVP-accelerated emulator code. It is mainly responsible for communicating with the Debug Monitor over the pipe, handling the ring transition code to talk to and from the System Call Provider, and doing the initial address space setup and PE loading of the `Ntdll.dll` loader library and the target application binary. 83 | 84 | 85 | 86 | ## Testing 87 | 88 | First, you must install the Windows Hypervisor Platform, which also requires Hyper-V to be installed and enabled. You can do so either by using the following command-line: 89 | 90 | ```Dism /Online /Enable-Feature /FeatureName:HypervisorPlatform``` 91 | 92 | Or by launching the GUI as below: 93 | 94 | ```OptionalFeatures.exe``` 95 | 96 | And then checking the "Hyper-V" and "Windows Hypervisor Platform" checkboxes, as seen in the screenshot below. 97 | 98 | ![Optional Features](https://raw.githubusercontent.com/ionescu007/Simpleator/master/OptionalFeatures.png) 99 | 100 | You must have administrative rights for usage of any of these commands. 101 | 102 | Obviously, please make sure that your hardware supports Hardware Virtualization Technology (such as Intel VT-x). 103 | 104 | ## References 105 | 106 | If you would like to know more about my research or work, I invite you check out my blog at http://www.alex-ionescu.com as well as my training & consulting company, Winsider Seminars & Solutions Inc., at http://www.windows-internals.com. 107 | 108 | tbd tbd 109 | 110 | ## Caveats and Limitations 111 | 112 | Simpleator is designed to minimize code size and complexity -- this does come at a cost of robustness and most importantly, security. For example, in the current implementation, `NtCreateFile`, `NtOpenFile` and `NtWriteFile` are fully passed through to the host OS kernel, meaning that a 'malicious' payload could overwrite any files on disk that the host emulator process has access to, since there is no additional sandboxing around the host. 113 | 114 | Furthermore, note that only the _strict minimum number of system calls_ were implemented to get the `Testapp.exe` application to launch, print its text, and exit. Running a more complex application such as `Cmd.exe` will require significantly more work, especially as certain APIs expect a connection to CSRSS to be made over LPC and for particular data to be returned back. Currently, Simpleator pretends that it is a Secure VTL-1 Protected Process, which limits significantly what some of the guest APIs attempt to do, and therefore, certain calls outright crash (such as, for example, some of those around locale). 115 | 116 | More complex emulation and modification of the guest's address space would be required to unblock such API usage. 117 | 118 | ***Simpleator does not do much error checking, validation, and exception handling. It is not robust software designed for production use, but rather a reference code base***. 119 | 120 | ## License 121 | 122 | ``` 123 | Copyright 2018 Alex Ionescu. All rights reserved. 124 | 125 | Redistribution and use in source and binary forms, with or without modification, are permitted provided 126 | that the following conditions are met: 127 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and 128 | the following disclaimer. 129 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 130 | and the following disclaimer in the documentation and/or other materials provided with the 131 | distribution. 132 | 133 | THIS SOFTWARE IS PROVIDED BY ALEX IONESCU ``AS IS'' AND ANY EXPRESS OR IMPLIED 134 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 135 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ALEX IONESCU 136 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 137 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 138 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 139 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 140 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 141 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 142 | 143 | The views and conclusions contained in the software and documentation are those of the authors and 144 | should not be interpreted as representing official policies, either expressed or implied, of Alex Ionescu. 145 | ``` 146 | -------------------------------------------------------------------------------- /debug.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionescu007/Simpleator/bbed7c2bc97fc5595fd6ff9adc78d8f556fa67f7/debug.PNG -------------------------------------------------------------------------------- /emulator/debug.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | debug.cpp 8 | 9 | Abstract: 10 | 11 | This module implements the debugging pipe for talking with the monitor 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | auto 26 | SemDbgTraceSystemCall ( 27 | _In_ USHORT Index, 28 | _In_ PCHAR Name, 29 | _In_ PULONG_PTR Arguments, 30 | _In_ USHORT ArgumentCount, 31 | _In_ NTSTATUS Result 32 | ) -> VOID 33 | { 34 | SEM_PIPE_BUFFER_MSG msg; 35 | 36 | // 37 | // Build the payload 38 | // 39 | msg.MessageType = SemSystemCall; 40 | msg.SystemCall.ArgumentCount = ArgumentCount; 41 | msg.SystemCall.Index = Index; 42 | memcpy(msg.SystemCall.Name, Name, strlen(Name) + 1); 43 | for (auto i = 0UL; i < ArgumentCount; i++) 44 | { 45 | msg.SystemCall.Arguments[i] = Arguments[i]; 46 | } 47 | msg.SystemCall.Result = Result; 48 | 49 | // 50 | // Send it to the monitor 51 | // 52 | WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &msg, sizeof(msg), NULL, NULL); 53 | } 54 | 55 | auto 56 | SemVpDumpRegisters ( 57 | _In_ PSEM_VP Vp 58 | ) -> VOID 59 | { 60 | SEM_PIPE_BUFFER_MSG msg; 61 | auto regs = Vp->Registers; 62 | 63 | // 64 | // Build the payload and send it to the monitor 65 | // 66 | msg.MessageType = SemUpdateRegisters; 67 | memcpy(&msg.Registers, regs, sizeof(msg.Registers)); 68 | WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &msg, sizeof(msg), NULL, NULL); 69 | }; 70 | 71 | auto 72 | SemVmError ( 73 | _In_ PCHAR ErrorString, 74 | ... 75 | ) -> VOID 76 | { 77 | SEM_PIPE_BUFFER_MSG pipeMessage; 78 | PCHAR endString; 79 | va_list argList; 80 | va_start(argList, ErrorString); 81 | 82 | // 83 | // Build the payload and send it to the monitor 84 | // 85 | pipeMessage.MessageType = SemInternalError; 86 | StringCbVPrintfExA(pipeMessage.Data, 87 | sizeof(pipeMessage.Data), 88 | &endString, 89 | NULL, 90 | 0, 91 | ErrorString, 92 | argList); 93 | WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), 94 | &pipeMessage, 95 | static_cast( 96 | endString + 1 - reinterpret_cast(&pipeMessage)), 97 | NULL, 98 | NULL); 99 | va_end(argList); 100 | } 101 | 102 | auto 103 | SemVmDebugPrint ( 104 | _In_ PCHAR Buffer, 105 | _In_ ULONG Length 106 | ) -> VOID 107 | { 108 | SEM_PIPE_BUFFER_MSG msg; 109 | 110 | // 111 | // Build the payload and send it to the monitor 112 | // 113 | msg.MessageType = SemUpdateDebugTrace; 114 | memcpy(msg.Data, Buffer, Length); 115 | msg.Data[Length] = ANSI_NULL; 116 | WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), 117 | &msg, 118 | FIELD_OFFSET(SEM_PIPE_BUFFER_MSG, Data) + Length, 119 | NULL, 120 | NULL); 121 | } 122 | -------------------------------------------------------------------------------- /emulator/emulator.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | emulator.cpp 8 | 9 | Abstract: 10 | 11 | This module implements the main Simple Emulator initialization code. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | const static SEM_PROVIDER_CALLBACKS s_SemCallbacks = 26 | { 27 | IsGuestMemoryPtr, 28 | SemMmMapSharedImage, 29 | SemMmMapSharedMemory, 30 | SemMmUnmapSharedMemory, 31 | SemRegisterSystemCall, 32 | SemRegisterDebugTrap, 33 | SemVmDebugPrint, 34 | SemVmError, 35 | SemVpSwitchMode, 36 | SemVpRestoreExceptionContext, 37 | SemVpGetCurrentTeb 38 | }; 39 | 40 | auto 41 | SemMain ( 42 | VOID 43 | ) 44 | { 45 | ULONG_PTR entryPoint; 46 | PSEM_PARTITION semPartition; 47 | SEM_VP_THREAD_STATE threadState; 48 | UINT32 bytesWritten; 49 | WHV_CAPABILITY whvCapability; 50 | auto dwExitCode = -1; 51 | 52 | // 53 | // First, check if the feature is enabled 54 | // 55 | auto hr = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, 56 | &whvCapability, 57 | sizeof(whvCapability), 58 | &bytesWritten); 59 | if (FAILED(hr) || (whvCapability.HypervisorPresent == FALSE)) 60 | { 61 | SemVmError("Windows Hypervisor Platform is not enabled: %lx\n", hr); 62 | goto Cleanup; 63 | } 64 | 65 | // 66 | // Next, check for partial unmapping support 67 | // 68 | hr = WHvGetCapability(WHvCapabilityCodeFeatures, 69 | &whvCapability, 70 | sizeof(whvCapability), 71 | &bytesWritten); 72 | if (FAILED(hr) || (whvCapability.Features.PartialUnmap == FALSE)) 73 | { 74 | SemVmError("Windows Hypervisor Platform is not enabled: %lx\n", hr); 75 | goto Cleanup; 76 | } 77 | 78 | // 79 | // Extended Vid is active, create the hypervisor partition 80 | // 81 | hr = SemVmCreatePartition(&semPartition); 82 | if (FAILED(hr)) 83 | { 84 | SemVmError("Partition creation failed: %lx\n", hr); 85 | goto Cleanup; 86 | } 87 | 88 | // 89 | // Loader should've created a 256GB region for us, we can now free it 90 | // 91 | auto bRes = VirtualFreeEx(GetCurrentProcess(), 92 | s_LowestValidAddress, 93 | 0, 94 | MEM_RELEASE); 95 | if (bRes != FALSE) 96 | { 97 | bRes = VirtualFreeEx(GetCurrentProcess(), 98 | s_UserSharedDataEnd, 99 | 0, 100 | MEM_RELEASE); 101 | } 102 | if ((bRes == FALSE) && (IsDebuggerPresent() == FALSE)) 103 | { 104 | SemVmError("256GB Reservation not found -- " 105 | "please do not launch this binary directly\n"); 106 | goto Cleanup; 107 | } 108 | 109 | // 110 | // Create the address space of the guest 111 | // 112 | hr = SemVmCreateAddressSpace(semPartition); 113 | if (FAILED(hr)) 114 | { 115 | SemVmError("Address space creation failed: %lx\n", hr); 116 | goto Cleanup; 117 | } 118 | 119 | // 120 | // Initialize the address space for the target image 121 | // 122 | hr = SemVmInitializeAddressSpace(semPartition, 123 | wcschr(GetCommandLine(), L' ') + 2, 124 | &entryPoint); 125 | if (FAILED(hr)) 126 | { 127 | SemVmError("Address space initialization failed: %lx\n", hr); 128 | goto Cleanup; 129 | } 130 | 131 | // 132 | // Register system call support 133 | // 134 | SemRegisterSystemCallProvider(&s_SemCallbacks); 135 | 136 | // 137 | // Run the processor thread 138 | // 139 | threadState.CpuIndex = 0; 140 | threadState.Partition = semPartition; 141 | threadState.InitialPc = entryPoint; 142 | threadState.InitialStack = s_StackLimit; 143 | auto hThread = CreateThread(NULL, 144 | 0, 145 | SemVpExecuteProcessor, 146 | &threadState, 147 | 0, 148 | NULL); 149 | if (hThread == nullptr) 150 | { 151 | SemVmError("Emulator thread creation failed: %lx\n", GetLastError()); 152 | goto Cleanup; 153 | } 154 | 155 | // 156 | // When the thread dies, we exit too 157 | // 158 | WaitForSingleObject(hThread, INFINITE); 159 | dwExitCode = 0; 160 | 161 | Cleanup: 162 | return dwExitCode; 163 | } 164 | -------------------------------------------------------------------------------- /emulator/emulator.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 15.0 11 | {7382AEC3-1D55-42E2-BD8B-621DCCF3FD91} 12 | Win32Proj 13 | 10.0.18290.0 14 | emulator 15 | 16 | 17 | 18 | Application 19 | false 20 | v141 21 | true 22 | Unicode 23 | 24 | 25 | 26 | 27 | ..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) 28 | 29 | 30 | 31 | Level4 32 | Disabled 33 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 34 | sem.h 35 | ProgramDatabase 36 | AnySuitable 37 | true 38 | Speed 39 | true 40 | true 41 | MultiThreaded 42 | true 43 | 44 | 45 | Console 46 | ntdllp.lib;winhvplatform.lib;winhvemulation.lib;onecoreuap.lib;BufferOverflowU.lib;%(AdditionalDependencies) 47 | DebugFull 48 | SemMain 49 | true 50 | true 51 | true 52 | 53 | 54 | 55 | 56 | Level4 57 | Use 58 | MaxSpeed 59 | true 60 | true 61 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Create 71 | sem.h 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | {88de519d-200d-44b2-a66c-58caa1eab946} 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /emulator/loader.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | loader.cpp 8 | 9 | Abstract: 10 | 11 | This module implements a simple host-based PE Loader for mapping the images 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | auto 26 | SemLdrLoadImage ( 27 | _In_ PSEM_PARTITION Partition, 28 | _In_ PWCHAR ImagePath, 29 | _In_ ULONG_PTR ImageBase, 30 | _Out_opt_ PULONG_PTR ThreadThunk, 31 | _Out_opt_ PULONG_PTR EntryPoint 32 | ) -> HRESULT 33 | { 34 | WCHAR fileName[MAX_PATH]; 35 | PVOID mapBase; 36 | 37 | // 38 | // Load the DLL somewhere in our address space 39 | // 40 | auto base = LoadLibrary(ImagePath); 41 | if (base == nullptr) 42 | { 43 | return HRESULT_FROM_WIN32(GetLastError()); 44 | } 45 | 46 | // 47 | // Get the entrypoint 48 | // 49 | auto ntHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)base + ((PIMAGE_DOS_HEADER)base)->e_lfanew); 50 | auto entryPoint = ntHeader->OptionalHeader.AddressOfEntryPoint; 51 | 52 | // 53 | // Get the full path 54 | // 55 | auto bRes = GetModuleFileName(base, fileName, MAX_PATH); 56 | if (bRes == FALSE) 57 | { 58 | FreeLibrary(base); 59 | return HRESULT_FROM_WIN32(GetLastError()); 60 | } 61 | 62 | // 63 | // Now open our own handle to the mapped file 64 | // 65 | auto fileHandle = CreateFile(fileName, 66 | GENERIC_READ | GENERIC_EXECUTE, 67 | FILE_SHARE_READ, 68 | NULL, 69 | OPEN_ALWAYS, 70 | 0, 71 | NULL); 72 | if (fileHandle == INVALID_HANDLE_VALUE) 73 | { 74 | FreeLibrary(base); 75 | return HRESULT_FROM_WIN32(GetLastError()); 76 | } 77 | 78 | // 79 | // Create our own section mapping for it 80 | // 81 | auto mapHandle = CreateFileMapping(fileHandle, 82 | NULL, 83 | SEC_IMAGE | PAGE_EXECUTE_READ, 84 | 0, 85 | 0, 86 | NULL); 87 | if (mapHandle == INVALID_HANDLE_VALUE) 88 | { 89 | CloseHandle(fileHandle); 90 | FreeLibrary(base); 91 | return HRESULT_FROM_WIN32(GetLastError()); 92 | } 93 | 94 | // 95 | // Now re-map it again into the hypervisor 96 | // 97 | auto hr = SemMmMapGuestImage(Partition, 98 | mapHandle, 99 | ImageBase, 100 | ntHeader->OptionalHeader.SizeOfImage, 101 | &mapBase); 102 | 103 | // 104 | // Close all the handles and free the library -- keeping only the copy 105 | // 106 | CloseHandle(fileHandle); 107 | CloseHandle(mapHandle); 108 | FreeLibrary(base); 109 | if (FAILED(hr)) 110 | { 111 | return hr; 112 | } 113 | 114 | // 115 | // Compute the delta between the two images 116 | // 117 | auto delta = reinterpret_cast(base) - 118 | reinterpret_cast(mapBase); 119 | 120 | // 121 | // Check if this is the system image, or the application image 122 | // 123 | if (ThreadThunk == nullptr) 124 | { 125 | // 126 | // Check if the image has an entrypoint 127 | // 128 | if (entryPoint != 0) 129 | { 130 | *EntryPoint = reinterpret_cast(mapBase) + entryPoint; 131 | } 132 | } 133 | else 134 | { 135 | // 136 | // It doesn't -- assume this is the system image 137 | // 138 | auto thunkAddress = GetProcAddress(base, "LdrInitializeThunk"); 139 | auto startAddress = GetProcAddress(base, "RtlUserThreadStart"); 140 | *EntryPoint = reinterpret_cast(thunkAddress) - delta; 141 | *ThreadThunk = reinterpret_cast(startAddress) - delta; 142 | } 143 | 144 | // 145 | // All done 146 | // 147 | return hr; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /emulator/memory.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | memory.cpp 8 | 9 | Abstract: 10 | 11 | This module implements the main GPA<->HVA handling functions for the guest 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | EXTERN_C void __std_terminate(void) { } 26 | EXTERN_C void __CxxFrameHandler3(void) { } 27 | 28 | auto 29 | SemAllocateGuestPrivateMemory ( 30 | _In_ PSEM_PARTITION Partition, 31 | _In_ ULONG_PTR GuestVa, 32 | _In_ SIZE_T Size, 33 | _Outptr_ PVOID* HostVa 34 | ) -> HRESULT 35 | { 36 | // 37 | // Allocate top-down as to not disturb the guest VA 38 | // 39 | *HostVa = VirtualAlloc2(GetCurrentProcess(), 40 | NULL, 41 | Size, 42 | MEM_COMMIT | MEM_TOP_DOWN, 43 | PAGE_READWRITE, 44 | NULL, 45 | 0); 46 | if (*HostVa == nullptr) 47 | { 48 | return HRESULT_FROM_WIN32(GetLastError()); 49 | } 50 | 51 | // 52 | // Map it into the partition 53 | // 54 | return WHvMapGpaRange(Partition->PartitionHandle, 55 | *HostVa, 56 | GuestVa, 57 | Size, 58 | WHvMapGpaRangeFlagRead | 59 | WHvMapGpaRangeFlagWrite); 60 | } 61 | 62 | auto 63 | SemMmMapGuestImage ( 64 | _In_opt_ PSEM_PARTITION Partition, 65 | _In_ HANDLE ImageHandle, 66 | _In_ ULONG_PTR GuestVa, 67 | _In_ SIZE_T Size, 68 | _Outptr_ PVOID* HostVa 69 | ) -> HRESULT 70 | { 71 | // 72 | // If no partition was passed in, use the current partition 73 | // 74 | if (Partition == nullptr) 75 | { 76 | Partition = SemPartitionFromVp(t_CurrentVp); 77 | } 78 | 79 | // 80 | // Map the image at the desired base address 81 | // 82 | *HostVa = MapViewOfFile3(ImageHandle, 83 | GetCurrentProcess(), 84 | reinterpret_cast(GuestVa), 85 | 0, 86 | 0, 87 | MEM_DIFFERENT_IMAGE_BASE_OK, 88 | PAGE_EXECUTE_READ, 89 | NULL, 90 | NULL); 91 | if (*HostVa == nullptr) 92 | { 93 | return HRESULT_FROM_WIN32(GetLastError()); 94 | } 95 | 96 | // 97 | // Map the image section into the partition. TODO: Unmap on failure? 98 | // 99 | auto hr = WHvMapGpaRange(Partition->PartitionHandle, 100 | *HostVa, 101 | GuestVa, 102 | Size, 103 | WHvMapGpaRangeFlagRead | 104 | WHvMapGpaRangeFlagWrite | 105 | WHvMapGpaRangeFlagExecute); 106 | 107 | return hr; 108 | } 109 | 110 | auto 111 | SemMmMapSharedImage ( 112 | _In_opt_ PSEM_PARTITION Partition, 113 | _In_ ULONG_PTR GuestVa, 114 | _In_ SIZE_T Size 115 | ) -> HRESULT 116 | { 117 | // 118 | // If no partition was passed in, use the current partition 119 | // 120 | if (Partition == nullptr) 121 | { 122 | Partition = SemPartitionFromVp(t_CurrentVp); 123 | } 124 | 125 | // 126 | // Map it into the partition 127 | // 128 | return WHvMapGpaRange(Partition->PartitionHandle, 129 | reinterpret_cast(GuestVa), 130 | GuestVa, 131 | Size, 132 | WHvMapGpaRangeFlagRead | 133 | WHvMapGpaRangeFlagWrite | 134 | WHvMapGpaRangeFlagExecute); 135 | } 136 | 137 | auto 138 | SemMmUnmapSharedMemory ( 139 | _In_opt_ PSEM_PARTITION Partition, 140 | _In_ ULONG_PTR GuestVa, 141 | _In_ SIZE_T Size, 142 | _In_opt_ PVOID HostVa 143 | ) -> HRESULT 144 | { 145 | // 146 | // If no partition was passed in, use the current partition 147 | // 148 | if (Partition == nullptr) 149 | { 150 | Partition = SemPartitionFromVp(t_CurrentVp); 151 | } 152 | 153 | // 154 | // Unmap the range from the virtual machine 155 | // 156 | auto hr = WHvUnmapGpaRange(Partition->PartitionHandle, GuestVa, Size); 157 | if (FAILED(hr)) 158 | { 159 | return hr; 160 | } 161 | 162 | // 163 | // Do we own the host allocation as well? 164 | // 165 | if (HostVa != nullptr) 166 | { 167 | // 168 | // Free it 169 | // 170 | auto bRes = VirtualFreeEx(GetCurrentProcess(), HostVa, 0, MEM_RELEASE); 171 | if (bRes == FALSE) 172 | { 173 | return HRESULT_FROM_WIN32(GetLastError()); 174 | } 175 | } 176 | 177 | // 178 | // All done 179 | // 180 | return hr; 181 | } 182 | 183 | auto 184 | SemMmMapSharedMemory ( 185 | _In_opt_ PSEM_PARTITION Partition, 186 | _In_ ULONG_PTR GuestVa, 187 | _In_ SIZE_T Size, 188 | _Outptr_opt_ PVOID* HostVa 189 | ) -> HRESULT 190 | { 191 | // 192 | // If no partition was passed in, use the current partition 193 | // 194 | if (Partition == nullptr) 195 | { 196 | Partition = SemPartitionFromVp(t_CurrentVp); 197 | } 198 | 199 | // 200 | // Carve out a piece from our reservation 201 | // 202 | if (HostVa != nullptr) 203 | { 204 | *HostVa = VirtualAlloc2(GetCurrentProcess(), 205 | reinterpret_cast(GuestVa), 206 | Size, 207 | MEM_COMMIT | MEM_RESERVE, 208 | PAGE_READWRITE, 209 | NULL, 210 | 0); 211 | if (*HostVa == nullptr) 212 | { 213 | return HRESULT_FROM_WIN32(GetLastError()); 214 | } 215 | } 216 | 217 | // 218 | // Map it into the partition. TODO: Unmap on failure? 219 | // 220 | return WHvMapGpaRange(Partition->PartitionHandle, 221 | reinterpret_cast(GuestVa), 222 | GuestVa, 223 | Size, 224 | WHvMapGpaRangeFlagRead | 225 | WHvMapGpaRangeFlagWrite); 226 | } 227 | -------------------------------------------------------------------------------- /emulator/partition.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | partition.cpp 8 | 9 | Abstract: 10 | 11 | This module implements initialization of the guest VA and its VM partition 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | typedef struct _MMPTE_HARDWARE 26 | { 27 | union 28 | { 29 | struct 30 | { 31 | UINT64 Valid : 1; 32 | UINT64 Write : 1; 33 | UINT64 Owner : 1; 34 | UINT64 WriteThrough : 1; 35 | UINT64 CacheDisable : 1; 36 | UINT64 Accessed : 1; 37 | UINT64 Dirty : 1; 38 | UINT64 LargePage : 1; 39 | UINT64 Available : 4; 40 | UINT64 PageFrameNumber : 36; 41 | UINT64 ReservedForHardware : 4; 42 | UINT64 ReservedForSoftware : 11; 43 | UINT64 NoExecute : 1; 44 | }; 45 | UINT64 AsUlonglong; 46 | }; 47 | } MMPTE_HARDWARE, *PMMPTE_HARDWARE; 48 | C_ASSERT(sizeof(MMPTE_HARDWARE) == 8); 49 | 50 | auto 51 | SemVmCreateAddressSpace ( 52 | _In_ PSEM_PARTITION Partition 53 | ) -> HRESULT 54 | { 55 | PMMPTE_HARDWARE pml4; 56 | MMPTE_HARDWARE pdpte; 57 | 58 | // 59 | // Allocate the PML4 60 | // 61 | auto hr = SemAllocateGuestPrivateMemory(Partition, 62 | s_Pml4PhysicalAddress, 63 | 512 * sizeof(*pml4) + 64 | 512 * sizeof(pdpte), 65 | reinterpret_cast(&pml4)); 66 | if (FAILED(hr)) 67 | { 68 | return hr; 69 | } 70 | 71 | // 72 | // Build a valid user-mode PML4E 73 | // 74 | pml4[0].AsUlonglong = 0; 75 | pml4[0].Valid = 1; 76 | pml4[0].Write = 1; 77 | pml4[0].Owner = 1; 78 | pml4[0].PageFrameNumber = (s_Pml4PhysicalAddress / USN_PAGE_SIZE) + 1; 79 | 80 | // 81 | // Build a valid user-mode 1GB PDPTE 82 | // 83 | pdpte.AsUlonglong = 0; 84 | pdpte.Valid = 1; 85 | pdpte.Write = 1; 86 | pdpte.Owner = 1; 87 | pdpte.LargePage = 1; 88 | 89 | // 90 | // Loop over the PDPT (PML3) minus the last 1GB 91 | // 92 | auto pdpt = &pml4[512]; 93 | for (auto i = 0; i < 511; i++) 94 | { 95 | // 96 | // Set the PDPTE to the next valid 1GB of RAM, creating a 1:1 map 97 | // 98 | pdpt[i] = pdpte; 99 | pdpt[i].PageFrameNumber = (i * s_1GB) / USN_PAGE_SIZE; 100 | } 101 | 102 | // 103 | // We mark the last GB of RAM as off-limits 104 | // This corresponds to 0x0000`007FC0000000->0x0000`007FFFFFFFFF 105 | // 106 | pdpt[511].Valid = 0; 107 | return ERROR_SUCCESS; 108 | } 109 | 110 | auto 111 | SemVmCreatePartition ( 112 | _Outptr_ PSEM_PARTITION* Partition 113 | ) -> HRESULT 114 | { 115 | WHV_PARTITION_PROPERTY prop; 116 | WHV_PARTITION_HANDLE partitionHandle; 117 | 118 | // 119 | // Create a Hyper-V External Partition 120 | // 121 | auto hr = WHvCreatePartition(&partitionHandle); 122 | if (FAILED(hr)) 123 | { 124 | return hr; 125 | } 126 | 127 | // 128 | // Allow a single processor 129 | // 130 | RtlZeroMemory(&prop, sizeof(prop)); 131 | prop.ProcessorCount = 1; 132 | hr = WHvSetPartitionProperty(partitionHandle, 133 | WHvPartitionPropertyCodeProcessorCount, 134 | &prop, 135 | sizeof(prop)); 136 | if (FAILED(hr)) 137 | { 138 | return hr; 139 | } 140 | 141 | // 142 | // Activate the partition 143 | // 144 | hr = WHvSetupPartition(partitionHandle); 145 | if (FAILED(hr)) 146 | { 147 | return hr; 148 | } 149 | 150 | // 151 | // Return the partition and result 152 | // 153 | *Partition = reinterpret_cast(HeapAlloc(GetProcessHeap(), 154 | 0, 155 | sizeof(**Partition))); 156 | if (*Partition == nullptr) 157 | { 158 | hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); 159 | } 160 | else 161 | { 162 | (*Partition)->PartitionHandle = partitionHandle; 163 | } 164 | return hr; 165 | } 166 | 167 | auto 168 | SemVmInitializeAddressSpace ( 169 | _In_ PSEM_PARTITION Partition, 170 | _In_ PWCHAR ImageFileName, 171 | _Out_ PULONG_PTR EntryPoint 172 | ) -> HRESULT 173 | { 174 | PTHREAD_CONTROL_BLOCK threadBlock; 175 | WIN32_MEMORY_REGION_INFORMATION regionInfo; 176 | 177 | // 178 | // Allocate our thread control block, which includes the PEB, TEB and stack 179 | // 180 | auto hr = SemMmMapSharedMemory(Partition, 181 | s_TcbRegionAddress, 182 | sizeof(*threadBlock), 183 | reinterpret_cast(&threadBlock)); 184 | if (FAILED(hr)) 185 | { 186 | return hr; 187 | } 188 | 189 | // 190 | // Capture address of TEB and PEB 191 | // 192 | auto teb = (PTEB)threadBlock->Teb; 193 | auto peb = (PPEB)threadBlock->Peb; 194 | auto ppb = (PRTL_USER_PROCESS_PARAMETERS)threadBlock->Ppb; 195 | auto currentPeb = NtCurrentTeb()->ProcessEnvironmentBlock; 196 | 197 | // 198 | // Configure basic TEB fields 199 | // 200 | auto tib = (PNT_TIB)teb; 201 | tib->Self = tib; 202 | teb->Reserved1[8] = (PVOID)(ULONG_PTR)GetCurrentProcessId(); 203 | teb->Reserved1[9] = (PVOID)(ULONG_PTR)GetCurrentThreadId(); 204 | teb->ProcessEnvironmentBlock = (PPEB)peb; 205 | 206 | // 207 | // Configure basic PEB fields, set global flags to show loader snaps 208 | // 209 | peb->ProcessParameters = (PRTL_USER_PROCESS_PARAMETERS)ppb; 210 | peb->Reserved9[10] = reinterpret_cast(1 | ((UINT64)2 << 32)); 211 | peb->Reserved3[1] = reinterpret_cast(s_AppImageBase); 212 | peb->Reserved9[0] = threadBlock->ApiSetMap; 213 | 214 | // 215 | // Configure the basic PPB fields 216 | // 217 | *(PULONG)&ppb->Reserved1[4] = 4096; 218 | *(PULONG)&ppb->Reserved1[8] = 0x80004001; 219 | auto ppbPath = (PWCHAR)&threadBlock->Ppb[512]; 220 | wcscpy_s(ppbPath, MAX_PATH, ImageFileName); 221 | RtlInitUnicodeString(&ppb->ImagePathName, ppbPath); 222 | RtlInitUnicodeString(&ppb->CommandLine, ppbPath); 223 | RtlInitUnicodeString((PUNICODE_STRING)&ppb->Reserved2[5], ppbPath); 224 | 225 | // 226 | // Get the size of the NLS Table Data 227 | // 228 | auto nlsData = currentPeb->Reserved9[7]; 229 | auto bRes = QueryVirtualMemoryInformation(GetCurrentProcess(), 230 | nlsData, 231 | MemoryRegionInfo, 232 | ®ionInfo, 233 | sizeof(regionInfo), 234 | NULL); 235 | if (bRes == FALSE) 236 | { 237 | return HRESULT_FROM_WIN32(GetLastError()); 238 | } 239 | 240 | // 241 | // Figure out the size of the tables by working backward 242 | // 243 | auto ansiSize = (ULONG_PTR)currentPeb->Reserved9[8] - 244 | (ULONG_PTR)nlsData; 245 | auto oemSize = (ULONG_PTR)currentPeb->Reserved9[9] - 246 | (ULONG_PTR)currentPeb->Reserved9[8]; 247 | 248 | // 249 | // Copy all the data (ANSI, OEM, Unicode) and set PEB pointers 250 | // 251 | auto ansiTable = threadBlock->NlsTables; 252 | RtlCopyMemory(ansiTable, nlsData, regionInfo.RegionSize); 253 | peb->Reserved9[7] = ansiTable; 254 | peb->Reserved9[8] = ansiTable + ansiSize; 255 | peb->Reserved9[9] = ansiTable + ansiSize + oemSize; 256 | 257 | // 258 | // Get the size of the API Set Map and copy it 259 | // 260 | bRes = QueryVirtualMemoryInformation(GetCurrentProcess(), 261 | currentPeb->Reserved9[0], 262 | MemoryRegionInfo, 263 | ®ionInfo, 264 | sizeof(regionInfo), 265 | NULL); 266 | if (bRes == FALSE) 267 | { 268 | return HRESULT_FROM_WIN32(GetLastError()); 269 | } 270 | RtlCopyMemory(peb->Reserved9[0], 271 | currentPeb->Reserved9[0], 272 | regionInfo.RegionSize); 273 | 274 | // 275 | // Map shared user data page into the hypervisor partition 276 | // 277 | hr = SemMmMapSharedMemory(Partition, 278 | reinterpret_cast(s_UserSharedData), 279 | USN_PAGE_SIZE, 280 | NULL); 281 | if (FAILED(hr)) 282 | { 283 | return hr; 284 | } 285 | 286 | // 287 | // Set the initial thread context 288 | // 289 | auto initialContext = (PCONTEXT)&threadBlock->InitialContext; 290 | initialContext->ContextFlags = CONTEXT_FULL; 291 | initialContext->Rdx = (DWORD64)peb; 292 | initialContext->Rsp = s_StackLimit - 8; // for alignment 293 | initialContext->SegSs = 0x2B; 294 | initialContext->SegCs = 0x33; 295 | initialContext->MxCsr = 0x1F80; 296 | initialContext->EFlags = 0x200; 297 | initialContext->FltSave.ControlWord = 0x27F; 298 | initialContext->FltSave.MxCsr = 0x1F80; 299 | initialContext->FltSave.MxCsr_Mask = 0xFFFF; 300 | 301 | // 302 | // Load the emulated EXE into the hypervisor guest 303 | // 304 | hr = SemLdrLoadImage(Partition, 305 | ImageFileName, 306 | s_AppImageBase, 307 | NULL, 308 | &initialContext->Rcx); 309 | if (FAILED(hr)) 310 | { 311 | return hr; 312 | } 313 | 314 | // 315 | // Load the system library into the hypervisor guest 316 | // 317 | return SemLdrLoadImage(Partition, 318 | L"ntdll.dll", 319 | s_NtdllBase, 320 | &initialContext->Rip, 321 | EntryPoint); 322 | } 323 | 324 | -------------------------------------------------------------------------------- /emulator/processor.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | processor.cpp 8 | 9 | Abstract: 10 | 11 | This module handles state management for the guest's Virtual Processor. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | thread_local PSEM_VP t_CurrentVp; 26 | 27 | auto 28 | SemVpInitialize ( 29 | _In_ PSEM_VP Vp, 30 | _In_ ULONG_PTR EntryPoint, 31 | _In_ ULONG_PTR Stack, 32 | _In_ ULONG_PTR Param1, 33 | _In_ ULONG_PTR Param2 34 | ) 35 | { 36 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 37 | auto regs = Vp->Registers; 38 | 39 | // 40 | // Create the virtual processor 41 | // 42 | auto hr = WHvCreateVirtualProcessor(hPartition, Vp->Index, 0); 43 | if (FAILED(hr)) 44 | { 45 | return hr; 46 | } 47 | 48 | // 49 | // Get the initial CPU state 50 | // 51 | RtlZeroMemory(regs, sizeof(Vp->Registers)); 52 | hr = WHvGetVirtualProcessorRegisters(hPartition, 53 | Vp->Index, 54 | s_Registers, 55 | RTL_NUMBER_OF(Vp->Registers), 56 | regs); 57 | if (FAILED(hr)) 58 | { 59 | return hr; 60 | } 61 | 62 | // 63 | // CS = 0x33, Ring 3, Code Segment, Long Mode 64 | // 65 | regs[WHvX64RegisterCs].Segment.DescriptorPrivilegeLevel = 3; 66 | regs[WHvX64RegisterCs].Segment.Long = 1; 67 | regs[WHvX64RegisterCs].Segment.Selector = 0x33; 68 | 69 | // 70 | // SS, DS, ES, GS = 0x2B, Ring 3, Data Segment 71 | // 72 | regs[WHvX64RegisterSs].Segment.Selector = 0x2B; 73 | regs[WHvX64RegisterSs].Segment.DescriptorPrivilegeLevel = 3; 74 | regs[WHvX64RegisterDs] = Vp->Registers[WHvX64RegisterSs]; 75 | regs[WHvX64RegisterEs] = Vp->Registers[WHvX64RegisterSs]; 76 | regs[WHvX64RegisterGs] = Vp->Registers[WHvX64RegisterSs]; 77 | regs[WHvX64RegisterGs].Segment.Base = s_Teb; 78 | 79 | // 80 | // CR3 = PML4 at s_Pml4PhysicalAddress 81 | // 82 | regs[WHvX64RegisterCr3 - 4].Reg64 = s_Pml4PhysicalAddress; 83 | 84 | // 85 | // CR0 = pg, pe, mp, ... 86 | // CR4 = pae, ... NO SMEP 87 | // EFER = lma, lme, sce, ... 88 | // 89 | regs[WHvX64RegisterCr0 - 4].Reg64 = 0x80050033; 90 | regs[WHvX64RegisterCr4 - 4].Reg64 = 0x6E8; 91 | regs[29].Reg64 = 0xD01; 92 | 93 | // 94 | // Set RSP and RIP to point to stack and entrypoint 95 | // 96 | regs[WHvX64RegisterRsp].Reg64 = Stack - 8; // For XMM Alignment 97 | regs[WHvX64RegisterRip].Reg64 = EntryPoint; 98 | 99 | // 100 | // Set RCX and RDX to input parameters 101 | // 102 | regs[WHvX64RegisterRcx].Reg64 = Param1; 103 | regs[WHvX64RegisterRdx].Reg64 = Param2; 104 | 105 | // 106 | // RFLAGS = nv up ei pl nz na pe nc 107 | // 108 | regs[WHvX64RegisterRflags].Reg64 = 0x202; 109 | 110 | // 111 | // SYSCALL CS = s_SyscallTarget 112 | // 113 | regs[30].Reg64 = s_SyscallTarget; 114 | 115 | // 116 | // Update the register state 117 | // 118 | return WHvSetVirtualProcessorRegisters(hPartition, 119 | Vp->Index, 120 | s_Registers, 121 | RTL_NUMBER_OF(Vp->Registers), 122 | regs); 123 | } 124 | 125 | auto 126 | SemVpGetCurrentTeb ( 127 | VOID 128 | ) -> PTEB 129 | { 130 | // 131 | // Get the value of the GS segment in the guest, where the TEB is stored 132 | // 133 | auto gsBase = t_CurrentVp->Registers[WHvX64RegisterGs].Segment.Base; 134 | return reinterpret_cast(gsBase); 135 | } 136 | 137 | auto 138 | SemVpRestoreExceptionContext ( 139 | _In_ UINT64 Rsp, 140 | _In_ UINT64 Rbp, 141 | _In_ UINT64 Rsi, 142 | _In_ UINT64 Rdi, 143 | _In_ UINT64 Rbx, 144 | _In_ UINT64 Rcx 145 | ) -> HRESULT 146 | { 147 | auto Vp = t_CurrentVp; 148 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 149 | auto regs = Vp->Registers; 150 | 151 | // 152 | // Update the registers 153 | // 154 | regs[WHvX64RegisterRsp].Reg64 = Rsp; 155 | regs[WHvX64RegisterRbp].Reg64 = Rbp; 156 | regs[WHvX64RegisterRsi].Reg64 = Rsi; 157 | regs[WHvX64RegisterRdi].Reg64 = Rdi; 158 | regs[WHvX64RegisterRbx].Reg64 = Rbx; 159 | regs[WHvX64RegisterRcx].Reg64 = Rcx; 160 | return WHvSetVirtualProcessorRegisters(hPartition, 161 | Vp->Index, 162 | s_Registers, 163 | RTL_NUMBER_OF(Vp->Registers), 164 | regs); 165 | } 166 | 167 | auto 168 | SemVpSwitchMode ( 169 | _In_ UINT64 Rcx, 170 | _In_ UINT64 Rdx, 171 | _In_ UINT64 Flags, 172 | _In_ UINT64 StackPointer, 173 | _In_ UINT64 ProgramCounter, 174 | _In_ UINT16 CodeSeg, 175 | _In_ UINT16 StackSeg 176 | ) -> HRESULT 177 | { 178 | auto Vp = t_CurrentVp; 179 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 180 | auto regs = Vp->Registers; 181 | 182 | // 183 | // Update the registers 184 | // Note that WHV enforces having flag 0x2 set in EFLAGS 185 | // 186 | regs[WHvX64RegisterRcx].Reg64 = Rcx; 187 | regs[WHvX64RegisterRdx].Reg64 = Rdx; 188 | regs[WHvX64RegisterRflags].Reg64 = Flags | 2; 189 | regs[WHvX64RegisterRsp].Reg64 = StackPointer; 190 | regs[WHvX64RegisterRip].Reg64 = ProgramCounter; 191 | regs[WHvX64RegisterCs].Segment.Selector = CodeSeg; 192 | regs[WHvX64RegisterCs].Segment.DescriptorPrivilegeLevel = 3; 193 | regs[WHvX64RegisterSs].Segment.Selector = StackSeg; 194 | regs[WHvX64RegisterSs].Segment.DescriptorPrivilegeLevel = 3; 195 | regs[31].PendingInterruption.AsUINT64 = 0; 196 | return WHvSetVirtualProcessorRegisters(hPartition, 197 | Vp->Index, 198 | s_Registers, 199 | RTL_NUMBER_OF(Vp->Registers), 200 | regs); 201 | } 202 | 203 | auto 204 | SemVpSYSEXIT ( 205 | _In_ PSEM_VP Vp, 206 | _In_ NTSTATUS Status 207 | ) -> HRESULT 208 | { 209 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 210 | auto regs = Vp->Registers; 211 | 212 | // 213 | // Set RAX to the result 214 | // 215 | regs[WHvX64RegisterRax].Reg64 = Status; 216 | 217 | // 218 | // Restore RFLAGS and RIP just like SYSEXIT would 219 | // 220 | regs[WHvX64RegisterRflags] = regs[WHvX64RegisterR11]; 221 | regs[WHvX64RegisterRip] = regs[WHvX64RegisterRcx]; 222 | 223 | // 224 | // Restore CS to Ring 3 Selector 33h and SS to Ring 3 Selector 2Bh 225 | // 226 | regs[WHvX64RegisterCs].Segment.DescriptorPrivilegeLevel = 3; 227 | regs[WHvX64RegisterCs].Segment.Selector = 0x33; 228 | regs[WHvX64RegisterSs].Segment.DescriptorPrivilegeLevel = 3; 229 | regs[WHvX64RegisterSs].Segment.Selector = 0x2B; 230 | 231 | // 232 | // Update the registers 233 | // 234 | return WHvSetVirtualProcessorRegisters(hPartition, 235 | Vp->Index, 236 | s_Registers, 237 | RTL_NUMBER_OF(Vp->Registers), 238 | regs); 239 | } 240 | 241 | auto 242 | SemVpIRET ( 243 | _In_ PSEM_VP Vp, 244 | _In_ NTSTATUS Status 245 | ) -> HRESULT 246 | { 247 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 248 | auto regs = Vp->Registers; 249 | 250 | // 251 | // Set RAX to the result, skip past the instruction, and acquiesce 252 | // 253 | regs[WHvX64RegisterRax].Reg64 = Status; 254 | regs[WHvX64RegisterRip].Reg64 += regs[31].PendingInterruption.InstructionLength; 255 | regs[31].PendingInterruption.AsUINT64 = 0; 256 | 257 | // 258 | // Update the registers 259 | // 260 | return WHvSetVirtualProcessorRegisters(hPartition, 261 | Vp->Index, 262 | s_Registers, 263 | RTL_NUMBER_OF(Vp->Registers), 264 | regs); 265 | } 266 | 267 | auto 268 | SemVpINT ( 269 | _In_ PSEM_VP Vp 270 | ) 271 | { 272 | // 273 | // Check which interrupt vector this was 274 | // 275 | auto vector = Vp->Registers[31].PendingInterruption.InterruptionVector; 276 | if (vector == 0x2D) 277 | { 278 | // 279 | // Handle DEBUG_BREAKPOINT Trap 280 | // 281 | SemVpIRET(Vp, SemHandleDebugTrap(Vp)); 282 | } 283 | else if (vector == 0x2E) 284 | { 285 | // 286 | // Handle System Call Trap (with HVCI) 287 | // 288 | SemHandleSystemCall(Vp, TRUE); 289 | } 290 | else 291 | { 292 | // 293 | // Generic unhandled software interrupt 294 | // 295 | DbgRaiseAssertionFailure(); 296 | SemVpIRET(Vp, STATUS_ASSERTION_FAILURE); 297 | } 298 | } 299 | 300 | auto 301 | SemVpHandleMemoryAccessExit ( 302 | _In_ PSEM_VP Vp 303 | ) 304 | { 305 | // 306 | // Check if this is a memory access on the special SYSCALL RIP 307 | // 308 | if (Vp->ExitContext.MemoryAccess.Gpa == s_SyscallTarget) 309 | { 310 | // 311 | // Go handle a system call 312 | // 313 | SemHandleSystemCall(Vp, FALSE); 314 | } 315 | else if (Vp->ExitContext.VpContext.ExecutionState.InterruptionPending != FALSE) 316 | { 317 | // 318 | // We don't have an IDT, so interrupts will look like invalid memory accesses 319 | // 320 | SemVpINT(Vp); 321 | } 322 | else 323 | { 324 | // 325 | // Print out a fault for debugging 326 | // 327 | DbgRaiseAssertionFailure(); 328 | } 329 | } 330 | 331 | auto 332 | SemVpRun ( 333 | _In_ PSEM_VP Vp, 334 | _Out_ WHV_RUN_VP_EXIT_REASON* ExitReason 335 | ) 336 | { 337 | auto hPartition = SemPartitionFromVp(Vp)->PartitionHandle; 338 | 339 | // 340 | // Run the guest 341 | // 342 | auto hr = WHvRunVirtualProcessor(hPartition, 343 | Vp->Index, 344 | &Vp->ExitContext, 345 | sizeof(Vp->ExitContext)); 346 | if (FAILED(hr)) 347 | { 348 | return hr; 349 | } 350 | 351 | // 352 | // Return why we exited 353 | // 354 | *ExitReason = Vp->ExitContext.ExitReason; 355 | 356 | // 357 | // Get the current CPU state 358 | // 359 | return WHvGetVirtualProcessorRegisters(hPartition, 360 | Vp->Index, 361 | s_Registers, 362 | RTL_NUMBER_OF(Vp->Registers), 363 | Vp->Registers); 364 | } 365 | 366 | auto 367 | SemVpExecuteProcessor ( 368 | _In_ LPVOID Parameter 369 | ) -> DWORD 370 | { 371 | auto threadState = static_cast(Parameter); 372 | WHV_RUN_VP_EXIT_REASON exitReason; 373 | 374 | // 375 | // Get the partition and VP that we are emulating on this thread 376 | // 377 | auto partition = threadState->Partition; 378 | auto vp = &partition->Vp[threadState->CpuIndex]; 379 | 380 | // 381 | // Initialize the virtual processor state for this thread 382 | // 383 | vp->Index = threadState->CpuIndex; 384 | vp->Self = vp; 385 | t_CurrentVp = vp; 386 | 387 | // 388 | // Initialize the initial processor state (registers) 389 | // 390 | auto hr = SemVpInitialize(vp, 391 | threadState->InitialPc, 392 | threadState->InitialStack, 393 | s_UserContext, 394 | s_NtdllBase); 395 | if (FAILED(hr)) 396 | { 397 | SemVmError("Processor initialization failed: %lx\n", hr); 398 | goto Exit; 399 | } 400 | 401 | // 402 | // This is the main guest VM execution loop 403 | // 404 | for (;;) 405 | { 406 | // 407 | // Execute processor run loop 408 | // 409 | hr = SemVpRun(vp, &exitReason); 410 | if (FAILED(hr)) 411 | { 412 | SemVmError("Processor execution failed: %lx\n", hr); 413 | break; 414 | } 415 | 416 | // 417 | // The VP returned due to an exit -- handle each case 418 | // Update the register window on each exit 419 | // 420 | SemVpDumpRegisters(vp); 421 | if (exitReason == WHvRunVpExitReasonMemoryAccess) 422 | { 423 | // 424 | // Invalid memory access, which could be a system call 425 | // 426 | SemVpHandleMemoryAccessExit(vp); 427 | } 428 | else 429 | { 430 | // 431 | // Something we don't handle (yet) 432 | // 433 | SemVmError("Unhandled exit reason: %lx\n", exitReason); 434 | DbgRaiseAssertionFailure(); 435 | break; 436 | } 437 | } 438 | Exit: 439 | return 0; 440 | } 441 | -------------------------------------------------------------------------------- /emulator/sem.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | sem.h 8 | 9 | Abstract: 10 | 11 | This header defines the structures and functions of the Simple Emulator. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // SDK Headers 25 | // 26 | #pragma once 27 | #pragma warning(disable:4201) 28 | #include 29 | #define WIN32_NO_STATUS 30 | #define _NO_CRT_STDIO_INLINE 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // 38 | // Internal header shared with all SEM components 39 | // 40 | #include "semdef.h" 41 | 42 | // 43 | // Internal header shared with the monitor 44 | // 45 | #include "semmsg.h" 46 | 47 | // 48 | // For each VP Thread, the TCB describes its state 49 | // 50 | typedef struct _THREAD_CONTROL_BLOCK 51 | { 52 | UCHAR Stack[s_1MB]; // 1MB 53 | UCHAR Teb[8 * 1024]; // 8KB 54 | UCHAR Peb[4 * 1024]; // 4KB 55 | UCHAR Ppb[4 * 1024]; // 4KB 56 | UCHAR Reserved[48 * 1024]; 57 | UCHAR NlsTables[256 * 1024]; // 256KB 58 | UCHAR ApiSetMap[128 * 1024]; // 128KB 59 | UCHAR InitialContext[64 * 1024]; // 64KB 60 | } THREAD_CONTROL_BLOCK, *PTHREAD_CONTROL_BLOCK; 61 | static_assert((sizeof(THREAD_CONTROL_BLOCK) % (64 * 1024)) == 0, "Fix TCB size"); 62 | 63 | // 64 | // Mapping between our register indices (in SEM_VP) and the WHv platform 65 | // 66 | static constexpr WHV_REGISTER_NAME s_Registers[] = 67 | { 68 | WHvX64RegisterRax, WHvX64RegisterRcx, WHvX64RegisterRdx, 69 | WHvX64RegisterRbx, WHvX64RegisterRsp, WHvX64RegisterRbp, 70 | WHvX64RegisterRsi, WHvX64RegisterRdi, WHvX64RegisterR8, 71 | WHvX64RegisterR9, WHvX64RegisterR10, WHvX64RegisterR11, 72 | WHvX64RegisterR12, WHvX64RegisterR13, WHvX64RegisterR14, 73 | WHvX64RegisterR15, WHvX64RegisterRip, WHvX64RegisterRflags, 74 | 75 | WHvX64RegisterEs, WHvX64RegisterCs, WHvX64RegisterSs, 76 | WHvX64RegisterDs, WHvX64RegisterFs, WHvX64RegisterGs, 77 | 78 | WHvX64RegisterCr0, WHvX64RegisterCr2, WHvX64RegisterCr3, 79 | WHvX64RegisterCr4, WHvX64RegisterCr8, 80 | 81 | WHvX64RegisterEfer, WHvX64RegisterLstar, WHvRegisterPendingInterruption 82 | }; 83 | 84 | // 85 | // Represents a virtual processor 86 | // 87 | typedef struct _SEM_VP 88 | { 89 | ULONG Index; 90 | struct _SEM_VP* Self; 91 | WHV_RUN_VP_EXIT_CONTEXT ExitContext; 92 | WHV_REGISTER_VALUE Registers[RTL_NUMBER_OF(s_Registers)]; 93 | } SEM_VP, *PSEM_VP; 94 | 95 | // 96 | // Represents a partition (Virtual Machine) 97 | // 98 | typedef struct _SEM_PARTITION 99 | { 100 | WHV_PARTITION_HANDLE PartitionHandle; 101 | SEM_VP Vp[ANYSIZE_ARRAY]; 102 | } SEM_PARTITION, *PSEM_PARTITION; 103 | 104 | FORCEINLINE 105 | auto 106 | SemPartitionFromVp ( 107 | _In_ PSEM_VP Vp 108 | ) 109 | { 110 | // 111 | // Given a VP, return the partition its associated with 112 | // 113 | return CONTAINING_RECORD(Vp, SEM_PARTITION, Vp[Vp->Index]); 114 | } 115 | 116 | // 117 | // This is the Host-side TLS for each VP thread 118 | // 119 | typedef struct _SEM_VP_THREAD_STATE 120 | { 121 | ULONG CpuIndex; 122 | PSEM_PARTITION Partition; 123 | ULONG_PTR InitialPc; 124 | ULONG_PTR InitialStack; 125 | } SEM_VP_THREAD_STATE, *PSEM_VP_THREAD_STATE; 126 | 127 | // 128 | // Virtual Processor Functions 129 | // 130 | auto 131 | SemVpExecuteProcessor ( 132 | _In_ LPVOID Parameter 133 | )->DWORD; 134 | 135 | auto 136 | SemVpSYSEXIT ( 137 | _In_ PSEM_VP Vp, 138 | _In_ NTSTATUS Status 139 | )->HRESULT; 140 | 141 | auto 142 | SemVpIRET ( 143 | _In_ PSEM_VP Vp, 144 | _In_ NTSTATUS Status 145 | )->HRESULT; 146 | 147 | // 148 | // Memory Functions 149 | // 150 | auto 151 | SemAllocateGuestPrivateMemory ( 152 | _In_ PSEM_PARTITION Partition, 153 | _In_ ULONG_PTR GuestVa, 154 | _In_ SIZE_T Size, 155 | _Outptr_ PVOID* HostVa 156 | )->HRESULT; 157 | 158 | // 159 | // Partition Functions 160 | // 161 | auto 162 | SemVmCreateAddressSpace ( 163 | _In_ PSEM_PARTITION Partition 164 | )->HRESULT; 165 | 166 | auto 167 | SemVmCreatePartition ( 168 | _Outptr_ PSEM_PARTITION* Partition 169 | )->HRESULT; 170 | 171 | auto 172 | SemVmInitializeAddressSpace ( 173 | _In_ PSEM_PARTITION Partition, 174 | _In_ PWCHAR ImageFileName, 175 | _Out_ PULONG_PTR EntryPoint 176 | )->HRESULT; 177 | 178 | // 179 | // Guest Trap Handlers 180 | // 181 | auto 182 | SemHandleSystemCall ( 183 | _In_ PSEM_VP Vp, 184 | _In_ BOOLEAN Interrupt 185 | )->VOID; 186 | 187 | auto 188 | SemHandleDebugTrap ( 189 | _In_ PSEM_VP Vp 190 | )->NTSTATUS; 191 | 192 | // 193 | // Loader functions 194 | // 195 | auto 196 | SemLdrLoadImage ( 197 | _In_ PSEM_PARTITION Partition, 198 | _In_ PWCHAR ImagePath, 199 | _In_ ULONG_PTR ImageBase, 200 | _Out_opt_ PULONG_PTR ThreadThunk, 201 | _Out_ PULONG_PTR EntryPoint 202 | )->HRESULT; 203 | 204 | auto 205 | SemLdrRelocateImage ( 206 | __in PVOID NewBase 207 | )->NTSTATUS; 208 | 209 | // 210 | // Debugging Functions 211 | // 212 | auto 213 | SemVpDumpRegisters ( 214 | _In_ PSEM_VP Vp 215 | )->VOID; 216 | 217 | auto 218 | SemDbgTraceSystemCall ( 219 | _In_ USHORT Index, 220 | _In_ PCHAR Name, 221 | _In_ PULONG_PTR Arguments, 222 | _In_ USHORT ArgumentCount, 223 | _In_ NTSTATUS Result 224 | )->VOID; 225 | 226 | // 227 | // TLS reference to the VP of the host OS thread 228 | // 229 | extern thread_local PSEM_VP t_CurrentVp; 230 | 231 | // 232 | // The TCB is right below the 256GB boundary 233 | // 234 | static constexpr auto s_TcbRegionAddress = s_256GB - sizeof(THREAD_CONTROL_BLOCK); 235 | 236 | // 237 | // 1MB Stack, after the TCB 238 | // 239 | static constexpr auto s_StackSize = 1ULL * s_1MB; 240 | static constexpr auto s_StackLimit = s_TcbRegionAddress + s_StackSize; 241 | static_assert(s_StackSize == offsetof(THREAD_CONTROL_BLOCK, Teb), "Structure mismatch"); 242 | 243 | // 244 | // Store the PML4 right below the 512GB boundary 245 | // 246 | static constexpr auto s_Pml4PhysicalAddress = s_512GB - s_1GB; 247 | 248 | // 249 | // Shortcuts to various addresses of fields in the TCB 250 | // 251 | static constexpr auto s_Teb = s_TcbRegionAddress + FIELD_OFFSET(THREAD_CONTROL_BLOCK, Teb); 252 | static constexpr auto s_Peb = s_TcbRegionAddress + FIELD_OFFSET(THREAD_CONTROL_BLOCK, Peb); 253 | static constexpr auto s_Ppb = s_TcbRegionAddress + FIELD_OFFSET(THREAD_CONTROL_BLOCK, Ppb); 254 | static constexpr auto s_UserContext = s_TcbRegionAddress + FIELD_OFFSET(THREAD_CONTROL_BLOCK, InitialContext); 255 | 256 | // 257 | // Magic address to recognize a system call attempt occured 258 | // 259 | static constexpr auto s_SyscallTarget = s_256GB - 1; 260 | 261 | // 262 | // Various load addresses on the guest side 263 | // 264 | static constexpr auto s_NtdllBase = 0x10000000ULL; 265 | static constexpr auto s_AppImageBase = 0x11000000ULL; 266 | 267 | // 268 | // Internal header shared with the provider 269 | // 270 | #include "semprov.h" 271 | -------------------------------------------------------------------------------- /emulator/syscalls.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | syscalls.cpp 8 | 9 | Abstract: 10 | 11 | This module implements handling Ring 3->0 transitions from the guest VM. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "sem.h" 24 | 25 | // 26 | // Support for system call emulation for up to 11 arguments 27 | // 28 | #define MAX_SYSTEM_CALL_INDEX 500 29 | typedef SEM_SYSCALL_STATUS (*PNT_NO_ARGUMENTS) (VOID); 30 | typedef SEM_SYSCALL_STATUS (*PNT_ONE_ARGUMENT) (ULONG_PTR); 31 | typedef SEM_SYSCALL_STATUS (*PNT_TWO_ARGUMENTS) (ULONG_PTR, ULONG_PTR); 32 | typedef SEM_SYSCALL_STATUS (*PNT_THREE_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR); 33 | typedef SEM_SYSCALL_STATUS (*PNT_FOUR_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 34 | typedef SEM_SYSCALL_STATUS (*PNT_FIVE_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 35 | typedef SEM_SYSCALL_STATUS (*PNT_SIX_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 36 | typedef SEM_SYSCALL_STATUS (*PNT_SEVEN_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 37 | typedef SEM_SYSCALL_STATUS (*PNT_EIGHT_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 38 | typedef SEM_SYSCALL_STATUS (*PNT_NINE_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 39 | typedef SEM_SYSCALL_STATUS (*PNT_TEN_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 40 | typedef SEM_SYSCALL_STATUS (*PNT_ELEVEN_ARGUMENTS) (ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 41 | typedef struct _SEM_SYSTEM_CALL_DESCRIPTOR 42 | { 43 | UCHAR Arguments; 44 | PVOID Handler; 45 | PCHAR Name; 46 | } SEM_SYSTEM_CALL_DESCRIPTOR, *PSEM_SYSTEM_CALL_DESCRIPTOR; 47 | static SEM_SYSTEM_CALL_DESCRIPTOR s_SystemCalls[MAX_SYSTEM_CALL_INDEX]; 48 | 49 | typedef struct _SEM_DEBUG_TRAP_DESCRIPTOR 50 | { 51 | UCHAR Arguments; 52 | PVOID Handler; 53 | } SEM_DEBUG_TRAP_DESCRIPTOR, *PSEM_DEBUG_TRAP_DESCRIPTOR; 54 | #define MAX_DEBUG_TRAP 5 55 | static SEM_DEBUG_TRAP_DESCRIPTOR s_DebugTraps[MAX_DEBUG_TRAP]; 56 | 57 | // 58 | // Generic Handler for N-arguments 59 | // 60 | typedef 61 | auto 62 | (SEM_HANDLE_SYSCALL) ( 63 | _In_ PVOID Handler, 64 | _In_ PULONG_PTR Arguments 65 | ) -> SEM_SYSCALL_STATUS; 66 | typedef SEM_HANDLE_SYSCALL* PSEM_HANDLE_SYSCALL; 67 | 68 | auto 69 | SemHandleSystemCall11 ( 70 | _In_ PVOID Handler, 71 | _In_ PULONG_PTR Arguments 72 | ) -> SEM_SYSCALL_STATUS 73 | { 74 | // 75 | // Call the 9-argument handler 76 | // 77 | return ((PNT_ELEVEN_ARGUMENTS)Handler)(Arguments[0], 78 | Arguments[1], 79 | Arguments[2], 80 | Arguments[3], 81 | Arguments[4], 82 | Arguments[5], 83 | Arguments[6], 84 | Arguments[7], 85 | Arguments[8], 86 | Arguments[9], 87 | Arguments[10]); 88 | } 89 | 90 | auto 91 | SemHandleSystemCall10 ( 92 | _In_ PVOID Handler, 93 | _In_ PULONG_PTR Arguments 94 | ) -> SEM_SYSCALL_STATUS 95 | { 96 | // 97 | // Call the 9-argument handler 98 | // 99 | return ((PNT_TEN_ARGUMENTS)Handler)(Arguments[0], 100 | Arguments[1], 101 | Arguments[2], 102 | Arguments[3], 103 | Arguments[4], 104 | Arguments[5], 105 | Arguments[6], 106 | Arguments[7], 107 | Arguments[8], 108 | Arguments[9]); 109 | } 110 | 111 | auto 112 | SemHandleSystemCall9 ( 113 | _In_ PVOID Handler, 114 | _In_ PULONG_PTR Arguments 115 | ) -> SEM_SYSCALL_STATUS 116 | { 117 | // 118 | // Call the 9-argument handler 119 | // 120 | return ((PNT_NINE_ARGUMENTS)Handler)(Arguments[0], 121 | Arguments[1], 122 | Arguments[2], 123 | Arguments[3], 124 | Arguments[4], 125 | Arguments[5], 126 | Arguments[6], 127 | Arguments[7], 128 | Arguments[8]); 129 | } 130 | 131 | auto 132 | SemHandleSystemCall8 ( 133 | _In_ PVOID Handler, 134 | _In_ PULONG_PTR Arguments 135 | ) -> SEM_SYSCALL_STATUS 136 | { 137 | // 138 | // Call the 8-argument handler 139 | // 140 | return ((PNT_EIGHT_ARGUMENTS)Handler)(Arguments[0], 141 | Arguments[1], 142 | Arguments[2], 143 | Arguments[3], 144 | Arguments[4], 145 | Arguments[5], 146 | Arguments[6], 147 | Arguments[7]); 148 | } 149 | 150 | auto 151 | SemHandleSystemCall7 ( 152 | _In_ PVOID Handler, 153 | _In_ PULONG_PTR Arguments 154 | ) -> SEM_SYSCALL_STATUS 155 | { 156 | // 157 | // Call the 7-argument handler 158 | // 159 | return ((PNT_SEVEN_ARGUMENTS)Handler)(Arguments[0], 160 | Arguments[1], 161 | Arguments[2], 162 | Arguments[3], 163 | Arguments[4], 164 | Arguments[5], 165 | Arguments[6]); 166 | } 167 | 168 | auto 169 | SemHandleSystemCall6 ( 170 | _In_ PVOID Handler, 171 | _In_ PULONG_PTR Arguments 172 | ) -> SEM_SYSCALL_STATUS 173 | { 174 | // 175 | // Call the 6-argument handler 176 | // 177 | return ((PNT_SIX_ARGUMENTS)Handler)(Arguments[0], 178 | Arguments[1], 179 | Arguments[2], 180 | Arguments[3], 181 | Arguments[4], 182 | Arguments[5]); 183 | } 184 | 185 | auto 186 | SemHandleSystemCall5 ( 187 | _In_ PVOID Handler, 188 | _In_ PULONG_PTR Arguments 189 | ) -> SEM_SYSCALL_STATUS 190 | { 191 | // 192 | // Call the 5-argument handler 193 | // 194 | return ((PNT_FIVE_ARGUMENTS)Handler)(Arguments[0], 195 | Arguments[1], 196 | Arguments[2], 197 | Arguments[3], 198 | Arguments[4]); 199 | } 200 | 201 | auto 202 | SemHandleSystemCall4 ( 203 | _In_ PVOID Handler, 204 | _In_ PULONG_PTR Arguments 205 | ) -> SEM_SYSCALL_STATUS 206 | { 207 | // 208 | // Call the 4-argument handler 209 | // 210 | return ((PNT_FOUR_ARGUMENTS)Handler)(Arguments[0], 211 | Arguments[1], 212 | Arguments[2], 213 | Arguments[3]); 214 | } 215 | 216 | auto 217 | SemHandleSystemCall3 ( 218 | _In_ PVOID Handler, 219 | _In_ PULONG_PTR Arguments 220 | ) -> SEM_SYSCALL_STATUS 221 | { 222 | // 223 | // Call the 3-argument handler 224 | // 225 | return ((PNT_THREE_ARGUMENTS)Handler)(Arguments[0], 226 | Arguments[1], 227 | Arguments[2]); 228 | } 229 | 230 | auto 231 | SemHandleSystemCall2 ( 232 | _In_ PVOID Handler, 233 | _In_ PULONG_PTR Arguments 234 | ) -> SEM_SYSCALL_STATUS 235 | { 236 | // 237 | // Call the 2-argument handler 238 | // 239 | return ((PNT_TWO_ARGUMENTS)Handler)(Arguments[0], 240 | Arguments[1]); 241 | } 242 | 243 | auto 244 | SemHandleSystemCall1 ( 245 | _In_ PVOID Handler, 246 | _In_ PULONG_PTR Arguments 247 | ) -> SEM_SYSCALL_STATUS 248 | { 249 | // 250 | // Call the 1-argument handler 251 | // 252 | return ((PNT_ONE_ARGUMENT)Handler)(Arguments[0]); 253 | } 254 | 255 | auto 256 | SemHandleSystemCall0 ( 257 | _In_ PVOID Handler, 258 | _In_ PULONG_PTR Arguments 259 | ) -> SEM_SYSCALL_STATUS 260 | { 261 | // 262 | // Call the 1-argument handler 263 | // 264 | UNREFERENCED_PARAMETER(Arguments); 265 | return ((PNT_NO_ARGUMENTS)Handler)(); 266 | } 267 | 268 | static const PSEM_HANDLE_SYSCALL s_SystemCallArgHandlers[12] = 269 | { 270 | SemHandleSystemCall0, 271 | SemHandleSystemCall1, 272 | SemHandleSystemCall2, 273 | SemHandleSystemCall3, 274 | SemHandleSystemCall4, 275 | SemHandleSystemCall5, 276 | SemHandleSystemCall6, 277 | SemHandleSystemCall7, 278 | SemHandleSystemCall8, 279 | SemHandleSystemCall9, 280 | SemHandleSystemCall10, 281 | SemHandleSystemCall11, 282 | }; 283 | 284 | auto 285 | SemRegisterSystemCall ( 286 | _In_ USHORT Index, 287 | _In_ UCHAR Arguments, 288 | _In_opt_ PVOID Function, 289 | _In_ PCHAR FunctionName 290 | ) -> void 291 | { 292 | // 293 | // This is a special way of saying the handler is a generic one which 294 | // return STATUS_NOT_IMPLEMENTED 295 | // 296 | if (Function == nullptr) 297 | { 298 | Function = reinterpret_cast(1); 299 | } 300 | 301 | // 302 | // Register the system call in the table 303 | // 304 | s_SystemCalls[Index].Arguments = Arguments; 305 | s_SystemCalls[Index].Handler = Function; 306 | s_SystemCalls[Index].Name = FunctionName; 307 | } 308 | 309 | auto 310 | SemRegisterDebugTrap ( 311 | _In_ USHORT Index, 312 | _In_ UCHAR Arguments, 313 | _In_ PVOID Function 314 | ) -> void 315 | { 316 | // 317 | // Register the system call in the table 318 | // 319 | s_DebugTraps[Index].Arguments = Arguments; 320 | s_DebugTraps[Index].Handler = Function; 321 | } 322 | 323 | auto 324 | IsGuestMemoryPtr ( 325 | _In_ PVOID Address 326 | ) -> bool 327 | { 328 | // 329 | // Guest memory should be between 0x10000 and 0x4000000000 330 | // 331 | if ((Address < s_LowestValidAddress) || (Address > s_HighestValidAddress)) 332 | { 333 | return false; 334 | } 335 | else 336 | { 337 | // 338 | // And should not be in the shared memory area hole 339 | // 340 | return ((Address < s_UserSharedData) || (Address > s_UserSharedDataEnd)); 341 | } 342 | } 343 | 344 | auto 345 | SemExtractSystemCallArguments ( 346 | _In_ PSEM_VP Vp, 347 | _In_ BOOLEAN Interrupt, 348 | _Out_ PUSHORT Index, 349 | _Out_ PULONG_PTR Arguments, 350 | _Out_ PUSHORT ArgumentCount, 351 | _Out_ PVOID* Handler 352 | ) 353 | { 354 | auto regs = Vp->Registers; 355 | 356 | // 357 | // Get the stack pointer at the time the system call was made and make sure 358 | // its valid guest memory. An even better check would be to see if it's in 359 | // the stack region. 360 | // 361 | auto stack = reinterpret_cast(regs[WHvX64RegisterRsp].Reg64); 362 | if (!IsGuestMemoryPtr(stack)) 363 | { 364 | SemVmError("System call stack (0x%p) is invalid", stack); 365 | DbgRaiseAssertionFailure(); 366 | return STATUS_INVALID_ADDRESS; 367 | } 368 | 369 | // 370 | // Read the system call ID and make sure it's potentially valid 371 | // 372 | *Index = regs[WHvX64RegisterRax].Reg16; 373 | if (*Index > _countof(s_SystemCalls)) 374 | { 375 | SemVmError("System call index %d is above max supported", *Index); 376 | DbgRaiseAssertionFailure(); 377 | return STATUS_IMPLEMENTATION_LIMIT; 378 | } 379 | 380 | // 381 | // Check if we support this system call 382 | // 383 | *Handler = s_SystemCalls[*Index].Handler; 384 | if (*Handler == nullptr) 385 | { 386 | SemVmError("System call index %d is not emulated", *Index); 387 | DbgRaiseAssertionFailure(); 388 | return STATUS_NOT_IMPLEMENTED; 389 | } 390 | 391 | // 392 | // Go over each argument this function takes 393 | // 394 | *ArgumentCount = s_SystemCalls[*Index].Arguments; 395 | for (auto i = 0UL; i < *ArgumentCount; i++) 396 | { 397 | // 398 | // Read the first four arguments from the appropriate register 399 | // 400 | if (i == 0) 401 | { 402 | if (Interrupt != FALSE) 403 | { 404 | Arguments[i] = regs[WHvX64RegisterRcx].Reg64; 405 | } 406 | else 407 | { 408 | Arguments[i] = regs[WHvX64RegisterR10].Reg64; 409 | } 410 | } 411 | else if (i == 1) 412 | { 413 | Arguments[i] = regs[WHvX64RegisterRdx].Reg64; 414 | } 415 | else if (i == 2) 416 | { 417 | Arguments[i] = regs[WHvX64RegisterR8].Reg64; 418 | } 419 | else if (i == 3) 420 | { 421 | Arguments[i] = regs[WHvX64RegisterR9].Reg64; 422 | } 423 | else 424 | { 425 | // 426 | // Other arguments come from the stack, after home space 427 | // Stack pointer could be bogus, so use SEH to detect garbage 428 | // Note that we'll still crash, but it'll be an assertion with a 429 | // debugging aid to identify the situation. 430 | // 431 | _try 432 | { 433 | Arguments[i] = stack[i + 1]; 434 | } 435 | _except (EXCEPTION_EXECUTE_HANDLER) 436 | { 437 | SemVmError("System call issued with bogus stack: %p", 438 | &stack[i + 1]); 439 | DbgRaiseAssertionFailure(); 440 | return STATUS_ACCESS_VIOLATION; 441 | } 442 | } 443 | } 444 | return STATUS_SUCCESS; 445 | } 446 | 447 | auto 448 | SemHandleSystemCall ( 449 | _In_ PSEM_VP Vp, 450 | _In_ BOOLEAN Interrupt 451 | ) -> VOID 452 | { 453 | USHORT index; 454 | ULONG_PTR arguments[19]; 455 | USHORT argumentCount; 456 | PVOID handler; 457 | SEM_SYSCALL_STATUS result; 458 | 459 | // 460 | // Extract the system call index and arguments, assert if anything failed 461 | // 462 | auto status = SemExtractSystemCallArguments(Vp, 463 | Interrupt, 464 | &index, 465 | arguments, 466 | &argumentCount, 467 | &handler); 468 | if (!NT_SUCCESS(status)) 469 | { 470 | DbgRaiseAssertionFailure(); 471 | } 472 | 473 | // 474 | // Check if this is the special value which marks this as a system call 475 | // which does not require implementation, otherwise go and call the wrapper 476 | // 477 | // 478 | if (handler != reinterpret_cast(1)) 479 | { 480 | result = s_SystemCallArgHandlers[argumentCount](handler, arguments); 481 | } 482 | else 483 | { 484 | result = EncodeFailureOk(STATUS_NOT_IMPLEMENTED); 485 | } 486 | 487 | // 488 | // Trace the result back 489 | // 490 | SemDbgTraceSystemCall(index, 491 | s_SystemCalls[index].Name, 492 | arguments, 493 | argumentCount, 494 | result.Status); 495 | 496 | // 497 | // If the system call emulation failed unexpectedly, assert 498 | // 499 | if (!(NT_SUCCESS(result.Status)) && !(result.Flags & SemFailureIsExpected)) 500 | { 501 | SemVmError("System call returned unexpected error: %lx", result.Status); 502 | DbgRaiseAssertionFailure(); 503 | } 504 | 505 | // 506 | // Resume after system call completion, except for special-case system 507 | // calls which restore CPU state differently. 508 | // 509 | if (!(result.Flags & SemDoNotResume)) 510 | { 511 | if (Interrupt != FALSE) 512 | { 513 | SemVpIRET(Vp, result.Status); 514 | } 515 | else 516 | { 517 | SemVpSYSEXIT(Vp, result.Status); 518 | } 519 | } 520 | } 521 | 522 | auto 523 | SemExtractDebugTrapArguments ( 524 | _In_ PSEM_VP Vp, 525 | _Out_ PUSHORT Index, 526 | _Out_ PULONG_PTR Arguments, 527 | _Out_ PULONG ArgumentCount, 528 | _Out_ PVOID* Handler 529 | ) 530 | { 531 | auto regs = Vp->Registers; 532 | 533 | // 534 | // Get the debug trap index and make sure it's not bogus 535 | // 536 | *Index = regs[WHvX64RegisterRax].Reg16; 537 | if (*Index > _countof(s_DebugTraps)) 538 | { 539 | SemVmError("Debug trap index %d is above max supported", *Index); 540 | DbgRaiseAssertionFailure(); 541 | return STATUS_IMPLEMENTATION_LIMIT; 542 | } 543 | 544 | // 545 | // Debug traps always have four parameters 546 | // 547 | *ArgumentCount = s_DebugTraps[*Index].Arguments; 548 | 549 | // 550 | // Check if we support this system call 551 | // 552 | *Handler = s_DebugTraps[*Index].Handler; 553 | if (*Handler == nullptr) 554 | { 555 | SemVmError("Debug trap index %d is not emulated", *Index); 556 | DbgRaiseAssertionFailure(); 557 | return STATUS_NOT_IMPLEMENTED; 558 | } 559 | 560 | // 561 | // Always capture 4 arguments since that's how much the trap always sends 562 | // 563 | Arguments[0] = regs[WHvX64RegisterRcx].Reg64; 564 | Arguments[1] = regs[WHvX64RegisterRdx].Reg32; 565 | Arguments[2] = regs[WHvX64RegisterR8].Reg32; 566 | Arguments[3] = regs[WHvX64RegisterR9].Reg32; 567 | return STATUS_SUCCESS; 568 | } 569 | 570 | auto 571 | SemHandleDebugTrap ( 572 | _In_ PSEM_VP Vp 573 | ) -> NTSTATUS 574 | { 575 | USHORT index; 576 | ULONG_PTR arguments[4]; 577 | ULONG argumentCount; 578 | PVOID handler; 579 | 580 | // 581 | // Extract the debug service index and arguments 582 | // 583 | auto status = SemExtractDebugTrapArguments(Vp, 584 | &index, 585 | arguments, 586 | &argumentCount, 587 | &handler); 588 | if (!NT_SUCCESS(status)) 589 | { 590 | DbgRaiseAssertionFailure(); 591 | } 592 | 593 | // 594 | // Call the appropriate handler for each number of arguments possible 595 | // 596 | auto result = s_SystemCallArgHandlers[argumentCount](handler, arguments); 597 | if (!(NT_SUCCESS(result.Status)) && !(result.Flags & SemFailureIsExpected)) 598 | { 599 | DbgRaiseAssertionFailure(); 600 | } 601 | 602 | // 603 | // Windows places an INT3 to detect correct trap handling of INT2D 604 | // 605 | Vp->Registers[WHvX64RegisterRip].Reg64 += 1; 606 | return result.Status; 607 | } 608 | -------------------------------------------------------------------------------- /inc/semdef.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | semdef.h 8 | 9 | Abstract: 10 | 11 | This header contains shared definitions for all Simple Emulator components. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // Some simple size constants to make things easier 25 | // 26 | static constexpr auto s_1MB = 1ULL * 1024 * 1024; 27 | static constexpr auto s_1GB = 1ULL * 1024 * 1024 * 1024; 28 | static constexpr auto s_256GB = 256ULL * s_1GB; 29 | static constexpr auto s_512GB = 512ULL * s_1GB; 30 | 31 | // 32 | // OS Constants for memory layout setup 33 | // 34 | static const auto s_UserSharedData = reinterpret_cast(0x7FFE0000ULL); 35 | static const auto s_UserSharedDataEnd = reinterpret_cast(0x7FFF0000ULL); 36 | static const auto s_LowestValidAddress = reinterpret_cast(0x10000ULL); 37 | static const auto s_HighestValidAddress = reinterpret_cast(s_256GB - 1); 38 | 39 | -------------------------------------------------------------------------------- /inc/semmsg.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | semmsg.h 8 | 9 | Abstract: 10 | 11 | This header defines the messages sent accross the Debug Monitor named pipe. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // Message types can be sent between monitor and emulator 25 | // 26 | typedef enum _SEM_MSG_TYPE 27 | { 28 | SemUpdateRegisters = 0x04030201, 29 | SemUpdateDebugTrace = 0x08070605, 30 | SemSystemCall = 0x41414141, 31 | SemInternalError = 0x0C0B0A09 32 | } SEM_MSG_TYPE; 33 | 34 | // 35 | // Payload of a message sent on the pipe 36 | // Note that 8KB is allways allocated on the user stack, but we only actually 37 | // write on the pipe up to the length of the message (string) itself 38 | // 39 | typedef struct _SEM_PIPE_BUFFER_MSG 40 | { 41 | SEM_MSG_TYPE MessageType; 42 | union 43 | { 44 | CHAR Data[8184]; 45 | struct 46 | { 47 | USHORT Index; 48 | USHORT ArgumentCount; 49 | NTSTATUS Result; 50 | ULONG_PTR Arguments[16]; 51 | CHAR Name[32]; 52 | } SystemCall; 53 | struct 54 | { 55 | // 56 | // These are sorted in the same order as the emulator's array to 57 | // make transfer a simple memcpy() 58 | // 59 | FLOAT128 Rax; 60 | FLOAT128 Rcx; 61 | FLOAT128 Rdx; 62 | FLOAT128 Rbx; 63 | FLOAT128 Rsp; 64 | FLOAT128 Rbp; 65 | FLOAT128 Rsi; 66 | FLOAT128 Rdi; 67 | FLOAT128 R8; 68 | FLOAT128 R9; 69 | FLOAT128 R10; 70 | FLOAT128 R11; 71 | FLOAT128 R12; 72 | FLOAT128 R13; 73 | FLOAT128 R14; 74 | FLOAT128 R15; 75 | FLOAT128 Rip; 76 | FLOAT128 Rflags; 77 | FLOAT128 Es; 78 | FLOAT128 Cs; 79 | FLOAT128 Ss; 80 | FLOAT128 Ds; 81 | FLOAT128 Fs; 82 | FLOAT128 Gs; 83 | 84 | FLOAT128 Cr0; 85 | FLOAT128 Cr2; 86 | FLOAT128 Cr3; 87 | FLOAT128 Cr4; 88 | FLOAT128 Cr8; 89 | } Registers; 90 | }; 91 | } SEM_PIPE_BUFFER_MSG, *PSEM_PIPE_BUFFER_MSG; 92 | C_ASSERT(sizeof(SEM_PIPE_BUFFER_MSG) == 8 * 1024); 93 | -------------------------------------------------------------------------------- /inc/semprov.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | semprov.h 8 | 9 | Abstract: 10 | 11 | This header defines the interface for Simple Emulator System Call Providers 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // System Call Provider hooks return this complex status to encode actions and result 25 | // 26 | typedef enum _SEM_STATUS_FLAGS 27 | { 28 | SemNoFlags = 0x0, 29 | SemDoNotResume = 0x1, 30 | SemFailureIsExpected = 0x2 31 | } SEM_STATUS_FLAGS; 32 | typedef union _SEM_SYSCALL_STATUS 33 | { 34 | struct 35 | { 36 | NTSTATUS Status; 37 | SEM_STATUS_FLAGS Flags; 38 | }; 39 | ULONG64 StatusValue; 40 | } SEM_SYSCALL_STATUS; 41 | 42 | auto FORCEINLINE EncodeNoResume (_In_ NTSTATUS Status) { return SEM_SYSCALL_STATUS{ Status, SemDoNotResume }; } 43 | auto FORCEINLINE EncodeFailureOk (_In_ NTSTATUS Status) { return SEM_SYSCALL_STATUS{ Status, SemFailureIsExpected }; } 44 | auto FORCEINLINE EncodeStatus (_In_ NTSTATUS Status) { return SEM_SYSCALL_STATUS{ Status, SemNoFlags }; } 45 | 46 | // 47 | // Virtual Processor Functions 48 | // 49 | auto 50 | SemVpSwitchMode ( 51 | _In_ UINT64 Rcx, 52 | _In_ UINT64 Rdx, 53 | _In_ UINT64 Flags, 54 | _In_ UINT64 StackPointer, 55 | _In_ UINT64 ProgramCounter, 56 | _In_ UINT16 CodeSeg, 57 | _In_ UINT16 StackSeg 58 | )->HRESULT; 59 | 60 | auto 61 | SemVpRestoreExceptionContext ( 62 | _In_ UINT64 Rsp, 63 | _In_ UINT64 Rbp, 64 | _In_ UINT64 Rsi, 65 | _In_ UINT64 Rdi, 66 | _In_ UINT64 Rbx, 67 | _In_ UINT64 Rcx 68 | )->HRESULT; 69 | 70 | auto 71 | SemVpGetCurrentTeb ( 72 | VOID 73 | )->PTEB; 74 | 75 | // 76 | // Memory Functions 77 | // 78 | auto 79 | SemMmMapGuestImage ( 80 | _In_ PSEM_PARTITION Partition, 81 | _In_ HANDLE ImageHandle, 82 | _In_ ULONG_PTR GuestVa, 83 | _In_ SIZE_T Size, 84 | _Outptr_ PVOID* HostVa 85 | )->HRESULT; 86 | 87 | auto 88 | SemMmMapSharedMemory ( 89 | _In_ PSEM_PARTITION Partition, 90 | _In_ ULONG_PTR GuestVa, 91 | _In_ SIZE_T Size, 92 | _Outptr_opt_ PVOID* HostVa 93 | )->HRESULT; 94 | 95 | auto 96 | SemMmMapSharedImage ( 97 | _In_ PSEM_PARTITION Partition, 98 | _In_ ULONG_PTR GuestVa, 99 | _In_ SIZE_T Size 100 | )->HRESULT; 101 | 102 | auto 103 | SemMmUnmapSharedMemory ( 104 | _In_ PSEM_PARTITION Partition, 105 | _In_ ULONG_PTR GuestVa, 106 | _In_ SIZE_T Size, 107 | _In_opt_ PVOID HostVa 108 | )->HRESULT; 109 | 110 | auto 111 | IsGuestMemoryPtr ( 112 | _In_ PVOID Address 113 | ) -> bool; 114 | 115 | // 116 | // Debugging Functions 117 | // 118 | auto 119 | SemVmDebugPrint ( 120 | _In_ PCHAR Buffer, 121 | _In_ ULONG Length 122 | )->VOID; 123 | 124 | auto 125 | SemVmError ( 126 | _In_ PCHAR ErrorString, 127 | ... 128 | )->VOID; 129 | 130 | // 131 | // System Call Registration API 132 | // 133 | auto 134 | SemRegisterSystemCall ( 135 | _In_ USHORT Index, 136 | _In_ UCHAR Arguments, 137 | _In_opt_ PVOID Function, 138 | _In_ PCHAR FunctionName 139 | )->VOID; 140 | 141 | auto 142 | SemRegisterDebugTrap ( 143 | _In_ USHORT Index, 144 | _In_ UCHAR Arguments, 145 | _In_ PVOID Function 146 | )->VOID; 147 | 148 | // 149 | // APIs exposed by emulator for the provider 150 | // 151 | typedef struct _SEM_PROVIDER_CALLBACKS 152 | { 153 | decltype(&IsGuestMemoryPtr) IsGuestMemory; 154 | decltype(&SemMmMapSharedImage) MapSharedImage; 155 | decltype(&SemMmMapSharedMemory) MapSharedMemory; 156 | decltype(&SemMmUnmapSharedMemory) UnmapSharedMemory; 157 | decltype(&SemRegisterSystemCall) RegisterSystemCall; 158 | decltype(&SemRegisterDebugTrap) RegisterDebugTrap; 159 | decltype(&SemVmDebugPrint) DebugPrint; 160 | decltype(&SemVmError) TraceError; 161 | decltype(&SemVpSwitchMode) SwitchMode; 162 | decltype(&SemVpRestoreExceptionContext) RestoreException; 163 | decltype(&SemVpGetCurrentTeb) GetCurrentTeb; 164 | } SEM_PROVIDER_CALLBACKS, *PSEM_PROVIDER_CALLBACKS; 165 | 166 | EXTERN_C 167 | VOID 168 | SemRegisterSystemCallProvider ( 169 | _In_ CONST SEM_PROVIDER_CALLBACKS* SemCallbacks 170 | ); 171 | -------------------------------------------------------------------------------- /monitor.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionescu007/Simpleator/bbed7c2bc97fc5595fd6ff9adc78d8f556fa67f7/monitor.PNG -------------------------------------------------------------------------------- /monitor/mon.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | monitor.h 8 | 9 | Abstract: 10 | 11 | This is the main header file for the Simple Emulator's Debug Monitor. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // SDK Headers 25 | // 26 | #pragma once 27 | #include 28 | #include 29 | #include 30 | 31 | // 32 | // Internal headers shared with the monitor 33 | // 34 | #include 35 | #include 36 | 37 | // 38 | // VT-100 Codes 39 | // 40 | #define ESC "\x1b" 41 | #define CSI "\x1b[" 42 | #define OSC "\x1b]" 43 | 44 | // 45 | // Size of the debug monitor window 46 | // 47 | #define DEBUG_MONITOR_WINDOW_TOP 450 48 | #define DEBUG_MONITOR_WINDOW_WIDTH 980 49 | #define DEBUG_MONITOR_WINDOW_HEIGHT 768 50 | 51 | // 52 | // UI Functions 53 | // 54 | auto 55 | UpdateDebugMonitor ( 56 | _In_ PCHAR DebugMessage, 57 | _In_ ULONG DebugMessageLength 58 | )->VOID; 59 | 60 | auto 61 | UpdateRegisterWindow ( 62 | _In_ PSEM_PIPE_BUFFER_MSG Msg 63 | )->VOID; 64 | 65 | auto 66 | SemCreateDebuggerWindows ( 67 | VOID 68 | )->HRESULT; 69 | 70 | -------------------------------------------------------------------------------- /monitor/monitor.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | monitor.cpp 8 | 9 | Abstract: 10 | 11 | This module handles the processing loop for Simple Emulator Pipe Messages 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "mon.h" 24 | 25 | VOID 26 | Trace ( 27 | _In_ PCCH pszMessage, 28 | ... 29 | ) 30 | { 31 | va_list list; 32 | 33 | // 34 | // Begin var arg processing 35 | // 36 | va_start(list, pszMessage); 37 | 38 | // 39 | // Print out the message in the appropriate colors and location 40 | // 41 | printf(ESC "7" CSI "30;1H" CSI "K" CSI "44;96m"); 42 | vprintf(pszMessage, list); 43 | printf(CSI "44m" ESC "8"); 44 | 45 | // 46 | // Terminate var arg processing 47 | // 48 | va_end(list); 49 | } 50 | 51 | ULONG 52 | SemMonThread ( 53 | _In_ LPVOID Parameter 54 | ) 55 | { 56 | SEM_PIPE_BUFFER_MSG pipeBuffer; 57 | ULONG bytesRead; 58 | BOOL bRes; 59 | do 60 | { 61 | // 62 | // Read a message from the pipe 63 | // 64 | bRes = ReadFile(reinterpret_cast(Parameter), 65 | &pipeBuffer, 66 | sizeof(pipeBuffer), 67 | &bytesRead, 68 | NULL); 69 | if ((bRes != FALSE) && 70 | (bytesRead >= FIELD_OFFSET(SEM_PIPE_BUFFER_MSG, Data))) 71 | { 72 | if (pipeBuffer.MessageType == SemUpdateRegisters) 73 | { 74 | // 75 | // Send the text to the window 76 | // 77 | UpdateRegisterWindow(&pipeBuffer); 78 | } 79 | else if (pipeBuffer.MessageType == SemUpdateDebugTrace) 80 | { 81 | // 82 | // Send the text to the window 83 | // 84 | UpdateDebugMonitor(pipeBuffer.Data, 85 | bytesRead - 1 - 86 | FIELD_OFFSET(SEM_PIPE_BUFFER_MSG, Data)); 87 | } 88 | else if (pipeBuffer.MessageType == SemInternalError) 89 | { 90 | // 91 | // Print out the error on the console 92 | // 93 | printf(CSI "44;91m" 94 | "[VMERROR] %s" 95 | ESC "(0" CSI "44;93m\tx\tx\tx" CSI "Z" CSI "Zx" ESC "(B", 96 | pipeBuffer.Data); 97 | } 98 | else if (pipeBuffer.MessageType == SemSystemCall) 99 | { 100 | // 101 | // Add the header and then parse each argument 102 | // 103 | printf(CSI "44;97m" 104 | "[SYSCALL] (%d, %s)", 105 | pipeBuffer.SystemCall.Index, 106 | pipeBuffer.SystemCall.Name); 107 | for (auto i = 0; i < pipeBuffer.SystemCall.ArgumentCount; i++) 108 | { 109 | // 110 | // Every 4 arguments add a new line to start over 111 | // Then add the indented argument 112 | // 113 | if ((i % 4) == 0) 114 | { 115 | printf("\t" ESC "(0" CSI "44;93mx\tx" ESC "(B"); 116 | } 117 | printf(CSI "44;97m" 118 | " Arg%d=0x%016I64X", 119 | i, 120 | pipeBuffer.SystemCall.Arguments[i]); 121 | } 122 | 123 | // 124 | // Add the result 125 | // 126 | printf("\t" ESC "(0" CSI "44;93mx\tx" ESC "(B" CSI "44;97m" 127 | " Ret=%08lx" 128 | ESC "(0" CSI "44;93m\tx\tx\tx" CSI "Z" CSI "Zx" ESC "(B", 129 | pipeBuffer.SystemCall.Result); 130 | } 131 | else 132 | { 133 | DbgRaiseAssertionFailure(); 134 | } 135 | } 136 | } while (bRes != FALSE); 137 | return 0; 138 | } 139 | 140 | VOID 141 | MonitorLoop ( 142 | _In_ HANDLE MonitorThread, 143 | _In_ HANDLE EmulatorHandle 144 | ) 145 | { 146 | HANDLE handleArray[1]; 147 | MSG msg; 148 | DWORD exitCode; 149 | DWORD handleCount; 150 | 151 | // 152 | // Wait on the monitor thread as well as window messages 153 | // 154 | handleArray[0] = MonitorThread; 155 | handleCount = _countof(handleArray); 156 | for (;;) 157 | { 158 | // 159 | // Do the wait 160 | // 161 | auto waitResult = MsgWaitForMultipleObjectsEx(handleCount, 162 | handleArray, 163 | INFINITE, 164 | QS_ALLINPUT, 165 | MWMO_ALERTABLE | 166 | MWMO_INPUTAVAILABLE); 167 | 168 | // 169 | // If the thread is the one that died, stop waiting on it 170 | // 171 | if (waitResult == handleCount - 1) 172 | { 173 | // 174 | // This'll make future window messages hit the codepath below 175 | // 176 | handleCount = 0; 177 | GetExitCodeProcess(EmulatorHandle, &exitCode); 178 | Trace("Emulator has exited (err=%lx). " 179 | "Investigate state and press ENTER to quit", 180 | exitCode); 181 | (VOID) getc(stdin); 182 | printf(CSI "0m"); 183 | printf(CSI "?1049h"); 184 | break; 185 | } 186 | else if (waitResult == handleCount) 187 | { 188 | // 189 | // This is a window message, so pull it, handle it, and move on 190 | // 191 | if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 192 | { 193 | TranslateMessage(&msg); 194 | DispatchMessage(&msg); 195 | } 196 | } 197 | } 198 | } 199 | 200 | auto 201 | wmain ( 202 | _In_ INT ArgumentCount, 203 | _In_ PWCHAR Arguments[] 204 | ) -> INT 205 | { 206 | SECURITY_ATTRIBUTES secAttr; 207 | HANDLE clientPipe, serverPipe; 208 | LPPROC_THREAD_ATTRIBUTE_LIST attributeList; 209 | SIZE_T size; 210 | PROCESS_INFORMATION procInfo; 211 | STARTUPINFOEX startupInfo; 212 | ULONG mode; 213 | CONSOLE_SCREEN_BUFFER_INFOEX info; 214 | DWORD dwMode; 215 | 216 | // 217 | // Initialize for failure path 218 | // 219 | ZeroMemory(&procInfo, sizeof(procInfo)); 220 | attributeList = nullptr; 221 | clientPipe = serverPipe = INVALID_HANDLE_VALUE; 222 | size = 0; 223 | 224 | // 225 | // Print usage 226 | // 227 | if (ArgumentCount != 2) 228 | { 229 | wprintf(L"Usage: simpleator \n"); 230 | goto Fail; 231 | } 232 | 233 | // 234 | // Create new console 235 | // 236 | FreeConsole(); 237 | AllocConsole(); 238 | 239 | // 240 | // Enable VT-100 processing 241 | // 242 | auto hOut = GetStdHandle(STD_OUTPUT_HANDLE); 243 | GetConsoleMode(hOut, &dwMode); 244 | dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 245 | SetConsoleMode(hOut, dwMode); 246 | 247 | // 248 | // Set desired screen buffer and monitor window position 249 | // 250 | MoveWindow(GetConsoleWindow(), 0, 0, 1500, 640, TRUE); 251 | info.cbSize = sizeof(info); 252 | GetConsoleScreenBufferInfoEx(hOut, &info); 253 | info.dwSize.X = 120; 254 | info.dwSize.Y = 30; 255 | SetConsoleScreenBufferInfoEx(hOut, &info); 256 | 257 | // 258 | // Setup initial VT-100 settings and screen buffers 259 | // 260 | printf(CSI "1;1H" CSI "44m" CSI "2J"); 261 | printf(CSI "1;1H" CSI "102;30mSimpleator v1.1.0-BETA [%S]", Arguments[1]); 262 | printf(CSI "2;1H" ESC "(0" CSI "44;93ml" 263 | "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" 264 | "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" 265 | "qqqqqqqqqqqqqqqqqq" 266 | "k" ESC "(B"); 267 | printf(CSI "29;1H" ESC "(0m" 268 | "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" 269 | "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" 270 | "qqqqqqqqqqqqqqqqqq" 271 | "j" ESC "(B"); 272 | printf(CSI "3g" ESC "H" OSC "0;Monitor Window\x07"); 273 | printf(CSI "3;1H" ESC "(0" 274 | "x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\t" 275 | "x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\t" 276 | "x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\t" 277 | "x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\t" 278 | "x\tx\tx\tx\t" 279 | ESC "(B"); 280 | printf(CSI "3;28r" CSI "3;2H"); 281 | 282 | // 283 | // Print monitor text 284 | // 285 | Trace("The picoVM is launching..."); 286 | 287 | // 288 | // Get the size of an attribute list with one item 289 | // 290 | auto bRes = InitializeProcThreadAttributeList(NULL, 1, 0, &size); 291 | if ((bRes != FALSE) || (size == 0)) 292 | { 293 | Trace("Failed to get size of attribute list (err=%d)\n", 294 | GetLastError()); 295 | goto Fail; 296 | } 297 | 298 | // 299 | // Allocate it 300 | // 301 | attributeList = reinterpret_cast( 302 | HeapAlloc(GetProcessHeap(), 0, size)); 303 | if (attributeList == nullptr) 304 | { 305 | Trace("Failed to allocate attribute list (err=%d)\n", 306 | GetLastError()); 307 | goto Fail; 308 | } 309 | 310 | // 311 | // Now initialize it 312 | // 313 | bRes = InitializeProcThreadAttributeList(attributeList, 1, 0, &size); 314 | if (bRes == FALSE) 315 | { 316 | Trace("Failed to initialize attribute list (err=%d)\n", 317 | GetLastError()); 318 | goto Fail; 319 | } 320 | 321 | // 322 | // Create the server pipe 323 | // 324 | serverPipe = CreateNamedPipe(L"\\\\.\\pipe\\SimplePipe", 325 | PIPE_ACCESS_DUPLEX, 326 | PIPE_TYPE_MESSAGE | 327 | PIPE_READMODE_MESSAGE | 328 | PIPE_WAIT, 329 | 1, 330 | 0, 331 | 0, 332 | 0, 333 | NULL); 334 | if (serverPipe == INVALID_HANDLE_VALUE) 335 | { 336 | Trace("Failed to create named pipe (err=%d)\n", 337 | GetLastError()); 338 | goto Fail; 339 | } 340 | 341 | // 342 | // Open the client end of the pipe and mark it inheritable 343 | // 344 | secAttr.bInheritHandle = TRUE; 345 | secAttr.nLength = sizeof(secAttr); 346 | secAttr.lpSecurityDescriptor = NULL; 347 | clientPipe = CreateFile(L"\\\\.\\pipe\\SimplePipe", 348 | GENERIC_WRITE, 349 | FILE_SHARE_WRITE | FILE_SHARE_READ, 350 | &secAttr, 351 | OPEN_EXISTING, 352 | FILE_ATTRIBUTE_NORMAL, 353 | NULL); 354 | if (serverPipe == INVALID_HANDLE_VALUE) 355 | { 356 | Trace("Failed to open client end of named pipe (err=%d)\n", 357 | GetLastError()); 358 | goto Fail; 359 | } 360 | 361 | // 362 | // Set the client to message mode 363 | // 364 | mode = PIPE_READMODE_MESSAGE; 365 | bRes = SetNamedPipeHandleState(clientPipe, &mode, NULL, NULL); 366 | if (bRes == FALSE) 367 | { 368 | Trace("Failed to change pipe message state (err=%d)\n", 369 | GetLastError()); 370 | goto Fail; 371 | } 372 | 373 | // 374 | // Add the client pipe handle to the list of inheritable handles 375 | // 376 | bRes = UpdateProcThreadAttribute(attributeList, 377 | 0, 378 | PROC_THREAD_ATTRIBUTE_HANDLE_LIST, 379 | &clientPipe, 380 | sizeof(clientPipe), 381 | NULL, 382 | NULL); 383 | if (bRes == FALSE) 384 | { 385 | Trace("Failed to update process attribute list (err=%d)\n", 386 | GetLastError()); 387 | goto Fail; 388 | } 389 | 390 | // 391 | // Setup the startup information to use the duplicate pipe handle as STDOUT 392 | // 393 | ZeroMemory(&startupInfo, sizeof(startupInfo)); 394 | startupInfo.StartupInfo.cb = sizeof(startupInfo); 395 | startupInfo.lpAttributeList = attributeList; 396 | startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES; 397 | startupInfo.StartupInfo.hStdOutput = clientPipe; 398 | 399 | // 400 | // Begin a loop launching the emulator 401 | // 402 | for (;;) 403 | { 404 | // 405 | // Start it up, passing in the target binary name 406 | // 407 | ZeroMemory(&procInfo, sizeof(procInfo)); 408 | bRes = CreateProcess(L"emulator.exe", 409 | GetCommandLine(), 410 | NULL, 411 | NULL, 412 | TRUE, 413 | CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT, 414 | NULL, 415 | NULL, 416 | &startupInfo.StartupInfo, 417 | &procInfo); 418 | 419 | // 420 | // If we couldn't launch the process, stop trying to launch it 421 | // 422 | if (bRes == FALSE) 423 | { 424 | Trace("Failed to launch process (err=%d)\n", 425 | GetLastError()); 426 | goto Fail; 427 | } 428 | 429 | // 430 | // Try to reserve the bottom 2GB 431 | // 432 | auto base = VirtualAllocEx(procInfo.hProcess, 433 | s_LowestValidAddress, 434 | reinterpret_cast( 435 | s_UserSharedData) - 436 | reinterpret_cast( 437 | s_LowestValidAddress), 438 | MEM_RESERVE, 439 | PAGE_READWRITE); 440 | if (base == s_LowestValidAddress) 441 | { 442 | // 443 | // Now allocate everything from 2GB to 256GB 444 | // 445 | base = VirtualAllocEx(procInfo.hProcess, 446 | s_UserSharedDataEnd, 447 | s_256GB - 448 | reinterpret_cast( 449 | s_UserSharedDataEnd), 450 | MEM_RESERVE, 451 | PAGE_READWRITE); 452 | if (base == s_UserSharedDataEnd) 453 | { 454 | // 455 | // We have reserved the address space, emulator is ready to go! 456 | // 457 | break; 458 | } 459 | } 460 | 461 | // 462 | // We failed to obtain the reservation we need, try relaunching 463 | // 464 | TerminateProcess(procInfo.hProcess, GetLastError()); 465 | CloseHandle(procInfo.hThread); 466 | CloseHandle(procInfo.hProcess); 467 | } 468 | 469 | // 470 | // Now close our copy of the duplicated pipe handle 471 | // 472 | CloseHandle(clientPipe); 473 | clientPipe = INVALID_HANDLE_VALUE; 474 | 475 | // 476 | // Create the debugger/monitor windows 477 | // 478 | auto hr = SemCreateDebuggerWindows(); 479 | if (FAILED(hr)) 480 | { 481 | goto Fail; 482 | } 483 | 484 | // 485 | // Create the pipe monitor thread 486 | // 487 | auto hThread = CreateThread(NULL, 488 | 0, 489 | SemMonThread, 490 | reinterpret_cast(serverPipe), 491 | 0, 492 | NULL); 493 | if (hThread == nullptr) 494 | { 495 | goto Fail; 496 | } 497 | 498 | // 499 | // Start the emulator and close our handles to it 500 | // 501 | ResumeThread(procInfo.hThread); 502 | CloseHandle(procInfo.hThread); 503 | procInfo.hThread = nullptr; 504 | 505 | // 506 | // Print monitor text 507 | // 508 | Trace("The picoVM is executing..."); 509 | 510 | // 511 | // Now we'll block until the emulator exists 512 | // 513 | MonitorLoop(hThread, procInfo.hProcess); 514 | CloseHandle(hThread); 515 | 516 | Fail: 517 | // 518 | // Cleanup all our handles and allocations if any are dangling 519 | // 520 | if (procInfo.hProcess != nullptr) 521 | { 522 | CloseHandle(procInfo.hProcess); 523 | } 524 | if (procInfo.hThread != nullptr) 525 | { 526 | CloseHandle(procInfo.hThread); 527 | } 528 | if (attributeList != nullptr) 529 | { 530 | HeapFree(GetProcessHeap(), 0, attributeList); 531 | } 532 | return 0; 533 | } 534 | -------------------------------------------------------------------------------- /monitor/monitor.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 15.0 11 | {11E5E98F-C3BF-4665-A294-6ADA7FE41B8E} 12 | Win32Proj 13 | 10.0.18290.0 14 | simpleator 15 | 16 | 17 | 18 | Application 19 | false 20 | v141 21 | true 22 | Unicode 23 | 24 | 25 | 26 | 27 | ..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath); 28 | 29 | 30 | 31 | NotUsing 32 | Level3 33 | Disabled 34 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 35 | ProgramDatabase 36 | Default 37 | MultiThreaded 38 | precomp.h 39 | 40 | 41 | Console 42 | winhvplatform.lib;winhvemulation.lib;onecoreuap.lib;%(AdditionalDependencies) 43 | 44 | 45 | 46 | 47 | Level4 48 | Use 49 | MaxSpeed 50 | true 51 | true 52 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 53 | mon.h 54 | AnySuitable 55 | Speed 56 | true 57 | 58 | 59 | Console 60 | true 61 | true 62 | 63 | 64 | 65 | 66 | 67 | Create 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /monitor/ui.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | ui.cpp 8 | 9 | Abstract: 10 | 11 | This module implements the main UI for the Simple Emulator Debug Monitor 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "mon.h" 24 | 25 | HWND hwndRegisterWindow; 26 | HDC dcRegisterWindow; 27 | RECT rcRegisterWindow; 28 | 29 | HWND hwndDebugWindow; 30 | HDC dcDebugWindow; 31 | RECT rcDebugWindow; 32 | 33 | HFONT hFont; 34 | HBRUSH hBrush; 35 | 36 | auto 37 | UpdateDebugMonitor ( 38 | _In_ PCHAR DebugMessage, 39 | _In_ ULONG DebugMessageLength 40 | ) -> VOID 41 | { 42 | RECT rcClear; 43 | 44 | // 45 | // First, calculate the rectangle for this text 46 | // 47 | auto calcRes = DrawTextA(dcDebugWindow, 48 | DebugMessage, 49 | DebugMessageLength, 50 | &rcDebugWindow, 51 | DT_CALCRECT); 52 | if (calcRes == 0) 53 | { 54 | DbgRaiseAssertionFailure(); 55 | } 56 | 57 | // 58 | // Clear the line we're about to draw on 59 | // 60 | rcClear = rcDebugWindow; 61 | rcClear.right = DEBUG_MONITOR_WINDOW_WIDTH; 62 | FillRect(dcDebugWindow, &rcClear, hBrush); 63 | 64 | // 65 | // Now draw the actual text, and return the height 66 | // 67 | auto height = DrawTextA(dcDebugWindow, 68 | DebugMessage, 69 | DebugMessageLength, 70 | &rcDebugWindow, 71 | DT_EXPANDTABS | DT_WORDBREAK); 72 | 73 | // 74 | // Redraw the window 75 | // 76 | RedrawWindow(hwndDebugWindow, &rcDebugWindow, NULL, RDW_UPDATENOW); 77 | 78 | // 79 | // Update the rectangle with the height of the text we just drew 80 | // 81 | OffsetRect(&rcDebugWindow, 0, height); 82 | 83 | // 84 | // If we went past the height of the window, restart at the top 85 | // 86 | if (rcDebugWindow.top > DEBUG_MONITOR_WINDOW_HEIGHT) 87 | { 88 | GetClientRect(hwndDebugWindow, &rcDebugWindow); 89 | } 90 | } 91 | 92 | auto 93 | UpdateRegisterWindow ( 94 | _In_ PSEM_PIPE_BUFFER_MSG Msg 95 | ) -> VOID 96 | { 97 | CHAR regBuffer[8192]; 98 | PCHAR buffer = regBuffer; 99 | 100 | // 101 | // Dump GPRs 102 | // 103 | buffer[0] = ANSI_NULL; 104 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 105 | "RAX=%016I64x RBX=%016I64x RCX=%016I64x\n", 106 | Msg->Registers.Rax.LowPart, 107 | Msg->Registers.Rbx.LowPart, 108 | Msg->Registers.Rcx.LowPart); 109 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 110 | "RDX=%016I64x RSI=%016I64x RDI=%016I64x\n", 111 | Msg->Registers.Rdx.LowPart, 112 | Msg->Registers.Rsi.LowPart, 113 | Msg->Registers.Rdi.LowPart); 114 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 115 | "RIP=%016I64x RSP=%016I64x RBP=%016I64x\n", 116 | Msg->Registers.Rip.LowPart, 117 | Msg->Registers.Rsp.LowPart, 118 | Msg->Registers.Rbp.LowPart); 119 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 120 | " R8=%016I64x R9=%016I64x R10=%016I64x\n", 121 | Msg->Registers.R8.LowPart, 122 | Msg->Registers.R9.LowPart, 123 | Msg->Registers.R10.LowPart); 124 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 125 | "R11=%016I64x R12=%016I64x R13=%016I64x\n", 126 | Msg->Registers.R11.LowPart, 127 | Msg->Registers.R12.LowPart, 128 | Msg->Registers.R13.LowPart); 129 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 130 | "R14=%016I64x R15=%016I64x\n", 131 | Msg->Registers.R14.LowPart, 132 | Msg->Registers.R15.LowPart); 133 | 134 | // 135 | // Dump flags and segments 136 | // 137 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 138 | "IOPL=%01d\n", 139 | (Msg->Registers.Rflags.LowPart & 0x3000) >> 12); 140 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 141 | "CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x" 142 | " EFL=%08I32x\n", 143 | Msg->Registers.Cs.LowPart & 0xFFFF, 144 | Msg->Registers.Ss.LowPart & 0xFFFF, 145 | Msg->Registers.Ds.LowPart & 0xFFFF, 146 | Msg->Registers.Es.LowPart & 0xFFFF, 147 | Msg->Registers.Fs.LowPart & 0xFFFF, 148 | Msg->Registers.Gs.LowPart & 0xFFFF, 149 | Msg->Registers.Rflags.LowPart); 150 | 151 | // 152 | // Dump control regs 153 | // 154 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 155 | "CR0=%08I32x CR2=%016I64x CR3=%016I64x\n", 156 | Msg->Registers.Cr0.LowPart, 157 | Msg->Registers.Cr2.LowPart, 158 | Msg->Registers.Cr3.LowPart); 159 | StringCbPrintfExA(buffer, sizeof(regBuffer), &buffer, NULL, 0, 160 | "CR4=%08I32x CR8=%08I32x\n", 161 | Msg->Registers.Cr4.LowPart, 162 | Msg->Registers.Cr8.LowPart); 163 | 164 | // 165 | // Draw the text 166 | // 167 | auto height = DrawTextA(dcRegisterWindow, 168 | regBuffer, 169 | static_cast(buffer - regBuffer), 170 | &rcRegisterWindow, 171 | 0); 172 | if (height == 0) 173 | { 174 | DbgRaiseAssertionFailure(); 175 | } 176 | 177 | // 178 | // Redraw the window 179 | // 180 | RedrawWindow(hwndRegisterWindow, &rcRegisterWindow, NULL, RDW_UPDATENOW); 181 | } 182 | 183 | auto 184 | CALLBACK 185 | WindowProc ( 186 | _In_ HWND hwnd, 187 | _In_ UINT uMsg, 188 | _In_ WPARAM wParam, 189 | _In_ LPARAM lParam 190 | ) -> LRESULT 191 | { 192 | PAINTSTRUCT ps; 193 | 194 | // 195 | // What window message is this? 196 | // 197 | switch (uMsg) 198 | { 199 | // 200 | // Paint window 201 | // 202 | case WM_PAINT: 203 | { 204 | { 205 | // 206 | // Start the paint operation 207 | // 208 | auto hdc = BeginPaint(hwnd, &ps); 209 | 210 | // 211 | // Which window is being painted? 212 | // 213 | if (hwnd == hwndDebugWindow) 214 | { 215 | // 216 | // The debug window has a blue background 217 | // 218 | FillRect(hdc, &ps.rcPaint, hBrush); 219 | } 220 | else 221 | { 222 | // 223 | // The regiser window has the default window text color 224 | // 225 | FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOWTEXT)); 226 | } 227 | 228 | // 229 | // Finish painting 230 | // 231 | EndPaint(hwnd, &ps); 232 | } 233 | 234 | // 235 | // We're done here, don't let Windows paint on top 236 | // 237 | return 0; 238 | } 239 | 240 | // 241 | // Destroy message 242 | // 243 | case WM_DESTROY: 244 | // 245 | // Exit the message loop 246 | // 247 | PostQuitMessage(0); 248 | return 0; 249 | } 250 | 251 | // 252 | // Call the default window procedure for anything else 253 | // 254 | return DefWindowProc(hwnd, uMsg, wParam, lParam); 255 | } 256 | 257 | auto 258 | SemCreateDebuggerWindows ( 259 | VOID 260 | ) -> HRESULT 261 | { 262 | WNDCLASS wc; 263 | 264 | // 265 | // Register the window class 266 | // 267 | ZeroMemory(&wc, sizeof(wc)); 268 | wc.lpfnWndProc = WindowProc; 269 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 270 | wc.lpszClassName = L"Simpleator"; 271 | auto atomClass = RegisterClass(&wc); 272 | if (atomClass == FALSE) 273 | { 274 | return GetLastError(); 275 | } 276 | 277 | // 278 | // Create a blue color brush 279 | // 280 | auto bClassRegistered = true; 281 | hBrush = CreateSolidBrush(RGB(0, 0, 128)); 282 | if (hBrush == nullptr) 283 | { 284 | goto Failure; 285 | } 286 | 287 | // 288 | // Create the debug window 289 | // 290 | hwndDebugWindow = CreateWindowEx(0, 291 | L"Simpleator", 292 | L"Debug Output Window", 293 | WS_VISIBLE, 294 | 0, 295 | DEBUG_MONITOR_WINDOW_TOP, 296 | DEBUG_MONITOR_WINDOW_WIDTH, 297 | DEBUG_MONITOR_WINDOW_HEIGHT, 298 | NULL, 299 | NULL, 300 | NULL, 301 | NULL); 302 | if (hwndDebugWindow == nullptr) 303 | { 304 | goto Failure; 305 | } 306 | 307 | // 308 | // Get the display context of the debug window 309 | // 310 | dcDebugWindow = GetDC(hwndDebugWindow); 311 | if (dcDebugWindow == nullptr) 312 | { 313 | goto Failure; 314 | } 315 | 316 | // 317 | // Get the window position of the debug window 318 | // 319 | auto bRes = GetClientRect(hwndDebugWindow, &rcDebugWindow); 320 | if (bRes == FALSE) 321 | { 322 | goto Failure; 323 | } 324 | 325 | // 326 | // Fully opaque background, deep blue background, white foreground 327 | // 328 | SetBkMode(dcDebugWindow, OPAQUE); 329 | SetBkColor(dcDebugWindow, RGB(0, 0, 128)); 330 | SetTextColor(dcDebugWindow, RGB(255, 255, 255)); 331 | 332 | 333 | // 334 | // Use the Consolas font for this window 335 | // 336 | hFont = CreateFont(12, 337 | 0, 338 | 0, 339 | 0, 340 | FW_HEAVY, 341 | FALSE, 342 | FALSE, 343 | FALSE, 344 | ANSI_CHARSET, 345 | OUT_DEFAULT_PRECIS, 346 | CLIP_DEFAULT_PRECIS, 347 | DEFAULT_QUALITY, 348 | DEFAULT_PITCH | FF_DONTCARE, 349 | L"Consolas"); 350 | auto hOldFont = SelectObject(dcDebugWindow, hFont); 351 | if (hOldFont == NULL) 352 | { 353 | goto Failure; 354 | } 355 | 356 | // 357 | // Create the regsiter window 358 | // 359 | hwndRegisterWindow = CreateWindowEx(0, 360 | L"Simpleator", 361 | L"Register Window", 362 | WS_VISIBLE, 363 | 960, 364 | 0, 365 | 632, 366 | 158, 367 | NULL, 368 | NULL, 369 | NULL, 370 | NULL); 371 | 372 | // 373 | // Get the display context of the register window 374 | // 375 | dcRegisterWindow = GetDC(hwndRegisterWindow); 376 | if (dcRegisterWindow == nullptr) 377 | { 378 | goto Failure; 379 | } 380 | 381 | // 382 | // Get the window position of the register window 383 | // 384 | bRes = GetClientRect(hwndRegisterWindow, &rcRegisterWindow); 385 | if (bRes == FALSE) 386 | { 387 | goto Failure; 388 | } 389 | 390 | // 391 | // Fully opaque background, black background, red foreground 392 | // 393 | SetBkMode(dcRegisterWindow, OPAQUE); 394 | SetBkColor(dcRegisterWindow, 0); 395 | SetTextColor(dcRegisterWindow, RGB(255, 0, 0)); 396 | 397 | // 398 | // Get a handle to the OEM fixed width font 399 | // 400 | hFont = (HFONT)GetStockObject(OEM_FIXED_FONT); 401 | if (hFont == nullptr) 402 | { 403 | goto Failure; 404 | } 405 | 406 | // 407 | // Use the OEM fixed width font into this window 408 | // 409 | hOldFont = SelectObject(dcRegisterWindow, hFont); 410 | if (hOldFont == NULL) 411 | { 412 | goto Failure; 413 | } 414 | 415 | // 416 | // Success path 417 | // 418 | return ERROR_SUCCESS; 419 | 420 | Failure: 421 | // 422 | // Destroy all GUI state 423 | // 424 | if (hwndRegisterWindow != nullptr) 425 | { 426 | DestroyWindow(hwndRegisterWindow); 427 | } 428 | 429 | if (hwndRegisterWindow != nullptr) 430 | { 431 | DestroyWindow(hwndRegisterWindow); 432 | } 433 | if (bClassRegistered) 434 | { 435 | UnregisterClass(L"Simpleator", NULL); 436 | } 437 | 438 | // 439 | // Return the failure code 440 | // 441 | return GetLastError(); 442 | } 443 | -------------------------------------------------------------------------------- /provider/ntemu.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | ntemu.h 8 | 9 | Abstract: 10 | 11 | This header defines the native system call function types for RS5/19H1. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #pragma once 24 | 25 | EXTERN_C_START 26 | 27 | #define ProcessCookie 0x24 28 | #define FileFsAttributeInformation 0x04 29 | #define ProcessOwnerInformation 0x31 30 | #define SystemEmulationBasicInformation 0x3E 31 | #define SystemHypervisorSharedPageInformation 0xC5 32 | #define SystemNumaProcessorMap 0x37 33 | #define SystemRangeStartInformation 0x32 34 | #define SystemFlushInformation 0xC0 35 | 36 | typedef ULONG SECTION_INHERIT; 37 | typedef ULONG EVENT_TYPE; 38 | typedef ULONG FS_INFORMATION_CLASS; 39 | typedef ULONG MEMORY_INFORMATION_CLASS; 40 | 41 | NTSYSAPI 42 | NTSTATUS 43 | NTAPI 44 | NtCreateEvent ( 45 | _Out_ PHANDLE EventHandle, 46 | _In_ ACCESS_MASK DesiredAccess, 47 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 48 | _In_ EVENT_TYPE EventType, 49 | _In_ BOOLEAN InitialState 50 | ); 51 | 52 | NTSYSAPI 53 | NTSTATUS 54 | NTAPI 55 | NtTerminateProcess ( 56 | _In_opt_ HANDLE ProcessHandle, 57 | _In_ NTSTATUS ExitStatus 58 | ); 59 | 60 | NTSYSAPI 61 | NTSTATUS 62 | NTAPI 63 | NtWriteFile ( 64 | _In_ HANDLE FileHandle, 65 | _In_opt_ HANDLE Event, 66 | _In_opt_ PIO_APC_ROUTINE ApcRoutine, 67 | _In_opt_ PVOID ApcContext, 68 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 69 | _In_reads_bytes_(Length) PVOID Buffer, 70 | _In_ ULONG Length, 71 | _In_opt_ PLARGE_INTEGER ByteOffset, 72 | _In_opt_ PULONG Key 73 | ); 74 | 75 | NTSYSAPI 76 | NTSTATUS 77 | NTAPI 78 | NtOpenSection ( 79 | _Out_ PHANDLE SectionHandle, 80 | _In_ ACCESS_MASK DesiredAccess, 81 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 82 | ); 83 | 84 | NTSYSAPI 85 | NTSTATUS 86 | NTAPI 87 | NtQueryVolumeInformationFile ( 88 | _In_ HANDLE FileHandle, 89 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 90 | _Out_writes_bytes_(Length) PVOID FsInformation, 91 | _In_ ULONG Length, 92 | _In_ FS_INFORMATION_CLASS FsInformationClass 93 | ); 94 | 95 | NTSYSAPI 96 | NTSTATUS 97 | NTAPI 98 | NtQueryPerformanceCounter ( 99 | _Out_ PLARGE_INTEGER PerformanceCounter, 100 | _Out_opt_ PLARGE_INTEGER PerformanceFrequency 101 | ); 102 | 103 | NTSYSAPI 104 | NTSTATUS 105 | NTAPI 106 | NtAllocateVirtualMemoryEx ( 107 | _In_opt_ HANDLE Process, 108 | _In_opt_ PVOID* BaseAddress, 109 | _In_ SIZE_T* RegionSize, 110 | _In_ ULONG AllocationType, 111 | _In_ ULONG PageProtection, 112 | _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* Parameters, 113 | _In_ ULONG ParameterCount 114 | ); 115 | 116 | NTSYSAPI 117 | NTSTATUS 118 | NTAPI 119 | NtSetInformationProcess ( 120 | _In_ HANDLE ProcessHandle, 121 | _In_ PROCESSINFOCLASS ProcessInformationClass, 122 | _In_reads_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation, 123 | _In_ ULONG ProcessInformationLength 124 | ); 125 | 126 | NTSYSAPI 127 | NTSTATUS 128 | NTAPI 129 | NtSetEvent ( 130 | _In_ HANDLE EventHandle, 131 | _Out_opt_ PLONG PreviousState 132 | ); 133 | 134 | NTSYSAPI 135 | NTSTATUS 136 | NTAPI 137 | NtOpenDirectoryObject ( 138 | _Out_ PHANDLE DirectoryHandle, 139 | _In_ ACCESS_MASK DesiredAccess, 140 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 141 | ); 142 | 143 | NTSYSAPI 144 | NTSTATUS 145 | NTAPI 146 | NtMapViewOfSectionEx ( 147 | _In_ HANDLE SectionHandle, 148 | _In_ HANDLE ProcessHandle, 149 | _Inout_ _At_ (*BaseAddress, 150 | _Readable_bytes_ (*ViewSize) 151 | _Writable_bytes_ (*ViewSize) 152 | _Post_readable_byte_size_ (*ViewSize)) PVOID *BaseAddress, 153 | _Inout_opt_ PLARGE_INTEGER SectionOffset, 154 | _Inout_ PSIZE_T ViewSize, 155 | _In_ ULONG AllocationType, 156 | _In_ ULONG Win32Protect, 157 | _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* Parameters, 158 | _In_ ULONG ParameterCount 159 | ); 160 | 161 | NTSYSAPI 162 | NTSTATUS 163 | NTAPI 164 | NtQueryVirtualMemory ( 165 | _In_ HANDLE ProcessHandle, 166 | _In_opt_ PVOID BaseAddress, 167 | _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, 168 | _Out_writes_bytes_(MemoryInformationLength) PVOID MemoryInformation, 169 | _In_ SIZE_T MemoryInformationLength, 170 | _Out_opt_ PSIZE_T ReturnLength 171 | ); 172 | 173 | NTSYSAPI 174 | NTSTATUS 175 | NTAPI 176 | NtProtectVirtualMemory ( 177 | _In_ HANDLE ProcessHandle, 178 | _Inout_ PVOID *BaseAddress, 179 | _Inout_ PSIZE_T RegionSize, 180 | _In_ ULONG NewProtect, 181 | _Out_ PULONG OldProtect 182 | ); 183 | 184 | NTSYSAPI 185 | NTSTATUS 186 | NTAPI 187 | NtDuplicateObject ( 188 | _In_ HANDLE SourceProcessHandle, 189 | _In_ HANDLE SourceHandle, 190 | _In_opt_ HANDLE TargetProcessHandle, 191 | _Out_opt_ PHANDLE TargetHandle, 192 | _In_ ACCESS_MASK DesiredAccess, 193 | _In_ ULONG HandleAttributes, 194 | _In_ ULONG Options 195 | ); 196 | 197 | NTSYSAPI 198 | NTSTATUS 199 | NTAPI 200 | NtQuerySymbolicLinkObject ( 201 | _In_ HANDLE LinkHandle, 202 | _Inout_ PUNICODE_STRING LinkTarget, 203 | _Out_opt_ PULONG ReturnedLength 204 | ); 205 | 206 | NTSYSAPI 207 | NTSTATUS 208 | NTAPI 209 | NtQueryDebugFilterState ( 210 | _In_ ULONG ComponentId, 211 | _In_ ULONG Level 212 | ); 213 | 214 | NTSYSAPI 215 | NTSTATUS 216 | NTAPI 217 | NtOpenSymbolicLinkObject ( 218 | _Out_ PHANDLE LinkHandle, 219 | _In_ ACCESS_MASK DesiredAccess, 220 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 221 | ); 222 | 223 | NTSYSAPI 224 | NTSTATUS 225 | NTAPI 226 | NtFreeVirtualMemory ( 227 | _In_ HANDLE ProcessHandle, 228 | _Inout_ __drv_freesMem(Mem) PVOID *BaseAddress, 229 | _Inout_ PSIZE_T RegionSize, 230 | _In_ ULONG FreeType 231 | ); 232 | 233 | EXTERN_C_END 234 | 235 | -------------------------------------------------------------------------------- /provider/provider.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | provider.cpp 8 | 9 | Abstract: 10 | 11 | This module implements the RS5/19H1 System Call Provider for guest VMs. 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include "provider.h" 24 | 25 | static SEM_PROVIDER_CALLBACKS s_SemCallbacks; 26 | 27 | auto 28 | HookNtMapViewOfSection ( 29 | _In_ HANDLE SectionHandle, 30 | _In_ HANDLE ProcessHandle, 31 | _Inout_ _At_ (*BaseAddress, 32 | _Readable_bytes_ (*ViewSize) 33 | _Writable_bytes_ (*ViewSize) 34 | _Post_readable_byte_size_ (*ViewSize)) PVOID *BaseAddress, 35 | _In_ ULONG_PTR ZeroBits, 36 | _In_ SIZE_T CommitSize, 37 | _Inout_opt_ PLARGE_INTEGER SectionOffset, 38 | _Inout_ PSIZE_T ViewSize, 39 | _In_ SECTION_INHERIT InheritDisposition, 40 | _In_ ULONG AllocationType, 41 | _In_ ULONG Win32Protect 42 | ) 43 | { 44 | MEM_EXTENDED_PARAMETER extendedParam; 45 | MEM_ADDRESS_REQUIREMENTS reqs; 46 | auto status = STATUS_SUCCESS; 47 | 48 | // 49 | // Combinations of parameters that we don't wish to emulate aren't handled 50 | // 51 | if ((ZeroBits) || (CommitSize) || (InheritDisposition != 1)) 52 | { 53 | status = STATUS_NOT_SUPPORTED; 54 | goto HookExit; 55 | } 56 | 57 | // 58 | // Nor are attempts to influence external host processes 59 | // 60 | if (ProcessHandle != GetCurrentProcess()) 61 | { 62 | status = STATUS_ACCESS_DENIED; 63 | goto HookExit; 64 | } 65 | 66 | // 67 | // Check if this is a brand new commit 68 | // 69 | if (*BaseAddress == nullptr) 70 | { 71 | // 72 | // Ask the kernel to only allocate in the region of valid guest memory 73 | // 74 | reqs.Alignment = 0; 75 | reqs.LowestStartingAddress = s_LowestValidAddress; 76 | reqs.HighestEndingAddress = s_HighestValidAddress; 77 | extendedParam.Reserved = 0; 78 | extendedParam.Pointer = &reqs; 79 | extendedParam.Type = MemExtendedParameterAddressRequirements; 80 | } 81 | else if (!s_SemCallbacks.IsGuestMemory(*BaseAddress)) 82 | { 83 | // 84 | // This is a commit on top of a reservation -- it must be guest memory 85 | // 86 | status = STATUS_CONFLICTING_ADDRESSES; 87 | goto HookExit; 88 | } 89 | 90 | // 91 | // Ask the kernel to do the map, noting that an existing reservation means 92 | // that we do not pass in extended parameters and simply re-use the base 93 | // 94 | status = NtMapViewOfSectionEx(SectionHandle, 95 | ProcessHandle, 96 | BaseAddress, 97 | SectionOffset, 98 | ViewSize, 99 | AllocationType, 100 | Win32Protect, 101 | (*BaseAddress == nullptr) ? 102 | &extendedParam : nullptr, 103 | (*BaseAddress == nullptr)); 104 | 105 | // 106 | // We expect all image mappings to 'fail' with this code, due to relocation 107 | // records, which will then be fixed up by the loader on the guest side 108 | // 109 | if (status != STATUS_IMAGE_AT_DIFFERENT_BASE) 110 | { 111 | status = STATUS_NO_MEMORY; 112 | goto HookExit; 113 | } 114 | 115 | // 116 | // Finally, we must map the shared image in the GPA 117 | // 118 | auto hr = s_SemCallbacks.MapSharedImage(nullptr, 119 | (ULONG_PTR)*BaseAddress, 120 | *ViewSize); 121 | if (FAILED(hr)) DbgRaiseAssertionFailure(); 122 | goto HookExit; 123 | 124 | HookExit: 125 | return EncodeStatus(status); 126 | } 127 | 128 | auto 129 | HookNtAllocateVirtualMemory ( 130 | _In_ HANDLE ProcessHandle, 131 | _Inout_ _At_ (*BaseAddress, 132 | _Readable_bytes_ (*RegionSize) 133 | _Writable_bytes_ (*RegionSize) 134 | _Post_readable_byte_size_ (*RegionSize)) PVOID *BaseAddress, 135 | _In_ ULONG_PTR ZeroBits, 136 | _Inout_ PSIZE_T RegionSize, 137 | _In_ ULONG AllocationType, 138 | _In_ ULONG Protect 139 | ) 140 | { 141 | MEM_EXTENDED_PARAMETER extendedParam; 142 | MEM_ADDRESS_REQUIREMENTS reqs; 143 | auto status = STATUS_SUCCESS; 144 | 145 | // 146 | // Combinations of parameters that we don't wish to emulate aren't handled 147 | // 148 | if (ZeroBits != 0) 149 | { 150 | status = STATUS_NOT_SUPPORTED; 151 | goto HookExit; 152 | } 153 | 154 | // 155 | // Nor are attempts to influence external host processes 156 | // 157 | if (ProcessHandle != GetCurrentProcess()) 158 | { 159 | status = STATUS_ACCESS_DENIED; 160 | goto HookExit; 161 | } 162 | 163 | // 164 | // Check if this is a brand new commit 165 | // 166 | if (*BaseAddress == nullptr) 167 | { 168 | // 169 | // Ask the kernel to only allocate in the region of valid guest memory 170 | // 171 | reqs.Alignment = 0; 172 | reqs.LowestStartingAddress = s_LowestValidAddress; 173 | reqs.HighestEndingAddress = s_HighestValidAddress; 174 | extendedParam.Reserved = 0; 175 | extendedParam.Pointer = &reqs; 176 | extendedParam.Type = MemExtendedParameterAddressRequirements; 177 | } 178 | else if (!s_SemCallbacks.IsGuestMemory(*BaseAddress)) 179 | { 180 | // 181 | // This is a commit on top of a reservation -- it must be guest memory 182 | // 183 | status = STATUS_CONFLICTING_ADDRESSES; 184 | goto HookExit; 185 | } 186 | 187 | // 188 | // Ask the kernel to allocate, noting that an existing reservation means 189 | // that we do not pass in extended parameters and simply re-use the base 190 | // 191 | status = NtAllocateVirtualMemoryEx(ProcessHandle, 192 | BaseAddress, 193 | RegionSize, 194 | AllocationType, 195 | Protect, 196 | (*BaseAddress == nullptr) ? 197 | &extendedParam : nullptr, 198 | (*BaseAddress == nullptr)); 199 | if (!NT_SUCCESS(status)) 200 | { 201 | goto HookExit; 202 | } 203 | 204 | // 205 | // If memory was actually committed, we must map it in the GPA 206 | // 207 | if (AllocationType & MEM_COMMIT) 208 | { 209 | auto hr = s_SemCallbacks.MapSharedMemory(nullptr, 210 | reinterpret_cast( 211 | *BaseAddress), 212 | *RegionSize, 213 | nullptr); 214 | if (FAILED(hr)) DbgRaiseAssertionFailure(); 215 | goto HookExit; 216 | } 217 | 218 | HookExit: 219 | return EncodeStatus(status); 220 | } 221 | 222 | auto 223 | HookNtAllocateVirtualMemoryEx ( 224 | _In_ HANDLE ProcessHandle, 225 | _Inout_ _At_ (*BaseAddress, 226 | _Readable_bytes_ (*RegionSize) 227 | _Writable_bytes_ (*RegionSize) 228 | _Post_readable_byte_size_ (*RegionSize)) PVOID *BaseAddress, 229 | _Inout_ PSIZE_T RegionSize, 230 | _In_ ULONG AllocationType, 231 | _In_ ULONG PageProtection, 232 | _Inout_updates_opt_(ParameterCount) PMEM_EXTENDED_PARAMETER Parameters, 233 | _In_ ULONG ParameterCount 234 | ) 235 | { 236 | auto status = STATUS_SUCCESS; 237 | 238 | // 239 | // Check if the caller provided extended parameters 240 | // 241 | if (ParameterCount >= 1) 242 | { 243 | // 244 | // The only one we support are the address requirement paremeters 245 | // 246 | if (Parameters->Type == MemExtendedParameterAddressRequirements) 247 | { 248 | // 249 | // Which we override to specify that only guest memory must be used 250 | // 251 | auto reqs = reinterpret_cast 252 | (Parameters->Pointer); 253 | reqs->LowestStartingAddress = s_LowestValidAddress; 254 | reqs->HighestEndingAddress = s_HighestValidAddress; 255 | } 256 | else 257 | { 258 | status = STATUS_NOT_SUPPORTED; 259 | goto HookExit; 260 | } 261 | } 262 | else if (!s_SemCallbacks.IsGuestMemory(*BaseAddress)) 263 | { 264 | // 265 | // This is a commit on top of a reservation -- it must be guest memory 266 | // 267 | status = STATUS_CONFLICTING_ADDRESSES; 268 | goto HookExit; 269 | } 270 | 271 | // 272 | // Attempts to influence external host processes are blocked 273 | // 274 | if (ProcessHandle != GetCurrentProcess()) 275 | { 276 | status = STATUS_ACCESS_DENIED; 277 | goto HookExit; 278 | } 279 | 280 | // 281 | // Ask the kernel to allocate 282 | // 283 | status = NtAllocateVirtualMemoryEx(ProcessHandle, 284 | BaseAddress, 285 | RegionSize, 286 | AllocationType, 287 | PageProtection, 288 | Parameters, 289 | ParameterCount); 290 | if (!NT_SUCCESS(status)) 291 | { 292 | goto HookExit; 293 | } 294 | 295 | // 296 | // If memory was actually committed, we must map it in the GPA 297 | // 298 | if (AllocationType & MEM_COMMIT) 299 | { 300 | auto hr = s_SemCallbacks.MapSharedMemory(nullptr, 301 | reinterpret_cast 302 | (*BaseAddress), 303 | *RegionSize, 304 | nullptr); 305 | if (FAILED(hr)) DbgRaiseAssertionFailure(); 306 | goto HookExit; 307 | } 308 | 309 | HookExit: 310 | return EncodeStatus(status); 311 | } 312 | 313 | auto 314 | HookNtFreeVirtualMemory ( 315 | _In_ HANDLE ProcessHandle, 316 | _Inout_ __drv_freesMem(Mem) PVOID *BaseAddress, 317 | _Inout_ PSIZE_T RegionSize, 318 | _In_ ULONG FreeType 319 | ) 320 | { 321 | auto status = STATUS_SUCCESS; 322 | 323 | // 324 | // If this is the loader trying to unmap the old PPB, make this a no-op 325 | // 326 | if (*BaseAddress == (PVOID)((ULONG_PTR)s_SemCallbacks.GetCurrentTeb()->ProcessEnvironmentBlock + 0x1000)) 327 | { 328 | goto HookExit; 329 | } 330 | 331 | // 332 | // Only guest memory should be freed 333 | // 334 | if (!s_SemCallbacks.IsGuestMemory(*BaseAddress)) 335 | { 336 | status = STATUS_UNABLE_TO_FREE_VM; 337 | goto HookExit; 338 | } 339 | 340 | // 341 | // Attempts to influence external host processes are blocked 342 | // 343 | if (ProcessHandle != GetCurrentProcess()) 344 | { 345 | status = STATUS_ACCESS_DENIED; 346 | goto HookExit; 347 | } 348 | 349 | // 350 | // Unmap the respective GPA 351 | // 352 | auto hr = s_SemCallbacks.UnmapSharedMemory(nullptr, 353 | reinterpret_cast 354 | (*BaseAddress), 355 | *RegionSize, 356 | nullptr); 357 | if (FAILED(hr)) DbgRaiseAssertionFailure(); 358 | 359 | // 360 | // And then issue the call to the host kernel to free the VA 361 | // 362 | status = NtFreeVirtualMemory(ProcessHandle, 363 | BaseAddress, 364 | RegionSize, 365 | FreeType); 366 | goto HookExit; 367 | 368 | HookExit: 369 | return EncodeStatus(status); 370 | } 371 | 372 | auto 373 | HookNtProtectVirtualMemory ( 374 | _In_ HANDLE ProcessHandle, 375 | _Inout_ PVOID *BaseAddress, 376 | _Inout_ PSIZE_T RegionSize, 377 | _In_ ULONG NewProtect, 378 | _Out_ PULONG OldProtect 379 | ) 380 | { 381 | auto status = STATUS_SUCCESS; 382 | 383 | // 384 | // Only guest memory should be protected 385 | // 386 | if (!s_SemCallbacks.IsGuestMemory(*BaseAddress)) 387 | { 388 | status = STATUS_INVALID_PAGE_PROTECTION; 389 | goto HookExit; 390 | } 391 | 392 | // 393 | // Attempts to influence external host processes are blocked 394 | // 395 | if (ProcessHandle != GetCurrentProcess()) 396 | { 397 | status = STATUS_ACCESS_DENIED; 398 | goto HookExit; 399 | } 400 | 401 | // 402 | // Issue the call to the host kernel to protect the VA 403 | // 404 | status = NtProtectVirtualMemory(ProcessHandle, 405 | BaseAddress, 406 | RegionSize, 407 | NewProtect, 408 | OldProtect); 409 | goto HookExit; 410 | 411 | HookExit: 412 | return EncodeStatus(status); 413 | } 414 | 415 | auto 416 | HookNtQueryDebugFilterState ( 417 | _In_ ULONG ComponentId, 418 | _In_ ULONG Level 419 | ) 420 | { 421 | auto status = STATUS_SUCCESS; 422 | 423 | // 424 | // Get the host debug filter state 425 | // 426 | status = NtQueryDebugFilterState(ComponentId, Level); 427 | goto HookExit; 428 | 429 | HookExit: 430 | return EncodeStatus(status); 431 | } 432 | 433 | auto 434 | HookNtDuplicateObject ( 435 | _In_ HANDLE SourceProcessHandle, 436 | _In_ HANDLE SourceHandle, 437 | _In_opt_ HANDLE TargetProcessHandle, 438 | _Out_opt_ PHANDLE TargetHandle, 439 | _In_ ACCESS_MASK DesiredAccess, 440 | _In_ ULONG HandleAttributes, 441 | _In_ ULONG Options 442 | ) 443 | { 444 | auto status = STATUS_SUCCESS; 445 | 446 | // 447 | // Attempts to influence external host processes are blocked 448 | // 449 | if ((SourceProcessHandle != GetCurrentProcess()) || 450 | (TargetProcessHandle != GetCurrentProcess())) 451 | { 452 | status = STATUS_ACCESS_DENIED; 453 | goto HookExit; 454 | } 455 | 456 | // 457 | // Ask the kernel to duplicate the handle 458 | // 459 | status = NtDuplicateObject(SourceProcessHandle, 460 | SourceHandle, 461 | TargetProcessHandle, 462 | TargetHandle, 463 | DesiredAccess, 464 | HandleAttributes, 465 | Options); 466 | goto HookExit; 467 | 468 | HookExit: 469 | return EncodeStatus(status); 470 | } 471 | 472 | auto 473 | HookNtQueryVirtualMemory ( 474 | _In_ HANDLE ProcessHandle, 475 | _In_opt_ PVOID BaseAddress, 476 | _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, 477 | _Out_writes_bytes_(MemoryInformationLength) PVOID MemoryInformation, 478 | _In_ SIZE_T MemoryInformationLength, 479 | _Out_opt_ PSIZE_T ReturnLength 480 | ) 481 | { 482 | auto status = STATUS_SUCCESS; 483 | 484 | // 485 | // Only guest memory should be queried, unless this is a working set dump 486 | // 487 | if (!(s_SemCallbacks.IsGuestMemory(BaseAddress)) && 488 | (MemoryInformationClass != 4)) 489 | { 490 | status = STATUS_MEMORY_NOT_ALLOCATED; 491 | goto HookExit; 492 | } 493 | 494 | // 495 | // Attempts to influence external host processes are blocked 496 | // 497 | if (ProcessHandle != GetCurrentProcess()) 498 | { 499 | status = STATUS_ACCESS_DENIED; 500 | goto HookExit; 501 | } 502 | 503 | // 504 | // Call the host kernel to query the VAD 505 | // 506 | status = NtQueryVirtualMemory(ProcessHandle, 507 | BaseAddress, 508 | MemoryInformationClass, 509 | MemoryInformation, 510 | MemoryInformationLength, 511 | ReturnLength); 512 | goto HookExit; 513 | 514 | HookExit: 515 | return EncodeStatus(status); 516 | } 517 | 518 | auto 519 | HookNtQueryInformationProcess ( 520 | _In_ HANDLE ProcessHandle, 521 | _In_ PROCESSINFOCLASS ProcessInformationClass, 522 | _Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation, 523 | _In_ ULONG ProcessInformationLength, 524 | _Out_opt_ PULONG ReturnLength 525 | ) 526 | { 527 | auto status = STATUS_SUCCESS; 528 | 529 | // 530 | // Attempts to influence external host processes are blocked 531 | // 532 | if (ProcessHandle != GetCurrentProcess()) 533 | { 534 | status = STATUS_ACCESS_DENIED; 535 | goto HookExit; 536 | } 537 | 538 | // 539 | // Only handle these 2 basic classes 540 | // 541 | if ((ProcessInformationClass != ProcessBasicInformation) && 542 | (ProcessInformationClass != ProcessCookie)) 543 | { 544 | status = STATUS_INVALID_INFO_CLASS; 545 | goto HookExit; 546 | } 547 | 548 | // 549 | // Call the host kernel to get information on the process 550 | // 551 | status = NtQueryInformationProcess(ProcessHandle, 552 | ProcessInformationClass, 553 | ProcessInformation, 554 | ProcessInformationLength, 555 | ReturnLength); 556 | goto HookExit; 557 | 558 | HookExit: 559 | return EncodeStatus(status); 560 | } 561 | 562 | auto 563 | HookNtTerminateProcess ( 564 | _In_opt_ HANDLE ProcessHandle, 565 | _In_ NTSTATUS ExitStatus 566 | ) 567 | { 568 | auto status = STATUS_SUCCESS; 569 | 570 | // 571 | // Attempts to influence external host processes are blocked 572 | // 573 | if ((ProcessHandle) && (ProcessHandle != GetCurrentProcess())) 574 | { 575 | status = STATUS_ACCESS_DENIED; 576 | goto HookExit; 577 | } 578 | 579 | // 580 | // Kill the host process that represents the guest 581 | // 582 | status = NtTerminateProcess(ProcessHandle, ExitStatus); 583 | goto HookExit; 584 | 585 | HookExit: 586 | return EncodeStatus(status); 587 | } 588 | 589 | auto 590 | HookNtQuerySystemInformation ( 591 | _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, 592 | _Out_writes_bytes_to_opt_(SystemInformationLength, 593 | *ReturnLength) PVOID SystemInformation, 594 | _In_ ULONG SystemInformationLength, 595 | _Out_opt_ PULONG ReturnLength 596 | ) 597 | { 598 | auto status = STATUS_INVALID_INFO_CLASS; 599 | 600 | // 601 | // Special fast path for the loader check done on RS5+ 602 | // 603 | if (SystemInformationClass == SystemHypervisorSharedPageInformation) 604 | { 605 | return EncodeFailureOk(status); 606 | } 607 | 608 | // 609 | // Special fast path which helps the guest environment use the right limits 610 | // 611 | if (SystemInformationClass == SystemRangeStartInformation) 612 | { 613 | *reinterpret_cast(SystemInformation) = s_256GB; 614 | status = STATUS_SUCCESS; 615 | goto HookExit; 616 | } 617 | 618 | // 619 | // We only implement the information classes below 620 | // 621 | if ((SystemInformationClass != SystemBasicInformation) && 622 | (SystemInformationClass != SystemFlushInformation) && 623 | (SystemInformationClass != SystemEmulationBasicInformation) && 624 | (SystemInformationClass != SystemNumaProcessorMap) && 625 | (SystemInformationClass != SystemTimeOfDayInformation)) 626 | { 627 | status = STATUS_INVALID_INFO_CLASS; 628 | goto HookExit; 629 | } 630 | 631 | // 632 | // Call the host kernel to get the information 633 | // 634 | status = NtQuerySystemInformation(SystemInformationClass, 635 | SystemInformation, 636 | SystemInformationLength, 637 | ReturnLength); 638 | 639 | HookExit: 640 | return EncodeStatus(status); 641 | } 642 | 643 | auto 644 | HookNtSetInformationProcess ( 645 | _In_ HANDLE ProcessHandle, 646 | _In_ PROCESSINFOCLASS ProcessInformationClass, 647 | _In_reads_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation, 648 | _In_ ULONG ProcessInformationLength 649 | ) 650 | { 651 | auto status = STATUS_SUCCESS; 652 | 653 | // 654 | // Attempts to influence external host processes are blocked 655 | // 656 | if (ProcessHandle != GetCurrentProcess()) 657 | { 658 | status = STATUS_ACCESS_DENIED; 659 | goto HookExit; 660 | } 661 | 662 | // 663 | // Only the console host owner is allowed to be set 664 | // 665 | if (ProcessInformationClass != ProcessOwnerInformation) 666 | { 667 | status = STATUS_INVALID_INFO_CLASS; 668 | goto HookExit; 669 | } 670 | 671 | // 672 | // Call the host kernel to set the information 673 | // 674 | status = NtSetInformationProcess(ProcessHandle, 675 | ProcessInformationClass, 676 | ProcessInformation, 677 | ProcessInformationLength); 678 | goto HookExit; 679 | 680 | HookExit: 681 | return EncodeStatus(status); 682 | } 683 | 684 | auto 685 | HookNtQueryVolumeInformationFile ( 686 | _In_ HANDLE FileHandle, 687 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 688 | _Out_writes_bytes_(Length) PVOID FsInformation, 689 | _In_ ULONG Length, 690 | _In_ FS_INFORMATION_CLASS FsInformationClass 691 | ) 692 | { 693 | auto status = STATUS_SUCCESS; 694 | 695 | // 696 | // Only attribute information can be queried 697 | // 698 | if (FsInformationClass != FileFsAttributeInformation) 699 | { 700 | status = STATUS_INVALID_INFO_CLASS; 701 | goto HookExit; 702 | } 703 | 704 | // 705 | // Call the host kernel to query the information 706 | // @TODO: Add handle tracking 707 | // 708 | status = NtQueryVolumeInformationFile(FileHandle, 709 | IoStatusBlock, 710 | FsInformation, 711 | Length, 712 | FsInformationClass); 713 | 714 | goto HookExit; 715 | 716 | HookExit: 717 | return EncodeStatus(status); 718 | } 719 | 720 | auto 721 | HookNtQueryPerformanceCounter ( 722 | _Out_ PLARGE_INTEGER PerformanceCounter, 723 | _Out_opt_ PLARGE_INTEGER PerformanceFrequency 724 | ) 725 | { 726 | auto status = STATUS_SUCCESS; 727 | 728 | // 729 | // Get the host QPC frequency and value 730 | // 731 | status = NtQueryPerformanceCounter(PerformanceCounter, 732 | PerformanceFrequency); 733 | goto HookExit; 734 | 735 | HookExit: 736 | return EncodeStatus(status); 737 | } 738 | 739 | auto 740 | HookNtOpenFile ( 741 | _Out_ PHANDLE FileHandle, 742 | _In_ ACCESS_MASK DesiredAccess, 743 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 744 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 745 | _In_ ULONG ShareAccess, 746 | _In_ ULONG OpenOptions 747 | ) 748 | { 749 | auto status = STATUS_SUCCESS; 750 | 751 | // 752 | // Open the file requested by the guest 753 | // @TODO: Add handle tracking 754 | // 755 | status = NtOpenFile(FileHandle, 756 | DesiredAccess, 757 | ObjectAttributes, 758 | IoStatusBlock, 759 | ShareAccess, 760 | OpenOptions); 761 | goto HookExit; 762 | 763 | HookExit: 764 | return EncodeStatus(status); 765 | } 766 | 767 | auto 768 | HookNtCreateFile ( 769 | _Out_ PHANDLE FileHandle, 770 | _In_ ACCESS_MASK DesiredAccess, 771 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 772 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 773 | _In_opt_ PLARGE_INTEGER AllocationSize, 774 | _In_ ULONG FileAttributes, 775 | _In_ ULONG ShareAccess, 776 | _In_ ULONG CreateDisposition, 777 | _In_ ULONG CreateOptions, 778 | _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, 779 | _In_ ULONG EaLength 780 | ) 781 | { 782 | auto status = STATUS_SUCCESS; 783 | 784 | // 785 | // Create the file requested by the guest 786 | // @TODO: Add handle tracking 787 | // 788 | status = NtCreateFile(FileHandle, 789 | DesiredAccess, 790 | ObjectAttributes, 791 | IoStatusBlock, 792 | AllocationSize, 793 | FileAttributes, 794 | ShareAccess, 795 | CreateDisposition, 796 | CreateOptions, 797 | EaBuffer, 798 | EaLength); 799 | goto HookExit; 800 | 801 | HookExit: 802 | return EncodeStatus(status); 803 | } 804 | 805 | auto 806 | HookNtDeviceIoControlFile ( 807 | _In_ HANDLE FileHandle, 808 | _In_opt_ HANDLE Event, 809 | _In_opt_ PIO_APC_ROUTINE ApcRoutine, 810 | _In_opt_ PVOID ApcContext, 811 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 812 | _In_ ULONG IoControlCode, 813 | _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, 814 | _In_ ULONG InputBufferLength, 815 | _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, 816 | _In_ ULONG OutputBufferLength 817 | ) 818 | { 819 | auto status = STATUS_SUCCESS; 820 | 821 | // 822 | // Send the IOCTL to the device requested by the guest 823 | // @TODO: Add handle tracking 824 | // 825 | status = NtDeviceIoControlFile(FileHandle, 826 | Event, 827 | ApcRoutine, 828 | ApcContext, 829 | IoStatusBlock, 830 | IoControlCode, 831 | InputBuffer, 832 | InputBufferLength, 833 | OutputBuffer, 834 | OutputBufferLength); 835 | if (!NT_SUCCESS(status)) 836 | { 837 | // 838 | // If this is a CONDRV message that's failing with an unsupported code 839 | // then allow the failure, as this is expected even on a real host 840 | // 841 | if ((IoControlCode == 0x00500016) && (status == 0xC00700BB)) 842 | { 843 | return EncodeFailureOk(status); 844 | } 845 | } 846 | goto HookExit; 847 | 848 | HookExit: 849 | return EncodeStatus(status); 850 | } 851 | 852 | auto 853 | HookNtWriteFile ( 854 | _In_ HANDLE FileHandle, 855 | _In_opt_ HANDLE Event, 856 | _In_opt_ PIO_APC_ROUTINE ApcRoutine, 857 | _In_opt_ PVOID ApcContext, 858 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 859 | _In_reads_bytes_(Length) PVOID Buffer, 860 | _In_ ULONG Length, 861 | _In_opt_ PLARGE_INTEGER ByteOffset, 862 | _In_opt_ PULONG Key 863 | ) 864 | { 865 | auto status = STATUS_SUCCESS; 866 | 867 | // 868 | // Write into the file requested by the guest 869 | // @TODO: Add handle tracking 870 | // 871 | status = NtWriteFile(FileHandle, 872 | Event, 873 | ApcRoutine, 874 | ApcContext, 875 | IoStatusBlock, 876 | Buffer, 877 | Length, 878 | ByteOffset, 879 | Key); 880 | goto HookExit; 881 | 882 | HookExit: 883 | return EncodeStatus(status); 884 | } 885 | 886 | auto 887 | HookNtCreateEvent ( 888 | _Out_ PHANDLE EventHandle, 889 | _In_ ACCESS_MASK DesiredAccess, 890 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, 891 | _In_ EVENT_TYPE EventType, 892 | _In_ BOOLEAN InitialState 893 | ) 894 | { 895 | auto status = STATUS_SUCCESS; 896 | 897 | // 898 | // Create the event requested by the guest 899 | // @TODO: Add handle tracking 900 | // 901 | status = NtCreateEvent(EventHandle, 902 | DesiredAccess, 903 | ObjectAttributes, 904 | EventType, 905 | InitialState); 906 | goto HookExit; 907 | 908 | HookExit: 909 | return EncodeStatus(status); 910 | } 911 | 912 | auto 913 | HookNtSetEvent ( 914 | _In_ HANDLE EventHandle, 915 | _Out_opt_ PLONG PreviousState 916 | ) 917 | { 918 | auto status = STATUS_SUCCESS; 919 | 920 | // 921 | // Signal the event requested by the guest 922 | // @TODO: Add handle tracking 923 | // 924 | status = NtSetEvent(EventHandle, PreviousState); 925 | goto HookExit; 926 | 927 | HookExit: 928 | return EncodeStatus(status); 929 | } 930 | 931 | auto 932 | HookNtOpenDirectoryObject ( 933 | _Out_ PHANDLE DirectoryHandle, 934 | _In_ ACCESS_MASK DesiredAccess, 935 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 936 | ) 937 | { 938 | auto status = STATUS_SUCCESS; 939 | 940 | // 941 | // Open the object directory requested by the guest 942 | // @TODO: Add handle tracking 943 | // 944 | status = NtOpenDirectoryObject(DirectoryHandle, 945 | DesiredAccess, 946 | ObjectAttributes); 947 | goto HookExit; 948 | 949 | HookExit: 950 | return EncodeStatus(status); 951 | } 952 | 953 | auto 954 | HookNtOpenSymbolicLinkObject ( 955 | _Out_ PHANDLE LinkHandle, 956 | _In_ ACCESS_MASK DesiredAccess, 957 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 958 | ) 959 | { 960 | auto status = STATUS_SUCCESS; 961 | 962 | // 963 | // Open the symbolic link object requested by the guest 964 | // @TODO: Add handle tracking 965 | // 966 | status = NtOpenSymbolicLinkObject(LinkHandle, 967 | DesiredAccess, 968 | ObjectAttributes); 969 | goto HookExit; 970 | 971 | HookExit: 972 | return EncodeStatus(status); 973 | } 974 | 975 | auto 976 | HookNtQuerySymbolicLinkObject ( 977 | _In_ HANDLE LinkHandle, 978 | _Inout_ PUNICODE_STRING LinkTarget, 979 | _Out_opt_ PULONG ReturnedLength 980 | ) 981 | { 982 | auto status = STATUS_SUCCESS; 983 | 984 | // 985 | // Query the symbolic link object requested by the guest 986 | // @TODO: Add handle tracking 987 | // 988 | status = NtQuerySymbolicLinkObject(LinkHandle, LinkTarget, ReturnedLength); 989 | goto HookExit; 990 | 991 | HookExit: 992 | return EncodeStatus(status); 993 | } 994 | 995 | auto 996 | HookNtOpenSection ( 997 | _Out_ PHANDLE SectionHandle, 998 | _In_ ACCESS_MASK DesiredAccess, 999 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 1000 | ) 1001 | { 1002 | auto status = STATUS_SUCCESS; 1003 | 1004 | // 1005 | // Open the section object requested by the guest 1006 | // @TODO: Add handle tracking 1007 | // 1008 | status = NtOpenSection(SectionHandle, DesiredAccess, ObjectAttributes); 1009 | goto HookExit; 1010 | 1011 | HookExit: 1012 | return EncodeStatus(status); 1013 | } 1014 | 1015 | auto 1016 | HookNtClose ( 1017 | _In_ HANDLE Handle 1018 | ) 1019 | { 1020 | auto status = STATUS_SUCCESS; 1021 | 1022 | // 1023 | // Close the handle requested by the guest 1024 | // @TODO: Add handle tracking 1025 | // 1026 | status = NtClose(Handle); 1027 | goto HookExit; 1028 | 1029 | HookExit: 1030 | return EncodeStatus(status); 1031 | } 1032 | 1033 | auto 1034 | HookNtContinue ( 1035 | _In_ PCONTEXT ContextRecord, 1036 | _In_ BOOLEAN TestAlert 1037 | ) 1038 | { 1039 | UNREFERENCED_PARAMETER(TestAlert); 1040 | 1041 | // 1042 | // Switch the CPU back to user mode and the specified program counter/stack 1043 | // 1044 | s_SemCallbacks.SwitchMode(ContextRecord->Rcx, 1045 | ContextRecord->Rdx, 1046 | ContextRecord->EFlags, 1047 | ContextRecord->Rsp, 1048 | ContextRecord->Rip, 1049 | ContextRecord->SegCs, 1050 | ContextRecord->SegSs); 1051 | 1052 | // 1053 | // Use a specialized return code to avoid resuming to the original context 1054 | // 1055 | return EncodeNoResume(STATUS_SUCCESS); 1056 | } 1057 | 1058 | auto 1059 | HookNtRaiseException ( 1060 | _In_ PEXCEPTION_RECORD ExceptionRecord, 1061 | _In_ PCONTEXT ContextRecord, 1062 | _In_ BOOLEAN FirstChance 1063 | ) 1064 | { 1065 | PCHAR buffer; 1066 | INT bufferSize; 1067 | auto status = STATUS_SUCCESS; 1068 | UNREFERENCED_PARAMETER(FirstChance); 1069 | 1070 | // 1071 | // The only exception we handle is a DebugPrint exception 1072 | // 1073 | if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C) 1074 | { 1075 | status = STATUS_NOT_IMPLEMENTED; 1076 | goto HookExit; 1077 | } 1078 | 1079 | // 1080 | // Extract the buffer and its size, then print it out 1081 | // 1082 | buffer = reinterpret_cast(ExceptionRecord->ExceptionInformation[1]); 1083 | bufferSize = static_cast(ExceptionRecord->ExceptionInformation[0]) - 1; 1084 | s_SemCallbacks.DebugPrint(buffer, bufferSize); 1085 | 1086 | // 1087 | // Restore the CPU state to resume after the debug exception 1088 | // 1089 | s_SemCallbacks.RestoreException(ContextRecord->Rsp, 1090 | ContextRecord->Rbp, 1091 | ContextRecord->Rsi, 1092 | ContextRecord->Rdi, 1093 | ContextRecord->Rbx, 1094 | reinterpret_cast 1095 | (ExceptionRecord->ExceptionAddress)); 1096 | goto HookExit; 1097 | 1098 | HookExit: 1099 | return EncodeStatus(status); 1100 | } 1101 | 1102 | auto 1103 | HookDebugPrint ( 1104 | _In_ PCHAR Buffer, 1105 | _In_ INT Length 1106 | ) 1107 | { 1108 | auto status = STATUS_SUCCESS; 1109 | 1110 | // 1111 | // Print it out the string 1112 | // 1113 | s_SemCallbacks.DebugPrint(Buffer, Length); 1114 | goto HookExit; 1115 | 1116 | HookExit: 1117 | return EncodeStatus(status); 1118 | } 1119 | 1120 | typedef struct _SEM_SYSCALL_PROVIDER_ENTRY 1121 | { 1122 | USHORT CallId; 1123 | UCHAR Arguments; 1124 | PVOID Function; 1125 | PCHAR FunctionName; 1126 | } SEM_SYSCALL_PROVIDER_ENTRY, *PSEM_SYSCALL_PROVIDER_ENTRY; 1127 | static SEM_SYSCALL_PROVIDER_ENTRY s_Win10RS5_Index_Table[] = 1128 | { 1129 | // 1130 | // Security Cookie Setup 1131 | // 1132 | {0x31, 2, HookNtQueryPerformanceCounter, "NtQueryPerformanceCounter"}, 1133 | 1134 | // 1135 | // Initial querying of .MRDATA 1136 | // 1137 | {0x36, 4, HookNtQuerySystemInformation, "NtQuerySystemInformation"}, 1138 | {0x50, 5, HookNtProtectVirtualMemory, "NtProtectVirtualMemory"}, 1139 | {0x19, 5, HookNtQueryInformationProcess, "NtQueryInformationProcess"}, 1140 | {0x23, 6, HookNtQueryVirtualMemory, "NtQueryVirtualMemory"}, 1141 | 1142 | // 1143 | // Initial IFEO setup 1144 | // 1145 | {0x12, 3, nullptr, "NtOpenKey"}, 1146 | 1147 | // 1148 | // First DebugPrint 1149 | // 1150 | {0x13A, 2, HookNtQueryDebugFilterState, "NtQueryDebugFilterState"}, 1151 | 1152 | // 1153 | // Heap setup 1154 | // 1155 | {0x153, 6, nullptr, "NtQuerySecurityAttributesToken"}, 1156 | {0x74, 7, HookNtAllocateVirtualMemoryEx, "NtAllocateVirtualMemoryEx"}, 1157 | {0x18, 6, HookNtAllocateVirtualMemory, "NtAllocateVirtualMemory"}, 1158 | {0x1E, 4, HookNtFreeVirtualMemory, "NtFreeVirtualMemory"}, 1159 | 1160 | // 1161 | // Create loader event 1162 | // 1163 | {0x48, 5, HookNtCreateEvent, "NtCreateEvent"}, 1164 | 1165 | // 1166 | // Worker Pool 1167 | // 1168 | {0xC4, 3, nullptr, "NtCreateWaitCompletionPacket"}, 1169 | {0xF, 1, HookNtClose, "NtClose"}, 1170 | {0x15A, 6, nullptr, "NtQuerySystemInformationEx"}, 1171 | 1172 | // 1173 | // Known DLL Validation 1174 | // 1175 | {0x58, 3, HookNtOpenDirectoryObject, "NtOpenDirectoryObject"}, 1176 | {0x127, 3, HookNtOpenSymbolicLinkObject, "NtOpenSymbolicLinkObject"}, 1177 | {0x157, 3, HookNtQuerySymbolicLinkObject, "NtQuerySymbolicLinkObject"}, 1178 | 1179 | // 1180 | // Initial LdrpLogDbgPrint 1181 | // 1182 | {0x15F, 3, HookNtRaiseException, "NtRaiseException"}, 1183 | 1184 | // 1185 | // Setting up current path 1186 | // 1187 | {0x33, 6, HookNtOpenFile, "NtOpenFile"}, 1188 | {0x49, 5, HookNtQueryVolumeInformationFile, "NtQueryVolumeInformationFile"}, 1189 | 1190 | // 1191 | // Signal loader event 1192 | // 1193 | {0xE, 2, HookNtSetEvent, "NtSetEvent"}, 1194 | {0x25, 5, nullptr, "NtQueryInformationThread"}, 1195 | 1196 | // 1197 | // Beginning DLL load of kernel32/base 1198 | // 1199 | {0x37, 3, HookNtOpenSection, "NtOpenSection"}, 1200 | {0x28, 10, HookNtMapViewOfSection, "NtMapViewOfSection"}, 1201 | {0x4C, 2, nullptr, "NtApphelpCacheControl"}, 1202 | 1203 | // 1204 | // Kernelbase init 1205 | // 1206 | {0x55, 11, HookNtCreateFile, "NtCreateFile"}, 1207 | {0x7, 10, HookNtDeviceIoControlFile, "NtDeviceIoControlFile"}, 1208 | {0x3C, 7, HookNtDuplicateObject, "NtDuplicateObject"}, 1209 | {0x1C, 4, HookNtSetInformationProcess, "NtSetInformationProcess"}, 1210 | 1211 | // 1212 | // RS5 Hotpatch Support 1213 | // 1214 | {0x197, 6, nullptr, "NtSetInformationVirtualMemory"}, 1215 | 1216 | // 1217 | // Init finished 1218 | // 1219 | {0x1B9, 0, nullptr, "NtTestAlert"}, 1220 | {0x43, 1, HookNtContinue, "NtContinue"}, 1221 | 1222 | // 1223 | // Print to console 1224 | // 1225 | {0x8, 9, HookNtWriteFile, "NtWriteFile"}, 1226 | 1227 | // 1228 | // Exit 1229 | // 1230 | {0x2C, 2, HookNtTerminateProcess, "NtTerminateProcess"}, 1231 | }; 1232 | static SEM_SYSCALL_PROVIDER_ENTRY s_Win1019H1_Index_Table[] = 1233 | { 1234 | // 1235 | // Security Cookie Setup 1236 | // 1237 | {0x31, 2, HookNtQueryPerformanceCounter, "NtQueryPerformanceCounter"}, 1238 | 1239 | // 1240 | // Initial querying of .MRDATA 1241 | // 1242 | {0x36, 4, HookNtQuerySystemInformation, "NtQuerySystemInformation"}, 1243 | {0x50, 5, HookNtProtectVirtualMemory, "NtProtectVirtualMemory"}, 1244 | {0x19, 5, HookNtQueryInformationProcess, "NtQueryInformationProcess"}, 1245 | {0x23, 6, HookNtQueryVirtualMemory, "NtQueryVirtualMemory"}, 1246 | 1247 | // 1248 | // Initial IFEO setup 1249 | // 1250 | {0x12, 3, nullptr, "NtOpenKey"}, 1251 | 1252 | // 1253 | // First DebugPrint 1254 | // 1255 | {0x13B, 2, HookNtQueryDebugFilterState, "NtQueryDebugFilterState"}, 1256 | 1257 | // 1258 | // Heap setup 1259 | // 1260 | {0x154, 6, nullptr, "NtQuerySecurityAttributesToken"}, 1261 | {0x74, 7, HookNtAllocateVirtualMemoryEx, "NtAllocateVirtualMemoryEx"}, 1262 | {0x18, 6, HookNtAllocateVirtualMemory, "NtAllocateVirtualMemory"}, 1263 | {0x1E, 4, HookNtFreeVirtualMemory, "NtFreeVirtualMemory"}, 1264 | 1265 | // 1266 | // Create loader event 1267 | // 1268 | {0x48, 5, HookNtCreateEvent, "NtCreateEvent"}, 1269 | 1270 | // 1271 | // Worker Pool 1272 | // 1273 | {0xC5, 3, nullptr, "NtCreateWaitCompletionPacket"}, 1274 | {0xF, 1, HookNtClose, "NtClose"}, 1275 | {0x15B, 6, nullptr, "NtQuerySystemInformationEx"}, 1276 | 1277 | // 1278 | // Known DLL Validation 1279 | // 1280 | {0x58, 3, HookNtOpenDirectoryObject, "NtOpenDirectoryObject"}, 1281 | {0x128, 3, HookNtOpenSymbolicLinkObject, "NtOpenSymbolicLinkObject"}, 1282 | {0x158, 3, HookNtQuerySymbolicLinkObject, "NtQuerySymbolicLinkObject"}, 1283 | 1284 | // 1285 | // Initial LdrpLogDbgPrint 1286 | // 1287 | {0x160, 3, HookNtRaiseException, "NtRaiseException"}, 1288 | 1289 | // 1290 | // Setting up current path 1291 | // 1292 | {0x33, 6, HookNtOpenFile, "NtOpenFile"}, 1293 | {0x49, 5, HookNtQueryVolumeInformationFile, "NtQueryVolumeInformationFile"}, 1294 | 1295 | // 1296 | // Signal loader event 1297 | // 1298 | {0xE, 2, HookNtSetEvent, "NtSetEvent"}, 1299 | {0x25, 5, nullptr, "NtQueryInformationThread"}, 1300 | 1301 | // 1302 | // Beginning DLL load of kernel32/base 1303 | // 1304 | {0x37, 3, HookNtOpenSection, "NtOpenSection"}, 1305 | {0x28, 10, HookNtMapViewOfSection, "NtMapViewOfSection"}, 1306 | {0x4C, 2, nullptr, "NtApphelpCacheControl"}, 1307 | 1308 | // 1309 | // Kernelbase init 1310 | // 1311 | {0x55, 11, HookNtCreateFile, "NtCreateFile"}, 1312 | {0x7, 10, HookNtDeviceIoControlFile, "NtDeviceIoControlFile"}, 1313 | {0x3C, 7, HookNtDuplicateObject, "NtDuplicateObject"}, 1314 | {0x1C, 4, HookNtSetInformationProcess, "NtSetInformationProcess"}, 1315 | 1316 | // 1317 | // RS5 Hotpatch Support 1318 | // 1319 | {0x198, 6, nullptr, "NtSetInformationVirtualMemory"}, 1320 | 1321 | // 1322 | // Init finished 1323 | // 1324 | {0x1BA, 0, nullptr, "NtTestAlert"}, 1325 | {0x43, 1, HookNtContinue, "NtContinue"}, 1326 | 1327 | // 1328 | // Print to console 1329 | // 1330 | {0x8, 9, HookNtWriteFile, "NtWriteFile"}, 1331 | 1332 | // 1333 | // Exit 1334 | // 1335 | {0x2C, 2, HookNtTerminateProcess, "NtTerminateProcess"}, 1336 | 1337 | }; 1338 | 1339 | EXTERN_C 1340 | VOID 1341 | SemRegisterSystemCallProvider ( 1342 | _In_ CONST SEM_PROVIDER_CALLBACKS* SemCallbacks 1343 | ) 1344 | { 1345 | auto static s_BuildNumber = *reinterpret_cast(0x7FFE0260); 1346 | 1347 | // 1348 | // Capture the callbacks locally 1349 | // 1350 | s_SemCallbacks = *SemCallbacks; 1351 | 1352 | // 1353 | // Check if this is RS5 (1809) or 19H1 (1903) 1354 | // @TODO: Once 19H2 is out, this should be made better 1355 | // 1356 | for (auto &entry : (s_BuildNumber == 17763) ? 1357 | s_Win10RS5_Index_Table : s_Win1019H1_Index_Table) 1358 | { 1359 | // 1360 | // Register all of our system calls 1361 | // 1362 | s_SemCallbacks.RegisterSystemCall(entry.CallId, 1363 | entry.Arguments, 1364 | entry.Function, 1365 | entry.FunctionName); 1366 | } 1367 | 1368 | // 1369 | // Register the debug trap 1370 | // 1371 | s_SemCallbacks.RegisterDebugTrap(1, 2, HookDebugPrint); 1372 | } 1373 | -------------------------------------------------------------------------------- /provider/provider.def: -------------------------------------------------------------------------------- 1 | ;/*++ 2 | ; 3 | ;Copyright (c) Alex Ionescu. All rights reserved. 4 | ; 5 | ;Header Name: 6 | ; 7 | ; provider.def 8 | ; 9 | ;Abstract: 10 | ; 11 | ; This is the export definition file for the RS5/19H1 System Call Provider. 12 | ; 13 | ;Author: 14 | ; 15 | ; Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | ; 17 | ;Environment: 18 | ; 19 | ; Kernel mode only. 20 | ; 21 | ;--*/ 22 | 23 | LIBRARY PROVIDER.DLL 24 | 25 | EXPORTS 26 | 27 | SemRegisterSystemCallProvider 28 | -------------------------------------------------------------------------------- /provider/provider.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | provider.h 8 | 9 | Abstract: 10 | 11 | This is the main header for the RS5/19H1 System Call Provider 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | // 24 | // SDK Headers 25 | // 26 | #pragma once 27 | #pragma warning(disable:4201) 28 | #include 29 | #define WIN32_NO_STATUS 30 | #define _NO_CRT_STDIO_INLINE 31 | #include 32 | #include 33 | #include 34 | 35 | // 36 | // Internal NT emulation data structures (undocumented) 37 | // 38 | #include "ntemu.h" 39 | 40 | // 41 | // Internal header shared with all SEM components 42 | // 43 | #include "semdef.h" 44 | 45 | // 46 | // Internal header shared with the provider 47 | // 48 | typedef PVOID PSEM_PARTITION; 49 | #include "semprov.h" 50 | -------------------------------------------------------------------------------- /provider/provider.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 15.0 11 | {88DE519D-200D-44B2-A66C-58CAA1EAB946} 12 | Win32Proj 13 | 10.0.18290.0 14 | provider 15 | 16 | 17 | 18 | DynamicLibrary 19 | false 20 | v141 21 | true 22 | Unicode 23 | 24 | 25 | 26 | 27 | ..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) 28 | 29 | 30 | 31 | Level4 32 | Full 33 | true 34 | true 35 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 36 | AnySuitable 37 | Speed 38 | true 39 | 40 | 41 | Console 42 | true 43 | true 44 | provider.def 45 | ntdllp.lib;onecoreuap.lib;BufferOverflowU.lib;%(AdditionalDependencies) 46 | true 47 | true 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /registers.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionescu007/Simpleator/bbed7c2bc97fc5595fd6ff9adc78d8f556fa67f7/registers.PNG -------------------------------------------------------------------------------- /simpleator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emulator", "emulator\emulator.vcxproj", "{7382AEC3-1D55-42E2-BD8B-621DCCF3FD91}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {88DE519D-200D-44B2-A66C-58CAA1EAB946} = {88DE519D-200D-44B2-A66C-58CAA1EAB946} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simplelator", "monitor\monitor.vcxproj", "{11E5E98F-C3BF-4665-A294-6ADA7FE41B8E}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "provider", "provider\provider.vcxproj", "{88DE519D-200D-44B2-A66C-58CAA1EAB946}" 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testapp", "testapp\testapp.vcxproj", "{529833A4-39E6-46A8-87B5-0F3CAE94BEAC}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {7382AEC3-1D55-42E2-BD8B-621DCCF3FD91}.Release|x64.ActiveCfg = Release|x64 23 | {7382AEC3-1D55-42E2-BD8B-621DCCF3FD91}.Release|x64.Build.0 = Release|x64 24 | {11E5E98F-C3BF-4665-A294-6ADA7FE41B8E}.Release|x64.ActiveCfg = Release|x64 25 | {11E5E98F-C3BF-4665-A294-6ADA7FE41B8E}.Release|x64.Build.0 = Release|x64 26 | {88DE519D-200D-44B2-A66C-58CAA1EAB946}.Release|x64.ActiveCfg = Release|x64 27 | {88DE519D-200D-44B2-A66C-58CAA1EAB946}.Release|x64.Build.0 = Release|x64 28 | {529833A4-39E6-46A8-87B5-0F3CAE94BEAC}.Release|x64.ActiveCfg = Release|x64 29 | {529833A4-39E6-46A8-87B5-0F3CAE94BEAC}.Release|x64.Build.0 = Release|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {39BC7C15-ACA8-40D3-8126-8CA5FC8F1508} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /testapp/testapp.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Header Name: 6 | 7 | testapp.cpp 8 | 9 | Abstract: 10 | 11 | This module implements a simple test command line console application 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 12-Dec-2018 - Initial version 16 | 17 | Environment: 18 | 19 | Kernel mode only. 20 | 21 | --*/ 22 | 23 | #include 24 | #include 25 | 26 | auto 27 | wmain ( 28 | _In_ INT ArgumentCount, 29 | _In_ PWCHAR Arguments[] 30 | ) -> INT 31 | { 32 | // 33 | // Just print hello and exit 34 | // 35 | wprintf(L"Hello World: %d %s\n", ArgumentCount, Arguments[0]); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /testapp/testapp.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 15.0 11 | {529833A4-39E6-46A8-87B5-0F3CAE94BEAC} 12 | Win32Proj 13 | 10.0.18290.0 14 | testapp 15 | 16 | 17 | 18 | Application 19 | false 20 | v141 21 | true 22 | Unicode 23 | 24 | 25 | 26 | 27 | 28 | Level4 29 | Full 30 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 31 | ProgramDatabase 32 | AnySuitable 33 | true 34 | Speed 35 | true 36 | true 37 | Default 38 | MultiThreaded 39 | stdcpp17 40 | Caret 41 | 42 | 43 | Console 44 | onecoreuap.lib;%(AdditionalDependencies) 45 | true 46 | true 47 | true 48 | 49 | 50 | 51 | 52 | 53 | 54 | --------------------------------------------------------------------------------