execheck 0.2.0

Multi-platform executable security checker for Linux (ELF), Windows (PE), and macOS (Mach-O)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# ExeCheck Library Usage Guide

ExeCheck can be used both as a command-line tool and as a Rust library. This guide covers using it as a library in your own Rust projects.

## Adding ExeCheck as a Dependency

Add to your `Cargo.toml`:

```toml
[dependencies]
execheck = "0.2.0"
```

## Core Concepts

### SecurityCheck
The `SecurityCheck` struct represents the analysis result for a single executable:

```rust
pub struct SecurityCheck {
    pub file_path: String,
    pub file_type: String,          // "ELF", "PE", "Mach-O"
    pub checks: HashMap<String, String>,
    pub overall_status: String,     // "Secure", "Mostly Secure", "Insecure"
}
```

### SecurityReport
The `SecurityReport` struct contains analysis results for multiple files:

```rust
pub struct SecurityReport {
    pub files: Vec<SecurityCheck>,
    pub summary: ReportSummary,
}
```

### ScanOptions
Configuration for directory scanning:

```rust
pub struct ScanOptions {
    pub recursive: bool,        // Recursive directory traversal
    pub issues_only: bool,      // Only return files with issues
    pub strict: bool,           // Enable strict error handling
    pub file_filter: FileFilter, // File type filtering
    pub one_filesystem: bool,   // Stay within single filesystem (Unix)
}
```

### FileFilter
File type filtering options:

```rust
pub enum FileFilter {
    All,                                    // All executable files (default)
    WindowsExecutables,                     // Only .exe files
    WindowsDlls,                           // Only .dll files  
    WindowsExecutablesAndDlls,             // Both .exe and .dll files
    Extensions(Vec<String>),               // Custom file extensions
    Custom(fn(&std::path::Path) -> bool),  // Custom predicate function
}
```

## Basic Usage

### Analyze a Single File

```rust
use execheck::analyze_file;
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let result = analyze_file(&PathBuf::from("/bin/ls"))?;
    
    println!("File: {}", result.file_path);
    println!("Type: {}", result.file_type);
    println!("Status: {}", result.overall_status);
    
    // Check specific security features
    if let Some(canary) = result.checks.get("canary") {
        println!("Stack canary: {}", canary);
    }
    
    Ok(())
}
```

### Scan a Directory

```rust
use execheck::{scan_directory, ScanOptions, FileFilter};
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ScanOptions {
        recursive: true,
        issues_only: false,
        strict: false,
        file_filter: FileFilter::All,
        one_filesystem: false,
    };
    
    let report = scan_directory(&PathBuf::from("/usr/bin"), &options)?;
    
    println!("Analyzed {} files", report.summary.total_files);
    println!("Secure: {}, Issues: {}", 
        report.summary.secure_files, 
        report.summary.insecure_files);
    
    Ok(())
}
```

## Advanced Usage

### File Type Filtering

The library supports advanced file filtering for targeted analysis:

```rust
use execheck::{collect_executable_files, ScanOptions, FileFilter};
use std::path::PathBuf;

// Filter only Windows executables
let exe_options = ScanOptions {
    recursive: true,
    file_filter: FileFilter::WindowsExecutables,
    ..Default::default()
};

// Filter only DLLs
let dll_options = ScanOptions {
    recursive: true,
    file_filter: FileFilter::WindowsDlls,
    ..Default::default()
};

// Custom extension filtering
let custom_options = ScanOptions {
    recursive: true,
    file_filter: FileFilter::Extensions(vec![
        "exe".to_string(),
        "dll".to_string(),
        "so".to_string(),
    ]),
    ..Default::default()
};

// Custom predicate filtering
let lib_filter = |path: &std::path::Path| {
    path.file_name()
        .and_then(|name| name.to_str())
        .map_or(false, |name| name.contains("lib"))
};

let predicate_options = ScanOptions {
    recursive: true,
    file_filter: FileFilter::Custom(lib_filter),
    ..Default::default()
};
```

### Filesystem Boundaries (Unix Only)

On Unix-like systems, you can restrict scanning to a single filesystem:

```rust
use execheck::{scan_directory, ScanOptions, FileFilter};
use std::path::PathBuf;

let options = ScanOptions {
    recursive: true,
    one_filesystem: true,  // Don't cross filesystem boundaries
    file_filter: FileFilter::All,
    ..Default::default()
};

// This will scan /usr but won't cross into /proc, /sys, mounted drives, etc.
let report = scan_directory(&PathBuf::from("/usr"), &options)?;
```

### Advanced Usage Patterns

### Custom File Collection

```rust
use execheck::{collect_executable_files, analyze_files, ScanOptions};
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Collect files from multiple directories
    let mut all_files = Vec::new();
    let options = ScanOptions {
        recursive: false,
        ..Default::default()
    };
    all_files.extend(collect_executable_files(&PathBuf::from("/bin"), &options)?);
    all_files.extend(collect_executable_files(&PathBuf::from("/usr/bin"), &options)?);
    
    // Analyze with the same options
    let report = analyze_files(all_files, &options)?;
    
    // Process results...
    Ok(())
}
```

### Custom Filtering

```rust
use execheck::{analyze_files, SecurityCheck};

fn find_files_missing_pie(results: &[SecurityCheck]) -> Vec<&SecurityCheck> {
    results.iter()
        .filter(|check| {
            check.checks.get("pie")
                .map_or(false, |v| v.contains("Disabled"))
        })
        .collect()
}

fn calculate_security_score(results: &[SecurityCheck]) -> f64 {
    if results.is_empty() {
        return 0.0;
    }
    
    let secure_count = results.iter()
        .filter(|check| check.overall_status == "Secure")
        .count();
        
    (secure_count as f64 / results.len() as f64) * 100.0
}
```

## Output Formats

### Generate Reports

```rust
use execheck::{print_report, OutputFormat};
use std::path::PathBuf;

// Print to stdout
print_report(&report, &OutputFormat::Human, None)?;

// Save to file
print_report(&report, &OutputFormat::Json, 
    Some(&PathBuf::from("security_report.json")))?;

// Available formats: Human, Json, Yaml, Xml, Csv
```

### Direct JSON Serialization

```rust
use serde_json;

let json = serde_json::to_string_pretty(&report)?;
println!("{}", json);
```

## Platform-Specific Checks

### Linux ELF
- `canary`: Stack canary protection
- `nx`: Non-executable stack
- `pie`: Position Independent Executable
- `relro`: Relocation Read-Only (Full/Partial)
- `rpath`/`runpath`: Runtime library paths
- `symbols`: Symbol table presence
- `fortified`: Number of fortified functions
- `fortifyable`: Number of fortifiable functions
- `cet`: Control-flow Enforcement Technology

### Windows PE
- `dynamic_base`: ASLR support
- `high_entropy_va`: 64-bit ASLR
- `force_integrity`: Code integrity
- `isolation`: Process isolation
- `nx`: Data Execution Prevention
- `seh`: Structured Exception Handling
- `cfg`: Control Flow Guard
- `rfg`: Return Flow Guard
- `safe_seh`: Safe SEH
- `gs`: Stack cookies
- `authenticode`: Code signing
- `dotnet`: .NET runtime

### macOS Mach-O
- `pie`: Position Independent Executable
- `canary`: Stack protection
- `nx`: Non-executable memory
- `arc`: Automatic Reference Counting
- `encrypted`: Binary encryption
- `code_signature`: Code signing

## Error Handling

All functions return `anyhow::Result` types:

```rust
use anyhow::Result;

fn analyze_with_error_handling() -> Result<()> {
    match analyze_file(&PathBuf::from("/some/file")) {
        Ok(result) => {
            println!("Analysis successful: {}", result.overall_status);
        }
        Err(e) => {
            eprintln!("Analysis failed: {}", e);
            // Handle error appropriately
        }
    }
    Ok(())
}
```

## Performance Considerations

### Large Directories
For large directories, consider:
- Using `issues_only: true` to reduce output
- Processing files in batches
- Implementing custom progress reporting

```rust
use execheck::{collect_executable_files, analyze_file};

fn analyze_large_directory(dir: &PathBuf) -> Result<()> {
    let files = collect_executable_files(dir, true)?;
    
    println!("Found {} files to analyze", files.len());
    
    for (i, file) in files.iter().enumerate() {
        if i % 100 == 0 {
            println!("Progress: {}/{}", i, files.len());
        }
        
        match analyze_file(file) {
            Ok(result) => {
                if result.overall_status != "Secure" {
                    println!("Issue in {}: {}", file.display(), result.overall_status);
                }
            }
            Err(e) => {
                eprintln!("Failed to analyze {}: {}", file.display(), e);
            }
        }
    }
    
    Ok(())
}
```

### Memory Usage
The library loads entire files into memory for analysis. For very large binaries or when processing many files simultaneously, monitor memory usage and consider processing files in smaller batches.

## Integration Patterns

### CI/CD Integration

```rust
use execheck::{scan_directory, ScanOptions};

fn ci_security_check() -> Result<()> {
    let options = ScanOptions {
        recursive: true,
        issues_only: true,
        strict: true,
    };
    
    let report = scan_directory(&PathBuf::from("./target/release"), &options)?;
    
    if report.summary.insecure_files > 0 {
        eprintln!("Security issues found in {} files", report.summary.insecure_files);
        std::process::exit(1);
    }
    
    println!("All binaries passed security checks!");
    Ok(())
}
```

### Custom Reporting

```rust
use execheck::{SecurityCheck, SecurityReport};

fn generate_custom_report(report: &SecurityReport) {
    println!("=== Security Analysis Report ===");
    
    // Summary
    println!("Total files: {}", report.summary.total_files);
    println!("Security score: {:.1}%", 
        (report.summary.secure_files as f64 / report.summary.total_files as f64) * 100.0);
    
    // Group by file type
    let mut elf_count = 0;
    let mut pe_count = 0;
    let mut macho_count = 0;
    
    for check in &report.files {
        match check.file_type.as_str() {
            "ELF" => elf_count += 1,
            "PE" => pe_count += 1,
            "Mach-O" => macho_count += 1,
            _ => {}
        }
    }
    
    println!("File types: ELF: {}, PE: {}, Mach-O: {}", elf_count, pe_count, macho_count);
}
```

This comprehensive library interface makes ExeCheck suitable for integration into larger security analysis workflows, CI/CD pipelines, and custom security tooling.