老规矩,先分析代码。代码的一开始就定义了一个_kstr2的函数,这个函数就是将传入的字符串,转换成明码字符串;从上图中也可以看出,代码中大量调用了这个函数,我们需要找出这些函数调用,并经过_kstr2函数运算后得出的明码替换在原来的位置就好差不多了;我们可以用正则匹配,执行_kstr2函数后,再替换到相应的位置;或者也可以用PHP-Parser遍历ast节点,找出相应节点,并替换成明码字符;正则替换比较容易,写个正则表达式,循环替换就ok,不过可能会有些意外情况,需要多调试几次;用 PHP-Parser 遍历ast节点就相对容易一些,下面给出部分代码:
class ConstGlobalNodeVisitor extends NodeVisitorAbstract{
public $constVars,$globalVars;
public function leaveNode(Node $node){
//删除_kstr2函数定义代码
if ($node instanceof PhpParser\Node\Stmt\If_
&& $node->cond instanceof PhpParser\Node\Expr\BooleanNot
&& $node->cond->expr instanceof PhpParser\Node\Expr\FuncCall
&& $node->cond->expr->name instanceof PhpParser\Node\Name
&& $node->cond->expr->name->parts[0] === 'function_exists'
&& $node->cond->expr->args[0] instanceof PhpParser\Node\Arg
&& $node->cond->expr->args[0]->value instanceof Node\Scalar\String_
&& $node->cond->expr->args[0]->byRef === false
&& $node->cond->expr->args[0]->unpack === false
&& $node->stmts[0] instanceof PhpParser\Node\Stmt\Expression
&& $node->stmts[1] instanceof PhpParser\Node\Stmt\Expression
&& $node->stmts[2] instanceof PhpParser\Node\Stmt\Expression
&& $node->stmts[3] instanceof PhpParser\Node\Stmt\If_
&& $node->stmts[4] instanceof PhpParser\Node\Stmt\Function_
) {
return NodeTraverser::REMOVE_NODE;
}
//替换_kstr2函数调用为正常代码
if ($node instanceof PhpParser\Node\Expr\FuncCall
&& $node->name instanceof PhpParser\Node\Name
&& $node->name->parts[0] === "_kstr2"
&& $node->args[0] instanceof PhpParser\Node\Arg
&& $node->args[0]->value instanceof PhpParser\Node\Scalar\String_
&& $node->args[0]->byRef === false
&& $node->args[0]->unpack === false){
return new Node\Scalar\String_(_kstr2($node->args[0]->value->value));
}
}
}
//其中_kstr2函数来自网络https://www.php.cn/php-ask-432462.html,有兴趣的朋友可以过去查看。
然后将上述节点遍历类加入Php-Parser的节点遍历中,代码如下:
$code = file_get_contents($file);
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
$nodeVisitor = new ConstGlobalNodeVisitor();
$traverser = new NodeTraverser();
$traverser->addVisitor($nodeVisitor);
$ast = $traverser->traverse($ast);
$prettyPrinter = new Standard;
$code = $prettyPrinter->prettyPrintFile($ast);
echo $code;
经过处理后的代码如下图:
可以看到还有一些GLOBALS变量和define常量,都可以加到节点遍历中,替换成相应的值,最后发现还有一些变量名混淆,替换成arg,替换后最终得到的解密文件部分截图如下:
仅记录一下解密过程,最终解密代码就不放出了,以免泛滥。这种加密比较简单,解起来也很顺利,没有什么梗。如果此文侵犯到了您的商业利益,请联系站长删除。
发表回复