PHP重命名代码中的所有变量

13

我想将文件中的所有变量重命名为随机名称。

例如:

$example = "some $string";
function ($variable2) {
    echo $variable2;
}
foreach ($variable3 as $key => $var3val) {
    echo $var3val . "somestring";
}

将会变成这样:

$frk43r = "some $string";
function ($izi34ee) {
    echo $izi34ee;
}
foreach ($erew7er as $iure7 => $er3k2) {
    echo $er3k2 . "some$string";
}  

这似乎不是一项容易的任务,因此任何建议都将有所帮助。


1
所以,你想使用 PHP 修改 PHP?不介意我问为什么吗?整个过程似乎不合逻辑。 - Epodax
2
如果你想可靠地更改变量名(即不改变代码的工作方式),你真的应该考虑“解析”代码:nikic(PHP源代码的贡献者)用php编写了一个PHP解析器,你可以在这里,github上获取它。 - Elias Van Ootegem
1
你可以使用正则表达式来识别所有变量名并将它们放入一个数组中。然后删除该数组中的重复项。最后,你可以对该数组中的每个唯一条目执行 str_replace(),用随机字符串替换... 编辑:当然,你需要解析 PHP 文件才能完成此任务,就像 Elias Van Ootegem 已经写过的那样。 - SaschaP
1
@SaschaP:你还需要更改右操作数(字符串常量'varname')为 $varname 的新变量名。当然,$var1 的值可能是由函数返回的字符串($var1 = $this->getPropertyName(); return $this->{$var1};),所以 getPropertyName 必须返回随机字符串... 这就是事情变得非常棘手的地方。 - Elias Van Ootegem
1
@SaschaP 和 OP:如果你能想出一个可靠地处理像这样的代码的解决方案(https://eval.in/427976),我会给你的答案颁发奖励,因为那将是相当令人印象深刻的 ;) - Elias Van Ootegem
显示剩余19条评论
5个回答

11
我会使用token_get_all来解析文档,并在所有有趣的标记上映射已注册的随机字符串替换。
为了混淆所有变量名,在一次传递中替换T_VARIABLE,忽略所有超级全局变量
此外,对于赏金所需的函数名称,首先替换所有T_FUNCTION声明。然后需要第二遍替换所有T_STRING调用,因为PHP允许您在声明函数之前使用函数。
对于这个示例,我生成了所有小写字母,以避免与函数名称大小写不敏感的冲突,但您可以使用任何字符并添加额外的条件检查以增加复杂性。只要记住它们不能以数字开头。
我还使用get_defined_functions注册了所有内部函数名称,以防止随机生成的字符串匹配其中一个函数名称的极端可能性。请注意,这不会保护针对安装在运行混淆脚本的机器上的特殊扩展程序,这些扩展程序不存在于服务器上混淆脚本。那种情况的几率极小,但您可以增加随机生成字符串的长度以进一步降低这些几率。
<?php

$tokens = token_get_all(file_get_contents('example.php'));

$globals = array(
    '$GLOBALS',
    '$_SERVER',
    '$_GET',
    '$_POST',
    '$_FILES',
    '$_COOKIE',
    '$_SESSION',
    '$_REQUEST',
    '$_ENV',
);

// prevent name clashes with randomly generated strings and native functions
$registry = get_defined_functions();
$registry = $registry['internal'];

// first pass to change all the variable names and function name declarations
foreach($tokens as $key => $element){
    // make sure it's an interesting token
    if(!is_array($element)){
        continue;
    }
    switch ($element[0]) {
        case T_FUNCTION:
            $prefix = '';
            // this jumps over the whitespace to get the function name
            $index = $key + 2;
            break;

        case T_VARIABLE:
            // ignore the superglobals
            if(in_array($element[1], $globals)){
                continue 2;
            }
            $prefix = '$';
            $index = $key;
            break;

        default:
            continue 2;
    }

    // check to see if we've already registered it
    if(!isset($registry[$tokens[$index][1]])){
        // make sure our random string hasn't already been generated
        // or just so crazily happens to be the same name as an internal function
        do {
            $replacement = $prefix.random_str(16);
        } while(in_array($replacement, $registry));

        // map the original and register the replacement
        $registry[$tokens[$index][1]] = $replacement;
    }

    // rename the variable
    $tokens[$index][1] = $registry[$tokens[$index][1]];
}

// second pass to rename all the function invocations
$tokens = array_map(function($element) use ($registry){
    // check to see if it's a function identifier
    if(is_array($element) && $element[0] === T_STRING){
        // make sure it's one of our registered function names
        if(isset($registry[$element[1]])){
            // rename the variable
            $element[1] = $registry[$element[1]];
        }
    }
    return $element;
},$tokens);

// dump the tokens back out to rebuild the page with obfuscated names
foreach($tokens as $token){
    echo $token[1] ?? $token;
}

/**
 * https://dev59.com/3W855IYBdhLWcg3waDiR#31107425
 * Generate a random string, using a cryptographically secure
 * pseudorandom number generator (random_int)
 *
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 *
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str($length, $keyspace = 'abcdefghijklmnopqrstuvwxyz')
{
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

考虑到这个example.php

<?php

$example = 'some $string';

if(isset($_POST['something'])){
  echo $_POST['something'];
}

function exampleFunction($variable2){
  echo $variable2;
}

exampleFunction($example);

$variable3 = array('example','another');

foreach($variable3 as $key => $var3val){
  echo $var3val."somestring";
}

产生以下输出:
<?php

$vsodjbobqokkaabv = 'some $string';

if(isset($_POST['something'])){
  echo $_POST['something'];
}

function gkfadicwputpvroj($zwnjrxupprkbudlr){
  echo $zwnjrxupprkbudlr;
}

gkfadicwputpvroj($vsodjbobqokkaabv);

$vfjzehtvmzzurxor = array('example','another');

foreach($vfjzehtvmzzurxor as $riuqtlravsenpspv => $mkdgtnpxaqziqkgo){
  echo $mkdgtnpxaqziqkgo."somestring";
}

非常感谢您的帮助。我肯定可以利用“token_get_all”来改进我已经拥有的代码,但是动态生成的函数名和变量名怎么办?它们能被“token_get_all”检测到吗?目前,这是一个巨大的安全风险。 - Dan Bray
1
@DanBray,您所说的动态是否指可变函数可变变量?如果是这样,那么我认为这不会像现在这样工作,但解析方法绝对比正则表达式更可取,因此该代码应该是可扩展的。但是就您的安全问题而言,我认为您也应该担心closures - Jeff Puckett
Puckett,是的,确切地说。出于安全考虑,我想拒绝包含“可变函数”和“可变变量”的任何代码。但是,“Closure”类应该已经不可用,因为我没有将“Closure”列入白名单。 - Dan Bray
@DanBray 嗯,我的意思是没有人会实例化 new Closure,所以你需要注意的是匿名函数 - Jeff Puckett

7

编辑于2016年4月12日 - 请参阅下面!(第一个答案后)

我刚刚尝试找到一种可以处理两种情况的解决方案:您提供的情况和Elias Van Ootegerm这个示例

当然,它应该像我在其中一个评论中提到的那样进行改进,但它适用于您的示例:

$source = file_get_contents("source.php");

// this should get all Variables BUT isn't right at the moment if a variable is followed by an ' or " !!
preg_match_all('/\$[\$a-zA-Z0-9\[\'.*\'\]]*/', $source, $matches);
$matches = array_unique($matches[0]);

// this array saves all old and new variable names to track all replacements
$replacements = array();
$obfuscated_source = $source;
foreach($matches as $varName)
{
    do // generates random string and tests if it already is used by an earlier replaced variable name
    {
        // generate a random string -> should be improved.
        $randomName = substr(md5(rand()), 0, 7);
        // ensure that first part of variable name is a character.
        // there could also be a random character...
        $randomName = "a" . $randomName;
    }
    while(in_array("$" . $randomName, $replacements));

    if(substr($varName, 0,8) == '$GLOBALS')
    {
        // this handles the case of GLOBALS variables
        $delimiter = substr($varName, 9, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$GLOBALS[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,8) == '$_SERVER')
    {
        // this handles the case of SERVER variables
        $delimiter = substr($varName, 9, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_SERVER[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,5) == '$_GET')
    {
        // this handles the case of GET variables
        $delimiter = substr($varName, 6, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_GET[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,6) == '$_POST')
    {
        // this handles the case of POST variables
        $delimiter = substr($varName, 7, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_POST[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,7) == '$_FILES')
    {
        // this handles the case of FILES variables
        $delimiter = substr($varName, 8, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_FILES[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,9) == '$_REQUEST')
    {
        // this handles the case of REQUEST variables
        $delimiter = substr($varName, 10, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_REQUEST[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,9) == '$_SESSION')
    {
        // this handles the case of SESSION variables
        $delimiter = substr($varName, 10, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_SESSION[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,5) == '$_ENV')
    {
        // this handles the case of ENV variables
        $delimiter = substr($varName, 6, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_ENV[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 0,8) == '$_COOKIE')
    {
        // this handles the case of COOKIE variables
        $delimiter = substr($varName, 9, 1);
        if($delimiter == '$') $delimiter = '';
        $newName = '$_COOKIE[' .$delimiter . $randomName . $delimiter . ']'; 
    }
    else if(substr($varName, 1, 1) == '$')
    {
        // this handles the case of variable variables
        $name = substr($varName, 2, strlen($varName)-2);
        $pattern = '/(?=\$)\$' . $name . '.*;/';
        preg_match_all($pattern, $source, $varDeclaration);
        $varDeclaration = $varDeclaration[0][0];

        preg_match('/\s*=\s*["\'](?:\\.|[^"\\]])*["\']/', $varDeclaration, $varContent);
        $varContent = $varContent[0];

        preg_match('/["\'](?:\\.|[^"\\]])*["\']/', $varContent, $varContentDetail);
        $varContentDetail = substr($varContentDetail[0], 1, strlen($varContentDetail[0])-2);

        $replacementDetail = str_replace($varContent, substr($replacements["$" . $varContentDetail], 1, strlen($replacements["$" . $varContentDetail])-1), $varContent);

        $explode = explode($varContentDetail, $varContent);
        $replacement = $explode[0] . $replacementDetail . $explode[1];
        $obfuscated_source = str_replace($varContent, $replacement, $obfuscated_source);
    }
    else
    {
        $newName = '$' . $randomName;   
    }

    $obfuscated_source = str_replace($varName, $newName, $obfuscated_source);

    $replacements[$varName] = $newName;
}

// this part may be useful to change hard-coded returns of functions.
// it changes all remaining words in the document which are like the previous changed variable names to the new variable names
// attention: if the variables in the document have common names it could also change text you don't like to change...
foreach($replacements as $before => $after)
{
    $name_before = str_replace("$", "", $before);
    $name_after = str_replace("$", "", $after);
    $obfuscated_source = str_replace($name_before, $name_after, $obfuscated_source);
}

// here you can place code to write back the obfuscated code to the same or to a new file, e.g:
$file = fopen("result.php", "w");
fwrite($file, $obfuscated_source);
fclose($file);

编辑:仍有一些情况需要一些努力。至少某些变量声明可能无法正确处理!

此外,第一个正则表达式并不完美,我的当前状态是: '/\$\$?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/' 但这不能得到预定义变量的索引值...但我认为它有一些潜力。如果您像这里一样使用它,您将获得所有18个涉及的变量...下一步可以确定变量名后是否跟随 [..]。如果是这样,任何预定义变量和这种情况,如 $g = $GLOBALS;,以及对这种 $g 的进一步使用都将被覆盖...


编辑 2016年4月12日

由于LSerni和原始问题以及一些解决方案的一些评论,我还编写了一个解析方案,您可以在下面找到它。它处理了一个扩展示例文件,这是我的目标。如果您发现其他挑战,请告诉我!

新解决方案:

 $variable_names_before = array();
 $variable_names_after  = array();
 $function_names_before = array();
 $function_names_after  = array();
 $forbidden_variables = array(
    '$GLOBALS',
    '$_SERVER',
    '$_GET',
    '$_POST',
    '$_FILES',
    '$_COOKIE',
    '$_SESSION',
    '$_REQUEST',
    '$_ENV',
 );
 $forbidden_functions = array(
     'unlink'
 );

 // read file
 $data = file_get_contents("example.php");

 $lock = false;
 $lock_quote = '';
 for($i = 0; $i < strlen($data); $i++)
 {
     // check if there are quotation marks
     if(($data[$i] == "'" || $data[$i] == '"'))
     {
         // if first quote
         if($lock_quote == '')
         {
             // remember quotation mark
             $lock_quote = $data[$i];
             $lock = true;
         }
         else if($data[$i] == $lock_quote)
         {
             $lock_quote = '';
             $lock = false;
         }
     }

     // detect variables
     if(!$lock && $data[$i] == '$')
     {
         $start = $i;
         // detect variable variable names
         if($data[$i+1] == '$')
         {
             $start++;
             // increment $i to avoid second detection of variable variable as "normal variable"
             $i++;
         }

         $end = 1;
         // find end of variable name
         while(ctype_alpha($data[$start+$end]) || is_numeric($data[$start+$end]) || $data[$start+$end] == "_")
         {
             $end++;
         }
         // extract variable name
         $variable_name = substr($data, $start, $end);
         if($variable_name == '$')
         {
             continue;
         }
         // check if variable name is allowed
         if(in_array($variable_name, $forbidden_variables))
         {
             // forbidden variable deteced, do whatever you want!
         }
         else
         {
             // check if variable name already has been detected
             if(!in_array($variable_name, $variable_names_before))
             {
                 $variable_names_before[] = $variable_name;
                 // generate random name for variable
                 $new_variable_name = "";
                 do
                 {
                     $new_variable_name = random_str(rand(5, 20));
                 }
                 while(in_array($new_variable_name, $variable_names_after));
                 $variable_names_after[] = $new_variable_name;
             }
             //var_dump("variable: " . $variable_name);
         }
     }

     // detect function-definitions
     // the third condition checks if the symbol before 'function' is neither a character nor a number
     if(!$lock && strtolower(substr($data, $i, 8)) == 'function' && (!ctype_alpha($data[$i-1]) && !is_numeric($data[$i-1])))
     {
         // find end of function name
         $end = strpos($data, '(', $i);
         // extract function name and remove possible spaces on the right side
         $function_name = rtrim(substr($data, ($i+9), $end-$i-9));
         // check if function name is allowed
         if(in_array($function_name, $forbidden_functions))
         {
             // forbidden function detected, do whatever you want!
         }
         else
         {
             // check if function name already has been deteced
             if(!in_array($function_name, $function_names_before))
             {
                 $function_names_before[] = $function_name;
                 // generate random name for variable
                 $new_function_name = "";
                 do
                 {
                     $new_function_name = random_str(rand(5, 20));
                 }
                 while(in_array($new_function_name, $function_names_after));
                 $function_names_after[] = $new_function_name;
             }
             //var_dump("function: " . $function_name);
         }
     }
 }

// this array contains prefixes and suffixes for string literals which
// may contain variable names.
// if string literals as a return of functions should not be changed
// remove the last two inner arrays of $possible_pre_suffixes
// this will enable correct handling of situations like
// - $func = 'getNewName'; echo $func();
// but it will break variable variable names like
// - ${getNewName()}
$possible_pre_suffixes = array(
    array(
        "prefix" => "= '",
        "suffix" => "'"
    ),
    array(
        "prefix" => '= "',
        "suffix" => '"'
    ),
    array(
        "prefix" => "='",
        "suffix" => "'"
    ),
    array(
        "prefix" => '="',
        "suffix" => '"'
    ),
    array(
        "prefix" => 'rn "', // return " ";
        "suffix" => '"'
    ),
    array(
        "prefix" => "rn '", // return ' ';
        "suffix" => "'"
    )
);
// replace variable names
for($i = 0; $i < count($variable_names_before); $i++)
{
    $data = str_replace($variable_names_before[$i], '$' . $variable_names_after[$i], $data);

    // try to find strings which equals variable names
    // this is an attempt to handle situations like:
    // $a = "123";
    // $b = "a";    <--
    // $$b = "321"; <--

    // and also
    // function getName() { return "a"; }
    // echo ${getName()};
    $name = substr($variable_names_before[$i], 1);
    for($j = 0; $j < count($possible_pre_suffixes); $j++)
    {
        $data = str_replace($possible_pre_suffixes[$j]["prefix"] . $name . $possible_pre_suffixes[$j]["suffix"],
                            $possible_pre_suffixes[$j]["prefix"] . $variable_names_after[$i] . $possible_pre_suffixes[$j]["suffix"],
                            $data);
    }
}
// replace funciton names
for($i = 0; $i < count($function_names_before); $i++)
{
    $data = str_replace($function_names_before[$i], $function_names_after[$i], $data);
}

/**
 * https://dev59.com/3W855IYBdhLWcg3waDiR#31107425
 * Generate a random string, using a cryptographically secure
 * pseudorandom number generator (random_int)
 *
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 *
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str($length, $keyspace = 'abcdefghijklmnopqrstuvwxyz')
{
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    for ($i = 0; $i < $length; ++$i)
    {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}

示例输入文件:

$example = 'some $string';
$test = '$abc 123' . $example . '$hello here I "$am"';

if(isset($_POST['something'])){
  echo $_POST['something'];
}

function exampleFunction($variable2){
  echo $variable2;
}

exampleFunction($example);

$variable3 = array('example','another');

foreach($variable3 as $key => $var3val){
  echo $var3val."somestring";
}

$test = "example";
$$test = 'hello';

exampleFunction($example);
exampleFunction($$test);

function getNewName()
{
    return "test";
}
exampleFunction(${getNewName()});

我的函数输出结果:

$fesvffyn = 'some $string';
$zimskk = '$abc 123' . $fesvffyn . '$hello here I "$am"';

if(isset($_POST['something'])){
  echo $_POST['something'];
}

function kainbtqpybl($yxjvlvmyfskwqcevo){
  echo $yxjvlvmyfskwqcevo;
}

kainbtqpybl($fesvffyn);

$lmiphctfgjfdnonjpia = array('example','another');

foreach($lmiphctfgjfdnonjpia as $qypdfcpcla => $gwlpcpnvnhbvbyflr){
  echo $gwlpcpnvnhbvbyflr."somestring";
}

$zimskk = "fesvffyn";
$$zimskk = 'hello';

kainbtqpybl($fesvffyn);
kainbtqpybl($$zimskk);

function tauevjkk()
{
    return "zimskk";
}
kainbtqpybl(${tauevjkk()});

我知道还有一些情况会出现变量变量名的问题,但这时你可能需要扩展$possible_pre_suffixes数组...

也许您还想区分全局变量和“禁止使用的变量”...


2
这个代码 substr(md5(rand()), 0, 7) 存在相当大的哈希冲突风险。 - Elias Van Ootegem
这部分只是尝试任何其他随机字符串生成器。 - SaschaP
不管怎么说,对你的努力给一点赞,但必须得说,你的方法在有人写了$g = $GLOBALS;并将$g用作超全局变量的缩写时就会彻底崩溃。你真的需要解析,而正则表达式无法做到这一点。 - Elias Van Ootegem
2个注意事项。你正在处理的那个(由于函数引起的未定义变量:foobar),以及末尾的var_dump - Elias Van Ootegem
@EliasVanOotegem 是的,我的方法确实远非完美!它只能作为 OP 的辅助。如果我在接下来的几天里有时间,我会尝试改进代码并考虑更多 PHP 代码编写风格。 - SaschaP
显示剩余14条评论

6
好的,你可以尝试编写自己的代码,但是你需要处理的奇怪问题可能会让你不知所措。我认为你更有兴趣使用这样的工具,而不是编写和维护一个工具(有很多破碎的PHP混淆器,人们已经试图做到这一点)。
如果你想要一个可靠的工具,你需要基于解析器构建它,否则你的工具将会错误地解析文本并处理它(这是第一个“奇怪的事情”)。简单的正则表达式并不能解决问题。
Semantic Designs PHP混淆器(来自我的公司),可以直接使用,它采用了Elias Van Ootegem的示例的略微修改版本。
 <?php

//non-obfuscated

function getVarname()
{//the return value has to change
return (('foobar'));
}

$format = '%s = %d';
$foobar = 123;

$variableVar = (('format'));//you need to change this string

printf($$variableVar, $variableVar = getVarname(), $$variableVar);

echo PHP_EOL;

var_dump($GLOBALS[(('foobar'))]);//note the key == the var

并且生成了如下结果:
<?php function l0() { return (('O0')); } $l1="%\163 = %d"; $O1=0173; $l2=(('O2')); printf($$l2,$l2=l0(),$$l2); echo PHP_EOL; var_dump($GLOBALS[(('O0'))]);

Elias的例子中关键问题在于字符串实际上包含变量名。通常情况下,工具无法知道"x"是一个变量名,而不仅仅是包含字母x的字符串。但是,程序员知道这一点。我们坚持将这些字符串标记出来(通过将它们括在((..))中),然后混淆器就可以正确地混淆它们的内容了。有时字符串包含变量名和其他内容;在这种情况下,程序员必须将字符串分解为“变量名”内容和其他所有内容。在实践中,这很容易做到,并且是我对他提供的代码所做的“轻微改变”。其他未标记的字符串则保持不变。您只需要对源文件进行一次操作。[您可以说这是作弊,但没有其他实用的答案会起作用;工具无法可靠地知道。停机问题,如果您坚持的话。]
接下来要做好的事情是在多个文件之间实现可靠的混淆。不能一次处理一个文件。这个混淆器已经被用于非常大的PHP应用程序(成千上万个PHP脚本文件)。
是的,它确实使用了完整的PHP解析器。不是nikic的。

点赞了很多非常好的观点,从“不要自己造轮子”到“停机问题”的内容。可惜我只能点赞一次。 - LSerni

2
我最终得到了这段简单的代码:
$tokens = token_get_all($src);
$skip = array('$this','$_GET','$_POST','$_REQUEST','$_SERVER','$_COOKIE','$_SESSION');
function renameVars($tokens,$content,$skip){
  $vars = array();
  foreach($tokens as $token) {
      if ($token[0] == T_VARIABLE && !in_array($token[1],$skip))
          $vars[generateRandomString()]= $token[1];
  }
  $vars = array_unique($vars);
  $vars2 = $vars;

  foreach($vars as $new => $old){
    foreach($vars2 as $var){
      if($old!=$var && strpos($var,$old)!==false){
        continue 2;
      }
    }  
    $content = str_replace($old,'${"'.$new.'"}',$content);
    //function(${"example"}) will trigger error. This is why we need this:
    $content = str_replace('(${"'.$new.'"}','($'.$new,$content);
    $content = str_replace(',${"'.$new.'"}',',$'.$new,$content);
    $chars = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
    //for things like function deleteExpired(Varien_Event_Observer $fz5eDWIt1si), Exception, 
    foreach($chars as $char){
      $content = str_replace($char.' ${"'.$new.'"}',$char.' $'.$new,$content);
    }           
} 

这段代码对我而言可行,因为它很简单。但我猜在某些情况下可能不适用。


0

我现在已经让它工作了,但是因为PHP允许动态生成函数名和变量名,所以可能仍然存在一些漏洞。

第一个函数用函数替换$_SESSION$_POST等:

function replaceArrayVariable($str, $arr, $function)
{
    $str = str_replace($arr, $function, $str);
    $lastPos = 0;

    while (($lastPos = strpos($str, $function, $lastPos)) !== false)
    {
        $lastPos = $lastPos + strlen($function);
        $currentPos = $lastPos;
        $openSqrBrackets = 1;
        while ($openSqrBrackets > 0)
        {
            if ($str[$currentPos] === '[')
                 $openSqrBrackets++;
            elseif ($str[$currentPos] === ']')
                 $openSqrBrackets--;
            $currentPos++;
        }
        $str[$currentPos - 1] = ')';
    }
    return $str;
}

第二个重命名函数,忽略白名单中的关键字:

function renameFunctions($str)
{
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $str, $matches, PREG_OFFSET_CAPTURE);
    $totalMatches = count($matches[0]);
    $offset = 0;
    for ($i = 0; $i < $totalMatches; $i++)
    {
        $matchIndex = $matches[0][$i][1] + $offset;
        if ($matchIndex === 0 || $str[$matchIndex - 1] !== '$')
        {
            $keyword = $matches[0][$i][0];
            if ($keyword !== 'true' && $keyword !== 'false' && $keyword !== 'if' && $keyword !== 'else' && $keyword !== 'getPost' && $keyword !== 'getSession')
            {
                $str = substr_replace($str, 'qq', $matchIndex, 0);
                $offset += 2;
            }
        }
    }
    return $str;
}

然后,要重命名函数、变量和非白名单关键字,我使用这段代码:

$str = replaceArrayVariable($str, '$_POST[', 'getPost(');
$str = replaceArrayVariable($str, '$_SESSION[', 'getSession(');
preg_match_all('/\'(?:\\\\.|[^\\\\\'])*\'|.[^\']+/', $str, $matches);
$str = '';
foreach ($matches[0] as $match)
{
    if ($match[0] != "'")
    {
        $match = preg_replace('!\s+!', ' ', $match);
        $match = renameFunctions($match);
        $match = str_replace('$', '$qq', $match);
    }
    $str .= $match;
}

PHP代码不需要加密或混淆以使其无法阅读。它只需要安全可靠地执行即可。我本应该在问题中解释这一点,但我最后一次编辑被拒绝了。我可以重命名命令。如果我将“goto”重命名为“qqgoto”,那么“goto”命令就已被禁用,客户端无法使用。例如,“unlink”命令可能会从服务器上删除所有内容,因此我将替换它为一个函数,检查客户端是否有权限删除文件。 - Dan Bray
我在答案中提供的代码运行良好,但我不确定修改它以使用“token_get_all”是否会使其更有效。我肯定需要防止动态生成的函数和变量,因为重要的是客户端无法访问框架之外的函数和变量。 - Dan Bray
我不明白你所说的将“goto”重命名为“qqgoto”的意思。如果你在代码中这样做,当PHP引擎在你的代码中遇到“qqgoto”语句时,它将拒绝你的代码,现在你的程序完全停止工作了。这似乎并不有用。如果你想从PHP引擎的功能中删除“goto”,你可以通过构建PHP的自定义实现来实现,但是没有人会同意将其放在他们的服务器上,因此这似乎并不有用。如果你想防止人们使用goto或动态生成的变量,你不需要混淆器... - Ira Baxter
2
如果你能强制客户在使用代码之前提交代码,那么你可以运行“样式检查器”来检查你不喜欢的结构。但这使得你对这个问题的奖金完全是错误的,因为这个问题是关于重命名变量,而不是样式检查。 - Ira Baxter
1
为了满足您的要求,您需要一个风格检查器。您真正的问题将是获得一个可以检查您想要的样式规则的检查器。您可以考虑从头开始使用TOKEN_GET_ALL(或者您对PHP说什么来获取文件的所有标记)来创建自己的检查器,但这条路会导致疯狂或至少很可能无法构建一个严肃的风格检查器。请参见http://www.semdesigns.com/Products/DMS/LifeAfterParsing.html。 - Ira Baxter
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接