前言#
异次元发卡支持第三方开发者开发插件来丰富网站功能,并且可以设置插件收费来获取收益。插件的限制主要是如果本机没有购买这个插件,就无法从商店安装。即使通过某种手段获取插件并且手动安装,也不能启用该插件。如果开发者自己开发的插件没有上架,也无法使用。
分析#
在 /kernel/kernel.php 里可以看到插件的初始化。先扫描插件,再调用初始化函数。
//插件库
if (\Kernel\Util\Context::get(\Kernel\Consts\Base::STORE_STATUS) && \Kernel\Util\Context::get(\Kernel\Consts\Base::IS_INSTALL)) {
require("Plugin.php");
//插件初始化
\Kernel\Util\Plugin::scan();
Initialize();
//插件初始化
hook(\App\Consts\Hook::KERNEL_INIT);
}
关键就在这个 Plugin.php 文件。
打开文件,发现文件被加密,那么一种思路是解密文件,然后修改相关检测代码。
踩坑#
这个文件采用的是 Z5 加密。它是由 Z-Blog 应用中心推出的 PHP 加密方案,也是 Z-Blog 应用中心唯一支持的加密方案。
解密难度比较大。
经过各种处理变成人类可识别的代码后,发现这是一个 php 写的虚拟机,真实代码转换成了虚拟机的代码然后在虚拟机中执行。要想获得原始代码,需要对虚拟机进行逆向,难度直接起飞。
思路#
在被解密代码困扰好几天后,今天午睡的时候灵光闪现,为什么不试试重构插件系统?
一开始感觉这个想法也不切实际,因为重构插件系统需要大量的工作。所以先从分析原有的插件系统开始。
经过分析发现,插件系统主要靠的是 hook 机制,通过注册钩子函数和触发钩子函数实现插件功能。
在 /kernel/Util/Plugin.php 下我找到了完整的注册钩子和触发钩子的函数,而且没有加密,这让我感到兴奋。
我一开始尝试删除 Initialize (); 这个函数,发现非法插件可以正常启用,但是无法被钩子触发。
再继续分析 hook 函数
public static function hook(int $point, mixed &...$args)
{
if (Context::get(\Kernel\Consts\Base::STORE_STATUS) && \Kernel\Util\Context::get(\Kernel\Consts\Base::IS_INSTALL)) {
$list = _Point($point);
foreach ($list as $item) {
$instance = _Instance($item);
$ref = new \ReflectionClass($instance);
$reflectionProperties = $ref->getProperties();
foreach ($reflectionProperties as $property) {
$reflectionProperty = new \ReflectionProperty($instance, $property->getName());
$reflectionPropertiesAttributes = $reflectionProperty->getAttributes();
foreach ($reflectionPropertiesAttributes as $reflectionAttribute) {
$ins = $reflectionAttribute->newInstance();
if ($ins instanceof \Kernel\Annotation\Inject) {
di($instance);
}
}
}
$result = call_user_func_array([$instance, $item['method']], $args);
if ($result) {
return $result;
}
}
}
}
猛然发现通过 key 值获取钩子函数的函数_Point ($point) 是在之前那个加密文件里定义的,直接 var_dump 获取返回值,然后自己按格式修改就可以绕过。
破解#
修改 /kernel/Util/Plugin.php 下的 hook 函数为如下所示,既可启用非法插件。
public static function hook(int $point, mixed &...$args)
{
if (Context::get(\Kernel\Consts\Base::STORE_STATUS) && \Kernel\Util\Context::get(\Kernel\Consts\Base::IS_INSTALL)) {
//$list = _Point($point);
$list = Plugin::$container['hook'][$point];
//var_dump($list,$point);
foreach ($list as $item) {
$instance = _Instance($item);
$ref = new \ReflectionClass($instance);
$reflectionProperties = $ref->getProperties();
foreach ($reflectionProperties as $property) {
$reflectionProperty = new \ReflectionProperty($instance, $property->getName());
$reflectionPropertiesAttributes = $reflectionProperty->getAttributes();
foreach ($reflectionPropertiesAttributes as $reflectionAttribute) {
$ins = $reflectionAttribute->newInstance();
if ($ins instanceof \Kernel\Annotation\Inject) {
di($instance);
}
}
}
$result = call_user_func_array([$instance, $item['method']], $args);
if ($result) {
return $result;
}
}
}
}
同时建议删除 /kernel/Plugin.php 文件和在 /kernel/kernel.php 里去除
if (\Kernel\Util\Context::get(\Kernel\Consts\Base::STORE_STATUS) && \Kernel\Util\Context::get(\Kernel\Consts\Base::IS_INSTALL)) {
//require("Plugin.php");//删这行
//插件初始化
\Kernel\Util\Plugin::scan();
//Initialize();删这行
//插件初始化
hook(\App\Consts\Hook::KERNEL_INIT);
}
其他#
建议不要使用异次元发卡,漏洞挺多的,我手里就好几个未被公开的漏洞。肯定有聪明人将来会发现这些漏洞,所以为了卡的安全,请停止使用异次元。
如果你坚信异次元没有漏洞 不要和我杠 你自己继续用就行
至于有没有其他机制防止插件被盗用我就不清楚了,反正我这里测试已经可以使用,插件还是需要蹭一下别人的。