2016年2月14日 星期日

為 PHP 檔加密(二)

早幾天編寫的「為 PHP 檔加密」,在應用上遇到一個情況,就是我寫的 PHP 很多時都會匯入其他 PHP 檔。要是匯入的 PHP 已被加密,那麼輸出來的 PHP 將會是加密中有加密;這會導致程式無法正常執行。原本我打算把被匯入的那些 PHP 加密就算數了事,但那些 PHP 多數是數據庫的中介程式,工作簡單,沒有需要保護的必要。那麼,該如何是好?我想出以下方法。

通常我是以「require()」指令來匯入其他 PHP,而這個功能就像是把匯入 PHP 嵌入到當前 PHP 內一樣。既然如此,那麼索性做這樣的處理,把匯入 PHP 嵌入後才加密,便不會出現「加密中有加密」的情況。我把程式改了一下:
<?php
//----------------------------------------------------------------------------------------
//  PHP File Obfuscator v1.20
//  This program will compress a PHP file and name it .min.php
//----------------------------------------------------------------------------------------
//  Written by Pacess HO
//  Copyright 2016 Pacess Studio.  All rights reserved.
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
//  1.00 - Search all .php in current and its child directories and apply encryption.
//  1.10 - Search all .php in current directory only since there are some .php do not need
//         encryption.  Moreover, this version will load all "require(xxx)" php and inject
//         into the opened .php which prevent encryption not working with require.
//  1.20 - Just encrypted .php which not encrypted yet, ignore those encrypted .php, add
//         encryption date time as well.

//----------------------------------------------------------------------------------------
function getDirectoryArray($directory)  {
 $resultArray = array(); 
 $fileArray = scandir($directory); 
 foreach ($fileArray as $key => $value)  {

  //  Skip . & ..
  if (in_array($value, array(".", "..")))  {continue;}

  $filePath = $directory.DIRECTORY_SEPARATOR.$value;
  if (is_dir($filePath) == false)  {$resultArray[] = $filePath;}
  }
 } 
 return $resultArray; 
} 

//=========================================================================================
//  Main program
$encryptedPHPHeader = "<?php\n".
 "//----------------------------------------------------------------------------------------\n".
 "//  Written by Pacess HO\n".
 "//  Encrypted at ".date("Y-m-d H:i:s")."\n".
 "//  Copyright 2015-2016 Pacess Studio.  All rights reserved.\n".
 "//----------------------------------------------------------------------------------------\n".
 "ob_start();";
$encryptedPHPFooter = 'eval(gzuncompress(base64_decode($code)));$v=ob_get_contents();ob_end_clean(); ?>';
$encrypedPHPCount = 0;

$thisPHPFile = baseName(__FILE__);

echo("\n");
echo("----------------------------------------------------------------\n");
echo("--  PHP File Obfuscator v1.20                                 --\n");
echo("----------------------------------------------------------------\n");
echo("--  Written by Pacess HO                                      --\n");
echo("--  Copyright 2015-2016 Pacess Studio.  All rights reserved.  --\n");
echo("----------------------------------------------------------------\n");
echo("\n");

$fileArray = getDirectoryArray(".");
foreach ($fileArray as $key => $filePath)  {
 echo("#$key ");

 $tail = strtolower(substr($filePath, -8));
 if ($tail == ".min.php")  {
  echo("Skip [$filePath] because it is already compressed.\n");
  continue;
 }

 $tail = strtolower(substr($filePath, -4));
 if ($tail != ".php")  {
  echo("Skip [$filePath] because it is not a PHP file.\n");
  continue;
 }

 //  Check if this .php file, if yes then skip
 if (strpos($filePath, $thisPHPFile) !== false)  {
  echo("Skip [$filePath] because it is current tool file.\n");
  continue;
 }

 //  It is .php file, encrypt now!
 $data = "ob_end_clean();?>";

 //----------------------------------------------------------------------------------------
 //  Somehow encryption is not work if PHP include another encrypted PHP, so I read PHP
 //  source code here and inject those require-PHPs into the source
 $phpContent = php_strip_whitespace($filePath);

 //  Check if PHP already encrypted
 $position = stripos($phpContent, "eval(gzuncompress(base64_decode(");
 if ($position !== false)  {
  echo("Skip [$filePath] because it is already encrypted.\n");
  continue;
 }

 $patternStart = "require(";
 $patternEnd = ");";

 $lengthPatternStart = strlen($patternStart);
 $lengthPatternEnd = strlen($patternEnd);

 $count = 0;
 $finish = false;
 while ($finish == false)  {

  //  Just for safe, never include so much files
  $count++;
  if ($count > 50)  {$finish = true;}

  $position = stripos($phpContent, $patternStart);
  if ($position === false)  {$finish = true;}
  else  {

   //  +1/-1 because there is a quote ""
   $startPoint = $position+$lengthPatternStart+1;
   $endPoint = stripos($phpContent, $patternEnd, $startPoint);
   if ($endPoint === false)  {$finish = true;}
   else  {

    $pathArray = pathinfo($filePath);
    $length = $endPoint-$startPoint-1;
    $requiredFilename = substr($phpContent, $startPoint, $length);
    $requiredFilePath = $pathArray["dirname"]."/".$requiredFilename;

    //  Read required file content
    $requiredFileContent = php_strip_whitespace($requiredFilePath);

    //  Inject the content to PHP file
    $length = ($endPoint+$lengthPatternEnd)-$position;
    $requiredCommand = substr($phpContent, $position, $length);
    $phpContent = str_replace($requiredCommand, " ?>".$requiredFileContent."<?php ", $phpContent);
   }
  }
 }

 //  Since injection may produce PHP-CLOSE-PHP-OPEN pattern, remove it now
 $length = strlen($phpContent);
 $lastLength = 0;
 while ($length != $lastLength)  {
  $lastLength = $length;
  $phpContent = str_replace("?><?php", "", $phpContent);
  $length = strlen($phpContent);
 }

 $data .= $phpContent;

 //----------------------------------------------------------------------------------------
 //  Compress data
 $zipData = gzcompress($data, 9);
 $base64Data = base64_encode($zipData);
 $encryptedPHPContent = '$code=\''.$base64Data.'\';';

 $outputContent = $encryptedPHPHeader.$encryptedPHPContent.$encryptedPHPFooter;

 $outputPath = substr($filePath, 0, -4).".min.php";
 $result = file_put_contents($outputPath, $outputContent);
 echo("Encoding [$filePath]...");
 echo("[$outputPath]...$result bytes written.\n");

 $encrypedPHPCount++;
}
echo("\nTotal $encrypedPHPCount PHP files have been encrypted.\n~ Finish ~\n\n");
?>

沒有留言: