【破文标题】CRACKME破解分析
【破文作者】逍遥风
【破解工具】OD,计算器
【破解平台】WINXP
【软件简介】Difficulty: 2 - Needs a little brain (or luck)
Platform: Windows
Language: Assembler
----------------------------------------------------------------------
CRACKME很有意思,第一次见如此注册方式的CRACKME.分析一下,
根据提示信息很容易找到关键代码。
00401288 /$ 55 push ebp ; 在这里下断点
00401289 |. 8BEC mov ebp, esp
0040128B |. 81C4 FCFEFFFF add esp, -104
00401291 |. C785 FCFEFFFF>mov dword ptr [ebp-104>
0040129B |. 68 80000000 push 80 ; /Count = 80 (128.)
004012A0 |. 8D45 80 lea eax, [ebp-80] ; |
004012A3 |. 50 push eax ; |Buffer
004012A4 |. 68 ED030000 push 3ED ; |ControlID = 3ED (1005.)
004012A9 |. FF35 0C304000 push dword ptr [40300C] ; |hWnd = 00190644 ('b2c_2k5',class='DLGCLASS')
004012AF |. E8 3A010000 call <jmp.&user32.GetDl>; \GetDlgItemTextA
004012B4 |. 83F8 05 cmp eax, 5 ; 注册名位数与5比较
004012B7 |. 0F82 C7000000 jb 00401384 ; 小于5位就跳向失败
004012BD |. 68 80000000 push 80 ; /Count = 80 (128.)
004012C2 |. 8D85 00FFFFFF lea eax, [ebp-100] ; |
004012C8 |. 50 push eax ; |Buffer
004012C9 |. 68 EE030000 push 3EE ; |ControlID = 3EE (1006.)
004012CE |. FF35 0C304000 push dword ptr [40300C] ; |hWnd = 00190644 ('b2c_2k5',class='DLGCLASS')
004012D4 |. E8 15010000 call <jmp.&user32.GetDl>; \GetDlgItemTextA
004012D9 |. 83F8 08 cmp eax, 8 ; 注册码位数与8比较
004012DC |. 0F85 A2000000 jnz 00401384 ; 注册码不等于8位就跳向失败
从这里可以得知:
注册名必须大于5位
注册码必须等于8位
004012E2 |. 8D95 00FFFFFF lea edx, [ebp-100] ; 使EDX等于输入的注册码
004012E8 |. 52 push edx
004012E9 |. E8 82010000 call 00401470 ; 将输入的注册码转换成对应的大写
004012EE |. 50 push eax
004012EF |. E8 92000000 call 00401386
004012F4 |. 83F8 00 cmp eax, 0 ; 输入注册码了吗?
004012F7 |. 0F84 87000000 je 00401384 ; 没有输入注册码就跳向失败
004012FD |. 33D2 xor edx, edx ; EDX清零,准备开始计算
004012FF |. 35 33644752 xor eax, 52476433 ; XOR (输入的注册码,0x52476433),结果设为A
00401304 |. 35 56244752 xor eax, 52472456 ; XOR (A,0x52472456),结果设为B
00401309 |. 2D 00000004 sub eax, 4000000 ; B减去0x4000000结果设为C
这是对注册码的处理,先将注册码中小写的部分转换成对应的大写形式。
设输入的注册码为;CODE
则对注册码的处理过程为:XOR [XOR(CODE,0x52476433),0x52472456],
作者:逍遥风
因为XOR(0x52476433,0x52472456)=0x4065
所以:对注册码处理的过程就相当与 XOR (CODE,0x4065)结果设为B
C=B-0x4000000
0040130E |. BB 01000000 mov ebx, 1 ; 使EBX等于1
00401313 |. C1C3 10 rol ebx, 10 ; ROL (EBX,10)
00401316 |. 8D75 80 lea esi, [ebp-80] ; 使ESI等于注册名
00401319 |> 803E 00 /cmp byte ptr [esi], 0
0040131C |. 74 0D |je short 0040132B
0040131E |. 0FB616 |movzx edx, byte ptr [esi] ; 取注册名每一位的ASCII码
00401321 |. C1C2 10 |rol edx, 10 ; ROL (注册名ASCII,10)
00401324 |. 03C2 |add eax, edx ; C与注册名ASCII码累加计算
00401326 |. 2BC3 |sub eax, ebx ; 每一次计算的结果减去定值0x10000
00401328 |. 46 |inc esi ; 每计算一次ESI加1
00401329 |.^ EB EE \jmp short 00401319 ; 循环计算
0040132B |> 8BD8 mov ebx, eax ; 使EBX等于计算结果,结果设为D
0040132D |. C1CB 10 ror ebx, 10 ; ROR(计算结果,10)
00401330 |. 66:81FB 90C3 cmp bx, 0C390 ; 计算结果的低位与0x0C390相比较
00401335 |. 75 4D jnz short 00401384 ; 不相等就注册失败
第一个关键的地方。在这里程序把上一步计算结果与注册名的ASCII码累加,并减去定值。
将最后计算结果的低位与定值0x0C390比较,如果不相等就跳向失败。
所以现在就要根据0x0C390这个值来逆推出注册码的相关部分。
先简化一下计算过程,理清一下思路:
取计算结果C,以注册名lovetc为例
程序的计算过程是
C+6C0000-10000+6F0000-10000+。。。+630000-10000
把它简化,那么就是:C+6C+6F+76+65+74+63-6=C390
现在求C
C+28D-6=C+287=C390
C390-287=C=C190
则C=C190,回归到程序中,C的形式就应该为C190****(8位)
联系上文,看看C是怎么得来的
XOR(A ,0x4065) = B
B - 0x4000000 = C
现在根据C就可以求出B了,简化后的过程就为
B - 400 = C,所以B = C + 400 = C190 + 400 = C590
所以B的形式就应该为:C590****(8位)
现在推倒一下A(即CODE注册码)的情况,但是我们现在并不知道B到底等于什么。
但是注意一下,如果要根据B来求A,就要 XOR(B ,0x4065) = A
B具体等于什么并不清楚,但是B是一个8位数,4065只有4位,这两者XOR运算的结果只会在B的后4位发生变化。
即:
C509XXXX XOR 4065 = (C509)(ABCD)
C509YYYY XOR 4065 = (C509)(EFGH)
| |------------------X,Y的值只影响这部分
|-------------------不论X,Y等于什么,这部分不变
所以就可以推出
注册名:lovetc
对应注册码的形式应该为:C509****
00401337 |. 8985 FCFEFFFF mov [ebp-104], eax ; 保存累加计算的结果D
0040133D |. B9 04000000 mov ecx, 4 ; 使ECX等于4
00401342 |. 33D2 xor edx, edx ; EDX清零
00401344 |. 33DB xor ebx, ebx ; EBX清零
00401346 |> 83F9 00 /cmp ecx, 0 ; 计算完了吗?
00401349 |. 74 0A |je short 00401355 ; 计算完就跳走
0040134B |. 8AD8 |mov bl, al ; 两位两位取计算结果D
0040134D |. 03D3 |add edx, ebx ; 累加,计算结果设为E
0040134F |. C1C8 08 |ror eax, 8 ; 取下一位
00401352 |. 49 |dec ecx ; 每计算一次ECX减1
00401353 |.^ EB F1 \jmp short 00401346 ; 循环计算
00401355 |> 81FA 85020000 cmp edx, 285 ; 计算结果与0x285比较
0040135B |. 75 27 jnz short 00401384 ; 不相等就注册失败
这里,程序对上一步的计算结果D,进行累加计算。
也就是说,我们以输入的注册码,C5091234为例,上一步将C5091234进行计算的结果为C3905251
在这里将C3905251进行计算的过程就是
E=C3+90+52+51 = 1F6
但是,程序要求E必须等于285。又要开始逆推了。
C3和90已经可以看作是定值了,C3+90=152
因为:E=C3+90+XX+YY=285 所以XX+YY=285-152=132,即后两位相加的和必须为132
现在根据这个条件可得到很多结果,任何选一组:BB + 77 = 132
所以这时:D = C390BB77,
那么 XOR (BB77,4065)= FB12 ,所一个可能的注册码就是:C509FB12
重新载如,看还有什么要求,经过了重复的步骤,现在来到这里
0040135D |. 8D9D FCFEFFFF lea ebx, [ebp-104]
00401363 |. FFD3 call ebx ; 关键CALL,跟进
00401365 |. 83FF 00 cmp edi, 0 ; EDI与0比较
00401368 |. 75 1A jnz short 00401384 ; EDI不等于0就跳向失败
到底在这里做什么呢?跟进关键CALL看看。
0012FB9C ^\77 BB ja short 0012FB59 ; 注意这里
|―|――――――77BB?巧合?与上一步任意取得值77+BB=132有关吗?
0012FB9E 90 nop
0012FB9F C3 retn
为了证明这里的77BB与上一步计算所取的值77+BB=132是否有关,再选一组相加等于132的值
CC+66=132。转换回要输入的注册码就是C5098C03。再次来到关键CALL
这次,关键CALL的内容发生了变化
0012FB9C 66:CC int3
|―|―――――――――――― CC+66=132
0012FB9E 90 nop
0012FB9F C3 retn
现在就可以看出,关键CALL的第一步如何去做是由我们选定的值决定的。联系关键CALL后面的代码
00401365 |. 83FF 00 cmp edi, 0 ; EDI与0比较
00401368 |. 75 1A jnz short 00401384 ; EDI不等于0就跳向失败
可以知道,程序要求在经过关键CALL后,EDI的值必须为0。所以,关键CALL的作用就是使EDI=0
联系赋值操作,和寄存器清零操作,想想使EDI等于0有哪些方法?
1)mov edi,0 这样代码就是BF00 可是BF+00不等于132
2)xor edi,edi 这样代码就是33FF,33+FF = 132
呵呵~!看到曙光了
即满足相加等于132,又满足可以使EDI等于0
所以,FF33就是我们需要的值,将他转换成要输入的注册码
(C509)[XOR(FF33,4065)]
| |
固定值 BF65
所以注册码就是C509BF65,即
注册名:lovetc
注册码:C509BF65
----------------------------------------------------------------------
验证方式另类的CRACKME
有趣的分析过程,佩服作者的智慧
呵呵
----------------------------------------------------------------------
【版权声明】本文只为交流,转载请保留作者及文章完整性
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)