1use crate::{
7 types::{Architecture, Import, Section, SecurityFeatures, SecurityIndicators, Symbol},
8 BinaryFile, Result,
9};
10use std::collections::HashSet;
11
12pub struct SecurityAnalyzer {
14 #[allow(dead_code)]
16 architecture: Architecture,
17 config: SecurityConfig,
19}
20
21#[derive(Debug, Clone)]
23pub struct SecurityConfig {
24 pub detect_suspicious_apis: bool,
26 pub detect_anti_debug: bool,
28 pub detect_anti_vm: bool,
30 pub detect_crypto: bool,
32 pub detect_network: bool,
34 pub detect_filesystem: bool,
36 pub detect_registry: bool,
38 pub min_string_length: usize,
40}
41
42impl Default for SecurityConfig {
43 fn default() -> Self {
44 Self {
45 detect_suspicious_apis: true,
46 detect_anti_debug: true,
47 detect_anti_vm: true,
48 detect_crypto: true,
49 detect_network: true,
50 detect_filesystem: true,
51 detect_registry: true,
52 min_string_length: 4,
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct SecurityAnalysisResult {
60 pub indicators: SecurityIndicators,
62 pub features: SecurityFeatures,
64 pub risk_score: f64,
66 pub findings: Vec<SecurityFinding>,
68}
69
70#[derive(Debug, Clone)]
72pub struct SecurityFinding {
73 pub category: FindingCategory,
75 pub severity: Severity,
77 pub description: String,
79 pub location: Option<String>,
81 pub data: Option<String>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum FindingCategory {
88 SuspiciousApi,
90 AntiDebug,
92 AntiVm,
94 Cryptographic,
96 Network,
98 Filesystem,
100 Registry,
102 MissingSecurity,
104 Obfuscation,
106 CodeInjection,
108 PrivilegeEscalation,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
114pub enum Severity {
115 Info,
117 Low,
119 Medium,
121 High,
123 Critical,
125}
126
127impl SecurityAnalyzer {
128 pub fn new(architecture: Architecture) -> Self {
130 Self {
131 architecture,
132 config: SecurityConfig::default(),
133 }
134 }
135
136 pub fn with_config(architecture: Architecture, config: SecurityConfig) -> Self {
138 Self {
139 architecture,
140 config,
141 }
142 }
143
144 pub fn analyze(&self, binary: &BinaryFile) -> Result<SecurityAnalysisResult> {
146 let mut indicators = SecurityIndicators::default();
147 let mut findings = Vec::new();
148
149 if self.config.detect_suspicious_apis {
151 self.analyze_imports(binary.imports(), &mut indicators, &mut findings);
152 }
153
154 self.analyze_sections(binary.sections(), &mut indicators, &mut findings);
156
157 self.analyze_symbols(binary.symbols(), &mut indicators, &mut findings);
159
160 let features = binary.metadata().security_features.clone();
162
163 self.analyze_security_features(&features, &mut findings);
165
166 let risk_score = self.calculate_risk_score(&indicators, &features, &findings);
168
169 Ok(SecurityAnalysisResult {
170 indicators,
171 features,
172 risk_score,
173 findings,
174 })
175 }
176
177 fn analyze_imports(
179 &self,
180 imports: &[Import],
181 indicators: &mut SecurityIndicators,
182 findings: &mut Vec<SecurityFinding>,
183 ) {
184 let suspicious_apis = self.get_suspicious_apis();
185 let anti_debug_apis = self.get_anti_debug_apis();
186 let anti_vm_apis = self.get_anti_vm_apis();
187 let crypto_apis = self.get_crypto_apis();
188 let network_apis = self.get_network_apis();
189 let filesystem_apis = self.get_filesystem_apis();
190 let registry_apis = self.get_registry_apis();
191
192 for import in imports {
193 let api_name = &import.name;
194
195 if suspicious_apis.contains(api_name.as_str()) {
196 indicators.suspicious_apis.push(api_name.clone());
197 findings.push(SecurityFinding {
198 category: FindingCategory::SuspiciousApi,
199 severity: Severity::High,
200 description: format!("Suspicious API call: {}", api_name),
201 location: import.library.clone(),
202 data: Some(api_name.clone()),
203 });
204 }
205
206 if anti_debug_apis.contains(api_name.as_str()) {
207 indicators.anti_debug.push(api_name.clone());
208 findings.push(SecurityFinding {
209 category: FindingCategory::AntiDebug,
210 severity: Severity::Medium,
211 description: format!("Anti-debugging API: {}", api_name),
212 location: import.library.clone(),
213 data: Some(api_name.clone()),
214 });
215 }
216
217 if anti_vm_apis.contains(api_name.as_str()) {
218 indicators.anti_vm.push(api_name.clone());
219 findings.push(SecurityFinding {
220 category: FindingCategory::AntiVm,
221 severity: Severity::Medium,
222 description: format!("Anti-VM API: {}", api_name),
223 location: import.library.clone(),
224 data: Some(api_name.clone()),
225 });
226 }
227
228 if crypto_apis.contains(api_name.as_str()) {
229 indicators.crypto_indicators.push(api_name.clone());
230 findings.push(SecurityFinding {
231 category: FindingCategory::Cryptographic,
232 severity: Severity::Info,
233 description: format!("Cryptographic API: {}", api_name),
234 location: import.library.clone(),
235 data: Some(api_name.clone()),
236 });
237 }
238
239 if network_apis.contains(api_name.as_str()) {
240 indicators.network_indicators.push(api_name.clone());
241 findings.push(SecurityFinding {
242 category: FindingCategory::Network,
243 severity: Severity::Low,
244 description: format!("Network API: {}", api_name),
245 location: import.library.clone(),
246 data: Some(api_name.clone()),
247 });
248 }
249
250 if filesystem_apis.contains(api_name.as_str()) {
251 indicators.filesystem_indicators.push(api_name.clone());
252 findings.push(SecurityFinding {
253 category: FindingCategory::Filesystem,
254 severity: Severity::Low,
255 description: format!("Filesystem API: {}", api_name),
256 location: import.library.clone(),
257 data: Some(api_name.clone()),
258 });
259 }
260
261 if registry_apis.contains(api_name.as_str()) {
262 indicators.registry_indicators.push(api_name.clone());
263 findings.push(SecurityFinding {
264 category: FindingCategory::Registry,
265 severity: Severity::Low,
266 description: format!("Registry API: {}", api_name),
267 location: import.library.clone(),
268 data: Some(api_name.clone()),
269 });
270 }
271 }
272 }
273
274 fn analyze_sections(
276 &self,
277 sections: &[Section],
278 _indicators: &mut SecurityIndicators,
279 findings: &mut Vec<SecurityFinding>,
280 ) {
281 for section in sections {
282 if section.permissions.execute && section.permissions.write {
284 findings.push(SecurityFinding {
285 category: FindingCategory::CodeInjection,
286 severity: Severity::High,
287 description: format!(
288 "Section '{}' is both executable and writable (RWX)",
289 section.name
290 ),
291 location: Some(format!("0x{:x}", section.address)),
292 data: Some(section.name.clone()),
293 });
294 }
295
296 if self.is_suspicious_section_name(§ion.name) {
298 findings.push(SecurityFinding {
299 category: FindingCategory::Obfuscation,
300 severity: Severity::Medium,
301 description: format!("Suspicious section name: {}", section.name),
302 location: Some(format!("0x{:x}", section.address)),
303 data: Some(section.name.clone()),
304 });
305 }
306 }
307 }
308
309 fn analyze_symbols(
311 &self,
312 symbols: &[Symbol],
313 _indicators: &mut SecurityIndicators,
314 findings: &mut Vec<SecurityFinding>,
315 ) {
316 for symbol in symbols {
317 if self.is_suspicious_symbol_name(&symbol.name) {
319 findings.push(SecurityFinding {
320 category: FindingCategory::SuspiciousApi,
321 severity: Severity::Medium,
322 description: format!("Suspicious symbol: {}", symbol.name),
323 location: Some(format!("0x{:x}", symbol.address)),
324 data: Some(symbol.name.clone()),
325 });
326 }
327 }
328 }
329
330 fn analyze_security_features(
332 &self,
333 features: &SecurityFeatures,
334 findings: &mut Vec<SecurityFinding>,
335 ) {
336 if !features.nx_bit {
337 findings.push(SecurityFinding {
338 category: FindingCategory::MissingSecurity,
339 severity: Severity::Medium,
340 description: "NX/DEP bit not enabled".to_string(),
341 location: None,
342 data: None,
343 });
344 }
345
346 if !features.aslr {
347 findings.push(SecurityFinding {
348 category: FindingCategory::MissingSecurity,
349 severity: Severity::Medium,
350 description: "ASLR not enabled".to_string(),
351 location: None,
352 data: None,
353 });
354 }
355
356 if !features.stack_canary {
357 findings.push(SecurityFinding {
358 category: FindingCategory::MissingSecurity,
359 severity: Severity::Low,
360 description: "Stack canaries not detected".to_string(),
361 location: None,
362 data: None,
363 });
364 }
365
366 if !features.cfi {
367 findings.push(SecurityFinding {
368 category: FindingCategory::MissingSecurity,
369 severity: Severity::Low,
370 description: "Control Flow Integrity not enabled".to_string(),
371 location: None,
372 data: None,
373 });
374 }
375 }
376
377 fn calculate_risk_score(
379 &self,
380 indicators: &SecurityIndicators,
381 features: &SecurityFeatures,
382 findings: &[SecurityFinding],
383 ) -> f64 {
384 let mut score = 0.0;
385
386 score += indicators.suspicious_apis.len() as f64 * 10.0;
388 score += indicators.anti_debug.len() as f64 * 5.0;
389 score += indicators.anti_vm.len() as f64 * 5.0;
390 score += indicators.crypto_indicators.len() as f64 * 1.0;
391 score += indicators.network_indicators.len() as f64 * 2.0;
392 score += indicators.filesystem_indicators.len() as f64 * 1.0;
393 score += indicators.registry_indicators.len() as f64 * 1.0;
394
395 if !features.nx_bit {
397 score += 10.0;
398 }
399 if !features.aslr {
400 score += 10.0;
401 }
402 if !features.stack_canary {
403 score += 5.0;
404 }
405 if !features.cfi {
406 score += 5.0;
407 }
408 if !features.pie {
409 score += 5.0;
410 }
411
412 for finding in findings {
414 match finding.severity {
415 Severity::Critical => score += 20.0,
416 Severity::High => score += 10.0,
417 Severity::Medium => score += 5.0,
418 Severity::Low => score += 2.0,
419 Severity::Info => score += 0.5,
420 }
421 }
422
423 (score / 2.0).min(100.0)
425 }
426
427 fn get_suspicious_apis(&self) -> HashSet<&'static str> {
429 let mut apis = HashSet::new();
430
431 apis.insert("VirtualAllocEx");
433 apis.insert("WriteProcessMemory");
434 apis.insert("CreateRemoteThread");
435 apis.insert("SetWindowsHookEx");
436 apis.insert("NtMapViewOfSection");
437 apis.insert("ZwMapViewOfSection");
438
439 apis.insert("OpenProcess");
441 apis.insert("TerminateProcess");
442 apis.insert("SuspendThread");
443 apis.insert("ResumeThread");
444
445 apis.insert("AdjustTokenPrivileges");
447 apis.insert("LookupPrivilegeValue");
448 apis.insert("SeDebugPrivilege");
449
450 apis
451 }
452
453 fn get_anti_debug_apis(&self) -> HashSet<&'static str> {
455 let mut apis = HashSet::new();
456
457 apis.insert("IsDebuggerPresent");
458 apis.insert("CheckRemoteDebuggerPresent");
459 apis.insert("NtQueryInformationProcess");
460 apis.insert("ZwQueryInformationProcess");
461 apis.insert("OutputDebugString");
462 apis.insert("GetTickCount");
463 apis.insert("QueryPerformanceCounter");
464 apis.insert("ptrace"); apis
467 }
468
469 fn get_anti_vm_apis(&self) -> HashSet<&'static str> {
471 let mut apis = HashSet::new();
472
473 apis.insert("GetSystemInfo");
474 apis.insert("GlobalMemoryStatusEx");
475 apis.insert("GetAdaptersInfo");
476 apis.insert("GetVolumeInformation");
477 apis.insert("RegOpenKeyEx");
478 apis.insert("CreateToolhelp32Snapshot");
479
480 apis
481 }
482
483 fn get_crypto_apis(&self) -> HashSet<&'static str> {
485 let mut apis = HashSet::new();
486
487 apis.insert("CryptAcquireContext");
488 apis.insert("CryptCreateHash");
489 apis.insert("CryptEncrypt");
490 apis.insert("CryptDecrypt");
491 apis.insert("CryptImportKey");
492 apis.insert("CryptExportKey");
493
494 apis
495 }
496
497 fn get_network_apis(&self) -> HashSet<&'static str> {
499 let mut apis = HashSet::new();
500
501 apis.insert("WSAStartup");
502 apis.insert("socket");
503 apis.insert("connect");
504 apis.insert("send");
505 apis.insert("recv");
506 apis.insert("InternetOpen");
507 apis.insert("InternetOpenUrl");
508 apis.insert("HttpSendRequest");
509
510 apis
511 }
512
513 fn get_filesystem_apis(&self) -> HashSet<&'static str> {
515 let mut apis = HashSet::new();
516
517 apis.insert("CreateFile");
518 apis.insert("WriteFile");
519 apis.insert("ReadFile");
520 apis.insert("DeleteFile");
521 apis.insert("MoveFile");
522 apis.insert("CopyFile");
523 apis.insert("FindFirstFile");
524 apis.insert("FindNextFile");
525
526 apis
527 }
528
529 fn get_registry_apis(&self) -> HashSet<&'static str> {
531 let mut apis = HashSet::new();
532
533 apis.insert("RegOpenKeyEx");
534 apis.insert("RegCreateKeyEx");
535 apis.insert("RegSetValueEx");
536 apis.insert("RegQueryValueEx");
537 apis.insert("RegDeleteKey");
538 apis.insert("RegDeleteValue");
539
540 apis
541 }
542
543 fn is_suspicious_section_name(&self, name: &str) -> bool {
545 let suspicious_names = [
546 ".packed",
547 ".upx",
548 ".themida",
549 ".aspack",
550 ".pecompact",
551 ".enigma",
552 ".vmprotect",
553 ".obsidium",
554 ".tElock",
555 ".shell",
556 ".stub",
557 ".overlay",
558 ];
559
560 suspicious_names
561 .iter()
562 .any(|&pattern| name.to_lowercase().contains(pattern))
563 }
564
565 fn is_suspicious_symbol_name(&self, name: &str) -> bool {
567 let suspicious_patterns = [
568 "bypass",
569 "inject",
570 "hook",
571 "shellcode",
572 "payload",
573 "exploit",
574 "backdoor",
575 "rootkit",
576 "keylog",
577 "stealth",
578 ];
579
580 let lower_name = name.to_lowercase();
581 suspicious_patterns
582 .iter()
583 .any(|&pattern| lower_name.contains(pattern))
584 }
585}
586
587pub fn analyze_binary_security(binary: &BinaryFile) -> Result<SecurityAnalysisResult> {
589 let analyzer = SecurityAnalyzer::new(binary.architecture());
590 analyzer.analyze(binary)
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::types::*;
597
598 fn create_test_metadata() -> BinaryMetadata {
600 BinaryMetadata {
601 size: 1024,
602 format: BinaryFormat::Pe,
603 architecture: Architecture::X86_64,
604 entry_point: Some(0x1000),
605 base_address: Some(0x400000),
606 timestamp: None,
607 compiler_info: None,
608 endian: Endianness::Little,
609 security_features: SecurityFeatures {
610 nx_bit: true,
611 aslr: true,
612 stack_canary: false,
613 cfi: false,
614 fortify: false,
615 pie: false,
616 relro: false,
617 signed: false,
618 },
619 }
620 }
621
622 fn create_test_binary_file() -> MockBinaryFile {
624 MockBinaryFile {
625 imports: vec![
626 Import {
627 name: "CreateProcess".to_string(),
628 library: Some("kernel32.dll".to_string()),
629 address: Some(0x1000),
630 ordinal: None,
631 },
632 Import {
633 name: "VirtualAllocEx".to_string(),
634 library: Some("kernel32.dll".to_string()),
635 address: Some(0x1004),
636 ordinal: None,
637 },
638 Import {
639 name: "IsDebuggerPresent".to_string(),
640 library: Some("kernel32.dll".to_string()),
641 address: Some(0x1008),
642 ordinal: None,
643 },
644 ],
645 sections: vec![
646 Section {
647 name: ".text".to_string(),
648 address: 0x1000,
649 size: 0x500,
650 offset: 0x400,
651 permissions: SectionPermissions {
652 read: true,
653 write: false,
654 execute: true,
655 },
656 section_type: SectionType::Code,
657 data: None,
658 },
659 Section {
660 name: ".upx0".to_string(),
661 address: 0x2000,
662 size: 0x300,
663 offset: 0x900,
664 permissions: SectionPermissions {
665 read: true,
666 write: true,
667 execute: true,
668 },
669 section_type: SectionType::Code,
670 data: None,
671 },
672 ],
673 symbols: vec![
674 Symbol {
675 name: "main".to_string(),
676 demangled_name: None,
677 address: 0x1000,
678 size: 100,
679 symbol_type: SymbolType::Function,
680 binding: SymbolBinding::Global,
681 visibility: SymbolVisibility::Default,
682 section_index: Some(0),
683 },
684 Symbol {
685 name: "inject_payload".to_string(),
686 demangled_name: None,
687 address: 0x1100,
688 size: 50,
689 symbol_type: SymbolType::Function,
690 binding: SymbolBinding::Local,
691 visibility: SymbolVisibility::Hidden,
692 section_index: Some(0),
693 },
694 ],
695 metadata: create_test_metadata(),
696 }
697 }
698
699 struct MockBinaryFile {
701 imports: Vec<Import>,
702 sections: Vec<Section>,
703 symbols: Vec<Symbol>,
704 metadata: BinaryMetadata,
705 }
706
707 impl MockBinaryFile {
708 fn imports(&self) -> &[Import] {
709 &self.imports
710 }
711
712 fn sections(&self) -> &[Section] {
713 &self.sections
714 }
715
716 fn symbols(&self) -> &[Symbol] {
717 &self.symbols
718 }
719
720 fn metadata(&self) -> &BinaryMetadata {
721 &self.metadata
722 }
723
724 fn architecture(&self) -> Architecture {
725 self.metadata.architecture
726 }
727 }
728
729 fn analyze_mock_binary(binary: &MockBinaryFile) -> SecurityAnalysisResult {
731 let analyzer = SecurityAnalyzer::new(binary.architecture());
732 let mut indicators = SecurityIndicators::default();
733 let mut findings = Vec::new();
734
735 analyzer.analyze_imports(binary.imports(), &mut indicators, &mut findings);
737
738 analyzer.analyze_sections(binary.sections(), &mut indicators, &mut findings);
740
741 analyzer.analyze_symbols(binary.symbols(), &mut indicators, &mut findings);
743
744 let features = binary.metadata().security_features.clone();
746
747 analyzer.analyze_security_features(&features, &mut findings);
749
750 let risk_score = analyzer.calculate_risk_score(&indicators, &features, &findings);
752
753 SecurityAnalysisResult {
754 indicators,
755 features,
756 risk_score,
757 findings,
758 }
759 }
760
761 #[test]
763 fn test_analyzer_creation() {
764 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
765 assert_eq!(analyzer.architecture, Architecture::X86_64);
766 }
767
768 #[test]
769 fn test_analyzer_with_custom_config() {
770 let config = SecurityConfig {
771 detect_suspicious_apis: false,
772 detect_anti_debug: true,
773 detect_anti_vm: false,
774 detect_crypto: true,
775 detect_network: false,
776 detect_filesystem: true,
777 detect_registry: false,
778 min_string_length: 8,
779 };
780 let analyzer = SecurityAnalyzer::with_config(Architecture::Arm64, config.clone());
781 assert_eq!(analyzer.architecture, Architecture::Arm64);
782 assert!(!analyzer.config.detect_suspicious_apis);
783 assert!(analyzer.config.detect_anti_debug);
784 assert_eq!(analyzer.config.min_string_length, 8);
785 }
786
787 #[test]
788 fn test_security_config_default() {
789 let config = SecurityConfig::default();
790 assert!(config.detect_suspicious_apis);
791 assert!(config.detect_anti_debug);
792 assert!(config.detect_anti_vm);
793 assert!(config.detect_crypto);
794 assert!(config.detect_network);
795 assert!(config.detect_filesystem);
796 assert!(config.detect_registry);
797 assert_eq!(config.min_string_length, 4);
798 }
799
800 #[test]
802 fn test_analyze_imports_suspicious_apis() {
803 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
804 let mut indicators = SecurityIndicators::default();
805 let mut findings = Vec::new();
806
807 let imports = vec![
808 Import {
809 name: "VirtualAllocEx".to_string(),
810 library: Some("kernel32.dll".to_string()),
811 address: Some(0x1000),
812 ordinal: None,
813 },
814 Import {
815 name: "WriteProcessMemory".to_string(),
816 library: Some("kernel32.dll".to_string()),
817 address: Some(0x1004),
818 ordinal: None,
819 },
820 Import {
821 name: "RegularFunction".to_string(),
822 library: Some("user32.dll".to_string()),
823 address: Some(0x1008),
824 ordinal: None,
825 },
826 ];
827
828 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
829
830 assert_eq!(indicators.suspicious_apis.len(), 2);
831 assert!(indicators
832 .suspicious_apis
833 .contains(&"VirtualAllocEx".to_string()));
834 assert!(indicators
835 .suspicious_apis
836 .contains(&"WriteProcessMemory".to_string()));
837
838 let suspicious_findings: Vec<_> = findings
839 .iter()
840 .filter(|f| f.category == FindingCategory::SuspiciousApi)
841 .collect();
842 assert_eq!(suspicious_findings.len(), 2);
843 assert_eq!(suspicious_findings[0].severity, Severity::High);
844 }
845
846 #[test]
847 fn test_analyze_imports_anti_debug() {
848 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
849 let mut indicators = SecurityIndicators::default();
850 let mut findings = Vec::new();
851
852 let imports = vec![
853 Import {
854 name: "IsDebuggerPresent".to_string(),
855 library: Some("kernel32.dll".to_string()),
856 address: Some(0x1000),
857 ordinal: None,
858 },
859 Import {
860 name: "CheckRemoteDebuggerPresent".to_string(),
861 library: Some("kernel32.dll".to_string()),
862 address: Some(0x1004),
863 ordinal: None,
864 },
865 ];
866
867 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
868
869 assert_eq!(indicators.anti_debug.len(), 2);
870 assert!(indicators
871 .anti_debug
872 .contains(&"IsDebuggerPresent".to_string()));
873 assert!(indicators
874 .anti_debug
875 .contains(&"CheckRemoteDebuggerPresent".to_string()));
876
877 let anti_debug_findings: Vec<_> = findings
878 .iter()
879 .filter(|f| f.category == FindingCategory::AntiDebug)
880 .collect();
881 assert_eq!(anti_debug_findings.len(), 2);
882 assert_eq!(anti_debug_findings[0].severity, Severity::Medium);
883 }
884
885 #[test]
886 fn test_analyze_imports_anti_vm() {
887 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
888 let mut indicators = SecurityIndicators::default();
889 let mut findings = Vec::new();
890
891 let imports = vec![
892 Import {
893 name: "GetSystemInfo".to_string(),
894 library: Some("kernel32.dll".to_string()),
895 address: Some(0x1000),
896 ordinal: None,
897 },
898 Import {
899 name: "GlobalMemoryStatusEx".to_string(),
900 library: Some("kernel32.dll".to_string()),
901 address: Some(0x1004),
902 ordinal: None,
903 },
904 ];
905
906 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
907
908 assert_eq!(indicators.anti_vm.len(), 2);
909 let anti_vm_findings: Vec<_> = findings
910 .iter()
911 .filter(|f| f.category == FindingCategory::AntiVm)
912 .collect();
913 assert_eq!(anti_vm_findings.len(), 2);
914 assert_eq!(anti_vm_findings[0].severity, Severity::Medium);
915 }
916
917 #[test]
918 fn test_analyze_imports_crypto() {
919 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
920 let mut indicators = SecurityIndicators::default();
921 let mut findings = Vec::new();
922
923 let imports = vec![
924 Import {
925 name: "CryptAcquireContext".to_string(),
926 library: Some("advapi32.dll".to_string()),
927 address: Some(0x1000),
928 ordinal: None,
929 },
930 Import {
931 name: "CryptEncrypt".to_string(),
932 library: Some("advapi32.dll".to_string()),
933 address: Some(0x1004),
934 ordinal: None,
935 },
936 ];
937
938 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
939
940 assert_eq!(indicators.crypto_indicators.len(), 2);
941 let crypto_findings: Vec<_> = findings
942 .iter()
943 .filter(|f| f.category == FindingCategory::Cryptographic)
944 .collect();
945 assert_eq!(crypto_findings.len(), 2);
946 assert_eq!(crypto_findings[0].severity, Severity::Info);
947 }
948
949 #[test]
950 fn test_analyze_imports_network() {
951 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
952 let mut indicators = SecurityIndicators::default();
953 let mut findings = Vec::new();
954
955 let imports = vec![
956 Import {
957 name: "WSAStartup".to_string(),
958 library: Some("ws2_32.dll".to_string()),
959 address: Some(0x1000),
960 ordinal: None,
961 },
962 Import {
963 name: "socket".to_string(),
964 library: Some("ws2_32.dll".to_string()),
965 address: Some(0x1004),
966 ordinal: None,
967 },
968 ];
969
970 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
971
972 assert_eq!(indicators.network_indicators.len(), 2);
973 let network_findings: Vec<_> = findings
974 .iter()
975 .filter(|f| f.category == FindingCategory::Network)
976 .collect();
977 assert_eq!(network_findings.len(), 2);
978 assert_eq!(network_findings[0].severity, Severity::Low);
979 }
980
981 #[test]
982 fn test_analyze_imports_filesystem() {
983 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
984 let mut indicators = SecurityIndicators::default();
985 let mut findings = Vec::new();
986
987 let imports = vec![
988 Import {
989 name: "CreateFile".to_string(),
990 library: Some("kernel32.dll".to_string()),
991 address: Some(0x1000),
992 ordinal: None,
993 },
994 Import {
995 name: "DeleteFile".to_string(),
996 library: Some("kernel32.dll".to_string()),
997 address: Some(0x1004),
998 ordinal: None,
999 },
1000 ];
1001
1002 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
1003
1004 assert_eq!(indicators.filesystem_indicators.len(), 2);
1005 let fs_findings: Vec<_> = findings
1006 .iter()
1007 .filter(|f| f.category == FindingCategory::Filesystem)
1008 .collect();
1009 assert_eq!(fs_findings.len(), 2);
1010 assert_eq!(fs_findings[0].severity, Severity::Low);
1011 }
1012
1013 #[test]
1014 fn test_analyze_imports_registry() {
1015 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1016 let mut indicators = SecurityIndicators::default();
1017 let mut findings = Vec::new();
1018
1019 let imports = vec![
1020 Import {
1021 name: "RegOpenKeyEx".to_string(),
1022 library: Some("advapi32.dll".to_string()),
1023 address: Some(0x1000),
1024 ordinal: None,
1025 },
1026 Import {
1027 name: "RegSetValueEx".to_string(),
1028 library: Some("advapi32.dll".to_string()),
1029 address: Some(0x1004),
1030 ordinal: None,
1031 },
1032 ];
1033
1034 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
1035
1036 assert_eq!(indicators.registry_indicators.len(), 2);
1037 let reg_findings: Vec<_> = findings
1038 .iter()
1039 .filter(|f| f.category == FindingCategory::Registry)
1040 .collect();
1041 assert_eq!(reg_findings.len(), 2);
1042 assert_eq!(reg_findings[0].severity, Severity::Low);
1043 }
1044
1045 #[test]
1047 fn test_analyze_sections_rwx_section() {
1048 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1049 let mut indicators = SecurityIndicators::default();
1050 let mut findings = Vec::new();
1051
1052 let sections = vec![
1053 Section {
1054 name: ".text".to_string(),
1055 address: 0x1000,
1056 size: 0x500,
1057 offset: 0x400,
1058 permissions: SectionPermissions {
1059 read: true,
1060 write: false,
1061 execute: true,
1062 },
1063 section_type: SectionType::Code,
1064 data: None,
1065 },
1066 Section {
1067 name: ".rwx_section".to_string(),
1068 address: 0x2000,
1069 size: 0x300,
1070 offset: 0x900,
1071 permissions: SectionPermissions {
1072 read: true,
1073 write: true,
1074 execute: true,
1075 },
1076 section_type: SectionType::Code,
1077 data: None,
1078 },
1079 ];
1080
1081 analyzer.analyze_sections(§ions, &mut indicators, &mut findings);
1082
1083 let code_injection_findings: Vec<_> = findings
1084 .iter()
1085 .filter(|f| f.category == FindingCategory::CodeInjection)
1086 .collect();
1087 assert_eq!(code_injection_findings.len(), 1);
1088 assert_eq!(code_injection_findings[0].severity, Severity::High);
1089 assert!(code_injection_findings[0].description.contains("RWX"));
1090 }
1091
1092 #[test]
1093 fn test_analyze_sections_suspicious_names() {
1094 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1095 let mut indicators = SecurityIndicators::default();
1096 let mut findings = Vec::new();
1097
1098 let sections = vec![
1099 Section {
1100 name: ".upx0".to_string(),
1101 address: 0x1000,
1102 size: 0x500,
1103 offset: 0x400,
1104 permissions: SectionPermissions {
1105 read: true,
1106 write: false,
1107 execute: true,
1108 },
1109 section_type: SectionType::Code,
1110 data: None,
1111 },
1112 Section {
1113 name: ".packed".to_string(),
1114 address: 0x2000,
1115 size: 0x300,
1116 offset: 0x900,
1117 permissions: SectionPermissions {
1118 read: true,
1119 write: false,
1120 execute: false,
1121 },
1122 section_type: SectionType::Data,
1123 data: None,
1124 },
1125 ];
1126
1127 analyzer.analyze_sections(§ions, &mut indicators, &mut findings);
1128
1129 let obfuscation_findings: Vec<_> = findings
1130 .iter()
1131 .filter(|f| f.category == FindingCategory::Obfuscation)
1132 .collect();
1133 assert_eq!(obfuscation_findings.len(), 2);
1134 assert_eq!(obfuscation_findings[0].severity, Severity::Medium);
1135 }
1136
1137 #[test]
1139 fn test_analyze_symbols_suspicious() {
1140 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1141 let mut indicators = SecurityIndicators::default();
1142 let mut findings = Vec::new();
1143
1144 let symbols = vec![
1145 Symbol {
1146 name: "main".to_string(),
1147 demangled_name: None,
1148 address: 0x1000,
1149 size: 100,
1150 symbol_type: SymbolType::Function,
1151 binding: SymbolBinding::Global,
1152 visibility: SymbolVisibility::Default,
1153 section_index: Some(0),
1154 },
1155 Symbol {
1156 name: "inject_payload".to_string(),
1157 demangled_name: None,
1158 address: 0x1100,
1159 size: 50,
1160 symbol_type: SymbolType::Function,
1161 binding: SymbolBinding::Local,
1162 visibility: SymbolVisibility::Hidden,
1163 section_index: Some(0),
1164 },
1165 Symbol {
1166 name: "bypass_security".to_string(),
1167 demangled_name: None,
1168 address: 0x1200,
1169 size: 75,
1170 symbol_type: SymbolType::Function,
1171 binding: SymbolBinding::Global,
1172 visibility: SymbolVisibility::Default,
1173 section_index: Some(0),
1174 },
1175 ];
1176
1177 analyzer.analyze_symbols(&symbols, &mut indicators, &mut findings);
1178
1179 let suspicious_symbol_findings: Vec<_> = findings
1180 .iter()
1181 .filter(|f| f.category == FindingCategory::SuspiciousApi && f.data.is_some())
1182 .collect();
1183 assert_eq!(suspicious_symbol_findings.len(), 2);
1184 assert_eq!(suspicious_symbol_findings[0].severity, Severity::Medium);
1185 }
1186
1187 #[test]
1189 fn test_analyze_security_features_missing() {
1190 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1191 let mut findings = Vec::new();
1192
1193 let features = SecurityFeatures {
1194 nx_bit: false,
1195 aslr: false,
1196 stack_canary: false,
1197 cfi: false,
1198 fortify: true,
1199 pie: false,
1200 relro: true,
1201 signed: false,
1202 };
1203
1204 analyzer.analyze_security_features(&features, &mut findings);
1205
1206 let missing_security_findings: Vec<_> = findings
1207 .iter()
1208 .filter(|f| f.category == FindingCategory::MissingSecurity)
1209 .collect();
1210
1211 assert_eq!(missing_security_findings.len(), 4);
1213
1214 assert!(findings.iter().any(|f| f.description.contains("NX/DEP")));
1216 assert!(findings.iter().any(|f| f.description.contains("ASLR")));
1217 assert!(findings
1218 .iter()
1219 .any(|f| f.description.contains("Stack canaries")));
1220 assert!(findings
1221 .iter()
1222 .any(|f| f.description.contains("Control Flow Integrity")));
1223 }
1224
1225 #[test]
1226 fn test_analyze_security_features_all_enabled() {
1227 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1228 let mut findings = Vec::new();
1229
1230 let features = SecurityFeatures {
1231 nx_bit: true,
1232 aslr: true,
1233 stack_canary: true,
1234 cfi: true,
1235 fortify: true,
1236 pie: true,
1237 relro: true,
1238 signed: true,
1239 };
1240
1241 analyzer.analyze_security_features(&features, &mut findings);
1242
1243 let missing_security_findings: Vec<_> = findings
1244 .iter()
1245 .filter(|f| f.category == FindingCategory::MissingSecurity)
1246 .collect();
1247 assert_eq!(missing_security_findings.len(), 0);
1248 }
1249
1250 #[test]
1252 fn test_calculate_risk_score_low_risk() {
1253 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1254
1255 let indicators = SecurityIndicators {
1256 suspicious_apis: vec![],
1257 anti_debug: vec![],
1258 anti_vm: vec![],
1259 crypto_indicators: vec!["CryptAcquireContext".to_string()],
1260 network_indicators: vec![],
1261 filesystem_indicators: vec!["CreateFile".to_string()],
1262 registry_indicators: vec![],
1263 };
1264
1265 let features = SecurityFeatures {
1266 nx_bit: true,
1267 aslr: true,
1268 stack_canary: true,
1269 cfi: true,
1270 fortify: true,
1271 pie: true,
1272 relro: true,
1273 signed: true,
1274 };
1275
1276 let findings = vec![
1277 SecurityFinding {
1278 category: FindingCategory::Cryptographic,
1279 severity: Severity::Info,
1280 description: "Crypto API".to_string(),
1281 location: None,
1282 data: None,
1283 },
1284 SecurityFinding {
1285 category: FindingCategory::Filesystem,
1286 severity: Severity::Low,
1287 description: "File API".to_string(),
1288 location: None,
1289 data: None,
1290 },
1291 ];
1292
1293 let risk_score = analyzer.calculate_risk_score(&indicators, &features, &findings);
1294 assert!(risk_score < 10.0); }
1296
1297 #[test]
1298 fn test_calculate_risk_score_high_risk() {
1299 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1300
1301 let indicators = SecurityIndicators {
1302 suspicious_apis: vec![
1303 "VirtualAllocEx".to_string(),
1304 "WriteProcessMemory".to_string(),
1305 ],
1306 anti_debug: vec!["IsDebuggerPresent".to_string()],
1307 anti_vm: vec!["GetSystemInfo".to_string()],
1308 crypto_indicators: vec![],
1309 network_indicators: vec!["socket".to_string()],
1310 filesystem_indicators: vec![],
1311 registry_indicators: vec!["RegSetValueEx".to_string()],
1312 };
1313
1314 let features = SecurityFeatures {
1315 nx_bit: false,
1316 aslr: false,
1317 stack_canary: false,
1318 cfi: false,
1319 fortify: false,
1320 pie: false,
1321 relro: false,
1322 signed: false,
1323 };
1324
1325 let findings = vec![
1326 SecurityFinding {
1327 category: FindingCategory::SuspiciousApi,
1328 severity: Severity::High,
1329 description: "Suspicious API".to_string(),
1330 location: None,
1331 data: None,
1332 },
1333 SecurityFinding {
1334 category: FindingCategory::CodeInjection,
1335 severity: Severity::Critical,
1336 description: "RWX section".to_string(),
1337 location: None,
1338 data: None,
1339 },
1340 SecurityFinding {
1341 category: FindingCategory::MissingSecurity,
1342 severity: Severity::Medium,
1343 description: "Missing ASLR".to_string(),
1344 location: None,
1345 data: None,
1346 },
1347 ];
1348
1349 let risk_score = analyzer.calculate_risk_score(&indicators, &features, &findings);
1350 assert!(risk_score > 50.0); }
1352
1353 #[test]
1355 fn test_get_suspicious_apis() {
1356 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1357 let apis = analyzer.get_suspicious_apis();
1358
1359 assert!(apis.contains("VirtualAllocEx"));
1360 assert!(apis.contains("WriteProcessMemory"));
1361 assert!(apis.contains("CreateRemoteThread"));
1362 assert!(apis.contains("SetWindowsHookEx"));
1363 assert!(apis.contains("OpenProcess"));
1364 assert!(apis.contains("AdjustTokenPrivileges"));
1365 assert!(!apis.contains("CreateFile")); }
1367
1368 #[test]
1369 fn test_get_anti_debug_apis() {
1370 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1371 let apis = analyzer.get_anti_debug_apis();
1372
1373 assert!(apis.contains("IsDebuggerPresent"));
1374 assert!(apis.contains("CheckRemoteDebuggerPresent"));
1375 assert!(apis.contains("NtQueryInformationProcess"));
1376 assert!(apis.contains("OutputDebugString"));
1377 assert!(apis.contains("ptrace"));
1378 assert!(!apis.contains("VirtualAllocEx")); }
1380
1381 #[test]
1382 fn test_get_anti_vm_apis() {
1383 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1384 let apis = analyzer.get_anti_vm_apis();
1385
1386 assert!(apis.contains("GetSystemInfo"));
1387 assert!(apis.contains("GlobalMemoryStatusEx"));
1388 assert!(apis.contains("GetAdaptersInfo"));
1389 assert!(apis.contains("GetVolumeInformation"));
1390 assert!(!apis.contains("IsDebuggerPresent")); }
1392
1393 #[test]
1394 fn test_get_crypto_apis() {
1395 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1396 let apis = analyzer.get_crypto_apis();
1397
1398 assert!(apis.contains("CryptAcquireContext"));
1399 assert!(apis.contains("CryptCreateHash"));
1400 assert!(apis.contains("CryptEncrypt"));
1401 assert!(apis.contains("CryptDecrypt"));
1402 assert!(!apis.contains("socket")); }
1404
1405 #[test]
1406 fn test_get_network_apis() {
1407 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1408 let apis = analyzer.get_network_apis();
1409
1410 assert!(apis.contains("WSAStartup"));
1411 assert!(apis.contains("socket"));
1412 assert!(apis.contains("connect"));
1413 assert!(apis.contains("InternetOpen"));
1414 assert!(!apis.contains("CreateFile")); }
1416
1417 #[test]
1418 fn test_get_filesystem_apis() {
1419 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1420 let apis = analyzer.get_filesystem_apis();
1421
1422 assert!(apis.contains("CreateFile"));
1423 assert!(apis.contains("WriteFile"));
1424 assert!(apis.contains("DeleteFile"));
1425 assert!(apis.contains("FindFirstFile"));
1426 assert!(!apis.contains("RegOpenKeyEx")); }
1428
1429 #[test]
1430 fn test_get_registry_apis() {
1431 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1432 let apis = analyzer.get_registry_apis();
1433
1434 assert!(apis.contains("RegOpenKeyEx"));
1435 assert!(apis.contains("RegCreateKeyEx"));
1436 assert!(apis.contains("RegSetValueEx"));
1437 assert!(apis.contains("RegDeleteKey"));
1438 assert!(!apis.contains("CreateFile")); }
1440
1441 #[test]
1443 fn test_suspicious_section_detection() {
1444 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1445
1446 assert!(analyzer.is_suspicious_section_name(".upx0"));
1448 assert!(analyzer.is_suspicious_section_name(".packed"));
1449 assert!(analyzer.is_suspicious_section_name(".THEMIDA"));
1450 assert!(analyzer.is_suspicious_section_name(".aspack"));
1451 assert!(analyzer.is_suspicious_section_name(".enigma"));
1452 assert!(analyzer.is_suspicious_section_name(".vmprotect"));
1453 assert!(analyzer.is_suspicious_section_name(".shell"));
1454
1455 assert!(analyzer.is_suspicious_section_name(".UPX0"));
1457 assert!(analyzer.is_suspicious_section_name(".PACKED"));
1458
1459 assert!(!analyzer.is_suspicious_section_name(".text"));
1461 assert!(!analyzer.is_suspicious_section_name(".data"));
1462 assert!(!analyzer.is_suspicious_section_name(".bss"));
1463 assert!(!analyzer.is_suspicious_section_name(".rdata"));
1464 }
1465
1466 #[test]
1467 fn test_suspicious_symbol_detection() {
1468 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1469
1470 assert!(analyzer.is_suspicious_symbol_name("inject_code"));
1472 assert!(analyzer.is_suspicious_symbol_name("bypass_check"));
1473 assert!(analyzer.is_suspicious_symbol_name("hook_function"));
1474 assert!(analyzer.is_suspicious_symbol_name("shellcode_payload"));
1475 assert!(analyzer.is_suspicious_symbol_name("exploit_buffer"));
1476 assert!(analyzer.is_suspicious_symbol_name("backdoor_entry"));
1477 assert!(analyzer.is_suspicious_symbol_name("rootkit_hide"));
1478 assert!(analyzer.is_suspicious_symbol_name("keylogger_start"));
1479 assert!(analyzer.is_suspicious_symbol_name("stealth_mode"));
1480
1481 assert!(analyzer.is_suspicious_symbol_name("INJECT_CODE"));
1483 assert!(analyzer.is_suspicious_symbol_name("Bypass_Check"));
1484
1485 assert!(!analyzer.is_suspicious_symbol_name("main"));
1487 assert!(!analyzer.is_suspicious_symbol_name("printf"));
1488 assert!(!analyzer.is_suspicious_symbol_name("malloc"));
1489 assert!(!analyzer.is_suspicious_symbol_name("strcmp"));
1490 assert!(!analyzer.is_suspicious_symbol_name("init_function"));
1491 }
1492
1493 #[test]
1495 fn test_severity_ordering() {
1496 assert!(Severity::Critical > Severity::High);
1497 assert!(Severity::High > Severity::Medium);
1498 assert!(Severity::Medium > Severity::Low);
1499 assert!(Severity::Low > Severity::Info);
1500 }
1501
1502 #[test]
1503 fn test_security_finding_categories() {
1504 let categories = [
1506 FindingCategory::SuspiciousApi,
1507 FindingCategory::AntiDebug,
1508 FindingCategory::AntiVm,
1509 FindingCategory::Cryptographic,
1510 FindingCategory::Network,
1511 FindingCategory::Filesystem,
1512 FindingCategory::Registry,
1513 FindingCategory::MissingSecurity,
1514 FindingCategory::Obfuscation,
1515 FindingCategory::CodeInjection,
1516 FindingCategory::PrivilegeEscalation,
1517 ];
1518
1519 for category in &categories {
1520 let finding = SecurityFinding {
1521 category: category.clone(),
1522 severity: Severity::Medium,
1523 description: "Test finding".to_string(),
1524 location: None,
1525 data: None,
1526 };
1527 assert_eq!(finding.category, *category);
1528 }
1529 }
1530
1531 #[test]
1533 fn test_analyze_imports_empty() {
1534 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1535 let mut indicators = SecurityIndicators::default();
1536 let mut findings = Vec::new();
1537
1538 analyzer.analyze_imports(&[], &mut indicators, &mut findings);
1539
1540 assert_eq!(indicators.suspicious_apis.len(), 0);
1541 assert_eq!(findings.len(), 0);
1542 }
1543
1544 #[test]
1545 fn test_analyze_sections_empty() {
1546 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1547 let mut indicators = SecurityIndicators::default();
1548 let mut findings = Vec::new();
1549
1550 analyzer.analyze_sections(&[], &mut indicators, &mut findings);
1551 assert_eq!(findings.len(), 0);
1552 }
1553
1554 #[test]
1555 fn test_analyze_symbols_empty() {
1556 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1557 let mut indicators = SecurityIndicators::default();
1558 let mut findings = Vec::new();
1559
1560 analyzer.analyze_symbols(&[], &mut indicators, &mut findings);
1561 assert_eq!(findings.len(), 0);
1562 }
1563
1564 #[test]
1565 fn test_risk_score_bounds() {
1566 let analyzer = SecurityAnalyzer::new(Architecture::X86_64);
1567
1568 let indicators = SecurityIndicators::default();
1570 let features = SecurityFeatures::default();
1571
1572 let empty_findings = vec![];
1574 let score = analyzer.calculate_risk_score(&indicators, &features, &empty_findings);
1575 assert!((0.0..=100.0).contains(&score));
1576
1577 let high_risk_indicators = SecurityIndicators {
1579 suspicious_apis: (0..20).map(|i| format!("api_{}", i)).collect(),
1580 anti_debug: (0..10).map(|i| format!("debug_{}", i)).collect(),
1581 anti_vm: (0..10).map(|i| format!("vm_{}", i)).collect(),
1582 crypto_indicators: (0..5).map(|i| format!("crypto_{}", i)).collect(),
1583 network_indicators: (0..5).map(|i| format!("net_{}", i)).collect(),
1584 filesystem_indicators: (0..5).map(|i| format!("fs_{}", i)).collect(),
1585 registry_indicators: (0..5).map(|i| format!("reg_{}", i)).collect(),
1586 };
1587
1588 let insecure_features = SecurityFeatures {
1589 nx_bit: false,
1590 aslr: false,
1591 stack_canary: false,
1592 cfi: false,
1593 fortify: false,
1594 pie: false,
1595 relro: false,
1596 signed: false,
1597 };
1598
1599 let critical_findings: Vec<SecurityFinding> = (0..10)
1600 .map(|i| SecurityFinding {
1601 category: FindingCategory::SuspiciousApi,
1602 severity: Severity::Critical,
1603 description: format!("Critical finding {}", i),
1604 location: None,
1605 data: None,
1606 })
1607 .collect();
1608
1609 let max_score = analyzer.calculate_risk_score(
1610 &high_risk_indicators,
1611 &insecure_features,
1612 &critical_findings,
1613 );
1614 assert!(max_score <= 100.0);
1615 }
1616
1617 #[test]
1619 fn test_analyze_with_disabled_suspicious_apis() {
1620 let config = SecurityConfig {
1621 detect_suspicious_apis: false,
1622 ..SecurityConfig::default()
1623 };
1624 let analyzer = SecurityAnalyzer::with_config(Architecture::X86_64, config);
1625 let mut indicators = SecurityIndicators::default();
1626 let mut findings = Vec::new();
1627
1628 let imports = vec![Import {
1629 name: "VirtualAllocEx".to_string(),
1630 library: Some("kernel32.dll".to_string()),
1631 address: Some(0x1000),
1632 ordinal: None,
1633 }];
1634
1635 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
1636
1637 assert!(!indicators.suspicious_apis.is_empty());
1641
1642 let config2 = SecurityConfig {
1645 detect_anti_debug: false,
1646 detect_anti_vm: false,
1647 detect_crypto: false,
1648 detect_network: false,
1649 detect_filesystem: false,
1650 detect_registry: false,
1651 ..SecurityConfig::default()
1652 };
1653 let analyzer2 = SecurityAnalyzer::with_config(Architecture::X86_64, config2);
1654
1655 assert!(!analyzer2.config.detect_anti_debug);
1657 assert!(!analyzer2.config.detect_anti_vm);
1658 assert!(analyzer2.config.detect_suspicious_apis); }
1660
1661 #[test]
1663 fn test_complete_security_analysis_high_risk() {
1664 let binary = MockBinaryFile {
1666 imports: vec![
1667 Import {
1668 name: "VirtualAllocEx".to_string(),
1669 library: Some("kernel32.dll".to_string()),
1670 address: Some(0x1000),
1671 ordinal: None,
1672 },
1673 Import {
1674 name: "WriteProcessMemory".to_string(),
1675 library: Some("kernel32.dll".to_string()),
1676 address: Some(0x1004),
1677 ordinal: None,
1678 },
1679 Import {
1680 name: "IsDebuggerPresent".to_string(),
1681 library: Some("kernel32.dll".to_string()),
1682 address: Some(0x1008),
1683 ordinal: None,
1684 },
1685 ],
1686 sections: vec![
1687 Section {
1688 name: ".text".to_string(),
1689 address: 0x1000,
1690 size: 0x500,
1691 offset: 0x400,
1692 permissions: SectionPermissions {
1693 read: true,
1694 write: false,
1695 execute: true,
1696 },
1697 section_type: SectionType::Code,
1698 data: None,
1699 },
1700 Section {
1701 name: ".rwx_evil".to_string(),
1702 address: 0x2000,
1703 size: 0x300,
1704 offset: 0x900,
1705 permissions: SectionPermissions {
1706 read: true,
1707 write: true,
1708 execute: true,
1709 },
1710 section_type: SectionType::Code,
1711 data: None,
1712 },
1713 Section {
1714 name: ".upx0".to_string(),
1715 address: 0x3000,
1716 size: 0x200,
1717 offset: 0xc00,
1718 permissions: SectionPermissions {
1719 read: true,
1720 write: false,
1721 execute: true,
1722 },
1723 section_type: SectionType::Code,
1724 data: None,
1725 },
1726 ],
1727 symbols: vec![
1728 Symbol {
1729 name: "main".to_string(),
1730 demangled_name: None,
1731 address: 0x1000,
1732 size: 100,
1733 symbol_type: SymbolType::Function,
1734 binding: SymbolBinding::Global,
1735 visibility: SymbolVisibility::Default,
1736 section_index: Some(0),
1737 },
1738 Symbol {
1739 name: "inject_shellcode".to_string(),
1740 demangled_name: None,
1741 address: 0x1100,
1742 size: 50,
1743 symbol_type: SymbolType::Function,
1744 binding: SymbolBinding::Local,
1745 visibility: SymbolVisibility::Hidden,
1746 section_index: Some(0),
1747 },
1748 Symbol {
1749 name: "bypass_protection".to_string(),
1750 demangled_name: None,
1751 address: 0x1200,
1752 size: 75,
1753 symbol_type: SymbolType::Function,
1754 binding: SymbolBinding::Global,
1755 visibility: SymbolVisibility::Default,
1756 section_index: Some(0),
1757 },
1758 ],
1759 metadata: BinaryMetadata {
1760 size: 2048,
1761 format: BinaryFormat::Pe,
1762 architecture: Architecture::X86_64,
1763 entry_point: Some(0x1000),
1764 base_address: Some(0x400000),
1765 timestamp: None,
1766 compiler_info: None,
1767 endian: Endianness::Little,
1768 security_features: SecurityFeatures {
1769 nx_bit: false, aslr: false, stack_canary: false, cfi: false, fortify: false,
1774 pie: false,
1775 relro: false,
1776 signed: false,
1777 },
1778 },
1779 };
1780
1781 let result = analyze_mock_binary(&binary);
1782
1783 assert!(!result.indicators.suspicious_apis.is_empty());
1785 assert!(result
1786 .indicators
1787 .suspicious_apis
1788 .contains(&"VirtualAllocEx".to_string()));
1789 assert!(result
1790 .indicators
1791 .suspicious_apis
1792 .contains(&"WriteProcessMemory".to_string()));
1793
1794 assert!(!result.indicators.anti_debug.is_empty());
1795 assert!(result
1796 .indicators
1797 .anti_debug
1798 .contains(&"IsDebuggerPresent".to_string()));
1799
1800 assert!(!result.features.nx_bit);
1802 assert!(!result.features.aslr);
1803 assert!(!result.features.stack_canary);
1804 assert!(!result.features.cfi);
1805
1806 assert!(!result.findings.is_empty());
1808
1809 let suspicious_findings: Vec<_> = result
1811 .findings
1812 .iter()
1813 .filter(|f| f.category == FindingCategory::SuspiciousApi)
1814 .collect();
1815 assert!(!suspicious_findings.is_empty());
1816
1817 let anti_debug_findings: Vec<_> = result
1819 .findings
1820 .iter()
1821 .filter(|f| f.category == FindingCategory::AntiDebug)
1822 .collect();
1823 assert!(!anti_debug_findings.is_empty());
1824
1825 let code_injection_findings: Vec<_> = result
1827 .findings
1828 .iter()
1829 .filter(|f| f.category == FindingCategory::CodeInjection)
1830 .collect();
1831 assert!(!code_injection_findings.is_empty());
1832
1833 let obfuscation_findings: Vec<_> = result
1835 .findings
1836 .iter()
1837 .filter(|f| f.category == FindingCategory::Obfuscation)
1838 .collect();
1839 assert!(!obfuscation_findings.is_empty());
1840
1841 let missing_security_findings: Vec<_> = result
1843 .findings
1844 .iter()
1845 .filter(|f| f.category == FindingCategory::MissingSecurity)
1846 .collect();
1847 assert!(!missing_security_findings.is_empty());
1848
1849 assert!(
1851 result.risk_score > 30.0,
1852 "Risk score should be high for malicious binary"
1853 );
1854 }
1855
1856 #[test]
1857 fn test_complete_security_analysis_low_risk() {
1858 let binary = MockBinaryFile {
1860 imports: vec![
1861 Import {
1862 name: "printf".to_string(),
1863 library: Some("msvcrt.dll".to_string()),
1864 address: Some(0x1000),
1865 ordinal: None,
1866 },
1867 Import {
1868 name: "malloc".to_string(),
1869 library: Some("msvcrt.dll".to_string()),
1870 address: Some(0x1004),
1871 ordinal: None,
1872 },
1873 Import {
1874 name: "ExitProcess".to_string(),
1875 library: Some("kernel32.dll".to_string()),
1876 address: Some(0x1008),
1877 ordinal: None,
1878 },
1879 ],
1880 sections: vec![
1881 Section {
1882 name: ".text".to_string(),
1883 address: 0x1000,
1884 size: 0x500,
1885 offset: 0x400,
1886 permissions: SectionPermissions {
1887 read: true,
1888 write: false,
1889 execute: true,
1890 },
1891 section_type: SectionType::Code,
1892 data: None,
1893 },
1894 Section {
1895 name: ".data".to_string(),
1896 address: 0x2000,
1897 size: 0x300,
1898 offset: 0x900,
1899 permissions: SectionPermissions {
1900 read: true,
1901 write: true,
1902 execute: false,
1903 },
1904 section_type: SectionType::Data,
1905 data: None,
1906 },
1907 Section {
1908 name: ".rdata".to_string(),
1909 address: 0x3000,
1910 size: 0x200,
1911 offset: 0xc00,
1912 permissions: SectionPermissions {
1913 read: true,
1914 write: false,
1915 execute: false,
1916 },
1917 section_type: SectionType::Data,
1918 data: None,
1919 },
1920 ],
1921 symbols: vec![
1922 Symbol {
1923 name: "main".to_string(),
1924 demangled_name: None,
1925 address: 0x1000,
1926 size: 100,
1927 symbol_type: SymbolType::Function,
1928 binding: SymbolBinding::Global,
1929 visibility: SymbolVisibility::Default,
1930 section_index: Some(0),
1931 },
1932 Symbol {
1933 name: "print_hello".to_string(),
1934 demangled_name: None,
1935 address: 0x1100,
1936 size: 50,
1937 symbol_type: SymbolType::Function,
1938 binding: SymbolBinding::Local,
1939 visibility: SymbolVisibility::Default,
1940 section_index: Some(0),
1941 },
1942 ],
1943 metadata: BinaryMetadata {
1944 size: 1024,
1945 format: BinaryFormat::Pe,
1946 architecture: Architecture::X86_64,
1947 entry_point: Some(0x1000),
1948 base_address: Some(0x400000),
1949 timestamp: None,
1950 compiler_info: None,
1951 endian: Endianness::Little,
1952 security_features: SecurityFeatures {
1953 nx_bit: true, aslr: true, stack_canary: true, cfi: true, fortify: true,
1958 pie: true,
1959 relro: true,
1960 signed: true,
1961 },
1962 },
1963 };
1964
1965 let result = analyze_mock_binary(&binary);
1966
1967 assert!(result.indicators.suspicious_apis.is_empty());
1969 assert!(result.indicators.anti_debug.is_empty());
1970 assert!(result.indicators.anti_vm.is_empty());
1971
1972 assert!(result.features.nx_bit);
1974 assert!(result.features.aslr);
1975 assert!(result.features.stack_canary);
1976 assert!(result.features.cfi);
1977
1978 let suspicious_findings: Vec<_> = result
1980 .findings
1981 .iter()
1982 .filter(|f| {
1983 matches!(
1984 f.category,
1985 FindingCategory::SuspiciousApi
1986 | FindingCategory::AntiDebug
1987 | FindingCategory::AntiVm
1988 | FindingCategory::CodeInjection
1989 )
1990 })
1991 .collect();
1992 assert!(suspicious_findings.is_empty());
1993
1994 let missing_security_findings: Vec<_> = result
1996 .findings
1997 .iter()
1998 .filter(|f| f.category == FindingCategory::MissingSecurity)
1999 .collect();
2000 assert!(missing_security_findings.is_empty());
2001
2002 assert!(
2004 result.risk_score < 10.0,
2005 "Risk score should be low for benign binary"
2006 );
2007 }
2008
2009 #[test]
2010 fn test_analyze_binary_security_function() {
2011 let binary = create_test_binary_file();
2014
2015 let result = analyze_mock_binary(&binary);
2017
2018 assert!(!result.indicators.suspicious_apis.is_empty());
2020 assert!(result.risk_score > 0.0);
2021 assert!(!result.findings.is_empty());
2022
2023 assert!(result
2025 .indicators
2026 .suspicious_apis
2027 .contains(&"VirtualAllocEx".to_string()));
2028 }
2029
2030 #[test]
2031 fn test_analyzer_different_architectures() {
2032 let architectures = [
2034 Architecture::X86,
2035 Architecture::X86_64,
2036 Architecture::Arm,
2037 Architecture::Arm64,
2038 Architecture::Mips,
2039 Architecture::PowerPC,
2040 ];
2041
2042 for arch in &architectures {
2043 let analyzer = SecurityAnalyzer::new(*arch);
2044 assert_eq!(analyzer.architecture, *arch);
2045
2046 let config = SecurityConfig::default();
2048 let analyzer_with_config = SecurityAnalyzer::with_config(*arch, config);
2049 assert_eq!(analyzer_with_config.architecture, *arch);
2050 }
2051 }
2052
2053 #[test]
2054 fn test_security_analysis_result_completeness() {
2055 let binary = create_test_binary_file();
2056 let result = analyze_mock_binary(&binary);
2057
2058 assert!(
2061 !result.indicators.suspicious_apis.is_empty()
2062 || !result.indicators.anti_debug.is_empty()
2063 || !result.indicators.anti_vm.is_empty()
2064 || !result.indicators.crypto_indicators.is_empty()
2065 || !result.indicators.network_indicators.is_empty()
2066 || !result.indicators.filesystem_indicators.is_empty()
2067 || !result.indicators.registry_indicators.is_empty()
2068 );
2069
2070 assert!(result.risk_score >= 0.0);
2074
2075 assert!(!result.findings.is_empty());
2077 }
2078
2079 #[test]
2080 fn test_config_detection_flags() {
2081 let mut config = SecurityConfig {
2083 detect_suspicious_apis: false,
2084 detect_anti_debug: false,
2085 detect_anti_vm: false,
2086 detect_crypto: false,
2087 detect_network: false,
2088 detect_filesystem: false,
2089 detect_registry: false,
2090 min_string_length: 4,
2091 };
2092
2093 config.detect_suspicious_apis = true;
2095 let analyzer = SecurityAnalyzer::with_config(Architecture::X86_64, config.clone());
2096 let mut indicators = SecurityIndicators::default();
2097 let mut findings = Vec::new();
2098
2099 let imports = vec![Import {
2100 name: "VirtualAllocEx".to_string(),
2101 library: Some("kernel32.dll".to_string()),
2102 address: Some(0x1000),
2103 ordinal: None,
2104 }];
2105
2106 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
2107 assert!(!indicators.suspicious_apis.is_empty());
2108
2109 config.detect_suspicious_apis = false;
2111 config.detect_anti_debug = true;
2112 let analyzer = SecurityAnalyzer::with_config(Architecture::X86_64, config);
2113 let mut indicators = SecurityIndicators::default();
2114 let mut findings = Vec::new();
2115
2116 let imports = vec![Import {
2117 name: "IsDebuggerPresent".to_string(),
2118 library: Some("kernel32.dll".to_string()),
2119 address: Some(0x1000),
2120 ordinal: None,
2121 }];
2122
2123 analyzer.analyze_imports(&imports, &mut indicators, &mut findings);
2124 assert!(!indicators.anti_debug.is_empty());
2125 }
2126}