首页
社区
课程
招聘
[旧帖] [原创]justinmind prototyper暴力破解(java) 0.00雪花
2011-2-27 02:47 3852

[旧帖] [原创]justinmind prototyper暴力破解(java) 0.00雪花

2011-2-27 02:47
3852
目标软件:justinmind官方网站下载30天试用版Justinmind Prototyper
保护方式:使用jProductivity的license文件方式保护
调试环境:win7 + MyEclipse8.6
代码阅读:SourceInsight 3.5
文件编辑:UltraEdit
java反编译:XJad 2.2、jd-gui 0.3.3

破解过程:
1.  经过初步分析,定位到jar包com.justinmind.evc_4.0.1.jar
2.  用XJad、jd-gui反编译此jar包。
3.  在SourceInsight中阅读代码,初步确定保护代码位置在com\jp目录,因为其中有com\jp\protection\security目录,com\jp\protection\pub目录下存在License*.java文件,经过网上搜索com.jp.protection,得知是jProductivity的产品。基本确认保护代码位于这里。
4.  暂时放弃反编译得到的除com\jp目录之外的其余部分,结合两个反编译情况,在MyEclipse中修正将com\jp目录的反编译结果,消除编译错误,以便能进行跟踪调试。
此步骤对于反编译软件不能正确反编译而直接显示为java byte code的部分,参考了网上搜索到的相关文章,以及java规范中对于java byte code的描述,在这些知识基础上,此步骤完全是体力活儿,这里略过。
这里稍微值得一提的:
a) 对于不能反编译的情况,XJad反编译结果中,给出了原始的java byte code,相当于c/c++程序的汇编码,可以自己分析处理,而jd-gui中给出的则非常不完整,没法使用。
b) 对于一些try/catch异常保护,以及其他循环、跳转等,jd-gui的反编译结果相对来说,比Xjad要好一些。
c) 代码中做的java代码混淆,主要是:把所有代码中的明文字符串,做了简单异或,在运行使用时才异或回来;除个别类外,大部分的包、类、方法、变量等命名,都采用单字符以降低可读性。

5.  跟踪调试,暴破。
a) 随便造一个文本文件,在软件的license文件选择对话框中选中,提示license文件破损。
b) 经过跟踪调试,发现了com\jp\protection\pub\LicenseReader.java中以下两个方法:
        // 验证license文件的入口函数
        public synchronized void a(InputStream inputstream, String s1)
                throws IOException
        {
                int i1;
                i1 = com.jp.protection.pub.m.q;
                g = true;
                b = null;
                Object obj = null;
                byte abyte0[];
                try{
                        abyte0 = b(inputstream);//取文件头明文字符串,验证后续数据的CRC32
                }finally{
                inputstream.close();
                }
label0:
                {
                        if (abyte0 == null)
                        {
                                b(this, s1);
                                if (i1 == 0)
                                        break label0;
                        }
                        // 省掉这一步,自己构造license文件,不加密 ????
                        //abyte0 = c(abyte0);        // 解密数据,需要公钥
                        if (abyte0 == null)
                        {
                                b(this, s1);
                                if (i1 == 0)
                                        break label0;
                        }
                        e(abyte0);
                        if (b != null)
                        {
                                ((LicenseImpl)b).d(s1);
                                c(this, s1);
                                if (i1 == 0)
                                        break label0;
                        }
                        b(this, s1);
                }
                return;
        }

        protected byte[] c(InputStream inputstream)
        {
                byte abyte0[] = null;
                DataInputStream datainputstream = new DataInputStream(inputstream);
                try
                {
                        String s1 = datainputstream.readUTF();
                        d(this, s1);
                        long l1 = datainputstream.readLong();
                        int i1 = datainputstream.available();
                        abyte0 = new byte[i1];
                        int j1 = 0;
                        int k1;
                        do
                        {
                                k1 = datainputstream.read(abyte0, j1, i1);
                                j1 += k1;
                                i1 -= k1;
                        } while (k1 != -1 && i1 > 0);
                        t t1 = new t();
                        t1.a(abyte0);
                        // 去掉校验CRC的部分
                        /*if (l1 != t1.a() && (inputstream instanceof FileInputStream))
                                abyte0 = null;
                                */
                }
                catch (IOException ioexception)
                {
                        a(ioexception);
                        abyte0 = null;
                }
                return abyte0;
        }

其中“//abyte0 = c(abyte0);        // 解密数据,需要公钥”,这里的方法c,内部是调用了RSA算法使用公钥解密,这说明正常license应该是需要私钥加密的,私钥我们没法拿到,所以这里只能把解密跳过。
至于“// 去掉校验CRC的部分”下边的代码,当然也可以保留,不过那需要构造license文件时计算一个正确的CRC32值,不方便手工构造license文件,因此注释掉。
事实上,代码中还有其他位置用到了RSA公钥,用于验证部分class文件的签名,以防止这些文件被篡改,幸运的是,LicenseReader.class没在这个验证文件列表中,因此我这里没有列出那部分代码,也没有考虑对其进行额外处理。

c) 以上步骤,基本上已经完成了暴力破解过程。后续就是构造合法的license文件,通过进一步跟踪上述licence文件解析代码,得到license文件格式如下:
[2字节UTF字符个数] + [UTF字符串] + [8字节后续数据的CRC32值,实际CRC32只用4字节,但是java的long型是8字节,所以这里用的8字节] + [文本内容]
其中[文本内容]都是类似如下的字符串:
# type 1:Evaluation , 2:Extended Evaluation ,  3:Commercial
typ=3
# Options: 1,2,8(fUserLicensingModel 0,2),64(fUserLicensingModel 3)
opt=0
# issue date: Mon May 31 00:00:00 CST 2010
isd=1283824000000
# expire date: Thu Mar 15 00:00:00 CST 2255
exd=9000000000000

d) 根据c)中分析得到license文件格式,在UltraEdit中手工编辑构造license文件,进一步调试跟踪代码的其他部分,以便确认在程序运行中都需要用到哪些属性,以及这些属性的合法取值范围都应该是多少,并将其以[属性]=[属性值]的形式补充到license文件中,最终形成合法license文件。

6. 以上都是在调试环境运行。
将编译得到的LicenseReader.class,直接覆盖安装目录Justinmind\Justinmind Prototyper 4.0.1\plugins\com.justinmind.evc_4.0.1.jar中的同名文件,程序可以正常运行,在程序About中可以看到预期的license信息描述。

暴破初步成功。

7. 由于此程序是30天试用版,关闭程序,将系统时间增加2个月,重新启动程序,仍能正常运行,且license信息显示正确。
关闭程序,把系统时间还原,再次启动程序,提示许可证已过期,无法正常运行,说明内部还存在其他验证。
继续进调试环境跟踪,在license文件读取解析后信息的访问位置设置断点,发现程序在C盘的两个目录,以及注册表中,都写入了信息,根据跟踪到的信息,将相关位置数据清空后,再次启动程序,正常启动,license信息显示正常,功能正常使用。
这里对c盘文件、注册表项,只作了简单异或处理,而这里的破解过程,也只是基本的跟踪调试,不再详细说明。
事实上这里也可以修改代码,把这个判断屏蔽掉,由于正常使用时,一般不会减小系统时间,这个用处不大,没有做这个修改。

至此,破解完全成功。

另:由于30天试用版在功能上是没有任何限制的(30试用版没有license文件,是代码内嵌的,只要从未提供过license文件,就当作试用版处理),所以根据上述步骤7,该程序可以完全不做暴破,只是在提示license到期时把C盘2个目录,以及注册表项清空,就可以再有30天的试用期。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞6
打赏
分享
最新回复 (4)
雪    币: 84
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzl 2011-3-3 19:42
2
0
没人回复啊。
java的太简单了?或者这里大家都不是冲着java来的?

个人以为,不管什么语言,破解的思路都应该是类似的,都有互相可以借鉴的地方。
前边说了过程,再总结一下经验:
1. 寻找切入点。
先看整体。
这个软件虽然本身以exe启动,但是目录下有java虚拟机环境,jar包等。用微软官网的Process Explorer观察,可以看到exe最后会启动javaw,因此可以确认exe的用途不过是解析配置文件,启动java虚拟机环境。
尝试自己直接启动javaw,成功,因此,可以直接把exe抛开,不管其中是否有壳,是否有license验证。
入手没必要上来就调试exe,或者exe脱壳,应该先熟悉软件的运行环境,用一些监视工具看一下软件运行时都在做什么。

2. 代码阅读和调试。
不管是java,还是exe调试直接看汇编,代码阅读能力,对破解都是非常重要的,除了有能力,还要有耐心。

3. 暴力破解的验证。
同样要有耐心。要像正常做软件测试一样,尝试正常、边界条件等各种情况的试验,确保暴力破解绕过了所有验证代码。
雪    币: 257
活跃值: (105)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
舵手 3 2011-3-8 09:29
3
0
java的不用直接看汇编吧!
雪    币: 84
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzl 2011-3-16 20:21
4
0
java当然不需要看汇编,只是说一下代码阅读的重要性而已。

另,java混淆代码,用jad反编译,有些不能正常反编译的代码,是直接显示为java字节码的,所以破解java程序时,可能需要看java字节码,这跟汇编是类似的,也得一条条指令分析来弄清楚整段代码功能。
雪    币: 265
活跃值: (56)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
omni 2011-4-30 22:46
5
0
拜读楼主的文章,有点感触,希望楼主多出些调试细节文章,感谢!!!!!
游客
登录 | 注册 方可回帖
返回