实验操作参考《C++黑客编程揭秘与防范》(作者: 冀云)
对于软件的破解来说,主要是取消软件中的各种限制,如,比如时间限制、序列号限制……对于破解来说,无疑与逆向工程有着密切 的关系,想要突破任何一种限制都要去了解该种限制的保护方式或保护机制。
EasyCrackMe程序
根据《C++黑客编程揭秘与防范》一书中提供的EasyCrackMe程序,进行“破解”。
从上图中可以看出,整个程序只有两个可以输入内容的编辑框,和两个可以单击的按钮,除此之外什么都没有了,更不会有什么提示。在界面上输入一个账号和密码,单击“确定”按钮后,执行生成密码的算法,如果密码匹配则提示“密码正确”,否则提示“密码错误”。生成密码算法是通过输入的账号来生成密码的,而不是有固定的账号和固定的密码进行一一对应的。(账号的长度必须大于7位)
测试一下,输入一个小于7位的账号,再随便输入一个密码,单击“确定”按钮,这时候程序不会有任何反应。那么,输入一个超过7位的账号,单击“确定”按钮来试试,如下图所示。
EasyCrackMe提示“密码错误”。当然了,密码是根据账号算出来的,而且和账号的长度是相等的。那么该如何获得这个EasyCrackMe的密码呢?如果这个EasyCrackMe不是我们写的,那我们该怎么办?接下来的工作,就交给OllyDbg来完成吧。
OllyDbg是一种强大的Win32调试工具,用户界面直观、简洁,支持插件扩展功能,用户可以免费下载。它体积小,运行速度快,很多逆向分析人员都喜欢使用。
用OD破解EasyCrackMe
对于破解来说,总是要找到一个突破点的,而对于这个简单的EasyCrackMe来说,突破点是非常多的。下面会以不同的方式来开始这次破解之旅。
破解方法一
用OD打开EasyCrackMe
首先来梳理一下思路。输入了“账号”及“密码”后,首先程序会从编辑框处获得“账号”的字符串及“密码”的字符串,然后进行一系列的比较验证,再通过“账号”来计算出正确的“密码”,最后来匹配正确的“密码”与输入的“密码”是否一致,根据匹配结果,给出相应的提示。
上面是EasyCrackMe代码的流程和思路,我们也可根据这个思路合理地设置断点(设置断点也叫下断点)。“断点”就是产生中断的位置。通过下断点,可以让程序中断在需要调试或分析的地方。下断点在调试中起着非常大的作用,学会在合理的地方下断点也是一个技巧性的知识。
现在就可以选择合适的地方设置断点了。可以在API函数上设置断点,比如在GetDlgltemText()行设置断点,也可以选择在strlen()上设置断点,还可以在strcmp()上设置断点,甚至可以在MessageBox()上设置断点。上面的这些API函数都是可以设置断点的,但是对于GetDlgItemText()和MessageBox()这两个API函数来说,需要下断点的时候指定是ANSI版本,还是UNICODE版本。也就是说系统中是没有这两个函数的,根据版本的不同存在系统的函数只有GetDlgltemTextA()、GetDlgltemTextW()、MessageBoxA()和MessageBoxW()这样的函数。通常使用ANSI版本的即可。
上面有如此多的API函数可供我们设置断点,那么要选择哪个进行设置呢?最好的选择是strcmp()这个函数,因为在比较函数处肯定会出现正确的“密码”。而在GetDlgltemTexA()和strlen()上设置断点,需要使用F8进行跟踪。如果在MessageBoxA()上设置断点,那么就不容易找到正确的“密码”存放的位置了。所以选择在strcmp()上设置断点。在“命令”窗口中输入“bp strcmp”,然后回车,如下图所示。
如何知道断点是否设置成功呢?按下Alt+B组合键打开断点窗口可以查看,如下图所示。
断点已经设置好了,按F9键运行程序。EasyCrackMe启动了,输入“账号”为“UserTest”,然后随便输入一个“密码”为“123456”,单击“确定”按钮,OD中断在断点,如下图所示。
从上图中可以看到,OD断在了strcmp这个函数的首地址处,地址为10218B80。断在这里如何找到真正的密码呢?其实在提示的地方已经显示出了正确的密码,我们可以看栈窗口。函数参数的传递是依赖于栈的,对于C语言来说,函数的参数是从右往左依次入栈的。
对于strcmp()函数来说,该函数有两个参数,这两个参数分别是要进行比较的字符串。那么在栈窗口中可以看到输入的密码及正确的密码,如下图所示。
可以看到,在调用strcmp()时,传递的两个参数的值分别是“123456”和“VtfsUftu”两个字符串。前面的字符串肯定是输入的密码,那么后面的字符串肯定就是正确的密码了。现在关闭OD,直接打开EasyCrackMe,现在仍然用刚才的账号“UserTest”,然后输入密码“VtfsUftu”,单击“确定”按钮,会提示“密码正确”,如下图所示。
破解方法二
在上一种方法中通过对API函数设置断点找到了正确的密码,现在通过提示字符串来完成破解。在不知道正确密码的情况下输入密码,通常会得到的提示字符串是“密码错误”,只要在程序中寻找该字符串,并且查看是何处使用了该字符串,那么就可以对破解起到提示性的作用。
用OD打开EasyCrackMe程序,在反汇编界面处单击鼠标右键,在弹出的菜单中依次选择“超级字串参考”->“查找ASCII”命令,会出现“超级字串参考”窗口。
在“超级字串参考”窗口中,双击“密码正确”这个字符串
从上图中可以看到3处比较关键性的内容,第一个是可以看到strcmp()函数,第二个是可以看到字符串“密码正确”,第三个是可看到字符串“密码错误”。由这3个内容可以让我们联想到这岂不是和C代码基本上是对应的啊。根据strcmp()的比较结果,if……else…..会选择不同的流程进行执行。也就是说,只要改变比较的结果,或者更换比较的条件,都可以改变程序的流程。
strcmp()是字符串比较函数,如果两个字符串相等也就是说输入的密码与正确的密码匹配,匹配则执行“密码正确”流程,否则反之。那么如果修改一下比较的条件,也就是说两个密码匹配不成功,使其执行“密码正确”的流程。这样,输入错误的密码,也会提示“密码正确”的提示。
对于反汇编应该如何做呢?其实非常简单,我们再回过头来看一下上面截图中的那几条反汇编代码。想要修改其判断条件,那么只要修改00401EA8处的指令代码jnz short EasyCrac.00401EBD即可,该指令的意思是如果比较结果不为0,则跳转到00401EBD处执行。jnz结果不为0则跳转,只要把jnz修改为jz即可,jz的意思刚好与jnz相反。修改的方法很简单,选中00401EA8地址所在的行,按下空格键进行编辑
把jnz修改为jz
单击窗口上的“汇编”按钮,然后按F9键运行,随便输入一个长度大于7位的账号,再输入一个密码,然后单击“确定”按钮,会提示“密码正确”,如下图所示。
关掉OD和EasyCrackMe,然后直接运行EasyCrackMe,随便输入密码和账号,单击“确定”按钮后提示密码错误。为什么呢?因为刚才只是在内存中进行了修改,需要对文件进行存盘在以后运行时我们的修改才有效。
上面就是两种破解该EasyCrackMe的方法,这两种方法都是极其简单的方法,现在可能已经很不实用了。这里是为了学习,为了提高动手能力,而采用了这两种方法。