二、试用许可证破解 – AnyDVD HD 8.5.1.0逆向破解全纪录

  本来以为破解了反调试及代码段加密之后应该就很容易了,但事实证明我想简单了。不得不感叹,AnyDVD不愧是搞加密破解的,自己的软件也是层层加密,本想着能将它完全破解,完全bypass许可证,但是发现难度比我想象的要大得多,于是只能退而求其次,先破解试用许可证的限制,从而可以一台电脑无限获取试用许可证来用。至于完全破解,等我闲下来了再慢慢研究。

0x01 去除虚拟机限制

  没有了调试限制之后,找到限制虚拟机的弹窗就很容易了,动态调试一下可以发现,对应的弹窗函数MessageBoxA位于sub_6E7D20->sub_6E7900->sub_6E7270中,而sub_6E7D20这个函数看上去应该是一个通用弹窗报错函数,真正的虚拟机逻辑判断应该在sub_6F77A0中。

sub_6F77A0函数反编译C代码

  sub_6F77A0这个函数也很容易分析清楚。sub_6F6D30为当前许可证状态的判断,返回0则为完全许可证,返回3为有效的试用许可证。sub_7EAB80为虚拟机判断,如果返回值大于等于10则为虚拟机。如果是完全许可证,则直接执行sub_6F38B0初始化窗口。否则的话判断是否为虚拟机,如果是虚拟机则弹窗报错结束。如果不是虚拟机,则判断试用许可证是否有效,有效的话执行sub_6F38B0初始化窗口,否则通过函数sub_6F74A0弹出请求许可证的窗口。sub_6F77A0的调用出现在sub_6F78A0中,而sub_6F78A0是对sub_6F77A0的简单循环,只要sub_6F77A0返回1就一直循环下去,因此只要不是主动退出程序,没有许可证的情况下就一直会显示请求许可证的窗口。

  这样一来去除虚拟机限制就简单了,直接把相应的条件判断改成无条件跳转就可以了,这样之后在虚拟机里就可以获取到试用许可证了,并且用着也没有发现任何问题。

0x02 JAVA代码

  看到上面可能会想,既然sub_6F38B0是程序的初始化逻辑,直接跳过许可证判断无条件跳转到窗口初始化可不可以呢?我其实也是这么想的,并且这样做了,但是我发现,虽然程序可以正常启动起来,但是会在一两分钟后自动退出,偶尔还会弹出“Your License is invalid, Error code 2”的错误框。

  错误框的情况稍后再讲,先来说说莫名退出的问题。我尝试在.idata导入段的ExitProcess地址处断点,但是并不能断点到,程序还是退出了,于是我想会不会是通过其他Win32 API退出的程序,比如TerminateProcessPostQuitMessage或者DestroyWindow之类的函数,但是仍然断点不到。最后我知道了,程序就是通过ExitProcess退出的,但是调用时并没有经过.idata段的地址,这也提醒了我,在对系统API调用断点时,最好断点在加载的dll中函数开头位置。

  由于之前并不知道程序怎么退出的也不懂得正确的断点方式,只能一点一点调试,最后我发现,退出位置位于__threadstart@4线程中sub_72A510->sub_723C60->sub_7286B0->sub_700970ExitProcesssub_700970中通过函数指针调用。在该动态调用处断点发现,调用的都是kernel32.dll中的函数,这样我意识到sub_700970应该是一个类似系统API调用代理的东西。另外我发现在包含该线程创建_beginthreadsub_7E8910函数中,类名频频出现“Java”字样,并且startAddress参数sub_7E8900->sub_7E8830的第一行代码为类名为CJavaThread的对象创建。这时我明白了,__threadstart@4这个线程运行了一个类似Java虚拟机的东西,而sub_72A510像是一个Java解释器(我就说为什么单步调试的时候一直在这个函数里循环),sub_700970这些函数是对系统API的封装,使得可以在Java代码中调用系统API,而退出程序的代码逻辑位于Java代码中。

  既然如此,那肯定有个地方存放着Java字节码,看一看安装的文件,只有BDPHash.bin文件有可能。暂时将BDPHash.bin移走,程序果然报错了,报错判断静态存储区dword_B3811C的字符串是否为空,进一步跟踪dword_B3811C字符串来源到sub_6EEEF0,动态调试这个函数可以发现,BDPHash.bin其实是一个加密过的jar文件,sub_7E1EE0应该为解密过程并将zip文件头信息加载到内存中,之后在sub_7E1C60->sub_7A1F10中通过CZarchive类获取压缩包中文件名为version.txt的文件内容。通过zip文件头信息也可以看到典型的jar目录结构与许多.class文件。但是这条路再往下走就很麻烦了,要么得完全破解了BDPHash.bin的加密,修改Java的.class文件后构造一个新的BDPHash.bin,要么得写一堆代码dll注入进去,绝不是几个patch能解决的,只能等我以后有时间再研究了。

0x03 部分破解

  调试过程中我也发现,当程序初始化结束后,即使暂停了__threadstart@4线程似乎程序也能够正常运行,那我直接干掉这个线程可不可以呢。要干掉这个线程就不能让这个线程阻塞住其他线程的运行,于是我通过动态调试在sub_7E7EE0中找到了该线程下的SetEventWaitForSingleObject,并且在主线程中的sub_7E4FA0找到了对应的SetEventWaitForSingleObject

  往下叙述前先插叙一下之前提到的“Your License is invalid, Error code 2”错误弹框,和之前一样通过对MessageBoxA断点可以很容易发现封装的通用弹框函数sub_6E7D20位于sub_6EF700中,而sub_6EF700WNDCLASSEXA结构的lpfnWndProc参数,为WindowProc回调函数。弹框位置对应的uMsg参数为0x113,在WinUser.h中可以看到#define WM_TIMER 0x0113,即对应计时器消息,对应的uIDEvent参数为7。sub_432800sub_7E5110返回的对象中获取许可证状态,返回0为未获取到状态,1为正常状态,其他为错误状态。此处有时返回2,有时返回82,返回82时进入default不会弹错误框,而返回2则会通过sub_6E7D20弹框退出程序。而在sub_7E5110中正是调用了sub_7E4FA0获取返回对象,这说明这里许可证状态真正的获取逻辑应该也是在__threadstart@4线程的Java代码中。

sub_6EF700函数反编译C代码(uMsg参数为0x113uIDEvent参数为7的区域)

  为了干掉__threadstart@4这个线程,我做了两处修改,一个是把sub_7E8910函数中的call _beginthread改成mov eax, 0来跳过__threadstart@4的线程创建,另一个是把主线程中的sub_7E4FA0直接改成返回0防止主线程被阻塞住。这样之后虽然放CD和DVD没有问题,但是放蓝光光盘就报错,我推测BDPHash.bin这个文件中的Java代码就是用来处理蓝光加密的,而__threadstart@4这个线程正是用来执行BDPHash.bin中Java代码的,因此这个线程没了的话就没办法处理蓝光了。我现在知道为什么网上找的破解版没办法处理蓝光光盘了,我怀疑他根本就没破解蓝光的部分,直接把这部分代码bypass了。至于为什么好端端的C++程序中又套了一个Java,我猜测应该不单单是为了防破解,更重要的原因是蓝光中使用的某些加密技术只能用Java来进行破解,因此在程序中又搞了个Java虚拟机来处理这部分的加密,而这又顺便增加了软件破解的难度。

0x04 无限试用许可证

  试用许可证根据电脑的硬件信息记录在AnyDVD的服务器上,每次启动AnyDVD时都会从服务器获取许可证信息,每次放入光盘也会连接服务器进行许可证验证,如果网络连接出错则会删除本地的许可证,但是服务器上会一直保存着许可证信息,如果没有过试用期限,下次联网时仍能够获取到之前的许可证进行使用。毫无疑问,网络通讯的内容也是加过密的。

  分析加载的dll发现,程序似乎是用了Win32 API wininet.h中的函数进行网络连接,那对HttpSendRequestA函数进行断点即可。之后动态调试可以发现HTTP请求过程位于sub_77EDE0线程的sub_77E3B0函数中,该函数应该为封装的通用HTTP请求函数,而sub_77EDE0线程应该是专门用来处理网络连接部分的逻辑的。

  请求试用许可证的sub_77E3B0调用发生在sub_780DA0中,跟踪lpOptional参数来源可以发现,HTTP body的内容包括了硬盘名称、硬盘序列号、机器ID、机器Hash等等内容,并且最初来源似乎是一个双向循环链表,链表的指针位于静态存储区的dword_D079A0。所以接下来就要找到链表节点是从哪里添加的,由于这里存在大量类和结构体,单单看已经很难分析了,于是需要添加一些结构体来美化反编译的C代码,具体添加的结构体和繁琐的分析过程就略去不讲了,这里只讲一讲结果。

  sub_6EF6D0这个函数用来执行用户点击获取许可证后的逻辑,sub_6EA740用来获取各种电脑硬件信息并存入链表,sub_780DA0来将链表内容转化为HTTP body数据并进行加密后发送请求。

sub_6EF6D0函数反编译C代码

  通过多次尝试发现,只要修改链表id(链表节点结构偏移量12的数据)为200的节点数据就可以获取到新的试用许可证。跟踪其来源发现该数据获取位于sub_76F000函数中,动态调试这个函数可以发现,程序获取了注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid的值并在sub_7DC760中对其进行了Hash计算,链表id为200的数据就是MachineGuid的Hash值。因此我推测,试用许可证就是依靠MachineGuid做判断的,只要每次使用不同的MachineGuid就可以获取到无限的试用许可证。当然直接改注册表里MachineGuid值的话对系统影响比较大,因此我采用的方式是对sub_76F000函数中计算出的Hash值进行patch,对其直接加一个数,之后等许可证到期了再换一个数,这样就可以一直用试用许可证了。

sub_76F000函数反编译C代码

0x05 进一步要做的

  到这里其实我已经达到我最开始的目标了,我可以不用再找新电脑来拷贝我的蓝光光盘了。不过我仍然没有对AnyDVD HD进行完全的破解,各种内容的加密都没有搞清楚。并且现在用试用许可证,每次加载蓝光光盘的时候都必须能够连接到其服务器才能进行蓝光的解密,那万一某天服务器挂了的话软件还是用不了。我其实很想知道联网对于蓝光光盘的解密是不是必须的,连接服务器究竟仅仅是验证许可证还是从服务器获取了必要的解密数据,这些还需要进一步研究才能够得出答案。

  因此,我后续的目标是希望做到下面的完全破解:

  • 搞清楚以.ki开头的段的加解密方式以及密钥
  • 搞清楚BDPHash.bin文件的加解密方式以及密钥
  • 搞清楚HTTP消息的加解密方式以及密钥
  • 搞清楚代码中常量字符串的加解密方式
  • 在Java代码中对程序进行完美破解

可以看到其实可研究的东西还有非常多,等我有空了慢慢研究。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注