下面的案例主要介绍反汇编的使用价值,即在软件加解密方面的应用,可以很好地体会到基本的汇编指令在实践中的应用,也可以认识到Ollydbg在软件调试过程中的威力。
选用的示例程序CRACKME.exe程序来自看雪论坛的“从零开始用ollydbg cracking”系列。本文只做学习交流用途,不作其他任何用途。
下面开工:
1. 采用ollydbg打开CRACKME.exe,命令:f3;
2. 注册的正确与否,程序会弹出窗口,窗口上会带有字符串,那我们这里就以正确与否的字符串作为线索;命令:右键->search for-> all referred strings;
3. 找到如下的string:Great work,mate! now try the next CrackMe!",并且双击进入,跳转到调用该字符串的地方;
4. 从下图可见,下面两个弹出窗口就是最后看到注册信息对错的提醒窗口,那么以这两个为线索,查看是从哪里跳转到这两个函数调用起始处的;
5. 鼠标停在push 30处,可以看到下面的观察窗口显示:local call from 0040124C;
也即意味着push 30处是由0040124C处跳转过来的;那么查看该处的指令;
6. 此处关键的指令就在401241处,通过该处指令的比较,来确定后面序列号的正确与否,再来选择后面的跳转。
7. 通过在CMP EAX, 0指令处打断点,我们F9运行程序,程序弹出注册窗口,我们输入:然后按ok后,整个程序就停在我们下断点的地方。如下图所示,
8. 根据右边的注释,上述的流程就很清晰,401228处的指令将用户名abc字符串地址压入,然后进行处理后,将结果保存在EAX中,后面的将序列号123也做同样的处理;可以猜测对于序列号操作的结果应该输出在EBX中,这样后面的CMP EAX, EBX这条语句才说得通;
9. 我们F7单步跟入40122D的函数调用,看到如下的处理过程:
简单跟踪下的话,可以发现上述的代码等同于下面的C语言代码:
int nameCrack(char* str) { char t; int sum = 0; while(*str) { int t = (int)(*str); if(t > 0x5A) sum += (int)(t-0x20); str++; } sum = sum ^ 0x5678; return sum; }
对于我们的输入abc,上述函数的输出为:0x56BE;
10. 同样地,我们对于401238处的指令也进行跟踪调试:
上述的代码等同于下面的C代码:
int numCrack(char* str) { int sum = 0; while(*str) { int c = (int)*str - 0x30; sum = 10*sum + c; str++; } return (sum ^ 0x1234); }
11.根据调试,EAX和EBX的值分别为:56BE和124F,两者不同,因此解密失败;为了让两者相同,我们保持abc不变,改变序列号来实现。
12. 56BE与0x1234亦或操作后的值的十进制表示为:17546。因此我们只要输入的字符串操作的值sum等于17546即可。又因为数字的ASSIC码是从”0x31”开始的,
而代码中有减去0x30的操作,因此我们输入的数字只要是17546即可。
注意事项:
1. 调试时请关闭杀毒软件的实时保护,否则,CRACKME.exe会被删除;
2. “从零开始用ollydbgcracking”系列教材中对该问题的分析是从API GetWindowTextA入手;具体分析上也是大同小异;
解密过程中的常用命令小结:
1. 定位使用的模块,ctrl + N;
2. 通过查看堆栈,找到GetWindowTextA使用的buffer的地址;
3. 采用Follow in dump找到buffer地址后,可以设定memoryon access类型的break point,这样一旦访问到buffer中的信息,就可以暂停,加速定位;