若说我图省事的,你们自己解的Crackme不也基本上都是菜鸟级的吗!有本事咱一块来逆向!
下载地址:http://www.crackmes.de/users/unicorn/crackme1/
使用工具:OllyDbg 这是一个VC(可能是吧,我没PEid过)写的Crackme,运行试注册,随便输个号,没有任何
提示便退出。用OllyDbg加载,找到一个关键字符串:
"Correct Serial Number !!!"
跟随这个字符串来到: =====================以下是代码===================
004013F3 /. 55 push ebp
004013F4 |. 8BEC mov ebp, esp
004013F6 |. 83EC 0C sub esp, 0C
004013F9 |. 56 push esi
004013FA |. 8D45 F4 lea eax, [ebp-C]
004013FD |. 6A 0B push 0B ; /Arg3 = 0000000B
004013FF |. 50 push eax ; |Arg2
00401400 |. 8BF1 mov esi, ecx ; |
00401402 |. 68 E8030000 push 3E8 ; |Arg1 = 000003E8
00401407 |. E8 69940000 call 0040A875 ; \Crackme1.0040A875
0040140C |. 8D45 F4 lea eax, [ebp-C]
0040140F |. 50 push eax
00401410 |. E8 DB0B0000 call 00401FF0 ; 求串长度
00401415 |. 83F8 0A cmp eax, 0A ; 注册码应为10字符长
00401418 |. 59 pop ecx
00401419 |. 75 2F jnz short 0040144A
0040141B |. 8D45 F4 lea eax, [ebp-C]
0040141E |. 50 push eax
0040141F |. E8 A7FCFFFF call 004010CB ; 检测注册码的字符组成(跟进去就知道了)
00401424 |. 85C0 test eax, eax
00401426 |. 59 pop ecx
00401427 |. 74 21 je short 0040144A ; 如果不是全由数码组成,则为非法注册码
00401429 |. 8D45 F4 lea eax, [ebp-C]
0040142C |. 50 push eax
0040142D |. E8 B7FCFFFF call 004010E9 ; 关键算法call
00401432 |. 85C0 test eax, eax
00401434 |. 59 pop ecx
00401435 |. 74 13 je short 0040144A ; 关键跳转
00401437 |. 6A 00 push 0
00401439 |. 68 BC404100 push 004140BC ; ASCII "Cool"
0040143E |. 68 A0404100 push 004140A0 ; ASCII "Correct Serial Number !!!"
00401443 |. 8BCE mov ecx, esi
00401445 |. E8 A1830000 call 004097EB
0040144A |> 8BCE mov ecx, esi
0040144C |. E8 CD680000 call 00407D1E
00401451 |. 5E pop esi
00401452 |. C9 leave
00401453 \. C3 retn
=====================以上是代码=================== 很自然地会想到断在40142C,但是一般情况下还是断不下来。再往上看,401402这一句里的3E8
是什么?好象是控件ID哦!(3E8的十进数值就是1000,象1000+xx这些典型的数值通常都会用在
对话框资源中作为模板ID或者控件ID。没有经过分析资源的实践是不会有这种经验的)想一下,
哪些API的参数里会用到控件ID?(GetDlgItemText……)呵呵,不管怎么说,想到这就好办
了。那么就断在这个call的下一句,也就是40140C这一句上面吧。
401410这个call把上面获得的输入码串指针作为参数,出来的时候马上把返回值跟0Ah比
较,这想都不用想,肯定是求串长度的一个过程。(话说回来,要达到“不用想”这种境界,也
不是一朝一夕的事情。想当初我开始破解第一个程序的时候,也是见call必入,把精力都浪费在
一些无关紧要的地方。虽然最后程序破了,头却同时也昏到非连睡24小时不可的地步)废话完了
没有啊!这明显是在说:注册码必须是10个字符长。下面(40141F这里)这个call呢?光看外表
就看不太明白了,那就跟进去看看吧。这个过程比较短小,一旦看见30和39这两个典型数值,不
用细看也知道它是在检测输入码的每个字符是否为数码。这些都是对注册码格式的前期合法性检
测。
接下来到了关键的地方了,40142D这个call跟进去: =====================以下是代码===================
004010E9 /$ 56 push esi
004010EA |. 8B7424 08 mov esi, [esp+8]
004010EE |. 57 push edi
004010EF |. 6A 31 push 31 ; 字符参数2
004010F1 |. 56 push esi
004010F2 |. E8 BDFFFFFF call 004010B4 ; 统计目的串中字符参数2的个数
004010F7 |. 59 pop ecx
004010F8 |. 59 pop ecx
004010F9 |. 0FBE0E movsx ecx, byte ptr [esi] ; 将注册码的第1位取出
004010FC |. 6A 30 push 30
004010FE |. 5F pop edi
004010FF |. 03C7 add eax, edi ; 上面得到的个数加上'0'
00401101 |. 3BC8 cmp ecx, eax
00401103 |. 0F85 C0000000 jnz 004011C9 ; 不相等则注册失败
00401109 |. 6A 32 push 32
0040110B |. 56 push esi
0040110C |. E8 A3FFFFFF call 004010B4
00401111 |. 59 pop ecx
00401112 |. 03C7 add eax, edi
00401114 |. 59 pop ecx
00401115 |. 0FBE4E 01 movsx ecx, byte ptr [esi+1]
00401119 |. 3BC8 cmp ecx, eax
0040111B |. 0F85 A8000000 jnz 004011C9
00401121 |. 6A 33 push 33
00401123 |. 56 push esi
00401124 |. E8 8BFFFFFF call 004010B4
00401129 |. 59 pop ecx
0040112A |. 03C7 add eax, edi
0040112C |. 59 pop ecx
0040112D |. 0FBE4E 02 movsx ecx, byte ptr [esi+2]
00401131 |. 3BC8 cmp ecx, eax
00401133 |. 0F85 90000000 jnz 004011C9
00401139 |. 6A 34 push 34
0040113B |. 56 push esi
0040113C |. E8 73FFFFFF call 004010B4
00401141 |. 59 pop ecx
00401142 |. 03C7 add eax, edi
00401144 |. 59 pop ecx
00401145 |. 0FBE4E 03 movsx ecx, byte ptr [esi+3]
00401149 |. 3BC8 cmp ecx, eax
0040114B |. 75 7C jnz short 004011C9
0040114D |. 6A 35 push 35
0040114F |. 56 push esi
00401150 |. E8 5FFFFFFF call 004010B4
00401155 |. 59 pop ecx
00401156 |. 03C7 add eax, edi
00401158 |. 59 pop ecx
00401159 |. 0FBE4E 04 movsx ecx, byte ptr [esi+4]
0040115D |. 3BC8 cmp ecx, eax
0040115F |. 75 68 jnz short 004011C9
00401161 |. 6A 36 push 36
00401163 |. 56 push esi
00401164 |. E8 4BFFFFFF call 004010B4
00401169 |. 59 pop ecx
0040116A |. 03C7 add eax, edi
0040116C |. 59 pop ecx
0040116D |. 0FBE4E 05 movsx ecx, byte ptr [esi+5]
00401171 |. 3BC8 cmp ecx, eax
00401173 |. 75 54 jnz short 004011C9
00401175 |. 6A 37 push 37
00401177 |. 56 push esi
00401178 |. E8 37FFFFFF call 004010B4
0040117D |. 59 pop ecx
0040117E |. 03C7 add eax, edi
00401180 |. 59 pop ecx
00401181 |. 0FBE4E 06 movsx ecx, byte ptr [esi+6]
00401185 |. 3BC8 cmp ecx, eax
00401187 |. 75 40 jnz short 004011C9
00401189 |. 6A 38 push 38
0040118B |. 56 push esi
0040118C |. E8 23FFFFFF call 004010B4
00401191 |. 59 pop ecx
00401192 |. 03C7 add eax, edi
00401194 |. 59 pop ecx
00401195 |. 0FBE4E 07 movsx ecx, byte ptr [esi+7]
00401199 |. 3BC8 cmp ecx, eax
0040119B |. 75 2C jnz short 004011C9
0040119D |. 6A 39 push 39
0040119F |. 56 push esi
004011A0 |. E8 0FFFFFFF call 004010B4
004011A5 |. 59 pop ecx
004011A6 |. 03C7 add eax, edi
004011A8 |. 59 pop ecx
004011A9 |. 0FBE4E 08 movsx ecx, byte ptr [esi+8]
004011AD |. 3BC8 cmp ecx, eax
004011AF |. 75 18 jnz short 004011C9
004011B1 |. 57 push edi
004011B2 |. 56 push esi
004011B3 |. E8 FCFEFFFF call 004010B4
004011B8 |. 59 pop ecx
004011B9 |. 03C7 add eax, edi
004011BB |. 59 pop ecx
004011BC |. 0FBE4E 09 movsx ecx, byte ptr [esi+9]
004011C0 |. 3BC8 cmp ecx, eax
004011C2 |. 75 05 jnz short 004011C9
004011C4 |. 6A 01 push 1
004011C6 |. 58 pop eax
004011C7 |. EB 02 jmp short 004011CB
004011C9 |> 33C0 xor eax, eax
004011CB |> 5F pop edi
004011CC |. 5E pop esi
004011CD \. C3 retn
=====================以上是代码=================== 这段代码看起来长,实际上都是差不多的一个片段在重复,就以第一小节为例,跟进004010B4这
个call可以知道它是在统计输入码中给定字符的个数,现在这个字符是31也就是'1',取得这个
个数以后加上30,然后与输入码的第1个字符比较,不相等就注册失败。如果撇开具体的数值转
ASCII码的手续,这就是说:注册码的第一个数字应该等于数码'1'在注册码串中的出现次数。
举一反三,下面的部分就不用仔细看了。验证算法可以总结如下:
注册码必须是10字符,且全由十进制数码构成。
注册码的第一个数字等于数码'1'在注册码串中的出现次数。
注册码的第二个数字等于数码'2'在注册码串中的出现次数。
注册码的第三个数字等于数码'3'在注册码串中的出现次数。
注册码的第四个数字等于数码'4'在注册码串中的出现次数。
注册码的第五个数字等于数码'5'在注册码串中的出现次数。
注册码的第六个数字等于数码'6'在注册码串中的出现次数。
注册码的第七个数字等于数码'7'在注册码串中的出现次数。
注册码的第八个数字等于数码'8'在注册码串中的出现次数。
注册码的第九个数字等于数码'9'在注册码串中的出现次数。
注册码的第十个数字等于数码'0'在注册码串中的出现次数。
这些规则看起来十分古怪,并且一时也不容易构造出一个正确的注册码。但是可以用逻辑推理把
合法注册码限制到一个很小的范围。
首先考虑注册码串中所有数码的和。由上述规则容易知道,这个和应该等于注册码的长度即
10。
这意味着,注册码的第5、6、7、8、9位都不会超过1。以第5位为例,如果这个位的值大于
或等于2,就意味着串中至少有两个5。如果第5位不是5,那么两个5加上第5位的值已经超过10,
矛盾。如果第5位是5,又意味着有5个5,加起来至少是25,同样得到矛盾。
由此导致的限制还有:第四位不超过2,第三位不超过3,第二位不超过4。
现在来估计得更精细些:首先考虑第九位能否为1。如果第九位为1,则意味着某一位是9,
这一位显然只可能是首位或末位。如果是首位,就意味着有9个1。9个1加一个9已经是18 > 10,
矛盾。如果是末位,就意味着有9个0。但是串中除了最后两位以外充其量只能还有8个0,矛盾,
因此第九位只能是0。
看第八位:如果第八位是1,如上所述,只可能末位是8,其他位全部为0。但这样整个串的
数码和等于9而不是10,矛盾,所以第八位也必须为0。
第七位:如果第七位是1,则必然末位是7,串中有7个0,还剩一个非零数码,为了凑够整个
串的数码和10,这个数码只能是2。这个2不可能出现在第2位或者以后的地方,不然串中至少还
有两个大于或等于2的数码,数码和就已经不小于7 + 1 + 2 * 2 = 12 > 10了,因此这个2只能
在第一位。但这意味着至少还有另一位是1,与刚才断言除这三个数字以外全是0矛盾,所以第七
位也是0。
第六位:如果这一位是1,则必然末位是6,串中有6个0,还剩两个非零数码。这两个非零数
码中至少有一个是出现在第2位或者往后的地方,假设出现在第n位,这里n >= 2。于是串中至少
有一个n。另一方面,由于第六位出现了1,第一位就不会是0,当然也不可能是1,假设是m,这
里m >= 2。如果m != n,则整个串的数码和至少是6 + 1 + n + m >= 11 > 10,矛盾。现在考虑
m = n,如果m > 2,则除了m和6以外至少还有三个1,这样就容不下6个0,矛盾;如果m = 2,则
另一个非零数码只能是1,并且由于出现了2,这个1只能被放在第二位,这样得到一个合法的注
册码:
2100010006
下面继续探讨第六位为0的情况:
第五位:前面已说过第五位不能大于1。另一方面,如果第五位是0,由于第六、七、八、九
位都是0,整个串中已至少有五个0,最末位的值势必大于或等于5,这意味着第五到第九位又不
可能全是0,矛盾,所以第五位恰好是1。于是串中有一个5,当然这个5只能在首位或末位。如果
在首位,就意味着除了首位的5以外还有5个1,由于数码和的限制,剩下的四位自然全是0。但是
这样导致末位的值为0或1,与0的数目为4矛盾,所以5应该在末位。于是正确注册码的格式部分
如下:
XXXX100005
并且前面的四个X中恰有一个是0。
如果第四位为1,必然还有一个4,4 + 1 + 1 + 5 = 11 > 10矛盾,于是第四位为0。
这样,剩下的三位都不可能是0了。第一位显然不会是1,至少是2以上。假设是3,则5 + 1
+ 3再加上剩下的两个非零数字已经超过10,所以第一位只能是2,第二位只能是1。但这样不管
第三位数字是什么都会导致矛盾。这就表明,第六位为0是不可能的。
综上所述,合法的注册码只有一个:2100010006
上传一份,若重复了告诉我把它删掉
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: