兔子的第一个破解版程序

作者 Known Rabbit 日期 2016-09-09
兔子的第一个破解版程序

说 (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 。啧啧啧。垃圾的从来都不是编程语言,而是……


  1. 关于匿名子类,详见 这里