很好的一篇文章,先存着以后用到。

为了防止官网更新修复,存一下版本

https://pan.lanzou.com/b220073/ 
密码:这是秘密

这篇文章以 JavaScript 为例讲解了破解的一些通用方法。

你甚至可以将本文章作为学习 Chrome DevTools 的教学文章。

样本

官方网站:https://ckeditor.com/ckeditor-4/ckfinder/

本次样本:CKFinder 3.4.2(PHP版)

破解目的

下载页面

All downloads are full versions with just a few features locked. Use your license key to unlock complete CKFinder functionality, with no need to download a separate package. Try it for a limited period of time and buy it when you are ready. We prepared easy licensing options for your development purposes.

所有下载都是完整版,只有少数功能被锁定。使用您的许可证密钥解锁完整的CKFinder功能,无需下载单独的软件包。尝试一段有限的时间,并在准备就绪时购买。 我们为您的开发目的准备了简单的许可选项。

上面说了,下载到的这个文件是完整版本,只是有一些功能被锁定了。当然自己使用的话完全没什么影响,给别人的话有些麻烦。

其实没什么锁定,就是上面会有一个 Demo 的字样,每 5 分钟会弹出 Demo 版提示框,其他功能都是全的。(破解完我才发现 Demo 版不能删除文件)

CKFinder 许可协议(CKFinder License Agreement)

Unlicensed Copies

If You did not pay the License Fee, You may use unlicensed copies of the Software for the exclusive purpose of demonstration. In this case You will be using the Software in “demo mode”. Without derogating from the forgoing, You may not use the Software in “demo mode” for any of your business purposes. The Software in “demo mode” shall only be used for evaluation purposes and may not be used or disclosed for any other purposes, including, without limitation, for external distribution. You may not remove the demo notices, if any, from the user interface of the Software nor disable the ability to display such notices nor otherwise modify the Software. Product support, if any, is not offered for the Software in “demo mode”.

未经许可的副本

如果您没有支付许可证费用,您可以仅以演示目的使用本软件的未经许可副本。在这种情况下,您将以“演示模式”使用本软件。 在不违背上述规定的前提下,您不得以任何商业目的在“演示模式”下使用本软件。“演示模式”中的软件仅用于评估目的,不得用于任何其他目的,包括但不限于外部分发。 您不能从软件的用户界面中删除演示提示(如有),也不能禁用显示此类通知或修改软件的功能。产品支持(如果有的话)不以提供给“演示模式”软件。

本教程仅供学习交流使用,请勿用于其他用途。

过程

运行软件

在代码的根目录下执行

php -S 127.0.0.1:8000

开启 php 内置服务器,然后访问 http://127.0.0.1:8000/ckfinder.html 即可。

<ignore_js_op>

如何填写许可证信息?

在 config.php 中有以下内容

$config['licenseName'] = '';$config['licenseKey']  = '';

然后我们就使用 PHPStorm 强大的全局搜索功能吧。

<ignore_js_op>

<ignore_js_op>

我们会发现,./src/CKSource/CKFinder/Command/Init.php 中有一写相关代码,然后就下断点分析一下原理吧。

分析 php 部分

这里调试 php 需要 xdebug 的支持,安装 xdebug 步骤请参照 官方文档,这里不作具体介绍。

$ln = '';$lc = str_replace('-', '', ($config->get('licenseKey') ?: $config->get('LicenseKey')) . '                                  ');$pos = strpos(CKFinder::CHARS, $lc[2]) % 5;if ($pos == 1 || $pos == 2) {
    $ln = $config->get('licenseName') ?: $config->get('LicenseName');
}$datacommandObject = $ln;$data->c = trim($lc[1] . $lc[8] . $lc[17] . $lc[22] . $lc[3] . $lc[13] . $lc[11] . $lc[20] . $lc[5] . $lc[24] . $lc[27]);// 此处省略其他代码return $data;

似乎并没有什么有用的信息,就是把 licenseKey 和 licenseName 转换了一下,然后就返回给浏览器了。

分析 js 与 php 交互

我们发现,php 断点停住以后,Chrome 开发者工具 Network 选项卡中有一个请求的状态一直处于 pending 状态。

<ignore_js_op>

我们直接跟踪到 XmlHttpRequest 的调用点。

<ignore_js_op>

下个断点?先格式化代码再说吧。

代码格式化

这一步没什么多说的,什么工具都可以,在 Chrome 开发者工具中打开 Source 标签,点击左下角的 {} 按钮,然后再复制粘贴到 ckfinder.js 中就行了。一般来说这样 uglify 的代码应该不会有文件校验吧。

解码

我们看到代码中有很多的 S('...') 的东西,我猜是字符串解码函数,应该是作者为了避免字符串搜索。

直接在断点停住时在 Chrome 控制台中把那个表达式粘贴上,执行一次试试。解码成功了,看样子不算太麻烦。

<ignore_js_op>

Chrome 断点停住时,控制台的上下文是断点语句处的上下文,可以访问局部变量,所以断点处调用了 S('...') 的语句,你在控制台执行的话,S 函数也一定存在。

自动解码

因为 JavaScript 的字符串太特殊了,使用字符串匹配的话很麻烦,我这里选择分析 AST(抽象语法树),针对 AST 进行替换。

首先安装 acorn 语法分析器和 escodegen 代码构造器,一个用来从代码生成 AST,一个用来把 AST 转换回代码。

npm install acorn escodegen

下面是我写的替换代码,判断了一下字符串和三元运算符

const acorn = require('acorn');const walk = require('acorn/dist/walk');const escodegen = require('escodegen');const fs = require('fs');const path = require('path');function S(e) {
    for (var t = '', n = e.charCodeAt(0), i = 1; i < e.length; ++i)
        t += String.fromCharCode(e.charCodeAt(i) ^ i + n & 127);
    return t;
}function recursiveDecode(node) {
    if (node.type === 'Literal') {
        node.value = S(node.value);
        // console.log(node.value);    } else if (node.type === 'ConditionalExpression') {
        recursiveDecode(node.consequent);
        recursiveDecode(node.alternate);
    } else {
        console.log('Node type is neither Literal nor ConditionalExpression. ' + node.start);
    }
}// 这里改成你的代码位置var inputFile = path.join(__dirname, 'ckfinder/ckfinder.min.js');var outputFile = path.join(__dirname, 'ckfinder/ckfinder.js'); fs.readFile(inputFile, {encoding: 'utf-8'}, function (err, data) {
    if (err) {
        console.log(err);
        return;
    }
    var ast = acorn.parse(data);
    walk.simple(ast, {
        CallExpression: function (node) {
            if (node.callee.type === 'Identifier' && node.callee.name === 'S' && node.arguments.length === 1) {
                var arg0 = node.arguments[0];
                recursiveDecode(arg0);
                if (arg0.type === 'Literal') {
                    node.type = arg0.type;
                    node.value = arg0.value;
                } else if (arg0.type === 'ConditionalExpression') {
                    node.type = arg0.type;
                    node.test = arg0.test;
                    node.consequent = arg0.consequent;
                    node.alternate = arg0.alternate;
                }
            }
        }
    });
    var code = escodegen.generate(ast);
    fs.writeFile(outputFile, code, function (err) {
        if (err) {
            return console.log(err);
        }
        console.log('The file was saved!');
    });
});

使用方法

node decode_ckfinder.js

分析过程

我们用 PHPStorm 打开 ckfinder.js,使用 PHPStorm 的代码定位直接找到 S 函数。

<ignore_js_op>

然后我们就找到了解码方法,这段代码已经嵌入我的解码代码中了。

function S(e) {
    for (var t = "", n = e.charCodeAt(0), i = 1; i < e.length; ++i)
        t += String.fromCharCode(e.charCodeAt(i) ^ i + n & 127);
    return t
}

运行结果

var CKFinder = function () {
    function __internalInit(e) {
        return e = e || {}, e['demoMessage'] = 'This is a demo version of CKFinder 3', e['hello'] = 'Hello fellow cracker! We are really sad that you are trying to crack our application - we put lots of effort to create it. ' + 'Would you like to get a free CKFinder license? Feel free to submit your translation! http://docs.cksource.com/ckfinder3/#!/guide/dev_translations', e['isDemo'] = !0, e;
    }// 后面省略了

哈哈,作者发现我们破解了他的软件了。

你好,你们这些破解者!我们真的很伤心,您正试图破解我们的应用程序——我们付出了很多努力来创建它。你想获得免费的 CKFinder 许可证吗?放心地提交您的翻译!http://docs.cksource.com/ckfinder3/#!/guide/dev_translations

其实很多软件作者挺有意思的。这可能是最简单的暗桩吧,就是一个提醒字符串。

继续分析 js

This is a demo version of CKFinder 3 这句话就是我们要找的。

后面还有一句 e['isDemo'] = !0,就是 e['isDemo'] = true,莫非我改成 false 就 OK 了?

在 Chrome 中下个断点,看看什么情况。

根本就没断下来,看来作者跟我们开了个玩笑。不过想想也对,怎么能这么容易就让你破解了呢。

尝试 DOM 断点

现在我们的线索断了,不过我们有个笨方法。在 XHR 的调用点断下之后,下 DOM 断点(当 DOM 节点修改的时候会断下),然后运行,直到插入的 node 就是那个 This is a demo version of CKFinder 3 的标题的时候,我们再继续分析。

<ignore_js_op>

这个过程可能比较枯燥,就是不断的继续运行,继续运行,直到那个被添加的 node 是 h2 的时候。

<ignore_js_op>

非常抱歉,我没找到......

新的想法?

为什么我们不能直接搜索到 This is a demo version of CKFinder 3 呢?因为肯定是被加密了啊,那么我们直接找出所有乱码字符串就行了。

我在 decode_ckfinder.js 中加了一行 console.log(node.value);(就是上面注释掉的那一行) 这一行会打印所有的一次解码之后的字符串,然后我们就排查一下吧,反正才 6246 行,不到五分钟差不多就能看完。

还真让我找到了。

<ignore_js_op>

<ignore_js_op>

<ignore_js_op>

直接在代码中搜索其中一个字符串,定位到附近,下断点,执行一次。

<ignore_js_op>

这个就是我们要找的了,断点之后单步运行,把这句话运行完,然后修改一下 t['message'] 的值,看看效果。

<ignore_js_op>

看来可行,然后我们就逆着调用栈找,找到判断语句。

<ignore_js_op>

类比推理

<ignore_js_op>

似乎,所有的加密都有 String.fromCharCode,我们直接搜索一下这个语句,应该就能找到所有的字符串加密,他们周围有其他验证的判断语句,直接 if (false)掉。

if (false) 这种方法在汇编语言里怎么表示?

一种方法是 jnz 变成 jmp 或 nop,另一种是 jnz xxx 变成 jnz 00

内存断点

上面这种方式好麻烦啊,我们还要猜原来作者是怎么想的。有没有方法直接在读取 $data->c(就是返回给 js 的那个许可证) 的时候断下来。

这个东西不就是内存断点嘛,只不过 Chrome 不支持(据说 Firefox 是支持的),不过 StackOverflow 上的朋友们已经给出了解决方案。

我是用 Bing 搜索 chrome var changed breakpoint 搜到的。

https://stackoverflow.com/questions/11618278/how-to-break-on-property-change-in-chrome/38646109#38646109

https://github.com/paulirish/break-on-access

<ignore_js_op>

在 Source Snippets New snippet 中粘贴下列代码,然后右键运行。(如果没有 Snippets 注意一下 >> 这个按钮)

function breakOn(obj, propertyName, mode, func) {
    // this is directly from https://github.com/paulmillr/es6-shim    function getPropertyDescriptor(obj, name) {
        var property = Object.getOwnPropertyDescriptor(obj, name);
        var proto = Object.getPrototypeOf(obj);
        while (property === undefined && proto !== null) {
            property = Object.getOwnPropertyDescriptor(proto, name);
            proto = Object.getPrototypeOf(proto);
        }
        return property;
    }     function verifyNotWritable() {
        if (mode !== 'read')
            throw "This property is not writable, so only possible mode is 'read'.";
    }     var enabled = true;
    var originalProperty = getPropertyDescriptor(obj, propertyName);
    var newProperty = { enumerable: originalProperty.enumerable };     // write    if (originalProperty.set) {// accessor property        newProperty.set = function(val) {
            if(enabled && (!func || func && func(val)))
                debugger;             originalProperty.set.call(this, val);
        }
    } else if (originalProperty.writable) {// value property        newProperty.set = function(val) {
            if(enabled && (!func || func && func(val)))
                debugger;             originalProperty.value = val;
        }
    } else  {
        verifyNotWritable();
    }     // read    newProperty.get = function(val) {
          if(enabled && mode === 'read' && (!func || func && func(val)))
            debugger;         return originalProperty.get ? originalProperty.get.call(this, val) : originalProperty.value;
    }     Object.defineProperty(obj, propertyName, newProperty);     return {
      disable: function() {
        enabled = false;
      },       enable: function() {
        enabled = true;
      }
    };
};

debugger; 这条语句可以让调试器直接断在这个位置处,配合数据绑定(给一个对象的属性设置 getter 和 setter),就可以做到内存断点了。然后在调用栈向上找一层,就是断点触发的位置了。

注意:断点断在数据修改之前。

<ignore_js_op>

在下方控制台执行

breakOn(o, 'c', read);

这样,任何代码在访问许可证秘钥信息的时候就会断下,然后在调用栈往上找一层就可以了。

注册 机

单步跟踪一下程序的流程就会找到验证函数,可以尝试分析一下算法,然后写一个注册机,这里暂时告一段落,毕竟能爆破何必注册。有兴趣的同学可以自己尝试做一个注册机。

<ignore_js_op>

<ignore_js_op>

总结

我们来分析一下破解软件的通用流程,不只是 JavaScript 破解。

这次的 JavaScript 破解

  • 我们首先找 licenseKey 这个字符串,然后追查到了 XmlHttpRequest。

  • 然后因为破解的需要,找到了 S 函数,然后解码了字符串加密。

  • 然后找到了 This is a demo version of CKFinder 3 这个字符串,发现被作者骗了。

  • 再之后,我们使用 XHR 断点和 DOM 断点,找到了真正写入这个字符串的位置,通过调用栈找到了另一个解码函数。

  • 然后,我们分析了程序逻辑,直接将 if 判断改成 if(false)

  • 接着,我们使用类比法找到了所有含 String.charCodeAt 的位置,把这些位置的判断都去掉了。

  • 我们换了另一种套路,内存断点,轻松地找到了验证函数的位置。

通用思路

  • 字符串搜索

  • 找到通用的API入口下断点(在 Windows 下就是跨模块调用,Javascript 中就是 XHR 断点或 DOM 断点)

  • 断下之后下内存断点

  • 其他语言或编译器的特性(易语言尤其明显)

  • 单步运行

  • 沿着调用栈向上找

  • 类比推理

一个js破解教程的更多相关文章

  1. [js插件开发教程]实现一个比较完整的开源级选项卡插件

    在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>>   [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件. 还缺少两个常用的切换(自动切换与透明度渐变),当然 ...

  2. StarUML破解教程

    StarUML破解教程 StarUML官方下载地址:http://staruml.io/download StarUML是一个非常好用的画UML图的工具,但是它是收费软件,以下是破解方法: 1.使用E ...

  3. 前端工具WebStorm好在哪里?(带详细破解教程)

    前端工具WebStorm好在哪里?(带详细破解教程) 一.总结 1.WebStorm对html特别是HTML5和JS的智能提示简直堪称大神. 2.WebStorm足够的轻量级. 3.WebStorm对 ...

  4. 【入门必备】最佳的 Node.js 学习教程和资料书籍

    Web 开发人员对 Node.js 的关注日益增多,更多的公司和开发者开始尝试使用 Node.js 来实现一些对实时性要求高,I/O密集型的业务.这篇文章中,我们整理了一批优秀的资源,你可以得到所有你 ...

  5. 【特别推荐】Node.js 入门教程和学习资源汇总

    这篇文章与大家分享一批很有用的 Node.js 入门教程和学习资源.Node 是一个服务器端的 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用 ...

  6. 【转】Dr.com 5.20破解教程

    Dr.com 5.20破解教程 方法一  1.首先下载相关工具 Process Explorer(大家可以自行百度 一般绿色汉化版就可以)右键选择以管理员权限运行process的主程序 然后运行drc ...

  7. 简单详细的OD破解教程

    2007-08-04 15:46作者:CCDebuger注:昨天在网上见到了这篇文章,但缺少插图,从另外一篇文章中也看到了类似的的教程文章,里面的插图质量实在不敢恭维.在一个论坛中正好下载了文章中所介 ...

  8. 完美:adobe premiere cs6破解版下载[序列号+汉化包+破解补丁+破解教程]

    原文地址:http://blog.sina.com.cn/s/blog_6306f2c60102f5ub.html 完美:adobe premiere cs6破解版下载,含序列号.汉化包.注册机.破解 ...

  9. Node.js 入门教程和学习资源汇总

    这篇文章与大家分享一批很有用的 Node.js 入门教程和学习资源.Node 是一个服务器端的 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用 ...

随机推荐

  1. Python Web Flask源码解读(一)——启动流程

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  2. 【IDEA】在IDEA中使用@Slf4j报错,找不到log

    题:在IDEA中使用@Slf4j报错,找不到log 解决方法:需要在IDEA中安装插件lombok 详细步骤: 1.File->Settings 2.Plugins->Browse rep ...

  3. 使用SVN钩子强制提交日志和限制提交文件类型

    Subversion本身有很好的扩展性,用户可以通过钩子实现一些自定义的功能.所谓钩子实际上是一种事件机制,当系统执行到某个特殊事件时,会触发我们预定义的动作,这样的特殊事件在Subversion里有 ...

  4. https免费证书申请certbot,nginx

    官网:https://certbot.eff.org/ 下载: wget https://dl.eff.org/certbot-auto chmod a+x certbot-auto ./certbo ...

  5. CF - 652 D Nested Segments

    题目传送门 题解: 可以将所有线段按照左端点优先小,其次右端点优先大进行排序. 然后对于第 i 条线段来说, 那么第 i+1 ---- n 的线段左端点都一定在第i条线段的右边, 接下来就需要知道 i ...

  6. [Swoole入门到进阶] [精选公开课] Swoole服务器-Server的四层生命周期

    PHP 完整生命周期 执行PHP文件 PHP扩展模块初始化(MINIT) PHP扩展请求初始化(RINIT) 执行 PHP 逻辑 PHP扩展请求结束(RSHUTDOWN) PHP脚本清理 PHP扩展模 ...

  7. happen before 原则

    并发一直都是程序开发者绕不开的难题,在上一篇文章中我们知道了导致并发问题的源头是 : 多核 CPU 缓存导致程序的可见性问题.多线程间切换带来的原子性问题以及编译优化带来的顺序性问题. 原子性问题我们 ...

  8. 基于 APIGateway 打造生产级别的 Knative 服务

    作者 | 阿里云智能事业群高级开发工程师  元毅 导读:在实际应用中,通过 APIGateway(即 API 网关),可以为内部服务提供保护.提供统一的鉴权管理.限流.监控等能力,开发人员只需要关注内 ...

  9. Storm VS Flink ——性能对比

    1.背景 Apache Flink 和 Apache Storm 是当前业界广泛使用的两个分布式实时计算框架.其中 Apache Storm(以下简称"Storm")在美团点评实时 ...

  10. java工作流快速开发之授权代办的设计

    关键词:工作流快速开发平台  工作流流设计  业务流程管理 Java工作流引擎 asp.net 开源工作流  net开源工作流引擎 开源工作流系统 一.授权代办开发背景 应用需求:项目审批人出差无法及 ...