在看雪论坛潜伏了好几年了 ,去年年底才注册帐号,现在忍不住了,上来发些帖子。
    我学过很多语言,但对Java只是纸上谈兵 ,看过两本书,但是几乎没写过代码。
    对于破解,我只是懂一点皮毛罢了,我用OD是为了看运行时一些变量的值,用IDA只是因为,用它看汇编比较好看 ……
 
    现在因为需要,要破解一个很大的软件系统,并写出注册机。这个系统,使用的语言包括Delphi、Java、C#三种,更郁闷的是,内部有个Delphi写的编译器,用来编译我在这个平台上写的代码,而在平台上写代码,所要调用的函数,由Delphi、Java、C#三种语言提供。它这个编译器,使用CLI规范,但又不完整……
 
软件限制就是:用这个平台的未注册版本开发的系统,限制了在线用户数量。
目前进度:已获释机器码产生算法,已确定注册后会生成一个授权文件。
 
    我想在这把对这个软件的破解记录下来,同时,也希望各位大侠帮忙解决一些碰到的问题。
 
    首先,我尝试了查找特征码的方法,因为在线用户超过限制时,会有提示,所以,我肯定这个信息来自Java写的一些类包。
    然后,我写了一段vbs脚本,调用最新版本的JAD,反编译了所有的jar包,包括JDK的,因为我担心它可能修改了JDK里面的jar,JDK是随着该软件发布的。用文本编辑工具UE,搜索所有反编译后的java文件,居然未发现该特征字符串。我已经打开JAD的-8选项,Unicode文字是可以显示了的,所以不存在乱码不能显示的可能。
那么,剩下两个可能:一,该字符串被加密了,如果是这样,我就肯定得放弃了,因为,仅仅jar包,就有四千多个,更不用说java文件了,我不可能找到它在哪里被解密,要不我也不用写脚本来处理了,我可是专门用了一台四CPU的服务器来反编译的;二,调用了外部程序取得该字符串,或者读配置文件了,配置文件我翻过了,没有。至于调用外部程序,这是常事,我今年才发现它调了一个DLL 
 
    后来,通过网络抓包,我找到了在出错前它调用了服务器的最后的一个方法(它是用Hessian通信的)。正想到jar里面找这个方法,突然发现,这个目录怎么只有class没有java,手工再调一次jad,不行,提示不是java格式程序,开IDA,也说格式不对,用UE,天哪,都是十六进制的……
我在所有java文件中全面搜索这个jar的名字,发了在一个java中使用了它,也发现了它旁边的System.load方法,赶快上百度。看到这里,别笑我,我真的不懂Java!
我不仅搜到了这个方法的用法,还知道了它跟Java加密技术密切相关。原来是一个Java中用户动态加载类的方法,许多人重写了该方法的实现来保护Java代码。
下一步,搜索ClassLoader。得到近千条结果,不怕,服务器都不怕累,我也不怕,呵呵。慢慢检查发现,有一个类继承了URLClassLoader,重写的方法,与网上的保护方法极为类似,记下,继续看(我可不会因为找到一个而放弃后面的,那可是服务器大哥花了好几分钟帮我找到的呀),后面都没有可疑文件了。看来,这个类就是我要找的目标。
    重点查看构造函数和defineClass方法,因为构造函数可能初始化一些东西(使用了上面提到的那个jar包的路径),而加载类的核心,就在defineClass中。在defineClass中,只是做了一些准备工作,最重要的加载,而通过调用另外一个方法完成的,但我只看到该方法声明是这样的:public native Class。我猜测(真的不懂Java,反正C#中是这样的),它应该是调用外部native代码(不懂该怎么理解,反正我理解成PE格式之类的)。继续查找该类的可疑地方,发现一个static{}代码段,晕倒,又不懂是干嘛用的,我学过的语言里面,都没有这样的写法。不过不要紧,懂里面的代码就行了。哈哈,用System.load调了一个dll,它应该就是上面那个方法的所在地了。
    找到那个dll,首先PEid商场,“什么都没发现”。这已经没什么奇怪的啦,1G多的文件,鬼才知道哪个是用那种工具写的呢,再说,它自身就有编译器,搞不好就是他自己编译出来的。不管了,用IDA能打开就行。真的得来不费功夫,唯一的导出函数,名字就是上面的那个方法。赶紧进去看,好恐怖哦,好多好多var_xxx。看到下面的代码,哭笑不得:
sub     esp, 666h
mov     eax, dword_10066060
xor     eax, esp
mov     [esp+666h+var_4], eax
mov     eax, [esp+666h+arg_8]
mov     edx, [esp+666h+arg_14]
mov     ecx, [esp+666h+arg_10]
esp先减去一个数,然后用的时候,每次都加上……不知道是哪门子编译器搞的。
往下直到第一个call:
push    esi
mov     esi, [esp+688h+arg_0]
mov     edx, [esi]
mov     eax, [edx+18h]
push    esi
call    eax
这种方法,很像OO中调用一个对象的方法的做法(昨天刚学会的一点皮毛)。似乎esi就是这个对象,在call eax前,用了push esi,可能是想把这个对象做法参数传进去。
Java中那个方法声明有6个参数,而这里有7个arg,呵呵,真的很像。
 
现在只是想说 阿你托佛 ,保佑这个dll里面,没有再调用别的程序……毕竟是千山万水追到这里来的。
 
昨天发现的注册窗体的线索断了,现在正在请教vhly。解决了再发出来。
 
好了今天就写到这里了,我前天才刚开始接触Java的逆向,欢迎大家赐教!

  • 标 题:一个软件的逆向分析日志(二)
  • 作 者:nnhy
  • 时 间:2007-05-11 23:16

继续昨天的活。
    大概分析了一下DLL的那个导出函数,下面把它成为Load吧。
    一开始减esp,创建一块很大的栈区域用于存储本地变量。
    然后,把六个参数复制到本地变量中。除了这六个参数外,第一个参数可能是jvm函数表的地址,因为后面可以简单大量的:
    mov     esi, [esp+688h+jvm_base] ;jvm_base是我重命名后的
    mov     edx, [esi]
    mov     eax, [edx+18h]      ;18h可能是别的。有点类似于位移
    call    eax            ;动态调试发现,这个eax的值是jvm.dll里面的jvm.6D7355CE,可惜我没有这个库的sig,不知道它名字
    为了方便阅读,我在IDA中对这些变量进行了重命名。
    往下是是两个jvm的call,不知道干嘛的,不管它。第一个call前面,把那个重写ClassLoader的类的路径压栈。第二call后就是一个长长的跳转,跳到函数尾部的,嗯,这一般是参数检查之类的写法。结合那个类路径,我猜测是检查调用者是否是那个类,如果不是,则返回。工作还挺到位的。
    再往下,数百行都是mov指令,把常量一个个拷贝到本地变量中。变量名几乎是递减的。为什么说几乎呢?那是因为,有个别的顺序是乱的,不过在别的地方,肯定能找到“丢失”了的指令。这可真是费尽心思哦,如果我没猜错,这个就应该是一个密码之类的东西。往往破解者习惯搜索字符串常量来获得密码,这种方法,在这里就无效了,它其实已经是相当于把密码写在指令里面了,还故意搞乱了顺序,因而,这个库,很有可能是手工修改过的,甚至可能就是人工写的。因此,可以猜测,这是很重要的字符串(密码?)
    往下,就开始不断调用jvm的方法了,我整理了一下字符串参考,如下:
    "java/security/spec/X509EncodedKeySpec")
  "<init>")
  "java/security/KeyFactory")
  "(Ljava/lang/String;)Ljava/security/KeyFactory;")
  "getInstance")
  "(Ljava/security/spec/KeySpec;)Ljava/security/PublicKey;")
  "generatePublic")
  "java/security/Signature")
  "com/要创建的类名以及路径.class")
  "(Ljava/lang/String;)Ljava/security/Signature;")
  "getInstance")
  "(Ljava/security/PublicKey;)V")
  "initVerify")
  "update")
  "verify")
  "java/io/FileInputStream")
  "(Ljava/lang/String;)V")
  "<init>")
  "available")
  "read")
  "close")
  "DSA")
  "SHA1withDSA")
  "java/security/SecureClassLoader")
  "(Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class;")
  "defineClass")
  看字符串参考,或者IDA识别出来的函数名,望文生义,是我在搞逆向中的常用方法。因为我比较熟悉VB,上次逆向一个VB的DLL的时候,几乎等于看VB代码,因为流程不是很复杂,所以,根本不用管本地变量怎么赋值,直接看函数名,就直到它想干嘛了。
  言归正传,从上面的字符串参考可以大概看出:先初始化了一个X509编码的实例,然后取得一个密钥工厂,生成一个公钥,接着取得数字签名对象,并用公钥初始化签名对象,取得被签名的数据,验证。
  然后声明FileInputStream,呵呵,开始读取文件了,不多说。
  DSA,鼎鼎大名的DSA非对称加密算法。
  SHA1withDSA,看到这,我只知道SHA1是一种散列算法,一般来说,散列算法和DSA结合起来使用,主要用于数字签名。其实,如果不懂这个是什么东西,只要上百度搜索一遍,马上就会明白了。
  综合上面的分析,可以发现,这里主要进行了数字签名,用于判断目标(可能就是那个类)是否被修改过。而上面猜测的那个密码,正是公钥。
  在信息安全中,为了确保信息的完整性,可以对信息进行数字签名。签名者持有一对密钥,分别是公钥和私钥,他使用私钥对信息进行签名,然后签名后的信息和公钥一起分发出去。另一方,就可以使用该公钥对信息进行签名验证,判断信息是否被修改过。公钥和私钥是唯一对应的。
  在这里,公钥存储与程序中,并且做了很好的保护,就是为了防止被人轻易获取。若第三方(比如我们)获取公钥,并可以修改,那么,他完全可以使用另外的一对密钥中的对信息进行签名,并把这里的公钥也换掉,那么,就算修改了信息,这里也会认为是没有修改过。这正是作者花费那么多心思来保护这个公钥的原因。
  回过头来,才发现,我的问题,根本就还没有解决。我猜测这里是解密class的,但并没有明显的痕迹。
  从上面的字符串参考可以看到,它已经调用SecureClassLoader的defineClass方法返回类对象了。从网络上的资料来看,调用defineClass前,类的字节码会作为参数传进去的。因此,我们可以修改SecureClassLoader类的defineClass方法拦截这些字节码。可惜,我不会Java,更不知道如何修改这个方法,这方面,我觉得 舵手 可以帮上忙。
  上面的字符串参考中间,穿插着一些本地的函数调用,偶然(对于破解工作来说,需要很多偶然)发现其中一个方法,其大无比,在IDA的流程图中,可以看到很多层循环。很遗憾,我没认真研究过各种加密算法的写法,不知道它是什么算法。直觉告诉我,这里是解密用的。因此,有了第二个获取class字节码的方法:把这个方法抽取出来,直接对class文件进行解密,希望除了这个函数外,作者没做过别的处理。
  第三种方法:动态调试,直接从内存中抽取解密后的字节码。这个方法对我来说,相对容易很多。但是,到现在为止,我还无法确定它把字节码存放在哪个地方。没有Client/JVM.dll的函数名,看到的全都是数字,想猜都猜不来。对于Java中方法的参数规范又不熟悉……
  
  这几天公司项目要很忙,这个软件的下一步逆向,就暂时压后一点,同时,也留一点时间查资料,已经请求论坛上各位高手的帮助。

成功解决那些jvm函数的问题。
经过查找资料,最终确定,那个DLL,就是用JNI编译的。然后我也做了一个JNI的DLL,把可能的函数写进去,编译,用IDA看,对应一下就知道哪个地址是哪个函数了。
此外,我还发现一个更惊人的事情,在IDA中新建一个结构体用于存放JNI方法名的时候,发现,方法的顺序居然和jni.h头文件中方法顺序一致……呵呵,这回不用一个个猜了。
 
好了,吃饭了,先把jni的idc发上来,希望对大家有用。迟点我再把这些日子做的写上来。
目前一切顺利,已经到了最后一关了,正在破解DSA的私钥中……