Skip to content

Commit 9238b6e

Browse files
committed
feat: add deobfuscate commands and functionality and fix backup path
1 parent 0c359ef commit 9238b6e

File tree

2 files changed

+159
-15
lines changed

2 files changed

+159
-15
lines changed

src/Actions.php

+8-3
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ public static function moveToQuarantine($file)
123123
*/
124124
public static function makeBackup($file)
125125
{
126-
$backup = Scanner::getPathBackups() . DIRECTORY_SEPARATOR . str_replace(realpath(Scanner::getPathScan()), '', realpath($file));
127-
$backup = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $backup);
128-
if (!is_dir(dirname($backup)) && (!mkdir($concurrentDirectory = dirname($backup), 0755, true) && !is_dir($concurrentDirectory))) {
126+
$scanPath = realpath(Scanner::getPathScan());
127+
if (is_file($scanPath)) {
128+
$scanPath = dirname($scanPath);
129+
}
130+
$backup = Scanner::getPathBackups() . DIRECTORY_SEPARATOR . str_replace($scanPath, '', realpath($file));
131+
$backup = Scanner::replaceSlash($backup);
132+
if (!is_dir(dirname($backup)) &&
133+
(!mkdir($concurrentDirectory = dirname($backup), 0755, true) && !is_dir($concurrentDirectory))) {
129134
throw new RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
130135
}
131136
copy($file, $backup);

src/Scanner.php

+151-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Phar;
2323
use RecursiveDirectoryIterator;
2424
use RecursiveIteratorIterator;
25+
use RuntimeException;
2526
use SplFileInfo;
2627

2728
/**
@@ -71,6 +72,13 @@ class Scanner
7172
*/
7273
public static $pathBackups = '/scanner-backups/';
7374

75+
/**
76+
* Deobfuscate path.
77+
*
78+
* @var string
79+
*/
80+
public static $pathDeobfuscate = '/deobfuscated/';
81+
7482
/**
7583
* Logs Path.
7684
*
@@ -254,14 +262,12 @@ public function __construct()
254262
self::$pathScan = self::currentDirectory();
255263
}
256264

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);
265271

266272
if (!self::isCli()) {
267273
self::setSilentMode(true);
@@ -388,13 +394,15 @@ private function arguments($args = null)
388394
self::$argv->addFlag('auto-whitelist', ['default' => false, 'help' => 'Auto whitelist (if you sure that source isn\'t compromised)']);
389395
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)"]);
390396
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"]);
391398
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"]);
392399
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"]);
393400
self::$argv->addFlag('path-logs', ['default' => self::$pathLogs, 'has_value' => true, 'value_name' => 'path', 'help' => 'Set quarantine log file']);
394401
self::$argv->addFlag('path-report', ['default' => self::$pathReport, 'has_value' => true, 'value_name' => 'path', 'help' => 'Set report log file']);
395402
self::$argv->addFlag('disable-colors', ['alias' => ['--no-colors', '--no-color'], 'default' => false, 'help' => 'Disable CLI colors']);
396403
self::$argv->addFlag('disable-cache', ['alias' => '--no-cache', 'default' => false, 'help' => 'Disable Cache']);
397404
self::$argv->addFlag('disable-report', ['alias' => '--no-report', 'default' => false, 'help' => 'Disable Report']);
405+
//self::$argv->addFlag('deobfuscate', ['default' => false, 'help' => 'Deobfuscate directory']);
398406
self::$argv->addArgument('path', ['var_args' => true, 'default' => self::currentDirectory(), 'help' => 'Define the path of the file or directory to scan']);
399407
self::$argv->parse($args);
400408

@@ -445,14 +453,14 @@ private function arguments($args = null)
445453
}
446454

447455
// Colors
448-
if (isset(self::$argv['disable-colors']) && self::$argv['disable-colors']) {
456+
if (!self::$argv['disable-colors']) {
449457
self::setColors(false);
450458
} elseif (function_exists('ncurses_has_colors')) {
451459
self::setColors(ncurses_has_colors());
452460
}
453461

454462
// Cache
455-
self::setCache(!(isset(self::$argv['disable-cache']) && self::$argv['disable-cache']));
463+
self::setCache(!self::$argv['disable-cache']);
456464

457465
// Max filesize
458466
if (isset(self::$argv['max-filesize']) && is_numeric(self::$argv['max-filesize'])) {
@@ -485,7 +493,12 @@ private function arguments($args = null)
485493

486494
// Path backups
487495
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']);
489502
}
490503

491504
// Path Whitelist
@@ -494,11 +507,17 @@ private function arguments($args = null)
494507
}
495508

496509
// Report
497-
self::setReport(!(isset(self::$argv['disable-report']) && self::$argv['disable-report']));
510+
self::setReport(!self::$argv['disable-report']);
498511

499512
// Report mode
500513
self::setReportMode((isset(self::$argv['report']) && self::$argv['report']) || !self::isCli());
501514

515+
// Deobfuscate mode
516+
if (isset(self::$argv['deobfuscate']) && self::$argv['deobfuscate']) {
517+
self::enableDeobfuscateMode();
518+
self::disableReport();
519+
}
520+
502521
// Path report
503522
if (isset(self::$argv['path-report']) && !empty(self::$argv['path-report'])) {
504523
self::setPathReport(self::$argv['path-report']);
@@ -1063,6 +1082,47 @@ public function scanFile($info)
10631082
return $result;
10641083
}
10651084

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+
10661126
/**
10671127
* Run index.php.
10681128
*
@@ -1087,6 +1147,13 @@ private function scan($iterator)
10871147
$fileExtension = $info->getExtension();
10881148
$fileSize = filesize($filePath);
10891149

1150+
if (self::isDeobfuscateMode()) {
1151+
self::$report['scanned']++;
1152+
usleep(10);
1153+
$this->deobfuscateFile($info);
1154+
continue;
1155+
}
1156+
10901157
$isFavicon = self::isInfectedFavicon($info);
10911158

10921159
if ((
@@ -1651,6 +1718,45 @@ public static function isCacheEnabled()
16511718
return isset(self::$settings['cache']) ? self::$settings['cache'] : true;
16521719
}
16531720

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+
16541760
/**
16551761
* @return self
16561762
*/
@@ -2019,6 +2125,24 @@ public static function setPathBackups($pathBackups)
20192125
return new static();
20202126
}
20212127

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+
20222146
/**
20232147
* @return string
20242148
*/
@@ -2381,4 +2505,19 @@ protected function setLastError($lastError)
23812505

23822506
return $this;
23832507
}
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+
}
23842523
}

0 commit comments

Comments
 (0)