127) return false; } return true; } // Command Line Help Listing function Help() { echo("\n"); echo("-input [filename] (Required)\n"); echo("-output [filename] (Optional, default is appended to results.log)\n"); echo("-report [logfile] (Optional, default none)\n"); echo("-blocksize [byte size] (Optional, default is 1 byte at a time)\n"); echo("Example:\n"); echo("filescan.php -input banner.jpg -output banner.hex -blocksize 512\n\n"); } // Converts decimal to hex function dec2hex($string) { return dechex($string); } // Converts hex to binary function hex2bin($s) { for ($i = 0; $i < strlen($s); $i += 2) { If(IsSet($bin)) $bin .= chr(hexdec(substr($s,$i,2))); else $bin = chr(hexdec(substr($s,$i,2))); } return $bin; } // Converts hex to decimal function hex2dec($string) { return hexdec($string); } // Create and write log file. function report($filename, $contents) { $handle = fopen($filename, "a"); if (fwrite($handle, "[". date("m-j-Y g:i:sa") ."] $contents\n") === FALSE) echo ("Cannot write to log file $filename\n"); fclose($handle); } // Converts string to hex function str2hex($string) { $hex = ''; $len = strlen($string); for ($i = 0; $i < $len; $i++) { $hex .= str_pad(dechex(ord($string[$i])), 2, 0, STR_PAD_LEFT); } return $hex; } // Search the supplied $contents string for a match within the $searchlist array. 0=header & footer, 1=header, 2=footer function hexsearch($contents, $searchlist, $searchtype = 0) { foreach ($searchlist as &$filetype) { //Compare BINARY string to BINARY header. if (IsSet($filetype['header'])&&($searchtype<=1)) { // Grab portion of data in memory equal to the size of the header and put into $tcontents. $tcontents = substr($contents, 0, strlen(hex2bin($filetype['header']))); // Now compare the header to the memory $tcontents to see if ascii case-insensitive/binary match. This also // confirms that 'header' is a pure ASCII string, otherwise no point in doing case-insensitive match check. if ($tcontents==hex2bin($filetype['header'])||(PureASCII(hex2bin($filetype['header']))&&(strtolower($tcontents)==strtolower(hex2bin($filetype['header']))))) { if (DEBUG_MODE) echo "ÿû ". $filetype['type'] ." header(". $filetype['header'] .") found at Offset: ". (dec2hex($start)) ." (". ($start) ." bytes in)\n"; // Add HEX location to $result array if (IsSet($result)) $counter = count($result); else $counter = 0; $result[$counter]['extension']=$filetype['extension']; if (IsSet($filetype['type'])) $result[$counter]['type']=$filetype['type']; $result[$counter]['header']=$filetype['header']; //$result[$counter]['offset']=(dec2hex($start)); // Should now jump to end of this buffer header since a confirmed hit. This fucks up the new // blocksize definition and needs to be fixed as it will put the file out of place from incrementing // per the blocksize. //$start=($start+strlen(hex2bin($filetype['header']))); } } if (IsSet($filetype['footer'])&&(($searchtype==0)||($searchtype==2))) { $tcontents = substr($contents, 0, strlen(hex2bin($filetype['footer']))); //if ($tcontents==hex2bin($filetype['footer'])) if (($tcontents==hex2bin($filetype['footer']))||(strtolower($tcontents)==strtolower(hex2bin($filetype['footer'])))) { if (DEBUG_MODE) echo "ÿû ". $filetype['type'] ." footer(". $filetype['footer'] .") found at Offset: ". (dec2hex($start)) ." (". ($start) ." bytes in)\n"; // Add HEX location to $result array. if (IsSet($result)) $counter = count($result); else $counter = 0; //$result[$counter]['file']=($filename); $result[$counter]['extension']=$filetype['extension']; if (IsSet($filetype['type'])) $result[$counter]['type']=$filetype['type']; $result[$counter]['footer']=$filetype['footer']; //$result[$counter]['offset']=(dec2hex($start)); // Should now jump to end of this buffer header since a confirmed hit. This fucks up the new // blocksize definition and needs to be fixed as it will put the file out of place from incrementing // per the blocksize. //$start=($start+strlen(hex2bin($filetype['footer']))); } } } if (IsSet($result)) return $result; else return; } // Given an array of hex, search for positive hits in the supplied filename, optional bs=1bytes (512bytes=1sector?) function filefind($searchlist, $filename, $blocksize = 1) { $searchlistmaxlen = 0; $filesize = filesize($filename); echo "þ Processing $filename (block size=$blocksize)...\n"; if (!$handle = fopen($filename, "r")) die ("Cannot open $filename\n"); // Lower case the header and footer hex codes and strip out unexpected characters. Also check case sensitive flag. foreach ($searchlist as &$filetype) { if(IsSet($filetype['header'])) { $filetype['header'] = strtolower($filetype['header']); $filetype['header'] = str_replace(" ", "", $filetype['header']); $filetype['header'] = str_replace("-", "", $filetype['header']); if (strlen(hex2bin($filetype['header']))>$searchlistmaxlen) $searchlistmaxlen = (strlen(hex2bin($filetype['header']))); } if(IsSet($filetype['footer'])) { $filetype['footer'] = strtolower($filetype['footer']); $filetype['footer'] = str_replace(" ", "", $filetype['footer']); $filetype['footer'] = str_replace("-", "", $filetype['footer']); if (strlen(hex2bin($filetype['footer']))>$searchlistmaxlen) $searchlistmaxlen = (strlen(hex2bin($filetype['footer']))); } // HEX in ASCII is not case sensitive unless we specifically say it is here. if(IsSet($filetype['case_sensitive'])) { if ($filetype['case_sensitive']==0) unSet($filetype['case_sensitive']); } } // Count through file and ensure don't go over filesize. The counter incrementer is at the end of this loop // as it wouldnt allow the (if) condition to be nested within the for statement. Boo-hoo. for ($start = 0; $start <= $filesize;) { // Jumping ahead to next proper byte place. if($start>0) $contents = fseek($handle, $start); // Read in buffer size that is equal to maximum HEX header size $contents = fread($handle, $searchlistmaxlen); // Compare each HEX header and footer (hence the value 0 at the end) to the contents in memory. $stringresults=(hexsearch($contents,$searchlist,0)); // Now need to go through array and correct the returned offset since it will be limited to only the size of // the string. if (IsSet($stringresults)) { foreach ($stringresults as &$searchhit) { echo (" Hit at ". hex2dec($start) . " ($start) ... \n"); $searchhit['file']=$filename; //$searchhit['offset']=(dec2hex($start+hex2dec($searchhit['offset']))); $searchhit['offset']=dec2hex($start); } if (IsSet($results)) $results = array_merge($results, $stringresults); else $results=$stringresults; } // Increment the loop to the next sector($blocksize), and if filespace left to go through is less than the // blocksize, then just go one at a time. if (($filesize-$start)<$blocksize) $start++; else $start=$start+$blocksize; } // Increment to next file spot in file. fclose($handle); if (isset($results)) return $results; else return 0; } // ***************** MAIN APP *************************** echo "\nfilescan v". VERSION ." - Gobble Gobble\n"; if (DEBUG_MODE) { echo " DEBUG_MODE=1\n"; echo " using PHP ". phpversion() . "\n"; echo 'The number of arguments that were fed: ', $_SERVER['argc'],"\n"; echo 'And those arguments are: '; print_r($_SERVER['argv']); echo "\n\n"; } if (!($_SERVER['argc'] > 1)) { Help(); die("\n\n"); } for ($counter = 0; $counter < count($_SERVER['argv']); $counter++) { if (strtolower($_SERVER['argv'][$counter]) == "-blocksize") $blocksize = ($_SERVER['argv'][$counter+1]); if (strtolower($_SERVER['argv'][$counter]) == "-input") $filename = ($_SERVER['argv'][$counter+1]); if (strtolower($_SERVER['argv'][$counter]) == "-output") $output = ($_SERVER['argv'][$counter+1]); if (strtolower($_SERVER['argv'][$counter]) == "-report") $log = ($_SERVER['argv'][$counter+1]); } if (!IsSet($blocksize)) $blocksize = 1; if (!IsSet($filename)) { Help(); exit; } if (!IsSet($output)) $output = "results.log"; if (!IsSet($filename)) { Help(); exit; } if (IsSet($log)) report($log, $_SERVER['argv'][0]." -input $filename -output $output -blocksize $blocksize -report $log"); $results = filefind($searchlist, $filename, $blocksize); if ($results==0) { echo " No headers found."; exit; } if (DEBUG_MODE) { echo "\nResults (". count($results) ." found): \n"; for ($counter = 0; $counter <= count($results); $counter++) { if (IsSet($results[$counter]['file'])) echo "Source filename: ". $results[$counter]['file'] ."\n"; if (IsSet($results[$counter]['extension'])) echo "File extension: ". $results[$counter]['extension'] ."\n"; if (IsSet($results[$counter]['type'])) echo "Desc: ". $results[$counter]['type'] ."\n"; if (IsSet($results[$counter]['header'])) echo "Header: ". $results[$counter]['header'] ."\n"; if (IsSet($results[$counter]['footer'])) echo "Footer: ". $results[$counter]['footer'] ."\n"; if (IsSet($results[$counter]['offset'])) echo "Offset: ". $results[$counter]['offset'] ."\n"; echo "\n"; } } if ((file_exists($output))&&is_writable($output)) { if (!$handle = fopen($output, 'a')) echo "Cannot open $output\n"; } else { if (!$handle = fopen($output, 'w')) echo "Cannot open $output\n"; } for ($counter = 0; $counter < count($results); $counter++) { if (IsSet($log)) report($log, "Adding " . $results[$counter]['file'] . " (". $results[$counter]['offset'] . " matches ". $results[$counter]['extension'] ." ) to $output."); if (fwrite($handle, "{\n") === FALSE) echo "Cannot write to file ($output)"; if (IsSet($results[$counter]['file'])) fwrite($handle, "FILE:". $results[$counter]['file'] ."\n"); if (IsSet($results[$counter]['extension'])) fwrite($handle, "EXTENSION:". $results[$counter]['extension'] ."\n"); if (IsSet($results[$counter]['type'])) fwrite($handle, "TYPE:". $results[$counter]['type'] ."\n"); if (IsSet($results[$counter]['offset'])) fwrite($handle, "OFFSET:". $results[$counter]['offset'] ."\n"); if (IsSet($results[$counter]['header'])) fwrite($handle, "HEADER:". $results[$counter]['header'] ."\n"); if (IsSet($results[$counter]['footer'])) fwrite($handle, "FOOTER:". $results[$counter]['footer'] ."\n"); if (fwrite($handle, "}\n") === FALSE) echo "Cannot write to file ($filename)"; } fclose($handle); if (IsSet($log)) report($log, " Completed."); /* rev 6-13: Went back to segregated header/footer scan and then to process this seperately in carver program. Renamed to filescan. Should assume 1byte at a time approach, though will still support 12byte sector approach as well since already have the weird logic in place for it. Changed default $blocksize back to 1 byte at a time. rev 6-11: Changed default $blocksize to 512. Renamed to headgrab as this will grab matching headers at each sector only and will shift logic to turkeycarver. Also commented out footers as was getting the occasional "footer" matchup at the first few bytes of a new sector. This seems to have resolved some minor file issues. rev 6-10: Added $blocksize support so that it can count through by sector instead of one byte at a time. Still need to make it flexible in case the end of the file is not a multiple of the block size. rev 6-1: Added file: directive so that turkeycarver can actually figure out the filename to apply the log file against. Whoops. */ ?>