2核1G3M服务器88一季度

腾讯云,阿里云百度云等 折扣价→点我←

基于"软件杀手"大牛首代dz魔方解密IMMWA模板开源cis_app克米文件混淆分析 discuz 插件

彪哥💯 一级用户组

基于"软件杀手"大牛首代dz魔方解密IMMWA模板开源cis_app克米文件混淆分析:
站长窝"软件杀手"原创,其他都是抄袭!!!

<?php

/**
* This script inject scripts into the encoded php files and dump source code when it
* is running.
*/

/**
* 此函数会输出n个不同的文件,每个文件可能只包括一个函数,一个类,或者只有全局代码。
* 如果输出的是函数和类,文件名中会包括函数和类的名字,如果是全局代码,文件名中会包括
* 'global'字样。
*/

/**
* 以下对IMMWA模板的开源适合3.12版本,其他版本如果有授权机制的更改,可以用最基本方法来还原所有加密代码,不过我由于时间有限,没有进行
* 更详细的整理。
*
* 开源cis_app:
* wget http://bbs.bangli.uk/im.php?mod=channel\&cid=1
* or
* wget http://bbs.bangli.us/im.php?mod=channel\&cid=1
* 可以发现调用了cis_app/touch/core/core.php中的immwa_mod函数,以及mod/d5cc4ce6.php,如果没有授权,
* mod/d5cc4ce6.php会显示"Access Denied"并且退出,如果有授权,mod/d5cc4ce6.php会正确执行。
*
* 关于cis_weixin插件里面的core.php里面的函数的开源:
* 因为cis_app里面会调用immwa_api,远程访问www.immwa.com/api进行授权验证,所以,我们可以直接取消这个授权验证,用任何可以编辑
* 二进制文件的编辑器打开cis_weixin/core.php文件, 找到function immwa_api($mid,$app,$type='temp') {, 直接在此函数开头添加
* 一个返回语句,禁止之行此函数即可,比如我修改的(参考我在站长窝分享的http://bbs.zhanzhangwo.com/t-26479-1-1.html当中的cis_weixin/core.php文件):
* function immwa_api($mid,$app,$type='temp'){return 1000;
*/

/**
src1idx start position to skip auth:

06313d56.php 320
0e2cafb1.php 299
1362207e.php 327
18bc7108.php 271 个人中心功能
1dc49c44.php 299
20f45683.php 844 not used yet, may be decoded bugged, cause there are global/env variable statement skipped.
21ec19d5.php 328 not used yet
2503875c.php no need to decode
49375678.php 333 not used yet
502be734.php 430 首页相关的东西
5ed8c361.php 271 not used yet
677b6c14.php 208 not used yet
684e4afb.php 271 朋友圈->右上角发布
69b3bf44.php 292 not used yet
6cad11f7.php 299
731691af.php 564 not used yet
7b8b1e4d.php 271
8c8db424.php 297
91f126c1.php 620
923794ff.php 339 not used yet, probably an empty file.
99830e77.php 353 not used yet
a13fe471.php 320 not used yet
aa909d0b.php 333
b046515c.php 271 not used yet
bd1c3198.php 325
cd237839.php 234
d5cc4ce6.php 271
db52b157.php 333 not used yet
dc2c63c7.php 299
eeeba9b4.php 373 个人中心->我的偏好
f826d362.php 352 新闻资讯
f86007ba.php 353

immwa的模板文件请求immwa api服务器进行授权的url地址如下:
http://www.immwa.com/api/immwa.php?siteid=baSaw3ae&app=cis_app&appkey=942c194365bd98a87b145b07d6eba5696e07b0c9&lid=1033&logtime=1487076196&mid=Pajepba8&siteurl=http%3A%2F%2Fbbs.bangli.uk%2F&method=get
其中siteid, app, appkey都是immwa作者给予的给予域名的授权信心。
*/

function inject_file($filename)
{
// Backup file if we have process it once
$origfile = $filename . '.bak';

// Check if we have already hacked the file
if (file_exists($origfile)) {
echo "File ${filename} already processed\n";
return;
}

// Backup original file
copy($filename, $origfile);

// Get original file content.
$encoded = file_get_contents($origfile);

// Begin to patch it

// 注意:对于IMMWA的模板,开源的时候不需要开源加密的函数或者类里面的函数,所以这里把对函数和类的修改给注释掉了。
// 对于其他模板,有可能需要修改函数和类,所以可以根据情况开启对下面对function/class的修改。
//
// 下面正则表达式的原理就是匹配函数或者全局的eval函数调用,因为所有加密的代码都是通过eval来执行的,所以eval执行
// 的内容,就是被加密的真正有用的源代码,我们可以通过调用fwrite或者echo等函数,把真正执行的源代码给打印出来,重新
// 整理成真正可用的源代码,就完成了开源。 通过这种方法,可以彻底开源所有的魔方加密的文件。
//
// 我开源IMMWA的模板,是基于以上方法,发现IMMWA模板的一个授权规律,进行了简单的修改原有的加密文件里面指向加密源代码
// 的位置指针,直接将指向被加密源代码的位置指针修改为跳过加密代码中的授权机制即可。 当然如果在这种情况下,如果出现错误,
// 比如函数未定义等,可以写一个外壳脚本,定义一些空壳函数, 然后再在这个外壳脚本中调用需要开源的文件进行执行。
// 下面是我开源IMMWA模板的一个简单方法步骤:
// 1. php ~/inject.php 06313d56.php <-- 用此脚本进行对所有需要开源文件的修改,修改之后会产生一个06313d56.php.bak文件,
// 这个.bak文件是一个没有修改的源文件的备份。
//
// 2. php ./06313d56.php <!-- 执行被修改的加密文件, 此时会在当前的执行目录下面产生一个de_gl_***.php的文件,这个
// de_gl_***.php的文件里面就包含正确的代码位置指针,用于跳过IMMWA的授权检测。
// 3. 使用任何编辑器来查看de_gl_***.php文件,当前例子中的文件名字为de_gl_06313d56.php,我们使用linux的less即可查看,比如下面是
// 产生de_gl_06313d56.php的文件内容:
// $<E3><DA><E5><92><BA>[++$<E3><DA><E5><DF><8B>]="\62";
// $<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>-1]=$<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>-1]==$<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>];
// $<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>]=!$<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>];
// if($<E3><DA><E5><92><BA>[$<E3><DA><E5><DF><8B>])$<E3><DA><E5><B6><FF>=0x000000DF;
// $<E3><DA><E5><B6><FF>=0x00000140;
// $<E3><DA><E5><B6><FF>=0x0000010F;

//
// 由于上面粘贴的内容有一些不可显示的字符(魔方加密的一个最大干扰就是把变量名变成非可读字符,
// 这个可以参考文件sample_function.php和sample_class.php,这两个文件就是把一个通过魔方加密的文件,直接替换变量名,然后加入换行,
// 还原成可读的文件,文件当中全局或者函数当中的那个很长的字符串乱码,就是被加密的真正源代码)
// 因为我知道了IMMWA是一个简单的授权检测机制,所以我们不需要读懂这个,不过这里我给出一个替换了变量名的上面源代码的可读版本:
//
// $filename[++$fileidx] = "\62";
// $filename[$fileidx-1] = $filename[$fileidx-1]==$filename[$fileidx];
// $filename[$fileidx] = !$filename[$fileidx];
// if($filename[$fileidx]) $srcidx = 0x000000DF;
// $srcidx = 0x00000140;
// $srcidx = 0x0000010f;
// 在上面的解密的代码中,0x000000DF就是如果授权错误,就跳到源代码的0xDF位置开始执行,这时候就会执行exit($filename[$fileidx])退出
// 但是在0x0000010F,就是真正的跳过授权之后,正常业务逻辑的源代码,所以对IMMWA的开源,就是找到这个源代码的位置,修改加密文件里面的
// srcidx指针,把指针的位置从0改为0x140(十进制320), 所以你参考我论坛发布的IMMWA手机模板当中的template/cis_app/touch/core/mod/06313d56.php
// 文件,搜索320这个数字,这个地方的那句($\343\332\345\266\377=320;),这个就是把srcidx直接跳到第320个字符的位置开始执行,从而
// 越过了授权机制。


// Functions: function func_name($optinal_args){......eval(substr($...,1));
// $1 $2 $3 $4
//$pattern = '/function ([a-zA-Z0-9_]+)\(([\$\'a-zA-Z0-9_,=]*)\)\{(.+)?(?=eval)eval\((substr\(\$[\w\W]{5},1\))\);/';
//$encoded = preg_replace($pattern, 'function $1($2){$fp = fopen("de_fun_$1_' . basename($filename) .'", \'w\'); $3; fwrite($fp, $4 . "\n"); eval ($4);', $encoded);

// Class: class class_name{public function func_name($optional_args){......eval(substr($...,1));
// $1 $2 $3 $4 $5
//$pattern = '/class ([a-zA-Z0-9_])\{public function ([a-zA-Z0-9_]+)\(([\'\$a-zA-Z0-9_,=]*)\)\{(.+)?(?=eval)eval\((substr\(\$[\w\W]{5},1\))\);/';
//$encoded = preg_replace($pattern, 'class $1 { public function $2($3){$fp = fopen("de_cls_$1_$2_' . basename($filename) . '", \'w\'); $4; fwrite($fp, $5, "\n"); eval ($5);', $encoded);

// Global variables
$encoded = '<?php $fpgl = fopen("de_gl_' . basename($filename) . '", \'w\'); ?>' . $encoded . 'fclose($fpgl);';
$pattern = '/}eval\((substr\(\$[\w\W.]{5},1\))\);/';
// We left a space between eval and '(' to avoid following preg_replace
// matches it again
// 注意: 有些源代码如果调用了eval执行,很可能看不到跳过授权之后的源代码位置,所以我默认情况下是让被开源的源代码不执行,所以注释掉的了下面这一行,
// 如果有些源代码解不出来,可以启用这一行,让eval执行。
//$encoded = preg_replace($pattern, '}fwrite($fpgl, $1 . "\n"); eval ($1);', $encoded, 1);
$encoded = preg_replace($pattern, '}fwrite($fpgl, $1 . "\n"); /*eval ($1);*/', $encoded, 1);


// Saving changes
file_put_contents($filename, $encoded);
}


echo "Injecting ${argv[1]} ...\n";
inject_file($argv[1]);

?>
-------------------------------------------------------------------------------------------------------
<?php
/**
* 克米文件混淆原理:
* 所有文件使用的同样的加密原理,每个加密文件都有两部分,第一部分,加密外壳,第二部分,加密的源码字符串。
* 两层代码的变量名都是使用了混淆方法,把变量名变成了不可读的ASCII以外的字符。加密的源码字符串,通过外层
* 的外壳程序先解密(解密算法就是外层程序的执行过程),然后调用eval执行真正的程序。所以我们要做的就是把
* 真正的加密源代码提取出来,通过我们手动构造的外壳程序进行解密,变量名还原等等。
*
* 外壳文件分为两种,一种是文件中没有class,只有全局变量和function,另一种是有全局变量,class和function。
*/


/**
* 克米外壳程序重写
* $filename, array(__FILE__) of original php file
* $encoded_src: 加密的真实程序字符串
* $src_type: global, function, class
* $function_name: 加密字符串属于的函数名字,用于重构被加密的函数
* $class_name: 加密字符串属于的类名,用于重构被加密的类
*/

define('IN_DISCUZ', '1');

function shell($filename, $encoded_src, $src_type, $function_name = null, $class_name = null)
{
if(empty($encoded_src))
return;

//$filename = array(__FILE__);

// Unknown
$var_14 = array(0);

$fileidx = $var_13 = $srcidx = 0;

$tmp = $plain_src = null;

// First 2 iter of 'global' src_type should be eval'ed
$iter = 0;

while(1)
{
while($srcidx >= 0)
{
$plain_src = $encoded_src[$srcidx++];

//echo "source1[" . ($srcidx-1) . "]: " . ord($encoded_src[$srcidx-1]) . "\n";
//echo "source1[" . ($srcidx) . "]: " . ord($encoded_src[$srcidx]) . "\n";
echo "switch: " . ord($plain_src ^ $encoded_src[$srcidx]) . "\n";

switch ($plain_src ^ $encoded_src[$srcidx++])
{
case '1':
echo " case '1':\n";
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]));
$srcidx += 2;
break;

case '2':
echo " case '2':\n";
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]) .
($plain_src ^ $encoded_src[$srcidx + 2]) .
($plain_src ^ $encoded_src[$srcidx + 3]));
$srcidx += 4;
break;

case '3':
echo " case '3':\n";
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]) .
($plain_src ^ $encoded_src[$srcidx + 2]) .
($plain_src ^ $encoded_src[$srcidx + 3]) .
($plain_src ^ $encoded_src[$srcidx + 4]) .
($plain_src ^ $encoded_src[$srcidx + 5]) .
($plain_src ^ $encoded_src[$srcidx + 6]) .
($plain_src ^ $encoded_src[$srcidx + 7]) .
($plain_src ^ $encoded_src[$srcidx + 8]) .
($plain_src ^ $encoded_src[$srcidx + 9]) );
$srcidx += 10;
break;

case 'a':
echo " case 'a':\n";

for ($i = 0; $i <= $fileidx; $i++)
echo " filename[${i}]: ${filename[$i]}\n";

echo " pop ${fileidx}: ${filename[$fileidx]}\n";
unset($filename[$fileidx--]);
continue 2;

case 'b':
echo " case 'b':\n";
$plain_src = $filename[$fileidx];
unset($filename[$fileidx]);
$filename[$fileidx] = $plain_src;
$plain_src = null;
continue 2;

case 'c':
echo " case 'c':\n";
$filename[++$fileidx] = null;
continue 2;

case 'd': // Decode environment variables or globals
echo " case 'd':\n";
if(is_scalar($filename[$fileidx-1]))
{
$plain_src = $filename[$fileidx-1];
unset($filename[$fileidx-1]);
$filename[$fileidx-1] = $plain_src[$filename[$fileidx]];
}
else
{
if(!is_array($filename[$fileidx-1]))
{
$filename[$fileidx-1] = array();
}

// For function:
// $filename[1] = '_G';
// $filename[2] = 'forum';
// $plain_src = &$_G['forum'];
$plain_src = &$filename[$fileidx-1][$filename[$fileidx]];
unset($filename[$fileidx-1]);
$filename[$fileidx-1] = &$plain_src;
unset($plain_src);
}
continue 2;

case 'e': // Environment variables or globals
echo " case 'e':\n";
echo " filename[" . $fileidx. "] = " . $filename[$fileidx] . "\n";

switch($filename[$fileidx])
{
case 'this':
$filename[$fileidx]=&$this;
break;
case 'GLOBALS':
$filename[$fileidx]=&$GLOBALS;
break;
case '_SERVER':
$filename[$fileidx]=&$_SERVER;
break;
case '_GET':
$filename[$fileidx]=&$_GET;
break;
case '_POST':
$filename[$fileidx]=&$_POST;
break;
case '_FILES':
$filename[$fileidx]=&$_FILES;
break;
case '_COOKIE':
$filename[$fileidx]=&$_COOKIE;
break;
case '_SESSION':
$filename[$fileidx]=&$_SESSION;
break;
case '_REQUEST':
$filename[$fileidx]=&$_REQUEST;
break;
case '_ENV':
$filename[$fileidx]=&$_ENV;
break;
default:
$filename[$fileidx]=&${$filename[$fileidx]};
}

echo " new filename[" . $fileidx. "] = " . $filename[$fileidx] . "\n";

continue 2;

case 'f':
echo " case 'f':\n";
$tmp = $plain_src^$encoded_src[$srcidx++];
if($tmp=='d')
{
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]));
$srcidx += 2;
}
else if($tmp=='q')
{
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]) .
($plain_src ^ $encoded_src[$srcidx + 2]) .
($plain_src ^ $encoded_src[$srcidx + 3]));
$srcidx += 4;
}
else if($tmp=='x')
{
$tmp = (int) (
($plain_src ^ $encoded_src[$srcidx]) .
($plain_src ^ $encoded_src[$srcidx + 1]) .
($plain_src ^ $encoded_src[$srcidx + 2]) .
($plain_src ^ $encoded_src[$srcidx + 3]) .
($plain_src ^ $encoded_src[$srcidx + 4]) .
($plain_src ^ $encoded_src[$srcidx + 5]) .
($plain_src ^ $encoded_src[$srcidx + 6]) .
($plain_src ^ $encoded_src[$srcidx + 7]) .
($plain_src ^ $encoded_src[$srcidx + 8]) .
($plain_src ^ $encoded_src[$srcidx + 9]) );
$srcidx += 10;
}
else
{
echo "Error 1\n";
break 2;
}

$filename[++$fileidx] = '';

echo " tmp = " . $tmp . "\n";


while(($tmp--) > 0)
{
$filename[$fileidx] .= $plain_src ^ $encoded_src[$srcidx++];
}

for ($i = 0; $i <= $fileidx; $i++) {
echo " filenname[${i}] = ${filename[$i]}\n";
}

continue 2;

default:
echo " default: " . ord($plain_src ^ $encoded_src[$srcidx-1]) . "\n";
break 2;
}

while(($tmp--) > 0)
{
$plain_src .= $plain_src[0] ^ $encoded_src[$srcidx++];
}

// I got you, disable execution and dump the code.
// Decoded: \101\143\143\145\163\163\40\104\145\156\151\145\144 : Access Denied
$readable_src = decode_variables(substr($plain_src, 1));
echo "Readable source (length: " . strlen($readable_src) ."): " . $readable_src . "\n";

// FIXME: Some file(e.g. *.inc.php does not fit these switch cases)
switch ($iter) {
case 0:
//$aecf9783bc[$aecf97dfa1-2]=$aecf9783bc[$aecf97dfa1-1]($aecf9783bc[$aecf97dfa1]);
//eval($filename[$fileidx - 2] = $filename[$fileidx - 1]($filename[$fileidx]););
$filename[$fileidx - 2] = $filename[$fileidx - 1]($filename[$fileidx]);
break;

case 1:
//$aecf9783bc[$aecf97dfa1]=!$aecf9783bc[$aecf97dfa1];
//eval("$filename[$fileidx] != $filename[$fileidx];");
// This actually does nothing, maybe is changed to boolean only.
$filename[$fileidx] != $filename[$fileidx];
break;

case 2:
//if($aecf9783bc[$aecf97dfa1])$aecf97cff8lBl
//if ($filename[$fileidx]) $srcidx
eval($readable_src);
break;

default:
eval($readable_src);

}
echo "filename[${fileidx}] in ${iter} instruction: ${filename[$fileidx]}\n";
$iter++;

//eval(substr($plain_src,1));
}

echo "LINE " . __LINE__ . ", srcidx = " . $srcidx . "\n";

if($srcidx == -1)
{
break;
}
else if($srcidx == -2)
{
$readable_src = decode_variables($var_14[$var_13-1]);
echo "Readable source error dump: " . $readable_src . "\n";
//eval($var_14[$var_13-1]);
$srcidx = $var_14[$var_13];
$var_13 -= 2;
}
else
{
exit('1. KIVIUQ VIRTUAL MACHINE ERROR : Access violation at address '.
($srcidx < 0 ? $srcidx : sprintf('%08X',$srcidx)) . "\n");
}
}
}

/**
* 从原始的加密文件当中通过字符串匹配,把真正的源代码字符串给提取出来
*
* 1. 全局变量和全局代码pattern:
* static $..... = null;if(empty($.....)){$.....='<需要提取的字串>';}$.....=array(__FILE__);
*
* 2. 函数pattern:
* function func_name(){[pattern1]
*
* 3. 类+函数pattern:
* class mobileplugin_name{public function func_name(){[pattern1]
*/
function extract_source_str($filename)
{
// 提取pattern1当中的字符串,需要减去匹配到的结尾的9个字符: ';}$.....
// pattern1不以'{'打头,一直匹配到'';}$'之前
//$pattern1 = '/(?<!\{)static \$[\w\W]{5} = null;if\(empty\(\$[\w\W]{5}\)\)\{\$[\w\W]{5}=\'(.*(?<!=array\(__FILE__\)))/';
$pattern1 = '/[^{]static \$[\w\W.]{5} = null;if\(empty\(\$[\w\W.]{5}\)\)\{\$[\w\W.]{5}=\'(.+?(?=\';\}\$))/';

//$pattern1 = '/[^{](static \$[\w\W]{5} = null;if\(empty\(\$[)/';

$content = file_get_contents($filename);

preg_match($pattern1, $content, $matches);

if (count($matches)) {
echo "Matched: " . bin2hex($matches[1]) . "\n";
shell(array($filename), $matches[1], 'global');
} else {
echo "Regexp doesn't match anything\n";
}
}

/**
* 遍历字符串,提取变量,以此替换变量名
* $plain_src: 已解密的源代码,但是变量名和字符串仍待解密
*/
function decode_variables($plain_src)
{
// string -> array
$decoded_ary = str_split($plain_src);

// New string
$new_src = null;

// Array length
$len = count($decoded_ary);

// Loop decoded_ary
for($i = 0; $i < $len; $i++) {
switch ($decoded_ary[$i]) {

// Decode variable name, variable name is 5 bytes fixed length
case '$':
$new_src .= '$';

$vname = bin2hex($decoded_ary[++$i]);
$vname .= bin2hex($decoded_ary[++$i]);
$vname .= bin2hex($decoded_ary[++$i]);
$vname .= bin2hex($decoded_ary[++$i]);
$vname .= bin2hex($decoded_ary[++$i]);

// Get readable name
$new_src .= get_readable_variable($vname);
break;

default:
//echo "non variable: ${decoded_ary[$i]}, " . ord($decoded_ary[$i]) ."\n";
$new_src .= $decoded_ary[$i];
}
}

return $new_src;
}

/**
* 建立一个加密变量->可读边变量的转换表,输入被加密的变量,返回可读变量,输入的变量会以
* 16进制的索引方式存储在变量索引表当中。每个被加密的变量都是5个字符长度。
*/
function get_readable_variable($encoded_var)
{
// TODO;
return $encoded_var;

//$decoded_var = '';
//return $decoded_var;
}

// Run the program with single source
echo "Processing file: " . $argv[1] . "...\n";
extract_source_str($argv[1]);

?>


站长窝论坛版权声明 1、本帖标题:基于"软件杀手"大牛首代dz魔方解密IMMWA模板开源cis_app克米文件混淆分析
2、论坛网址:站长窝论坛
3、站长窝论坛的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
4、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
5、站长窝论坛一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本帖由彪哥💯在站长窝论坛《程序综合区》版块原创发布, 转载请注明出处!
评论
最新回复 (66)
返回
发新帖