用OllyDbg做破解
朋友所托,要帮忙破解一个MFC的小程序,他急等着用 (背景:几个人合伙创业,其中一个负责写这个有点小核心的项目,为了巩固自己的”地位“搞的小把戏,给加了密,要用必须通过他 - 我艹~~~)。
虽说自己搞C++比较多,相对来讲native一点,但是对于汇编与破解,了解相当有限,去年这朋友也找过我,因为当时刚换公司比较忙,是求助另外一好友才搞定的。这次还是自己花点时间研究研究吧。工具吗,用windbg貌似只能看,softice与ollydbg之间,选择了ollydbg。
一、基础知识
ollydbg是一个动态反汇编分析调试工具,其功能强大的令人发指,而且居然还是免费的,其官方地址为:http://www.ollydbg.de/,下载与介绍都在上面。
要用ollydbg做破解,最好先过一遍下面的知识点:
- 了解汇编语言的基本结构,特别是函数的调用与返回,参数传递等,可以参考这篇文章:反汇编深入分析函数调用, 简短却又清晰,切中要点,我配合visualstudio的反汇编窗口执行了几遍,基本掌握了其结构。(我自己也维护了一个分析:https://gist.github.com/2904354)
另外,Intel这三大卷:Intel® 64 and IA-32 Architectures Software Developer Manuals也是非常经典的参考资料,没必要读,但不理解时可以查查,尤其是第一卷中的基本结构介绍和第二卷的指令集介绍。 - OllyDbg入门完全教程(完美排版), 非常详尽的介绍了ollydbg的各个组成部分及功能,40来页的内容,花一个小时过一遍就可以了。
- OD入门系列图文详细教程-破解做辅助起步,通过一个例子详细解释了破解的全过程,非常有用。其中文中用到的看雪《加密与解密》书中的程序crackme可以在这里下载到:http://download.csdn.net/detail/tokey003/3672236
二、实例操作
先是一个非常简单的helloworld程序,源码如下:
1 #include <cstdio> 2 #include <windows.h> 3 4 #pragma comment(lib, "user32.lib") 5 6 void printHelloWorld(int a) 7 { 8 MessageBox(NULL, "Hello, World", "Greetings", MB_ICONWARNING); 9 } 10 11 int main() 12 { 13 bool bMsgBox = false; 14 if(bMsgBox) 15 { 16 printHelloWorld(10); 17 } 18 else 19 { 20 printf("Hello, World"); 21 } 22 }
该程序总是在console输出"Hello, World",现在要修改其binary,使其总是弹出一个MessageBox。
编译并使用ollydbg打开,因为我知道要传一个参数10到printHelloWorld函数中去,可以肯定会有一个指令:PUSH 0A,关键代码在其附近,在反汇编窗口搜索定位:
可以看到,在PUSH 0A前面是一个条件判断,对应源代码中的那个判断,于是不难推出,Mov BYTE PTR SS:[LOCAL.1+3],0就是给bool型变量bMsgBox赋值的指令,0就是false。
于是,在这里双击该指令,把0改成1,运行便看到程序弹出了messagebox。
注意,为了保存修改过的可执行文件,你需要:
- 右键-Edit-Copy to executable, 弹出该二进制文件的内容窗口
- 右键 - Save file
三、破解
需要破解的程序,其行为是:在任何一台机器上使用该程序,你需要输入一个授权码,该授权码由特定程序通过系统信息产生,其工作流程为:
- 在客户电脑上运行该程序,获取系统信息(一个字符串)
- 将该字符串发给授权人,他有一个工具会产生授权码,并发给使用者
- 使用者输入该授权码,程序验证,如通过则可使用。
单从行为上分析,突破口在于授权验证这一步,该程序要验证输入的授权码是否正确,也就是说它肯定知道正确的验证码,也就是说由系统信息产生授权码的算法也是内置在客户程序这边的,我们只不过需要一个办法把它读出来。
该验证步骤是:获取系统信息,并个根据该信息产生正确的授权码,然后从某文本文件读入用户输入的验证码,对两者进行对比,如果成功,则弹出一个messagebox:output(true), 否则就是output(false)。根据以上信息,设置以下断点:
- 菜单View-executable modules - show names,找到ReadFile和MessageBoxA,打上断点
- 在反汇编窗口右键,Search for - all referenced strings, 找到output字符串,并设断点
有了这几个断点,你就可以在运行中观察寄存器/栈/内存等内容,以及汇编指令来了解整个程序的运作机理。多走几遍,多观察思考,适当添加断点与注释。特别注意哪些条件测试指令与call指令,确定其行为后继续跟进去。
这样,就找到了其内部获取系统信息的函数调用,与根据系统信息算出授权码的函数调用,从而得到了正确的授权码,结束。
当然,要做的好一点的话,还可以:
- 分析其授权码产生函数的算法,自己写一个产生器
- 或者,动态修改其可执行文件,将其产生的授权码用一个MessageBox输出。
但对于第一项,这需要阅读很多汇编代码,我没时间;对于第二项,需要插入对windows API函数的调用,这比简单的修改一个指令要复杂,暂时还不会,也不打算仔细研究。毕竟,是朋友所托,完成任务即可,不打算仔细研究 - 我还有很多其他重要的事情要做。
四、技巧总结
- 你可以通过猜测可能调到的Windows API函数,设置断点作为突破口
- 你也可以通过可能reference到的字符串设置断点
- 查到指令也是一个方法,但估计不是很有用 - 在没有源代码的情况下你很难预测一个特殊的指令被调用到了
- 选中指令后,你可以右键-help on command来查到汇编指令帮助
- 你可以双击汇编指令进行修改
- 你可以添加注释 - 在一个漫长的破解过程中,记下你发现的是蛮重要的
- 选中指令后,你可以看到其跳转关系
- 我还很初级,这个强大的工具有更多技巧去发现。