├── .gitignore ├── version.php ├── LICENSE.md ├── include ├── classes │ ├── parser_extensions │ │ ├── my_autoloader.php │ │ ├── my_pretty_printer.php │ │ └── my_node_visitor.php │ ├── config.php │ └── scrambler.php ├── check_version.php ├── get_default_defined_objects.php ├── functions.php └── retrieve_config_and_arguments.php ├── yakpro-po.php ├── yakpro-po.cnf ├── README.md └── locale └── fr └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | #----------------------- 2 | # directories 3 | #----------------------- 4 | PHP-Parser/* 5 | tests/* 6 | old/* 7 | #----------------------- 8 | # files 9 | #----------------------- 10 | run_tests.php 11 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Published under the MIT License 2 | 3 | Copyright (c) 2015-2018 Pascal KISSIAN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/classes/parser_extensions/my_autoloader.php: -------------------------------------------------------------------------------- 1 | {'extra'}->{'branch-alias'}->{'dev-master'}; 26 | $required_php_version = $t_composer->{'require'}->{'php'}; 27 | 28 | $operator = '';for($i=0;!ctype_digit($c=$required_php_version[$i]);++$i) $operator.=$c; $required_php_version = substr($required_php_version,$i); 29 | 30 | if (substr($php_parser_branch,0,2)!='4.') 31 | { 32 | fprintf(STDERR,"Error:\tWrong version of PHP-Parser detected!%sCurrently, only 4.x branch of PHP-Parser is supported!%s\tYou can try to use the following command:%s\t# %s%s",PHP_EOL,PHP_EOL,PHP_EOL,$php_parser_git_commandline,PHP_EOL); 33 | exit(22); 34 | } 35 | 36 | if (!version_compare(PHP_VERSION,$required_php_version,$operator)) 37 | { 38 | fprintf(STDERR,"Error:\tPHP Version must be %s %s%s",$operator,$required_php_version,PHP_EOL); 39 | exit(23); 40 | } 41 | 42 | 43 | ?> -------------------------------------------------------------------------------- /include/get_default_defined_objects.php: -------------------------------------------------------------------------------- 1 | $dummy) 24 | { 25 | $t = array_flip(array_map('strtolower', get_class_methods($pre_defined_class_name))); 26 | if (count($t)) $t_pre_defined_class_methods_by_class[$pre_defined_class_name] = $t; 27 | $t_pre_defined_class_methods = array_merge($t_pre_defined_class_methods, $t); 28 | 29 | $t = get_class_vars($pre_defined_class_name); 30 | if (count($t)) $t_pre_defined_class_properties_by_class[$pre_defined_class_name] = $t; 31 | $t_pre_defined_class_properties = array_merge($t_pre_defined_class_properties, $t); 32 | 33 | 34 | $r = new ReflectionClass($pre_defined_class_name); 35 | $t = $r->getConstants(); 36 | if (count($t)) $t_pre_defined_class_constants_by_class[$pre_defined_class_name] = $t; 37 | $t_pre_defined_class_constants = array_merge($t_pre_defined_class_constants, $t); 38 | } 39 | 40 | ?> 41 | -------------------------------------------------------------------------------- /include/classes/parser_extensions/my_pretty_printer.php: -------------------------------------------------------------------------------- 1 | obfuscate_string($node->value); if (!strlen($result)) return "''"; 31 | return '"'.$this->obfuscate_string($node->value).'"'; 32 | } 33 | 34 | 35 | //TODO: pseudo-obfuscate HEREDOC string 36 | protected function pScalar_Encapsed(PhpParser\Node\Scalar\Encapsed $node) 37 | { 38 | /* 39 | if ($node->getAttribute('kind') === PhpParser\Node\Scalar\String_::KIND_HEREDOC) 40 | { 41 | $label = $node->getAttribute('docLabel'); 42 | if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) 43 | { 44 | if (count($node->parts) === 1 45 | && $node->parts[0] instanceof PhpParser\Node\Scalar\EncapsedStringPart 46 | && $node->parts[0]->value === '' 47 | ) 48 | { 49 | return "<<<$label\n$label" . $this->docStringEndToken; 50 | } 51 | 52 | return "<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label" 53 | . $this->docStringEndToken; 54 | } 55 | } 56 | */ 57 | $result = ''; 58 | foreach ($node->parts as $element) 59 | { 60 | if ($element instanceof PhpParser\Node\Scalar\EncapsedStringPart) 61 | { 62 | $result .= $this->obfuscate_string($element->value); 63 | } 64 | else 65 | { 66 | $result .= '{' . $this->p($element) . '}'; 67 | } 68 | } 69 | return '"'.$result.'"'; 70 | } 71 | } 72 | 73 | ?> -------------------------------------------------------------------------------- /yakpro-po.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | Comand Line Interface Only!"; die; } 15 | 16 | 17 | const PHP_PARSER_DIRECTORY = 'PHP-Parser'; 18 | 19 | 20 | require_once 'include/check_version.php'; 21 | 22 | require_once 'include/get_default_defined_objects.php'; // include this file before defining something.... 23 | 24 | 25 | require_once 'include/classes/config.php'; 26 | require_once 'include/classes/scrambler.php'; 27 | require_once 'include/functions.php'; 28 | require_once 'version.php'; 29 | 30 | include 'include/retrieve_config_and_arguments.php'; 31 | 32 | require_once 'include/classes/parser_extensions/my_autoloader.php'; 33 | require_once 'include/classes/parser_extensions/my_pretty_printer.php'; 34 | require_once 'include/classes/parser_extensions/my_node_visitor.php'; 35 | 36 | 37 | if ($clean_mode && file_exists("$target_directory/yakpro-po/.yakpro-po-directory") ) 38 | { 39 | if (!$conf->silent) fprintf(STDERR,"Info:\tRemoving directory\t= [%s]%s","$target_directory/yakpro-po",PHP_EOL); 40 | remove_directory("$target_directory/yakpro-po"); 41 | exit(31); 42 | } 43 | 44 | use PhpParser\Error; 45 | use PhpParser\ParserFactory; 46 | use PhpParser\NodeTraverser; 47 | use PhpParser\PrettyPrinter; 48 | 49 | switch($conf->parser_mode) 50 | { 51 | case 'PREFER_PHP7': $parser_mode = ParserFactory::PREFER_PHP7; break; 52 | case 'PREFER_PHP5': $parser_mode = ParserFactory::PREFER_PHP5; break; 53 | case 'ONLY_PHP7': $parser_mode = ParserFactory::ONLY_PHP7; break; 54 | case 'ONLY_PHP5': $parser_mode = ParserFactory::ONLY_PHP5; break; 55 | default: $parser_mode = ParserFactory::PREFER_PHP5; break; 56 | } 57 | 58 | $parser = (new ParserFactory)->create($parser_mode); 59 | 60 | 61 | $traverser = new NodeTraverser; 62 | 63 | if ($conf->obfuscate_string_literal) $prettyPrinter = new myPrettyprinter; 64 | else $prettyPrinter = new PrettyPrinter\Standard; 65 | 66 | $t_scrambler = array(); 67 | //foreach(array('variable','function','method','property','class','class_constant','constant','label') as $scramble_what) 68 | foreach(array('variable','function_or_class','method','property','class_constant','constant','label') as $scramble_what) 69 | { 70 | $t_scrambler[$scramble_what] = new Scrambler($scramble_what, $conf, ($process_mode=='directory') ? $target_directory : null); 71 | } 72 | if ($whatis!=='') 73 | { 74 | if ($whatis[0] == '$') $whatis = substr($whatis,1); 75 | // foreach(array('variable','function','method','property','class','class_constant','constant','label') as $scramble_what) 76 | foreach(array('variable','function_or_class','method','property','class_constant','constant','label') as $scramble_what) 77 | { 78 | if ( ( $s = $t_scrambler[$scramble_what]-> unscramble($whatis)) !== '') 79 | { 80 | switch($scramble_what) 81 | { 82 | case 'variable': 83 | case 'property': 84 | $prefix = '$'; 85 | break; 86 | default: 87 | $prefix = ''; 88 | } 89 | echo "$scramble_what: {$prefix}{$s}".PHP_EOL; 90 | } 91 | } 92 | exit(32); 93 | } 94 | 95 | $traverser->addVisitor(new MyNodeVisitor); 96 | 97 | switch($process_mode) 98 | { 99 | case 'file': 100 | $obfuscated_str = obfuscate($source_file); 101 | if ($obfuscated_str===null) { exit(33); } 102 | if ($target_file ==='' ) { echo $obfuscated_str.PHP_EOL.PHP_EOL; exit(34); } 103 | file_put_contents($target_file,$obfuscated_str); 104 | exit(0); 105 | case 'directory': 106 | if (isset($conf->t_skip) && is_array($conf->t_skip)) foreach($conf->t_skip as $key=>$val) $conf->t_skip[$key] = "$source_directory/$val"; 107 | if (isset($conf->t_keep) && is_array($conf->t_keep)) foreach($conf->t_keep as $key=>$val) $conf->t_keep[$key] = "$source_directory/$val"; 108 | obfuscate_directory($source_directory,"$target_directory/yakpro-po/obfuscated"); 109 | exit(0); 110 | } 111 | 112 | ?> 113 | -------------------------------------------------------------------------------- /yakpro-po.cnf: -------------------------------------------------------------------------------- 1 | t_ignore_pre_defined_classes = 'all'; // 'all' (default value) , 'none', or array of pre-defined classes that you use in your software: 18 | // ex: array('Exception', 'PDO', 'PDOStatement', 'PDOException'); 19 | // As instantiation is done at runtime, it is impossible to statically determinate when a method call is detected, on which class the object belong. 20 | // so, all method names that exists in a pre_defined_class to ignore are ignored within every classes. 21 | // if you have some method names in your classes that have the same name that a predefine class method, it will not be obfuscated. 22 | // you can limit the number of method names to ignore by providing an array of the pre-defined classes you really use in your software! 23 | // same behaviour for properties... 24 | 25 | $conf->t_ignore_constants = null; // array where values are names to ignore. 26 | $conf->t_ignore_variables = null; // array where values are names to ignore. 27 | $conf->t_ignore_functions = null; // array where values are names to ignore. 28 | $conf->t_ignore_class_constants = null; // array where values are names to ignore. 29 | $conf->t_ignore_methods = null; // array where values are names to ignore. 30 | $conf->t_ignore_properties = null; // array where values are names to ignore. 31 | $conf->t_ignore_classes = null; // array where values are names to ignore. 32 | $conf->t_ignore_interfaces = null; // array where values are names to ignore. 33 | $conf->t_ignore_traits = null; // array where values are names to ignore. 34 | $conf->t_ignore_namespaces = null; // array where values are names to ignore. 35 | $conf->t_ignore_labels = null; // array where values are names to ignore. 36 | 37 | $conf->t_ignore_constants_prefix = null; // array where values are prefix of names to ignore. 38 | $conf->t_ignore_variables_prefix = null; // array where values are prefix of names to ignore. 39 | $conf->t_ignore_functions_prefix = null; // array where values are prefix of names to ignore. 40 | 41 | $conf->t_ignore_class_constants_prefix = null; // array where values are prefix of names to ignore. 42 | $conf->t_ignore_properties_prefix = null; // array where values are prefix of names to ignore. 43 | $conf->t_ignore_methods_prefix = null; // array where values are prefix of names to ignore. 44 | 45 | $conf->t_ignore_classes_prefix = null; // array where values are prefix of names to ignore. 46 | $conf->t_ignore_interfaces_prefix = null; // array where values are prefix of names to ignore. 47 | $conf->t_ignore_traits_prefix = null; // array where values are prefix of names to ignore. 48 | $conf->t_ignore_namespaces_prefix = null; // array where values are prefix of names to ignore. 49 | $conf->t_ignore_labels_prefix = null; // array where values are prefix of names to ignore. 50 | 51 | $conf->parser_mode = 'PREFER_PHP5';// allowed modes are 'PREFER_PHP7', 'PREFER_PHP5', 'ONLY_PHP7', 'ONLY_PHP5' 52 | // see PHP-Parser documentation for meaning... 53 | 54 | $conf->scramble_mode = 'identifier'; // allowed modes are 'identifier', 'hexa', 'numeric' 55 | $conf->scramble_length = 5; // min length of scrambled names (min = 2; max = 16 for identifier, 32 for hexa and numeric) 56 | 57 | $conf->t_obfuscate_php_extension = array('php'); // array where values are extensions of php files to be obfuscated. 58 | 59 | $conf->obfuscate_constant_name = true; // self explanatory 60 | $conf->obfuscate_variable_name = true; // self explanatory 61 | $conf->obfuscate_function_name = true; // self explanatory 62 | $conf->obfuscate_class_name = true; // self explanatory 63 | $conf->obfuscate_interface_name = true; // self explanatory 64 | $conf->obfuscate_trait_name = true; // self explanatory 65 | $conf->obfuscate_class_constant_name = true; // self explanatory 66 | $conf->obfuscate_property_name = true; // self explanatory 67 | $conf->obfuscate_method_name = true; // self explanatory 68 | $conf->obfuscate_namespace_name = true; // self explanatory 69 | $conf->obfuscate_label_name = true; // label: , goto label; obfuscation 70 | $conf->obfuscate_if_statement = true; // obfuscation of if else elseif statements 71 | $conf->obfuscate_loop_statement = true; // obfuscation of for while do while statements 72 | $conf->obfuscate_string_literal = true; // pseudo-obfuscation of string literals 73 | 74 | $conf->shuffle_stmts = true; // shuffle chunks of statements! disable this obfuscation (or minimize the number of chunks) if performance is important for you! 75 | $conf->shuffle_stmts_min_chunk_size = 1; // minimum number of statements in a chunk! the min value is 1, that gives you the maximum of obfuscation ... and the minimum of performance... 76 | $conf->shuffle_stmts_chunk_mode = 'fixed'; // 'fixed' or 'ratio' in fixed mode, the chunk_size is always equal to the min chunk size! 77 | $conf->shuffle_stmts_chunk_ratio = 20; // ratio > 1 100/ratio is the percentage of chunks in a statements sequence ratio = 2 means 50% ratio = 100 mins 1% ... 78 | // if you increase the number of chunks, you increase also the obfuscation level ... and you increase also the performance overhead! 79 | 80 | $conf->strip_indentation = true; // all your obfuscated code will be generated on a single line 81 | $conf->abort_on_error = true; // self explanatory 82 | $conf->confirm = true; // rfu : will answer Y on confirmation request (reserved for future use ... or not...) 83 | $conf->silent = false; // display or not Information level messages. 84 | 85 | 86 | $conf->source_directory = null; // self explanatory 87 | $conf->target_directory = null; // self explanatory 88 | 89 | $conf->t_keep = null; // array of directory or file pathnames, to keep 'as is' (i.e. not obfuscate.) 90 | $conf->t_skip = null; // array of directory or file pathnames, to skip when exploring source tree structure ... they will not be on target! 91 | $conf->allow_and_overwrite_empty_files = true; // allow empty files to be kept as is 92 | 93 | $conf->max_nested_directory = 99; 94 | $conf->follow_symlinks = false; // WARNING: setting it to true will copy the directory instead of replicating the link... 95 | // WARNING: if there is a loop of links, $conf->max_nested_directory can be created... 96 | 97 | $conf->user_comment = null; // user comment to insert inside each obfuscated file 98 | 99 | $conf->extract_comment_from_line = null; // when both 2 are set, each obfuscated file will contain an extract of the corresponding source file, 100 | $conf->extract_comment_to_line = null; // starting from extract_comment_from_line number, and ending at extract_comment_to_line line number. 101 | 102 | ?> 103 | -------------------------------------------------------------------------------- /include/classes/config.php: -------------------------------------------------------------------------------- 1 | 1 100/ratio is the percentage of chunks in a statements sequence ratio = 2 means 50% ratio = 100 mins 1% ... 79 | // if you increase the number of chunks, you increase also the obfuscation level ... and you increase also the performance overhead! 80 | 81 | public $strip_indentation = true; // all your obfuscated code will be generated on a single line 82 | public $abort_on_error = true; // self explanatory 83 | public $confirm = true; // rfu : will answer Y on confirmation request (reserved for future use ... or not...) 84 | public $silent = false; // display or not Information level messages. 85 | 86 | public $t_keep = false; // array of directory or file pathnames to keep 'as is' ... i.e. not obfuscate. 87 | public $t_skip = false; // array of directory or file pathnames to skip when exploring source tree structure ... they will not be on target! 88 | public $allow_and_overwrite_empty_files = false; // allow empty files to be kept as is 89 | 90 | public $source_directory = null; // self explanatory 91 | public $target_directory = null; // self explanatory 92 | 93 | 94 | public $max_nested_directory = 99; 95 | public $follow_symlinks = false; // WARNING: setting it to true will copy the directory instead of replicating the link... 96 | // WARNING: if there is a loop of links, $conf->max_nested_directory can be created... 97 | 98 | public $user_comment = null; // user comment to insert inside each obfuscated file 99 | 100 | public $extract_comment_from_line = null; // when both 2 are set, each obfuscated file will contain an extract of the corresponding source file, 101 | public $extract_comment_to_line = null; // starting from extract_comment_from_line number, and endng at extract_comment_to_line line number. 102 | 103 | private $comment = ''; 104 | 105 | function __construct() 106 | { 107 | $this->comment .= "/* __________________________________________________".PHP_EOL; 108 | $this->comment .= " | Obfuscated by YAK Pro - Php Obfuscator %-6.6s |".PHP_EOL; 109 | $this->comment .= " | on %s |".PHP_EOL; 110 | $this->comment .= " | GitHub: https://github.com/pk-fr/yakpro-po |".PHP_EOL; 111 | $this->comment .= " |__________________________________________________|".PHP_EOL; 112 | $this->comment .= "*/".PHP_EOL; 113 | } 114 | 115 | public function get_comment() 116 | { 117 | global $yakpro_po_version; 118 | $now = date('Y-m-d H:i:s'); 119 | 120 | return sprintf($this->comment,$yakpro_po_version,$now); 121 | } 122 | 123 | public function validate() 124 | { 125 | $this->shuffle_stmts_min_chunk_size += 0; 126 | if ($this->shuffle_stmts_min_chunk_size<1) $this->shuffle_stmts_min_chunk_size = 1; 127 | 128 | $this->shuffle_stmts_chunk_ratio += 0; 129 | if ($this->shuffle_stmts_chunk_ratio<2) $this->shuffle_stmts_chunk_ratio = 2; 130 | 131 | if ($this->shuffle_stmts_chunk_mode!='ratio') $this->shuffle_stmts_chunk_mode = 'fixed'; 132 | 133 | if (!isset( $this->t_ignore_pre_defined_classes)) $this->t_ignore_pre_defined_classes = 'all'; 134 | if (!is_array($this->t_ignore_pre_defined_classes) && ( $this->t_ignore_pre_defined_classes != 'none')) $this->t_ignore_pre_defined_classes = 'all'; 135 | } 136 | } 137 | 138 | ?> 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Cette page en Français](https://github.com/pk-fr/yakpro-po/blob/master/locale/fr/README.md) 2 | 3 | # YAK Pro - Php Obfuscator 4 | 5 | **YAK Pro** stands for **Y**et **A**nother **K**iller **Pro**duct. 6 | 7 | Free, Open Source, Published under the MIT License. 8 | 9 | This tool parses php with the best existing php parser [PHP-Parser 4.x](https://github.com/nikic/PHP-Parser/tree/4.x/), 10 | which is an awesome php parsing library written by [nikic](https://github.com/nikic). 11 | 12 | You just have to download the zip archive and uncompress it under the PHP-Parser subdirectory, 13 | or make a git clone ... 14 | 15 | ### Warning: 16 | yakpro-po 2.x works on PhpParser 4.x 17 | it will run on php >= 7.0, obfuscating code for php 5.2 to php 7.3 18 | 19 | If you are running php 5.3 or higher, 20 | Please use unsupported [yakpro-po 1.x](https://github.com/pk-fr/yakpro-po/tree/1.x) which works on 1.x branch of PhpParser. 21 | 22 | 23 | 24 | The yakpro-po.cnf self-documented file contains many configuration options! 25 | Take a look at it! 26 | 27 | Demo : [yakpro-po demo](https://www.php-obfuscator.com/?demo). 28 | 29 | Prerequisites: php 7.0 or higher, [PHP-Parser 4.x](https://github.com/nikic/PHP-Parser/tree/4.x/). 30 | 31 | Note: This tool has been written in order to obfuscate pure php sources. 32 | it is not intended to be used with html and embeded php inside 33 | (use at your own risks...you may try to deactivate statements shuffling...). 34 | You can still embed html within php using the echo <<obfuscate_function_name to false in your yakpro-po.cnf config file, 245 | or declare all the functions names you are using in $conf->t_ignore_functions 246 | example : $conf->t_ignore_functions = array('my_func1','my_func2'); 247 | 248 | if the library consists of classes : 249 | set the $conf->obfuscate_class_name, 250 | $conf->obfuscate_property_name, 251 | $conf->obfuscate_method_name 252 | to false in your yakpro-po.cnf config file... 253 | ... or declare all the classes, properties, methods names you are using in 254 | $conf->t_ignore_classes, 255 | $conf->t_ignore_properties, 256 | $conf->t_ignore_methods. 257 | 258 | This is also true for PDO::FETCH_OBJ that retrieves properties from external source 259 | (i.e. database columns). 260 | 261 | ## Hints for preparing your Software to be run obfuscated 262 | 263 | At first you can test obfuscating only variable names... 264 | 265 | 266 | If you obfuscate functions, do not use indirect function calls like 267 | $my_var = 'my_function'; 268 | $my_var(); 269 | or put all the function names you call indirectly in the $conf->t_ignore_functions array! 270 | 271 | 272 | Do not use indirect variable names! 273 | $$my_var = something; 274 | or put all the variable names you use indirectly in the $conf->t_ignore_variables array! 275 | 276 | 277 | Do not use PDO::FETCH_OBJ but use PDO::FETCH_ASSOC instead! 278 | or disable properties obfuscation in the config file. 279 | 280 | 281 | If you use the define function for defining constants, the only allowed form is when the 282 | define function has exactly 2 arguments, and the first one is a litteral string! 283 | You MUST disable constants obfuscation in the config file, if you use any other forms 284 | of the define function! 285 | There is no problem with the const MY_CONST = something; form! 286 | 287 | 288 | ## Performance considerations 289 | 290 | Except for the statements shuffling obfuscation option, 291 | the obfuscated program speed is almost the same than the original one. 292 | 293 | $conf->shuffle_stmts is set to true by default. 294 | 295 | If you encounter performance issues, you can either set the option to false, 296 | or fine tune the shuffle parameters with the associated options. 297 | 298 | You must know that the lesser the chunk size, the better the obfuscation, 299 | and the lower your software performance! 300 | 301 | (during my own tests, the maximum of obfuscation costs me about 13% of performance) 302 | 303 | You can tune it as you wish! 304 | 305 | 306 | 307 | ## Known Issues 308 | 309 | [segmentation fault](https://github.com/php/php-src/issues/8193) 310 | 311 | opcache crashes on Ubuntu 21.10 - php 8.0.8 (segfault) both within apache2 and cli when 312 | shuffle-statements is turned on for big files 313 | 314 | works perfectly with newer versions of php (8.0.16 8.1 ) 315 | 316 | 317 | [sedimentation-fault](https://github.com/sedimentation-fault) reported on issue [#75](https://github.com/pk-fr/yakpro-po/issues/75) that a segmentation fault could occure in php's garbage collector when obfuscating many big files in a project: 318 | 319 | Trying to obfuscate ~5000 PHP files of ~1000 lines each, yakpro-po stopped after processing ~1600 files 320 | with a simple (and frustrating) Segmentation fault 321 | 322 | Workaround: 323 | 324 | There is a stack overflow in garbage collector. The solution is to increase limit for stack. 325 | To see your current limit, type 326 | 327 | ulimit -s 328 | 329 | I had 8192 - for a task of this size obviously totally undersized... 330 | Change this to something more appropriate, say 331 | 332 | ulimit -s 102400 333 | 334 | and retry - the segmentation fault is gone! :-) 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /include/functions.php: -------------------------------------------------------------------------------- 1 | allow_and_overwrite_empty_files) return $source; 39 | throw new Exception("Error obfuscating [$src_filename]: php_strip_whitespace returned an empty string!"); 40 | } 41 | try 42 | { 43 | $stmts = $parser->parse($source); // PHP-Parser returns the syntax tree 44 | } 45 | catch (PhpParser\Error $e) // if an error occurs, then redo it without php_strip_whitespace, in order to display the right line number with error! 46 | { 47 | $source = file_get_contents($filename); 48 | $stmts = $parser->parse($source); 49 | } 50 | if ($debug_mode===2) // == 2 is true when debug_mode is true! 51 | { 52 | $source = file_get_contents($filename); 53 | $stmts = $parser->parse($source); 54 | } 55 | if ($debug_mode) var_dump($stmts); 56 | 57 | $stmts = $traverser->traverse($stmts); // Use PHP-Parser function to traverse the syntax tree and obfuscate names 58 | if ($conf->shuffle_stmts && (count($stmts)>2) ) 59 | { 60 | $last_inst = array_pop($stmts); 61 | $last_use_stmt_pos = -1; 62 | foreach($stmts as $i => $stmt) // if a use statement exists, do not shuffle before the last use statement 63 | { //TODO: enhancement: keep all use statements at their position, and shuffle all sub-parts 64 | if ( $stmt instanceof PhpParser\Node\Stmt\Use_ ) $last_use_stmt_pos = $i; 65 | } 66 | 67 | if ($last_use_stmt_pos<0) { $stmts_to_shuffle = $stmts; $stmts = array(); } 68 | else { $stmts_to_shuffle = array_slice($stmts,$last_use_stmt_pos+1); $stmts = array_slice($stmts,0,$last_use_stmt_pos+1); } 69 | 70 | $stmts = array_merge($stmts,shuffle_statements($stmts_to_shuffle)); 71 | $stmts[] = $last_inst; 72 | } 73 | // if ($debug_mode) var_dump($stmts); 74 | 75 | 76 | $code = trim($prettyPrinter->prettyPrintFile($stmts)); // Use PHP-Parser function to output the obfuscated source, taking the modified obfuscated syntax tree as input 77 | 78 | if (isset($conf->strip_indentation) && $conf->strip_indentation) // self-explanatory 79 | { 80 | $code = remove_whitespaces($code); 81 | } 82 | $endcode = substr($code,6); 83 | 84 | $code = 'get_comment(); // comment obfuscated source 86 | if (isset($conf->extract_comment_from_line) && isset($conf->extract_comment_to_line) ) 87 | { 88 | $t_source = file($filename); 89 | for($i=$conf->extract_comment_from_line-1;$i<$conf->extract_comment_to_line;++$i) $code .= $t_source[$i]; 90 | } 91 | if (isset($conf->user_comment)) 92 | { 93 | $code .= '/*'.PHP_EOL.$conf->user_comment.PHP_EOL.'*/'.PHP_EOL; 94 | } 95 | $code .= $endcode; 96 | 97 | if (($tmp_filename!='') && ($first_line!='')) 98 | { 99 | $code = $first_line.$code; 100 | unlink($tmp_filename); 101 | } 102 | 103 | return trim($code); 104 | } 105 | catch (Exception $e) 106 | { 107 | fprintf(STDERR,"Obfuscator Parse Error [%s]:%s\t%s%s", $filename,PHP_EOL, $e->getMessage(),PHP_EOL); 108 | return null; 109 | } 110 | } 111 | 112 | function check_preload_file($filename) // self-explanatory 113 | { 114 | for($ok=false;;) 115 | { 116 | if (!file_exists($filename)) return false; 117 | if (!is_readable($filename)) 118 | { 119 | fprintf(STDERR,"Warning:[%s] is not readable!%s",$filename,PHP_EOL); 120 | return false; 121 | } 122 | $fp = fopen($filename,"r"); if($fp===false) break; 123 | $line = trim(fgets($fp)); if ($line!=' $dir) 157 | { 158 | if (!file_exists($dir)) mkdir($dir,0777,true); 159 | if (!file_exists($dir)) 160 | { 161 | fprintf(STDERR,"Error:\tCannot create directory [%s]%s",$dir,PHP_EOL); 162 | exit(51); 163 | } 164 | } 165 | $target_directory = realpath($target_directory); 166 | if (!file_exists("$target_directory/yakpro-po/.yakpro-po-directory")) touch("$target_directory/yakpro-po/.yakpro-po-directory"); 167 | } 168 | 169 | 170 | function remove_directory($path) // self-explanatory 171 | { 172 | if ($dp = opendir($path)) 173 | { 174 | while (($entry = readdir($dp)) !== false ) 175 | { 176 | if ($entry == ".") continue; 177 | if ($entry == "..") continue; 178 | 179 | if (is_link("$path/$entry")) unlink("$path/$entry" ); // remove symbolic links first, to not dereference... 180 | else if (is_dir ("$path/$entry")) remove_directory("$path/$entry"); 181 | else unlink("$path/$entry" ); 182 | } 183 | closedir($dp); 184 | rmdir($path); 185 | } 186 | } 187 | 188 | function confirm($str) // self-explanatory not yet used ... rfu 189 | { 190 | global $conf; 191 | if (!$conf->confirm) return true; 192 | for(;;) 193 | { 194 | fprintf(STDERR,"%s [y/n] : ",$str); 195 | $r = strtolower(trim(fgets(STDIN))); 196 | if ($r=='y') return true; 197 | if ($r=='n') return false; 198 | } 199 | } 200 | 201 | function obfuscate_directory($source_dir,$target_dir,$keep_mode=false) // self-explanatory recursive obfuscation 202 | { 203 | global $conf; 204 | 205 | static $recursion_level = 0; 206 | 207 | if (++$recursion_level > $conf->max_nested_directory) 208 | { 209 | if ($conf->follow_symlinks) 210 | { 211 | fprintf(STDERR,"Error:\t [%s] nested directories have been created!\nloop detected when follow_symlinks option is set to true!%s",$conf->max_nested_directory,PHP_EOL); 212 | exit(52); 213 | } 214 | } 215 | if (!$dp = opendir($source_dir)) 216 | { 217 | fprintf(STDERR,"Error:\t [%s] directory does not exists!%s",$source_dir,PHP_EOL); 218 | exit(53); 219 | } 220 | $t_dir = array(); 221 | $t_file = array(); 222 | while (($entry = readdir($dp)) !== false) 223 | { 224 | if ($entry == "." || $entry == "..") continue; 225 | 226 | $new_keep_mode = $keep_mode; 227 | 228 | $source_path = "$source_dir/$entry"; $source_stat = @lstat($source_path); 229 | $target_path = "$target_dir/$entry"; $target_stat = @lstat($target_path); 230 | if ($source_stat===false) 231 | { 232 | fprintf(STDERR,"Error:\t cannot stat [%s] !%s",$source_path,PHP_EOL); 233 | exit(54); 234 | } 235 | 236 | if (isset($conf->t_skip) && is_array($conf->t_skip) && in_array($source_path,$conf->t_skip)) continue; 237 | 238 | if (!$conf->follow_symlinks && is_link($source_path)) 239 | { 240 | if ( ($target_stat!==false) && is_link($target_path) && ($source_stat['mtime']<=$target_stat['mtime']) ) continue; 241 | if ( $target_stat!==false ) 242 | { 243 | if (is_dir($target_path)) remove_directory($target_path); 244 | else 245 | { 246 | if (unlink($target_path)===false) 247 | { 248 | fprintf(STDERR,"Error:\t cannot unlink [%s] !%s",$target_path,PHP_EOL); 249 | exit(55); 250 | } 251 | } 252 | } 253 | @symlink(readlink($source_path), $target_path); // Do not warn on non existing symbolinc link target! 254 | if (strtolower(PHP_OS)=='linux') $x = `touch '$target_path' --no-dereference --reference='$source_path' `; 255 | continue; 256 | } 257 | if (is_dir($source_path)) 258 | { 259 | if ($target_stat!==false) 260 | { 261 | if (!is_dir($target_path)) 262 | { 263 | if (unlink($target_path)===false) 264 | { 265 | fprintf(STDERR,"Error:\t cannot unlink [%s] !%s",$target_path,PHP_EOL); 266 | exit(56); 267 | } 268 | } 269 | } 270 | if (!file_exists($target_path)) mkdir($target_path,0777, true); 271 | if (isset($conf->t_keep) && is_array($conf->t_keep) && in_array($source_path,$conf->t_keep)) $new_keep_mode = true; 272 | obfuscate_directory($source_path,$target_path,$new_keep_mode); 273 | continue; 274 | } 275 | if(is_file($source_path)) 276 | { 277 | if ( ($target_stat!==false) && is_dir($target_path) ) remove_directory($target_path); 278 | if ( ($target_stat!==false) && ($source_stat['mtime']<=$target_stat['mtime']) ) continue; // do not process if source timestamp is not greater than target 279 | 280 | $extension = pathinfo($source_path,PATHINFO_EXTENSION); 281 | 282 | $keep = $keep_mode; 283 | if (isset($conf->t_keep) && is_array($conf->t_keep) && in_array($source_path,$conf->t_keep)) $keep = true; 284 | if (!in_array($extension,$conf->t_obfuscate_php_extension) ) $keep = true; 285 | 286 | if ($keep) 287 | { 288 | file_put_contents($target_path,file_get_contents($source_path)); 289 | } 290 | else 291 | { 292 | $obfuscated_str = obfuscate($source_path); 293 | if ($obfuscated_str===null) 294 | { 295 | if (isset($conf->abort_on_error)) 296 | { 297 | fprintf(STDERR, "Aborting...%s",PHP_EOL); 298 | exit(57); 299 | } 300 | } 301 | file_put_contents($target_path,$obfuscated_str.PHP_EOL); 302 | } 303 | touch($target_path,$source_stat['mtime']); 304 | chmod($target_path,$source_stat['mode']); 305 | chgrp($target_path,$source_stat['gid']); 306 | chown($target_path,$source_stat['uid']); 307 | continue; 308 | } 309 | } 310 | closedir($dp); 311 | --$recursion_level; 312 | } 313 | 314 | function shuffle_get_chunk_size(&$stmts) 315 | { 316 | global $conf; 317 | 318 | $n = count($stmts); 319 | switch($conf->shuffle_stmts_chunk_mode) 320 | { 321 | case 'ratio': 322 | $chunk_size = sprintf("%d",$n/$conf->shuffle_stmts_chunk_ratio)+0; 323 | if ($chunk_size<$conf->shuffle_stmts_min_chunk_size) $chunk_size = $conf->shuffle_stmts_min_chunk_size; 324 | break; 325 | case 'fixed': 326 | $chunk_size = $conf->shuffle_stmts_min_chunk_size; 327 | break; 328 | default: 329 | $chunk_size = 1; // should never occur! 330 | } 331 | return $chunk_size; 332 | } 333 | 334 | function shuffle_statements($stmts) 335 | { 336 | global $conf; 337 | global $t_scrambler; 338 | 339 | if (!$conf->shuffle_stmts) return $stmts; 340 | 341 | $chunk_size = shuffle_get_chunk_size($stmts); 342 | if ($chunk_size<=0) return $stmts; // should never occur! 343 | 344 | $n = count($stmts); 345 | if ($n<(2*$chunk_size)) return $stmts; 346 | 347 | $scrambler = $t_scrambler['label']; 348 | $label_name_prev = $scrambler->scramble($scrambler->generate_label_name()); 349 | $first_goto = new PhpParser\Node\Stmt\Goto_($label_name_prev); 350 | $t = array(); 351 | $t_chunk = array(); 352 | for($i=0;$i<$n;++$i) 353 | { 354 | $t_chunk[] = $stmts[$i]; 355 | if (count($t_chunk)>=$chunk_size) 356 | { 357 | $label = array(new PhpParser\Node\Stmt\Label($label_name_prev)); 358 | $label_name = $scrambler->scramble($scrambler->generate_label_name()); 359 | $goto = array(new PhpParser\Node\Stmt\Goto_($label_name)); 360 | $t[] = array_merge($label,$t_chunk,$goto); 361 | $label_name_prev = $label_name; 362 | $t_chunk = array(); 363 | } 364 | } 365 | if (count($t_chunk)>0) 366 | { 367 | $label = array(new PhpParser\Node\Stmt\Label($label_name_prev)); 368 | $label_name = $scrambler->scramble($scrambler->generate_label_name()); 369 | $goto = array(new PhpParser\Node\Stmt\Goto_($label_name)); 370 | $t[] = array_merge($label,$t_chunk,$goto); 371 | $label_name_prev = $label_name; 372 | $t_chunk = array(); 373 | } 374 | 375 | $last_label = new PhpParser\Node\Stmt\Label($label_name); 376 | shuffle($t); 377 | $stmts = array(); 378 | $stmts[] = $first_goto; 379 | foreach($t as $dummy => $stmt) 380 | { 381 | foreach($stmt as $dummy => $inst) $stmts[] = $inst; 382 | } 383 | $stmts[] = $last_label; 384 | return $stmts; 385 | } 386 | 387 | function remove_whitespaces($str) 388 | { 389 | $tmp_filename = @tempnam(sys_get_temp_dir(),'po-'); 390 | file_put_contents($tmp_filename,$str); 391 | $str = php_strip_whitespace($tmp_filename); // can remove more whitespaces 392 | unlink($tmp_filename); 393 | return $str; 394 | } 395 | 396 | ?> 397 | -------------------------------------------------------------------------------- /include/retrieve_config_and_arguments.php: -------------------------------------------------------------------------------- 1 | $where) if (check_config_file($where)) { $config_filename = $where; break; } 106 | 107 | $conf = new Config; 108 | 109 | if ($force_conf_silent) $conf->silent = true; 110 | 111 | if ($config_filename=='') fprintf(STDERR,"Warning:No config file found... using default values!%s",PHP_EOL); 112 | else 113 | { 114 | $config_filename = realpath($config_filename); 115 | if (!$conf->silent) fprintf(STDERR,"Info:\tUsing [%s] Config File...%s",$config_filename,PHP_EOL); 116 | require_once $config_filename; 117 | $conf->validate(); 118 | if ($force_conf_silent) $conf->silent = true; 119 | } 120 | //var_dump($conf); 121 | 122 | if (!$conf->silent) fprintf(STDERR,"Info:\tyakpro-po version = %s%s",$yakpro_po_version,PHP_EOL); 123 | 124 | 125 | $pos = array_search('-y',$t_args); 126 | if (isset($pos) && ($pos!==false) ) 127 | { 128 | $conf->confirm = false; 129 | array_splice($t_args,$pos,1); // remove the arg and reorder 130 | } 131 | 132 | $pos = array_search('-s',$t_args); if (isset($pos) && ($pos!==false)) { $conf->strip_indentation = false; array_splice($t_args,$pos,1); } 133 | $pos = array_search('--no-strip-indentation',$t_args); if (isset($pos) && ($pos!==false)) { $conf->strip_indentation = false; array_splice($t_args,$pos,1); } 134 | $pos = array_search('--strip-indentation',$t_args); if (isset($pos) && ($pos!==false)) { $conf->strip_indentation = true; array_splice($t_args,$pos,1); } 135 | 136 | $pos = array_search('--no-shuffle-statements',$t_args); if (isset($pos) && ($pos!==false)) { $conf->shuffle_stmts = false; array_splice($t_args,$pos,1); } 137 | $pos = array_search('--shuffle-statements',$t_args); if (isset($pos) && ($pos!==false)) { $conf->shuffle_stmts = true; array_splice($t_args,$pos,1); } 138 | 139 | $pos = array_search('--no-obfuscate-string-literal',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_string_literal = false; array_splice($t_args,$pos,1); } 140 | $pos = array_search('--obfuscate-string-literal',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_string_literal = true; array_splice($t_args,$pos,1); } 141 | 142 | $pos = array_search('--no-obfuscate-loop-statement',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_loop_statement = false; array_splice($t_args,$pos,1); } 143 | $pos = array_search('--obfuscate-loop-statement',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_loop_statement = true; array_splice($t_args,$pos,1); } 144 | 145 | $pos = array_search('--no-obfuscate-if-statement',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_if_statement = false; array_splice($t_args,$pos,1); } 146 | $pos = array_search('--obfuscate-if-statement',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_if_statement = true; array_splice($t_args,$pos,1); } 147 | 148 | 149 | $pos = array_search('--no-obfuscate-constant-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_constant_name = false; array_splice($t_args,$pos,1); } 150 | $pos = array_search('--obfuscate-constant-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_constant_name = true; array_splice($t_args,$pos,1); } 151 | 152 | $pos = array_search('--no-obfuscate-variable-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_variable_name = false; array_splice($t_args,$pos,1); } 153 | $pos = array_search('--obfuscate-variable-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_variable_name = true; array_splice($t_args,$pos,1); } 154 | 155 | $pos = array_search('--no-obfuscate-function-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_function_name = false; array_splice($t_args,$pos,1); } 156 | $pos = array_search('--obfuscate-function-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_function_name = true; array_splice($t_args,$pos,1); } 157 | 158 | $pos = array_search('--no-obfuscate-class-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_class_name = false; array_splice($t_args,$pos,1); } 159 | $pos = array_search('--obfuscate-class-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_class_name = true; array_splice($t_args,$pos,1); } 160 | 161 | $pos = array_search('--no-obfuscate-class_constant-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_class_constant_name = false; array_splice($t_args,$pos,1); } 162 | $pos = array_search('--obfuscate-class_constant-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_class_constant_name = true; array_splice($t_args,$pos,1); } 163 | 164 | $pos = array_search('--no-obfuscate-interface-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_interface_name = false; array_splice($t_args,$pos,1); } 165 | $pos = array_search('--obfuscate-interface-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_interface_name = true; array_splice($t_args,$pos,1); } 166 | 167 | $pos = array_search('--no-obfuscate-trait-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_trait_name = false; array_splice($t_args,$pos,1); } 168 | $pos = array_search('--obfuscate-trait-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_trait_name = true; array_splice($t_args,$pos,1); } 169 | 170 | $pos = array_search('--no-obfuscate-property-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_property_name = false; array_splice($t_args,$pos,1); } 171 | $pos = array_search('--obfuscate-property-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_property_name = true; array_splice($t_args,$pos,1); } 172 | 173 | $pos = array_search('--no-obfuscate-method-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_method_name = false; array_splice($t_args,$pos,1); } 174 | $pos = array_search('--obfuscate-method-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_method_name = true; array_splice($t_args,$pos,1); } 175 | 176 | $pos = array_search('--no-obfuscate-namespace-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_namespace_name = false; array_splice($t_args,$pos,1); } 177 | $pos = array_search('--obfuscate-namespace-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_namespace_name = true; array_splice($t_args,$pos,1); } 178 | 179 | $pos = array_search('--no-obfuscate-label-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_label_name = false; array_splice($t_args,$pos,1); } 180 | $pos = array_search('--obfuscate-label-name',$t_args); if (isset($pos) && ($pos!==false)) { $conf->obfuscate_label_name = true; array_splice($t_args,$pos,1); } 181 | 182 | 183 | 184 | 185 | $pos = array_search('--scramble-mode',$t_args); 186 | if ( isset($pos) && ($pos!==false) && isset($t_args[$pos+1]) ) 187 | { 188 | $conf->scramble_mode = $t_args[$pos+1]; 189 | array_splice($t_args,$pos,2); // remove the 2 args and reorder 190 | } 191 | 192 | $pos = array_search('--scramble-length',$t_args); 193 | if ( isset($pos) && ($pos!==false) && isset($t_args[$pos+1]) ) 194 | { 195 | $conf->scramble_length = $t_args[$pos+1]+0; 196 | array_splice($t_args,$pos,2); // remove the 2 args and reorder 197 | } 198 | 199 | 200 | switch(count($t_args)) 201 | { 202 | case 0: 203 | if (isset($conf->source_directory) && isset($conf->target_directory)) 204 | { 205 | $process_mode = 'directory'; 206 | $source_directory = $conf->source_directory; 207 | $target_directory = $conf->target_directory; 208 | create_context_directories($target_directory); 209 | break; 210 | } 211 | fprintf(STDERR,"Error:\tsource_directory and target_directory not specified!%s\tneither within command line parameter,%s\tnor in config file!%s",PHP_EOL,PHP_EOL,PHP_EOL); 212 | exit(12); 213 | case 1: 214 | $source_file = realpath($t_args[0]); 215 | if (($source_file!==false) && file_exists($source_file)) 216 | { 217 | if (is_file($source_file) && is_readable($source_file)) 218 | { 219 | $process_mode = 'file'; 220 | $target_file = $target; 221 | if ( ($target_file!=='') && file_exists($target_file) ) 222 | { 223 | $x = realpath($target_file); 224 | if (is_dir($x)) 225 | { 226 | fprintf(STDERR,"Error:\tTarget file [%s] is a directory!%s", ($x!==false) ? $x : $target_file,PHP_EOL); 227 | exit(13); 228 | } 229 | if ( is_readable($x) && is_writable($x) && is_file($x) && (file_get_contents($x)!=='') ) 230 | { 231 | $fp = fopen($target_file,"r"); 232 | $y = fgets($fp); 233 | $y = fgets($fp).fgets($fp).fgets($fp).fgets($fp).fgets($fp); 234 | if (strpos($y,' | Obfuscated by YAK Pro - Php Obfuscator ')===false) // comment is a magic string, used to not overwrite wrong files!!! 235 | { 236 | $x = realpath($target_file); 237 | fprintf(STDERR,"Error:\tTarget file [%s] exists and is not an obfuscated file!%s", ($x!==false) ? $x : $target_file,PHP_EOL); 238 | exit(14); 239 | } 240 | fclose($fp); 241 | } 242 | } 243 | break; 244 | } 245 | if (is_dir($source_file)) 246 | { 247 | $process_mode = 'directory'; 248 | $source_directory = $source_file; 249 | $target_directory = $target; 250 | if (($target_directory=='') && isset($conf->target_directory)) $target_directory = $conf->target_directory; 251 | if ( $target_directory=='') 252 | { 253 | fprintf(STDERR,"Error:\tTarget directory is not specified!%s",PHP_EOL); 254 | exit(15); 255 | } 256 | create_context_directories($target_directory); 257 | break; 258 | } 259 | } 260 | fprintf(STDERR,"Error:\tSource file [%s] is not readable!%s",($source_file!==false) ? $source_file : $t_args[0],PHP_EOL); 261 | exit(16); 262 | default: 263 | fprintf(STDERR,"Error:\tToo much parameters are specified, I do not know how to deal with that!!!%s",PHP_EOL); 264 | exit(17); 265 | } 266 | //print_r($t_args); 267 | 268 | if (!$conf->silent) fprintf(STDERR,"Info:\tProcess Mode\t\t= %s%s",$process_mode,PHP_EOL); 269 | switch($process_mode) 270 | { 271 | case 'file': 272 | if (!$conf->silent) fprintf(STDERR,"Info:\tsource_file\t\t= [%s]%s",$source_file,PHP_EOL); 273 | if (!$conf->silent) fprintf(STDERR,"Info:\ttarget_file\t\t= [%s]%s",($target_file!=='') ? $target_file : 'stdout',PHP_EOL); 274 | break; 275 | case 'directory': 276 | if (!$conf->silent) fprintf(STDERR,"Info:\tsource_directory\t= [%s]%s",$source_directory,PHP_EOL); 277 | if (!$conf->silent) fprintf(STDERR,"Info:\ttarget_directory\t= [%s]%s",$target_directory,PHP_EOL); 278 | break; 279 | } 280 | 281 | 282 | ?> 283 | -------------------------------------------------------------------------------- /locale/fr/README.md: -------------------------------------------------------------------------------- 1 | [This page in English](https://github.com/pk-fr/yakpro-po) 2 | 3 | # YAK Pro - Php Obfuscator 4 | 5 | **YAK Pro** vient de **Y**et **A**nother **K**iller **Pro**duct. 6 | 7 | Gratuit, Open Source, Publié sous les termes de la licence MIT. 8 | 9 | Ce programme utilise [PHP-Parser 4.x](https://github.com/nikic/PHP-Parser/tree/4.x/) pour analyser le php. 10 | [PHP-Parser 4.x](https://github.com/nikic/PHP-Parser/tree/4.x/) est une remarquable bibliothèque développée par [nikic](https://github.com/nikic). 11 | 12 | Télécharger l'archive zip et décompressez la dans le sous-répertoire PHP-Parser . 13 | ou alors utilisez git clone. 14 | 15 | ### Attention : 16 | yakpro-po 2.x utilise PhpParser 4.x 17 | il fonctionne avec php >= 7.0, et obfusque le code pour php 5.2 à php 8.1 18 | 19 | Si vous utilisez php 5.3 ou supérieur, 20 | Veuillez utiliser [yakpro-po 1.x](https://github.com/pk-fr/yakpro-po/tree/1.x) (non supporté) qui fonctionne avec la branche 1.x de PhpParser. 21 | 22 | 23 | 24 | 25 | Le fichier de configuration yakpro-po.cnf est auto-documenté et contient de 26 | nombreuses options de configuration ! 27 | Un petit coup d'oeil vaut le détour. 28 | 29 | Démo : [yakpro-po demo](https://www.php-obfuscator.com/?demo). 30 | 31 | Pré-requis: php 7.0 ou supérieur, [PHP-Parser 4.x](https://github.com/nikic/PHP-Parser/tree/4.x/). 32 | 33 | Remarque : Cet outil à été conçu dans le but d'obfusquer des sources en php pur. 34 | Il n'a pas été conçu pour être utilisé avec du html avec des bouts de code php à l'intérieur. 35 | (utilisation à vos risques et périls) 36 | Par contre, vous pouvez intégrer du html dans votre php en utilisant la syntaxe echo <<obfuscate_function_name à false dans votre fichier de configuration, 259 | ou alors déclarez tous les noms de fonction que vous utilisez dans 260 | $conf->t_ignore_functions 261 | Par example : $conf->t_ignore_functions = array('my_func1','my_func2'); 262 | 263 | Si la bibliothèque est faite de classes : 264 | renseignez $conf->obfuscate_class_name, 265 | $conf->obfuscate_property_name, 266 | $conf->obfuscate_method_name 267 | à false dans votre fichier de configuration yakpro-po.cnf ... 268 | ... ou alors renseignez tous les noms de classes, attributs, et methodes 269 | que vous utilisez dans 270 | $conf->t_ignore_classes, 271 | $conf->t_ignore_properties, 272 | $conf->t_ignore_methods. 273 | 274 | Cela vaut aussi pour PDO::FETCH_OBJ qui récupère les noms d'attributs à partir de 275 | sources externes (i.e. colonnes de bases de données). 276 | 277 | ## Conseils pour préparer votre Logiciel à s'exécuter correctement lorsqu'il est obfusqué 278 | 279 | Commencez par tester en n'obfusquant que les variables. 280 | 281 | 282 | Si vous obfusquez des fonctions, n'utilisez pas d'appels indirects tels que: 283 | $ma_var = 'ma_function'; 284 | $ma_var(); 285 | ou alors, renseignez tous les noms de fonctions que vous appellez de façon indirecte 286 | dans le tableau $conf->t_ignore_functions ! 287 | 288 | 289 | N'utilisez pas les variables indirectes. 290 | $$ma_var = qqe_chose; 291 | ou alors, renseignez tous les noms de variables que vous utilisez de façon indirecte 292 | dans le tableau $conf->t_ignore_variables ! 293 | 294 | 295 | N'utilisez pas PDO::FETCH_OBJ mais utilisez PDO::FETCH_ASSOC à la place ! 296 | ou alors désactivez l'obfuscation des attributs dans le fichier de configuration. 297 | 298 | 299 | Si vous utilisez la fonction define pour définir des constantes, la seule forme autorisée 300 | est lorsque la fonction define est utilisée avec exactement 2 arguments, 301 | et que le premier argument est une chaine de caractères ! 302 | Vous DEVEZ désactiver l'obfuscation des constantes dans le fichier de configuration 303 | si vous utilisez la fonction define autrement ! 304 | Il n'y a aucune restriction si vous utilisez la construction : 305 | const MA_CONSTANTE = quelque_chose; 306 | 307 | 308 | 309 | 310 | ## Considérations sur les Performances 311 | 312 | Excepté pour l'option d'obfuscation concernant le mélange des instructions, 313 | la vitesse du programme obfusqué est équivalent à celle du programme original. 314 | 315 | $conf->shuffle_stmts est positionné à true par défaut. 316 | 317 | Si vous rencontrez des problèmes de performance, vous pouvez soit désactiver l'option, 318 | soit paramétrer finement les options de mélange... 319 | 320 | Plus la taille du chunk est petite, meilleure sera l'obfuscation, 321 | ... et plus le coût sur les performance de votre logiciel sera élevé... 322 | 323 | (lors de mes propres tests, avec l'obfuscation maximale, la dégradation est de l'ordre de 13%) 324 | 325 | Vous avez tous les paramètres nécessaires à votre disposition pour parvenir à votre meilleur compromis. 326 | 327 | 328 | 329 | ## Problèmes connus 330 | 331 | [segmentation fault](https://github.com/php/php-src/issues/8193) 332 | 333 | opcache plante sur Ubuntu 21.10 - php 8.0.8 (segfault) que se soit en mode cli ou à travers apache2 334 | lorsque l'option shuffle-statements est activée sur des gros fichiers. 335 | 336 | fonctionne parfaitement sur des versions plus récentes de php ( 8.0.16 et 8.1 ) 337 | 338 | 339 | 340 | [sedimentation-fault](https://github.com/sedimentation-fault) a rapporté sur le problème [#75](https://github.com/pk-fr/yakpro-po/issues/75) que un segmentation fault pouvait survenir dans le 'garbage collector' de php lors de l'obfuscation de beaucoup de gros fichiers dans un projet : 341 | 342 | Trying to obfuscate ~5000 PHP files of ~1000 lines each, yakpro-po stopped after processing ~1600 files 343 | with a simple (and frustrating) Segmentation fault 344 | 345 | Workaround: 346 | 347 | There is a stack overflow in garbage collector. The solution is to increase limit for stack. 348 | To see your current limit, type 349 | 350 | ulimit -s 351 | 352 | I had 8192 - for a task of this size obviously totally undersized... 353 | Change this to something more appropriate, say 354 | 355 | ulimit -s 102400 356 | 357 | and retry - the segmentation fault is gone! :-) 358 | 359 | 360 | -------------------------------------------------------------------------------- /include/classes/scrambler.php: -------------------------------------------------------------------------------- 1 | scramble_type = $type; 68 | $this->t_first_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 69 | $this->t_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'; 70 | $this->r = md5(microtime(true)); // random seed 71 | $this->t_scramble = array(); 72 | $this->silent = $conf->silent; 73 | if (isset($conf->scramble_mode)) 74 | { 75 | switch($conf->scramble_mode) 76 | { 77 | case 'numeric': 78 | $this->scramble_length_max = 32; 79 | $this->scramble_mode = $conf->scramble_mode; 80 | $this->t_first_chars = 'O'; 81 | $this->t_chars = '0123456789'; 82 | break; 83 | case 'hexa': 84 | $this->scramble_length_max = 32; 85 | $this->scramble_mode = $conf->scramble_mode; 86 | $this->t_first_chars = 'abcdefABCDEF'; 87 | break; 88 | case 'identifier': 89 | default: 90 | $this->scramble_length_max = 16; 91 | $this->scramble_mode = 'identifier'; 92 | } 93 | } 94 | $this->l1 = strlen($this->t_first_chars)-1; 95 | $this->l2 = strlen($this->t_chars )-1; 96 | $this->scramble_length_min = 2; 97 | $this->scramble_length = 5; 98 | if (isset($conf->scramble_length)) 99 | { 100 | $conf->scramble_length += 0; 101 | if ( ($conf->scramble_length >= $this->scramble_length_min) && ($conf->scramble_length <= $this->scramble_length_max) ) 102 | { 103 | $this->scramble_length = $conf->scramble_length; 104 | } 105 | } 106 | switch($type) 107 | { 108 | case 'constant': 109 | $this->case_sensitive = true; 110 | $this->t_ignore = array_flip($this->t_reserved_function_names); 111 | $this->t_ignore = array_merge($this->t_ignore,get_defined_constants(false)); 112 | if (isset($conf->t_ignore_constants)) 113 | { 114 | $t = $conf->t_ignore_constants; $t = array_flip($t); 115 | $this->t_ignore = array_merge($this->t_ignore,$t); 116 | } 117 | if (isset($conf->t_ignore_constants_prefix)) 118 | { 119 | $t = $conf->t_ignore_constants_prefix; $t = array_flip($t); 120 | $this->t_ignore_prefix = $t; 121 | } 122 | break; 123 | case 'class_constant': 124 | $this->case_sensitive = true; 125 | $this->t_ignore = array_flip($this->t_reserved_function_names); 126 | $this->t_ignore = array_merge($this->t_ignore,get_defined_constants(false)); 127 | if ($conf->t_ignore_pre_defined_classes!='none') 128 | { 129 | if ($conf->t_ignore_pre_defined_classes=='all') $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_constants); 130 | if (is_array($conf->t_ignore_pre_defined_classes)) 131 | { 132 | $t_class_names = array_map('strtolower',$conf->t_ignore_pre_defined_classes); 133 | foreach($t_class_names as $class_name) if (isset($t_pre_defined_class_constants_by_class[$class_name])) $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_constants_by_class[$class_name]); 134 | } 135 | } 136 | if (isset($conf->t_ignore_class_constants)) 137 | { 138 | $t = $conf->t_ignore_class_constants; $t = array_flip($t); 139 | $this->t_ignore = array_merge($this->t_ignore,$t); 140 | } 141 | if (isset($conf->t_ignore_class_constants_prefix)) 142 | { 143 | $t = $conf->t_ignore_class_constants_prefix; $t = array_flip($t); 144 | $this->t_ignore_prefix = $t; 145 | } 146 | break; 147 | case 'variable': 148 | $this->case_sensitive = true; 149 | $this->t_ignore = array_flip($this->t_reserved_variable_names); 150 | if (isset($conf->t_ignore_variables)) 151 | { 152 | $t = $conf->t_ignore_variables; $t = array_flip($t); 153 | $this->t_ignore = array_merge($this->t_ignore,$t); 154 | } 155 | if (isset($conf->t_ignore_variables_prefix)) 156 | { 157 | $t = $conf->t_ignore_variables_prefix; $t = array_flip($t); 158 | $this->t_ignore_prefix = $t; 159 | } 160 | break; 161 | /* 162 | case 'function': 163 | $this->case_sensitive = false; 164 | $this->t_ignore = array_flip($this->t_reserved_function_names); 165 | $t = get_defined_functions(); $t = array_map('strtolower',$t['internal']); $t = array_flip($t); 166 | $this->t_ignore = array_merge($this->t_ignore,$t); 167 | if (isset($conf->t_ignore_functions)) 168 | { 169 | $t = $conf->t_ignore_functions; $t = array_map('strtolower',$t); $t = array_flip($t); 170 | $this->t_ignore = array_merge($this->t_ignore,$t); 171 | } 172 | if (isset($conf->t_ignore_functions_prefix)) 173 | { 174 | $t = $conf->t_ignore_functions_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 175 | $this->t_ignore_prefix = $t; 176 | } 177 | break; 178 | */ 179 | case 'property': 180 | $this->case_sensitive = true; 181 | $this->t_ignore = array_flip($this->t_reserved_variable_names); 182 | if ($conf->t_ignore_pre_defined_classes!='none') 183 | { 184 | if ($conf->t_ignore_pre_defined_classes=='all') $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_properties); 185 | if (is_array($conf->t_ignore_pre_defined_classes)) 186 | { 187 | $t_class_names = array_map('strtolower',$conf->t_ignore_pre_defined_classes); 188 | foreach($t_class_names as $class_name) if (isset($t_pre_defined_class_properties_by_class[$class_name])) $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_properties_by_class[$class_name]); 189 | } 190 | } 191 | if (isset($conf->t_ignore_properties)) 192 | { 193 | $t = $conf->t_ignore_properties; $t = array_flip($t); 194 | $this->t_ignore = array_merge($this->t_ignore,$t); 195 | } 196 | if (isset($conf->t_ignore_properties_prefix)) 197 | { 198 | $t = $conf->t_ignore_properties_prefix; $t = array_flip($t); 199 | $this->t_ignore_prefix = $t; 200 | } 201 | break; 202 | case 'function_or_class': // same instance is used for scrambling classes, interfaces, and traits. and namespaces... and functions ...for aliasing 203 | $this->case_sensitive = false; 204 | $this->t_ignore = array_flip($this->t_reserved_function_names); 205 | $t = get_defined_functions(); $t = array_map('strtolower',$t['internal']); $t = array_flip($t); 206 | $this->t_ignore = array_merge($this->t_ignore,$t); 207 | if (isset($conf->t_ignore_functions)) 208 | { 209 | $t = $conf->t_ignore_functions; $t = array_map('strtolower',$t); $t = array_flip($t); 210 | $this->t_ignore = array_merge($this->t_ignore,$t); 211 | } 212 | if (isset($conf->t_ignore_functions_prefix)) 213 | { 214 | $t = $conf->t_ignore_functions_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 215 | $this->t_ignore_prefix = $t; 216 | } 217 | 218 | $this->t_ignore = array_merge($this->t_ignore, array_flip($this->t_reserved_class_names)); 219 | $this->t_ignore = array_merge($this->t_ignore, array_flip($this->t_reserved_variable_names)); 220 | // $this->t_ignore = array_merge($this->t_ignore, array_flip($this->t_reserved_function_names)); 221 | $t = get_defined_functions(); $t = array_flip($t['internal']); 222 | $this->t_ignore = array_merge($this->t_ignore,$t); 223 | if ($conf->t_ignore_pre_defined_classes!='none') 224 | { 225 | if ($conf->t_ignore_pre_defined_classes=='all') $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_classes); 226 | if (is_array($conf->t_ignore_pre_defined_classes)) 227 | { 228 | $t_class_names = array_map('strtolower',$conf->t_ignore_pre_defined_classes); 229 | foreach($t_class_names as $class_name) if (isset($t_pre_defined_classes[$class_name])) $this->t_ignore[$class_name] = 1; 230 | } 231 | } 232 | if (isset($conf->t_ignore_classes)) 233 | { 234 | $t = $conf->t_ignore_classes; $t = array_map('strtolower',$t); $t = array_flip($t); 235 | $this->t_ignore = array_merge($this->t_ignore,$t); 236 | } 237 | if (isset($conf->t_ignore_interfaces)) 238 | { 239 | $t = $conf->t_ignore_interfaces; $t = array_map('strtolower',$t); $t = array_flip($t); 240 | $this->t_ignore = array_merge($this->t_ignore,$t); 241 | } 242 | if (isset($conf->t_ignore_traits)) 243 | { 244 | $t = $conf->t_ignore_traits; $t = array_map('strtolower',$t); $t = array_flip($t); 245 | $this->t_ignore = array_merge($this->t_ignore,$t); 246 | } 247 | if (isset($conf->t_ignore_namespaces)) 248 | { 249 | $t = $conf->t_ignore_namespaces; $t = array_map('strtolower',$t); $t = array_flip($t); 250 | $this->t_ignore = array_merge($this->t_ignore,$t); 251 | } 252 | if (isset($conf->t_ignore_classes_prefix)) 253 | { 254 | $t = $conf->t_ignore_classes_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 255 | $this->t_ignore_prefix = array_merge($this->t_ignore_prefix,$t); 256 | } 257 | if (isset($conf->t_ignore_interfaces_prefix)) 258 | { 259 | $t = $conf->t_ignore_interfaces_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 260 | $this->t_ignore_prefix = array_merge($this->t_ignore_prefix,$t); 261 | } 262 | if (isset($conf->t_ignore_traits_prefix)) 263 | { 264 | $t = $conf->t_ignore_traits_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 265 | $this->t_ignore_prefix = array_merge($this->t_ignore_prefix,$t); 266 | } 267 | if (isset($conf->t_ignore_namespaces_prefix)) 268 | { 269 | $t = $conf->t_ignore_namespaces_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 270 | $this->t_ignore_prefix = array_merge($this->t_ignore_prefix,$t); 271 | } 272 | break; 273 | case 'method': 274 | $this->case_sensitive = false; 275 | if ($conf->parser_mode=='ONLY_PHP7') $this->t_ignore = array(); // in php7 method names can be keywords 276 | else $this->t_ignore = array_flip($this->t_reserved_function_names); 277 | 278 | $t = array_flip($this->t_reserved_method_names); 279 | $this->t_ignore = array_merge($this->t_ignore,$t); 280 | 281 | $t = get_defined_functions(); $t = array_map('strtolower',$t['internal']); $t = array_flip($t); 282 | $this->t_ignore = array_merge($this->t_ignore,$t); 283 | if ($conf->t_ignore_pre_defined_classes!='none') 284 | { 285 | if ($conf->t_ignore_pre_defined_classes=='all') $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_methods); 286 | if (is_array($conf->t_ignore_pre_defined_classes)) 287 | { 288 | $t_class_names = array_map('strtolower',$conf->t_ignore_pre_defined_classes); 289 | foreach($t_class_names as $class_name) if (isset($t_pre_defined_class_methods_by_class[$class_name])) $this->t_ignore = array_merge($this->t_ignore,$t_pre_defined_class_methods_by_class[$class_name]); 290 | } 291 | } 292 | if (isset($conf->t_ignore_methods)) 293 | { 294 | $t = $conf->t_ignore_methods; $t = array_map('strtolower',$t); $t = array_flip($t); 295 | $this->t_ignore = array_merge($this->t_ignore,$t); 296 | } 297 | if (isset($conf->t_ignore_methods_prefix)) 298 | { 299 | $t = $conf->t_ignore_methods_prefix; $t = array_map('strtolower',$t); $t = array_flip($t); 300 | $this->t_ignore_prefix = $t; 301 | } 302 | break; 303 | case 'label': 304 | $this->case_sensitive = true; 305 | $this->t_ignore = array_flip($this->t_reserved_function_names); 306 | if (isset($conf->t_ignore_labels)) 307 | { 308 | $t = $conf->t_ignore_labels; $t = array_flip($t); 309 | $this->t_ignore = array_merge($this->t_ignore,$t); 310 | } 311 | if (isset($conf->t_ignore_labels_prefix)) 312 | { 313 | $t = $conf->t_ignore_labels_prefix; $t = array_flip($t); 314 | $this->t_ignore_prefix = $t; 315 | } 316 | break; 317 | } 318 | if (isset($target_directory)) // the constructor will restore previous saved context if exists 319 | { 320 | $this->context_directory = $target_directory; 321 | if (file_exists("{$this->context_directory}/yakpro-po/context/{$this->scramble_type}")) 322 | { 323 | $t = unserialize(file_get_contents("{$this->context_directory}/yakpro-po/context/{$this->scramble_type}")); 324 | if ($t[0] !== self::SCRAMBLER_CONTEXT_VERSION) 325 | { 326 | fprintf(STDERR,"Error:\tContext format has changed! run with --clean option!".PHP_EOL); 327 | $this->context_directory = null; // do not overwrite incoherent values when exiting 328 | exit(1); 329 | } 330 | $this->t_scramble = $t[1]; 331 | $this->t_rscramble = $t[2]; 332 | $this->scramble_length = $t[3]; 333 | $this->label_counter = $t[4]; 334 | } 335 | } 336 | } 337 | 338 | function __destruct() 339 | { 340 | //print_r($this->t_scramble); 341 | if (!$this->silent) fprintf(STDERR,"Info:\t[%-17s] scrambled \t: %8d%s",$this->scramble_type,count($this->t_scramble),PHP_EOL); 342 | if (isset($this->context_directory)) // the destructor will save the current context 343 | { 344 | $t = array(); 345 | $t[0] = self::SCRAMBLER_CONTEXT_VERSION; 346 | $t[1] = $this->t_scramble; 347 | $t[2] = $this->t_rscramble; 348 | $t[3] = $this->scramble_length; 349 | $t[4] = $this->label_counter; 350 | file_put_contents("{$this->context_directory}/yakpro-po/context/{$this->scramble_type}",serialize($t)); 351 | } 352 | } 353 | 354 | private function str_scramble($s) // scramble the string according parameters 355 | { 356 | $c1 = $this->t_first_chars[mt_rand(0, $this->l1)]; // first char of the identifier 357 | $c2 = $this->t_chars [mt_rand(0, $this->l2)]; // prepending salt for md5 358 | $this->r = str_shuffle(md5($c2.$s.md5($this->r))); // 32 chars random hex number derived from $s and lot of pepper and salt 359 | 360 | $s = $c1; 361 | switch($this->scramble_mode) 362 | { 363 | case 'numeric': 364 | for($i=0,$l=$this->scramble_length-1;$i<$l;++$i) $s .= $this->t_chars[base_convert(substr($this->r,$i,2),16,10)%($this->l2+1)]; 365 | break; 366 | case 'hexa': 367 | for($i=0,$l=$this->scramble_length-1;$i<$l;++$i) $s .= substr($this->r,$i,1); 368 | break; 369 | case 'identifier': 370 | default: 371 | for($i=0,$l=$this->scramble_length-1;$i<$l;++$i) $s .= $this->t_chars[base_convert(substr($this->r,2*$i,2),16,10)%($this->l2+1)]; 372 | } 373 | return $s; 374 | } 375 | 376 | private function case_shuffle($s) // this function is used to even more obfuscate insensitive names: on each acces to the name, a different randomized case of each letter is used. 377 | { 378 | for($i=0;$icase_sensitive ? $s : strtolower($s); 385 | if ( array_key_exists($r,$this->t_ignore) ) return $s; 386 | 387 | if (isset($this->t_ignore_prefix)) 388 | { 389 | foreach($this->t_ignore_prefix as $key => $dummy) if (substr($r,0,strlen($key))===$key) return $s; 390 | } 391 | 392 | if (!isset($this->t_scramble[$r])) // if not already scrambled: 393 | { 394 | for($i=0;$i<50;++$i) // try at max 50 times if the random generated scrambled string has already beeen generated! 395 | { 396 | $x = $this->str_scramble($s); 397 | $z = strtolower($x); 398 | $y = $this->case_sensitive ? $x : $z; 399 | if (isset($this->t_rscramble[$y]) || isset($this->t_ignore[$z]) ) // this random value is either already used or a reserved name 400 | { 401 | if (($i==5) && ($this->scramble_length < $this->scramble_length_max)) ++$this->scramble_length; // if not found after 5 attempts, increase the length... 402 | continue; // the next attempt will always be successfull, unless we already are maxlength 403 | } 404 | $this->t_scramble [$r] = $y; 405 | $this->t_rscramble[$y] = $r; 406 | break; 407 | } 408 | if (!isset($this->t_scramble[$r])) 409 | { 410 | fprintf(STDERR,"Scramble Error: Identifier not found after 50 iterations!%sAborting...%s",PHP_EOL,PHP_EOL); // should statistically never occur! 411 | exit(2); 412 | } 413 | } 414 | return $this->case_sensitive ? $this->t_scramble[$r] : $this->case_shuffle($this->t_scramble[$r]); 415 | } 416 | 417 | public function unscramble($s) 418 | { 419 | if (!$this->case_sensitive) $s = strtolower($s); 420 | return isset($this->t_rscramble[$s]) ? $this->t_rscramble[$s] : ''; 421 | } 422 | 423 | public function generate_label_name($prefix = "!label") 424 | { 425 | return $prefix.($this->label_counter++); 426 | } 427 | } 428 | 429 | ?> 430 | -------------------------------------------------------------------------------- /include/classes/parser_extensions/my_node_visitor.php: -------------------------------------------------------------------------------- 1 | shuffle_stmts) 25 | { 26 | if (isset($node->stmts)) 27 | { 28 | $stmts = $node->stmts; 29 | $chunk_size = shuffle_get_chunk_size($stmts); 30 | if ($chunk_size<=0) return false; // should never occur! 31 | 32 | if (count($stmts)>(2*$chunk_size)) 33 | { 34 | // $last_inst = array_pop($stmts); 35 | $stmts = shuffle_statements($stmts); 36 | // $stmts[] = $last_inst; 37 | $node->stmts = $stmts; 38 | return true; 39 | } 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | private function get_identifier_name(PhpParser\Node $node) 46 | { 47 | if ($node instanceof PhpParser\Node\Identifier || $node instanceof PhpParser\Node\VarLikeIdentifier) return $node->name; 48 | return ''; 49 | } 50 | 51 | private function set_identifier_name(PhpParser\Node &$node,$name) 52 | { 53 | if ($node instanceof PhpParser\Node\Identifier || $node instanceof PhpParser\Node\VarLikeIdentifier) 54 | { 55 | $node->name = $name; 56 | } 57 | } 58 | 59 | public function enterNode(PhpParser\Node $node) 60 | { 61 | global $conf; 62 | global $t_scrambler; 63 | 64 | if (count($this->t_node_stack)) 65 | { 66 | $node->setAttribute('parent', $this->t_node_stack[count($this->t_node_stack)-1]); 67 | } 68 | $this->t_node_stack[] = $node; 69 | 70 | if ($conf->obfuscate_loop_statement) // loop statements are replaced by goto ... 71 | { 72 | $scrambler = $t_scrambler['label']; 73 | if ( ($node instanceof PhpParser\Node\Stmt\For_) || ($node instanceof PhpParser\Node\Stmt\Foreach_) || ($node instanceof PhpParser\Node\Stmt\Switch_) 74 | || ($node instanceof PhpParser\Node\Stmt\While_) || ($node instanceof PhpParser\Node\Stmt\Do_) ) 75 | { 76 | $label_loop_break_name = $scrambler->scramble($scrambler->generate_label_name()); 77 | $label_loop_continue_name = $scrambler->scramble($scrambler->generate_label_name()); 78 | $this->t_loop_stack[] = array($label_loop_break_name,$label_loop_continue_name); 79 | } 80 | } 81 | if ( ($node instanceof PhpParser\Node\Stmt\Class_) && ($node->name != null) ) 82 | { 83 | $name = $this->get_identifier_name($node->name); 84 | if ( is_string($name) && (strlen($name) !== 0) ) 85 | { 86 | $this->current_class_name = $name; 87 | } 88 | } 89 | if ($node instanceof PhpParser\Node\Stmt\ClassConst) 90 | { 91 | $this->is_in_class_const_definition = true; 92 | } 93 | } 94 | 95 | public function leaveNode(PhpParser\Node $node) 96 | { 97 | global $conf; 98 | global $t_scrambler; 99 | global $debug_mode; 100 | 101 | $node_modified = false; 102 | 103 | if ($node instanceof PhpParser\Node\Stmt\Class_) $this->current_class_name = null; 104 | if ($node instanceof PhpParser\Node\Stmt\ClassConst) $this->is_in_class_const_definition = false; 105 | 106 | if ($conf->obfuscate_string_literal) 107 | { 108 | if ($node instanceof PhpParser\Node\Stmt\InlineHTML) 109 | { 110 | $node = new PhpParser\Node\Stmt\Echo_([new PhpParser\Node\Scalar\String_($node->value)]); 111 | $node_modified = true; 112 | } 113 | } 114 | 115 | if ($conf->obfuscate_variable_name) 116 | { 117 | $scrambler = $t_scrambler['variable']; 118 | if ($node instanceof PhpParser\Node\Expr\Variable) 119 | { 120 | $name = $node->name; 121 | if ( is_string($name) && (strlen($name) !== 0) ) 122 | { 123 | $r = $scrambler->scramble($name); 124 | if ($r!==$name) 125 | { 126 | $node->name = $r; 127 | $node_modified = true; 128 | } 129 | } 130 | } 131 | if ( ($node instanceof PhpParser\Node\Stmt\Catch_) || ($node instanceof PhpParser\Node\Expr\ClosureUse) || ($node instanceof PhpParser\Node\Param) ) 132 | { 133 | $name = $node->{'var'}; // equivalent to $node->var, that works also on my php version! 134 | if ( is_string($name) && (strlen($name) !== 0) ) // but 'var' is a reserved function name, so there is no warranty 135 | { // that it will work in the future, so the $node->{'var'} form 136 | $r = $scrambler->scramble($name); // has been used! 137 | if ($r!==$name) 138 | { 139 | $node->{'var'} = $r; 140 | $node_modified = true; 141 | } 142 | } 143 | } 144 | } 145 | 146 | if ($conf->obfuscate_function_name) 147 | { 148 | $scrambler = $t_scrambler['function_or_class']; 149 | if ($node instanceof PhpParser\Node\Stmt\Function_) 150 | { 151 | $name = $node->name->name; 152 | if ( is_string($name) && (strlen($name) !== 0) ) 153 | { 154 | $r = $scrambler->scramble($name); 155 | if ($r!==$name) 156 | { 157 | $node->name = $r; 158 | $node_modified = true; 159 | } 160 | } 161 | } 162 | if ($node instanceof PhpParser\Node\Expr\FuncCall ) 163 | { 164 | if (isset($node->name->parts)) // not set when indirect call (i.e.function name is a variable value!) 165 | { 166 | $parts = $node->name->parts; 167 | $name = $parts[count($parts)-1]; 168 | if ( is_string($name) && (strlen($name) !== 0) ) 169 | { 170 | $r = $scrambler->scramble($name); 171 | if ($r!==$name) 172 | { 173 | $node->name->parts[count($parts)-1] = $r; 174 | $node_modified = true; 175 | } 176 | } 177 | } 178 | } 179 | if ($node instanceof PhpParser\Node\Expr\FuncCall) // processing function_exists('function_name'); 180 | { 181 | if (isset($node->name->parts)) // not set when indirect call (i.e.function name is a variable value!) 182 | { 183 | $parts = $node->name->parts; 184 | $name = $parts[count($parts)-1]; 185 | if ( is_string($name) && ($name=='function_exists') ) 186 | { 187 | for($ok=false;;) 188 | { 189 | if (!isset($node->args[0]->value)) break; 190 | if (count($node->args)!=1) break; 191 | $arg = $node->args[0]->value; if (! ($arg instanceof PhpParser\Node\Scalar\String_) ) { $ok = true; $warning = true; break; } 192 | $name = $arg->value; if (! is_string($name) || (strlen($name) == 0) ) break; 193 | $ok = true; 194 | $warning= false; 195 | $r = $scrambler->scramble($name); 196 | if ($r!==$name) 197 | { 198 | $arg->value = $r; 199 | $node_modified = true; 200 | } 201 | break; 202 | } 203 | if (!$ok) 204 | { 205 | throw new Exception("Error: your use of function_exists() function is not compatible with yakpro-po!".PHP_EOL."\tOnly 1 literal string parameter is allowed..."); 206 | } 207 | if ($warning) fprintf(STDERR, "Warning: your use of function_exists() function is not compatible with yakpro-po!".PHP_EOL."\t Only 1 literal string parameter is allowed...".PHP_EOL); 208 | } 209 | } 210 | } 211 | } 212 | 213 | if ($conf->obfuscate_class_name) 214 | { 215 | $scrambler = $t_scrambler['function_or_class']; 216 | if ($node instanceof PhpParser\Node\Stmt\Class_) 217 | { 218 | if ($node->name != null) 219 | { 220 | $name = $this->get_identifier_name($node->name); 221 | if ( is_string($name) && (strlen($name) !== 0) ) 222 | { 223 | $r = $scrambler->scramble($name); 224 | if ($r!==$name) 225 | { 226 | $this->set_identifier_name($node->name,$r); 227 | $node_modified = true; 228 | } 229 | } 230 | } 231 | if (isset($node->{'extends'})) 232 | { 233 | $parts = $node->{'extends'}->parts; 234 | $name = $parts[count($parts)-1]; 235 | if ( is_string($name) && (strlen($name) !== 0) ) 236 | { 237 | $r = $scrambler->scramble($name); 238 | if ($r!==$name) 239 | { 240 | $node->{'extends'}->parts[count($parts)-1] = $r; 241 | $node_modified = true; 242 | } 243 | } 244 | } 245 | } 246 | if ( ($node instanceof PhpParser\Node\Expr\New_) 247 | || ($node instanceof PhpParser\Node\Expr\StaticCall) 248 | || ($node instanceof PhpParser\Node\Expr\StaticPropertyFetch) 249 | || ($node instanceof PhpParser\Node\Expr\ClassConstFetch) 250 | || ($node instanceof PhpParser\Node\Expr\Instanceof_) 251 | ) 252 | { 253 | if (isset($node->{'class'}->parts)) 254 | { 255 | $parts = $node->{'class'}->parts; 256 | $name = $parts[count($parts)-1]; 257 | if ( is_string($name) && (strlen($name) !== 0) ) 258 | { 259 | $r = $scrambler->scramble($name); 260 | if ($r!==$name) 261 | { 262 | $node->{'class'}->parts[count($parts)-1] = $r; 263 | $node_modified = true; 264 | } 265 | } 266 | } 267 | } 268 | if ($node instanceof PhpParser\Node\Param) 269 | { 270 | if (isset($node->type) && isset($node->type->parts)) 271 | { 272 | $parts = $node->type->parts; 273 | $name = $parts[count($parts)-1]; 274 | if ( is_string($name) && (strlen($name) !== 0) ) 275 | { 276 | $r = $scrambler->scramble($name); 277 | if ($r!==$name) 278 | { 279 | $node->type->parts[count($parts)-1] = $r; 280 | $node_modified = true; 281 | } 282 | } 283 | } 284 | } 285 | if ($node instanceof PhpParser\Node\Stmt\ClassMethod || $node instanceof PhpParser\Node\Stmt\Function_) 286 | { 287 | if (isset($node->returnType)) 288 | { 289 | $node_tmp = $node->returnType; 290 | if ($node_tmp instanceof PhpParser\Node\NullableType && isset($node_tmp->type) ) 291 | { 292 | $node_tmp = $node_tmp->type; 293 | } 294 | if ($node_tmp instanceof PhpParser\Node\Name && isset($node_tmp->parts)) 295 | { 296 | $parts = $node_tmp->parts; 297 | $name = $parts[count($parts)-1]; 298 | if ( is_string($name) && (strlen($name) !== 0) ) 299 | { 300 | $r = $scrambler->scramble($name); 301 | if ($r!==$name) 302 | { 303 | $node_tmp->parts[count($parts)-1] = $r; 304 | $node_modified = true; 305 | } 306 | } 307 | } 308 | } 309 | } 310 | if ($node instanceof PhpParser\Node\Stmt\Catch_) 311 | { 312 | if (isset($node->types)) 313 | { 314 | $types = $node->types; 315 | foreach($types as &$type) 316 | { 317 | $parts = $type->parts; 318 | $name = $parts[count($parts)-1]; 319 | if ( is_string($name) && (strlen($name) !== 0) ) 320 | { 321 | $r = $scrambler->scramble($name); 322 | if ($r!==$name) 323 | { 324 | $type->parts[count($parts)-1] = $r; 325 | $node_modified = true; 326 | } 327 | } 328 | } 329 | } 330 | } 331 | } 332 | 333 | if ($conf->obfuscate_interface_name) 334 | { 335 | $scrambler = $t_scrambler['function_or_class']; 336 | if ($node instanceof PhpParser\Node\Stmt\Interface_) 337 | { 338 | $name = $this->get_identifier_name($node->name); 339 | if ( is_string($name) && (strlen($name) !== 0) ) 340 | { 341 | $r = $scrambler->scramble($name); 342 | if ($r!==$name) 343 | { 344 | $this->set_identifier_name($node->name,$r); 345 | $node_modified = true; 346 | } 347 | } 348 | if ( isset($node->{'extends'}) && count($node->{'extends'}) ) 349 | { 350 | for($j=0;$j{'extends'});++$j) 351 | { 352 | $parts = $node->{'extends'}[$j]->parts; 353 | $name = $parts[count($parts)-1]; 354 | if ( is_string($name) && (strlen($name) !== 0) ) 355 | { 356 | $r = $scrambler->scramble($name); 357 | if ($r!==$name) 358 | { 359 | $node->{'extends'}[$j]->parts[count($parts)-1] = $r; 360 | $node_modified = true; 361 | } 362 | } 363 | } 364 | } 365 | } 366 | if ($node instanceof PhpParser\Node\Stmt\Class_) 367 | { 368 | if ( isset($node->{'implements'}) && count($node->{'implements'}) ) 369 | { 370 | for($j=0;$j{'implements'});++$j) 371 | { 372 | $parts = $node->{'implements'}[$j]->parts; 373 | $name = $parts[count($parts)-1]; 374 | if ( is_string($name) && (strlen($name) !== 0) ) 375 | { 376 | $r = $scrambler->scramble($name); 377 | if ($r!==$name) 378 | { 379 | $node->{'implements'}[$j]->parts[count($parts)-1] = $r; 380 | $node_modified = true; 381 | } 382 | } 383 | } 384 | } 385 | } 386 | } 387 | 388 | if ($conf->obfuscate_trait_name) 389 | { 390 | $scrambler = $t_scrambler['function_or_class']; 391 | if ($node instanceof PhpParser\Node\Stmt\Trait_) 392 | { 393 | $name = $this->get_identifier_name($node->name); 394 | if ( is_string($name) && (strlen($name) !== 0) ) 395 | { 396 | $r = $scrambler->scramble($name); 397 | if ($r!==$name) 398 | { 399 | $this->set_identifier_name($node->name,$r); 400 | $node_modified = true; 401 | } 402 | } 403 | } 404 | if ($node instanceof PhpParser\Node\Stmt\TraitUse) 405 | { 406 | if ( isset($node->{'traits'}) && count($node->{'traits'}) ) 407 | { 408 | for($j=0;$j{'traits'});++$j) 409 | { 410 | $parts = $node->{'traits'}[$j]->parts; 411 | $name = $parts[count($parts)-1]; 412 | if ( is_string($name) && (strlen($name) !== 0) ) 413 | { 414 | $r = $scrambler->scramble($name); 415 | if ($r!==$name) 416 | { 417 | $node->{'traits'}[$j]->parts[count($parts)-1] = $r; 418 | $node_modified = true; 419 | } 420 | } 421 | } 422 | } 423 | } 424 | } 425 | 426 | if ($conf->obfuscate_property_name) 427 | { 428 | $scrambler = $t_scrambler['property']; 429 | if ( ($node instanceof PhpParser\Node\Expr\PropertyFetch) || ($node instanceof PhpParser\Node\Stmt\PropertyProperty) || ($node instanceof PhpParser\Node\Expr\StaticPropertyFetch) ) 430 | { 431 | $name = $this->get_identifier_name($node->name); 432 | if ( is_string($name) && (strlen($name) !== 0) ) 433 | { 434 | $r = $scrambler->scramble($name); 435 | if ($r!==$name) 436 | { 437 | $this->set_identifier_name($node->name,$r); 438 | $node_modified = true; 439 | } 440 | } 441 | } 442 | } 443 | 444 | if ($conf->obfuscate_method_name) 445 | { 446 | $scrambler = $t_scrambler['method']; 447 | if ( ($node instanceof PhpParser\Node\Stmt\ClassMethod) || ($node instanceof PhpParser\Node\Expr\MethodCall) || ($node instanceof PhpParser\Node\Expr\StaticCall) ) 448 | { 449 | $name = $this->get_identifier_name($node->name); 450 | if ( is_string($name) && (strlen($name) !== 0) ) 451 | { 452 | $r = $scrambler->scramble($name); 453 | if ($r!==$name) 454 | { 455 | $this->set_identifier_name($node->name,$r); 456 | $node_modified = true; 457 | } 458 | } 459 | } 460 | } 461 | 462 | if ($conf->obfuscate_constant_name) 463 | { 464 | $scrambler = $t_scrambler['constant']; 465 | if ($node instanceof PhpParser\Node\Expr\FuncCall) // processing define('constant_name',value); 466 | { 467 | if (isset($node->name->parts)) // not set when indirect call (i.e.function name is a variable value!) 468 | { 469 | $parts = $node->name->parts; 470 | $fn_name = $parts[count($parts)-1]; 471 | if ( is_string($fn_name) && ( ($fn_name=='define') || ($fn_name=='defined') ) ) 472 | { 473 | for($ok=false;;) 474 | { 475 | if (!isset($node->args[0]->value)) break; 476 | if ( ($fn_name=='define') && (count($node->args)!=2) ) break; 477 | $arg = $node->args[0]->value; if (! ($arg instanceof PhpParser\Node\Scalar\String_) ) break; 478 | $name = $arg->value; if (! is_string($name) || (strlen($name) == 0) ) break; 479 | $ok = true; 480 | $r = $scrambler->scramble($name); 481 | if ($r!==$name) 482 | { 483 | $arg->value = $r; 484 | $node_modified = true; 485 | } 486 | break; 487 | } 488 | if (!$ok) 489 | { 490 | if ($fn_name=='define') throw new Exception("Error: your use of $fn_name() function is not compatible with yakpro-po!".PHP_EOL."\tOnly 2 parameters, when first is a literal string is allowed..."); 491 | else throw new Exception("Error: your use of $fn_name() function is not compatible with yakpro-po!".PHP_EOL."\tOnly 1 literal string parameter is allowed..."); 492 | } 493 | } 494 | } 495 | } 496 | if ($node instanceof PhpParser\Node\Expr\ConstFetch) 497 | { 498 | $parts = $node->name->parts; 499 | $name = $parts[count($parts)-1]; 500 | if ( is_string($name) && (strlen($name) !== 0) ) 501 | { 502 | $r = $scrambler->scramble($name); 503 | if ($r!==$name) 504 | { 505 | $node->name->parts[count($parts)-1] = $r; 506 | $node_modified = true; 507 | } 508 | } 509 | } 510 | if ( ($node instanceof PhpParser\Node\Const_) && !$this->is_in_class_const_definition ) 511 | { 512 | $name = $this->get_identifier_name($node->name); 513 | if ( is_string($name) && (strlen($name) !== 0) ) 514 | { 515 | $r = $scrambler->scramble($name); 516 | if ($r!==$name) 517 | { 518 | $this->set_identifier_name($node->name,$r); 519 | $node_modified = true; 520 | } 521 | } 522 | } 523 | } 524 | 525 | if ($conf->obfuscate_class_constant_name) 526 | { 527 | $scrambler = $t_scrambler['class_constant']; 528 | if ( ($node instanceof PhpParser\Node\Const_) && $this->is_in_class_const_definition ) 529 | { 530 | $name = $this->get_identifier_name($node->name); 531 | if ( is_string($name) && (strlen($name) !== 0) ) 532 | { 533 | $r = $scrambler->scramble($name); 534 | if ($r!==$name) 535 | { 536 | $this->set_identifier_name($node->name,$r); 537 | $node_modified = true; 538 | } 539 | } 540 | } 541 | if ($node instanceof PhpParser\Node\Expr\ClassConstFetch) 542 | { 543 | $name = $node->name; 544 | $name = $this->get_identifier_name($node->name); 545 | if ( is_string($name) && (strlen($name) !== 0) ) 546 | { 547 | $r = $scrambler->scramble($name); 548 | if ($r!==$name) 549 | { 550 | $this->set_identifier_name($node->name,$r); 551 | $node_modified = true; 552 | } 553 | } 554 | } 555 | } 556 | 557 | if ($node instanceof PhpParser\Node\Stmt\UseUse) 558 | { 559 | if ($conf->obfuscate_function_name || $conf->obfuscate_class_name) 560 | { 561 | if (isset($node->alias)) 562 | { 563 | if (!$conf->obfuscate_function_name || !$conf->obfuscate_class_name) 564 | { 565 | fprintf(STDERR, "Warning:[use alias] cannot determine at compile time if it is a function or a class alias".PHP_EOL."\tyou must obfuscate both functions and classes or none...".PHP_EOL."\tObfuscated code may not work!".PHP_EOL); 566 | } 567 | $scrambler = $t_scrambler['function_or_class']; 568 | $name = $this->get_identifier_name($node->alias); 569 | if ( is_string($name) && (strlen($name) !== 0) ) 570 | { 571 | $r = $scrambler->scramble($name); 572 | if ($r!==$name) 573 | { 574 | //$node->alias = $r; 575 | $this->set_identifier_name($node->alias,$r); 576 | $node_modified = true; 577 | } 578 | } 579 | } 580 | } 581 | } 582 | 583 | 584 | if ($conf->obfuscate_namespace_name) 585 | { 586 | $scrambler = $t_scrambler['function_or_class']; 587 | if ( ($node instanceof PhpParser\Node\Stmt\Namespace_) || ($node instanceof PhpParser\Node\Stmt\UseUse) ) 588 | { 589 | if (isset($node->name->parts)) 590 | { 591 | $parts = $node->name->parts; 592 | for($i=0;$iscramble($name); 598 | if ($r!==$name) 599 | { 600 | $node->name->parts[$i] = $r; 601 | $node_modified = true; 602 | } 603 | } 604 | } 605 | } 606 | } 607 | /* 608 | if ($node instanceof PhpParser\Node\Stmt\UseUse) 609 | { 610 | //$name = $node->alias; 611 | $name = $this->get_identifier_name($node->alias); 612 | if ( is_string($name) && (strlen($name) !== 0) ) 613 | { 614 | $r = $scrambler->scramble($name); 615 | if ($r!==$name) 616 | { 617 | //$node->alias = $r; 618 | $this->set_identifier_name($node->alias,$r); 619 | $node_modified = true; 620 | } 621 | } 622 | } 623 | */ 624 | if ( ($node instanceof PhpParser\Node\Expr\FuncCall) || ($node instanceof PhpParser\Node\Expr\ConstFetch) ) 625 | { 626 | if (isset($node->name->parts)) // not set when indirect call (i.e.function name is a variable value!) 627 | { 628 | $parts = $node->name->parts; 629 | for($i=0;$iscramble($name); 635 | if ($r!==$name) 636 | { 637 | $node->name->parts[$i] = $r; 638 | $node_modified = true; 639 | } 640 | } 641 | } 642 | } 643 | } 644 | if ( ($node instanceof PhpParser\Node\Expr\New_) 645 | || ($node instanceof PhpParser\Node\Expr\Instanceof_) 646 | || ($node instanceof PhpParser\Node\Expr\StaticCall) 647 | || ($node instanceof PhpParser\Node\Expr\StaticPropertyFetch) 648 | || ($node instanceof PhpParser\Node\Expr\ClassConstFetch) 649 | ) 650 | { 651 | if (isset($node->{'class'}->parts)) // not set when indirect call (i.e.function name is a variable value!) 652 | { 653 | $parts = $node->{'class'}->parts; 654 | for($i=0;$iscramble($name); 660 | if ($r!==$name) 661 | { 662 | $node->{'class'}->parts[$i] = $r; 663 | $node_modified = true; 664 | } 665 | } 666 | } 667 | } 668 | } 669 | if ($node instanceof PhpParser\Node\Stmt\Class_) 670 | { 671 | if (isset($node->{'extends'}) && isset($node->{'extends'}->parts)) 672 | { 673 | $parts = $node->{'extends'}->parts; 674 | for($i=0;$iscramble($name); 680 | if ($r!==$name) 681 | { 682 | $node->{'extends'}->parts[$i] = $r; 683 | $node_modified = true; 684 | } 685 | } 686 | } 687 | } 688 | if ( isset($node->{'implements'}) && count($node->{'implements'}) ) 689 | { 690 | for($j=0;$j{'implements'});++$j) 691 | { 692 | $parts = $node->{'implements'}[$j]->parts; 693 | for($i=0;$iscramble($name); 699 | if ($r!==$name) 700 | { 701 | $node->{'implements'}[$j]->parts[$i] = $r; 702 | $node_modified = true; 703 | } 704 | } 705 | } 706 | } 707 | } 708 | } 709 | if ($node instanceof PhpParser\Node\Param) 710 | { 711 | if (isset($node->type) && isset($node->type->parts)) 712 | { 713 | $parts = $node->type->parts; 714 | for($i=0;$iscramble($name); 720 | if ($r!==$name) 721 | { 722 | $node->type->parts[$i] = $r; 723 | $node_modified = true; 724 | } 725 | } 726 | } 727 | } 728 | } 729 | if ($node instanceof PhpParser\Node\Stmt\Interface_) 730 | { 731 | if (isset($node->{'extends'}) && isset($node->{'extends'}->parts)) 732 | { 733 | for($j=0;$j{'extends'});++$j) 734 | { 735 | $parts = $node->{'extends'}[$j]->parts; 736 | for($i=0;$iscramble($name); 742 | if ($r!==$name) 743 | { 744 | $node->{'extends'}[$j]->parts[$i] = $r; 745 | $node_modified = true; 746 | } 747 | } 748 | } 749 | } 750 | } 751 | } 752 | if ($node instanceof PhpParser\Node\Stmt\TraitUse) 753 | { 754 | if ( isset($node->{'traits'}) && count($node->{'traits'}) ) 755 | { 756 | for($j=0;$j{'traits'});++$j) 757 | { 758 | $parts = $node->{'traits'}[$j]->parts; 759 | for($i=0;$iscramble($name); 765 | if ($r!==$name) 766 | { 767 | $node->{'traits'}[$j]->parts[$i] = $r; 768 | $node_modified = true; 769 | } 770 | } 771 | } 772 | } 773 | } 774 | } 775 | if ($node instanceof PhpParser\Node\Stmt\Catch_) 776 | { 777 | if (isset($node->types)) 778 | { 779 | $types = $node->types; 780 | foreach($types as &$type) 781 | { 782 | $parts = $type->parts; 783 | for($i=0;$iscramble($name); 789 | if ($r!==$name) 790 | { 791 | $type->parts[$i] = $r; 792 | $node_modified = true; 793 | } 794 | } 795 | } 796 | } 797 | } 798 | } 799 | } 800 | 801 | if ($conf->obfuscate_label_name) // label: goto label; - 802 | { 803 | $scrambler = $t_scrambler['label']; 804 | if ( ($node instanceof PhpParser\Node\Stmt\Label) || ($node instanceof PhpParser\Node\Stmt\Goto_) ) 805 | { 806 | $name = $this->get_identifier_name($node->name); 807 | if ( is_string($name) && (strlen($name) !== 0) ) 808 | { 809 | $r = $scrambler->scramble($name); 810 | if ($r!==$name) 811 | { 812 | $node->name = $r; 813 | $node_modified = true; 814 | } 815 | } 816 | } 817 | } 818 | 819 | if ($conf->obfuscate_if_statement) // if else elseif are replaced by goto ... 820 | { 821 | $scrambler = $t_scrambler['label']; 822 | $ok_to_scramble = false; 823 | if ( ($node instanceof PhpParser\Node\Stmt\If_) ) // except if function_exists is ther... 824 | { 825 | $ok_to_scramble = true; 826 | $condition = $node->cond; 827 | if ($condition instanceof PhpParser\Node\Expr\BooleanNot) 828 | { 829 | $expr = $condition->expr; 830 | if ($expr instanceof PhpParser\Node\Expr\FuncCall) 831 | { 832 | $name = $expr->name; 833 | if ($name instanceof PhpParser\Node\Name) 834 | { 835 | $parts = $name->parts; 836 | $part = $parts[0]; 837 | if ($part == 'function_exists') 838 | { 839 | $ok_to_scramble = false; 840 | } 841 | } 842 | } 843 | } 844 | } 845 | if ( $ok_to_scramble ) 846 | { 847 | $condition = $node->cond; 848 | $stmts = $node->stmts; 849 | $else = isset($node->{'else'}) ? $node->{'else'}->stmts : null; 850 | $elseif = $node->elseifs; 851 | 852 | if (isset($elseif) && count($elseif)) // elseif mode 853 | { 854 | $label_endif_name = $scrambler->scramble($scrambler->generate_label_name()); 855 | $label_endif = array(new PhpParser\Node\Stmt\Label($label_endif_name)); 856 | $goto_endif = array(new PhpParser\Node\Stmt\Goto_($label_endif_name)); 857 | 858 | $new_nodes_1 = array(); 859 | $new_nodes_2 = array(); 860 | $label_if_name = $scrambler->scramble($scrambler->generate_label_name()); 861 | $label_if = array(new PhpParser\Node\Stmt\Label($label_if_name)); 862 | $goto_if = array(new PhpParser\Node\Stmt\Goto_($label_if_name)); 863 | $if = new PhpParser\Node\Stmt\If_($condition); 864 | $if->stmts = $goto_if; 865 | $new_nodes_1 = array_merge($new_nodes_1,array($if)); 866 | $new_nodes_2 = array_merge($new_nodes_2,$label_if,$stmts,$goto_endif); 867 | 868 | for($i=0;$icond; 871 | $stmts = $elseif[$i]->stmts; 872 | $label_if_name = $scrambler->scramble($scrambler->generate_label_name()); 873 | $label_if = array(new PhpParser\Node\Stmt\Label($label_if_name)); 874 | $goto_if = array(new PhpParser\Node\Stmt\Goto_($label_if_name)); 875 | $if = new PhpParser\Node\Stmt\If_($condition); 876 | $if->stmts = $goto_if; 877 | $new_nodes_1 = array_merge($new_nodes_1,array($if)); 878 | $new_nodes_2 = array_merge($new_nodes_2,$label_if,$stmts); 879 | if ($iscramble($scrambler->generate_label_name()); 897 | $label_then = array(new PhpParser\Node\Stmt\Label($label_then_name)); 898 | $goto_then = array(new PhpParser\Node\Stmt\Goto_($label_then_name)); 899 | $label_endif_name = $scrambler->scramble($scrambler->generate_label_name()); 900 | $label_endif = array(new PhpParser\Node\Stmt\Label($label_endif_name)); 901 | $goto_endif = array(new PhpParser\Node\Stmt\Goto_($label_endif_name)); 902 | $node->stmts = $goto_then; 903 | $node->{'else'} = null; 904 | return array_merge(array($node),$else,$goto_endif,$label_then,$stmts,$label_endif); 905 | } 906 | else // no else statement found 907 | { 908 | if ($condition instanceof PhpParser\Node\Expr\BooleanNot) // avoid !! in generated code 909 | { 910 | $new_condition = $condition->expr; 911 | } 912 | else 913 | { 914 | $new_condition = new PhpParser\Node\Expr\BooleanNot($condition); 915 | } 916 | $label_endif_name = $scrambler->scramble($scrambler->generate_label_name()); 917 | $label_endif = array(new PhpParser\Node\Stmt\Label($label_endif_name)); 918 | $goto_endif = array(new PhpParser\Node\Stmt\Goto_($label_endif_name)); 919 | $node->cond = $new_condition; 920 | $node->stmts = $goto_endif; 921 | return array_merge(array($node),$stmts,$label_endif); 922 | } 923 | } 924 | } 925 | } 926 | 927 | if ($conf->obfuscate_loop_statement) // for while do while are replaced by goto ... 928 | { 929 | $scrambler = $t_scrambler['label']; 930 | if ($node instanceof PhpParser\Node\Stmt\For_) 931 | { 932 | list($label_loop_break_name,$label_loop_continue_name) = array_pop($this->t_loop_stack); 933 | 934 | //$init = $node->init; 935 | $init = null; 936 | if ((isset($node->init) && count($node->init))) foreach($node->init as $tmp) $init[] = new PhpParser\Node\Stmt\Expression($tmp); 937 | 938 | $condition = (isset($node->cond) && count($node->cond)) ? $node->cond[0] : null; 939 | 940 | //$loop = $node->loop; 941 | $loop = null; 942 | if ((isset($node->loop) && count($node->loop))) foreach($node->loop as $tmp) $loop[] = new PhpParser\Node\Stmt\Expression($tmp); 943 | 944 | $stmts = $node->stmts; 945 | $label_loop_name = $scrambler->scramble($scrambler->generate_label_name()); 946 | $label_loop = array(new PhpParser\Node\Stmt\Label($label_loop_name)); 947 | $goto_loop = array(new PhpParser\Node\Stmt\Goto_($label_loop_name)); 948 | $label_break = array(new PhpParser\Node\Stmt\Label($label_loop_break_name)); 949 | $goto_break = array(new PhpParser\Node\Stmt\Goto_($label_loop_break_name)); 950 | $label_continue = array(new PhpParser\Node\Stmt\Label($label_loop_continue_name)); 951 | $goto_continue = array(new PhpParser\Node\Stmt\Goto_($label_loop_continue_name)); 952 | 953 | $new_node = array(); 954 | if (isset($init)) 955 | { 956 | $new_node = array_merge($new_node,$init); 957 | } 958 | $new_node = array_merge($new_node,$label_loop); 959 | if (isset($condition)) 960 | { 961 | if ($condition instanceof PhpParser\Node\Expr\BooleanNot) // avoid !! in generated code 962 | { 963 | $new_condition = $condition->expr; 964 | } 965 | else 966 | { 967 | $new_condition = new PhpParser\Node\Expr\BooleanNot($condition); 968 | } 969 | $if = new PhpParser\Node\Stmt\If_($new_condition); 970 | $if->stmts = $goto_break; 971 | $new_node = array_merge($new_node,array($if)); 972 | } 973 | if (isset($stmts)) 974 | { 975 | $new_node = array_merge($new_node,$stmts); 976 | } 977 | $new_node = array_merge($new_node,$label_continue); 978 | if (isset($loop)) 979 | { 980 | $new_node = array_merge($new_node,$loop); 981 | } 982 | $new_node = array_merge($new_node,$goto_loop); 983 | $new_node = array_merge($new_node,$label_break); 984 | return $new_node; 985 | } 986 | 987 | if ( $node instanceof PhpParser\Node\Stmt\Foreach_) 988 | { 989 | list($label_loop_break_name,$label_loop_continue_name) = array_pop($this->t_loop_stack); 990 | 991 | $label_break = array(new PhpParser\Node\Stmt\Label($label_loop_break_name)); 992 | $node->stmts[] = new PhpParser\Node\Stmt\Label($label_loop_continue_name); 993 | $this->shuffle_stmts($node); 994 | return array_merge(array($node),$label_break); 995 | } 996 | 997 | if ( $node instanceof PhpParser\Node\Stmt\Switch_) 998 | { 999 | list($label_loop_break_name,$label_loop_continue_name) = array_pop($this->t_loop_stack); 1000 | 1001 | $label_break = array(new PhpParser\Node\Stmt\Label($label_loop_break_name)); 1002 | $label_continue = array(new PhpParser\Node\Stmt\Label($label_loop_continue_name)); 1003 | return array_merge(array($node),$label_continue,$label_break); 1004 | } 1005 | 1006 | if ( $node instanceof PhpParser\Node\Stmt\While_) 1007 | { 1008 | list($label_loop_break_name,$label_loop_continue_name) = array_pop($this->t_loop_stack); 1009 | 1010 | $condition = $node->cond; 1011 | $stmts = $node->stmts; 1012 | $label_break = array(new PhpParser\Node\Stmt\Label($label_loop_break_name)); 1013 | $goto_break = array(new PhpParser\Node\Stmt\Goto_($label_loop_break_name)); 1014 | $label_continue = array(new PhpParser\Node\Stmt\Label($label_loop_continue_name)); 1015 | $goto_continue = array(new PhpParser\Node\Stmt\Goto_($label_loop_continue_name)); 1016 | if ($condition instanceof PhpParser\Node\Expr\BooleanNot) // avoid !! in generated code 1017 | { 1018 | $new_condition = $condition->expr; 1019 | } 1020 | else 1021 | { 1022 | $new_condition = new PhpParser\Node\Expr\BooleanNot($condition); 1023 | } 1024 | $if = new PhpParser\Node\Stmt\If_($new_condition); 1025 | $if->stmts = $goto_break; 1026 | return array_merge($label_continue,array($if),$stmts,$goto_continue,$label_break); 1027 | } 1028 | 1029 | if ( $node instanceof PhpParser\Node\Stmt\Do_) 1030 | { 1031 | list($label_loop_break_name,$label_loop_continue_name) = array_pop($this->t_loop_stack); 1032 | 1033 | $condition = $node->cond; 1034 | $stmts = $node->stmts; 1035 | $label_break = array(new PhpParser\Node\Stmt\Label($label_loop_break_name)); 1036 | $label_continue = array(new PhpParser\Node\Stmt\Label($label_loop_continue_name)); 1037 | $goto_continue = array(new PhpParser\Node\Stmt\Goto_($label_loop_continue_name)); 1038 | $if = new PhpParser\Node\Stmt\If_($condition); 1039 | $if->stmts = $goto_continue; 1040 | return array_merge($label_continue,$stmts,array($if),$label_break); 1041 | } 1042 | 1043 | if ($node instanceof PhpParser\Node\Stmt\Break_) 1044 | { 1045 | $n = 1; 1046 | if (isset($node->num)) 1047 | { 1048 | if ($node->num instanceof PhpParser\Node\Scalar\LNumber) 1049 | { 1050 | $n = $node->num->value; 1051 | } 1052 | else 1053 | { 1054 | throw new Exception("Error: your use of break statement is not compatible with yakpro-po!".PHP_EOL."\tAt max 1 literal numeric parameter is allowed..."); 1055 | } 1056 | } 1057 | if (count($this->t_loop_stack) - $n <0) 1058 | { 1059 | throw new Exception("Error: break statement outside loop found!;".PHP_EOL.(($debug_mode==2) ? print_r($node,true) : '') ); 1060 | } 1061 | list($label_loop_break_name,$label_loop_continue_name) = $this->t_loop_stack[count($this->t_loop_stack) - $n ]; 1062 | $node = new PhpParser\Node\Stmt\Goto_($label_loop_break_name); 1063 | $node_modified = true; 1064 | } 1065 | if ($node instanceof PhpParser\Node\Stmt\Continue_) 1066 | { 1067 | $n = 1; 1068 | if (isset($node->num)) 1069 | { 1070 | if ($node->num instanceof PhpParser\Node\Scalar\LNumber) 1071 | { 1072 | $n = $node->num->value; 1073 | } 1074 | else 1075 | { 1076 | throw new Exception("Error: your use of continue statement is not compatible with yakpro-po!".PHP_EOL."\tAt max 1 literal numeric parameter is allowed..."); 1077 | } 1078 | } 1079 | if (count($this->t_loop_stack) - $n <0) 1080 | { 1081 | throw new Exception("Error: continue statement outside loop found!;".PHP_EOL.(($debug_mode==2) ? print_r($node,true) : '')); 1082 | } 1083 | list($label_loop_break_name,$label_loop_continue_name) = $this->t_loop_stack[count($this->t_loop_stack) - $n ]; 1084 | $node = new PhpParser\Node\Stmt\Goto_($label_loop_continue_name); 1085 | $node_modified = true; 1086 | } 1087 | } 1088 | 1089 | if ($conf->shuffle_stmts) 1090 | { 1091 | if ( ($node instanceof PhpParser\Node\Stmt\Function_) 1092 | || ($node instanceof PhpParser\Node\Expr\Closure) 1093 | || ($node instanceof PhpParser\Node\Stmt\ClassMethod) 1094 | || ($node instanceof PhpParser\Node\Stmt\Foreach_) // occurs when $conf->obfuscate_loop_statement is set to false 1095 | || ($node instanceof PhpParser\Node\Stmt\If_) // occurs when $conf->obfuscate_loop_statement is set to false 1096 | || ($node instanceof PhpParser\Node\Stmt\TryCatch) 1097 | || ($node instanceof PhpParser\Node\Stmt\Catch_) 1098 | || ($node instanceof PhpParser\Node\Stmt\Case_) 1099 | //|| ($node instanceof PhpParser\Node\Stmt\Namespace_) 1100 | ) 1101 | { 1102 | if ($this->shuffle_stmts($node)) $node_modified = true; 1103 | } 1104 | 1105 | if ( ($node instanceof PhpParser\Node\Stmt\If_) ) // occurs when $conf->obfuscate_if_statement is set to false 1106 | { 1107 | if (isset($node->{'else'})) 1108 | { 1109 | if ($this->shuffle_stmts($node->{'else'})) $node_modified = true; 1110 | } 1111 | 1112 | $elseif = $node->elseifs; 1113 | if (isset($elseif) && count($elseif)) // elseif mode 1114 | { 1115 | for($i=0;$ishuffle_stmts($elseif[$i])) $node_modified = true; 1118 | } 1119 | } 1120 | } 1121 | } 1122 | array_pop($this->t_node_stack); 1123 | if ($node_modified) return $node; 1124 | } 1125 | } 1126 | 1127 | ?> 1128 | --------------------------------------------------------------------------------