HolyGrail v1.0

This commit is contained in:
BlackSnufkin
2025-08-19 01:07:01 -07:00
parent fb1949b593
commit 713e85e5bf
2 changed files with 134 additions and 48 deletions
+10 -9
View File
@@ -5,7 +5,7 @@ A BYOVD hunter for finding the HolyGrail driver - Windows drivers suitable for B
## Install
```bash
git clone <repository-url>
git clone https://github.com/BlackSnufkin/HolyGrail
cd HolyGrail
cargo build --release
```
@@ -25,16 +25,17 @@ HolyGrail.exe -d "C:\path\to\driver.sys" -o ".\out" -p ".\Policies" --json
## CLI
```
Usage: HolyGrail.exe [OPTIONS] --driver <DRIVER_FILE>
Usage: HolyGrail.exe [OPTIONS]
Options:
-d, --driver <DRIVER_FILE> Path to the driver file (.sys/.dll) to analyze
-o, --output <OUTPUT_DIRECTORY> Output directory for analysis results [default: .\Analysis]
-p, --policies <POLICY_DIRECTORY> Path to directory containing policy files [default: Policies]
-j, --json Output results in JSON instead of text
-v, --verbose Enable verbose logging
-h, --help Print help
-V, --version Print version
-d, --driver <DRIVER_FILE> Path to the driver file (.sys) to analyze
-D, --directory <DRIVER_DIRECTORY> Directory containing driver files to analyze
-o, --output <OUTPUT_DIRECTORY> Output directory for analysis results [default: .\Analysis]
-p, --policies <POLICY_DIRECTORY> Path to directory containing policy files [default: Policies]
-j, --json Output results in JSON format instead of text
-v, --verbose Enable verbose logging
-h, --help Print help
-V, --version Print version
```
## What it checks
+124 -39
View File
@@ -1,19 +1,26 @@
#![allow(dead_code)]
mod find_vuln;
mod utils;
mod driver_policy;
use clap::Parser;
use log::{error, info, LevelFilter};
use log::{error, info, warn, LevelFilter};
use simplelog::{ColorChoice, CombinedLogger, Config as LogConfig, TermLogger, TerminalMode};
use std::error::Error;
use std::path::Path;
use std::fs;
use crate::find_vuln::analyze_single_driver;
#[derive(Parser)]
#[clap(name = "Driver Vulnerability Analyzer", version = "1.0", author = "BlackSnufkin")]
#[clap(about = "BYOVD hunter for identifying Windows drivers suitable for exploitation.")]
struct Args {
#[clap(short = 'd', long = "driver", help = "Path to the driver file (.sys) to analyze")]
driver_file: String,
driver_file: Option<String>,
#[clap(short = 'D', long = "directory", help = "Directory containing driver files to analyze")]
driver_directory: Option<String>,
#[clap(short = 'o', long = "output", help = "Output directory for analysis results")]
#[clap(default_value = ".\\Analysis")]
@@ -30,6 +37,67 @@ struct Args {
verbose: bool,
}
fn is_driver_file(path: &Path) -> bool {
match path.extension().and_then(|ext| ext.to_str()).map(|ext| ext.to_lowercase()).as_deref() {
Some("sys") | Some("dll") => true,
_ => false,
}
}
fn analyze_directory(directory: &str, output_dir: &str, policy_dir: &str, json_output: bool) -> Result<(), Box<dyn Error>> {
let dir_path = Path::new(directory);
if !dir_path.exists() {
return Err(format!("Directory does not exist: {}", directory).into());
}
if !dir_path.is_dir() {
return Err(format!("Path is not a directory: {}", directory).into());
}
info!("Scanning directory: {}", directory);
let entries = fs::read_dir(dir_path)?;
let mut driver_files = Vec::new();
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() && is_driver_file(&path) {
driver_files.push(path.to_string_lossy().into_owned());
}
}
if driver_files.is_empty() {
warn!("No driver files found in directory: {}", directory);
return Ok(());
}
info!("Found {} driver files to analyze", driver_files.len());
let mut successful = 0;
let mut failed = 0;
for driver_path in driver_files {
info!("Analyzing: {}", driver_path);
match analyze_single_driver(&driver_path, output_dir, policy_dir, json_output) {
Ok(()) => {
successful += 1;
info!("✓ Successfully analyzed: {}", driver_path);
},
Err(e) => {
failed += 1;
error!("✗ Failed to analyze {}: {}", driver_path, e);
}
}
}
info!("Directory analysis complete. Successful: {}, Failed: {}", successful, failed);
Ok(())
}
fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
@@ -41,24 +109,22 @@ fn main() -> Result<(), Box<dyn Error>> {
ColorChoice::Auto,
)])?;
// Validate input arguments
match (&args.driver_file, &args.driver_directory) {
(Some(_), Some(_)) => {
return Err("Cannot specify both --driver and --directory".into());
},
(None, None) => {
return Err("Must specify either --driver or --directory".into());
},
_ => {}
}
info!("Starting Driver Vulnerability Analysis");
info!("Driver file: {}", args.driver_file);
info!("Output: {}", args.output_directory);
info!("Policy directory: {}", args.policy_directory);
info!("Format: {}", if args.json_output { "JSON" } else { "Text" });
// Validate input file
let driver_path = Path::new(&args.driver_file);
if !driver_path.exists() {
error!("Driver file does not exist: {}", args.driver_file);
return Err("Driver file not found".into());
}
if !driver_path.is_file() {
error!("Input path is not a file: {}", args.driver_file);
return Err("Input must be a driver file, not a directory".into());
}
// Validate policy directory
let policy_path = Path::new(&args.policy_directory);
if !policy_path.exists() {
@@ -71,32 +137,51 @@ fn main() -> Result<(), Box<dyn Error>> {
return Err("Policy path must be a directory".into());
}
// Validate file extension
match driver_path.extension().and_then(|ext| ext.to_str()).map(|ext| ext.to_lowercase()).as_deref() {
Some("sys") | Some("dll") => {
info!("Analyzing driver file: {}", driver_path.display());
},
Some(ext) => {
error!("Unsupported file extension: .{}", ext);
return Err("Driver file must have .sys or .dll extension".into());
},
None => {
error!("No file extension found");
return Err("Driver file must have .sys or .dll extension".into());
}
}
// Create output directory if it doesn't exist
std::fs::create_dir_all(&args.output_directory)?;
// Run vulnerability analysis
match analyze_single_driver(&args.driver_file, &args.output_directory, &args.policy_directory, args.json_output) {
Ok(()) => {
info!("Analysis completed successfully!");
},
Err(e) => {
error!("Analysis failed: {}", e);
return Err(e);
// Process either single driver or directory
if let Some(driver_file) = &args.driver_file {
// Single driver analysis (existing functionality)
let driver_path = Path::new(driver_file);
if !driver_path.exists() {
error!("Driver file does not exist: {}", driver_file);
return Err("Driver file not found".into());
}
if !driver_path.is_file() {
error!("Input path is not a file: {}", driver_file);
return Err("Input must be a driver file, not a directory".into());
}
if !is_driver_file(driver_path) {
error!("Invalid file extension. Driver file must have .sys or .dll extension");
return Err("Invalid driver file extension".into());
}
info!("Analyzing single driver: {}", driver_file);
match analyze_single_driver(driver_file, &args.output_directory, &args.policy_directory, args.json_output) {
Ok(()) => {
info!("Analysis completed successfully!");
},
Err(e) => {
error!("Analysis failed: {}", e);
return Err(e);
}
}
} else if let Some(driver_directory) = &args.driver_directory {
// Directory analysis (new functionality)
match analyze_directory(driver_directory, &args.output_directory, &args.policy_directory, args.json_output) {
Ok(()) => {
info!("Directory analysis completed!");
},
Err(e) => {
error!("Directory analysis failed: {}", e);
return Err(e);
}
}
}