5
5
//! both protobuf structure fields and constants. This together with
6
6
//! also exported functions can be later used in YARA rules.
7
7
8
+ use std:: cell:: RefCell ;
9
+
8
10
use crate :: modules:: prelude:: * ;
9
11
use crate :: modules:: protos:: macho:: * ;
10
12
use bstr:: BString ;
@@ -15,6 +17,19 @@ mod parser;
15
17
#[ cfg( test) ]
16
18
mod tests;
17
19
20
+ thread_local ! (
21
+ static DYLIB_MD5_CACHE : RefCell <Option <String >> =
22
+ const { RefCell :: new( None ) } ;
23
+ static ENTITLEMENT_MD5_CACHE : RefCell <Option <String >> =
24
+ const { RefCell :: new( None ) } ;
25
+ static EXPORT_MD5_CACHE : RefCell <Option <String >> =
26
+ const { RefCell :: new( None ) } ;
27
+ static IMPORT_MD5_CACHE : RefCell <Option <String >> =
28
+ const { RefCell :: new( None ) } ;
29
+ static SYM_MD5_CACHE : RefCell <Option <String >> =
30
+ const { RefCell :: new( None ) } ;
31
+ ) ;
32
+
18
33
/// Get the index of a Mach-O file within a fat binary based on CPU type.
19
34
///
20
35
/// This function iterates through the architecture types contained in a
@@ -320,6 +335,17 @@ fn has_export(ctx: &ScanContext, export: RuntimeString) -> Option<bool> {
320
335
/// Returns a md5 hash of the dylibs designated in the mach-o binary
321
336
#[ module_export]
322
337
fn dylib_hash ( ctx : & mut ScanContext ) -> Option < RuntimeString > {
338
+ let cached = DYLIB_MD5_CACHE . with ( |cache| -> Option < RuntimeString > {
339
+ cache
340
+ . borrow ( )
341
+ . as_deref ( )
342
+ . map ( |s| RuntimeString :: from_slice ( ctx, s. as_bytes ( ) ) )
343
+ } ) ;
344
+
345
+ if cached. is_some ( ) {
346
+ return cached;
347
+ }
348
+
323
349
let macho = ctx. module_output :: < Macho > ( ) ?;
324
350
let mut dylibs_to_hash = & macho. dylibs ;
325
351
@@ -351,12 +377,29 @@ fn dylib_hash(ctx: &mut ScanContext) -> Option<RuntimeString> {
351
377
md5_hash. update ( dylibs_to_hash. as_bytes ( ) ) ;
352
378
353
379
let digest = format ! ( "{:x}" , md5_hash. finalize( ) ) ;
380
+
381
+ DYLIB_MD5_CACHE . with ( |cache| {
382
+ * cache. borrow_mut ( ) = Some ( digest. clone ( ) ) ;
383
+ } ) ;
384
+
354
385
Some ( RuntimeString :: new ( digest) )
355
386
}
356
387
357
388
/// Returns a md5 hash of the entitlements designated in the mach-o binary
358
389
#[ module_export]
359
390
fn entitlement_hash ( ctx : & mut ScanContext ) -> Option < RuntimeString > {
391
+ let cached =
392
+ ENTITLEMENT_MD5_CACHE . with ( |cache| -> Option < RuntimeString > {
393
+ cache
394
+ . borrow ( )
395
+ . as_deref ( )
396
+ . map ( |s| RuntimeString :: from_slice ( ctx, s. as_bytes ( ) ) )
397
+ } ) ;
398
+
399
+ if cached. is_some ( ) {
400
+ return cached;
401
+ }
402
+
360
403
let macho = ctx. module_output :: < Macho > ( ) ?;
361
404
let mut entitlements_to_hash = & macho. entitlements ;
362
405
@@ -383,12 +426,28 @@ fn entitlement_hash(ctx: &mut ScanContext) -> Option<RuntimeString> {
383
426
md5_hash. update ( entitlements_str. as_bytes ( ) ) ;
384
427
385
428
let digest = format ! ( "{:x}" , md5_hash. finalize( ) ) ;
429
+
430
+ ENTITLEMENT_MD5_CACHE . with ( |cache| {
431
+ * cache. borrow_mut ( ) = Some ( digest. clone ( ) ) ;
432
+ } ) ;
433
+
386
434
Some ( RuntimeString :: new ( digest) )
387
435
}
388
436
389
437
/// Returns a md5 hash of the export symbols in the mach-o binary
390
438
#[ module_export]
391
439
fn export_hash ( ctx : & mut ScanContext ) -> Option < RuntimeString > {
440
+ let cached = EXPORT_MD5_CACHE . with ( |cache| -> Option < RuntimeString > {
441
+ cache
442
+ . borrow ( )
443
+ . as_deref ( )
444
+ . map ( |s| RuntimeString :: from_slice ( ctx, s. as_bytes ( ) ) )
445
+ } ) ;
446
+
447
+ if cached. is_some ( ) {
448
+ return cached;
449
+ }
450
+
392
451
let macho = ctx. module_output :: < Macho > ( ) ?;
393
452
let mut exports_to_hash = & macho. exports ;
394
453
@@ -415,12 +474,28 @@ fn export_hash(ctx: &mut ScanContext) -> Option<RuntimeString> {
415
474
md5_hash. update ( exports_str. as_bytes ( ) ) ;
416
475
417
476
let digest = format ! ( "{:x}" , md5_hash. finalize( ) ) ;
477
+
478
+ EXPORT_MD5_CACHE . with ( |cache| {
479
+ * cache. borrow_mut ( ) = Some ( digest. clone ( ) ) ;
480
+ } ) ;
481
+
418
482
Some ( RuntimeString :: new ( digest) )
419
483
}
420
484
421
485
/// Returns a md5 hash of the imported symbols in the mach-o binary
422
486
#[ module_export]
423
487
fn import_hash ( ctx : & mut ScanContext ) -> Option < RuntimeString > {
488
+ let cached = IMPORT_MD5_CACHE . with ( |cache| -> Option < RuntimeString > {
489
+ cache
490
+ . borrow ( )
491
+ . as_deref ( )
492
+ . map ( |s| RuntimeString :: from_slice ( ctx, s. as_bytes ( ) ) )
493
+ } ) ;
494
+
495
+ if cached. is_some ( ) {
496
+ return cached;
497
+ }
498
+
424
499
let macho = ctx. module_output :: < Macho > ( ) ?;
425
500
let mut imports_to_hash = & macho. imports ;
426
501
@@ -447,12 +522,28 @@ fn import_hash(ctx: &mut ScanContext) -> Option<RuntimeString> {
447
522
md5_hash. update ( imports_str. as_bytes ( ) ) ;
448
523
449
524
let digest = format ! ( "{:x}" , md5_hash. finalize( ) ) ;
525
+
526
+ IMPORT_MD5_CACHE . with ( |cache| {
527
+ * cache. borrow_mut ( ) = Some ( digest. clone ( ) ) ;
528
+ } ) ;
529
+
450
530
Some ( RuntimeString :: new ( digest) )
451
531
}
452
532
453
533
/// Returns a md5 hash of the symbol table in the mach-o binary
454
534
#[ module_export]
455
535
fn sym_hash ( ctx : & mut ScanContext ) -> Option < RuntimeString > {
536
+ let cached = SYM_MD5_CACHE . with ( |cache| -> Option < RuntimeString > {
537
+ cache
538
+ . borrow ( )
539
+ . as_deref ( )
540
+ . map ( |s| RuntimeString :: from_slice ( ctx, s. as_bytes ( ) ) )
541
+ } ) ;
542
+
543
+ if cached. is_some ( ) {
544
+ return cached;
545
+ }
546
+
456
547
let macho = ctx. module_output :: < Macho > ( ) ?;
457
548
let mut symtab_to_hash = & macho. symtab . entries ;
458
549
@@ -479,11 +570,22 @@ fn sym_hash(ctx: &mut ScanContext) -> Option<RuntimeString> {
479
570
md5_hash. update ( symtab_hash_entries) ;
480
571
481
572
let digest = format ! ( "{:x}" , md5_hash. finalize( ) ) ;
573
+
574
+ SYM_MD5_CACHE . with ( |cache| {
575
+ * cache. borrow_mut ( ) = Some ( digest. clone ( ) ) ;
576
+ } ) ;
577
+
482
578
Some ( RuntimeString :: new ( digest) )
483
579
}
484
580
485
581
#[ module_main]
486
582
fn main ( data : & [ u8 ] , _meta : Option < & [ u8 ] > ) -> Macho {
583
+ DYLIB_MD5_CACHE . with ( |cache| * cache. borrow_mut ( ) = None ) ;
584
+ ENTITLEMENT_MD5_CACHE . with ( |cache| * cache. borrow_mut ( ) = None ) ;
585
+ EXPORT_MD5_CACHE . with ( |cache| * cache. borrow_mut ( ) = None ) ;
586
+ IMPORT_MD5_CACHE . with ( |cache| * cache. borrow_mut ( ) = None ) ;
587
+ SYM_MD5_CACHE . with ( |cache| * cache. borrow_mut ( ) = None ) ;
588
+
487
589
match parser:: MachO :: parse ( data) {
488
590
Ok ( macho) => macho. into ( ) ,
489
591
Err ( _) => Macho :: new ( ) ,
0 commit comments