Created
December 7, 2012 17:12
-
-
Save mermshaus/4234769 to your computer and use it in GitHub Desktop.
PHP source code obfuscator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
obfuscate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace org\ermshaus; | |
use Exception; | |
/** | |
* Obfuscates PHP source code | |
* | |
* *** The code is by no means feature complete. *** | |
* | |
* Example input: | |
* | |
* <?php | |
* | |
* namespace org\example; | |
* | |
* // A class | |
* class App { | |
* public function __construct() { $this->sayHello(); } | |
* public function sayHello() { echo "Hello World!\n"; } | |
* } | |
* | |
* new App(); | |
* | |
* Obfuscated output (line breaks added): | |
* | |
* <?php | |
* namespace _0\_1;class _0{public function __construct(){$this->_1();}publi | |
* c function _1(){echo"\110\145\154\154\157\40\127\157\162\154\144\41\12";} | |
* }new _0(); | |
* | |
* Obfuscate a file: | |
* | |
* $ php -f obfuscate.php < file.php > file-obfuscated.php | |
* | |
* Obfuscate this file (obfuscate.php) and then test the obfuscated obfuscator: | |
* | |
* $ php -f obfuscate.php < obfuscate.php > obfuscate | |
* $ echo '<?php echo "Hello world!\n";' | php -f obfuscate | php | |
* | |
* @todo $funcmap should not be used to hold functions as well as methods and | |
* instance variables. | |
* | |
* @copyright (c) 2012, Marc Ermshaus <marc@ermshaus.org> | |
* @license http://www.gnu.org/licenses/gpl-3.0.en.html | |
* GNU General Public License v3 | |
* @version 2013-06-02 | |
*/ | |
class Obfuscator | |
{ | |
/** | |
* The obfuscated string will be assembled in this variable | |
* | |
* @var string | |
*/ | |
protected $out; | |
// Arrays used for alias mappings | |
protected $varmap; | |
protected $funcmap; | |
protected $namespacemap; | |
// Parser states | |
protected $stateNextVariableIsInstanceVariable; | |
protected $stateNextStringIsClassName; | |
protected $stateNextStringIsFunctionName; | |
protected $stateNextStringIsNamespaceName; | |
protected $stateNextStringIsObjectMember; | |
// Data | |
protected $reservedVariableNames = array( | |
'$this' | |
); | |
protected $reservedConstantNames = array( | |
'false', | |
'true', | |
'null', | |
'STDERR', | |
'STDIN', | |
'STDOUT', | |
); | |
protected $reservedFunctionNames = array( | |
'__call', | |
'__callStactic', | |
'__clone', | |
'__construct', | |
'__destruct', | |
'__get', | |
'__invoke', | |
'__isset', | |
'__set', | |
'__set_state', | |
'__sleep', | |
'__toString', | |
'__unset', | |
'__wakeup' | |
); | |
// Configuration settings | |
protected $removeWhiteSpace = false; | |
protected $removeComments = false; | |
protected $whiteSpaceChar = ' '; | |
protected $obfuscateStrings = true; | |
protected $obfuscateStringsMode = 'oct'; // oct, hex | |
protected $replaceConstants = true; | |
protected $replaceNamespaces = true; | |
protected $variableNamePattern = '_%s'; | |
protected $functionNamePattern = '_%s'; | |
protected $namespaceNamePattern = '_%s'; | |
/** | |
* | |
*/ | |
public function __construct() | |
{ | |
$this->reset(); | |
$newNamespaceMap = new AliasMap(); | |
$newNamespaceMap->addAlias('test'); | |
if ($newNamespaceMap->hasAlias('test')) { | |
$x = $newNamespaceMap->getAlias('test'); | |
} | |
} | |
/** | |
* | |
*/ | |
protected function reset() | |
{ | |
$this->varmap = array(); | |
$this->funcmap = array(); | |
$this->namespacemap = array(); | |
$this->out = ''; | |
$this->stateNextVariableIsInstanceVariable = false; | |
$this->stateNextStringIsClassName = false; | |
$this->stateNextStringIsFunctionName = false; | |
$this->stateNextStringIsNamespaceName = false; | |
$this->stateNextStringIsObjectMember = false; | |
} | |
/** | |
* | |
* @param string $s | |
* @return string | |
*/ | |
protected function unescapeString($s) | |
{ | |
ob_start(); | |
eval('echo ' . $s . ';'); | |
return ob_get_clean(); | |
} | |
/** | |
* Appends whitespace to end of working string if necessary | |
* | |
* @param string $s | |
* @return string | |
*/ | |
protected function appendWhiteSpaceIfNecessary($s) | |
{ | |
if ( | |
1 === preg_match('/^\w/u', $s) | |
&& 1 === preg_match('/\w(?!\n)$/u', $this->out) | |
) { | |
$s = $this->whiteSpaceChar . $s; | |
} | |
return $s; | |
} | |
/** | |
* | |
* @param string $s | |
* @return string | |
*/ | |
protected function handleEncapsedString($s) | |
{ | |
if (!$this->obfuscateStrings) { | |
return $s; | |
} | |
$ret = ''; | |
$unescaped = $this->unescapeString($s); | |
$chars = str_split($unescaped); | |
foreach ($chars as $c) { | |
if ($c === '') { | |
continue; | |
} | |
if ($this->obfuscateStringsMode === 'oct') { | |
$ret .= "\\" . decoct(ord($c)); | |
} else { | |
$ret .= "\\x" . sprintf('%02x', ord($c)); | |
} | |
} | |
$ret = '"' . $ret . '"'; | |
return $ret; | |
} | |
protected function getFuncName($s) | |
{ | |
if (!array_key_exists($s, $this->funcmap)) { | |
$name = sprintf($this->functionNamePattern, count($this->funcmap)); | |
$this->funcmap[$s] = $name; | |
} | |
return $this->funcmap[$s]; | |
} | |
protected function hasFuncName($s) | |
{ | |
return array_key_exists($s, $this->funcmap); | |
} | |
protected function getNamespaceName($s) | |
{ | |
if (!$this->replaceNamespaces) { | |
return $s; | |
} | |
if (!array_key_exists($s, $this->namespacemap)) { | |
$name = sprintf( | |
$this->namespaceNamePattern, | |
count($this->namespacemap) | |
); | |
$this->namespacemap[$s] = $name; | |
} | |
return $this->namespacemap[$s]; | |
} | |
/** | |
* | |
* @param string $s | |
* @return string | |
*/ | |
protected function handleString($s) | |
{ | |
if ($this->stateNextStringIsNamespaceName) { | |
return $this->getNamespaceName($s); | |
} | |
$ret = ''; | |
if ($this->stateNextStringIsFunctionName) { | |
if (in_array($s, $this->reservedFunctionNames)) { | |
$ret = $s; | |
} else { | |
$ret = $this->getFuncName($s); | |
} | |
$this->stateNextStringIsFunctionName = false; | |
} elseif ($this->stateNextStringIsClassName) { | |
$ret = $this->getFuncName($s); | |
$this->stateNextStringIsClassName = false; | |
} elseif ($this->hasFuncName($s)) { | |
$ret = $this->getFuncName($s); | |
} elseif ($this->hasFuncName('$' . $s)) { | |
$var = $this->getFuncName('$' . $s); | |
$ret = substr($var, 1); | |
} elseif ( | |
$this->replaceConstants | |
&& defined($s) | |
&& !(in_array($s, $this->reservedConstantNames)) | |
) { | |
$ret = constant($s); | |
} elseif ($this->stateNextStringIsObjectMember) { | |
$ret = $this->getFuncName($s); | |
} else { | |
$ret = $s; | |
} | |
$this->stateNextStringIsObjectMember = false; | |
return $ret; | |
} | |
/** | |
* | |
* @param string $var | |
* @return string | |
*/ | |
protected function handleVariable($var) | |
{ | |
if (in_array($var, $this->reservedVariableNames)) { | |
return $var; | |
} | |
if ($this->stateNextVariableIsInstanceVariable) { | |
if (!array_key_exists($var, $this->funcmap)) { | |
$name = sprintf( | |
'$' . $this->variableNamePattern, | |
count($this->funcmap) | |
); | |
$this->funcmap[$var] = $name; | |
} | |
$this->stateNextVariableIsInstanceVariable = false; | |
return $this->funcmap[$var]; | |
} | |
if (!array_key_exists($var, $this->varmap)) { | |
$name = sprintf( | |
'$' . $this->variableNamePattern, | |
count($this->varmap) | |
); | |
$this->varmap[$var] = $name; | |
} | |
return $this->varmap[$var]; | |
} | |
/** | |
* | |
* @param string $source Source to obfuscate | |
* @return string Obfuscated source | |
*/ | |
public function obfuscate($source) | |
{ | |
$this->reset(); | |
$tokens = token_get_all($source); | |
$tokenName = ''; | |
$valueToAppend = ''; | |
foreach ($tokens as $token) { | |
if (is_string($token)) { | |
$tokenName = $token; | |
$valueToAppend = $token; | |
} else { | |
$tokenName = $token[0]; | |
$valueToAppend = $token[1]; | |
} | |
switch ($tokenName) { | |
case ';': | |
$this->stateNextStringIsNamespaceName = false; | |
break; | |
case T_COMMENT: | |
case T_DOC_COMMENT: | |
if ($this->removeComments) { | |
$valueToAppend = ''; | |
} | |
break; | |
case T_WHITESPACE: | |
if ($this->removeWhiteSpace) { | |
$valueToAppend = ''; | |
} | |
break; | |
case T_NEW: | |
case T_CLASS: | |
$this->stateNextStringIsClassName = true; | |
break; | |
case T_FUNCTION: | |
$this->stateNextVariableIsInstanceVariable = false; | |
$this->stateNextStringIsFunctionName = true; | |
break; | |
case T_STRING: | |
$valueToAppend = $this->handleString($valueToAppend); | |
break; | |
case T_CONSTANT_ENCAPSED_STRING: | |
$valueToAppend = $this->handleEncapsedString( | |
$valueToAppend | |
); | |
break; | |
case T_VARIABLE: | |
$valueToAppend = $this->handleVariable($valueToAppend); | |
break; | |
case T_PUBLIC: | |
case T_PROTECTED: | |
case T_PRIVATE: | |
$this->stateNextVariableIsInstanceVariable = true; | |
break; | |
case T_NAMESPACE: | |
$this->stateNextStringIsNamespaceName = true; | |
break; | |
case T_OBJECT_OPERATOR: | |
$this->stateNextStringIsObjectMember = true; | |
break; | |
default: | |
// nop | |
break; | |
} | |
$this->out .= $this->appendWhiteSpaceIfNecessary($valueToAppend); | |
} | |
// Because we can | |
$this->out .= "\n"; | |
return $this->out; | |
} | |
} | |
/** | |
* | |
*/ | |
class AliasMap | |
{ | |
protected $symbolToAliasMap; | |
protected $generationStrategy; | |
public function __construct(Closure $generationStrategy = null) | |
{ | |
$this->symbolToAliasMap = array(); | |
$this->generationStrategy = function ($map, $symbol) { | |
#return sprintf('_%s', count($map->symbolToAliasMap)); | |
}; | |
if ($generationStrategy !== null) { | |
$this->generationStrategy = $generationStrategy; | |
} | |
} | |
public function addAlias($symbol) | |
{ | |
$func = $this->generationStrategy; | |
$alias = $func($this, $symbol); | |
$this->symbolToAliasMap[$symbol] = $alias; | |
} | |
public function hasAlias($symbol) | |
{ | |
return array_key_exists($symbol, $this->symbolToAliasMap); | |
} | |
public function getAlias($symbol) | |
{ | |
if (!$this->hasAlias($symbol)) { | |
throw new Exception(''); | |
} | |
return $this->symbolToAliasMap[$symbol]; | |
} | |
} | |
error_reporting(-1); | |
ini_set('display_errors', 1); | |
$source = ''; | |
while (!feof(STDIN)) { | |
$source .= fgets(STDIN); | |
} | |
$obfuscator = new Obfuscator(); | |
$obfuscated = $obfuscator->obfuscate($source); | |
fwrite(STDOUT, $obfuscated); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment