From b503b8124cd6129ec0c1cb413ea26a32570d8f8a Mon Sep 17 00:00:00 2001 From: nichogenius Date: Sat, 19 Aug 2017 19:55:04 -0600 Subject: [PATCH] Added Comments and Documentation Added Comments and Documentation --- scan.php | 108 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/scan.php b/scan.php index 6ddd354..4d62a62 100644 --- a/scan.php +++ b/scan.php @@ -44,24 +44,24 @@ class MalwareScanner ); private $followSymlink = false; + //Pattern File Attributes private $patterns_raw = array(); private $patterns_iraw = array(); private $patterns_re = array(); private $patterns_b64functions = array(); private $patterns_b64keywords = array(); + //Constructor - Likes to do as little as possible. public function __construct() { //Read Run Options $this->parseArgs(); - //Load Patterns - $this->initializePatterns(); - //Initiate Scan $this->run($this->dir); } + //Allows the -n/--no-color flag to easily remove color characters. private function disableColor() { $this->ANSI_GREEN = ''; @@ -71,6 +71,8 @@ class MalwareScanner $this->ANSI_OFF = ''; } + //Prints the passed 'string' in red text, calls showHelp(). + //Exits private function error($msg) { echo $this->ANSI_RED . 'Error: ' . $msg . $this->ANSI_OFF . PHP_EOL; @@ -79,29 +81,35 @@ class MalwareScanner exit(-1); } + //Handles pattern loading and saving to the class object private function initializePatterns() { + //Loads either the primary scanning patterns or the base64 patterns depending on -b/--base64 flag if (!$this->flagBase64) { $this->patterns_raw = $this->loadPatterns(dirname(__FILE__) . '/patterns_raw.txt'); $this->patterns_iraw = $this->loadPatterns(dirname(__FILE__) . '/patterns_iraw.txt'); $this->patterns_re = $this->loadPatterns(dirname(__FILE__) . '/patterns_re.txt'); } - - if ($this->flagBase64) { + else { $this->patterns_b64functions = $this->loadPatterns(dirname(__FILE__). '/php_functions.txt'); $this->patterns_b64keywrds = $this->loadPatterns(dirname(__FILE__). '/php_keywords.txt'); } - + + //Adds additional checks to patterns_raw + //This may be something to move into a pattern file rather than leave hardcoded. if ($this->extraCheck) { - array_push($this->patterns_raw, "googleBot", "htaccess"); + $this->patterns_raw['googleBot'] = '# '; + $this->patterns_raw['htaccess'] = '# '; } } + //Check if the md5 checksum exists in the whitelist and returns true if it does. private function inWhitelist($hash) { return in_array($hash, $this->whitelist); } + //Check if -i/--ignore flag listed this path to be omitted. private function isIgnored($pathname) { foreach ($this->ignore as $pattern) { @@ -113,6 +121,10 @@ class MalwareScanner return false; } + //Loads individual pattern files + //Skips blank linese + //Stores most recent comment with the pattern in the list[] array + //Returns an array of patterns:comments in key:value pairs private function loadPatterns($file) { $last_comment = ''; @@ -124,6 +136,8 @@ class MalwareScanner continue; } //Check if first char in pattern is a '#' which indicates a comment and skips. + //Stores the comment to be stored with the pattern in the list as key:value pairs. + //The pattern is the key and the comment is the value. if ($pattern[0] === '#') { $last_comment = $pattern; continue; @@ -134,6 +148,7 @@ class MalwareScanner return $list; } + //Loads the whitelist file private function loadWhitelist() { if (!is_file(__DIR__ . '/whitelist.txt')) { @@ -146,6 +161,8 @@ class MalwareScanner } } + //Handles the getopt() function call, sets attributes according to flags. + //All flag handling stuff should be setup here. private function parseArgs() { $options = getopt('d:e:i:bmcxlhkwntv', array('directory:', 'extension:', 'ignore:', 'base', 'checksum', 'comment', 'extra-check', 'follow-link', 'help', 'hide-ok', 'hide-whitelist', 'no-color', 'time', 'verbose')); @@ -173,13 +190,13 @@ class MalwareScanner } //Simple Flag Options - if (isset($options['base64']) || isset($options['b'])){ + if (isset($options['base64']) || isset($options['b'])) { $this->flagBase64 = true; } - if (isset($options['checksum']) || isset($options['m'])){ + if (isset($options['checksum']) || isset($options['m'])) { $this->flagChecksum = true; } - if (isset($options['comment']) || isset($options['c'])){ + if (isset($options['comment']) || isset($options['c'])) { $this->flagComments = true; } if (isset($options['extra-check']) || isset($options['x'])) { @@ -194,13 +211,13 @@ class MalwareScanner if (isset($options['hide-whitelist']) || isset($options['w'])) { $this->flagHideWhitelist = true; } - if (isset($options['no-color']) || isset($options['n'])){ + if (isset($options['no-color']) || isset($options['n'])) { $this->disableColor(); } - if (isset($options['time']) || isset($options['t'])){ + if (isset($options['time']) || isset($options['t'])) { $this->flagTime = true; } - if (isset($options['verbose']) || isset($options['v'])){ + if (isset($options['verbose']) || isset($options['v'])) { $this->verbose = true; } } @@ -231,6 +248,16 @@ class MalwareScanner return (bool)preg_match($expr, $path); } + /* + Formats and prints the scan result output line by line. + Depending on specified options, it will print: + -Status code + -Last Modified Time + -MD5 Hash + -File Path + -Pattern Matched + -The last comment to appear in the pattern file before this pattern + */ private function printPath(&$found, &$path, &$pattern, &$comment, &$hash) { $output_string = '# '; @@ -261,7 +288,7 @@ class MalwareScanner $output_string = $output_string . $this->ANSI_BLUE . $htime . $this->ANSI_OFF . ' '; } - //Include Checksum + //Include Checksum/Hash if ($this->flagChecksum) { $output_string = $output_string . $this->ANSI_BLUE . $hash . $this->ANSI_OFF . ' '; } @@ -286,6 +313,8 @@ class MalwareScanner echo $output_string; } + //Recursively scales the file system. + //Calls the scan() function for each file found. private function process($dir) { $dh = opendir($dir); @@ -315,6 +344,7 @@ class MalwareScanner closedir($dh); } + //Prints stats on the run. private function report($start, $dir) { $end = time(); @@ -327,6 +357,9 @@ class MalwareScanner echo 'Total malware identified: ' . $this->stat['files_infected'] . PHP_EOL; } + //Validates the input directory + //Calls the load pattern and load whitelist functions + //Calls the process and report functions. private function run($dir) { //Make sure a directory was specified. @@ -340,16 +373,22 @@ class MalwareScanner $this->error('Specified path is not a directory: ' . $dir); } + //Load Patterns + $this->initializePatterns(); + + //Load Whitelist + $this->loadWhitelist(); + $start = time(); - $this->loadWhitelist(); $this->process($dir . '/'); $this->report($start, $dir . '/'); } + //Loads target file contents for scanning + //Initiates the multiple scan types by calling the scanLoop function private function scan($path) { $this->stat['files_scanned']++; - $fileContent = file_get_contents($path); $found = false; $hash = ''; @@ -361,7 +400,7 @@ class MalwareScanner $this->scanLoop('scanFunc_STRI', $fileContent, $this->patterns_iraw, $path, $found, $hash); $this->scanLoop('scanFunc_RE', $fileContent, $this->patterns_re, $path, $found, $hash); } - if ($this->flagBase64) { + else { $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64functions, $path, $found, $hash); $this->scanLoop('scanFunc_STR', $fileContent, $this->patterns_b64keywords, $path, $found, $hash); } @@ -379,40 +418,52 @@ class MalwareScanner return true; } + //Performs raw string, case sensitive matching. + //Returns true if the raw string exists in the file contents. private function scanFunc_STR(&$pattern, &$content) { return (strpos($content, $pattern) !== false); } + //Performs raw string, case insensitive matching. + //Returns true if the raw string exists in the file contents, ignoring case. private function scanFunc_STRI(&$pattern, &$content) { return (stripos($content, $pattern) !== false); } + //Performs regular expression matching. + //Returns true if the Regular Expression matches something in the file. + //Patterns will match multiple lines, though you can use ^$ to match the beginning and end of a line. private function scanFunc_RE(&$pattern, &$content) { return preg_match('/' . $pattern . '/im', $content); } + //First parameter '$scanFunction' is a defined function name passed as a string. + //This function should accept a pattern string and a content string. + //This function will return true if the pattern exists in the content. + //See 'scanFunc_STR', 'scanFunc_STRI', 'scanFUNC_RE' above as examples. + + //Loops through all patterns in a file using the passed function name to determine a match. + //Variables passed by reference for performance and modification access. private function scanLoop($scanFunction, &$fileContent, &$patterns, &$path, &$found, &$hash) { if (!$found || $this->verbose) { foreach ($patterns as $pattern => $comment) { + //Call the function that is named in $scanFunction + //This allows multiple search/match functions to be used without duplicating the loop code. if ($this->$scanFunction($pattern, $fileContent)) { $found = true; - if ($hash === ''){ - $hash = md5($fileContent); - } + if ($hash === ''){$hash = md5($fileContent);} $this->printPath($found, $path, $pattern, $comment, $hash); - if (!$this->verbose){ - return; - } + if (!$this->verbose){return;} } } } } - + //Prints out the usage menu options. private function showHelp() { echo 'Usage: scan.php -d ' . PHP_EOL; @@ -421,17 +472,18 @@ class MalwareScanner echo ' -e --extension File Extension to Scan' . PHP_EOL; echo ' -i --ignore Directory of file to igonre' . PHP_EOL; echo ' -b --base64 Scan for base64 encoded PHP keywords' . PHP_EOL; - echo ' -m --checksum Display MD5 Checksum of file' . PHP_EOL; + echo ' -m --checksum Display MD5 Hash/Checksum of file' . PHP_EOL; echo ' -c --comment Display comments for matched patterns' . PHP_EOL; echo ' -x --extra-check Adds GoogleBot and htaccess to Scan List' . PHP_EOL; echo ' -l --follow-symlink Follow symlinked directories' . PHP_EOL; - echo ' -k --hide-ok Hide OK aka not infected messages' . PHP_EOL; - echo ' -w --hide-whitelist Hide whitelisted messages' . PHP_EOL; + echo ' -k --hide-ok Hide results with \'OK\' status' . PHP_EOL; + echo ' -w --hide-whitelist Hide results with \'WL\' status' . PHP_EOL; echo ' -t --time Show time of last file change' . PHP_EOL; - echo ' -v --verbose Continue scanning file afater first hit' . PHP_EOL; + echo ' -v --verbose Continue scanning file after first hit' . PHP_EOL; } } +//Creates a new MalwareScanner object which does all the work. new MalwareScanner(); ?>