兔子的第一个破解版程序
说 (shui) 好 (shui) 的周更来啦!
前言
偶然之际得知一条消息,一个从没用过的据说很有名的脑图软件,最近在搞促销,原价39,49刀的软件,现在统统只要一块钱!本着买不了吃亏买不了上当的原则去看了一眼。 链接在这儿 (活动到12号现在还有哦)感觉虽然不错但是,没有强需求还是不想付钱。到一个破解版网站一搜,提示这样子……
注:网友反馈,此破解尚有问题,可以激活但功能无法使用,请大家先用旧版本,等待新版本 1.将net.xmind.verify_3.6.50.201606270419.jar复制到/Applications/XMind.app/Contents/Eclipse/plugins/目录中 2.打开Serial.rtfd文件,将序列号添加在软件激活页面即可(邮箱任意)。 3.以前如果安装过,务必先用AppDelete卸载旧版本
呃……用不了你个卵用啊。不过注意到了两点。
- 破解文件名后缀
.jar
- 只需要替换一个文件
换句话来讲,
- 源程序 Java 编写,可能可以反编译成高级语言代码
- 只需要破解一个文件
这么简单的事情当然要自己来啦~上好佳的学习机会必然不能错过啊。下面简单总结一下破解一个(又是)没有加密没有混淆的 Java 应用所需要的步骤和思路。
工具
- Java Decompiler 反编译工具,带界面的
- 如果愿意还有 在线版
- 还有 Eclipse 和 Intelij 的插件哦
- Java Bytecode Editor 也带界面的,反汇编/汇编工具
- Krakatau 看名字就像是日本人做的,另一个 Java 汇编、反汇编、反编译工具
- 你喜欢的编辑器
分析
首先,先观察目录结构(Mac版)
|-Contents
|---Eclipse
|-------org.xmind.share.localnetwork.feature_3.6.50.201606271038
|-------org.xmind.share.localnetwork.nls.feature_3.6.50.201606271038
|-------org.xmind.ui.iconfinder.feature_3.6.50.201606270419
|-------...
|-----jre
|-------bin
|-------...
|-----p2
|-------org.eclipse.equinox.p2.core
|---------cache
|-----------binary
|-------org.eclipse.equinox.p2.engine
|---------profileRegistry
|-----------XMindProfile.profile
|-----plugins
|-------org.eclipse.core.runtime.compatibility.registry_3.6.0.v20150318-1505
|---------META-INF
|-------org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.300.v20150602-1417
|---------META-INF
|-------org.eclipse.ui.themes_1.1.0.v20150511-0913
|---------META-INF
|---------css
|-----------dark
|---------images
|-------...
|-------org.xmind.ui.help_3.6.50.201606270302
|---------META-INF
|-----------maven
|-------------org.xmind.help.plugins
|---------------org.xmind.ui.help
|---------contents
|-----------images
|-------org.xmind.ui.resources_3.6.50.201606271038
|---------META-INF
|-----------maven
|-------------org.xmind.cathy.plugins
|---------------org.xmind.ui.resources
|---------markers
|---------styles
|---------templates
|---------wallpaper
|-----------pattern
|-----------wallpaper
|---Library
|-----QuickLook
|-------XMindQuickLook.qlgenerator
|---------Contents
|-----------MacOS
|-----------Resources
|-------------en.lproj
|-----------_CodeSignature
|-----Spotlight
|-------XMindSpotlightImporter.mdimporter
|---------Contents
|-----------MacOS
|-----------Resources
|-------------en.lproj
|-----------_CodeSignature
|---MacOS
|-----XMindURLHandler.app
|-------Contents
|---------MacOS
|---------Resources
|-----------Base.lproj
|-----------en.lproj
|---------_CodeSignature
|---Resources
|-----Fonts
|---_CodeSignature
jre都看到了,那就确定是 java 咯。看看那个 Plugins 目录很可疑,里面超级多的jar文件,一搜索,果然,verify在里面。
载入到 JD 里面去。界面如图。
打开其中一个类。唔,
Java 好良心。连枚举类型都要给你列出来。啧,毕竟用的人多。 JD GUI 让人印象最深的那个点击跳转函数定义的功能。脑子转起来操作也跟得上思路,在代码中间跳转那一种行云流水的感觉简直就是我等程序员的毒药!
代码里面的变量名、方法名都写的很清楚,到这里思路就是顺着调用向下走就可以。在这期间,竟然还找到了密钥加解密的部分,不过,很遗憾不能自动生成,也因此网络上的破解版都要替换文件。往后,我会讲讲因为什么。
Java 编译以后,和 C# 一样,生成一种中间码,Bytecode,形式上靠近汇编,需要关心入栈出栈,关心字节码长度 patch 后会不会超限,不过也能用一些高级的数据类型和函数调用,而且可执行文件里面包含了行数对照表,常量表等等,相当于 debug symbol 都给你了,想想他们码农就觉得他们傻——不然的话,spigot和forge哪儿还用那么老些开发者啊?
从验证函数里走出来,发现只能曲线救国了,因为注册码的内容是私钥加密的不可能自行生成。换一个思路。所有的注册码总会有黑名单。我到网上随便找了个注册码,肯定被黑名单了啊不过签名校验能通过,然后把代码改掉,让它在服务器获取名单时候既不读取,也不保存,保存后也不再读取。从网上请求并保存黑名单时候,名字和序列号中间隔一个|
,读取并识别黑密钥时候,让它中间随便隔个别的,它就永远不会发现黑名单不对……逆向永远要比写程序的更猥琐,嘛~
// 保存的黑名单
example@qq.com|XAka34A2rVRYJ4XBI...
// 读取名单时对比所用的字串
example@qq.com*XAka34A2rVRYJ4XBI...
// 永远不会相等
过了一会儿发现,他会在黑名单文件里面不停不停的写入密钥黑名单,因为程序发现, MD 都说过了在黑名单里面怎么还不拉黑,就一直加一直加。在线验证是在 VerificationJob
这样一个单独的线程里面,所以程序运行时候,即使已经认证了 Pro 他也会旋转跳跃不停歇。所以,只能找到获取保存文件的方法, nop 掉。
private static File getBlacklistFile()
{
return null;
}
Σ( ̄。 ̄ノ)ノ
破解方式
至于怎么进行 Patch ,主要是两种方法。一是直接用 JBE 或者 Krakatau 编辑反汇编,然后汇编回去保存,另一种使用 JD 或者 Krakatau 反编译以后,编辑源码然后编译回去。虽然没有 JDK 不过编译一下也是可以。而且,在 Java 一个类里面再内嵌其它的 class ,编译时会将它分别保存到其它文件中,反编译也会。这就产生了两个问题。
第一个问题是,分散开来的源码一起编译会出问题,用什么顺序?把哪些哪些归在一起?和没 patch 过的类怎么结合?类里面有优化过度反编译失败的字节码怎么办?所以反编译对于大项目来说根本不可能,只能去小打小闹改字节码。
决定反汇编了,就要决定哪些代码有用哪些代码没用。上面讲过,一个 class 产生的代码会分散到几个文件,而且有的类有名字有的没有1。
有名字的子类会保存在形如 LicenseVerifier$VerificationJob.class
的文件中。匿名子类则为 LicenseVerifier$1.class
,多用于回调函数或者创建线程时指定需要执行的程序。比如这样。
public void notifyValidityListener(final IValidity validity)
{
if (this.display != null)
{
if (!this.display.isDisposed()) {
this.display.syncExec(new Runnable()
{ // LicenseVerifier$ValidityNotifier$1.class
public void run()
{
SafeRunner.run(new SafeRunnable()
{ // LicenseVerifier$ValidityNotifier$1$1.class
public void run()
throws Exception
{
LicenseVerifier.ValidityNotifier.this.listener.notifyValidity(this.val$validity);
}
});
}
});
}
}
else {
SafeRunner.run(new SafeRunnable()
{ // LicenseVerifier$ValidityNotifier$2.class
public void run()
throws Exception
{
LicenseVerifier.ValidityNotifier.this.listener.notifyValidity(validity);
}
});
}
}
从从能上一般的匿名子类对逆向来说都是没有用的(摊手),而有名字的子类一般是我们重点关注的对象。不过更简单一点的方式就是,看大小,特别是混淆过的代码,其中有些变态还可能会把匿名类写上名字装作若无其事的样子呢。总之,代码多的肯定是有用的类咯~
剩下的过程就是上面随便讲的那些了,定位函数,删掉,完事。
几个小坑
0x01
开始把 java bytecode 当成普通 x86 汇编一样,改掉方法以后还用 nop 填充,到大小一样为止。后来发现不用啊,反汇编里面都是用数字编号代替常量和名字的,汇编回去自动就挤到一个文件里面,把空闲和多余的地方都打理好了。这样就很方便了,而且还能自己添一点恶意代码呢(耶)
0x02
开始的时候不熟悉这种字节码 (现在也不熟悉) ,用 JBE 修改完汇编以后,总是点不开修改后的反汇编,开始以为是软件垃圾。后来才发现了 Krakatau ,自带语法检查,用上了才知道是自己的问题,比如一个 ldc_w
写成 ldc
(这个也是 Krakatau 的修改建议超智能),带界面的软件扔出了一个异常就不理我了。还是命令行工具好,嗯。
0x03
忘了……想起来再补
防范
非对称加密
说一下如何写一个合格的验证码。这个程序就是一个好例子。待我用刚破解的程序画一个流程图 [斜眼]
其中最最重要的一点就是要用 非对称方式 加密。以任何形式将密钥保存在本地,不仅仅会被人破解,还会被做出注册机,被注册机的恶果是,即使以后再更新,除了黑客以外的普通人也可以轻松破解你的最新版软件,劳动成果很容易的被盗取,很让人伤心啊。
代码混淆
Java 这种东西,反编译之后和二进制差别是不大的,毕竟是高级语言向低级语言而不是机器语言的转换,信息量丢失的不大,更何况还有猪队友把调试信息留在里面。我是没学过 Java 啊,不过看反汇编以后的文件内容,默认的编译好的 jar 或者 class 里面都有 local variable table, stackmap table 和 line number table (???) 。。这就是,,连源码和字节码的对应关系都给你了,我都看出来这就是本来应该自己用的 debug 版嘛!
Java 这个语言并不烂,比 node、 python 都差不到哪儿去,坏就坏在它太好学了。林子大了什么鸟都有,什么奇葩的事情啊,都有人干。所有好学的语言都一样, C#, VB, python 。啧啧啧。垃圾的从来都不是编程语言,而是……