Подтвердить что ты не робот

PHP переименовывает все переменные внутри кода

Я хотел бы переименовать все переменные в файле в случайное имя.

Например:

$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";
}  

Это не выглядит так просто, поэтому любые предложения будут полезны.

4b9b3361

Ответ 1

Я бы использовал token_get_all для анализа документа и сопоставления зарегистрированной случайной строки замена всех интересных токенов.

Чтобы запутать все имена переменных, замените T_VARIABLE за один проход, игнорируя все суперглобалы.

Кроме того, для имен функций требуемой функции перебора замените все объявления T_FUNCTION в первом проходе. Затем требуется второй проход, чтобы заменить все вызовы T_STRING, потому что PHP позволяет вам использовать функцию до ее объявления.

В этом примере я сгенерировал все строчные буквы, чтобы избежать беспорядочных столкновений с именами функций, но вы можете, очевидно, использовать любые символы, которые хотите, и добавить дополнительную условную проверку для большей сложности. Просто помните, что они не могут начинаться с числа.

Я также зарегистрировал все внутренние имена функций get_defined_functions, чтобы защитить от крайне неожиданной возможности того, что случайно сгенерированная строка будет соответствуют одному из этих имен функций. Имейте в виду, что это не защитит от специальных расширений, установленных на машине, на которой запущен запутанный script, который отсутствует на сервере, запутывающем script. Шансы на это астрономические, но вы всегда можете увеличить длину случайно созданной строки, чтобы еще больше уменьшить эти коэффициенты.

<?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 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 a function identifier
    if(is_array($element) && $element[0] === T_STRING){
        // make sure it 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;
}

/**
 * /questions/7882/php-random-string-generator/56752#56752
 * 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";
}

Ответ 2

РЕДАКТИРОВАТЬ 4.12.2016 - см. ниже! (после первого ответа)

Я просто попытался найти решение, которое может обрабатывать оба случая: ваш данный случай и этот пример из Элиас Ван Оотегерм.

конечно, он должен быть улучшен, как указано в одном из моих комментариев, но он работает для вашего примера:

$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, будут покрыты...


РЕДАКТИРОВАТЬ 4.12.2016

из-за 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://stackoverflow.com/a/31107425/4233593
 * 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...

Возможно, вы также хотите различать глобальные переменные и "запрещенные переменные"...

Ответ 3

Ну, вы можете попробовать написать свое собственное, но количество странных вещей, с которыми вам приходится обращаться, скорее всего, сокрушит вас, и я полагаю, вы больше заинтересованы в использовании такого инструмента, а не в написании и поддержании его самостоятельно. (Там много сломанных PHP-обфускаторов там, где люди пытались это сделать).

Если вы хотите, чтобы он был надежным, у вас есть база на парсере, или ваш инструмент будет неправильно анализировать текст и обрабатывать его неправильно (это первая "странная вещь" ). Regexes просто не будет делать трюк.

Семантические проекты PHP Obfuscator (из моей компании), взятый из коробки, взял эту слегка измененную версию примера 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 script).

Да, он использует полный парсер PHP. Не Никич.

Ответ 4

Я закончил с этим простым кодом:

$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);
    }           
} 

Это работает для меня, потому что код прост. Я предполагаю, что это не будет работать во всех сценариях.

Ответ 5

Теперь я работаю, но все же могут быть некоторые уязвимости, поскольку 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;
}

Никакие из существующих ответов не помогают мне. Поэтому я хотел бы присудить награду всем, кто может помочь мне сделать код на 100% безопасным.