/** * PHP Internal functions * @string $int_functions is buildin function to get all php core functions * @global $this->_classes included all classes in files * @global $this->_fullclasses is @array included each class and all functions related to it * @global $this->FuncArray is @array included all functions and class if requested * @global $this->ConstArray is @array included all constants definded in file * @global $this->VarArray is @array included all vars definded in file * @return void */ function PHPFetchContent($contents, $analyze = false, $file = false) { //$contents = file_get_contents('dbconnector.php'); $tokens = new TokenStream($contents); $int_functions = get_defined_functions(); $this->fetchExcluded($file); $i = 0; //echo '<pre>'; //print_r($tokens->debugDump()); //echo '</pre>'; while ($i = $tokens->skipWhitespace($i)) { if ($tokens[$i]->is(T_CLASS)) { //extract class and assign it to class object $this->class = $tokens[$i + 2]->content; if (input::get('ReplaceClasses')) { //extract class and assign it to class object $this->class = $tokens[$i + 2]->content; if (!in_array($this->class, $this->_classes)) { $this->_classes[] = $this->class; } //obfuscate the class object with prefix "C" $class_obfuc = "C" . substr(md5($this->class), 0, 8); //generate classes and encoded if (!in_array($this->class, $this->ClassArray)) { //if process is not practical analyze, obfuscate it //if(!$analyze) $this->ClassArray[$this->class] = $class_obfuc; } //generate class name and replace it $tokens[$i + 2]->content = $class_obfuc; } //if(!isset($this->_fullclasses['classes'])) // $this->_fullclasses['classes']['NONE_OOP'] = 'on'; } elseif ($tokens->findEOS($i, false) && $tokens[$i]->is(T_PUBLIC, T_PRIVATE, T_PROTECTED, T_VAR, T_STATIC)) { /** * check if after the T_* is not 'static' object * check if after the T_* is not 'function' object * private static $_singleton; */ if (!$tokens[$i + 2]->is(T_STATIC) && !$tokens[$i + 2]->is(T_FUNCTION)) { $defineded_vars = $tokens[$i + 2]->content; //so ? assign the existent vars to check it later! if (!in_array($defineded_vars, $this->existent_vars)) { $this->existent_vars[substr($tokens[$i + 2]->content, 1)] = $defineded_vars; } } //echo '<pre>'; //print_r($this->existent_vars); //echo '</pre>'; } elseif ($tokens[$i]->is(T_NEW)) { /** * search where class is used and replace it * check if class in our classes * fixme: fix the object it added to class */ if ($tokens[$i + 2]->is(T_STRING) and in_array($tokens[$i + 2]->content, array_flip($this->ClassArray))) { $set_in = array_search($tokens[$i + 2]->content, $this->_fullclasses['classes']); $tokens[$i + 2]->content = $this->ClassArray[$tokens[$i + 2]->content]; //if(isset($this->_fullclasses['classes'][$set_in])) //$this->_fullclasses['classes'][$set_in] = $tokens[$i-4]->content; } } elseif ($tokens[$i]->is(T_FUNCTION)) { if (input::get('ReplaceFunctions')) { //extract function and assign it to function object $this->function = $tokens[$i + 2]->content; //check if the function not in exclude list if (!in_array($this->function, $this->UdExcFuncArray)) { //check if the function object not defined in functions list if (!isset($this->FuncArray[$this->function])) { /** * Array ( [MAP_SETTINGS] => F8018a28e [getAutocomplete] => F48b9a789 ) * generate function name and push to FuncArray **/ $this->FuncArray[$this->function] = "F" . substr(md5($this->function), 0, 8); } // it's Absolutely an function ! if ($tokens[$i + 3]->content == '(') { // generate functions tree under the main class if ($this->function and $this->class) { //check if the function not defined if (!isset($this->_fullclasses['classes'][$this->class]['functions'][$this->function])) { //assign it to class $this->_fullclasses['classes'][$this->class]['functions'][$this->function] = $this->function; } } //obfuscate the function object with prefix "F" $tokens[$i + 2]->content = "F" . substr(md5($this->function), 0, 8); } } //extends inside functions for ... //$extends = $tokens->find($i,T_VARIABLE); //Debugbar::info($tokens[$extends+2]->content); /** * extends search if the function is used inside the current function and replace it * @example * $this->function * fixme: need to check if it real function not sample var if($tokens[$extends+2]->is(T_STRING) and in_array($tokens[$extends+2]->content, array_flip($this->FuncArray)) ){ ## set the function name $this->function = $tokens[$extends+2]->content; if(isset($this->_fullclasses['classes'][$this->class]['functions'][$this->function])){ $tokens[$extends+2]->content = $this->FuncArray[$this->function]; }else $tokens[$extends+2]->content = $this->FuncArray[$this->function]; } */ } } elseif ($tokens[$i]->is(T_VARIABLE)) { if (input::get('ReplaceVariables')) { //clear $ from var $VarName = substr($tokens[$i]->content, 1); /** * extract the #variable and obfuscate it * Absolutely skip $this object * check again in exclude variables array list * TODO: Check if there duplicated vars and it is an objects * $tokens[$i+1]->type != T_OBJECT_OPERATOR '->' */ if ($tokens[$i]->content != '$this' && !in_array($VarName, $this->UdExcVarArray)) { //check if the var no defineded in variables array list //if process is not practical analyze, obfuscate it if (!isset($this->VarArray[$VarName])) { //obfuscate the variable string with prefix "V" $this->VarArray[$VarName] = 'V' . substr(md5(uniqid($VarName, true)), 0, mt_rand(5, 12)); } $tokens[$i]->content = "\${$this->VarArray[$VarName]}"; //fixme: #exclude the vars in existent_vars from vars $this->_fullclasses['classes'][$this->class]['vars'][$VarName] = $VarName; //$tokens[$i]->content = '${\''.$this->encode_string($this->VarArray[$VarName]).'\'}'; } /** * Search for objects and push founded to objects array */ if ($tokens[$i]->content != '$this' && $tokens[$i + 1]->type == T_OBJECT_OPERATOR) { if (!empty($VarName) && !in_array($VarName, $this->ObjectArray)) { array_push($this->ObjectArray, $VarName); } } /** * search for vars after $this need to replace * $var or function ? * $this->object */ if ($tokens[$i]->content == '$this' && $tokens[$i + 1]->type == T_OBJECT_OPERATOR) { if ($tokens[$i + 2]->is(T_STRING)) { //check if the string after $this in existent vars array list if (array_key_exists($tokens[$i + 2]->content, $this->existent_vars)) { //make sure it's not an function have a duplicate var name if ($tokens[$i + 3]->content != '(') { $this->_fullclasses['classes'][$this->class]['existent_vars'][$tokens[$i + 2]->content] = $tokens[$i + 2]->content; //if process is not practical analyze, obfuscate it //if(!$analyze) if (!in_array($tokens[$i + 2]->content, $this->UdExcVarArray)) { $tokens[$i + 2]->content = $this->VarArray[$tokens[$i + 2]->content]; } } } //so ? maybe it's a function okay check it if ($tokens[$i + 3]->content == '(') { if (in_array($tokens[$i + 2]->content, array_flip($this->FuncArray))) { //hmmm okay f**k it too! //if process is not practical analyze, obfuscate it //if(!$analyze) $tokens[$i + 2]->content = $this->FuncArray[$tokens[$i + 2]->content]; } } } } } } elseif ($tokens[$i]->is(T_CONSTANT_ENCAPSED_STRING)) { //standard PHP ENV variables that should be replaced //if(in_array(substr($tokens[$i-2]->content, 1),$this->StdExcVarArray)){ //check if the this string start with single quote //if (preg_match('/^\'(.*)\'$/', $tokens[$i]->content)) { if (preg_match('/\'.*(.*)\'/Uis', $tokens[$i]->content) && input::get('ReplaceEncode')) { $clean_quote = trim($tokens[$i]->content, "'"); //endcode it and add double quote $tokens[$i]->content = MagicalHelpers::random_encode("\"{$clean_quote}\""); } //}else{ //if (!preg_match('/^\'(.*)\'$/', $tokens[$i]->content)) // $tokens[$i]->content = MagicalHelpers::random_encode($tokens[$i]->content); //} } elseif ($tokens[$i]->is(T_STRING)) { /** * wide check .. * static method CLASS::FUNCTION() * OOP method CLASS->FUNCTION() */ if ($tokens[$i - 1]->is(T_DOUBLE_COLON) || $tokens[$i - 1]->is(T_OBJECT_OPERATOR)) { //check if it's an function if ($tokens[$i + 1]->content == '(') { //search if it definded in functions array list if (in_array($tokens[$i]->content, array_flip($this->FuncArray))) { $class = $tokens[$i - 2]->content; $function = $tokens[$i]->content; //check if this class exists in class array list if (in_array($class, $this->_fullclasses['classes'])) { $found = false; //deep check array_walk_recursive($this->_fullclasses['classes'], function ($v, $k) use(&$found) { $found |= $k == $this->class; }); if ($found) { ##TODO: add check if class obfuse is required if (in_array($class, $this->_fullclasses['classes'])) { //if process is not practical analyze, obfuscate it if (isset($this->ClassArray[$class])) { $tokens[$i - 2]->content = $this->ClassArray[$class]; } } } } if (isset($function, $this->_fullclasses['classes']["{$class}"])) { //check if this function assigned into this class if (in_array($function, $this->_fullclasses['classes']["{$class}"]['functions'])) { //if process is not practical analyze, obfuscate it //if(!$analyze) $tokens[$i]->content = $this->FuncArray[$tokens[$i]->content]; } } } } } /** * search where the function is used and replace it * support none OOP, inline function */ if ($tokens[$i - 1]->type != T_OBJECT_OPERATOR && $tokens[$i - 1]->type != T_DOUBLE_COLON) { $string_name = $tokens[$i]->content; if (!in_array($string_name, $int_functions['internal']) and in_array($string_name, array_flip($this->FuncArray))) { $this->_fullclasses['classes']['NONE_OOP']['functions'][$string_name] = $string_name; //if process is not practical analyze, obfuscate it //if(!$analyze) $tokens[$i]->content = $this->FuncArray[$string_name]; } } } } $contents = $tokens; if (Input::has('Analyze')) { //echo '<pre>'; //print_r(array_merge_recursive($this->_fullclasses,array('classes_exists' => $this->_classes))); //echo '</pre>'; if (!empty($this->_classes)) { return array_merge_recursive($this->_fullclasses, array('classes_exists' => $this->_classes)); } else { return $this->_fullclasses; } } else { return $contents; } }
test($tokenStream->find(0, T_EXIT), 2, 'find token'); test($tokenStream->find(0, array(T_CLOSE_ROUND, T_OPEN_ROUND)), 3, 'find tokens'); test($tokenStream->find($max, T_CLOSE_TAG), false, 'find from last index'); test($tokenStream->findEOS(0), 6, 'find eos semicolon'); test($tokenStream->findEOS(6), 12, 'find eos tag'); test($tokenStream->find(0, T_OPEN_TAG), false, 'find from already matchind token'); test($tokenStream->find($max, T_EXIT, true), 2, 'find token backwards'); test($tokenStream->find(0, T_OPEN_TAG, true), false, 'find from first index backwards'); test($tokenStream->findEOS($max, true), 6, 'find eos semicolon'); // 0 123 4567890 $hacker = new TokenStream('<?php A(function(){;});'); test($hacker->findEOS(2), 10, 'find EOS skipping lambda'); test($hacker->findEOS(9, true), 0, 'find EOS skipping lambda backwards'); // 0 123 $hacker = new TokenStream('<?php {} '); test($hacker->findEOS(3, true), 2, 'find closing bracket as EOS backwards'); }); group('skip', function () use($tokenStream, $max) { test($tokenStream->skip(0, T_WHITESPACE), 2, 'skip token'); test($tokenStream->skip(0, array(T_WHITESPACE, T_EXIT)), 3, 'skip tokens'); test($tokenStream->skip($max, array(T_WHITESPACE, T_CONSTANT_ENCAPSED_STRING), true), 8, 'skip tokens backwards'); test($tokenStream->skipWhitespace(0), 2, 'skip whitespace'); }); // 0 12 34 5678901 234 56 789 $tokenStream = new TokenStream('<?php (hi,hi,(),((hi),hi)) (()'); group('complementaryBracket', function () use($tokenStream) { test($tokenStream->complementaryBracket(1), 16, 'find forward'); test($tokenStream->complementaryBracket(15), 9, 'find backwards'); testException(function () use($tokenStream) { $tokenStream->complementaryBracket(17); }, 'TokenException', 'brackets not matching');