• 标 题:象棋桥2.1版(8千字)
  • 作 者:powerful
  • 时 间:2003-5-6 22:47:14
  • 链 接:http://bbs.pediy.com

朋友托我给他看一看一个名叫“象棋桥”的软件。下载,安装,运行,注册是用户名和注册码的形式,经过几个小时的努力,搞定。有些经验不敢独享,遂写篇文章和大家交流。

破解对象:象棋桥2.1版
下载地址:http://www.shareware.net.cn/download.asp?id={FD40E2F5-C31E-4A96-B31C-9D6EA03F08D8}
破解环境:win2000
使用工具:stud_pe.1.7.1,odbg109b,dede25,w32Dasm(20020828)

破解过程:
选用stud_pe看看是用什么加的壳,结果让高手(先声明我是菜鸟)大失所望,没加壳,是用delphi写的,知道用什么工具了吧,Yes!DEDE啦。(别高兴太早,继续看后面!)
先运行程序,点注册,弹出注册窗口,在里面随便输入用户名,注册码,弹出对话框说“注册码错误!”(当然是错啦!),但就是这个MessageBox框却成为我们查找真正注册码的突破点!

根据一般的经验我们可以这样做:
1.用静态反汇编的工具(如w32dasm,IDA等),将软件反汇编;
2.在反汇编的程序中查找相应字符串(如本例中的“注册码错误!”);
3.找到引用字符串的地方,然后记下地址,用调试工具(如odbg,trw等)加载原程序;
4.在刚才的记下的地址处下断点,使程序运行,输入测试的注册码,程序就会断在断点处;
5.这时我们就可以在这个地方附近分析加代码,一般的情况下程序都是根据根据你输入的用户,运算出一个正确的注册码,拿来和你输入的注册码比较,如果错误就显示出错对话框;
6.我们就可以利用原程序来产生真正的注册码,而不必弄清楚其加密算法,就可以得到注册码。

最浪费时间的是在第5步的,我们只是断在了此处,只是意味着程序判断出你输入的注册码与原注册码不符,所以跳转到这里来显示错误对话框的。因此,我们要沿着这个地址向上探所,不时地下断点,反复地进行调试,这样才能找到真正的算法代码。
如果运气好的话,你在堆栈里找一找有没有可疑的字符串,尤其和你输入的注册码存放位置比较接近的地方,这个字符串很可能就是真正的注册码!
但大多数情况下还是要分析的,当然在查找真正算法代码的过程也是有技巧可循的,就是仔细地看一看寄存器所指的内存的字符串,堆栈中所存的字符串的地址等。如果用的是odbg的话,它会及时明显地指示出来的,你只要稍加注意就行了。如果在某处发现了你输入的用户名时就要倍加注意了,这时很可能程序已经开始了运算,一步步向下调试,可能就会发现运算出来的真正注册码了!尤其发现两个字符串和指针(其中一个是你输入的测试注册码)放在两个寄存器中,然后进入一个CALL的时候,有极大的可能另外那个字符串就是真正的注册码!

按照上面的思路我开始了对破解对象的调试。因为我已经测出是Delphi写的软件,所以不用wdasm了,直接用DeDe来反汇编就更方便。拿来DeDe,转储成功!在窗体中找疑似对象(可不是非-典哦......),发现了一个RegFrm,看到它还包含了很多控件,有tbutton1和tbutton2两个控件,无疑一个是OK,一个是CANCEL。再看看RegFrm的程序段中,有没有TbuttonClick字样,居然没有?我晕,这怎么办?看起来用DEDE是找不到注册码的算法代码所在位置了,我换WDASM。
于拿来另外这件法宝WDASM,反汇编,用stringref来查找“注册码错误!”,居然也没找到?这怎么回事,看起来只有另想办法了!

凭经验,我们上面提到了,错误显示是一个MessageBox对话框。于是我们可以这样做,找到程序中所有引用MessageBoxA这个API的地方下断点,然后再利用上面的思路来找算法代码。
用odbg109b加载程序,完成后,在CPU DISASSEBLE窗口中右击,执行“Search For Name(label) in current module”,之后在显示的窗口中,我们找MessageBoxA这个label,找到两个,我们在第2个上面右击,执行“Set breakpoint on every reference",于是我们已经在每个MessageBoxA上都下了断点。然后[F9]执行程序,发生中断就按“Shift+F9”,让原程序运行。我们再点注册按钮,输入用户名和测试注册码,点确定,这下可好了,我们中断在一个MessageBoxA处:
005018C6  |.  50            PUSH EAX                                ; |hOwner
;下面就是中断地址
005018C7  |.  E8 985CF0FF  CALL <JMP.&user32.MessageBoxA>          ; \MessageBoxA
利用上面介绍的思路,我们沿着这个地址向上找,发现这个地址所在的代码区是一个子程序:
0050187C  /$  55            PUSH EBP <----在这里右击看一看
0050187D  |.  8BEC          MOV EBP,ESP
0050187F  |.  83C4 F8      ADD ESP,-8
00501882  |.  53            PUSH EBX
00501883  |.  894D F8      MOV DWORD PTR SS:[EBP-8],ECX
00501886  |.  8955 FC      MOV DWORD PTR SS:[EBP-4],EDX
00501889  |.  8BD8          MOV EBX,EAX
0050188B  |.  8B45 FC      MOV EAX,DWORD PTR SS:[EBP-4]
0050188E  |.  E8 F127F0FF  CALL CCBridge.00404084
00501893  |.  8B45 F8      MOV EAX,DWORD PTR SS:[EBP-8]
00501896  |.  E8 E927F0FF  CALL CCBridge.00404084
0050189B  |.  33C0          XOR EAX,EAX
0050189D  |.  55            PUSH EBP
0050189E  |.  68 E9185000  PUSH CCBridge.005018E9
005018A3  |.  64:FF30      PUSH DWORD PTR FS:[EAX]
005018A6  |.  64:8920      MOV DWORD PTR FS:[EAX],ESP
005018A9  |.  8B45 08      MOV EAX,DWORD PTR SS:[EBP+8]
005018AC  |.  50            PUSH EAX
005018AD  |.  8B45 F8      MOV EAX,DWORD PTR SS:[EBP-8]
005018B0  |.  E8 DF27F0FF  CALL CCBridge.00404094
005018B5  |.  50            PUSH EAX
005018B6  |.  8B45 FC      MOV EAX,DWORD PTR SS:[EBP-4]
005018B9  |.  E8 D627F0FF  CALL CCBridge.00404094
005018BE  |.  50            PUSH EAX
005018BF  |.  8BC3          MOV EAX,EBX
005018C1  |.  E8 DA88F3FF  CALL CCBridge.0043A1A0
005018C6  |.  50            PUSH EAX                                ; |hOwner
005018C7  |.  E8 985CF0FF  CALL <JMP.&user32.MessageBoxA>          ; \MessageBoxA
005018CC  |.  8BD8          MOV EBX,EAX
005018CE  |.  33C0          XOR EAX,EAX
005018D0  |.  5A            POP EDX
005018D1  |.  59            POP ECX
005018D2  |.  59            POP ECX
005018D3  |.  64:8910      MOV DWORD PTR FS:[EAX],EDX
005018D6  |.  68 F0185000  PUSH CCBridge.005018F0
005018DB  |>  8D45 F8      LEA EAX,DWORD PTR SS:[EBP-8]
005018DE  |.  BA 02000000  MOV EDX,2
005018E3  |.  E8 8C23F0FF  CALL CCBridge.00403C74
005018E8  \.  C3            RETN
我们可以预计是系统判断完注册码之后就会调用这个过程的。于是我们只要找到是哪段代码调用了这个过程就可以了,于是我们到这个过程的首地址处:0050187C  /$  55            PUSH EBP,在这里右击,执行goto,看一看下面,有很多的CaLL From XXXXXXXX,这么多的代码调用这个过程,我们怎么知道哪一个是注册码算法啊?我晕......
别着急,[F2]我们先在这里(0050187C)下个断点。

****:用Alt+F2关闭程序,再用Ctrl+F2重新装载程序。[F9]执行程序,发生中断就按“Shift+F9”,让原程序运行。我们再点注册按钮,输入用户名和测试注册码,点确定。

我们中断在0050187C处,这时我们看CPU窗口的右下角的堆栈窗口中显示:
0162FBF8  0051E61A  RETURN to CCBridge.0051E61A from CCBridge.0050187C
于是我们可以判断在地址0051E615(call XXXXXX是6个字节,0051E615=0051E61A-6)处有个CALL CCBridge.0050187C。于是我们转到0051E615处,果然不出我们的所料。
0051E5FA  .  8B0D A4655300 MOV ECX,DWORD PTR DS:[5365A4]            ;  CCBridge.00546B7C
0051E600  .  8B89 4C020000 MOV ECX,DWORD PTR DS:[ECX+24C]
0051E606  .  8B15 A4655300 MOV EDX,DWORD PTR DS:[5365A4]            ;  CCBridge.00546B7C
0051E60C  .  8B92 48020000 MOV EDX,DWORD PTR DS:[EDX+248]
0051E612  .  8B45 FC      MOV EAX,DWORD PTR SS:[EBP-4]
0051E615  .  E8 6232FEFF  CALL CCBridge.0050187C <----------------预料结果
0051E61A  >  33C0          XOR EAX,EAX
我们沿着这个地址向上找,我们发现了:
0051E3EB  .  BA 70E65100  MOV EDX,CCBridge.0051E670                ;  ASCII "CCB21R-"
这个地址引用了字符串"CCB21R-",我这就是注册码开始的几个字符。于是[F2]在这里下断点,重复上文“****”处的操作,这时我们在注册码的编辑框处要输入如“CCB21R-12345"形式的注册码,于是我们中断在:
0051E3EB  .  BA 70E65100  MOV EDX,CCBridge.0051E670                ;  ASCII "CCB21R-"
0051E3F0  .  E8 EB5BEEFF  CALL CCBridge.00403FE0
0051E3F5  .  75 1E        JNZ SHORT CCBridge.0051E415
0051E3F7  .  8D55 E4      LEA EDX,DWORD PTR SS:[EBP-1C]
0051E3FA  .  8B45 FC      MOV EAX,DWORD PTR SS:[EBP-4]
0051E3FD  .  8B80 70090000 MOV EAX,DWORD PTR DS:[EAX+970]
0051E403  .  E8 5462F6FF  CALL CCBridge.0048465C
0051E408  .  8B55 E4      MOV EDX,DWORD PTR SS:[EBP-1C]
0051E40B  .  8B45 F0      MOV EAX,DWORD PTR SS:[EBP-10]
0051E40E  .  E8 CD5BEEFF  CALL CCBridge.00403FE0 <----这里便是真正注册码与我们输入的注册码比较处
我们用[F8]继续一走,一直到0051E40E处,这时我们看到:
EAX指向了一个字符串“12345” <--这不是我们输入的注册码的后几位吗?
DEX指向了一个字符串“15168” <--这就是根据我们输入的用户运算后的真正注册码了!!
(CCB21R-15168,和我输入的用名试验一下果然正确,要问我的注册名是什么,她是我老婆的中文名,是什么?可不能告诉你哟......)

明白了吧,想做内存注册机的,就可以断在这里啦!
如果想做个算法注册机,就要分析上面这段代码哦!

注意:我为了简化写作,略去了在不知注册码形式下试验的过程,注册码的形式是:“CCB21R-”+5个数字。

总结:1.odbg109b是一个十分强大的工具,其中有很多功能需要我们去开发;
    2.写这篇冗长的文章主要是给大一个分析的思路,就是要掌握设置断点的技巧;
    3.更不要忽略堆栈中的内容。
声明:本篇只是做为技术交流,别无他意!有意转载,请保持原文的完整性!谢谢观赏!
by powerful 2003.5.6