发表此文的目的,在于通过破解通达OA短信服务器这个具体实例,来分享破解软件的一般思路及定位关键代码段几个常用的方法,帮助对逆向工程领域有初步了解的朋友,在具备一定理论的基础上,加强实践分析的能力。本人在该领域也是初学者,本文所有观点仅为本人对现阶段所掌握知识理解之上的个人看法,在用词表达和思路方法上会有不妥之处,望大家能够及时指正!
程序都有自己执行的流程,需要注册的软件也一样,当你输入正确的注册码时,会执行一种流程,当注册失败时,又会执行另外的流程,那么破解软件的关键就在于对流程的分析,并不见得是对整个程序流程的分析,只要我们找到关键的流程,修改关键代码段,最终使之按着注册成功的流程去执行,破解就基本大功告成了!那么怎么去找关键流程,又怎样去修改关键代码段呢?下面我们就通过对通达OA短信服务器(以下简称OA)程序流程的分析,去一一阐述。
OA运行时,会提示输入管理员口令,默认口令为空,直接点确定即可,图1是程序主界面。
图1
该软件未注册,红箭头指向的是未注册信息。
点击立即注册,弹出图2对话框。
图2
通过尝试,可以发现,软件序列号可以是任意字符串,但对字符串长度有要求,当长度<=15位时,会弹出图3所示的对话框。
图3
当序列号字符串长度>=16位时,会弹出图4所示的对话框。
图4
由此可以判断,合法序列号为>=16位的字符串,注册码则应该是将机器码和序列号通过某种算法得到的一串字符。我们在注册码栏任意输入几个字符,点击确定看会有什么提示,如图5所示。
图5
分析:
通过对软件注册机制的黑盒测试分析,破解该软件我们目前大致有两条思路:
1、通过反汇编定位到注册码的算法函数,分析注册码的算法,然后自己写程序去实现该算法,从而得到注册码。
2、当我们输入序列号,注册码,点击确定的时候,程序必然要根据机器码和输入的序列号判断注册码是否正确,程序怎样判断注册码是否正确呢?可以猜测,程序先根据机器码和我们输入的合法序列号,通过某种算法,得出正确的注册码,然后再和我们输入的注册码比较,如果相等,注册成功,否则,注册失败!由此可以推测出,当我们点击确定的时候,正确的注册码已经存在于某个函数的变量里,我们只要想办法让它显示出来即可。
上述的第一个思路需要较强的分析汇编代码的能力,对我们初学者来说,不是一件简单的事。第二个思路我们只需把握关键流程即可,不需分析所有的细节。在这里我们采取第二个思路。现在我们面对的问题是,怎样找到关键的流程,即怎样定位到关键代码段。
关于定位问题,有很多种解决办法:
1、我们可以通过寻找软件运行过程中显示的字符串,找到关键代码。例如我们想定位到调用图5所示对话框的函数的位置,我们可以通过寻找“注册失败:”字符串,就可以很容易找到调用该对话框的函数的位置。
2、也可以在关键API函数处下断点,通过单步运行,当API函数返回后,即定位到了调用该API函数的父函数位置。
3、还有其他定位的方法,例如设置消息断点等。
总之,上述三种方法都离不开一条思路,即
把握程序的流程,在流程中寻找关键点,无论是程序可能经过的地方(系统API,消息处理函数等),或者程序在某个地方留下的痕迹(关键字符串等),都可以成为我们定位的依据。
有了破解思路,也知道了怎么定位,那么现在就开始对程序解剖吧!这里我们用IDA对MYOASAS.exe反汇编。首先是定位,定位到哪呢?显然,应该找到验证注册码是否成功的代码段,我们可以通过寻找“注册失败:”字符串,来定位到关键位置。
图6
双击选中的字符串,可以找到引用该字符串的函数sub_408F30,如图7所示。
图7
继续双击红框位置,定位到sub_408F30函数代码段。通过IDA强大的图形界面分析sub_408F30的流程,可以断定,该函数就是验证注册码是否正确的关键函数。图8为该函数在IDA中Graph view的缩略图(英文字母及紫色方框是本人截图时画上去的,方便后面讲解引用),基于篇幅有限,没有显示汇编代码,这里只对函数的流程做粗略分析。A处判断序列号是否>=16位,即判断序列号是否合法,不合法的话,执行B流程,否则执行另一条流程;在C和D分支的上方最后几条指令是判断有无输入注册码,没有输入的话执行C流程,否则执行D流程;E和F上方,即紫色方框内,判断注册码是否正确,正确的话执行E流程,即注册成功,否则执行F流程,注册失败。通过B、C、D、E、F中出现的关键字符串,很容易得出以上判断。
由此可以猜测,程序根据机器码和序列号算出的注册码应该在紫色方框内的某个时机赋给某个变量,且在此之前会调用注册码算法。果然,在D处发现调用了sub_403520函数,通过分析该函数的伪代码,得知该函数有三个参数,其中有两个分别是机器码的偏移地址和序列号的偏移地址,该函数应该就是实现注册码算法函数,我们可以在此下一个断点,动态调试的时候,看看该函数返回前后堆栈的变化。如果我们分析正确的话,当函数返回后,堆栈中应该会出现注册码字符串。函数返回后堆栈的内容如图9所示。
图8
图9
当实现注册码算法的函数返回后,发现堆栈中0011AC98处存储了一个字符串的偏移地址,和var_1c相对应,通过分析sub_408F30函数的伪代码,可以断定,该字符串就是正确的注册码,程序就是拿这个字符串和我们输入的注册码相比较,如果一致,即注册成功。接下来的问题,就是怎样显示这个正确的注册码。
图8紫色方框里的指令执行完以后,程序会根据判断两个注册码是否一致,来决定走E流程还是走F流程。当我们不知道注册码的情况下,任意输入一个合法的序列号和一个任意的注册码,程序会走F流程,即提示注册失败,我们在F中发现以下指令:
图10
显然,sub_42802E调用了MessageBoxA函数,上面push的两个字符串偏移是作为参数传递给MessageBoxA,这样就会出现前面图5所示的对话框。如果我们把上述任意一个push的字符串偏移替换成前面我们找到的正确的注册码的偏移地址,当程序再次执行到此处的时候,岂不是就能通过MessageBox对话框显示出正确的注册码了!
我们在第二个push处下一个断点,当程序执行到此处的时候,再次观察堆栈的内容,如图11所示。
图11
ESP+18H存储的内容即为注册码字符串的偏移地址,我们只需将
push aVSZG 该为 push [esp+18h] 即可。
比较这两条指令的机器码分别为 68 BC 2D 44 00 和 FF 74 24 18 ,由于长度不一致,需将新指令结尾添加一个90(nop指令),即改为 FF 74 24 18 90 。当然,还需要计算出
push aVSZG 指令在磁盘exe文件中的偏移地址,再通过UtralEdit在该偏移处将68 BC 2D 44 00 替换为 FF 74 24 18 90 。
现在我们再次运行MYOASMS.exe,输入任意合法的序列号,再输入任意的注册码,看会有什么提示,如图12所示。
图12
正确的注册码显示了出来!该注册码是程序根据我们输入的合法序列号和机器码经过注册码算法函数(sub_403520)得出的,我们只需将该注册码记录下来,填在注册码栏,点击确定即可成功注册!如图13,图14所示。
图13
图14
或许有朋友会提出这样的破解方法:既然E和F分别是处理注册成功和注册失败的分支,那我们只需修改E和F之上的跳转指令不就可以了吗,要么将其改成和原来逻辑相反的跳转指令,要么将其改成nop,只要能实现当我们输入错的注册码时,它向E流程(注册成功)跳转即可。这的确是一个方法,但是这种成功只是个假象,如果程序执行到其他功能的时候,再次验证我们输入的注册码,那么这种方法就失败了。所以要使用这种方法,必须在所有验证的地方都实现逻辑逆转。
这个软件没有引入反破解的机制,还是比较容易分析的,比较适合初学者拿来练手(已上传至附件crack实验)。在整个分析的过程中,我们应该
重在掌握分析问题,解决问题的思路,而不仅仅在于掌握某个具体的技术,这样才能以不变应万变。希望这篇文章能够对同是初学者的朋友有所帮助。
这篇文章能够完成离不开一位前辈的指导,在此向他表示感谢!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课