├── .editorconfig
├── .gitignore
├── .gitmodules
├── LICENSE.txt
├── README.md
├── img
├── KiUserApcDispatcher.png
├── KiUserCallForwarder.png
├── injection.png
└── injldr.png
├── include
└── ntdll.h
├── inj.sln
└── src
├── injdll
├── injdll.vcxproj
├── injdll.vcxproj.filters
├── main.cpp
├── wow64log.cpp
└── wow64log.h
├── injdrv
├── injdrv.inf
├── injdrv.vcxproj
├── injdrv.vcxproj.filters
└── main.c
├── injldr
├── injldr.vcxproj
├── injldr.vcxproj.filters
├── install.c
├── install.h
└── main.c
└── injlib
├── injlib.c
├── injlib.h
├── injlib.vcxproj
├── injlib.vcxproj.filters
└── reparse.c
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.{c,h,cpp,hpp,asm}]
4 | charset = utf-8
5 | trim_trailing_whitespace = true
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 2
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #
2 | # Compiled binaries.
3 | #
4 | bin/**
5 |
6 | #
7 | # Visual Studio files.
8 | #
9 | .vs/**
10 | *.VC.db
11 | *.VC.opendb
12 | *.vcxproj.user
13 |
14 | #
15 | # VS Code files.
16 | #
17 | .vscode/**
18 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "src/DetoursNT"]
2 | path = src/DetoursNT
3 | url = git@github.com:wbenny/DetoursNT.git
4 | [submodule "include/phnt"]
5 | path = include/phnt
6 | url = git@github.com:processhacker/phnt.git
7 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Petr Benes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # injdrv
2 |
3 | injdrv is a proof-of-concept Windows Driver for injecting DLL into user-mode processes using APC.
4 |
5 | ### Motivation
6 |
7 | Even though [APCs][apc] are [undocumented to decent extent][inside-apc], the technique of using them to inject a DLL
8 | into a user-mode process is not new and has been talked through many times. Such APC can be queued from regular
9 | user-mode process (seen in [Cuckoo][apc-cuckoo]) as well as from kernel-mode driver (seen in [Blackbone][apc-blackbone]).
10 |
11 | Despite its popularity, finding small, easy-to-understand and actually working projects demonstrating usage of this
12 | technique isn't very easy. This project tries to fill this gap.
13 |
14 | ### Features
15 |
16 | - Support for **Windows 7** up to **Windows 10**
17 | - Support for **x86**, **x64**, **ARM32** & **ARM64** architectures
18 | - Ability to inject **Wow64** processes
19 | - With DLL of the same architecture as the **injected process** (e.g. **x86 DLL into x86 Wow64 process**)
20 | - With DLL of the same architecture as the **OS** (e.g. **x64 DLL into Wow64 process on Windows x64**)
21 | - DLL is injected in very early process initialization stage
22 | - Injection is performed from the `PsSetLoadImageNotifyRoutine` callback
23 | - Native processes (e.g. x86 on Windows x86, x64 on Windows x64, ...) are injected on next load of DLL after `ntdll.dll`
24 | - Wow64 processes are injected on next load of DLL after the Wow64-DLLs are loaded
25 | - Because of that, injected DLL must depend only on `ntdll.dll`
26 | - Demonstrative DLL performs hooking of few `ntdll.dll` functions
27 | - Achieved using [DetoursNT][DetoursNT]
28 | - Detoured functions use `ETW` to trace hooked function calls
29 |
30 | ### Compilation
31 |
32 | Because [DetoursNT][DetoursNT] project is attached as a git submodule, which itself carries the [Detours][Detours]
33 | git submodule, you must not forget to fetch them:
34 |
35 | `git clone --recurse-submodules git@github.com:wbenny/injdrv.git`
36 |
37 | After that, compile this project using Visual Studio 2017. Solution file is included. The only required dependency
38 | is [WDK][wdk].
39 |
40 | ### Implementation
41 |
42 | When the driver is loaded, it'll register two callbacks:
43 | - For process create/exit notification ([`PsSetCreateProcessNotifyRoutineEx`][MSDN-CreateProcessNotify])
44 | - For image load notification ([`PsSetLoadImageNotifyRoutine`][MSDN-LoadImageNotify])
45 |
46 | When a new process is created, the driver allocates small structure, which will hold information relevant to the process
47 | injection, such as:
48 | - Which DLLs are already loaded in the process
49 | - Addresses of important functions (such as `LdrLoadDll` in `ntdll.dll`)
50 |
51 | Start of a new Windows process is followed by mapping `ntdll.dll` into its address space and then ongoing load of DLLs
52 | from the process's import table. In case of **Wow64** processes on Windows x64, the following libraries are loaded
53 | immediately after native `ntdll.dll`: `wow64.dll`, `wow64cpu.dll`, `wow64win.dll` and second (Wow64) `ntdll.dll`.
54 | The driver is notified about load of these DLLs and marks down this information.
55 |
56 | When these DLLs are loaded, it is safe for the driver to queue the user-mode APC to the process, which will load our
57 | DLL into the process.
58 |
59 | ### Challenges
60 |
61 | Although such project might seem trivial to implement, there are some obstacles you might be facing along the way.
62 | Here I will try to summarize some of them:
63 |
64 | #### "Thunk"-method
65 |
66 | _This method injects DLL of the same architecture as the process. This method is available on all architectures._
67 |
68 | Injection of DLL requires a small allocation inside of the user-mode address space. This allocation
69 | holds path to the DLL to be injected and a small thunk (shellcode), which basically calls `LdrLoadDll` with the DLL path as
70 | a parameter. It is obvious that this memory requires `PAGE_EXECUTE_READ` protection, but the driver has to fill this
71 | memory somehow - and `PAGE_EXECUTE_READWRITE` is unacceptable security concern.
72 |
73 | It might be tempting to use `ZwAllocateVirtualMemory` and `ZwProtectVirtualMemory` but unfortunatelly, the second
74 | function is exported only since Windows 8.1.
75 |
76 | The solution used in this driver is to create section ([`ZwCreateSection`][MSDN-CreateSection]), map it
77 | ([`ZwMapViewOfSection`][MSDN-MapViewOfSection]) with `PAGE_READWRITE` protection, write the data, unmap it
78 | ([`ZwUnmapViewOfSection`][MSDN-UnmapViewOfSection]) and then map it again with `PAGE_EXECUTE_READ` protection.
79 |
80 | With usage of sections another problem arises. Since this driver performs injection from the image load notification
81 | callback - which is often called from the `NtMapViewOfSection` function - we'd be calling `MapViewOfSection`
82 | recursively. This wouldn't be a problem, if mapping of the section wouldn't lock the `EPROCESS->AddressCreationLock`.
83 | Because of that, we would end up in deadlock.
84 |
85 | The solution used in this driver is to inject **kernel-mode APC** first, from which the `ZwMapViewOfSection` is called.
86 | This kernel-mode APC is triggered right before the kernel-to-user-mode transition, so the internal `NtMapViewOfSection`
87 | call won't be on the callstack anymore (and therefore, `AddressCreationLock` will be unlocked).
88 |
89 | Injection of our DLL is triggered on first load of DLL which happens after all important system DLLs (mentioned above)
90 | are already loaded.
91 |
92 | In case of native processes, the codeflow is following:
93 | - `process.exe` is created (process create notification)
94 | - `process.exe` is loaded (image load notification)
95 | - `ntdll.dll` is loaded (image load notification)
96 | - `kernel32.dll` is loaded (image load notification + **injection happens here**)
97 |
98 | In case of Wow64 processes, the codeflow is following:
99 | - `process.exe` is created (process create notification)
100 | - `process.exe` is loaded (image load notification)
101 | - `ntdll.dll` is loaded (image load notification)
102 | - `wow64.dll` is loaded (image load notification)
103 | - `wow64cpu.dll` is loaded (image load notification)
104 | - `wow64win.dll` is loaded (image load notification)
105 | - `ntdll.dll` is loaded (image load notification - note, this is 32-bit ntdll.dll)
106 | - `kernel32.dll` is loaded (image load notification + **injection happens here**)
107 |
108 |
109 |
110 | > **NOTE:** Load of the `kernel32.dll` was used as an example. In fact, load of any DLL will trigger the injection.
111 | > But in practice, `kernel32.dll` is loaded into **every Windows process**, even if:
112 | > - it has no import table
113 | > - it doesn't depend on `kernel32.dll`
114 | > - it does depend only on `ntdll.dll` (covered in previous point, I just wanted to make that crystal-clear)
115 | > - it is a console application
116 | >
117 | > Also note that the order of loaded DLLs mentioned above might not reflect the exact order the OS is performing.
118 | >
119 | > The only processes that won't be injected by this method are:
120 | > - native processes (such as `csrss.exe`)
121 | > - pico processes (such as applications running inside [Windows Subsystem for Linux][WSL])
122 | >
123 | > Injection of these processes is not in the scope of this project.
124 | >
125 | > **NOTE:** On Windows 7, the Wow64 loads `kernel32.dll` and `user32.dll` (both native and Wow64) into the process.
126 | > Unfortunatelly, this load is performed in the initialization of Wow64 (by `wow64!ProcessInit`), therefore on Windows 7
127 | > we have to wait until these DLLs are loaded as well before injecting a Wow64 process.
128 |
129 | The injected user-mode APC is then force-delivered by calling `KeTestAlertThread(UserMode)`. This call internally
130 | checks if any user-mode APCs are queued and if so, sets the `Thread->ApcState.UserApcPending` variable to `TRUE`.
131 | Because of this, the kernel immediately delivers this user-mode APC (by `KiDeliverApc`) on next transition from
132 | kernel-mode to user-mode.
133 |
134 | > If we happened to not force the delivery of the APC, the APC would've been delivered when the thread would be in
135 | > the alertable state. (There are two alertable states per each thread, one for kernel-mode, one for user-mode;
136 | > this paragraph is talking about `Thread->Alerted[UserMode] == TRUE`.) Luckily, this happens when the Windows loader
137 | > in the `ntdll.dll` finishes its job and gives control to the application - particularly by calling `NtAlertThread`
138 | > in the `LdrpInitialize` (or `_LdrpInitialize`) function. So even if we happened to not force the APC, our DLL would
139 | > still be injected before the main execution would take place.
140 | >
141 | > **NOTE:** This means that if we wouldn't force delivery of the APC on our own, the APC would be delivered **BEFORE**
142 | > the `main`/`WinMain` is executed, but **AFTER** all [TLS callbacks][TLS-callbacks] are executed. This is because
143 | > TLS callbacks are executed also in the early process initialization stage, within the `LdrpInitialize` function.
144 | >
145 | > This behavior is configurable in this project by the `ForceUserApc` variable (by default it's `TRUE`).
146 | >
147 | > **NOTE:** Some badly written drivers try to inject DLL into processes by queuing APC at wrong time. For example:
148 | > - Queuing an APC for injecting DLL that **doesn't depend only on ntdll.dll** right when **ntdll.dll** is mapped
149 | > - Queuing an APC for injecting DLL that **depends on kernel32.dll** right when **kernel32.dll** is mapped (but not loaded!)
150 | >
151 | > Such injection will actually work as long as someone won't try to forcefully deliver user-mode APCs. Because this
152 | > driver triggers immediate deliver of user-mode APCs (**all of them**, you can't pick which should be delivered),
153 | > it might happen that APC of other driver will be triggered. If such APC consisted, let's say, of calling
154 | > `LoadLibraryA` from `kernel32.dll` and the `kernel32.dll` won't be fully loaded (just mapped), such APC would fail.
155 | > And because this injection happens in early process initialization stage, this error would be considered critical
156 | > and the process start would fail. Also because basically every process is being injected, if start of every process
157 | > would fail, it would make the system very unusable.
158 |
159 | The reason why our DLL is not injected immediately from the `ntdll.dll` image load callback is simple: the image
160 | load callback is called when the DLL is mapped into the process - and at this stage, the DLL is not fully initialized.
161 | The initialization takes place **after** this callback (in user-mode, obviously). If we would happen to inject
162 | `LdrLoadDll` call before `ntdll.dll` is initialized, the call would fail somewhere in that function, because some
163 | variable it relies on would not be initialized.
164 |
165 | Injection of **Wow64** processes is handled via `PsWrapApcWow64Thread(&NormalContext, &NormalRoutine)` call. This
166 | function essentially alters provided arguments in a way (not covered here) that `KiUserApcDispatcher` in native
167 | `ntdll.dll` is able to recognize and handle such APCs differently. Handling of such APCs is internally resolved
168 | by calling `Wow64ApcRoutine` (from `wow64.dll`). This function then emulates queuing of "32-bit APC" and resumes
169 | its execution in `KiUserApcDispatcher` in the Wow64 `ntdll.dll`.
170 |
171 | #### "Thunkless"-method
172 |
173 | _This method injects x64 DLL into both x64 (native) and x86 (Wow64) processes. This method is available only on Windows x64._
174 |
175 | Injection of **x64 DLL** into **Wow64** processes is tricky on its own, and **SentinelOne** wrote an excellent 3-part
176 | blogpost series on how to achieve that:
177 | - https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-1
178 | - https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-2
179 | - https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-3
180 |
181 | In short, if you try to use the same approach as with "thunk"-method for injecting x64 DLL into Wow64 process,
182 | you will run into problems with [Control Flow Guard][MSDN-CFG] on Windows 10.
183 | - On x64 system, CFG maintains 2 bitmaps for Wow64 processes
184 | - One for "x86 address space" (used when checking execution of < 4GB memory)
185 | - One for "x64 address space" (used when checking execution of >= 4 GB memory)
186 | - You cannot allocate memory in > 4GB range (even from the kernel-mode), because of [VAD][VAD] that reserves this
187 | memory range
188 | - You can theoretically unlink such VAD from `EPROCESS->VadRoot` and decrement `EPROCESS->VadCount`, but that's
189 | highly unrecommended
190 | - That means, when you allocate memory inside of Wow64 process (even from the kernel-mode) or change its protection,
191 | the _x86 CFG bitmap_ is used.
192 | - x64 `ntdll.dll` is mapped **above** 4GB, therefore, the `KiUserApcDispatcher` function is also located in > 4GB
193 | address.
194 | - Before `KiUserApcDispatcher` calls (indirectly) the `NormalRoutine` provided to the `KeInitializeApc` function,
195 | it checks whether `NormalRoutine` can be executed via CFG
196 | - Because `KiUserApcDispatcher` is called from > 4GB address, this CFG check is performed on _x64 CFG bitmap_, but
197 | this check will fail, because the allocated memory of ours is in < 4GB memory
198 | - You can theoreticaly work around this by disabling the CFG with various hacks, but that's also highly
199 | unrecommended
200 | - `ZwProtectVirtualMemory` and even `ZwSetInformationVirtualMemory` won't help you, because these APIs will operate
201 | on _x86 CFG bitmap_ as well, if you feed them with < 4GB address
202 |
203 | The solution outlined in the **SentinelOne** blogpost rests in calling `LdrLoadDll` of x64 `ntdll.dll` directly
204 | from the user APC dispatcher - effectively, making `NormalRoutine` point to the address of the `LdrLoadDll`.
205 | The issue here is that `PKNORMAL_ROUTINE` takes only 3 parameters, while `LdrLoadDll` takes 4.
206 |
207 | ```c
208 | typedef
209 | VOID
210 | (NTAPI *PKNORMAL_ROUTINE) (
211 | _In_ PVOID NormalContext,
212 | _In_ PVOID SystemArgument1,
213 | _In_ PVOID SystemArgument2
214 | );
215 |
216 | NTSTATUS
217 | NTAPI
218 | LdrLoadDll (
219 | _In_opt_ PWSTR SearchPath,
220 | _In_opt_ PULONG DllCharacteristics,
221 | _In_ PUNICODE_STRING DllName,
222 | _Out_ PVOID *BaseAddress
223 | );
224 | ```
225 |
226 | Note that 4th parameter of the `LdrLoadDll` **must** point to some valid address, where the `BaseAddress` will
227 | be stored. The devil is always in the details - the solution takes advance of "couple of lucky coincidences":
228 | - `KiUserApcDispatcher` is a function expecting `RSP` to point to the `CONTEXT` structure
229 | - From this structure, values `P1Home` ... `P4Home` are fetched:
230 | - `P1Home` (moved to `RCX`) represent `NormalContext`
231 | - `P2Home` (moved to `RDX`) represent `SystemArgument1`
232 | - `P3Home` (moved to `R8`) represent `SystemArgument2`
233 | - `P4Home` (moved to `RAX`) represent `NormalRoutine`
234 | - Also, `R9` is set to point to the `RSP` (the `CONTEXT` structure)
235 | - Note that `RCX`, `RDX`, `R8` and `R9` are used as first four function parameters in [Microsoft x64 calling convention][x64-abi]
236 |
237 |
238 |
239 |
240 |
241 | - `KiUserApcDispatcher` calls `KiUserCallForwarder`
242 | - `KiUserCallForwarder` checks whether `RAX` points to valid execution target (in _x64 CFG bitmap_)
243 | - `KiUserCallForwarder` calls function pointed by `RAX` with parameters `RCX`, `RDX`, `R8` and `R9`
244 | - This is basically equivalent of calling APC's `PKNORMAL_ROUTINE`
245 | - `NormalRoutine(NormalContext, SystemArgument1, SystemArgument2)`
246 | - ...except that, because `R9` is set, it is in fact called like this:
247 | - `NormalRoutine(NormalContext, SystemArgument1, SystemArgument2, ContinueContext)`
248 |
249 |
250 |
251 |
252 |
253 | - Therefore, if we queue the user-mode APC like this:
254 | - `NormalRoutine` = address of `LdrLoadDll` in 64-bit `ntdll.dll`
255 | - `NormalContext` = `NULL` (translates to 1st param. of `LdrLoadDll` (`SearchPath`))
256 | - `SystemArgument1` = `NULL` (translates to 2nd param. of `LdrLoadDll` (`DllCharacteristics`))
257 | - `SystemArgument2` = pointer to `UNICODE_STRING DllName` (translates to 3rd param. of `LdrLoadDll` (`DllName`))
258 | - (as mentioned above, the 4th parameter (`BaseAddress`) will be provided automatically by the `KiUserApcDispatcher`)
259 | - ...it will effectively result in the following call: `LdrLoadDll(NULL, 0, &DllName, &ContinueContext)`
260 | - `LdrLoadDll` overwrites first 8 bytes of the `CONTEXT` structure, which happens to be its `P1Home` field
261 | - It doesn't break anything, because this field has been already used (when fetching `NormalContext`) and is
262 | no longer accessed (not even by `ZwContinue`)
263 |
264 | > **NOTE:** Not all function calls from x86 NTDLL end up in x64 NTDLL. This is because some functions are fully
265 | > implemented on its own in both x86 and x64 NTDLL. This applies mainly on functions that does not require any
266 | > syscall - i.e. `Rtl*` functions. For example, if you wanted to hook `RtlDecompressBuffer` in Wow64 process,
267 | > hooking that function in x64 NTDLL wouldn't have any effect and such hooked function would be never called.
268 | >
269 | > **NOTE:** Because of differences in APC-dispatching mechanism, this method is not possible to use on **x86** or
270 | > **ARM64** Windows.
271 |
272 | #### "wow64log.dll reparse"-method
273 |
274 | _This method injects native DLL into all processes. This method is available on all architectures._
275 |
276 | When **Wow64** process is starting, the `wow64.dll` tries to load `wow64log.dll`. This DLL is never present in
277 | regular Windows installation (it's probably used internally by Microsoft for debugging of the Wow64 subsystem).
278 | Therefore, load of this DLL will normally fail. This isn't problem, though, because no critical functionality of
279 | the Wow64 subsystem depends on it. If the load actually succeeds, it tries to find following exported functions
280 | in the DLL:
281 | - `Wow64LogInitialize`
282 | - `Wow64LogMessageArgList`
283 | - `Wow64LogSystemService`
284 | - `Wow64LogTerminate`
285 |
286 | If one of these functions is not exported by the DLL, the DLL is immediately unloaded.
287 |
288 | If we drop custom `wow64log.dll` (which exports functions mentioned above) into the `%SystemRoot%\System32` directory,
289 | it gets loaded into every **Wow64 process**.
290 |
291 | For more details, this method is greatly described by [Walied Assar][wow64log]
292 |
293 | The actual injection of **Wow64** processes by **injdrv** is handled via redirection of `wow64log.dll` path to the
294 | path of our native DLL. This redirection is solved via filter driver, which registers `IRP_MJ_CREATE` pre-callback.
295 | When this pre-callback detects that the `wow64log.dll` file is being opened, it replaces the path in the `FILE_OBJECT`
296 | by using [`IoReplaceFileObjectName`][MSDN-IoReplaceFileObjectName] function and returning `STATUS_REPARSE` in the
297 | `IO_STATUS_BLOCK`. The code of the filter driver is entirely based on [SimRep][SimRep] example found in Microsoft's
298 | WDK examples.
299 |
300 | > **NOTE:** Because native processes do not load `wow64.dll`, **injdrv** injects them using **"thunk"-method** when
301 | > **"wow64log.dll reparse"-method** is selected.
302 | >
303 | > **NOTE:** Because `wow64.dll` itself is compiled for native architecture, the `wow64log.dll` must be also native.
304 |
305 | #### Protected processes
306 |
307 | Injection of protected processes is simply skipped, as it triggers code-integrity errors. Such processes are detected
308 | by the `PsIsProtectedProcess` function. If you're curious about workaround of this issue (by temporarily unprotecting
309 | these processes), you can peek into [Blackbone source code][blackbone-unprotect-process]. Keep in mind that
310 | unprotecting protected processes requires manipulation with undocumented structures, which change dramatically
311 | between Windows versions.
312 |
313 | #### ETW logging
314 |
315 | Finally, as mentioned in the beginning, the injected DLL performs logging of hooked functions with ETW.
316 | Because functions such as `EventRegister`, `EventWriteString`, ... are located in the `advapi32.dll`, we can't
317 | use them from our NTDLL-only dependent DLL. Luckily, ETW support is hardwired in the `ntdll.dll` too. In fact,
318 | most of the `Event*` functions in the `advapi32.dll` are simply redirected to the `EtwEvent*` functions in `ntdll.dll`
319 | without any change to the arguments! Therefore, we can simply mock the `Event*` functions and just include the
320 | `` header:
321 |
322 | ```c
323 | //
324 | // Include support for ETW logging.
325 | // Note that following functions are mocked, because they're
326 | // located in advapi32.dll. Fortunatelly, advapi32.dll simply
327 | // redirects calls to these functions to the ntdll.dll.
328 | //
329 |
330 | #define EventActivityIdControl EtwEventActivityIdControl
331 | #define EventEnabled EtwEventEnabled
332 | #define EventProviderEnabled EtwEventProviderEnabled
333 | #define EventRegister EtwEventRegister
334 | #define EventSetInformation EtwEventSetInformation
335 | #define EventUnregister EtwEventUnregister
336 | #define EventWrite EtwEventWrite
337 | #define EventWriteEndScenario EtwEventWriteEndScenario
338 | #define EventWriteEx EtwEventWriteEx
339 | #define EventWriteStartScenario EtwEventWriteStartScenario
340 | #define EventWriteString EtwEventWriteString
341 | #define EventWriteTransfer EtwEventWriteTransfer
342 |
343 | #include
344 | ```
345 |
346 | ...easy, wasn't it?
347 |
348 | ### Usage
349 |
350 | > Following example is performed on **Windows 10 x64**
351 |
352 | Enable [Test-Signing][test-signing] boot configuration option (note that you'll need administrative privileges to use
353 | `bcdedit`) and reboot the machine:
354 |
355 | ```
356 | bcdedit /set testsigning on
357 | shutdown /r /t 0
358 | ```
359 |
360 | Now open administrator command line and run following command:
361 |
362 | `injldr -i`
363 |
364 | The `-i` option installs the driver. After the driver is installed, it waits for newly created processes.
365 | When a new process is created, it is hooked. Prepare some **x86** application, for example, [PuTTY][putty] and run it.
366 | With **Process Explorer** we can check that indeed, our x64 DLL is injected in this x86 application.
367 |
368 |
369 |
370 |
371 |
372 | Also, immediately after `injldr` is started, it starts an ETW tracing session and prints out information
373 | about called hooked functions:
374 |
375 |
376 |
377 |
378 |
379 | You can exit `injldr` by pressing `Ctrl+C`. Now you can run `injldr` without any parameters to just start
380 | the tracing session. If you wish to uninstall the driver, run `injldr -u`.
381 |
382 | > This driver by default uses following injection methods:
383 | > - `InjMethodThunk` on Windows x86
384 | > - `InjMethodThunkless` on Windows x64
385 | > - `InjMethodWow64LogReparse` on Windows ARM64
386 | >
387 | > Therefore, it always tries to inject native DLL into all processes, including Wow64 processes. If you wish to
388 | > change this behavior and e.g. inject x86 DLL into x86 Wow64 process, set injection method to `InjMethodThunk`.
389 | > Also, do not forget to compile `injdll` for the corresponding architectures and place it in the same directory
390 | > as `injldr.exe`.
391 |
392 | ### License
393 |
394 | This software is open-source under the MIT license. See the LICENSE.txt file in this repository.
395 |
396 | Dependencies are licensed by their own licenses.
397 |
398 | If you find this project interesting, you can buy me a coffee
399 |
400 | ```
401 | BTC 3GwZMNGvLCZMi7mjL8K6iyj6qGbhkVMNMF
402 | LTC MQn5YC7bZd4KSsaj8snSg4TetmdKDkeCYk
403 | ```
404 |
405 | [apc]:
406 | [inside-apc]:
407 | [apc-cuckoo]:
408 | [apc-blackbone]:
409 | [Detours]:
410 | [DetoursNT]:
411 | [MSDN-CreateProcessNotify]:
412 | [MSDN-LoadImageNotify]:
413 | [MSDN-CreateSection]:
414 | [MSDN-MapViewOfSection]:
415 | [MSDN-UnmapViewOfSection]:
416 | [MSDN-CFG]:
417 | [VAD]:
418 | [x64-abi]:
419 | [blackbone-unprotect-process]:
420 | [WSL]:
421 | [TLS-callbacks]:
422 | [test-signing]:
423 | [PuTTY]:
424 | [wdk]:
425 | [MSDN-IoReplaceFileObjectName]:
426 | [SimRep]:
427 | [wow64log]:
428 |
--------------------------------------------------------------------------------
/img/KiUserApcDispatcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wbenny/injdrv/a8dadf43a09f33c33a3957a250cb7ee05a986608/img/KiUserApcDispatcher.png
--------------------------------------------------------------------------------
/img/KiUserCallForwarder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wbenny/injdrv/a8dadf43a09f33c33a3957a250cb7ee05a986608/img/KiUserCallForwarder.png
--------------------------------------------------------------------------------
/img/injection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wbenny/injdrv/a8dadf43a09f33c33a3957a250cb7ee05a986608/img/injection.png
--------------------------------------------------------------------------------
/img/injldr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wbenny/injdrv/a8dadf43a09f33c33a3957a250cb7ee05a986608/img/injldr.png
--------------------------------------------------------------------------------
/include/ntdll.h:
--------------------------------------------------------------------------------
1 | #ifndef _NTDLL_H
2 | #define _NTDLL_H
3 |
4 | //
5 | // Remap definitions.
6 | //
7 |
8 | #ifdef NTDLL_NO_INLINE_INIT_STRING
9 | #define PHNT_NO_INLINE_INIT_STRING
10 | #endif
11 |
12 | //
13 | // Hack, because prototype in PH's headers and evntprov.h
14 | // don't match.
15 | //
16 |
17 | #define EtwEventRegister __EtwEventRegisterIgnored
18 |
19 | #include "phnt/phnt_windows.h"
20 | #include "phnt/phnt.h"
21 |
22 | #undef EtwEventRegister
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/inj.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2000
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{01B84E09-7D4F-4415-95AD-C9291497D28C}"
7 | ProjectSection(SolutionItems) = preProject
8 | .editorconfig = .editorconfig
9 | EndProjectSection
10 | EndProject
11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injdrv", "src\injdrv\injdrv.vcxproj", "{46A74761-6CFA-41AF-A536-47F08E2C7B48}"
12 | EndProject
13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injdll", "src\injdll\injdll.vcxproj", "{558C8AC2-041C-44AC-B41C-2DAB9277A3AB}"
14 | EndProject
15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DetoursNT", "src\DetoursNT\DetoursNT\DetoursNT.vcxproj", "{C78B9003-FC49-4BBF-8F29-52FAD48BB58A}"
16 | EndProject
17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injldr", "src\injldr\injldr.vcxproj", "{A72DAEF5-C739-4E70-B57E-4310ABA03749}"
18 | EndProject
19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injlib", "src\injlib\injlib.vcxproj", "{E2ABAE21-2862-4356-BA49-28C060878D76}"
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|ARM = Debug|ARM
24 | Debug|ARM64 = Debug|ARM64
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|ARM = Release|ARM
28 | Release|ARM64 = Release|ARM64
29 | Release|x64 = Release|x64
30 | Release|x86 = Release|x86
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|ARM.ActiveCfg = Debug|ARM64
34 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|ARM.Build.0 = Debug|ARM64
35 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|ARM64.ActiveCfg = Debug|ARM64
36 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|ARM64.Build.0 = Debug|ARM64
37 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|x64.ActiveCfg = Debug|x64
38 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|x64.Build.0 = Debug|x64
39 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|x86.ActiveCfg = Debug|Win32
40 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Debug|x86.Build.0 = Debug|Win32
41 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|ARM.ActiveCfg = Release|Win32
42 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|ARM64.ActiveCfg = Release|ARM64
43 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|ARM64.Build.0 = Release|ARM64
44 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|x64.ActiveCfg = Release|x64
45 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|x64.Build.0 = Release|x64
46 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|x86.ActiveCfg = Release|Win32
47 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}.Release|x86.Build.0 = Release|Win32
48 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|ARM.ActiveCfg = Debug|ARM
49 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|ARM.Build.0 = Debug|ARM
50 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|ARM64.ActiveCfg = Debug|ARM64
51 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|ARM64.Build.0 = Debug|ARM64
52 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|x64.ActiveCfg = Debug|x64
53 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|x64.Build.0 = Debug|x64
54 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|x86.ActiveCfg = Debug|Win32
55 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Debug|x86.Build.0 = Debug|Win32
56 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|ARM.ActiveCfg = Release|ARM
57 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|ARM.Build.0 = Release|ARM
58 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|ARM64.ActiveCfg = Release|ARM64
59 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|ARM64.Build.0 = Release|ARM64
60 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|x64.ActiveCfg = Release|x64
61 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|x64.Build.0 = Release|x64
62 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|x86.ActiveCfg = Release|Win32
63 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}.Release|x86.Build.0 = Release|Win32
64 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|ARM.ActiveCfg = Debug|ARM
65 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|ARM.Build.0 = Debug|ARM
66 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|ARM64.ActiveCfg = Debug|ARM64
67 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|ARM64.Build.0 = Debug|ARM64
68 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|x64.ActiveCfg = Debug|x64
69 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|x64.Build.0 = Debug|x64
70 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|x86.ActiveCfg = Debug|Win32
71 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Debug|x86.Build.0 = Debug|Win32
72 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|ARM.ActiveCfg = Release|ARM
73 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|ARM.Build.0 = Release|ARM
74 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|ARM64.ActiveCfg = Release|ARM64
75 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|ARM64.Build.0 = Release|ARM64
76 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|x64.ActiveCfg = Release|x64
77 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|x64.Build.0 = Release|x64
78 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|x86.ActiveCfg = Release|Win32
79 | {C78B9003-FC49-4BBF-8F29-52FAD48BB58A}.Release|x86.Build.0 = Release|Win32
80 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|ARM.ActiveCfg = Debug|ARM
81 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|ARM.Build.0 = Debug|ARM
82 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|ARM64.ActiveCfg = Debug|ARM64
83 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|ARM64.Build.0 = Debug|ARM64
84 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|x64.ActiveCfg = Debug|x64
85 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|x64.Build.0 = Debug|x64
86 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|x86.ActiveCfg = Debug|Win32
87 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Debug|x86.Build.0 = Debug|Win32
88 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|ARM.ActiveCfg = Release|ARM
89 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|ARM.Build.0 = Release|ARM
90 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|ARM64.ActiveCfg = Release|ARM64
91 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|ARM64.Build.0 = Release|ARM64
92 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|x64.ActiveCfg = Release|x64
93 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|x64.Build.0 = Release|x64
94 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|x86.ActiveCfg = Release|Win32
95 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}.Release|x86.Build.0 = Release|Win32
96 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|ARM.ActiveCfg = Debug|Win32
97 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|ARM64.ActiveCfg = Debug|ARM64
98 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|ARM64.Build.0 = Debug|ARM64
99 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|x64.ActiveCfg = Debug|x64
100 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|x64.Build.0 = Debug|x64
101 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|x86.ActiveCfg = Debug|Win32
102 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Debug|x86.Build.0 = Debug|Win32
103 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|ARM.ActiveCfg = Release|Win32
104 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|ARM64.ActiveCfg = Release|ARM64
105 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|ARM64.Build.0 = Release|ARM64
106 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|x64.ActiveCfg = Release|x64
107 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|x64.Build.0 = Release|x64
108 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|x86.ActiveCfg = Release|Win32
109 | {E2ABAE21-2862-4356-BA49-28C060878D76}.Release|x86.Build.0 = Release|Win32
110 | EndGlobalSection
111 | GlobalSection(SolutionProperties) = preSolution
112 | HideSolutionNode = FALSE
113 | EndGlobalSection
114 | GlobalSection(ExtensibilityGlobals) = postSolution
115 | SolutionGuid = {B2F42D07-8CF5-40C5-924F-6EAB82D9ABF0}
116 | EndGlobalSection
117 | EndGlobal
118 |
--------------------------------------------------------------------------------
/src/injdll/injdll.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | ARM
8 |
9 |
10 | Debug
11 | ARM64
12 |
13 |
14 | Debug
15 | Win32
16 |
17 |
18 | Release
19 | ARM
20 |
21 |
22 | Release
23 | ARM64
24 |
25 |
26 | Release
27 | Win32
28 |
29 |
30 | Debug
31 | x64
32 |
33 |
34 | Release
35 | x64
36 |
37 |
38 |
39 |
40 | injdll
41 | {558C8AC2-041C-44AC-B41C-2DAB9277A3AB}
42 | 15.0
43 | Win32Proj
44 | 10.0
45 |
46 |
47 |
48 | DynamicLibrary
49 | v142
50 | Unicode
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | $(ProjectName)$(PlatformShortName)
62 | $(SolutionDir)bin\$(PlatformShortName)\$(Configuration)\
63 | $(SolutionDir)bin\obj\$(PlatformShortName)\$(Configuration)\$(ProjectName)\
64 |
65 |
66 |
67 | Level3
68 | true
69 | stdcpplatest
70 | $(IntDir)%(RelativeDir)%(Filename)%(Extension).obj
71 | $(SolutionDir)include;$(SolutionDir)include\phnt;$(SolutionDir)src\DetoursNT\Detours\src;%(AdditionalIncludeDirectories)
72 | true
73 | true
74 | false
75 | false
76 | false
77 | Default
78 |
79 |
80 | Windows
81 | DebugFull
82 | ntdll.lib;%(AdditionalDependencies)
83 | true
84 | NtDllMain
85 |
86 |
87 |
88 |
89 | true
90 |
91 |
92 | true
93 |
94 |
95 |
96 | _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
97 | Disabled
98 | MultiThreadedDebug
99 |
100 |
101 |
102 |
103 | false
104 | true
105 |
106 |
107 | false
108 |
109 |
110 |
111 | NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
112 | MaxSpeed
113 | MultiThreaded
114 | true
115 | true
116 |
117 |
118 | true
119 | true
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | {c78b9003-fc49-4bbf-8f29-52fad48bb58a}
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/src/injdll/injdll.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {40251b32-677c-4466-8e51-cd6f878b2f71}
18 |
19 |
20 | {7fff6dd8-1c85-4070-bc54-273c39035c97}
21 |
22 |
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 |
32 |
33 | Header Files
34 |
35 |
36 | Header Files\ntdll\phnt
37 |
38 |
39 | Header Files\ntdll\phnt
40 |
41 |
42 | Header Files\ntdll\phnt
43 |
44 |
45 | Header Files\ntdll\phnt
46 |
47 |
48 | Header Files\ntdll\phnt
49 |
50 |
51 | Header Files\ntdll\phnt
52 |
53 |
54 | Header Files\ntdll\phnt
55 |
56 |
57 | Header Files\ntdll\phnt
58 |
59 |
60 | Header Files\ntdll\phnt
61 |
62 |
63 | Header Files\ntdll\phnt
64 |
65 |
66 | Header Files\ntdll\phnt
67 |
68 |
69 | Header Files\ntdll\phnt
70 |
71 |
72 | Header Files\ntdll\phnt
73 |
74 |
75 | Header Files\ntdll\phnt
76 |
77 |
78 | Header Files\ntdll\phnt
79 |
80 |
81 | Header Files\ntdll\phnt
82 |
83 |
84 | Header Files\ntdll\phnt
85 |
86 |
87 | Header Files\ntdll\phnt
88 |
89 |
90 | Header Files\ntdll\phnt
91 |
92 |
93 | Header Files\ntdll\phnt
94 |
95 |
96 | Header Files\ntdll\phnt
97 |
98 |
99 | Header Files\ntdll\phnt
100 |
101 |
102 | Header Files\ntdll\phnt
103 |
104 |
105 | Header Files\ntdll\phnt
106 |
107 |
108 | Header Files\ntdll\phnt
109 |
110 |
111 | Header Files\ntdll\phnt
112 |
113 |
114 | Header Files\ntdll\phnt
115 |
116 |
117 | Header Files\ntdll\phnt
118 |
119 |
120 | Header Files\ntdll\phnt
121 |
122 |
123 | Header Files\ntdll\phnt
124 |
125 |
126 | Header Files\ntdll\phnt
127 |
128 |
129 | Header Files\ntdll
130 |
131 |
132 |
--------------------------------------------------------------------------------
/src/injdll/main.cpp:
--------------------------------------------------------------------------------
1 | #define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
2 |
3 | #include "wow64log.h"
4 |
5 | //
6 | // Include NTDLL-related headers.
7 | //
8 | #define NTDLL_NO_INLINE_INIT_STRING
9 | #include
10 |
11 | #if defined(_M_IX86)
12 | # define ARCH_A "x86"
13 | # define ARCH_W L"x86"
14 | #elif defined(_M_AMD64)
15 | # define ARCH_A "x64"
16 | # define ARCH_W L"x64"
17 | #elif defined(_M_ARM)
18 | # define ARCH_A "ARM32"
19 | # define ARCH_W L"ARM32"
20 | #elif defined(_M_ARM64)
21 | # define ARCH_A "ARM64"
22 | # define ARCH_W L"ARM64"
23 | #else
24 | # error Unknown architecture
25 | #endif
26 |
27 |
28 | // size_t strlen(const char * str)
29 | // {
30 | // const char *s;
31 | // for (s = str; *s; ++s) {}
32 | // return(s - str);
33 | // }
34 |
35 | //
36 | // Include support for ETW logging.
37 | // Note that following functions are mocked, because they're
38 | // located in advapi32.dll. Fortunatelly, advapi32.dll simply
39 | // redirects calls to these functions to the ntdll.dll.
40 | //
41 |
42 | #define EventActivityIdControl EtwEventActivityIdControl
43 | #define EventEnabled EtwEventEnabled
44 | #define EventProviderEnabled EtwEventProviderEnabled
45 | #define EventRegister EtwEventRegister
46 | #define EventSetInformation EtwEventSetInformation
47 | #define EventUnregister EtwEventUnregister
48 | #define EventWrite EtwEventWrite
49 | #define EventWriteEndScenario EtwEventWriteEndScenario
50 | #define EventWriteEx EtwEventWriteEx
51 | #define EventWriteStartScenario EtwEventWriteStartScenario
52 | #define EventWriteString EtwEventWriteString
53 | #define EventWriteTransfer EtwEventWriteTransfer
54 |
55 | #include
56 |
57 | //
58 | // Include Detours.
59 | //
60 |
61 | #include
62 |
63 | //
64 | // This is necessary for x86 builds because of SEH,
65 | // which is used by Detours. Look at loadcfg.c file
66 | // in Visual Studio's CRT source codes for the original
67 | // implementation.
68 | //
69 |
70 | #if defined(_M_IX86) || defined(_X86_)
71 |
72 | EXTERN_C PVOID __safe_se_handler_table[]; /* base of safe handler entry table */
73 | EXTERN_C BYTE __safe_se_handler_count; /* absolute symbol whose address is
74 | the count of table entries */
75 | EXTERN_C
76 | CONST
77 | DECLSPEC_SELECTANY
78 | IMAGE_LOAD_CONFIG_DIRECTORY
79 | _load_config_used = {
80 | sizeof(_load_config_used),
81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 | (SIZE_T)__safe_se_handler_table,
83 | (SIZE_T)&__safe_se_handler_count,
84 | };
85 |
86 | #endif
87 |
88 | //
89 | // Unfortunatelly sprintf-like functions are not exposed
90 | // by ntdll.lib, which we're linking against. We have to
91 | // load them dynamically.
92 | //
93 |
94 | using _snwprintf_fn_t = int (__cdecl*)(
95 | wchar_t *buffer,
96 | size_t count,
97 | const wchar_t *format,
98 | ...
99 | );
100 |
101 | inline _snwprintf_fn_t _snwprintf = nullptr;
102 |
103 | //
104 | // ETW provider GUID and global provider handle.
105 | //
106 |
107 | //
108 | // GUID:
109 | // {a4b4ba50-a667-43f5-919b-1e52a6d69bd5}
110 | //
111 |
112 | GUID ProviderGuid = {
113 | 0xa4b4ba50, 0xa667, 0x43f5, { 0x91, 0x9b, 0x1e, 0x52, 0xa6, 0xd6, 0x9b, 0xd5 }
114 | };
115 |
116 | REGHANDLE ProviderHandle;
117 |
118 | //
119 | // Hooking functions and prototypes.
120 | //
121 |
122 | inline decltype(NtQuerySystemInformation)* OrigNtQuerySystemInformation = nullptr;
123 |
124 | EXTERN_C
125 | NTSTATUS
126 | NTAPI
127 | HookNtQuerySystemInformation(
128 | _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
129 | _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
130 | _In_ ULONG SystemInformationLength,
131 | _Out_opt_ PULONG ReturnLength
132 | )
133 | {
134 | //
135 | // Log the function call.
136 | //
137 |
138 | WCHAR Buffer[128];
139 | _snwprintf(Buffer,
140 | RTL_NUMBER_OF(Buffer),
141 | L"NtQuerySystemInformation(%i, %p, %i)",
142 | SystemInformationClass,
143 | SystemInformation,
144 | SystemInformationLength);
145 |
146 | EtwEventWriteString(ProviderHandle, 0, 0, Buffer);
147 |
148 | //
149 | // Call original function.
150 | //
151 |
152 | return OrigNtQuerySystemInformation(SystemInformationClass,
153 | SystemInformation,
154 | SystemInformationLength,
155 | ReturnLength);
156 | }
157 |
158 | inline decltype(NtCreateThreadEx)* OrigNtCreateThreadEx = nullptr;
159 |
160 | NTSTATUS
161 | NTAPI
162 | HookNtCreateThreadEx(
163 | _Out_ PHANDLE ThreadHandle,
164 | _In_ ACCESS_MASK DesiredAccess,
165 | _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
166 | _In_ HANDLE ProcessHandle,
167 | _In_ PVOID StartRoutine, // PUSER_THREAD_START_ROUTINE
168 | _In_opt_ PVOID Argument,
169 | _In_ ULONG CreateFlags, // THREAD_CREATE_FLAGS_*
170 | _In_ SIZE_T ZeroBits,
171 | _In_ SIZE_T StackSize,
172 | _In_ SIZE_T MaximumStackSize,
173 | _In_opt_ PPS_ATTRIBUTE_LIST AttributeList
174 | )
175 | {
176 | //
177 | // Log the function call.
178 | //
179 |
180 | WCHAR Buffer[128];
181 | _snwprintf(Buffer,
182 | RTL_NUMBER_OF(Buffer),
183 | L"NtCreateThreadEx(%p, %p)",
184 | ProcessHandle,
185 | StartRoutine);
186 |
187 | EtwEventWriteString(ProviderHandle, 0, 0, Buffer);
188 |
189 | //
190 | // Call original function.
191 | //
192 |
193 | return OrigNtCreateThreadEx(ThreadHandle,
194 | DesiredAccess,
195 | ObjectAttributes,
196 | ProcessHandle,
197 | StartRoutine,
198 | Argument,
199 | CreateFlags,
200 | ZeroBits,
201 | StackSize,
202 | MaximumStackSize,
203 | AttributeList);
204 | }
205 |
206 | NTSTATUS
207 | NTAPI
208 | ThreadRoutine(
209 | _In_ PVOID ThreadParameter
210 | )
211 | {
212 | LARGE_INTEGER Delay;
213 | Delay.QuadPart = -10 * 1000 * 100; // 100ms
214 |
215 | for (;;)
216 | {
217 | // EtwEventWriteString(ProviderHandle, 0, 0, L"NtDelayExecution(100ms)");
218 |
219 | NtDelayExecution(FALSE, &Delay);
220 | }
221 | }
222 |
223 | NTSTATUS
224 | NTAPI
225 | EnableDetours(
226 | VOID
227 | )
228 | {
229 | DetourTransactionBegin();
230 | {
231 | OrigNtQuerySystemInformation = NtQuerySystemInformation;
232 | DetourAttach((PVOID*)&OrigNtQuerySystemInformation, HookNtQuerySystemInformation);
233 |
234 | OrigNtCreateThreadEx = NtCreateThreadEx;
235 | DetourAttach((PVOID*)&OrigNtCreateThreadEx, HookNtCreateThreadEx);
236 | }
237 | DetourTransactionCommit();
238 |
239 | return STATUS_SUCCESS;
240 | }
241 |
242 | NTSTATUS
243 | NTAPI
244 | DisableDetours(
245 | VOID
246 | )
247 | {
248 | DetourTransactionBegin();
249 | {
250 | DetourDetach((PVOID*)&OrigNtQuerySystemInformation, HookNtQuerySystemInformation);
251 | DetourDetach((PVOID*)&OrigNtCreateThreadEx, HookNtCreateThreadEx);
252 | }
253 | DetourTransactionCommit();
254 |
255 | return STATUS_SUCCESS;
256 | }
257 |
258 | NTSTATUS
259 | NTAPI
260 | OnProcessAttach(
261 | _In_ PVOID ModuleHandle
262 | )
263 | {
264 | //
265 | // First, resolve address of the _snwprintf function.
266 | //
267 |
268 | ANSI_STRING RoutineName;
269 | RtlInitAnsiString(&RoutineName, (PSTR)"_snwprintf");
270 |
271 | UNICODE_STRING NtdllPath;
272 | RtlInitUnicodeString(&NtdllPath, (PWSTR)L"ntdll.dll");
273 |
274 | HANDLE NtdllHandle;
275 | LdrGetDllHandle(NULL, 0, &NtdllPath, &NtdllHandle);
276 | LdrGetProcedureAddress(NtdllHandle, &RoutineName, 0, (PVOID*)&_snwprintf);
277 |
278 | //
279 | // Make us unloadable (by FreeLibrary calls).
280 | //
281 |
282 | LdrAddRefDll(LDR_ADDREF_DLL_PIN, ModuleHandle);
283 |
284 | //
285 | // Hide this DLL from the PEB.
286 | //
287 |
288 | PPEB Peb = NtCurrentPeb();
289 | PLIST_ENTRY ListEntry;
290 |
291 | for (ListEntry = Peb->Ldr->InLoadOrderModuleList.Flink;
292 | ListEntry != &Peb->Ldr->InLoadOrderModuleList;
293 | ListEntry = ListEntry->Flink)
294 | {
295 | PLDR_DATA_TABLE_ENTRY LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
296 |
297 | //
298 | // ModuleHandle is same as DLL base address.
299 | //
300 |
301 | if (LdrEntry->DllBase == ModuleHandle)
302 | {
303 | RemoveEntryList(&LdrEntry->InLoadOrderLinks);
304 | RemoveEntryList(&LdrEntry->InInitializationOrderLinks);
305 | RemoveEntryList(&LdrEntry->InMemoryOrderLinks);
306 | RemoveEntryList(&LdrEntry->HashLinks);
307 |
308 | break;
309 | }
310 | }
311 |
312 | //
313 | // Create exports for Wow64Log* functions in
314 | // the PE header of this DLL.
315 | //
316 |
317 | Wow64LogCreateExports(ModuleHandle);
318 |
319 |
320 | //
321 | // Register ETW provider.
322 | //
323 |
324 | EtwEventRegister(&ProviderGuid,
325 | NULL,
326 | NULL,
327 | &ProviderHandle);
328 |
329 | //
330 | // Create dummy thread - used for testing.
331 | //
332 |
333 | // RtlCreateUserThread(NtCurrentProcess(),
334 | // NULL,
335 | // FALSE,
336 | // 0,
337 | // 0,
338 | // 0,
339 | // &ThreadRoutine,
340 | // NULL,
341 | // NULL,
342 | // NULL);
343 |
344 | //
345 | // Get command line of the current process and send it.
346 | //
347 |
348 | PWSTR CommandLine = Peb->ProcessParameters->CommandLine.Buffer;
349 |
350 | WCHAR Buffer[1024];
351 | _snwprintf(Buffer,
352 | RTL_NUMBER_OF(Buffer),
353 | L"Arch: %s, CommandLine: '%s'",
354 | ARCH_W,
355 | CommandLine);
356 |
357 | EtwEventWriteString(ProviderHandle, 0, 0, Buffer);
358 |
359 | //
360 | // Hook all functions.
361 | //
362 |
363 | return EnableDetours();
364 | }
365 |
366 | NTSTATUS
367 | NTAPI
368 | OnProcessDetach(
369 | _In_ HANDLE ModuleHandle
370 | )
371 | {
372 | //
373 | // Unhook all functions.
374 | //
375 |
376 | return DisableDetours();
377 | }
378 |
379 | EXTERN_C
380 | BOOL
381 | NTAPI
382 | NtDllMain(
383 | _In_ HANDLE ModuleHandle,
384 | _In_ ULONG Reason,
385 | _In_ LPVOID Reserved
386 | )
387 | {
388 | switch (Reason)
389 | {
390 | case DLL_PROCESS_ATTACH:
391 | OnProcessAttach(ModuleHandle);
392 | break;
393 |
394 | case DLL_PROCESS_DETACH:
395 | OnProcessDetach(ModuleHandle);
396 | break;
397 |
398 | case DLL_THREAD_ATTACH:
399 |
400 | break;
401 |
402 | case DLL_THREAD_DETACH:
403 |
404 | break;
405 | }
406 |
407 | return TRUE;
408 | }
409 |
410 |
--------------------------------------------------------------------------------
/src/injdll/wow64log.cpp:
--------------------------------------------------------------------------------
1 | #define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
2 |
3 | #include "wow64log.h"
4 |
5 | #if defined(_M_AMD64) || defined(_ARM64)
6 |
7 | # if defined(WOW64_LOG_FAKE_EXPORT_ENABLE)
8 |
9 | # define DECLSPEC_EXPORT
10 | # define WOW64_LOG_NUMBER_OF_FUNCTIONS 4
11 |
12 | typedef struct _WOW64_LOG_FAKE_EXPORT_DIRECTORY
13 | {
14 | IMAGE_EXPORT_DIRECTORY ExportDirectory;
15 |
16 | ULONG ExportTable [WOW64_LOG_NUMBER_OF_FUNCTIONS];
17 | ULONG NameTable [WOW64_LOG_NUMBER_OF_FUNCTIONS];
18 | SHORT OrdinalTable [WOW64_LOG_NUMBER_OF_FUNCTIONS];
19 | CHAR FunctionNames [WOW64_LOG_NUMBER_OF_FUNCTIONS][32];
20 | } WOW64_LOG_FAKE_EXPORT_DIRECTORY, *PWOW64_LOG_FAKE_EXPORT_DIRECTORY;
21 |
22 | WOW64_LOG_FAKE_EXPORT_DIRECTORY Wow64LogFakeExportDirectory;
23 |
24 | # else
25 | # define DECLSPEC_EXPORT __declspec(dllexport)
26 | # endif
27 |
28 | typedef ULONG (*WOW64_LOG_ARGUMENTS)[32];
29 |
30 | typedef struct _WOW64_LOG_SERVICE
31 | {
32 | PLDR_DATA_TABLE_ENTRY BtLdrEntry; // NULL on Win7
33 | WOW64_LOG_ARGUMENTS Arguments;
34 | ULONG Reserved;
35 | ULONG ServiceNumber;
36 | NTSTATUS Status;
37 | BOOLEAN PostCall;
38 | } WOW64_LOG_SERVICE, *PWOW64_LOG_SERVICE;
39 |
40 | EXTERN_C
41 | DECLSPEC_EXPORT
42 | NTSTATUS
43 | NTAPI
44 | Wow64LogInitialize(
45 | VOID
46 | )
47 | {
48 | return STATUS_SUCCESS;
49 | }
50 |
51 | EXTERN_C
52 | DECLSPEC_EXPORT
53 | NTSTATUS
54 | NTAPI
55 | Wow64LogMessageArgList(
56 | UCHAR Level,
57 | const CHAR* Format,
58 | va_list Args
59 | )
60 | {
61 | #if 0
62 | # define DPFLTR_ERROR_LEVEL 0
63 | # define DPFLTR_WARNING_LEVEL 1
64 | # define DPFLTR_TRACE_LEVEL 2
65 | # define DPFLTR_INFO_LEVEL 3
66 |
67 | # define DPFLTR_IHVDRIVER_ID 77
68 |
69 | vDbgPrintEx(DPFLTR_IHVDRIVER_ID,
70 | DPFLTR_ERROR_LEVEL,
71 | (PCH)Format,
72 | Args);
73 | #else
74 | UNREFERENCED_PARAMETER(Level);
75 | UNREFERENCED_PARAMETER(Format);
76 | UNREFERENCED_PARAMETER(Args);
77 | #endif
78 |
79 | return STATUS_SUCCESS;
80 | }
81 |
82 | EXTERN_C
83 | DECLSPEC_EXPORT
84 | NTSTATUS
85 | NTAPI
86 | Wow64LogSystemService(
87 | PWOW64_LOG_SERVICE ServiceParameters
88 | )
89 | {
90 | UNREFERENCED_PARAMETER(ServiceParameters);
91 |
92 | return STATUS_SUCCESS;
93 | }
94 |
95 | EXTERN_C
96 | DECLSPEC_EXPORT
97 | NTSTATUS
98 | NTAPI
99 | Wow64LogTerminate(
100 | VOID
101 | )
102 | {
103 | return STATUS_SUCCESS;
104 | }
105 |
106 | #else
107 | //
108 | // Force #undef of WOW64_LOG_FAKE_EXPORT_ENABLE on platforms
109 | // that don't support Wow64 (such as x86, ARM32).
110 | //
111 | # undef WOW64_LOG_FAKE_EXPORT_ENABLE
112 | #endif
113 |
114 | EXTERN_C
115 | NTSTATUS
116 | NTAPI
117 | Wow64LogCreateExports(
118 | PVOID BaseAddress
119 | )
120 | {
121 | #if defined(WOW64_LOG_FAKE_EXPORT_ENABLE)
122 |
123 | # define RelVa(Address) (ULONG)((ULONG_PTR)Address - (ULONG_PTR)BaseAddress)
124 |
125 | PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(BaseAddress);
126 | PIMAGE_OPTIONAL_HEADER OptionalHeader = &NtHeaders->OptionalHeader;
127 | PIMAGE_DATA_DIRECTORY ExportDataDirectory = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
128 |
129 | RTL_ASSERT(
130 | ExportDataDirectory->VirtualAddress == 0 &&
131 | ExportDataDirectory->Size == 0
132 | );
133 |
134 | NTSTATUS Status;
135 |
136 | PVOID ExportDataDirectoryAddress = ExportDataDirectory;
137 | SIZE_T RegionSize = sizeof(IMAGE_DATA_DIRECTORY);
138 | ULONG OldProtect;
139 | Status = NtProtectVirtualMemory(NtCurrentProcess(),
140 | &ExportDataDirectoryAddress,
141 | &RegionSize,
142 | PAGE_READWRITE,
143 | &OldProtect);
144 |
145 | if (!NT_SUCCESS(Status))
146 | {
147 | return Status;
148 | }
149 |
150 | ExportDataDirectory->VirtualAddress = RelVa(&Wow64LogFakeExportDirectory);
151 | ExportDataDirectory->Size = sizeof(Wow64LogFakeExportDirectory);
152 |
153 | Status = NtProtectVirtualMemory(NtCurrentProcess(),
154 | &ExportDataDirectoryAddress,
155 | &RegionSize,
156 | OldProtect,
157 | &OldProtect);
158 |
159 | if (!NT_SUCCESS(Status))
160 | {
161 | //
162 | // Ignore...
163 | //
164 | }
165 |
166 | Wow64LogFakeExportDirectory.ExportDirectory.NumberOfFunctions = WOW64_LOG_NUMBER_OF_FUNCTIONS;
167 | Wow64LogFakeExportDirectory.ExportDirectory.NumberOfNames = WOW64_LOG_NUMBER_OF_FUNCTIONS;
168 | Wow64LogFakeExportDirectory.ExportDirectory.AddressOfFunctions = RelVa(Wow64LogFakeExportDirectory.ExportTable);
169 | Wow64LogFakeExportDirectory.ExportDirectory.AddressOfNames = RelVa(Wow64LogFakeExportDirectory.NameTable);
170 | Wow64LogFakeExportDirectory.ExportDirectory.AddressOfNameOrdinals = RelVa(Wow64LogFakeExportDirectory.OrdinalTable);
171 |
172 | Wow64LogFakeExportDirectory.NameTable[0] = RelVa(Wow64LogFakeExportDirectory.FunctionNames[0]);
173 | Wow64LogFakeExportDirectory.NameTable[1] = RelVa(Wow64LogFakeExportDirectory.FunctionNames[1]);
174 | Wow64LogFakeExportDirectory.NameTable[2] = RelVa(Wow64LogFakeExportDirectory.FunctionNames[2]);
175 | Wow64LogFakeExportDirectory.NameTable[3] = RelVa(Wow64LogFakeExportDirectory.FunctionNames[3]);
176 |
177 | Wow64LogFakeExportDirectory.OrdinalTable[0] = 0;
178 | Wow64LogFakeExportDirectory.OrdinalTable[1] = 1;
179 | Wow64LogFakeExportDirectory.OrdinalTable[2] = 2;
180 | Wow64LogFakeExportDirectory.OrdinalTable[3] = 3;
181 |
182 | Wow64LogFakeExportDirectory.ExportTable[0] = RelVa(&Wow64LogInitialize);
183 | Wow64LogFakeExportDirectory.ExportTable[1] = RelVa(&Wow64LogMessageArgList);
184 | Wow64LogFakeExportDirectory.ExportTable[2] = RelVa(&Wow64LogSystemService);
185 | Wow64LogFakeExportDirectory.ExportTable[3] = RelVa(&Wow64LogTerminate);
186 |
187 | ANSI_STRING Wow64LogInitializeRoutineName;
188 | RtlInitAnsiString(&Wow64LogInitializeRoutineName, (PSTR)"Wow64LogInitialize");
189 | RtlCopyMemory(Wow64LogFakeExportDirectory.FunctionNames[0],
190 | Wow64LogInitializeRoutineName.Buffer,
191 | Wow64LogInitializeRoutineName.Length + 1);
192 |
193 | ANSI_STRING Wow64LogMessageArgListRoutineName;
194 | RtlInitAnsiString(&Wow64LogMessageArgListRoutineName, (PSTR)"Wow64LogMessageArgList");
195 | RtlCopyMemory(Wow64LogFakeExportDirectory.FunctionNames[1],
196 | Wow64LogMessageArgListRoutineName.Buffer,
197 | Wow64LogMessageArgListRoutineName.Length + 1);
198 |
199 | ANSI_STRING Wow64LogSystemServiceRoutineName;
200 | RtlInitAnsiString(&Wow64LogSystemServiceRoutineName, (PSTR)"Wow64LogSystemService");
201 | RtlCopyMemory(Wow64LogFakeExportDirectory.FunctionNames[2],
202 | Wow64LogSystemServiceRoutineName.Buffer,
203 | Wow64LogSystemServiceRoutineName.Length + 1);
204 |
205 | ANSI_STRING Wow64LogTerminateRoutineName;
206 | RtlInitAnsiString(&Wow64LogTerminateRoutineName, (PSTR)"Wow64LogTerminate");
207 | RtlCopyMemory(Wow64LogFakeExportDirectory.FunctionNames[3],
208 | Wow64LogTerminateRoutineName.Buffer,
209 | Wow64LogTerminateRoutineName.Length + 1);
210 |
211 | #else
212 |
213 | UNREFERENCED_PARAMETER(BaseAddress);
214 |
215 | #endif
216 |
217 | return STATUS_SUCCESS;
218 | }
219 |
--------------------------------------------------------------------------------
/src/injdll/wow64log.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #define NTDLL_NO_INLINE_INIT_STRING
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | //#define WOW64_LOG_FAKE_EXPORT_ENABLE
10 |
11 | NTSTATUS
12 | NTAPI
13 | Wow64LogCreateExports(
14 | PVOID BaseAddress
15 | );
16 |
17 | #ifdef __cplusplus
18 | }
19 | #endif
20 |
--------------------------------------------------------------------------------
/src/injdrv/injdrv.inf:
--------------------------------------------------------------------------------
1 | ;
2 | ; injdrv.inf
3 | ;
4 |
5 | [Version]
6 | Signature="$WINDOWS NT$"
7 | Class=Sample ; TODO: edit Class
8 | ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid
9 | Provider=%ManufacturerName%
10 | CatalogFile=injdrv.cat
11 | DriverVer= ; TODO: set DriverVer in stampinf property pages
12 |
13 | [DestinationDirs]
14 | DefaultDestDir = 12
15 | injdrv_Device_CoInstaller_CopyFiles = 11
16 |
17 | ; ================= Class section =====================
18 |
19 | [ClassInstall32]
20 | Addreg=SampleClassReg
21 |
22 | [SampleClassReg]
23 | HKR,,,0,%ClassName%
24 | HKR,,Icon,,-5
25 |
26 | [SourceDisksNames]
27 | 1 = %DiskName%,,,""
28 |
29 | [SourceDisksFiles]
30 | injdrv.sys = 1,,
31 | WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames
32 |
33 | ;*****************************************
34 | ; Install Section
35 | ;*****************************************
36 |
37 | [Manufacturer]
38 | %ManufacturerName%=Standard,NT$ARCH$
39 |
40 | [Standard.NT$ARCH$]
41 | %injdrv.DeviceDesc%=injdrv_Device, Root\injdrv ; TODO: edit hw-id
42 |
43 | [injdrv_Device.NT]
44 | CopyFiles=Drivers_Dir
45 |
46 | [Drivers_Dir]
47 | injdrv.sys
48 |
49 | ;-------------- Service installation
50 | [injdrv_Device.NT.Services]
51 | AddService = injdrv,%SPSVCINST_ASSOCSERVICE%, injdrv_Service_Inst
52 |
53 | ; -------------- injdrv driver install sections
54 | [injdrv_Service_Inst]
55 | DisplayName = %injdrv.SVCDESC%
56 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER
57 | StartType = 3 ; SERVICE_DEMAND_START
58 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL
59 | ServiceBinary = %12%\injdrv.sys
60 |
61 | ;
62 | ;--- injdrv_Device Coinstaller installation ------
63 | ;
64 |
65 | [Strings]
66 | SPSVCINST_ASSOCSERVICE= 0x00000002
67 | ManufacturerName="" ;TODO: Replace with your manufacturer name
68 | ClassName="Samples" ; TODO: edit ClassName
69 | DiskName = "injdrv Installation Disk"
70 | injdrv.DeviceDesc = "injdrv Device"
71 | injdrv.SVCDESC = "injdrv Service"
72 |
--------------------------------------------------------------------------------
/src/injdrv/injdrv.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | ARM64
8 |
9 |
10 | Debug
11 | Win32
12 |
13 |
14 | Release
15 | ARM64
16 |
17 |
18 | Release
19 | Win32
20 |
21 |
22 | Debug
23 | x64
24 |
25 |
26 | Release
27 | x64
28 |
29 |
30 |
31 |
32 | injdrv
33 | {46A74761-6CFA-41AF-A536-47F08E2C7B48}
34 | {1bc93793-694f-48fe-9372-81e2b05556fd}
35 | v4.5
36 | 12.0
37 | Debug
38 | Win32
39 | $(LatestTargetPlatformVersion)
40 |
41 |
42 |
43 |
44 |
45 | WindowsKernelModeDriver10.0
46 | Driver
47 | WDM
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | DbgengKernelDebugger
56 | $(ProjectDir);$(VC_IncludePath);$(IncludePath);$(KMDF_INC_PATH)$(KMDF_VER_PATH)
57 | $(SolutionDir)bin\$(PlatformShortName)\$(Configuration)\
58 | $(SolutionDir)bin\obj\$(PlatformShortName)\$(Configuration)\$(ProjectName)\
59 | false
60 | true
61 |
62 |
63 |
64 | 4201;4748;%(DisableSpecificWarnings)
65 |
66 | stdcpplatest
67 | $(IntDir)%(RelativeDir)%(Filename)%(Extension).obj
68 | true
69 | false
70 |
71 |
72 | DebugFull
73 | /INTEGRITYCHECK %(AdditionalOptions)
74 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
75 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
76 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
77 |
78 |
79 |
80 |
81 | true
82 |
83 |
84 |
85 |
86 |
87 | false
88 |
89 |
90 |
91 | AnySuitable
92 | true
93 |
94 |
95 | UseLinkTimeCodeGeneration
96 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
97 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
98 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | true
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | {e2abae21-2862-4356-ba49-28c060878d76}
118 |
119 |
120 |
--------------------------------------------------------------------------------
/src/injdrv/injdrv.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {8E41214B-6785-4CFE-B992-037D68949A14}
18 | inf;inv;inx;mof;mc;
19 |
20 |
21 |
22 |
23 | Driver Files
24 |
25 |
26 |
27 |
28 | Source Files
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/injdrv/main.c:
--------------------------------------------------------------------------------
1 | #include "../injlib/injlib.h"
2 |
3 | #include
4 |
5 | //////////////////////////////////////////////////////////////////////////
6 | // Helper functions.
7 | //////////////////////////////////////////////////////////////////////////
8 |
9 | //
10 | // Taken from ReactOS, used by InjpInitializeDllPaths.
11 | //
12 |
13 | typedef union
14 | {
15 | WCHAR Name[sizeof(ULARGE_INTEGER) / sizeof(WCHAR)];
16 | ULARGE_INTEGER Alignment;
17 | } ALIGNEDNAME;
18 |
19 | //
20 | // DOS Device Prefix \??\
21 | //
22 |
23 | ALIGNEDNAME ObpDosDevicesShortNamePrefix = { { L'\\', L'?', L'?', L'\\' } };
24 | UNICODE_STRING ObpDosDevicesShortName = {
25 | sizeof(ObpDosDevicesShortNamePrefix), // Length
26 | sizeof(ObpDosDevicesShortNamePrefix), // MaximumLength
27 | (PWSTR)&ObpDosDevicesShortNamePrefix // Buffer
28 | };
29 |
30 | NTSTATUS
31 | NTAPI
32 | InjpJoinPath(
33 | _In_ PUNICODE_STRING Directory,
34 | _In_ PUNICODE_STRING Filename,
35 | _Inout_ PUNICODE_STRING FullPath
36 | )
37 | {
38 | UNICODE_STRING UnicodeBackslash = RTL_CONSTANT_STRING(L"\\");
39 |
40 | BOOLEAN DirectoryEndsWithBackslash = Directory->Length > 0 &&
41 | Directory->Buffer[Directory->Length - 1] == L'\\';
42 |
43 | if (FullPath->MaximumLength < Directory->Length ||
44 | FullPath->MaximumLength - Directory->Length -
45 | (!DirectoryEndsWithBackslash ? 1 : 0) < Filename->Length)
46 | {
47 | return STATUS_DATA_ERROR;
48 | }
49 |
50 | RtlCopyUnicodeString(FullPath, Directory);
51 |
52 | if (!DirectoryEndsWithBackslash)
53 | {
54 | RtlAppendUnicodeStringToString(FullPath, &UnicodeBackslash);
55 | }
56 |
57 | RtlAppendUnicodeStringToString(FullPath, Filename);
58 |
59 | return STATUS_SUCCESS;
60 | }
61 |
62 | NTSTATUS
63 | NTAPI
64 | InjCreateSettings(
65 | _In_ PUNICODE_STRING RegistryPath,
66 | _Inout_ PINJ_SETTINGS Settings
67 | )
68 | {
69 | //
70 | // In the "ImagePath" key of the RegistryPath, there
71 | // is a full path of this driver file. Fetch it.
72 | //
73 |
74 | NTSTATUS Status;
75 |
76 | UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"ImagePath");
77 |
78 | OBJECT_ATTRIBUTES ObjectAttributes;
79 | InitializeObjectAttributes(&ObjectAttributes,
80 | RegistryPath,
81 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
82 | NULL,
83 | NULL);
84 |
85 | HANDLE KeyHandle;
86 | Status = ZwOpenKey(&KeyHandle,
87 | KEY_READ,
88 | &ObjectAttributes);
89 |
90 | if (!NT_SUCCESS(Status))
91 | {
92 | return Status;
93 | }
94 |
95 | //
96 | // Save all information on stack - simply fail if path
97 | // is too long.
98 | //
99 |
100 | UCHAR KeyValueInformationBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + sizeof(WCHAR) * 128];
101 | PKEY_VALUE_FULL_INFORMATION KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)KeyValueInformationBuffer;
102 |
103 | ULONG ResultLength;
104 | Status = ZwQueryValueKey(KeyHandle,
105 | &ValueName,
106 | KeyValueFullInformation,
107 | KeyValueInformation,
108 | sizeof(KeyValueInformationBuffer),
109 | &ResultLength);
110 |
111 | ZwClose(KeyHandle);
112 |
113 | //
114 | // Check for succes. Also check if the value is of expected
115 | // type and whether the path has a meaninful length.
116 | //
117 |
118 | if (!NT_SUCCESS(Status) ||
119 | KeyValueInformation->Type != REG_EXPAND_SZ ||
120 | KeyValueInformation->DataLength < sizeof(ObpDosDevicesShortNamePrefix))
121 | {
122 | return Status;
123 | }
124 |
125 | //
126 | // Save pointer to the fetched ImagePath value and test if
127 | // the path starts with "\??\" prefix - if so, skip it.
128 | //
129 |
130 | PWCHAR ImagePathValue = (PWCHAR)((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset);
131 | ULONG ImagePathValueLength = KeyValueInformation->DataLength;
132 |
133 | if (*(PULONGLONG)(ImagePathValue) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)
134 | {
135 | ImagePathValue += ObpDosDevicesShortName.Length / sizeof(WCHAR);
136 | ImagePathValueLength -= ObpDosDevicesShortName.Length;
137 | }
138 |
139 | //
140 | // Cut the string by the last '\' character, leaving there
141 | // only the directory path.
142 | //
143 |
144 | PWCHAR LastBackslash = wcsrchr(ImagePathValue, L'\\');
145 |
146 | if (!LastBackslash)
147 | {
148 | return STATUS_DATA_ERROR;
149 | }
150 |
151 | *LastBackslash = UNICODE_NULL;
152 |
153 | UNICODE_STRING Directory;
154 | RtlInitUnicodeString(&Directory, ImagePathValue);
155 |
156 | //
157 | // Finally, fill all the buffers...
158 | //
159 |
160 | #define INJ_DLL_X86_NAME L"injdllx86.dll"
161 | UNICODE_STRING InjDllNameX86 = RTL_CONSTANT_STRING(INJ_DLL_X86_NAME);
162 | InjpJoinPath(&Directory, &InjDllNameX86, &Settings->DllPath[InjArchitectureX86]);
163 | InjDbgPrint("[injdrv]: DLL path (x86): '%wZ'\n", &Settings->DllPath[InjArchitectureX86]);
164 |
165 | #define INJ_DLL_X64_NAME L"injdllx64.dll"
166 | UNICODE_STRING InjDllNameX64 = RTL_CONSTANT_STRING(INJ_DLL_X64_NAME);
167 | InjpJoinPath(&Directory, &InjDllNameX64, &Settings->DllPath[InjArchitectureX64]);
168 | InjDbgPrint("[injdrv]: DLL path (x64): '%wZ'\n", &Settings->DllPath[InjArchitectureX64]);
169 |
170 | #define INJ_DLL_ARM32_NAME L"injdllARM.dll"
171 | UNICODE_STRING InjDllNameARM32 = RTL_CONSTANT_STRING(INJ_DLL_ARM32_NAME);
172 | InjpJoinPath(&Directory, &InjDllNameARM32, &Settings->DllPath[InjArchitectureARM32]);
173 | InjDbgPrint("[injdrv]: DLL path (ARM32): '%wZ'\n", &Settings->DllPath[InjArchitectureARM32]);
174 |
175 | #define INJ_DLL_ARM64_NAME L"injdllARM64.dll"
176 | UNICODE_STRING InjDllNameARM64 = RTL_CONSTANT_STRING(INJ_DLL_ARM64_NAME);
177 | InjpJoinPath(&Directory, &InjDllNameARM64, &Settings->DllPath[InjArchitectureARM64]);
178 | InjDbgPrint("[injdrv]: DLL path (ARM64): '%wZ'\n", &Settings->DllPath[InjArchitectureARM64]);
179 |
180 | return STATUS_SUCCESS;
181 | }
182 |
183 | //////////////////////////////////////////////////////////////////////////
184 | // DriverEntry and DriverDestroy.
185 | //////////////////////////////////////////////////////////////////////////
186 |
187 | VOID
188 | NTAPI
189 | DriverDestroy(
190 | _In_ PDRIVER_OBJECT DriverObject
191 | )
192 | {
193 | UNREFERENCED_PARAMETER(DriverObject);
194 |
195 | PsRemoveLoadImageNotifyRoutine(&InjLoadImageNotifyRoutine);
196 | PsSetCreateProcessNotifyRoutineEx(&InjCreateProcessNotifyRoutineEx, TRUE);
197 |
198 | InjDestroy();
199 | }
200 |
201 | NTSTATUS
202 | NTAPI
203 | DriverEntry(
204 | _In_ PDRIVER_OBJECT DriverObject,
205 | _In_ PUNICODE_STRING RegistryPath
206 | )
207 | {
208 | NTSTATUS Status;
209 |
210 | //
211 | // Register DriverUnload routine.
212 | //
213 |
214 | DriverObject->DriverUnload = &DriverDestroy;
215 |
216 | //
217 | // Create injection settings.
218 | //
219 |
220 | INJ_SETTINGS Settings;
221 |
222 | WCHAR BufferDllPathX86[128];
223 | Settings.DllPath[InjArchitectureX86].Length = 0;
224 | Settings.DllPath[InjArchitectureX86].MaximumLength = sizeof(BufferDllPathX86);
225 | Settings.DllPath[InjArchitectureX86].Buffer = BufferDllPathX86;
226 |
227 | WCHAR BufferDllPathX64[128];
228 | Settings.DllPath[InjArchitectureX64].Length = 0;
229 | Settings.DllPath[InjArchitectureX64].MaximumLength = sizeof(BufferDllPathX64);
230 | Settings.DllPath[InjArchitectureX64].Buffer = BufferDllPathX64;
231 |
232 | WCHAR BufferDllPathARM32[128];
233 | Settings.DllPath[InjArchitectureARM32].Length = 0;
234 | Settings.DllPath[InjArchitectureARM32].MaximumLength = sizeof(BufferDllPathARM32);
235 | Settings.DllPath[InjArchitectureARM32].Buffer = BufferDllPathARM32;
236 |
237 | WCHAR BufferDllPathARM64[128];
238 | Settings.DllPath[InjArchitectureARM64].Length = 0;
239 | Settings.DllPath[InjArchitectureARM64].MaximumLength = sizeof(BufferDllPathARM64);
240 | Settings.DllPath[InjArchitectureARM64].Buffer = BufferDllPathARM64;
241 |
242 | Status = InjCreateSettings(RegistryPath, &Settings);
243 |
244 | if (!NT_SUCCESS(Status))
245 | {
246 | return Status;
247 | }
248 |
249 | #if defined (_M_IX86)
250 | Settings.Method = InjMethodThunk;
251 | #elif defined (_M_AMD64)
252 | Settings.Method = InjMethodThunkless;
253 | #elif defined (_M_ARM64)
254 | Settings.Method = InjMethodWow64LogReparse;
255 | #endif
256 | //
257 | // Initialize injection driver.
258 | //
259 |
260 | Status = InjInitialize(DriverObject, RegistryPath, &Settings);
261 |
262 | if (!NT_SUCCESS(Status))
263 | {
264 | return Status;
265 | }
266 |
267 | //
268 | // Install CreateProcess and LoadImage notification routines.
269 | //
270 |
271 | Status = PsSetCreateProcessNotifyRoutineEx(&InjCreateProcessNotifyRoutineEx, FALSE);
272 |
273 | if (!NT_SUCCESS(Status))
274 | {
275 | return Status;
276 | }
277 |
278 | Status = PsSetLoadImageNotifyRoutine(&InjLoadImageNotifyRoutine);
279 |
280 | if (!NT_SUCCESS(Status))
281 | {
282 | PsSetCreateProcessNotifyRoutineEx(&InjCreateProcessNotifyRoutineEx, TRUE);
283 | return Status;
284 | }
285 |
286 | return STATUS_SUCCESS;
287 | }
288 |
--------------------------------------------------------------------------------
/src/injldr/injldr.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | ARM
8 |
9 |
10 | Debug
11 | ARM64
12 |
13 |
14 | Debug
15 | Win32
16 |
17 |
18 | Release
19 | ARM
20 |
21 |
22 | Release
23 | ARM64
24 |
25 |
26 | Release
27 | Win32
28 |
29 |
30 | Debug
31 | x64
32 |
33 |
34 | Release
35 | x64
36 |
37 |
38 |
39 |
40 | injldr
41 | {A72DAEF5-C739-4E70-B57E-4310ABA03749}
42 | 15.0
43 | Win32Proj
44 | 10.0
45 |
46 |
47 |
48 | Application
49 | v142
50 | Unicode
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | $(SolutionDir)bin\$(PlatformShortName)\$(Configuration)\
62 | $(SolutionDir)bin\obj\$(PlatformShortName)\$(Configuration)\$(ProjectName)\
63 |
64 |
65 |
66 | Level3
67 | true
68 | true
69 | stdcpplatest
70 | $(IntDir)%(RelativeDir)%(Filename)%(Extension).obj
71 | true
72 |
73 |
74 | Console
75 | DebugFull
76 | ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
77 |
78 |
79 |
80 |
81 | true
82 |
83 |
84 | true
85 |
86 |
87 |
88 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
89 | Disabled
90 | MultiThreadedDebug
91 |
92 |
93 |
94 |
95 | false
96 | true
97 |
98 |
99 | false
100 |
101 |
102 |
103 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
104 | MaxSpeed
105 | MultiThreaded
106 | true
107 | true
108 | true
109 |
110 |
111 | true
112 | true
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/src/injldr/injldr.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 |
26 |
27 | Header Files
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/injldr/install.c:
--------------------------------------------------------------------------------
1 | #define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
2 | #include "install.h"
3 |
4 | #include
5 | #include
6 |
7 | //
8 | // Code taken from the Windows-driver-samples github repository.
9 | // https://github.com/Microsoft/Windows-driver-samples/blob/master/general/event/exe/install.c
10 | //
11 |
12 | //////////////////////////////////////////////////////////////////////////
13 | // Function prototypes.
14 | //////////////////////////////////////////////////////////////////////////
15 |
16 | BOOLEAN
17 | InstallDriver(
18 | _In_ SC_HANDLE SchSCManager,
19 | _In_ LPCTSTR DriverName,
20 | _In_ LPCTSTR ServiceExe
21 | );
22 |
23 | BOOLEAN
24 | RemoveDriver(
25 | _In_ SC_HANDLE SchSCManager,
26 | _In_ LPCTSTR DriverName
27 | );
28 |
29 | BOOLEAN
30 | StartDriver(
31 | _In_ SC_HANDLE SchSCManager,
32 | _In_ LPCTSTR DriverName
33 | );
34 |
35 | BOOLEAN
36 | StopDriver(
37 | _In_ SC_HANDLE SchSCManager,
38 | _In_ LPCTSTR DriverName
39 | );
40 |
41 | //////////////////////////////////////////////////////////////////////////
42 | // Private functions.
43 | //////////////////////////////////////////////////////////////////////////
44 |
45 | BOOLEAN
46 | InstallDriver(
47 | _In_ SC_HANDLE SchSCManager,
48 | _In_ LPCTSTR DriverName,
49 | _In_ LPCTSTR ServiceExe
50 | )
51 | {
52 | SC_HANDLE schService;
53 | DWORD err;
54 |
55 | //
56 | // NOTE: This creates an entry for a standalone driver. If this
57 | // is modified for use with a driver that requires a Tag,
58 | // Group, and/or Dependencies, it may be necessary to
59 | // query the registry for existing driver information
60 | // (in order to determine a unique Tag, etc.).
61 | //
62 |
63 | //
64 | // Create a new a service object.
65 | //
66 |
67 | schService = CreateService(SchSCManager, // handle of service control manager database
68 | DriverName, // address of name of service to start
69 | DriverName, // address of display name
70 | SERVICE_ALL_ACCESS, // type of access to service
71 | SERVICE_KERNEL_DRIVER, // type of service
72 | SERVICE_DEMAND_START, // when to start service
73 | SERVICE_ERROR_NORMAL, // severity if service fails to start
74 | ServiceExe, // address of name of binary file
75 | NULL, // service does not belong to a group
76 | NULL, // no tag requested
77 | NULL, // no dependency names
78 | NULL, // use LocalSystem account
79 | NULL); // no password for service account
80 |
81 | if (schService == NULL)
82 | {
83 | err = GetLastError();
84 |
85 | if (err == ERROR_SERVICE_EXISTS)
86 | {
87 | //
88 | // Ignore this error.
89 | //
90 | return TRUE;
91 | }
92 | else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
93 | {
94 | //
95 | // Previous instance of the service is not fully deleted so sleep
96 | // and try again.
97 | //
98 | printf("Previous instance of the service is not fully deleted. Try again...\n");
99 | return FALSE;
100 | }
101 | else
102 | {
103 | printf("CreateService failed! Error = %d \n", err);
104 |
105 | //
106 | // Indicate an error.
107 | //
108 | return FALSE;
109 | }
110 | }
111 |
112 | //
113 | // Close the service object.
114 | //
115 |
116 | if (schService)
117 | {
118 | CloseServiceHandle(schService);
119 | }
120 |
121 | //
122 | // Indicate success.
123 | //
124 |
125 | return TRUE;
126 | }
127 |
128 | BOOLEAN
129 | RemoveDriver(
130 | _In_ SC_HANDLE SchSCManager,
131 | _In_ LPCTSTR DriverName
132 | )
133 | {
134 | SC_HANDLE schService;
135 | BOOLEAN rCode;
136 |
137 | //
138 | // Open the handle to the existing service.
139 | //
140 |
141 | schService = OpenService(SchSCManager,
142 | DriverName,
143 | SERVICE_ALL_ACCESS);
144 |
145 | if (schService == NULL)
146 | {
147 | printf("OpenService failed! Error = %d \n", GetLastError());
148 |
149 | //
150 | // Indicate error.
151 | //
152 |
153 | return FALSE;
154 | }
155 |
156 | //
157 | // Mark the service for deletion from the service control manager database.
158 | //
159 |
160 | if (DeleteService(schService))
161 | {
162 | //
163 | // Indicate success.
164 | //
165 |
166 | rCode = TRUE;
167 | }
168 | else
169 | {
170 | printf("DeleteService failed! Error = %d \n", GetLastError());
171 |
172 | //
173 | // Indicate failure. Fall through to properly close the service handle.
174 | //
175 |
176 | rCode = FALSE;
177 | }
178 |
179 | //
180 | // Close the service object.
181 | //
182 |
183 | if (schService)
184 | {
185 | CloseServiceHandle(schService);
186 | }
187 |
188 | return rCode;
189 | }
190 |
191 | BOOLEAN
192 | StartDriver(
193 | _In_ SC_HANDLE SchSCManager,
194 | _In_ LPCTSTR DriverName
195 | )
196 | {
197 | SC_HANDLE schService;
198 | DWORD err;
199 |
200 | //
201 | // Open the handle to the existing service.
202 | //
203 |
204 | schService = OpenService(SchSCManager,
205 | DriverName,
206 | SERVICE_ALL_ACCESS);
207 |
208 | if (schService == NULL)
209 | {
210 | printf("OpenService failed! Error = %d \n", GetLastError());
211 |
212 | //
213 | // Indicate failure.
214 | //
215 |
216 | return FALSE;
217 | }
218 |
219 | //
220 | // Start the execution of the service (i.e. start the driver).
221 | //
222 |
223 | if (!StartService(schService, // service identifier
224 | 0, // number of arguments
225 | NULL)) // pointer to arguments
226 | {
227 |
228 | err = GetLastError();
229 |
230 | if (err == ERROR_SERVICE_ALREADY_RUNNING)
231 | {
232 | //
233 | // Ignore this error.
234 | //
235 |
236 | return TRUE;
237 | }
238 | else
239 | {
240 | printf("StartService failure! Error = %d \n", err);
241 |
242 | //
243 | // Indicate failure. Fall through to properly close the service handle.
244 | //
245 |
246 | return FALSE;
247 | }
248 | }
249 |
250 | //
251 | // Close the service object.
252 | //
253 |
254 | if (schService)
255 | {
256 | CloseServiceHandle(schService);
257 | }
258 |
259 | return TRUE;
260 | }
261 |
262 | BOOLEAN
263 | StopDriver(
264 | _In_ SC_HANDLE SchSCManager,
265 | _In_ LPCTSTR DriverName
266 | )
267 | {
268 | BOOLEAN rCode = TRUE;
269 | SC_HANDLE schService;
270 | SERVICE_STATUS serviceStatus;
271 |
272 | //
273 | // Open the handle to the existing service.
274 | //
275 |
276 | schService = OpenService(SchSCManager,
277 | DriverName,
278 | SERVICE_ALL_ACCESS);
279 |
280 | if (schService == NULL)
281 | {
282 | printf("OpenService failed! Error = %d \n", GetLastError());
283 |
284 | return FALSE;
285 | }
286 |
287 | //
288 | // Request that the service stop.
289 | //
290 |
291 | if (ControlService(schService,
292 | SERVICE_CONTROL_STOP,
293 | &serviceStatus))
294 | {
295 | //
296 | // Indicate success.
297 | //
298 |
299 | rCode = TRUE;
300 | }
301 | else
302 | {
303 | printf("ControlService failed! Error = %d \n", GetLastError());
304 |
305 | //
306 | // Indicate failure. Fall through to properly close the service handle.
307 | //
308 |
309 | rCode = FALSE;
310 | }
311 |
312 | //
313 | // Close the service object.
314 | //
315 |
316 | if (schService)
317 | {
318 | CloseServiceHandle(schService);
319 | }
320 |
321 | return rCode;
322 | }
323 |
324 | //////////////////////////////////////////////////////////////////////////
325 | // Public functions.
326 | //////////////////////////////////////////////////////////////////////////
327 |
328 | BOOLEAN
329 | ManageDriver(
330 | _In_ LPCTSTR DriverName,
331 | _In_ LPCTSTR ServiceName,
332 | _In_ USHORT Function
333 | )
334 | {
335 | SC_HANDLE schSCManager;
336 |
337 | BOOLEAN rCode = TRUE;
338 |
339 | //
340 | // Insure (somewhat) that the driver and service names are valid.
341 | //
342 |
343 | if (!DriverName || !ServiceName)
344 | {
345 | printf("Invalid Driver or Service provided to ManageDriver() \n");
346 |
347 | return FALSE;
348 | }
349 |
350 | //
351 | // Connect to the Service Control Manager and open the Services database.
352 | //
353 |
354 | schSCManager = OpenSCManager(NULL, // local machine
355 | NULL, // local database
356 | SC_MANAGER_ALL_ACCESS); // access required
357 |
358 | if (!schSCManager)
359 | {
360 | printf("Open SC Manager failed! Error = %d \n", GetLastError());
361 |
362 | return FALSE;
363 | }
364 |
365 | //
366 | // Do the requested function.
367 | //
368 |
369 | switch (Function)
370 | {
371 | case DRIVER_FUNC_INSTALL:
372 |
373 | //
374 | // Install the driver service.
375 | //
376 |
377 | if (InstallDriver(schSCManager,
378 | DriverName,
379 | ServiceName))
380 | {
381 | //
382 | // Start the driver service (i.e. start the driver).
383 | //
384 |
385 | rCode = StartDriver(schSCManager, DriverName);
386 | }
387 | else
388 | {
389 | //
390 | // Indicate an error.
391 | //
392 |
393 | rCode = FALSE;
394 | }
395 |
396 | break;
397 |
398 | case DRIVER_FUNC_REMOVE:
399 |
400 | //
401 | // Stop the driver.
402 | //
403 |
404 | StopDriver(schSCManager, DriverName);
405 |
406 | //
407 | // Remove the driver service.
408 | //
409 |
410 | RemoveDriver(schSCManager, DriverName);
411 |
412 | //
413 | // Ignore all errors.
414 | //
415 |
416 | rCode = TRUE;
417 | break;
418 |
419 | default:
420 | printf("Unknown ManageDriver() function. \n");
421 |
422 | rCode = FALSE;
423 | break;
424 | }
425 |
426 | //
427 | // Close handle to service control manager.
428 | //
429 |
430 | if (schSCManager)
431 | {
432 | CloseServiceHandle(schSCManager);
433 | }
434 |
435 | return rCode;
436 | }
437 |
438 | BOOLEAN
439 | SetupDriverName(
440 | _Inout_updates_bytes_all_(BufferLength) PTCHAR DriverLocation,
441 | _In_ ULONG BufferLength
442 | )
443 | {
444 | HANDLE fileHandle;
445 | DWORD driverLocLen = 0;
446 |
447 | //
448 | // Get the current directory.
449 | //
450 |
451 | driverLocLen = GetCurrentDirectory(BufferLength,
452 | DriverLocation);
453 |
454 | if (driverLocLen == 0)
455 | {
456 | printf("GetCurrentDirectory failed! Error = %d \n", GetLastError());
457 |
458 | return FALSE;
459 | }
460 |
461 | //
462 | // Setup path name to driver file.
463 | //
464 |
465 | if (FAILED(StringCbCat(DriverLocation, BufferLength, TEXT("\\" DRIVER_NAME ".sys"))))
466 | {
467 | return FALSE;
468 | }
469 |
470 | //
471 | // Insure driver file is in the specified directory.
472 | //
473 |
474 | if ((fileHandle = CreateFile(DriverLocation,
475 | GENERIC_READ,
476 | 0,
477 | NULL,
478 | OPEN_EXISTING,
479 | FILE_ATTRIBUTE_NORMAL,
480 | NULL)) == INVALID_HANDLE_VALUE)
481 | {
482 | printf("%s.sys is not loaded.\n", DRIVER_NAME);
483 |
484 | //
485 | // Indicate failure.
486 | //
487 |
488 | return FALSE;
489 | }
490 |
491 | //
492 | // Close open file handle.
493 | //
494 |
495 | if (fileHandle)
496 | {
497 | CloseHandle(fileHandle);
498 | }
499 |
500 | //
501 | // Indicate success.
502 | //
503 |
504 | return TRUE;
505 | }
506 |
--------------------------------------------------------------------------------
/src/injldr/install.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #define DRIVER_FUNC_INSTALL 0x01
5 | #define DRIVER_FUNC_REMOVE 0x02
6 |
7 | #define DRIVER_NAME "injdrv"
8 |
9 | BOOLEAN
10 | ManageDriver(
11 | _In_ LPCTSTR DriverName,
12 | _In_ LPCTSTR ServiceName,
13 | _In_ USHORT Function
14 | );
15 |
16 | BOOLEAN
17 | SetupDriverName(
18 | _Inout_updates_bytes_all_(BufferLength) PTCHAR DriverLocation,
19 | _In_ ULONG BufferLength
20 | );
21 |
--------------------------------------------------------------------------------
/src/injldr/main.c:
--------------------------------------------------------------------------------
1 | #define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "install.h"
9 |
10 | //
11 | // GUID:
12 | // {a4b4ba50-a667-43f5-919b-1e52a6d69bd5}
13 | //
14 |
15 | GUID ProviderGuid = {
16 | 0xa4b4ba50, 0xa667, 0x43f5, { 0x91, 0x9b, 0x1e, 0x52, 0xa6, 0xd6, 0x9b, 0xd5 }
17 | };
18 |
19 | //
20 | // GUID:
21 | // {53d82d11-cede-4dff-8eb4-f06631800128}
22 | //
23 |
24 | GUID SessionGuid = {
25 | 0x53d82d11, 0xcede, 0x4dff, { 0x8e, 0xb4, 0xf0, 0x66, 0x31, 0x80, 0x1, 0x28 }
26 | };
27 |
28 | TCHAR SessionName[] = TEXT("InjSession");
29 |
30 | VOID
31 | WINAPI
32 | TraceEventCallback(
33 | _In_ PEVENT_RECORD EventRecord
34 | )
35 | {
36 | if (!EventRecord->UserData)
37 | {
38 | return;
39 | }
40 |
41 | //
42 | // TODO: Check that EventRecord contains only WCHAR string.
43 | //
44 |
45 | wprintf(L"[PID:%04X][TID:%04X] %s\n",
46 | EventRecord->EventHeader.ProcessId,
47 | EventRecord->EventHeader.ThreadId,
48 | (PWCHAR)EventRecord->UserData);
49 | }
50 |
51 | ULONG
52 | NTAPI
53 | TraceStart(
54 | VOID
55 | )
56 | {
57 | //
58 | // Start new trace session.
59 | // For an awesome blogpost on ETW API, see:
60 | // https://caseymuratori.com/blog_0025
61 | //
62 |
63 | ULONG ErrorCode;
64 |
65 | TRACEHANDLE TraceSessionHandle = INVALID_PROCESSTRACE_HANDLE;
66 |
67 | BYTE Buffer[sizeof(EVENT_TRACE_PROPERTIES) + 4096];
68 | RtlZeroMemory(Buffer, sizeof(Buffer));
69 |
70 | PEVENT_TRACE_PROPERTIES EventTraceProperties = (PEVENT_TRACE_PROPERTIES)Buffer;
71 | EventTraceProperties->Wnode.BufferSize = sizeof(Buffer);
72 | EventTraceProperties->Wnode.ClientContext = 1; // Use QueryPerformanceCounter, see MSDN
73 | EventTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
74 | EventTraceProperties->LogFileMode = PROCESS_TRACE_MODE_REAL_TIME;
75 | EventTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
76 |
77 | ErrorCode = StartTrace(&TraceSessionHandle, SessionName, EventTraceProperties);
78 | if (ErrorCode != ERROR_SUCCESS)
79 | {
80 | goto Exit;
81 | }
82 |
83 | //
84 | // Enable tracing of our provider.
85 | //
86 |
87 | ErrorCode = EnableTrace(TRUE, 0, 0, &ProviderGuid, TraceSessionHandle);
88 | if (ErrorCode != ERROR_SUCCESS)
89 | {
90 | goto Exit;
91 | }
92 |
93 | EVENT_TRACE_LOGFILE TraceLogfile = { 0 };
94 | TraceLogfile.LoggerName = SessionName;
95 | TraceLogfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME;
96 | TraceLogfile.EventRecordCallback = &TraceEventCallback;
97 |
98 | //
99 | // Open real-time tracing session.
100 | //
101 |
102 | TRACEHANDLE TraceHandle = OpenTrace(&TraceLogfile);
103 | if (TraceHandle == INVALID_PROCESSTRACE_HANDLE)
104 | {
105 | //
106 | // Synthetic error code.
107 | //
108 | ErrorCode = ERROR_FUNCTION_FAILED;
109 | goto Exit;
110 | }
111 |
112 | //
113 | // Process trace events. This call is blocking.
114 | //
115 |
116 | ErrorCode = ProcessTrace(&TraceHandle, 1, NULL, NULL);
117 |
118 | Exit:
119 | if (TraceHandle)
120 | {
121 | CloseTrace(TraceHandle);
122 | }
123 |
124 | if (TraceSessionHandle)
125 | {
126 | CloseTrace(TraceSessionHandle);
127 | }
128 |
129 | RtlZeroMemory(Buffer, sizeof(Buffer));
130 | EventTraceProperties->Wnode.BufferSize = sizeof(Buffer);
131 | StopTrace(0, SessionName, EventTraceProperties);
132 |
133 | if (ErrorCode != ERROR_SUCCESS)
134 | {
135 | printf("Error: %08x\n", ErrorCode);
136 | }
137 |
138 | return ErrorCode;
139 | }
140 |
141 | VOID
142 | NTAPI
143 | TraceStop(
144 | VOID
145 | )
146 | {
147 | BYTE Buffer[sizeof(EVENT_TRACE_PROPERTIES) + 4096];
148 | RtlZeroMemory(Buffer, sizeof(Buffer));
149 |
150 | PEVENT_TRACE_PROPERTIES EventTraceProperties = (PEVENT_TRACE_PROPERTIES)Buffer;
151 | EventTraceProperties->Wnode.BufferSize = sizeof(Buffer);
152 |
153 | StopTrace(0, SessionName, EventTraceProperties);
154 | }
155 |
156 | //////////////////////////////////////////////////////////////////////////
157 |
158 | BOOLEAN
159 | DoInstallUninstall(
160 | _In_ BOOLEAN Install
161 | )
162 | {
163 | TCHAR driverLocation[MAX_PATH] = { 0 };
164 |
165 | //
166 | // The driver is not started yet so let us install the driver.
167 | // First setup full path to driver name.
168 | //
169 |
170 | if (!SetupDriverName(driverLocation, sizeof(driverLocation)))
171 | {
172 | return FALSE;
173 | }
174 |
175 | if (Install)
176 | {
177 | if (!ManageDriver(TEXT(DRIVER_NAME),
178 | driverLocation,
179 | DRIVER_FUNC_INSTALL))
180 | {
181 | printf("Unable to install driver.\n");
182 |
183 | //
184 | // Error - remove driver.
185 | //
186 |
187 | ManageDriver(TEXT(DRIVER_NAME),
188 | driverLocation,
189 | DRIVER_FUNC_REMOVE);
190 |
191 | return FALSE;
192 | }
193 | }
194 | else
195 | {
196 | //
197 | // Ignore errors.
198 | //
199 |
200 | ManageDriver(TEXT(DRIVER_NAME),
201 | driverLocation,
202 | DRIVER_FUNC_REMOVE);
203 | }
204 |
205 | return TRUE;
206 | }
207 |
208 | BOOL
209 | WINAPI
210 | CtrlCHandlerRoutine(
211 | _In_ DWORD dwCtrlType
212 | )
213 | {
214 | if (dwCtrlType == CTRL_C_EVENT)
215 | {
216 | //
217 | // Ctrl+C was pressed, stop the trace session.
218 | //
219 | printf("Ctrl+C pressed, stopping trace session...\n");
220 |
221 | TraceStop();
222 | }
223 |
224 | return FALSE;
225 | }
226 |
227 | int main(int argc, char* argv[])
228 | {
229 | // LoadLibrary(TEXT("injdllx64.dll"));
230 | //
231 | // return 0;
232 | //
233 | SetConsoleCtrlHandler(&CtrlCHandlerRoutine, TRUE);
234 |
235 | //
236 | // Stop any previous trace session (if exists).
237 | //
238 |
239 | TraceStop();
240 |
241 | //
242 | // Parse command-line parameters.
243 | //
244 |
245 | if (argc == 2)
246 | {
247 | TCHAR DriverLocation[MAX_PATH];
248 | SetupDriverName(DriverLocation, sizeof(DriverLocation));
249 |
250 | if (!strcmp(argv[1], "-i"))
251 | {
252 | printf("Installing driver...\n");
253 |
254 | if (DoInstallUninstall(TRUE))
255 | {
256 | printf("Driver installed!\n");
257 | }
258 | else
259 | {
260 | printf("Error!\n");
261 | return EXIT_FAILURE;
262 | }
263 | }
264 | else if (!strcmp(argv[1], "-u"))
265 | {
266 | printf("Uninstalling driver...\n");
267 |
268 | DoInstallUninstall(FALSE);
269 |
270 | return EXIT_SUCCESS;
271 | }
272 | }
273 |
274 | printf("Starting tracing session...\n");
275 |
276 | ULONG ErrorCode = TraceStart();
277 |
278 | return ErrorCode == ERROR_SUCCESS
279 | ? EXIT_SUCCESS
280 | : EXIT_FAILURE;
281 | }
282 |
--------------------------------------------------------------------------------
/src/injlib/injlib.c:
--------------------------------------------------------------------------------
1 | #include "injlib.h"
2 |
3 | #include
4 | #include
5 |
6 | #if defined(_M_AMD64) || defined(_M_ARM64)
7 | # define INJ_CONFIG_SUPPORTS_WOW64
8 | #endif
9 |
10 | //////////////////////////////////////////////////////////////////////////
11 | // ke.h
12 | //////////////////////////////////////////////////////////////////////////
13 |
14 | typedef enum _KAPC_ENVIRONMENT
15 | {
16 | OriginalApcEnvironment,
17 | AttachedApcEnvironment,
18 | CurrentApcEnvironment,
19 | InsertApcEnvironment
20 | } KAPC_ENVIRONMENT;
21 |
22 | typedef
23 | VOID
24 | (NTAPI *PKNORMAL_ROUTINE)(
25 | _In_ PVOID NormalContext,
26 | _In_ PVOID SystemArgument1,
27 | _In_ PVOID SystemArgument2
28 | );
29 |
30 | typedef
31 | VOID
32 | (NTAPI *PKKERNEL_ROUTINE)(
33 | _In_ PKAPC Apc,
34 | _Inout_ PKNORMAL_ROUTINE* NormalRoutine,
35 | _Inout_ PVOID* NormalContext,
36 | _Inout_ PVOID* SystemArgument1,
37 | _Inout_ PVOID* SystemArgument2
38 | );
39 |
40 | typedef
41 | VOID
42 | (NTAPI *PKRUNDOWN_ROUTINE) (
43 | _In_ PKAPC Apc
44 | );
45 |
46 | NTKERNELAPI
47 | VOID
48 | NTAPI
49 | KeInitializeApc(
50 | _Out_ PRKAPC Apc,
51 | _In_ PETHREAD Thread,
52 | _In_ KAPC_ENVIRONMENT Environment,
53 | _In_ PKKERNEL_ROUTINE KernelRoutine,
54 | _In_opt_ PKRUNDOWN_ROUTINE RundownRoutine,
55 | _In_opt_ PKNORMAL_ROUTINE NormalRoutine,
56 | _In_opt_ KPROCESSOR_MODE ApcMode,
57 | _In_opt_ PVOID NormalContext
58 | );
59 |
60 | NTKERNELAPI
61 | BOOLEAN
62 | NTAPI
63 | KeInsertQueueApc(
64 | _Inout_ PRKAPC Apc,
65 | _In_opt_ PVOID SystemArgument1,
66 | _In_opt_ PVOID SystemArgument2,
67 | _In_ KPRIORITY Increment
68 | );
69 |
70 | NTKERNELAPI
71 | BOOLEAN
72 | NTAPI
73 | KeAlertThread(
74 | _Inout_ PKTHREAD Thread,
75 | _In_ KPROCESSOR_MODE AlertMode
76 | );
77 |
78 | NTKERNELAPI
79 | BOOLEAN
80 | NTAPI
81 | KeTestAlertThread(
82 | _In_ KPROCESSOR_MODE AlertMode
83 | );
84 |
85 | //////////////////////////////////////////////////////////////////////////
86 | // ps.h
87 | //////////////////////////////////////////////////////////////////////////
88 |
89 | NTKERNELAPI
90 | PVOID
91 | NTAPI
92 | PsGetProcessWow64Process(
93 | _In_ PEPROCESS Process
94 | );
95 |
96 | NTKERNELAPI
97 | PCHAR
98 | NTAPI
99 | PsGetProcessImageFileName(
100 | _In_ PEPROCESS Process
101 | );
102 |
103 | NTKERNELAPI
104 | BOOLEAN
105 | NTAPI
106 | PsIsProtectedProcess(
107 | _In_ PEPROCESS Process
108 | );
109 |
110 | NTKERNELAPI
111 | USHORT
112 | NTAPI
113 | PsWow64GetProcessMachine(
114 | _In_ PEPROCESS Process
115 | );
116 |
117 | //////////////////////////////////////////////////////////////////////////
118 | // ntrtl.h
119 | //////////////////////////////////////////////////////////////////////////
120 |
121 | #define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE (0x00000001)
122 | #define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING (0x00000002)
123 |
124 | NTSYSAPI
125 | NTSTATUS
126 | NTAPI
127 | RtlDuplicateUnicodeString(
128 | _In_ ULONG Flags,
129 | _In_ PUNICODE_STRING StringIn,
130 | _Out_ PUNICODE_STRING StringOut
131 | );
132 |
133 | NTSYSAPI
134 | PVOID
135 | NTAPI
136 | RtlImageDirectoryEntryToData(
137 | _In_ PVOID BaseOfImage,
138 | _In_ BOOLEAN MappedAsImage,
139 | _In_ USHORT DirectoryEntry,
140 | _Out_ PULONG Size
141 | );
142 |
143 | //////////////////////////////////////////////////////////////////////////
144 | // Definitions.
145 | //////////////////////////////////////////////////////////////////////////
146 |
147 | #define INJ_MEMORY_TAG ' jnI'
148 |
149 | //////////////////////////////////////////////////////////////////////////
150 | // Enumerations.
151 | //////////////////////////////////////////////////////////////////////////
152 |
153 | typedef enum _INJ_SYSTEM_DLL
154 | {
155 | INJ_NOTHING_LOADED = 0x0000,
156 | INJ_SYSARM32_NTDLL_LOADED = 0x0001,
157 | INJ_SYCHPE32_NTDLL_LOADED = 0x0002,
158 | INJ_SYSWOW64_NTDLL_LOADED = 0x0004,
159 | INJ_SYSTEM32_NTDLL_LOADED = 0x0008,
160 | INJ_SYSTEM32_WOW64_LOADED = 0x0010,
161 | INJ_SYSTEM32_WOW64WIN_LOADED = 0x0020,
162 | INJ_SYSTEM32_WOW64CPU_LOADED = 0x0040,
163 | INJ_SYSTEM32_WOWARMHW_LOADED = 0x0080,
164 | INJ_SYSTEM32_XTAJIT_LOADED = 0x0100,
165 | } INJ_SYSTEM_DLL;
166 |
167 | //////////////////////////////////////////////////////////////////////////
168 | // Structures.
169 | //////////////////////////////////////////////////////////////////////////
170 |
171 | typedef struct _INJ_SYSTEM_DLL_DESCRIPTOR
172 | {
173 | UNICODE_STRING DllPath;
174 | INJ_SYSTEM_DLL Flag;
175 | } INJ_SYSTEM_DLL_DESCRIPTOR, *PINJ_SYSTEM_DLL_DESCRIPTOR;
176 |
177 | typedef struct _INJ_THUNK
178 | {
179 | PVOID Buffer;
180 | USHORT Length;
181 | } INJ_THUNK, *PINJ_THUNK;
182 |
183 | //////////////////////////////////////////////////////////////////////////
184 | // Function prototypes.
185 | //////////////////////////////////////////////////////////////////////////
186 |
187 | NTSTATUS
188 | NTAPI
189 | InjpQueueApc(
190 | _In_ KPROCESSOR_MODE ApcMode,
191 | _In_ PKNORMAL_ROUTINE NormalRoutine,
192 | _In_ PVOID NormalContext,
193 | _In_ PVOID SystemArgument1,
194 | _In_ PVOID SystemArgument2
195 | );
196 |
197 | VOID
198 | NTAPI
199 | InjpInjectApcNormalRoutine(
200 | _In_ PVOID NormalContext,
201 | _In_ PVOID SystemArgument1,
202 | _In_ PVOID SystemArgument2
203 | );
204 |
205 | VOID
206 | NTAPI
207 | InjpInjectApcKernelRoutine(
208 | _In_ PKAPC Apc,
209 | _Inout_ PKNORMAL_ROUTINE* NormalRoutine,
210 | _Inout_ PVOID* NormalContext,
211 | _Inout_ PVOID* SystemArgument1,
212 | _Inout_ PVOID* SystemArgument2
213 | );
214 |
215 | //
216 | // reparse.c
217 | //
218 |
219 | NTSTATUS
220 | NTAPI
221 | SimRepInitialize(
222 | _In_ PDRIVER_OBJECT DriverObject,
223 | _In_ PUNICODE_STRING RegistryPath
224 | );
225 |
226 | //////////////////////////////////////////////////////////////////////////
227 | // Private constant variables.
228 | //////////////////////////////////////////////////////////////////////////
229 |
230 | ANSI_STRING LdrLoadDllRoutineName = RTL_CONSTANT_STRING("LdrLoadDll");
231 |
232 | //
233 | // Paths can have format "\Device\HarddiskVolume3\Windows\System32\ntdll.dll",
234 | // so only the end of the string is compared.
235 | //
236 |
237 | INJ_SYSTEM_DLL_DESCRIPTOR InjpSystemDlls[] = {
238 | { RTL_CONSTANT_STRING(L"\\SysArm32\\ntdll.dll"), INJ_SYSARM32_NTDLL_LOADED },
239 | { RTL_CONSTANT_STRING(L"\\SyChpe32\\ntdll.dll"), INJ_SYCHPE32_NTDLL_LOADED },
240 | { RTL_CONSTANT_STRING(L"\\SysWow64\\ntdll.dll"), INJ_SYSWOW64_NTDLL_LOADED },
241 | { RTL_CONSTANT_STRING(L"\\System32\\ntdll.dll"), INJ_SYSTEM32_NTDLL_LOADED },
242 | { RTL_CONSTANT_STRING(L"\\System32\\wow64.dll"), INJ_SYSTEM32_WOW64_LOADED },
243 | { RTL_CONSTANT_STRING(L"\\System32\\wow64win.dll"), INJ_SYSTEM32_WOW64WIN_LOADED },
244 | { RTL_CONSTANT_STRING(L"\\System32\\wow64cpu.dll"), INJ_SYSTEM32_WOW64CPU_LOADED },
245 | { RTL_CONSTANT_STRING(L"\\System32\\wowarmhw.dll"), INJ_SYSTEM32_WOWARMHW_LOADED },
246 | { RTL_CONSTANT_STRING(L"\\System32\\xtajit.dll"), INJ_SYSTEM32_XTAJIT_LOADED },
247 | };
248 |
249 | //
250 | // ;++
251 | // ;
252 | // ; VOID
253 | // ; NTAPI
254 | // ; ApcNormalRoutine(
255 | // ; _In_ PVOID NormalContext,
256 | // ; _In_ PVOID SystemArgument1,
257 | // ; _In_ PVOID SystemArgument2
258 | // ; )
259 | // ;
260 | // ; Routine Description:
261 | // ;
262 | // ; This routine loads DLL specified in the NormalContext.
263 | // ;
264 | // ; If native process is being injected, this function is called
265 | // ; from the ntdll.dll!KiUserApcDispatcher routine.
266 | // ;
267 | // ; If Wow64 process is being injected, the following code-flow
268 | // ; is responsible for reaching this function:
269 | // ;
270 | // ; - wow64.dll!Wow64ApcRoutine (set by PsWrapApcWow64Thread):
271 | // ; - Puts NormalRoutine, NormalContext, SystemArgument1 and
272 | // ; SystemArgument2 on the top of the stack, sets EIP to
273 | // ; KiUserApcDispatcher of Wow64 ntdll.dll.
274 | // ; - ntdll.dll!KiUserApcDispatcher (note this is Wow64 ntdll.dll)
275 | // ; - Pops NormalRoutine - our ApcNormalRoutine - from the stack
276 | // ; and calls it (note that NormalCountext, SystemArgument1 and
277 | // ; SystemArgument2 remain on the stack).
278 | // ;
279 | // ; The shellcode is equivalent to this code - regardless of the
280 | // ; architecture:
281 | // ;
282 | // ; VOID
283 | // ; NTAPI
284 | // ; ApcNormalRoutine(
285 | // ; _In_ PVOID NormalContext, // LdrLoadDll routine address
286 | // ; _In_ PVOID SystemArgument1, // DllPath
287 | // ; _In_ PVOID SystemArgument2 // DllPath length
288 | // ; )
289 | // ; {
290 | // ; UNICODE_STRING DllName;
291 | // ; PVOID BaseAddress;
292 | // ;
293 | // ; DllName.Length = (USHORT)SystemArgument2;
294 | // ; DllName.MaximumLength = (USHORT)SystemArgument2;
295 | // ; DllName.Buffer = (PWSTR) SystemArgument1;
296 | // ;
297 | // ; ((PLDRLOADDLL_ROUTINE)NormalContext)(0, 0, &DllName, &BaseAddress);
298 | // ; }
299 | // ;
300 | // ; // See: https://gcc.godbolt.org/z/1DDtuW
301 | // ;
302 | // ; Arguments:
303 | // ;
304 | // ; NormalContext - LdrLoadDll routine address.
305 | // ; SystemArgument1 - DLL path.
306 | // ; SystemArgument2 - Length of DLL path.
307 | // ;
308 | // ; Return Value:
309 | // ;
310 | // ; None.
311 | // ;
312 | // ;--
313 | //
314 |
315 | UCHAR InjpThunkX86[] = { //
316 | 0x83, 0xec, 0x08, // sub esp,0x8
317 | 0x0f, 0xb7, 0x44, 0x24, 0x14, // movzx eax,[esp + 0x14]
318 | 0x66, 0x89, 0x04, 0x24, // mov [esp],ax
319 | 0x66, 0x89, 0x44, 0x24, 0x02, // mov [esp + 0x2],ax
320 | 0x8b, 0x44, 0x24, 0x10, // mov eax,[esp + 0x10]
321 | 0x89, 0x44, 0x24, 0x04, // mov [esp + 0x4],eax
322 | 0x8d, 0x44, 0x24, 0x14, // lea eax,[esp + 0x14]
323 | 0x50, // push eax
324 | 0x8d, 0x44, 0x24, 0x04, // lea eax,[esp + 0x4]
325 | 0x50, // push eax
326 | 0x6a, 0x00, // push 0x0
327 | 0x6a, 0x00, // push 0x0
328 | 0xff, 0x54, 0x24, 0x1c, // call [esp + 0x1c]
329 | 0x83, 0xc4, 0x08, // add esp,0x8
330 | 0xc2, 0x0c, 0x00, // ret 0xc
331 | }; //
332 |
333 | UCHAR InjpThunkX64[] = { //
334 | 0x48, 0x83, 0xec, 0x38, // sub rsp,0x38
335 | 0x48, 0x89, 0xc8, // mov rax,rcx
336 | 0x66, 0x44, 0x89, 0x44, 0x24, 0x20, // mov [rsp+0x20],r8w
337 | 0x66, 0x44, 0x89, 0x44, 0x24, 0x22, // mov [rsp+0x22],r8w
338 | 0x4c, 0x8d, 0x4c, 0x24, 0x40, // lea r9,[rsp+0x40]
339 | 0x48, 0x89, 0x54, 0x24, 0x28, // mov [rsp+0x28],rdx
340 | 0x4c, 0x8d, 0x44, 0x24, 0x20, // lea r8,[rsp+0x20]
341 | 0x31, 0xd2, // xor edx,edx
342 | 0x31, 0xc9, // xor ecx,ecx
343 | 0xff, 0xd0, // call rax
344 | 0x48, 0x83, 0xc4, 0x38, // add rsp,0x38
345 | 0xc2, 0x00, 0x00, // ret 0x0
346 | }; //
347 |
348 | UCHAR InjpThunkARM32[] = { //
349 | 0x1f, 0xb5, // push {r0-r4,lr}
350 | 0xad, 0xf8, 0x08, 0x20, // strh r2,[sp,#8]
351 | 0xad, 0xf8, 0x0a, 0x20, // strh r2,[sp,#0xA]
352 | 0x03, 0x91, // str r1,[sp,#0xC]
353 | 0x02, 0xaa, // add r2,sp,#8
354 | 0x00, 0x21, // movs r1,#0
355 | 0x04, 0x46, // mov r4,r0
356 | 0x6b, 0x46, // mov r3,sp
357 | 0x00, 0x20, // movs r0,#0
358 | 0xa0, 0x47, // blx r4
359 | 0x1f, 0xbd, // pop {r0-r4,pc}
360 | }; //
361 |
362 | UCHAR InjpThunkARM64[] = { //
363 | 0xfe, 0x0f, 0x1f, 0xf8, // str lr,[sp,#-0x10]!
364 | 0xff, 0x83, 0x00, 0xd1, // sub sp,sp,#0x20
365 | 0xe9, 0x03, 0x00, 0xaa, // mov x9,x0
366 | 0xe2, 0x13, 0x00, 0x79, // strh w2,[sp,#8]
367 | 0x00, 0x00, 0x80, 0xd2, // mov x0,#0
368 | 0xe2, 0x17, 0x00, 0x79, // strh w2,[sp,#0xA]
369 | 0xe2, 0x23, 0x00, 0x91, // add x2,sp,#8
370 | 0xe1, 0x0b, 0x00, 0xf9, // str x1,[sp,#0x10]
371 | 0x01, 0x00, 0x80, 0xd2, // mov x1,#0
372 | 0xe3, 0x03, 0x00, 0x91, // mov x3,sp
373 | 0x20, 0x01, 0x3f, 0xd6, // blr x9
374 | 0xff, 0x83, 0x00, 0x91, // add sp,sp,#0x20
375 | 0xfe, 0x07, 0x41, 0xf8, // ldr lr,[sp],#0x10
376 | 0xc0, 0x03, 0x5f, 0xd6, // ret
377 | }; //
378 |
379 | //////////////////////////////////////////////////////////////////////////
380 | // Variables.
381 | //////////////////////////////////////////////////////////////////////////
382 |
383 | LIST_ENTRY InjInfoListHead;
384 |
385 | INJ_METHOD InjMethod;
386 |
387 | UNICODE_STRING InjDllPath[InjArchitectureMax];
388 |
389 | INJ_THUNK InjThunk[InjArchitectureMax] = {
390 | { InjpThunkX86, sizeof(InjpThunkX86) },
391 | { InjpThunkX64, sizeof(InjpThunkX64) },
392 | { InjpThunkARM32, sizeof(InjpThunkARM32) },
393 | { InjpThunkARM64, sizeof(InjpThunkARM64) },
394 | };
395 |
396 | BOOLEAN InjIsWindows7;
397 |
398 | //////////////////////////////////////////////////////////////////////////
399 | // Helper functions.
400 | //////////////////////////////////////////////////////////////////////////
401 |
402 | PVOID
403 | NTAPI
404 | RtlxFindExportedRoutineByName(
405 | _In_ PVOID DllBase,
406 | _In_ PANSI_STRING ExportName
407 | )
408 | {
409 | //
410 | // RtlFindExportedRoutineByName is not exported by ntoskrnl until Win10.
411 | // Following code is borrowed from ReactOS.
412 | //
413 |
414 | PULONG NameTable;
415 | PUSHORT OrdinalTable;
416 | PIMAGE_EXPORT_DIRECTORY ExportDirectory;
417 | LONG Low = 0, Mid = 0, High, Ret;
418 | USHORT Ordinal;
419 | PVOID Function;
420 | ULONG ExportSize;
421 | PULONG ExportTable;
422 |
423 | //
424 | // Get the export directory.
425 | //
426 |
427 | ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
428 | TRUE,
429 | IMAGE_DIRECTORY_ENTRY_EXPORT,
430 | &ExportSize);
431 |
432 | if (!ExportDirectory)
433 | {
434 | return NULL;
435 | }
436 |
437 | //
438 | // Setup name tables.
439 | //
440 |
441 | NameTable = (PULONG) ((ULONG_PTR)DllBase + ExportDirectory->AddressOfNames);
442 | OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase + ExportDirectory->AddressOfNameOrdinals);
443 |
444 | //
445 | // Do a binary search.
446 | //
447 |
448 | High = ExportDirectory->NumberOfNames - 1;
449 | while (High >= Low)
450 | {
451 | //
452 | // Get new middle value.
453 | //
454 |
455 | Mid = (Low + High) >> 1;
456 |
457 | //
458 | // Compare name.
459 | //
460 |
461 | Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
462 | if (Ret < 0)
463 | {
464 | //
465 | // Update high.
466 | //
467 | High = Mid - 1;
468 | }
469 | else if (Ret > 0)
470 | {
471 | //
472 | // Update low.
473 | //
474 | Low = Mid + 1;
475 | }
476 | else
477 | {
478 | //
479 | // We got it.
480 | //
481 | break;
482 | }
483 | }
484 |
485 | //
486 | // Check if we couldn't find it.
487 | //
488 |
489 | if (High < Low)
490 | {
491 | return NULL;
492 | }
493 |
494 | //
495 | // Otherwise, this is the ordinal.
496 | //
497 |
498 | Ordinal = OrdinalTable[Mid];
499 |
500 | //
501 | // Validate the ordinal.
502 | //
503 |
504 | if (Ordinal >= ExportDirectory->NumberOfFunctions)
505 | {
506 | return NULL;
507 | }
508 |
509 | //
510 | // Resolve the address and write it.
511 | //
512 |
513 | ExportTable = (PULONG)((ULONG_PTR)DllBase + ExportDirectory->AddressOfFunctions);
514 | Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
515 |
516 | //
517 | // We found it!
518 | //
519 |
520 | NT_ASSERT(
521 | (Function < (PVOID)ExportDirectory) ||
522 | (Function > (PVOID)((ULONG_PTR)ExportDirectory + ExportSize))
523 | );
524 |
525 | return Function;
526 | }
527 |
528 | BOOLEAN
529 | NTAPI
530 | RtlxSuffixUnicodeString(
531 | _In_ PUNICODE_STRING String1,
532 | _In_ PUNICODE_STRING String2,
533 | _In_ BOOLEAN CaseInSensitive
534 | )
535 | {
536 | //
537 | // RtlSuffixUnicodeString is not exported by ntoskrnl until Win10.
538 | //
539 |
540 | return String2->Length >= String1->Length &&
541 | RtlCompareUnicodeStrings(String2->Buffer + (String2->Length - String1->Length) / sizeof(WCHAR),
542 | String1->Length / sizeof(WCHAR),
543 | String1->Buffer,
544 | String1->Length / sizeof(WCHAR),
545 | CaseInSensitive) == 0;
546 |
547 | }
548 |
549 | //////////////////////////////////////////////////////////////////////////
550 | // Private functions.
551 | //////////////////////////////////////////////////////////////////////////
552 |
553 | NTSTATUS
554 | NTAPI
555 | InjpQueueApc(
556 | _In_ KPROCESSOR_MODE ApcMode,
557 | _In_ PKNORMAL_ROUTINE NormalRoutine,
558 | _In_ PVOID NormalContext,
559 | _In_ PVOID SystemArgument1,
560 | _In_ PVOID SystemArgument2
561 | )
562 | {
563 | //
564 | // Allocate memory for the KAPC structure.
565 | //
566 |
567 | PKAPC Apc = ExAllocatePoolWithTag(NonPagedPoolNx,
568 | sizeof(KAPC),
569 | INJ_MEMORY_TAG);
570 |
571 | if (!Apc)
572 | {
573 | return STATUS_INSUFFICIENT_RESOURCES;
574 | }
575 |
576 | //
577 | // Initialize and queue the APC.
578 | //
579 |
580 | KeInitializeApc(Apc, // Apc
581 | PsGetCurrentThread(), // Thread
582 | OriginalApcEnvironment, // Environment
583 | &InjpInjectApcKernelRoutine, // KernelRoutine
584 | NULL, // RundownRoutine
585 | NormalRoutine, // NormalRoutine
586 | ApcMode, // ApcMode
587 | NormalContext); // NormalContext
588 |
589 | BOOLEAN Inserted = KeInsertQueueApc(Apc, // Apc
590 | SystemArgument1, // SystemArgument1
591 | SystemArgument2, // SystemArgument2
592 | 0); // Increment
593 |
594 | if (!Inserted)
595 | {
596 | ExFreePoolWithTag(Apc, INJ_MEMORY_TAG);
597 | return STATUS_UNSUCCESSFUL;
598 | }
599 |
600 | return STATUS_SUCCESS;
601 | }
602 |
603 | VOID
604 | NTAPI
605 | InjpInjectApcNormalRoutine(
606 | _In_ PVOID NormalContext,
607 | _In_ PVOID SystemArgument1,
608 | _In_ PVOID SystemArgument2
609 | )
610 | {
611 | UNREFERENCED_PARAMETER(SystemArgument1);
612 | UNREFERENCED_PARAMETER(SystemArgument2);
613 |
614 | PINJ_INJECTION_INFO InjectionInfo = NormalContext;
615 | InjInject(InjectionInfo);
616 | }
617 |
618 | VOID
619 | NTAPI
620 | InjpInjectApcKernelRoutine(
621 | _In_ PKAPC Apc,
622 | _Inout_ PKNORMAL_ROUTINE* NormalRoutine,
623 | _Inout_ PVOID* NormalContext,
624 | _Inout_ PVOID* SystemArgument1,
625 | _Inout_ PVOID* SystemArgument2
626 | )
627 | {
628 | UNREFERENCED_PARAMETER(NormalRoutine);
629 | UNREFERENCED_PARAMETER(NormalContext);
630 | UNREFERENCED_PARAMETER(SystemArgument1);
631 | UNREFERENCED_PARAMETER(SystemArgument2);
632 |
633 | //
634 | // Common kernel routine for both user-mode and
635 | // kernel-mode APCs queued by the InjpQueueApc
636 | // function. Just release the memory of the APC
637 | // structure and return back.
638 | //
639 |
640 | ExFreePoolWithTag(Apc, INJ_MEMORY_TAG);
641 | }
642 |
643 | NTSTATUS
644 | NTAPI
645 | InjpInject(
646 | _In_ PINJ_INJECTION_INFO InjectionInfo,
647 | _In_ INJ_ARCHITECTURE Architecture,
648 | _In_ HANDLE SectionHandle,
649 | _In_ SIZE_T SectionSize
650 | )
651 | {
652 | NTSTATUS Status;
653 |
654 | //
655 | // First, map this section with read-write access.
656 | //
657 |
658 | PVOID SectionMemoryAddress = NULL;
659 | Status = ZwMapViewOfSection(SectionHandle,
660 | ZwCurrentProcess(),
661 | &SectionMemoryAddress,
662 | 0,
663 | SectionSize,
664 | NULL,
665 | &SectionSize,
666 | ViewUnmap,
667 | 0,
668 | PAGE_READWRITE);
669 |
670 | if (!NT_SUCCESS(Status))
671 | {
672 | goto Exit;
673 | }
674 |
675 | //
676 | // Code of the APC routine (ApcNormalRoutine defined in the
677 | // "shellcode" above) starts at the SectionMemoryAddress.
678 | // Copy the shellcode to the allocated memory.
679 | //
680 |
681 | PVOID ApcRoutineAddress = SectionMemoryAddress;
682 | RtlCopyMemory(ApcRoutineAddress,
683 | InjThunk[Architecture].Buffer,
684 | InjThunk[Architecture].Length);
685 |
686 | //
687 | // Fill the data of the ApcContext.
688 | //
689 |
690 | PWCHAR DllPath = (PWCHAR)((PUCHAR)SectionMemoryAddress + InjThunk[Architecture].Length);
691 | RtlCopyMemory(DllPath,
692 | InjDllPath[Architecture].Buffer,
693 | InjDllPath[Architecture].Length);
694 |
695 | //
696 | // Unmap the section and map it again, but now
697 | // with read-execute (no write) access.
698 | //
699 |
700 | ZwUnmapViewOfSection(ZwCurrentProcess(), SectionMemoryAddress);
701 |
702 | SectionMemoryAddress = NULL;
703 | Status = ZwMapViewOfSection(SectionHandle,
704 | ZwCurrentProcess(),
705 | &SectionMemoryAddress,
706 | 0,
707 | PAGE_SIZE,
708 | NULL,
709 | &SectionSize,
710 | ViewUnmap,
711 | 0,
712 | PAGE_EXECUTE_READ);
713 |
714 | if (!NT_SUCCESS(Status))
715 | {
716 | goto Exit;
717 | }
718 |
719 | //
720 | // Reassign remapped address.
721 | //
722 |
723 | ApcRoutineAddress = SectionMemoryAddress;
724 | DllPath = (PWCHAR)((PUCHAR)SectionMemoryAddress + InjThunk[Architecture].Length);
725 |
726 | PVOID ApcContext = (PVOID)InjectionInfo->LdrLoadDllRoutineAddress;
727 | PVOID ApcArgument1 = (PVOID)DllPath;
728 | PVOID ApcArgument2 = (PVOID)InjDllPath[Architecture].Length;
729 |
730 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
731 |
732 | if (PsGetProcessWow64Process(PsGetCurrentProcess()))
733 | {
734 | //
735 | // The ARM32 ntdll.dll uses "BLX" instruction for calling the ApcRoutine.
736 | // This instruction can change the processor state (between Thumb & ARM),
737 | // based on the LSB (least significant bit). If this bit is 0, the code
738 | // will run in the ARM instruction set. If this bit is 1, the code will
739 | // run in Thumb instruction set. Because Windows can run only in the Thumb
740 | // instruction set, we have to ensure this bit is set. Otherwise, Windows
741 | // would raise STATUS_ILLEGAL_INSTRUCTION upon execution of the ApcRoutine.
742 | //
743 |
744 | if (Architecture == InjArchitectureARM32)
745 | {
746 | ApcRoutineAddress = (PVOID)((ULONG_PTR)ApcRoutineAddress | 1);
747 | }
748 |
749 | //
750 | // PsWrapApcWow64Thread essentially assigns wow64.dll!Wow64ApcRoutine
751 | // to the NormalRoutine. This Wow64ApcRoutine (which is 64-bit code)
752 | // in turn calls KiUserApcDispatcher (in 32-bit ntdll.dll) which finally
753 | // calls our provided ApcRoutine.
754 | //
755 |
756 | PsWrapApcWow64Thread(&ApcContext, &ApcRoutineAddress);
757 | }
758 |
759 | #endif
760 |
761 | PKNORMAL_ROUTINE ApcRoutine = (PKNORMAL_ROUTINE)(ULONG_PTR)ApcRoutineAddress;
762 |
763 | Status = InjpQueueApc(UserMode,
764 | ApcRoutine,
765 | ApcContext,
766 | ApcArgument1,
767 | ApcArgument2);
768 |
769 | if (!NT_SUCCESS(Status))
770 | {
771 | //
772 | // If injection failed for some reason, unmap the section.
773 | //
774 |
775 | ZwUnmapViewOfSection(ZwCurrentProcess(), SectionMemoryAddress);
776 | }
777 |
778 | Exit:
779 | return Status;
780 | }
781 |
782 | NTSTATUS
783 | NTAPI
784 | InjpInjectX64NoThunk(
785 | _In_ PINJ_INJECTION_INFO InjectionInfo,
786 | _In_ INJ_ARCHITECTURE Architecture,
787 | _In_ HANDLE SectionHandle,
788 | _In_ SIZE_T SectionSize
789 | )
790 | {
791 | NT_ASSERT(InjectionInfo->LdrLoadDllRoutineAddress);
792 | NT_ASSERT(Architecture == InjArchitectureX64);
793 |
794 | UNREFERENCED_PARAMETER(Architecture);
795 |
796 | NTSTATUS Status;
797 |
798 | PVOID SectionMemoryAddress = NULL;
799 | Status = ZwMapViewOfSection(SectionHandle,
800 | ZwCurrentProcess(),
801 | &SectionMemoryAddress,
802 | 0,
803 | PAGE_SIZE,
804 | NULL,
805 | &SectionSize,
806 | ViewUnmap,
807 | 0,
808 | PAGE_READWRITE);
809 |
810 | if (!NT_SUCCESS(Status))
811 | {
812 | goto Exit;
813 | }
814 |
815 | //
816 | // Create the UNICODE_STRING structure and fill out the
817 | // full path of the DLL.
818 | //
819 |
820 | PUNICODE_STRING DllPath = (PUNICODE_STRING)(SectionMemoryAddress);
821 | PWCHAR DllPathBuffer = (PWCHAR)((PUCHAR)DllPath + sizeof(UNICODE_STRING));
822 |
823 | RtlCopyMemory(DllPathBuffer,
824 | InjDllPath[Architecture].Buffer,
825 | InjDllPath[Architecture].Length);
826 |
827 | RtlInitUnicodeString(DllPath, DllPathBuffer);
828 |
829 | Status = InjpQueueApc(UserMode,
830 | (PKNORMAL_ROUTINE)(ULONG_PTR)InjectionInfo->LdrLoadDllRoutineAddress,
831 | NULL, // Translates to 1st param. of LdrLoadDll (SearchPath)
832 | NULL, // Translates to 2nd param. of LdrLoadDll (DllCharacteristics)
833 | DllPath); // Translates to 3rd param. of LdrLoadDll (DllName)
834 |
835 | //
836 | // 4th param. of LdrLoadDll (BaseAddress) is actually an output parameter.
837 | //
838 | // When control is transferred to the KiUserApcDispatcher routine of the
839 | // 64-bit ntdll.dll, the RSP points to the CONTEXT structure which might
840 | // be eventually provided to the ZwContinue function (in case this APC
841 | // dispatch will be routed to the Wow64 subsystem).
842 | //
843 | // Also, the value of the RSP register is moved to the R9 register before
844 | // calling the KiUserCallForwarder function. The KiUserCallForwarder
845 | // function actually passes this value of the R9 register down to the
846 | // NormalRoutine as a "hidden 4th parameter".
847 | //
848 | // Because LdrLoadDll writes to the provided address, it'll actually
849 | // result in overwrite of the CONTEXT.P1Home field (the first field of
850 | // the CONTEXT structure).
851 | //
852 | // Luckily for us, this field is only used in the very early stage of
853 | // the APC dispatch and can be overwritten without causing any troubles.
854 | //
855 | // For excellent explanation, see:
856 | // https://www.sentinelone.com/blog/deep-hooks-monitoring-native-execution-wow64-applications-part-2
857 | //
858 |
859 | Exit:
860 | return Status;
861 | }
862 |
863 | //////////////////////////////////////////////////////////////////////////
864 | // Public functions.
865 | //////////////////////////////////////////////////////////////////////////
866 |
867 | NTSTATUS
868 | NTAPI
869 | InjInitialize(
870 | _In_ PDRIVER_OBJECT DriverObject,
871 | _In_ PUNICODE_STRING RegistryPath,
872 | _In_ PINJ_SETTINGS Settings
873 | )
874 | {
875 | NTSTATUS Status;
876 |
877 | //
878 | // Initialize injection info linked list.
879 | //
880 |
881 | InitializeListHead(&InjInfoListHead);
882 |
883 | ULONG Flags = RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
884 | | RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING;
885 |
886 | for (ULONG Architecture = 0; Architecture < InjArchitectureMax; Architecture += 1)
887 | {
888 | Status = RtlDuplicateUnicodeString(Flags,
889 | &Settings->DllPath[Architecture],
890 | &InjDllPath[Architecture]);
891 | if (!NT_SUCCESS(Status))
892 | {
893 | goto Error;
894 | }
895 | }
896 |
897 | //
898 | // Check if we're running on Windows 7.
899 | //
900 |
901 | RTL_OSVERSIONINFOW VersionInformation = { 0 };
902 | VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
903 | RtlGetVersion(&VersionInformation);
904 |
905 | if (VersionInformation.dwMajorVersion == 6 &&
906 | VersionInformation.dwMinorVersion == 1)
907 | {
908 | InjDbgPrint("[injlib]: Current system is Windows 7\n");
909 | InjIsWindows7 = TRUE;
910 | }
911 |
912 | //
913 | // Default setting of the injection of Wow64 processes.
914 | //
915 |
916 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
917 | InjMethod = Settings->Method;
918 |
919 | # if !defined(_M_AMD64)
920 | //
921 | // Thunkless method is available on x64.
922 | //
923 |
924 | if (InjMethod == InjMethodThunkless)
925 | {
926 | InjMethod = InjMethodThunk;
927 | }
928 | # endif
929 |
930 | #else
931 | InjMethod = InjMethodThunk;
932 | #endif
933 |
934 | InjDbgPrint("[injlib]: InjMethod: '%s'\n",
935 | InjMethod == InjMethodThunk ? "InjMethodThunk" :
936 | InjMethod == InjMethodThunkless ? "InjMethodThunkLess" :
937 | InjMethod == InjMethodWow64LogReparse ? "InjMethodWow64LogReparse" :
938 | "UNKNOWN"
939 | );
940 |
941 | if (InjMethod == InjMethodWow64LogReparse)
942 | {
943 | Status = SimRepInitialize(DriverObject, RegistryPath);
944 | }
945 |
946 | return Status;
947 |
948 | Error:
949 | InjDestroy();
950 | return Status;
951 | }
952 |
953 | VOID
954 | NTAPI
955 | InjDestroy(
956 | VOID
957 | )
958 | {
959 | //
960 | // Release memory of all injection-info entries.
961 | //
962 |
963 | PLIST_ENTRY NextEntry = InjInfoListHead.Flink;
964 |
965 | while (NextEntry != &InjInfoListHead)
966 | {
967 | PINJ_INJECTION_INFO InjectionInfo = CONTAINING_RECORD(NextEntry,
968 | INJ_INJECTION_INFO,
969 | ListEntry);
970 | NextEntry = NextEntry->Flink;
971 |
972 | ExFreePoolWithTag(InjectionInfo, INJ_MEMORY_TAG);
973 | }
974 |
975 | //
976 | // Release memory of all buffers.
977 | //
978 |
979 | for (ULONG Architecture = 0; Architecture < InjArchitectureMax; Architecture += 1)
980 | {
981 | RtlFreeUnicodeString(&InjDllPath[Architecture]);
982 | }
983 | }
984 |
985 | NTSTATUS
986 | NTAPI
987 | InjCreateInjectionInfo(
988 | _In_opt_ PINJ_INJECTION_INFO* InjectionInfo,
989 | _In_ HANDLE ProcessId
990 | )
991 | {
992 | PINJ_INJECTION_INFO CapturedInjectionInfo;
993 |
994 | if (InjectionInfo && *InjectionInfo)
995 | {
996 | CapturedInjectionInfo = *InjectionInfo;
997 | }
998 | else
999 | {
1000 | CapturedInjectionInfo = ExAllocatePoolWithTag(NonPagedPoolNx,
1001 | sizeof(INJ_INJECTION_INFO),
1002 | INJ_MEMORY_TAG);
1003 |
1004 | if (!CapturedInjectionInfo)
1005 | {
1006 | return STATUS_INSUFFICIENT_RESOURCES;
1007 | }
1008 |
1009 | if (InjectionInfo)
1010 | {
1011 | *InjectionInfo = CapturedInjectionInfo;
1012 | }
1013 | }
1014 |
1015 | RtlZeroMemory(CapturedInjectionInfo, sizeof(INJ_INJECTION_INFO));
1016 |
1017 | CapturedInjectionInfo->ProcessId = ProcessId;
1018 | CapturedInjectionInfo->ForceUserApc = TRUE;
1019 | CapturedInjectionInfo->Method = InjMethod;
1020 |
1021 | InsertTailList(&InjInfoListHead, &CapturedInjectionInfo->ListEntry);
1022 |
1023 | return STATUS_SUCCESS;
1024 | }
1025 |
1026 | VOID
1027 | NTAPI
1028 | InjRemoveInjectionInfo(
1029 | _In_ PINJ_INJECTION_INFO InjectionInfo,
1030 | _In_ BOOLEAN FreeMemory
1031 | )
1032 | {
1033 | RemoveEntryList(&InjectionInfo->ListEntry);
1034 |
1035 | if (FreeMemory)
1036 | {
1037 | ExFreePoolWithTag(InjectionInfo, INJ_MEMORY_TAG);
1038 | }
1039 | }
1040 |
1041 | VOID
1042 | NTAPI
1043 | InjRemoveInjectionInfoByProcessId(
1044 | _In_ HANDLE ProcessId,
1045 | _In_ BOOLEAN FreeMemory
1046 | )
1047 | {
1048 | PINJ_INJECTION_INFO InjectionInfo = InjFindInjectionInfo(ProcessId);
1049 |
1050 | if (InjectionInfo)
1051 | {
1052 | InjRemoveInjectionInfo(InjectionInfo, FreeMemory);
1053 | }
1054 | }
1055 |
1056 | PINJ_INJECTION_INFO
1057 | NTAPI
1058 | InjFindInjectionInfo(
1059 | _In_ HANDLE ProcessId
1060 | )
1061 | {
1062 | PLIST_ENTRY NextEntry = InjInfoListHead.Flink;
1063 |
1064 | while (NextEntry != &InjInfoListHead)
1065 | {
1066 | PINJ_INJECTION_INFO InjectionInfo = CONTAINING_RECORD(NextEntry,
1067 | INJ_INJECTION_INFO,
1068 | ListEntry);
1069 |
1070 | if (InjectionInfo->ProcessId == ProcessId)
1071 | {
1072 | return InjectionInfo;
1073 | }
1074 |
1075 | NextEntry = NextEntry->Flink;
1076 | }
1077 |
1078 | return NULL;
1079 | }
1080 |
1081 | BOOLEAN
1082 | NTAPI
1083 | InjCanInject(
1084 | _In_ PINJ_INJECTION_INFO InjectionInfo
1085 | )
1086 | {
1087 | //
1088 | // DLLs that need to be loaded in the native process
1089 | // (i.e.: x64 process on x64 Windows, x86 process on
1090 | // x86 Windows) before we can safely load our DLL.
1091 | //
1092 |
1093 | ULONG RequiredDlls = INJ_SYSTEM32_NTDLL_LOADED;
1094 |
1095 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
1096 |
1097 | if (PsGetProcessWow64Process(PsGetCurrentProcess()))
1098 | {
1099 | //
1100 | // DLLs that need to be loaded in the Wow64 process
1101 | // before we can safely load our DLL.
1102 | //
1103 |
1104 | RequiredDlls |= INJ_SYSTEM32_NTDLL_LOADED;
1105 | RequiredDlls |= INJ_SYSTEM32_WOW64_LOADED;
1106 | RequiredDlls |= INJ_SYSTEM32_WOW64WIN_LOADED;
1107 |
1108 | # if defined (_M_AMD64)
1109 |
1110 | RequiredDlls |= INJ_SYSTEM32_WOW64CPU_LOADED;
1111 | RequiredDlls |= INJ_SYSWOW64_NTDLL_LOADED;
1112 |
1113 | # elif defined (_M_ARM64)
1114 |
1115 | switch (PsWow64GetProcessMachine(PsGetCurrentProcess()))
1116 | {
1117 | case IMAGE_FILE_MACHINE_I386:
1118 |
1119 | //
1120 | // Emulated x86 processes can load either SyCHPE32\ntdll.dll or
1121 | // SysWOW64\ntdll.dll - depending on whether "hybrid execution
1122 | // mode" is enabled or disabled.
1123 | //
1124 | // PsWow64GetProcessNtdllType(Process) can provide this information,
1125 | // by returning EPROCESS->Wow64Process.NtdllType. Unfortunatelly,
1126 | // that function is not exported and EPROCESS is not documented.
1127 | //
1128 | // The solution here is to pick the Wow64 NTDLL which is already
1129 | // loaded and set it as "required".
1130 | //
1131 |
1132 | RequiredDlls |= InjectionInfo->LoadedDlls & (
1133 | INJ_SYSWOW64_NTDLL_LOADED |
1134 | INJ_SYCHPE32_NTDLL_LOADED
1135 | );
1136 | RequiredDlls |= INJ_SYSTEM32_XTAJIT_LOADED;
1137 | break;
1138 |
1139 | case IMAGE_FILE_MACHINE_ARMNT:
1140 | RequiredDlls |= INJ_SYSARM32_NTDLL_LOADED;
1141 | RequiredDlls |= INJ_SYSTEM32_WOWARMHW_LOADED;
1142 | break;
1143 |
1144 | case IMAGE_FILE_MACHINE_ARM64:
1145 | break;
1146 | }
1147 |
1148 | # endif
1149 |
1150 | }
1151 |
1152 | #endif
1153 |
1154 | return (InjectionInfo->LoadedDlls & RequiredDlls) == RequiredDlls;
1155 | }
1156 |
1157 | NTSTATUS
1158 | NTAPI
1159 | InjInject(
1160 | _In_ PINJ_INJECTION_INFO InjectionInfo
1161 | )
1162 | {
1163 | NTSTATUS Status;
1164 |
1165 | //
1166 | // Create memory space for injection-specific data,
1167 | // such as path to the to-be-injected DLL. Memory
1168 | // of this section will be eventually mapped to the
1169 | // injected process.
1170 | //
1171 | // Note that this memory is created using sections
1172 | // instead of ZwAllocateVirtualMemory, mainly because
1173 | // function ZwProtectVirtualMemory is not exported
1174 | // by ntoskrnl.exe until Windows 8.1. In case of
1175 | // sections, the effect of memory protection change
1176 | // is achieved by remaping the section with different
1177 | // protection type.
1178 | //
1179 |
1180 | OBJECT_ATTRIBUTES ObjectAttributes;
1181 | InitializeObjectAttributes(&ObjectAttributes,
1182 | NULL,
1183 | OBJ_KERNEL_HANDLE,
1184 | NULL,
1185 | NULL);
1186 |
1187 | HANDLE SectionHandle;
1188 | SIZE_T SectionSize = PAGE_SIZE;
1189 | LARGE_INTEGER MaximumSize;
1190 | MaximumSize.QuadPart = SectionSize;
1191 | Status = ZwCreateSection(&SectionHandle,
1192 | GENERIC_READ | GENERIC_WRITE,
1193 | &ObjectAttributes,
1194 | &MaximumSize,
1195 | PAGE_EXECUTE_READWRITE,
1196 | SEC_COMMIT,
1197 | NULL);
1198 |
1199 | if (!NT_SUCCESS(Status))
1200 | {
1201 | return Status;
1202 | }
1203 |
1204 | INJ_ARCHITECTURE Architecture = InjArchitectureMax;
1205 |
1206 | if (InjectionInfo->Method == InjMethodThunk ||
1207 | InjectionInfo->Method == InjMethodWow64LogReparse)
1208 | {
1209 | #if defined(_M_IX86)
1210 |
1211 | Architecture = InjArchitectureX86;
1212 |
1213 | #elif defined(_M_AMD64)
1214 |
1215 | Architecture = PsGetProcessWow64Process(PsGetCurrentProcess())
1216 | ? InjArchitectureX86
1217 | : InjArchitectureX64;
1218 |
1219 | #elif defined(_M_ARM64)
1220 |
1221 | switch (PsWow64GetProcessMachine(PsGetCurrentProcess()))
1222 | {
1223 | case IMAGE_FILE_MACHINE_I386:
1224 | Architecture = InjArchitectureX86;
1225 | break;
1226 |
1227 | case IMAGE_FILE_MACHINE_ARMNT:
1228 | Architecture = InjArchitectureARM32;
1229 | break;
1230 |
1231 | case IMAGE_FILE_MACHINE_ARM64:
1232 | Architecture = InjArchitectureARM64;
1233 | break;
1234 | }
1235 |
1236 | #endif
1237 |
1238 | NT_ASSERT(Architecture != InjArchitectureMax);
1239 |
1240 | InjpInject(InjectionInfo,
1241 | Architecture,
1242 | SectionHandle,
1243 | SectionSize);
1244 | }
1245 | #if defined(_M_AMD64)
1246 | else if (InjectionInfo->Method == InjMethodThunkless)
1247 | {
1248 | Architecture = InjArchitectureX64;
1249 |
1250 | InjpInjectX64NoThunk(InjectionInfo,
1251 | Architecture,
1252 | SectionHandle,
1253 | SectionSize);
1254 | }
1255 | #endif
1256 |
1257 | ZwClose(SectionHandle);
1258 |
1259 | if (NT_SUCCESS(Status) && InjectionInfo->ForceUserApc)
1260 | {
1261 | //
1262 | // Sets CurrentThread->ApcState.UserApcPending to TRUE.
1263 | // This causes the queued user APC to be triggered immediately
1264 | // on next transition of this thread to the user-mode.
1265 | //
1266 |
1267 | KeTestAlertThread(UserMode);
1268 | }
1269 |
1270 | return Status;
1271 | }
1272 |
1273 | //////////////////////////////////////////////////////////////////////////
1274 | // Notify routines.
1275 | //////////////////////////////////////////////////////////////////////////
1276 |
1277 | VOID
1278 | NTAPI
1279 | InjCreateProcessNotifyRoutineEx(
1280 | _Inout_ PEPROCESS Process,
1281 | _In_ HANDLE ProcessId,
1282 | _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
1283 | )
1284 | {
1285 | UNREFERENCED_PARAMETER(Process);
1286 |
1287 | if (CreateInfo)
1288 | {
1289 | InjCreateInjectionInfo(NULL, ProcessId);
1290 | }
1291 | else
1292 | {
1293 | InjRemoveInjectionInfoByProcessId(ProcessId, TRUE);
1294 | }
1295 | }
1296 |
1297 | VOID
1298 | NTAPI
1299 | InjLoadImageNotifyRoutine(
1300 | _In_opt_ PUNICODE_STRING FullImageName,
1301 | _In_ HANDLE ProcessId,
1302 | _In_ PIMAGE_INFO ImageInfo
1303 | )
1304 | {
1305 | //
1306 | // Check if current process is injected.
1307 | //
1308 |
1309 | PINJ_INJECTION_INFO InjectionInfo = InjFindInjectionInfo(ProcessId);
1310 |
1311 | if (!InjectionInfo || InjectionInfo->IsInjected)
1312 | {
1313 | return;
1314 | }
1315 |
1316 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
1317 | //
1318 | // If reparse-injection is enabled and this process is
1319 | // Wow64 process, then do not track load-images.
1320 | //
1321 |
1322 | if (InjectionInfo->Method == InjMethodWow64LogReparse &&
1323 | PsGetProcessWow64Process(PsGetCurrentProcess()))
1324 | {
1325 | return;
1326 | }
1327 | #endif
1328 |
1329 | if (PsIsProtectedProcess(PsGetCurrentProcess()))
1330 | {
1331 | //
1332 | // Protected processes throw code-integrity error when
1333 | // they are injected. Signing policy can be changed, but
1334 | // it requires hacking with lots of internal and Windows-
1335 | // version-specific structures. Simly don't inject such
1336 | // processes.
1337 | //
1338 | // See Blackbone project (https://github.com/DarthTon/Blackbone)
1339 | // if you're interested how protection can be temporarily
1340 | // disabled on such processes. (Look for BBSetProtection).
1341 | //
1342 |
1343 | InjDbgPrint("[injlib]: Ignoring protected process (PID: %u, Name: '%s')\n",
1344 | (ULONG)(ULONG_PTR)ProcessId,
1345 | PsGetProcessImageFileName(PsGetCurrentProcess()));
1346 |
1347 | InjRemoveInjectionInfoByProcessId(ProcessId, TRUE);
1348 |
1349 | return;
1350 | }
1351 |
1352 | if (!InjCanInject(InjectionInfo))
1353 | {
1354 | //
1355 | // This process is in early stage - important DLLs (such as
1356 | // ntdll.dll - or wow64.dll in case of Wow64 process) aren't
1357 | // properly initialized yet. We can't inject the DLL until
1358 | // they are.
1359 | //
1360 | // Check if any of the system DLLs we're interested in is being
1361 | // currently loaded - if so, mark that information down into the
1362 | // LoadedDlls field.
1363 | //
1364 |
1365 | for (ULONG Index = 0; Index < RTL_NUMBER_OF(InjpSystemDlls); Index += 1)
1366 | {
1367 | PUNICODE_STRING SystemDllPath = &InjpSystemDlls[Index].DllPath;
1368 |
1369 | if (RtlxSuffixUnicodeString(SystemDllPath, FullImageName, TRUE))
1370 | {
1371 | PVOID LdrLoadDllRoutineAddress = RtlxFindExportedRoutineByName(ImageInfo->ImageBase,
1372 | &LdrLoadDllRoutineName);
1373 |
1374 | ULONG DllFlag = InjpSystemDlls[Index].Flag;
1375 | InjectionInfo->LoadedDlls |= DllFlag;
1376 |
1377 | switch (DllFlag)
1378 | {
1379 | //
1380 | // In case of "thunk method", capture address of the LdrLoadDll
1381 | // routine from the ntdll.dll (which is of the same architecture
1382 | // as the process).
1383 | //
1384 |
1385 | case INJ_SYSARM32_NTDLL_LOADED:
1386 | case INJ_SYCHPE32_NTDLL_LOADED:
1387 | case INJ_SYSWOW64_NTDLL_LOADED:
1388 | if (InjectionInfo->Method != InjMethodThunkless)
1389 | {
1390 | InjectionInfo->LdrLoadDllRoutineAddress = LdrLoadDllRoutineAddress;
1391 | }
1392 | break;
1393 |
1394 | //
1395 | // For "thunkless method", capture address of the LdrLoadDll
1396 | // routine from the native ntdll.dll.
1397 | //
1398 |
1399 | case INJ_SYSTEM32_NTDLL_LOADED:
1400 | InjectionInfo->LdrLoadDllRoutineAddress = LdrLoadDllRoutineAddress;
1401 | break;
1402 |
1403 | default:
1404 | break;
1405 | }
1406 |
1407 | //
1408 | // Break the for-loop.
1409 | //
1410 |
1411 | break;
1412 | }
1413 | }
1414 | }
1415 | else
1416 | {
1417 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
1418 |
1419 | if (InjIsWindows7 &&
1420 | InjectionInfo->Method == InjMethodThunk &&
1421 | PsGetProcessWow64Process(PsGetCurrentProcess()))
1422 | {
1423 | //
1424 | // On Windows 7, if we're injecting DLL into Wow64 process using
1425 | // the "thunk method", we have additionaly postpone the load after
1426 | // these system DLLs.
1427 | //
1428 | // This is because on Windows 7, these DLLs are loaded as part of
1429 | // the wow64!ProcessInit routine, therefore the Wow64 subsystem
1430 | // is not fully initialized to execute our injected Wow64ApcRoutine.
1431 | //
1432 |
1433 | UNICODE_STRING System32Kernel32Path = RTL_CONSTANT_STRING(L"\\System32\\kernel32.dll");
1434 | UNICODE_STRING SysWOW64Kernel32Path = RTL_CONSTANT_STRING(L"\\SysWOW64\\kernel32.dll");
1435 | UNICODE_STRING System32User32Path = RTL_CONSTANT_STRING(L"\\System32\\user32.dll");
1436 | UNICODE_STRING SysWOW64User32Path = RTL_CONSTANT_STRING(L"\\SysWOW64\\user32.dll");
1437 |
1438 | if (RtlxSuffixUnicodeString(&System32Kernel32Path, FullImageName, TRUE) ||
1439 | RtlxSuffixUnicodeString(&SysWOW64Kernel32Path, FullImageName, TRUE) ||
1440 | RtlxSuffixUnicodeString(&System32User32Path, FullImageName, TRUE) ||
1441 | RtlxSuffixUnicodeString(&SysWOW64User32Path, FullImageName, TRUE))
1442 | {
1443 | InjDbgPrint("[injlib]: Postponing injection (%wZ)\n", FullImageName);
1444 | return;
1445 | }
1446 | }
1447 |
1448 | #endif
1449 |
1450 | //
1451 | // All necessary DLLs are loaded - perform the injection.
1452 | //
1453 | // Note that injection is done via kernel-mode APC, because
1454 | // InjInject calls ZwMapViewOfSection and MapViewOfSection
1455 | // might be already on the callstack. Because MapViewOfSection
1456 | // locks the EPROCESS->AddressCreationLock, we would be risking
1457 | // deadlock by calling InjInject directly.
1458 | //
1459 |
1460 | #if defined(INJ_CONFIG_SUPPORTS_WOW64)
1461 | InjDbgPrint("[injlib]: Injecting (PID: %u, Wow64: %s, Name: '%s')\n",
1462 | (ULONG)(ULONG_PTR)ProcessId,
1463 | PsGetProcessWow64Process(PsGetCurrentProcess()) ? "TRUE" : "FALSE",
1464 | PsGetProcessImageFileName(PsGetCurrentProcess()));
1465 | #else
1466 | InjDbgPrint("[injlib]: Injecting (PID: %u, Name: '%s')\n",
1467 | (ULONG)(ULONG_PTR)ProcessId,
1468 | PsGetProcessImageFileName(PsGetCurrentProcess()));
1469 |
1470 | #endif
1471 |
1472 | InjpQueueApc(KernelMode,
1473 | &InjpInjectApcNormalRoutine,
1474 | InjectionInfo,
1475 | NULL,
1476 | NULL);
1477 |
1478 | //
1479 | // Mark that this process is injected.
1480 | //
1481 |
1482 | InjectionInfo->IsInjected = TRUE;
1483 | }
1484 | }
1485 |
--------------------------------------------------------------------------------
/src/injlib/injlib.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | //////////////////////////////////////////////////////////////////////////
9 | // Definitions.
10 | //////////////////////////////////////////////////////////////////////////
11 |
12 | #if DBG
13 | # define InjDbgPrint(Format, ...) \
14 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, \
15 | DPFLTR_ERROR_LEVEL, \
16 | Format, \
17 | __VA_ARGS__)
18 | #else
19 | # define InjDbgPrint(Format, ...)
20 | #endif
21 |
22 | //////////////////////////////////////////////////////////////////////////
23 | // Enumerations.
24 | //////////////////////////////////////////////////////////////////////////
25 |
26 | typedef enum _INJ_ARCHITECTURE
27 | {
28 | InjArchitectureX86,
29 | InjArchitectureX64,
30 | InjArchitectureARM32,
31 | InjArchitectureARM64,
32 | InjArchitectureMax,
33 |
34 | #if defined(_M_IX86)
35 | InjArchitectureNative = InjArchitectureX86
36 | #elif defined (_M_AMD64)
37 | InjArchitectureNative = InjArchitectureX64
38 | #elif defined (_M_ARM64)
39 | InjArchitectureNative = InjArchitectureARM64
40 | #endif
41 | } INJ_ARCHITECTURE;
42 |
43 | typedef enum _INJ_METHOD
44 | {
45 | //
46 | // Inject process by executing short "shellcode" which
47 | // calls LdrLoadDll.
48 | // This method always loads DLL of the same architecture
49 | // as the process.
50 | //
51 |
52 | InjMethodThunk,
53 |
54 | //
55 | // Inject process by directly setting LdrLoadDll as the
56 | // user-mode APC routine.
57 | // This method always loads x64 DLL into the process.
58 | //
59 | // N.B. Available only on x64.
60 | //
61 |
62 | InjMethodThunkless,
63 |
64 | //
65 | // Inject Wow64 process by redirecting path of the "wow64log.dll"
66 | // to the path of the "injdll". Native processes are injected
67 | // as if the "thunk method" was selected (InjMethodThunk).
68 | //
69 | // This method always loads DLL of the same architecture
70 | // as the OS into the process.
71 | //
72 |
73 | InjMethodWow64LogReparse,
74 | } INJ_METHOD;
75 |
76 | //////////////////////////////////////////////////////////////////////////
77 | // Structures.
78 | //////////////////////////////////////////////////////////////////////////
79 |
80 | typedef struct _INJ_SETTINGS
81 | {
82 | //
83 | // Paths to the inject DLLs for each architecture.
84 | // Unsupported architectures (either by OS or the
85 | // method) can have empty string.
86 | //
87 |
88 | UNICODE_STRING DllPath[InjArchitectureMax];
89 |
90 | //
91 | // Injection method.
92 | //
93 |
94 | INJ_METHOD Method;
95 | } INJ_SETTINGS, *PINJ_SETTINGS;
96 |
97 | typedef struct _INJ_INJECTION_INFO
98 | {
99 | LIST_ENTRY ListEntry;
100 |
101 | //
102 | // Process ID.
103 | //
104 |
105 | HANDLE ProcessId;
106 |
107 | //
108 | // Combination of INJ_SYSTEM_DLL flags indicating
109 | // which DLLs has been already loaded into this
110 | // process.
111 | //
112 |
113 | ULONG LoadedDlls;
114 |
115 | //
116 | // If true, the process has been already injected.
117 | //
118 |
119 | BOOLEAN IsInjected;
120 |
121 | //
122 | // If true, trigger of the queued user APC will be
123 | // immediately forced upon next kernel->user transition.
124 | //
125 |
126 | BOOLEAN ForceUserApc;
127 |
128 | //
129 | // Address of LdrLoadDll routine within ntdll.dll
130 | // (which ntdll.dll is selected is based on the INJ_METHOD).
131 | //
132 |
133 | PVOID LdrLoadDllRoutineAddress;
134 |
135 | //
136 | // Injection method.
137 | //
138 |
139 | INJ_METHOD Method;
140 | } INJ_INJECTION_INFO, *PINJ_INJECTION_INFO;
141 |
142 | //////////////////////////////////////////////////////////////////////////
143 | // Public functions.
144 | //////////////////////////////////////////////////////////////////////////
145 |
146 | NTSTATUS
147 | NTAPI
148 | InjInitialize(
149 | _In_ PDRIVER_OBJECT DriverObject,
150 | _In_ PUNICODE_STRING RegistryPath,
151 | _In_ PINJ_SETTINGS Settings
152 | );
153 |
154 | VOID
155 | NTAPI
156 | InjDestroy(
157 | VOID
158 | );
159 |
160 | NTSTATUS
161 | NTAPI
162 | InjCreateInjectionInfo(
163 | _In_opt_ PINJ_INJECTION_INFO* InjectionInfo,
164 | _In_ HANDLE ProcessId
165 | );
166 |
167 | VOID
168 | NTAPI
169 | InjRemoveInjectionInfo(
170 | _In_ PINJ_INJECTION_INFO InjectionInfo,
171 | _In_ BOOLEAN FreeMemory
172 | );
173 |
174 | VOID
175 | NTAPI
176 | InjRemoveInjectionInfoByProcessId(
177 | _In_ HANDLE ProcessId,
178 | _In_ BOOLEAN FreeMemory
179 | );
180 |
181 | PINJ_INJECTION_INFO
182 | NTAPI
183 | InjFindInjectionInfo(
184 | _In_ HANDLE ProcessId
185 | );
186 |
187 | BOOLEAN
188 | NTAPI
189 | InjCanInject(
190 | _In_ PINJ_INJECTION_INFO InjectionInfo
191 | );
192 |
193 | NTSTATUS
194 | NTAPI
195 | InjInject(
196 | _In_ PINJ_INJECTION_INFO InjectionInfo
197 | );
198 |
199 | //////////////////////////////////////////////////////////////////////////
200 | // Notify routines.
201 | //////////////////////////////////////////////////////////////////////////
202 |
203 | VOID
204 | NTAPI
205 | InjCreateProcessNotifyRoutineEx(
206 | _Inout_ PEPROCESS Process,
207 | _In_ HANDLE ProcessId,
208 | _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
209 | );
210 |
211 | VOID
212 | NTAPI
213 | InjLoadImageNotifyRoutine(
214 | _In_opt_ PUNICODE_STRING FullImageName,
215 | _In_ HANDLE ProcessId,
216 | _In_ PIMAGE_INFO ImageInfo
217 | );
218 |
219 | #ifdef __cplusplus
220 | }
221 | #endif
222 |
--------------------------------------------------------------------------------
/src/injlib/injlib.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | ARM64
8 |
9 |
10 | Debug
11 | Win32
12 |
13 |
14 | Release
15 | ARM64
16 |
17 |
18 | Release
19 | Win32
20 |
21 |
22 | Debug
23 | x64
24 |
25 |
26 | Release
27 | x64
28 |
29 |
30 |
31 |
32 | injlib
33 | {E2ABAE21-2862-4356-BA49-28C060878D76}
34 | {1bc93793-694f-48fe-9372-81e2b05556fd}
35 | v4.5
36 | 12.0
37 | Debug
38 | Win32
39 | $(LatestTargetPlatformVersion)
40 |
41 |
42 |
43 |
44 |
45 | WindowsKernelModeDriver10.0
46 | StaticLibrary
47 | WDM
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | DbgengKernelDebugger
56 | $(ProjectDir);$(VC_IncludePath);$(IncludePath);$(KMDF_INC_PATH)$(KMDF_VER_PATH)
57 | $(SolutionDir)bin\$(PlatformShortName)\$(Configuration)\
58 | $(SolutionDir)bin\obj\$(PlatformShortName)\$(Configuration)\$(ProjectName)\
59 | false
60 | true
61 |
62 |
63 |
64 | 4201;4748;%(DisableSpecificWarnings)
65 |
66 | stdcpplatest
67 | $(IntDir)%(RelativeDir)%(Filename)%(Extension).obj
68 | true
69 | false
70 |
71 |
72 | DebugFull
73 | /INTEGRITYCHECK %(AdditionalOptions)
74 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
75 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
76 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
77 |
78 |
79 | true
80 |
81 |
82 |
83 |
84 | true
85 |
86 |
87 |
88 |
89 |
90 | false
91 |
92 |
93 |
94 | AnySuitable
95 | true
96 |
97 |
98 | UseLinkTimeCodeGeneration
99 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
100 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
101 | FltMgr.lib;%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib
102 |
103 |
104 | true
105 |
106 |
107 | true
108 |
109 |
110 | true
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/injlib/injlib.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 |
26 |
27 | Header Files
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/injlib/reparse.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "injlib.h"
4 |
5 | //
6 | // Code taken from the Windows-driver-samples github repository.
7 | // https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/miniFilter/simrep/simrep.c
8 | //
9 |
10 | //////////////////////////////////////////////////////////////////////////
11 | // Definitions.
12 | //////////////////////////////////////////////////////////////////////////
13 |
14 | #define SIMREP_STRING_TAG 'tSpR'
15 |
16 | #if DBG
17 |
18 | #define DEBUG_TRACE_ERROR 0x00000001 // Errors - whenever we return a failure code
19 | #define DEBUG_TRACE_LOAD_UNLOAD 0x00000002 // Loading/unloading of the filter
20 | #define DEBUG_TRACE_INSTANCES 0x00000004 // Attach / detach of instances
21 |
22 | #define DEBUG_TRACE_REPARSE_OPERATIONS 0x00000008 // Operations that are performed to determine if we should return STATUS_REPARSE
23 | #define DEBUG_TRACE_REPARSED_OPERATIONS 0x00000010 // Operations that return STATUS_REPARSE
24 | #define DEBUG_TRACE_REPARSED_REISSUE 0X00000020 // Operations that need to be reissued with an IRP.
25 |
26 | #define DEBUG_TRACE_NAME_OPERATIONS 0x00000040 // Operations involving name provider callbacks
27 |
28 | #define DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS 0x00000080 // Operations involving rename or hardlink redirection
29 |
30 | #define DEBUG_TRACE_ALL_IO 0x00000100 // All IO operations tracked by this filter
31 |
32 | #define DEBUG_TRACE_ALL 0xFFFFFFFF // All flags
33 |
34 | #define DebugTrace(Level, Data) \
35 | /* if ((Level) & Globals.DebugLevel) { */ \
36 | DbgPrint Data; \
37 | /* } */
38 |
39 | #else
40 |
41 | #define DebugTrace(Level, Data) {NOTHING;}
42 |
43 | #endif
44 |
45 | //////////////////////////////////////////////////////////////////////////
46 | // Function prototypes.
47 | //////////////////////////////////////////////////////////////////////////
48 |
49 | //
50 | // Functions that provide string allocation support
51 | //
52 |
53 | _When_(return==0, _Post_satisfies_(String->Buffer != NULL))
54 | NTSTATUS
55 | NTAPI
56 | SimRepAllocateUnicodeString (
57 | _Inout_ PUNICODE_STRING String
58 | );
59 |
60 | VOID
61 | NTAPI
62 | SimRepFreeUnicodeString (
63 | _Inout_ PUNICODE_STRING String
64 | );
65 |
66 | BOOLEAN
67 | NTAPI
68 | SimRepCompareMapping(
69 | _In_ PFLT_FILE_NAME_INFORMATION NameInfo,
70 | _In_ PUNICODE_STRING MappingPath,
71 | _In_ BOOLEAN IgnoreCase,
72 | _Out_opt_ PBOOLEAN ExactMatch
73 | );
74 |
75 | NTSTATUS
76 | NTAPI
77 | SimRepMungeName(
78 | _In_ PFLT_FILE_NAME_INFORMATION NameInfo,
79 | _In_ PUNICODE_STRING SubPath,
80 | _In_ PUNICODE_STRING NewSubPath,
81 | _In_ BOOLEAN IgnoreCase,
82 | _In_ BOOLEAN ExactMatch,
83 | _Out_ PUNICODE_STRING MungedPath
84 | );
85 |
86 | //
87 | // Functions that handle instance setup/cleanup
88 | //
89 |
90 | NTSTATUS
91 | NTAPI
92 | SimRepInstanceSetup (
93 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
94 | _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
95 | _In_ DEVICE_TYPE VolumeDeviceType,
96 | _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
97 | );
98 |
99 | NTSTATUS
100 | NTAPI
101 | SimRepInstanceQueryTeardown (
102 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
103 | _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
104 | );
105 |
106 | //
107 | // Functions that track operations on the volume
108 | //
109 |
110 | FLT_PREOP_CALLBACK_STATUS
111 | NTAPI
112 | SimRepPreCreate(
113 | _Inout_ PFLT_CALLBACK_DATA Cbd,
114 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
115 | _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
116 | );
117 |
118 | //
119 | // Public functions.
120 | //
121 |
122 | NTSTATUS
123 | NTAPI
124 | SimRepInitialize(
125 | _In_ PDRIVER_OBJECT DriverObject,
126 | _In_ PUNICODE_STRING RegistryPath
127 | );
128 |
129 | NTSTATUS
130 | NTAPI
131 | SimRepDestroy(
132 | _In_ FLT_FILTER_UNLOAD_FLAGS Flags
133 | );
134 |
135 | //////////////////////////////////////////////////////////////////////////
136 | // Variables.
137 | //////////////////////////////////////////////////////////////////////////
138 |
139 | UNICODE_STRING OldName = RTL_CONSTANT_STRING(L"\\Windows\\System32\\wow64log.dll");
140 | UNICODE_STRING NewName; // Set up in SimRepInitialize
141 |
142 | //
143 | // Filter callback routines
144 | //
145 |
146 | FLT_OPERATION_REGISTRATION Callbacks[] = {
147 | { IRP_MJ_CREATE,
148 | FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IO,
149 | SimRepPreCreate,
150 | NULL },
151 |
152 | { IRP_MJ_OPERATION_END }
153 | };
154 |
155 | //
156 | // Filter registration data structure
157 | //
158 |
159 | FLT_REGISTRATION FilterRegistration = {
160 | sizeof( FLT_REGISTRATION ), // Size
161 | FLT_REGISTRATION_VERSION, // Version
162 | 0, // Flags
163 | NULL, // Context
164 | Callbacks, // Operation callbacks
165 | SimRepDestroy, // Filters unload routine
166 | SimRepInstanceSetup, // InstanceSetup routine
167 | SimRepInstanceQueryTeardown, // InstanceQueryTeardown routine
168 | NULL, // InstanceTeardownStart routine
169 | NULL, // InstanceTeardownComplete routine
170 | NULL, // Filename generation support callback
171 | NULL, // Filename normalization support callback
172 | NULL, // Normalize name component cleanup callback
173 | NULL, // Transaction notification callback
174 | NULL // Filename normalization support callback
175 | };
176 |
177 | PFLT_FILTER Filter;
178 |
179 | PDRIVER_UNLOAD PreviousDriverDestroy;
180 | PDRIVER_OBJECT GlobalDriverObject;
181 |
182 | //////////////////////////////////////////////////////////////////////////
183 | // Private functions.
184 | //////////////////////////////////////////////////////////////////////////
185 |
186 | _When_(return==0, _Post_satisfies_(String->Buffer != NULL))
187 | NTSTATUS
188 | NTAPI
189 | SimRepAllocateUnicodeString (
190 | _Inout_ PUNICODE_STRING String
191 | )
192 | /*++
193 | Routine Description:
194 | This routine allocates a unicode string
195 | Arguments:
196 | Size - the size in bytes needed for the string buffer
197 | String - supplies the size of the string to be allocated in the MaximumLength field
198 | return the unicode string
199 | Return Value:
200 | STATUS_SUCCESS - success
201 | STATUS_INSUFFICIENT_RESOURCES - failure
202 | --*/
203 | {
204 |
205 | PAGED_CODE();
206 |
207 | String->Buffer = ExAllocatePoolWithTag( NonPagedPool,
208 | String->MaximumLength,
209 | SIMREP_STRING_TAG );
210 |
211 | if (String->Buffer == NULL) {
212 |
213 | DebugTrace( DEBUG_TRACE_ERROR,
214 | ("[SimRep]: Failed to allocate unicode string of size 0x%x\n",
215 | String->MaximumLength) );
216 |
217 | return STATUS_INSUFFICIENT_RESOURCES;
218 | }
219 |
220 | String->Length = 0;
221 |
222 | return STATUS_SUCCESS;
223 | }
224 |
225 | VOID
226 | NTAPI
227 | SimRepFreeUnicodeString (
228 | _Inout_ PUNICODE_STRING String
229 | )
230 | /*++
231 | Routine Description:
232 | This routine frees a unicode string
233 | Arguments:
234 | String - supplies the string to be freed
235 | Return Value:
236 | None
237 | --*/
238 | {
239 | PAGED_CODE();
240 |
241 | if (String->Buffer) {
242 |
243 | ExFreePoolWithTag( String->Buffer,
244 | SIMREP_STRING_TAG );
245 | String->Buffer = NULL;
246 | }
247 |
248 | String->Length = String->MaximumLength = 0;
249 | String->Buffer = NULL;
250 | }
251 |
252 | BOOLEAN
253 | NTAPI
254 | SimRepCompareMapping(
255 | _In_ PFLT_FILE_NAME_INFORMATION NameInfo,
256 | _In_ PUNICODE_STRING MappingPath,
257 | _In_ BOOLEAN IgnoreCase,
258 | _Out_opt_ PBOOLEAN ExactMatch
259 | )
260 | /*++
261 | Routine Description:
262 | This routine will compare the file specified by the
263 | name information structure to the given mapping path
264 | to determine if the file is the mapping path itself
265 | or a child of the mapping path.
266 | Arguments:
267 | NameInfo - Pointer to the name information for the file.
268 | MappingPath - The mapping path to compare against.
269 | IgnoreCase - If TRUE do a case insenstive comparison.
270 | ExactMatch - If supplied receives TRUE if the name exactly
271 | matches the mapping path.
272 | Return Value:
273 | TRUE - the file matches the mapping path
274 | FALSE - the file is not in the mapping path
275 | --*/
276 | {
277 | UNICODE_STRING fileName;
278 | BOOLEAN match;
279 | BOOLEAN exactMatch;
280 |
281 | PAGED_CODE();
282 |
283 | //
284 | // The NameInfo parameter is assumed to have been parsed
285 | //
286 |
287 | NT_ASSERT (FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_FINAL_COMPONENT) &&
288 | FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_EXTENSION) &&
289 | FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_STREAM) &&
290 | FlagOn(NameInfo->NamesParsed, FLTFL_FILE_NAME_PARSED_PARENT_DIR));
291 |
292 | //
293 | // Point filename to the name of the file, excluding the name of the volume
294 | //
295 |
296 | NT_ASSERT( NameInfo->Name.Buffer == NameInfo->Volume.Buffer );
297 | NT_ASSERT( NameInfo->Name.Length >= NameInfo->Volume.Length);
298 |
299 | match = FALSE;
300 | exactMatch = FALSE;
301 | fileName.Buffer = Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length );
302 | fileName.MaximumLength = NameInfo->Name.Length - NameInfo->Volume.Length;
303 | fileName.Length = fileName.MaximumLength;
304 |
305 | //
306 | // Check if the filename matches this mapping entry (is the mapping
307 | // entry itself or some child directory of the mapping entry)
308 | //
309 |
310 | if (RtlPrefixUnicodeString( MappingPath, &fileName, IgnoreCase )) {
311 |
312 | if (fileName.Length == MappingPath->Length) {
313 |
314 | //
315 | // This path is the mapping itself
316 | //
317 |
318 | match = TRUE;
319 |
320 | exactMatch = TRUE;
321 |
322 | } else if (fileName.Buffer[(MappingPath->Length/sizeof( WCHAR ))] == OBJ_NAME_PATH_SEPARATOR) {
323 |
324 | //
325 | // This path is a child of the mapping
326 | //
327 |
328 | match = TRUE;
329 | }
330 |
331 | //
332 | // No match here means the path simply overlaps the mapping like
333 | // \a\b\c overlaps \a\b\cd.txt
334 | //
335 |
336 | }
337 |
338 | if (ARGUMENT_PRESENT( ExactMatch )) {
339 | *ExactMatch = exactMatch;
340 | }
341 |
342 | return match;
343 | }
344 |
345 | NTSTATUS
346 | NTAPI
347 | SimRepMungeName(
348 | _In_ PFLT_FILE_NAME_INFORMATION NameInfo,
349 | _In_ PUNICODE_STRING SubPath,
350 | _In_ PUNICODE_STRING NewSubPath,
351 | _In_ BOOLEAN IgnoreCase,
352 | _In_ BOOLEAN ExactMatch,
353 | _Out_ PUNICODE_STRING MungedPath
354 | )
355 | /*++
356 | Routine Description:
357 | This routine will create a new path by munginging a new subpath
358 | over and existing subpath.
359 | Arguments:
360 | NameInfo - Pointer to the name information for the file.
361 | SubPath - The path to munge.
362 | IgnoreCase - If TRUE do a case insenstive comparison.
363 | ExactMatch - If TRUE only proceed if the whole path will be replaced
364 | MungedPath - A unicode string to received the munged path created. The
365 | buffer of the string will be allocated in this function.
366 | Return Value:
367 | STATUS_SUCCESS - the path was successfully munged
368 | STATUS_NOT_FOUND - the SubPath was not found or is not an exact match
369 | An appropriate NTSTATUS error otherwise.
370 | --*/
371 | {
372 | NTSTATUS status = STATUS_NOT_FOUND;
373 | BOOLEAN match;
374 | BOOLEAN exactMatch;
375 | USHORT length;
376 |
377 | PAGED_CODE();
378 |
379 | match = SimRepCompareMapping( NameInfo, SubPath, IgnoreCase, &exactMatch );
380 |
381 | if (match) {
382 |
383 | InjDbgPrint("[SimRep]: match=%i, exactMatch=%i\n", match, exactMatch);
384 |
385 | if (ExactMatch && !exactMatch) {
386 |
387 | goto SimRepMungeNameCleanup;
388 | }
389 |
390 | NT_ASSERT( NameInfo->Name.Length >= SubPath->Length );
391 |
392 | length = NameInfo->Name.Length - SubPath->Length + NewSubPath->Length;
393 |
394 | RtlInitUnicodeString( MungedPath, NULL );
395 |
396 | MungedPath->MaximumLength = (USHORT)length;
397 |
398 | status = SimRepAllocateUnicodeString( MungedPath );
399 |
400 | if (!NT_SUCCESS( status )) {
401 |
402 | goto SimRepMungeNameCleanup;
403 | }
404 |
405 | //
406 | // Copy the volume portion of the name (part of the name preceding the matching part)
407 | //
408 |
409 | RtlCopyUnicodeString( MungedPath, &NameInfo->Volume );
410 |
411 | //
412 | // Copy the new file name in place of the matching part of the name
413 | //
414 |
415 | status = RtlAppendUnicodeStringToString( MungedPath, NewSubPath );
416 |
417 | NT_ASSERT( NT_SUCCESS( status ) );
418 |
419 | //
420 | // Copy the portion of the name following the matching part of the name
421 | //
422 |
423 | RtlCopyMemory( Add2Ptr( MungedPath->Buffer, NameInfo->Volume.Length + NewSubPath->Length ),
424 | Add2Ptr( NameInfo->Name.Buffer, NameInfo->Volume.Length + SubPath->Length ),
425 | NameInfo->Name.Length - NameInfo->Volume.Length - SubPath->Length );
426 |
427 | //
428 | // Compute the final length of the new name
429 | //
430 |
431 | MungedPath->Length = length;
432 |
433 | }
434 |
435 | SimRepMungeNameCleanup:
436 |
437 | return status;
438 | }
439 |
440 | NTSTATUS
441 | NTAPI
442 | SimRepInstanceSetup (
443 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
444 | _In_ FLT_INSTANCE_SETUP_FLAGS Flags,
445 | _In_ DEVICE_TYPE VolumeDeviceType,
446 | _In_ FLT_FILESYSTEM_TYPE VolumeFilesystemType
447 | )
448 | /*++
449 | Routine Description:
450 | This routine is called whenever a new instance is created on a volume. This
451 | gives us a chance to decide if we need to attach to this volume or not.
452 | SimRep does not attach on automatic attachment, but will attach when asked
453 | manually.
454 | Arguments:
455 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
456 | opaque handles to this filter, instance and its associated volume.
457 | Flags - Flags describing the reason for this attach request.
458 | Return Value:
459 | STATUS_SUCCESS - attach
460 | STATUS_FLT_DO_NOT_ATTACH - do not attach
461 | --*/
462 | {
463 |
464 | UNREFERENCED_PARAMETER( FltObjects );
465 | UNREFERENCED_PARAMETER( Flags );
466 | UNREFERENCED_PARAMETER( VolumeDeviceType );
467 | UNREFERENCED_PARAMETER( VolumeFilesystemType );
468 |
469 | PAGED_CODE();
470 |
471 | /*
472 | if ( FlagOn( Flags, FLTFL_INSTANCE_SETUP_AUTOMATIC_ATTACHMENT ) ) {
473 |
474 | //
475 | // Do not automatically attach to a volume.
476 | //
477 |
478 | DebugTrace( DEBUG_TRACE_INSTANCES,
479 | ("[Simrep]: Instance setup skipped (Volume = %p, Instance = %p)\n",
480 | FltObjects->Volume,
481 | FltObjects->Instance) );
482 |
483 | return STATUS_FLT_DO_NOT_ATTACH;
484 | }
485 | */
486 |
487 | //
488 | // Attach on manual attachment.
489 | //
490 |
491 | DebugTrace( DEBUG_TRACE_INSTANCES,
492 | ("[SimRep]: Instance setup started (Volume = %p, Instance = %p)\n",
493 | FltObjects->Volume,
494 | FltObjects->Instance) );
495 |
496 |
497 | return STATUS_SUCCESS;
498 | }
499 |
500 | NTSTATUS
501 | NTAPI
502 | SimRepInstanceQueryTeardown (
503 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
504 | _In_ FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
505 | )
506 | /*++
507 | Routine Description:
508 | This is called when an instance is being manually deleted by a
509 | call to FltDetachVolume or FilterDetach thereby giving us a
510 | chance to fail that detach request. SimRep only implements it
511 | because otherwise calls to FltDetachVolume or FilterDetach would
512 | fail to detach.
513 | Arguments:
514 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
515 | opaque handles to this filter, instance and its associated volume.
516 | Flags - Indicating where this detach request came from.
517 | Return Value:
518 | Returns the status of this operation.
519 | --*/
520 | {
521 | UNREFERENCED_PARAMETER( FltObjects );
522 | UNREFERENCED_PARAMETER( Flags );
523 |
524 | PAGED_CODE();
525 |
526 | DebugTrace( DEBUG_TRACE_INSTANCES,
527 | ("[SimRep]: Instance query teadown ended (Instance = %p)\n",
528 | FltObjects->Instance) );
529 |
530 | return STATUS_SUCCESS;
531 | }
532 |
533 | FLT_PREOP_CALLBACK_STATUS
534 | NTAPI
535 | SimRepPreCreate (
536 | _Inout_ PFLT_CALLBACK_DATA Cbd,
537 | _In_ PCFLT_RELATED_OBJECTS FltObjects,
538 | _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
539 | )
540 | /*++
541 | Routine Description:
542 | This routine does the work for SimRep sample. SimRepPreCreate is called in
543 | the pre-operation path for IRP_MJ_CREATE and IRP_MJ_NETWORK_QUERY_OPEN.
544 | The function queries the requested file name for the create and compares
545 | it to the mapping path. If the file is down the "old mapping path", the
546 | filter checks to see if the request is fast io based. If it is we cannot
547 | reparse the create because fast io does not support STATUS_REPARSE.
548 | Instead we return FLT_PREOP_DISALLOW_FASTIO to force the io to be reissued
549 | on the IRP path. If the create is IRP based, then we replace the file
550 | object's file name field with a new path based on the "new mapping path".
551 | This is pageable because it could not be called on the paging path
552 | Arguments:
553 | Cbd - Pointer to the filter callbackData that is passed to us.
554 | FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
555 | opaque handles to this filter, instance, its associated volume and
556 | file object.
557 | CompletionContext - The context for the completion routine for this
558 | operation.
559 | Return Value:
560 | The return value is the status of the operation.
561 | --*/
562 | {
563 | PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
564 | NTSTATUS status;
565 | FLT_PREOP_CALLBACK_STATUS callbackStatus;
566 | UNICODE_STRING newFileName;
567 |
568 | UNREFERENCED_PARAMETER( FltObjects );
569 | UNREFERENCED_PARAMETER( CompletionContext );
570 |
571 | PAGED_CODE();
572 |
573 | DebugTrace( DEBUG_TRACE_ALL_IO,
574 | ("[SimRep]: SimRepPreCreate -> Enter (Cbd = %p, FileObject = %p)\n",
575 | Cbd,
576 | FltObjects->FileObject) );
577 |
578 |
579 | //
580 | // Initialize defaults
581 | //
582 |
583 | status = STATUS_SUCCESS;
584 | callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; // pass through - default is no post op callback
585 |
586 | RtlInitUnicodeString( &newFileName, NULL );
587 |
588 | //
589 | // We only registered for this irp, so thats all we better get!
590 | //
591 |
592 | NT_ASSERT( Cbd->Iopb->MajorFunction == IRP_MJ_CREATE );
593 |
594 | //
595 | // Check if this is a paging file as we don't want to redirect
596 | // the location of the paging file.
597 | //
598 |
599 | if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_PAGING_FILE )) {
600 |
601 | DebugTrace( DEBUG_TRACE_ALL_IO,
602 | ("[SimRep]: SimRepPreCreate -> Ignoring paging file open (Cbd = %p, FileObject = %p)\n",
603 | Cbd,
604 | FltObjects->FileObject) );
605 |
606 | goto SimRepPreCreateCleanup;
607 | }
608 |
609 | //
610 | // We are not allowing volume opens to be reparsed in the sample.
611 | //
612 |
613 | if (FlagOn( Cbd->Iopb->TargetFileObject->Flags, FO_VOLUME_OPEN )) {
614 |
615 | DebugTrace( DEBUG_TRACE_ALL_IO,
616 | ("[SimRep]: SimRepPreCreate -> Ignoring volume open (Cbd = %p, FileObject = %p)\n",
617 | Cbd,
618 | FltObjects->FileObject) );
619 |
620 | goto SimRepPreCreateCleanup;
621 |
622 | }
623 |
624 | //
625 | // SimRep does not honor the FILE_OPEN_REPARSE_POINT create option. For a
626 | // symbolic the caller would pass this flag, for example, in order to open
627 | // the link for deletion. There is no concept of deleting the mapping for
628 | // this filter so it is not clear what the purpose of honoring this flag
629 | // would be.
630 | //
631 |
632 | //
633 | // Don't reparse an open by ID because it is not possible to determine create path intent.
634 | //
635 |
636 | if (FlagOn( Cbd->Iopb->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID )) {
637 |
638 | goto SimRepPreCreateCleanup;
639 | }
640 |
641 | /*
642 | if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY ) &&
643 | !Globals.RemapRenamesAndLinks) {
644 |
645 | //
646 | // This is a prelude to a rename or hard link creation but the filter
647 | // is NOT configured to filter these operations. To perform the operation
648 | // successfully and in a consistent manner this create must not trigger
649 | // a reparse. Pass through the create without attempting any redirection.
650 | //
651 |
652 | goto SimRepPreCreateCleanup;
653 |
654 | }
655 | */
656 |
657 | // Get the name information.
658 | //
659 |
660 | if (FlagOn( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY )) {
661 |
662 | //
663 | // The SL_OPEN_TARGET_DIRECTORY flag indicates the caller is attempting
664 | // to open the target of a rename or hard link creation operation. We
665 | // must clear this flag when asking fltmgr for the name or the result
666 | // will not include the final component. We need the full path in order
667 | // to compare the name to our mapping.
668 | //
669 |
670 | ClearFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY );
671 |
672 | DebugTrace( DEBUG_TRACE_RENAME_REDIRECTION_OPERATIONS,
673 | ("[SimRep]: SimRepPreCreate -> Clearing SL_OPEN_TARGET_DIRECTORY for %wZ (Cbd = %p, FileObject = %p)\n",
674 | &nameInfo->Name,
675 | Cbd,
676 | FltObjects->FileObject) );
677 |
678 |
679 | //
680 | // Get the filename as it appears below this filter. Note that we use
681 | // FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY when querying the filename
682 | // so that the filename as it appears below this filter does not end up
683 | // in filter manager's name cache.
684 | //
685 |
686 | status = FltGetFileNameInformation( Cbd,
687 | FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY,
688 | &nameInfo );
689 |
690 | //
691 | // Restore the SL_OPEN_TARGET_DIRECTORY flag so the create will proceed
692 | // for the target. The file systems depend on this flag being set in
693 | // the target create in order for the subsequent SET_INFORMATION
694 | // operation to proceed correctly.
695 | //
696 |
697 | SetFlag( Cbd->Iopb->OperationFlags, SL_OPEN_TARGET_DIRECTORY );
698 |
699 |
700 | } else {
701 |
702 | //
703 | // Note that we use FLT_FILE_NAME_QUERY_DEFAULT when querying the
704 | // filename. In the precreate the filename should not be in filter
705 | // manager's name cache so there is no point looking there.
706 | //
707 |
708 | status = FltGetFileNameInformation( Cbd,
709 | FLT_FILE_NAME_OPENED |
710 | FLT_FILE_NAME_QUERY_DEFAULT,
711 | &nameInfo );
712 | }
713 |
714 | if (!NT_SUCCESS( status )) {
715 |
716 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR,
717 | ("[SimRep]: SimRepPreCreate -> Failed to get name information (Cbd = %p, FileObject = %p)\n",
718 | Cbd,
719 | FltObjects->FileObject) );
720 |
721 | goto SimRepPreCreateCleanup;
722 | }
723 |
724 |
725 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS,
726 | ("[SimRep]: SimRepPreCreate -> Processing create for file %wZ (Cbd = %p, FileObject = %p)\n",
727 | &nameInfo->Name,
728 | Cbd,
729 | FltObjects->FileObject) );
730 |
731 | //
732 | // Parse the filename information
733 | //
734 |
735 | status = FltParseFileNameInformation( nameInfo );
736 | if (!NT_SUCCESS( status )) {
737 |
738 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR,
739 | ("[SimRep]: SimRepPreCreate -> Failed to parse name information for file %wZ (Cbd = %p, FileObject = %p)\n",
740 | &nameInfo->Name,
741 | Cbd,
742 | FltObjects->FileObject) );
743 |
744 | goto SimRepPreCreateCleanup;
745 | }
746 |
747 | //
748 | // Munge the path from the old mapping to new mapping if the query overlaps
749 | // the mapping path. Note: if the create is case sensitive this comparison
750 | // must be as well.
751 | //
752 |
753 | status = SimRepMungeName( nameInfo,
754 | &OldName,
755 | &NewName,
756 | !FlagOn( Cbd->Iopb->OperationFlags, SL_CASE_SENSITIVE ),
757 | FALSE,
758 | &newFileName);
759 |
760 | if (!NT_SUCCESS( status )) {
761 |
762 | if (status == STATUS_NOT_FOUND) {
763 | status = STATUS_SUCCESS;
764 | }
765 |
766 | goto SimRepPreCreateCleanup;
767 | }
768 |
769 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS,
770 | ("[SimRep]: SimRepPreCreate -> File name %wZ matches mapping. (Cbd = %p, FileObject = %p)\n"
771 | "\tMapping.OldFileName = %wZ\n"
772 | "\tMapping.NewFileName = %wZ\n",
773 | &nameInfo->Name,
774 | Cbd,
775 | FltObjects->FileObject,
776 | OldName,
777 | NewName) );
778 |
779 |
780 | //
781 | // Switch names
782 | //
783 |
784 | status = IoReplaceFileObjectName( Cbd->Iopb->TargetFileObject,
785 | newFileName.Buffer,
786 | newFileName.Length );
787 |
788 | if ( !NT_SUCCESS( status )) {
789 |
790 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_ERROR,
791 | ("[SimRep]: SimRepPreCreate -> Failed to allocate string for file %wZ (Cbd = %p, FileObject = %p)\n",
792 | &nameInfo->Name,
793 | Cbd,
794 | FltObjects->FileObject ));
795 |
796 | goto SimRepPreCreateCleanup;
797 | }
798 |
799 | //
800 | // Set the status to STATUS_REPARSE
801 | //
802 |
803 | status = STATUS_REPARSE;
804 |
805 |
806 | DebugTrace( DEBUG_TRACE_REPARSE_OPERATIONS | DEBUG_TRACE_REPARSED_OPERATIONS,
807 | ("[SimRep]: SimRepPreCreate -> Returning STATUS_REPARSE for file %wZ. (Cbd = %p, FileObject = %p)\n"
808 | "\tNewName = %wZ\n",
809 | &nameInfo->Name,
810 | Cbd,
811 | FltObjects->FileObject,
812 | &newFileName) );
813 |
814 | SimRepPreCreateCleanup:
815 |
816 | //
817 | // Release the references we have acquired
818 | //
819 |
820 | SimRepFreeUnicodeString( &newFileName );
821 |
822 | if (nameInfo != NULL) {
823 |
824 | FltReleaseFileNameInformation( nameInfo );
825 | }
826 |
827 | if (status == STATUS_REPARSE) {
828 |
829 | //
830 | // Reparse the open
831 | //
832 |
833 | Cbd->IoStatus.Status = STATUS_REPARSE;
834 | Cbd->IoStatus.Information = IO_REPARSE;
835 | callbackStatus = FLT_PREOP_COMPLETE;
836 |
837 | } else if (!NT_SUCCESS( status )) {
838 |
839 | //
840 | // An error occurred, fail the open
841 | //
842 |
843 | DebugTrace( DEBUG_TRACE_ERROR,
844 | ("[SimRep]: SimRepPreCreate -> Failed with status 0x%x \n",
845 | status) );
846 |
847 | Cbd->IoStatus.Status = status;
848 | callbackStatus = FLT_PREOP_COMPLETE;
849 | }
850 |
851 | DebugTrace( DEBUG_TRACE_ALL_IO,
852 | ("[SimRep]: SimRepPreCreate -> Exit (Cbd = %p, FileObject = %p)\n",
853 | Cbd,
854 | FltObjects->FileObject) );
855 |
856 | return callbackStatus;
857 |
858 | }
859 |
860 |
861 | //////////////////////////////////////////////////////////////////////////
862 | // Public functions.
863 | //////////////////////////////////////////////////////////////////////////
864 |
865 | #define SIMREP_INSTANCE_NAME L"Reparse"
866 | #define SIMREP_INSTANCE_ALTITUDE L"370040"
867 |
868 | NTSTATUS
869 | NTAPI
870 | SimRepInitializeRegistry(
871 | _In_ PUNICODE_STRING RegistryPath
872 | )
873 | {
874 | NTSTATUS Status;
875 | OBJECT_ATTRIBUTES ObjectAttributes;
876 |
877 | //
878 | // ...\CurrentControlSet\Services\[DriverName]\Instances
879 | //
880 |
881 | UNICODE_STRING InstancesSubkeyString = RTL_CONSTANT_STRING(L"\\Instances");
882 | UNICODE_STRING InstancesSubkeyPath;
883 | InstancesSubkeyPath.MaximumLength = RegistryPath->Length + InstancesSubkeyString.Length;
884 | Status = SimRepAllocateUnicodeString(&InstancesSubkeyPath);
885 |
886 | if (!NT_SUCCESS(Status))
887 | {
888 | goto ErrorInstancesSubkeyPath;
889 | }
890 |
891 | RtlAppendUnicodeStringToString(&InstancesSubkeyPath, RegistryPath);
892 | RtlAppendUnicodeStringToString(&InstancesSubkeyPath, &InstancesSubkeyString);
893 |
894 | InitializeObjectAttributes(&ObjectAttributes,
895 | &InstancesSubkeyPath,
896 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
897 | NULL,
898 | NULL);
899 |
900 | HANDLE InstancesSubkeyHandle;
901 | Status = ZwCreateKey(&InstancesSubkeyHandle,
902 | KEY_ALL_ACCESS,
903 | &ObjectAttributes,
904 | 0,
905 | NULL,
906 | 0,
907 | NULL);
908 |
909 | if (!NT_SUCCESS(Status))
910 | {
911 | goto ErrorInstanceSubkeyHandle;
912 | }
913 |
914 | //
915 | // ...\CurrentControlSet\Services\[DriverName]\Instances\Reparse
916 | //
917 |
918 | UNICODE_STRING ReparseSubkeyString = RTL_CONSTANT_STRING(L"\\" SIMREP_INSTANCE_NAME);
919 | UNICODE_STRING ReparseSubkeyPath;
920 | ReparseSubkeyPath.MaximumLength = InstancesSubkeyPath.Length + ReparseSubkeyString.Length;
921 | Status = SimRepAllocateUnicodeString(&ReparseSubkeyPath);
922 |
923 | if (!NT_SUCCESS(Status))
924 | {
925 | goto ErrorReparseSubkeyPath;
926 | }
927 |
928 | RtlAppendUnicodeStringToString(&ReparseSubkeyPath, &InstancesSubkeyPath);
929 | RtlAppendUnicodeStringToString(&ReparseSubkeyPath, &ReparseSubkeyString);
930 |
931 |
932 | InitializeObjectAttributes(&ObjectAttributes,
933 | &ReparseSubkeyPath,
934 | OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
935 | NULL,
936 | NULL);
937 |
938 | HANDLE ReparseSubkeyHandle;
939 | Status = ZwCreateKey(&ReparseSubkeyHandle,
940 | KEY_ALL_ACCESS,
941 | &ObjectAttributes,
942 | 0,
943 | NULL,
944 | 0,
945 | NULL);
946 |
947 | if (!NT_SUCCESS(Status))
948 | {
949 | goto ErrorReparseSubkeyHandle;
950 | }
951 |
952 | //
953 | // ...\CurrentControlSet\Services\[DriverName]\Instances\DefaultInstance
954 | //
955 |
956 | UNICODE_STRING DefaultInstanceString = RTL_CONSTANT_STRING(L"DefaultInstance");
957 | WCHAR DefaultInstanceValue[] = SIMREP_INSTANCE_NAME;
958 |
959 | Status = ZwSetValueKey(InstancesSubkeyHandle,
960 | &DefaultInstanceString,
961 | 0,
962 | REG_SZ,
963 | DefaultInstanceValue,
964 | sizeof(DefaultInstanceValue));
965 |
966 | if (!NT_SUCCESS(Status))
967 | {
968 | goto ErrorSetValueKey;
969 | }
970 |
971 | //
972 | // ...\CurrentControlSet\Services\[DriverName]\Instances\Reparse\Altitude
973 | //
974 |
975 | UNICODE_STRING AltitudeString = RTL_CONSTANT_STRING(L"Altitude");
976 | WCHAR AltitudeValue[] = SIMREP_INSTANCE_ALTITUDE;
977 |
978 | Status = ZwSetValueKey(ReparseSubkeyHandle,
979 | &AltitudeString,
980 | 0,
981 | REG_SZ,
982 | AltitudeValue,
983 | sizeof(AltitudeValue));
984 |
985 | if (!NT_SUCCESS(Status))
986 | {
987 | goto ErrorSetValueKey;
988 | }
989 |
990 | //
991 | // ...\CurrentControlSet\Services\[DriverName]\Instances\Reparse\Flags
992 | //
993 |
994 | UNICODE_STRING FlagsString = RTL_CONSTANT_STRING(L"Flags");
995 | ULONG FlagsValue = 0;
996 |
997 | Status = ZwSetValueKey(ReparseSubkeyHandle,
998 | &FlagsString,
999 | 0,
1000 | REG_DWORD,
1001 | &FlagsValue,
1002 | sizeof(FlagsValue));
1003 |
1004 | ErrorSetValueKey:
1005 | ZwClose(ReparseSubkeyHandle);
1006 |
1007 | ErrorReparseSubkeyHandle:
1008 | ZwClose(InstancesSubkeyHandle);
1009 |
1010 | ErrorInstanceSubkeyHandle:
1011 | SimRepFreeUnicodeString(&ReparseSubkeyPath);
1012 |
1013 | ErrorReparseSubkeyPath:
1014 | SimRepFreeUnicodeString(&InstancesSubkeyPath);
1015 |
1016 | ErrorInstancesSubkeyPath:
1017 | return Status;
1018 | }
1019 |
1020 | NTSTATUS
1021 | NTAPI
1022 | SimRepInitialize(
1023 | _In_ PDRIVER_OBJECT DriverObject,
1024 | _In_ PUNICODE_STRING RegistryPath
1025 | )
1026 | {
1027 | NTSTATUS status;
1028 | PFLT_REGISTRATION Registration;
1029 |
1030 | //
1031 | // Set path to the native DLL.
1032 | //
1033 |
1034 | extern UNICODE_STRING InjDllPath[InjArchitectureMax];
1035 |
1036 | PUNICODE_STRING NativeInjDllPath = &InjDllPath[InjArchitectureNative];
1037 |
1038 | if (NativeInjDllPath->Length == 0) {
1039 |
1040 | InjDbgPrint("[SimRep]: Invalid native DLL path!\n");
1041 |
1042 | return STATUS_UNSUCCESSFUL;
1043 | }
1044 |
1045 | //
1046 | // Initialize registry keys for the mini-filter.
1047 | //
1048 |
1049 | status = SimRepInitializeRegistry(RegistryPath);
1050 |
1051 | if (!NT_SUCCESS(status)) {
1052 |
1053 | return status;
1054 | }
1055 |
1056 | //
1057 | // Skip the drive name (such as "C:").
1058 | //
1059 |
1060 | RtlInitUnicodeString(&NewName, InjDllPath[InjArchitectureNative].Buffer + 2);
1061 |
1062 | InjDbgPrint("[SimRep]: NewName = '%wZ'\n", &NewName);
1063 |
1064 | //
1065 | // Set default global configuration
1066 | //
1067 |
1068 | Registration = &FilterRegistration;
1069 |
1070 | status = FltRegisterFilter( DriverObject,
1071 | Registration,
1072 | &Filter );
1073 |
1074 | if (!NT_SUCCESS( status )) {
1075 |
1076 | return status;
1077 | }
1078 |
1079 | PreviousDriverDestroy = DriverObject->DriverUnload;
1080 | GlobalDriverObject = DriverObject;
1081 |
1082 | //
1083 | // Start filtering I/O
1084 | //
1085 |
1086 | status = FltStartFiltering( Filter );
1087 |
1088 | if (!NT_SUCCESS( status )) {
1089 | FltUnregisterFilter( Filter );
1090 | }
1091 |
1092 | return status;
1093 | }
1094 |
1095 | NTSTATUS
1096 | NTAPI
1097 | SimRepDestroy(
1098 | _In_ FLT_FILTER_UNLOAD_FLAGS Flags
1099 | )
1100 | /*++
1101 | Routine Description:
1102 | This is the unload routine for this filter driver. This is called
1103 | when the minifilter is about to be unloaded. SimRep can unload
1104 | easily because it does not own any IOs. When the filter is unloaded
1105 | existing reparsed creates will continue to work, but new creates will
1106 | not be reparsed. This is fine from the filter's perspective, but could
1107 | result in unexpected bahavior for apps.
1108 | Arguments:
1109 | Flags - Indicating if this is a mandatory unload.
1110 | Return Value:
1111 | Returns the final status of this operation.
1112 | --*/
1113 | {
1114 | UNREFERENCED_PARAMETER( Flags );
1115 |
1116 | PAGED_CODE();
1117 |
1118 | DebugTrace( DEBUG_TRACE_LOAD_UNLOAD,
1119 | ("[SimRep]: Unloading driver\n") );
1120 |
1121 | if (Filter) {
1122 | FltUnregisterFilter( Filter );
1123 |
1124 | Filter = NULL;
1125 |
1126 | if (PreviousDriverDestroy) {
1127 |
1128 | PreviousDriverDestroy( GlobalDriverObject );
1129 | PreviousDriverDestroy = NULL;
1130 | GlobalDriverObject = NULL;
1131 |
1132 | }
1133 | }
1134 |
1135 | return STATUS_SUCCESS;
1136 | }
1137 |
--------------------------------------------------------------------------------