22
22
use Phar ;
23
23
use RecursiveDirectoryIterator ;
24
24
use RecursiveIteratorIterator ;
25
+ use RuntimeException ;
25
26
use SplFileInfo ;
26
27
27
28
/**
@@ -71,6 +72,13 @@ class Scanner
71
72
*/
72
73
public static $ pathBackups = '/scanner-backups/ ' ;
73
74
75
+ /**
76
+ * Deobfuscate path.
77
+ *
78
+ * @var string
79
+ */
80
+ public static $ pathDeobfuscate = '/deobfuscated/ ' ;
81
+
74
82
/**
75
83
* Logs Path.
76
84
*
@@ -254,14 +262,12 @@ public function __construct()
254
262
self ::$ pathScan = self ::currentDirectory ();
255
263
}
256
264
257
- $ replaceSlash = function ($ str ) {
258
- return str_replace (['/ ' , '\\' ], DIRECTORY_SEPARATOR , $ str );
259
- };
260
-
261
- self ::$ pathQuarantine = $ replaceSlash (self ::$ root . self ::$ pathQuarantine );
262
- self ::$ pathLogs = $ replaceSlash (self ::$ root . self ::$ pathLogs );
263
- self ::$ pathWhitelist = $ replaceSlash (self ::$ root . self ::$ pathWhitelist );
264
- self ::$ pathReport = $ replaceSlash (self ::$ root . self ::$ pathReport );
265
+ self ::$ pathQuarantine = self ::replaceSlash (self ::$ root . self ::$ pathQuarantine );
266
+ self ::$ pathLogs = self ::replaceSlash (self ::$ root . self ::$ pathLogs );
267
+ self ::$ pathWhitelist = self ::replaceSlash (self ::$ root . self ::$ pathWhitelist );
268
+ self ::$ pathReport = self ::replaceSlash (self ::$ root . self ::$ pathReport );
269
+ self ::$ pathBackups = self ::replaceSlash (self ::$ root . self ::$ pathBackups );
270
+ self ::$ pathDeobfuscate = self ::replaceSlash (self ::$ root . self ::$ pathDeobfuscate );
265
271
266
272
if (!self ::isCli ()) {
267
273
self ::setSilentMode (true );
@@ -388,13 +394,15 @@ private function arguments($args = null)
388
394
self ::$ argv ->addFlag ('auto-whitelist ' , ['default ' => false , 'help ' => 'Auto whitelist (if you sure that source isn \'t compromised) ' ]);
389
395
self ::$ argv ->addFlag ('auto-prompt ' , ['default ' => null , 'has_value ' => true , 'value_name ' => 'prompt ' , 'help ' => "Set auto prompt command . \nex. --auto-prompt= \"delete \" or --auto-prompt= \"1 \" (alias of auto-delete) " ]);
390
396
self ::$ argv ->addFlag ('path-whitelist ' , ['default ' => self ::$ pathWhitelist , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => 'Set whitelist file ' ]);
397
+ self ::$ argv ->addFlag ('path-deobfuscate ' , ['default ' => self ::$ pathDeobfuscate , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => "Set debofuscated files path directory. \nIs recommended put files outside the public document path " ]);
391
398
self ::$ argv ->addFlag ('path-backups ' , ['default ' => self ::$ pathBackups , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => "Set backups path directory. \nIs recommended put files outside the public document path " ]);
392
399
self ::$ argv ->addFlag ('path-quarantine ' , ['default ' => self ::$ pathQuarantine , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => "Set quarantine path directory. \nIs recommended put files outside the public document path " ]);
393
400
self ::$ argv ->addFlag ('path-logs ' , ['default ' => self ::$ pathLogs , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => 'Set quarantine log file ' ]);
394
401
self ::$ argv ->addFlag ('path-report ' , ['default ' => self ::$ pathReport , 'has_value ' => true , 'value_name ' => 'path ' , 'help ' => 'Set report log file ' ]);
395
402
self ::$ argv ->addFlag ('disable-colors ' , ['alias ' => ['--no-colors ' , '--no-color ' ], 'default ' => false , 'help ' => 'Disable CLI colors ' ]);
396
403
self ::$ argv ->addFlag ('disable-cache ' , ['alias ' => '--no-cache ' , 'default ' => false , 'help ' => 'Disable Cache ' ]);
397
404
self ::$ argv ->addFlag ('disable-report ' , ['alias ' => '--no-report ' , 'default ' => false , 'help ' => 'Disable Report ' ]);
405
+ //self::$argv->addFlag('deobfuscate', ['default' => false, 'help' => 'Deobfuscate directory']);
398
406
self ::$ argv ->addArgument ('path ' , ['var_args ' => true , 'default ' => self ::currentDirectory (), 'help ' => 'Define the path of the file or directory to scan ' ]);
399
407
self ::$ argv ->parse ($ args );
400
408
@@ -445,14 +453,14 @@ private function arguments($args = null)
445
453
}
446
454
447
455
// Colors
448
- if (isset ( self :: $ argv [ ' disable-colors ' ]) && self ::$ argv ['disable-colors ' ]) {
456
+ if (! self ::$ argv ['disable-colors ' ]) {
449
457
self ::setColors (false );
450
458
} elseif (function_exists ('ncurses_has_colors ' )) {
451
459
self ::setColors (ncurses_has_colors ());
452
460
}
453
461
454
462
// Cache
455
- self ::setCache (!( isset ( self ::$ argv ['disable-cache ' ]) && self :: $ argv [ ' disable-cache ' ]) );
463
+ self ::setCache (!self ::$ argv ['disable-cache ' ]);
456
464
457
465
// Max filesize
458
466
if (isset (self ::$ argv ['max-filesize ' ]) && is_numeric (self ::$ argv ['max-filesize ' ])) {
@@ -485,7 +493,12 @@ private function arguments($args = null)
485
493
486
494
// Path backups
487
495
if (isset (self ::$ argv ['path-backups ' ]) && !empty (self ::$ argv ['path-backups ' ])) {
488
- self ::setPathQuarantine (self ::$ argv ['path-backups ' ]);
496
+ self ::setPathBackups (self ::$ argv ['path-backups ' ]);
497
+ }
498
+
499
+ // Path deobfuscate
500
+ if (isset (self ::$ argv ['path-deobfuscate ' ]) && !empty (self ::$ argv ['path-deobfuscate ' ])) {
501
+ self ::setPathDeobfuscate (self ::$ argv ['path-deobfuscate ' ]);
489
502
}
490
503
491
504
// Path Whitelist
@@ -494,11 +507,17 @@ private function arguments($args = null)
494
507
}
495
508
496
509
// Report
497
- self ::setReport (!( isset ( self ::$ argv ['disable-report ' ]) && self :: $ argv [ ' disable-report ' ]) );
510
+ self ::setReport (!self ::$ argv ['disable-report ' ]);
498
511
499
512
// Report mode
500
513
self ::setReportMode ((isset (self ::$ argv ['report ' ]) && self ::$ argv ['report ' ]) || !self ::isCli ());
501
514
515
+ // Deobfuscate mode
516
+ if (isset (self ::$ argv ['deobfuscate ' ]) && self ::$ argv ['deobfuscate ' ]) {
517
+ self ::enableDeobfuscateMode ();
518
+ self ::disableReport ();
519
+ }
520
+
502
521
// Path report
503
522
if (isset (self ::$ argv ['path-report ' ]) && !empty (self ::$ argv ['path-report ' ])) {
504
523
self ::setPathReport (self ::$ argv ['path-report ' ]);
@@ -1063,6 +1082,47 @@ public function scanFile($info)
1063
1082
return $ result ;
1064
1083
}
1065
1084
1085
+ /**
1086
+ * Deobfuscate file.
1087
+ *
1088
+ * @param $info
1089
+ */
1090
+ public function deobfuscateFile ($ info )
1091
+ {
1092
+ $ filePath = $ info ->getPathname ();
1093
+
1094
+ $ mimeType = 'text/php ' ;
1095
+ if (function_exists ('mime_content_type ' )) {
1096
+ $ mimeType = mime_content_type ($ filePath );
1097
+ } elseif (function_exists ('finfo_open ' )) {
1098
+ $ finfo = finfo_open (FILEINFO_MIME );
1099
+ $ mimeType = finfo_file ($ finfo , $ filePath );
1100
+ finfo_close ($ finfo );
1101
+ }
1102
+
1103
+ if (0 === stripos ($ mimeType , 'text ' )) {
1104
+ $ deobfuscator = new Deobfuscator ();
1105
+ $ content = php_strip_whitespace ($ filePath );
1106
+ $ content = $ deobfuscator ->deobfuscate ($ content );
1107
+ $ content = $ deobfuscator ->decode ($ content );
1108
+
1109
+ // TODO: format code
1110
+
1111
+ $ scanPath = realpath (self ::getPathScan ());
1112
+ if (is_file ($ scanPath )) {
1113
+ $ scanPath = dirname ($ scanPath );
1114
+ }
1115
+ $ filePath = self ::getPathDeobfuscate () . DIRECTORY_SEPARATOR . str_replace ($ scanPath , '' , realpath ($ filePath ));
1116
+ $ filePath = self ::replaceSlash ($ filePath );
1117
+ if (!is_dir (dirname ($ filePath )) &&
1118
+ (!mkdir ($ concurrentDirectory = dirname ($ filePath ), 0755 , true ) && !is_dir ($ concurrentDirectory ))) {
1119
+ throw new RuntimeException (sprintf ('Directory "%s" was not created ' , $ concurrentDirectory ));
1120
+ }
1121
+
1122
+ Actions::putContents ($ filePath , $ content );
1123
+ }
1124
+ }
1125
+
1066
1126
/**
1067
1127
* Run index.php.
1068
1128
*
@@ -1087,6 +1147,13 @@ private function scan($iterator)
1087
1147
$ fileExtension = $ info ->getExtension ();
1088
1148
$ fileSize = filesize ($ filePath );
1089
1149
1150
+ if (self ::isDeobfuscateMode ()) {
1151
+ self ::$ report ['scanned ' ]++;
1152
+ usleep (10 );
1153
+ $ this ->deobfuscateFile ($ info );
1154
+ continue ;
1155
+ }
1156
+
1090
1157
$ isFavicon = self ::isInfectedFavicon ($ info );
1091
1158
1092
1159
if ((
@@ -1651,6 +1718,45 @@ public static function isCacheEnabled()
1651
1718
return isset (self ::$ settings ['cache ' ]) ? self ::$ settings ['cache ' ] : true ;
1652
1719
}
1653
1720
1721
+ /**
1722
+ * @return self
1723
+ */
1724
+ public static function setDeobfuscateMode ($ mode = true )
1725
+ {
1726
+ self ::$ settings ['deobfuscate-mode ' ] = $ mode ;
1727
+ self ::enableReport ();
1728
+
1729
+ return new static ();
1730
+ }
1731
+
1732
+ /**
1733
+ * @return bool
1734
+ */
1735
+ public static function isDeobfuscateMode ()
1736
+ {
1737
+ return isset (self ::$ settings ['deobfuscate-mode ' ]) ? self ::$ settings ['deobfuscate-mode ' ] : false ;
1738
+ }
1739
+
1740
+ /**
1741
+ * @return self
1742
+ */
1743
+ public static function enableDeobfuscateMode ()
1744
+ {
1745
+ self ::setDeobfuscateMode (true );
1746
+
1747
+ return new static ();
1748
+ }
1749
+
1750
+ /**
1751
+ * @return self
1752
+ */
1753
+ public static function disableDeobfuscateMode ()
1754
+ {
1755
+ self ::setDeobfuscateMode (false );
1756
+
1757
+ return new static ();
1758
+ }
1759
+
1654
1760
/**
1655
1761
* @return self
1656
1762
*/
@@ -2019,6 +2125,24 @@ public static function setPathBackups($pathBackups)
2019
2125
return new static ();
2020
2126
}
2021
2127
2128
+ /**
2129
+ * @return string
2130
+ */
2131
+ public static function getPathDeobfuscate ()
2132
+ {
2133
+ return self ::$ pathDeobfuscate ;
2134
+ }
2135
+
2136
+ /**
2137
+ * @param string $pathDeobfuscate
2138
+ */
2139
+ public static function setPathDeobfuscate ($ pathDeobfuscate )
2140
+ {
2141
+ self ::$ pathDeobfuscate = $ pathDeobfuscate ;
2142
+
2143
+ return new static ();
2144
+ }
2145
+
2022
2146
/**
2023
2147
* @return string
2024
2148
*/
@@ -2381,4 +2505,19 @@ protected function setLastError($lastError)
2381
2505
2382
2506
return $ this ;
2383
2507
}
2508
+
2509
+ /**
2510
+ * Replace slash.
2511
+ *
2512
+ * @param $path
2513
+ *
2514
+ * @return string
2515
+ */
2516
+ public static function replaceSlash ($ path )
2517
+ {
2518
+ $ path = str_replace (['/ ' , '\\' ], DIRECTORY_SEPARATOR , $ path );
2519
+ $ path = preg_replace ('/ ' . preg_quote (DIRECTORY_SEPARATOR , '/ ' ) . '+/ ' , DIRECTORY_SEPARATOR , $ path );
2520
+
2521
+ return $ path ;
2522
+ }
2384
2523
}
0 commit comments