1. OD加载,F9试运行,任意输入一串字符,看有何结果,如图1.
图1
2. 看到这儿我们一定心中窃喜,为什么呢?因为看到了错误提示Wrong S/N#,于是很自然地想到去搜索字符串:右键--------Search For----------All Referenced Text Strings,找到Wrong S/N#,双击之,来到图2.
图2
3. 直觉告诉我们,关键跳转与关键算法就在附近,于是向上翻翻,果然找到了;由于本题不允许爆破,那我们就来分析算法吧,显然,要解密,就要跟进call 0040120B.让我们一鼓作气吧!
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、重点分析关键算法
1. F7 跟进 call 0040120B,看到如下代码:
[Asm] 纯文本查看 复制代码
0040120B /$ C8 000000 enter 0, 0
0040120F |. 53 push ebx
00401210 |. 52 push edx
00401211 |. 33C0 xor eax, eax
00401213 |. B8 A6204000 mov eax, 004020A6 ; ASCII "kaoyange"
00401218 |. 8038 00 cmp byte ptr [eax], 0
0040121B |. 74 60 je short 0040127D
0040121D |. 33DB xor ebx, ebx
0040121F |. 33D2 xor edx, edx
00401221 |> 8A18 /mov bl, byte ptr [eax];取出一个字符放入bl
00401223 |. C1C3 08 |rol ebx, 8 ;将ebx循环左移8位
00401226 |. 03D3 |add edx, ebx ;edx为累加器
00401228 |. 40 |inc eax
00401229 |. 8038 00 |cmp byte ptr [eax], 0
0040122C |.^ 75 F3 \jnz short 00401221
0040122E |. 52 push edx ; /<%lX>
0040122F |. 68 54204000 push 00402054 ; |Format = "%lX"
00401234 |. 68 BF204000 push 004020BF ; |s = crackme5.004020BF
00401239 |. E8 8F000000 call <jmp.&USER32.wsprintfA> ; \wsprintfA
0040123E |. BB BF204000 mov ebx, 004020BF
00401243 |. 803B 38 cmp byte ptr [ebx], 38
00401246 |. 75 35 jnz short 0040127D
00401248 |. 807B 01 44 cmp byte ptr [ebx+1], 44
0040124C |. 75 2F jnz short 0040127D
0040124E |. 807B 02 43 cmp byte ptr [ebx+2], 43
00401252 |. 75 29 jnz short 0040127D
00401254 |. 807B 03 41 cmp byte ptr [ebx+3], 41
00401258 |. 75 23 jnz short 0040127D
0040125A |. 807B 04 46 cmp byte ptr [ebx+4], 46
0040125E |. 75 1D jnz short 0040127D
00401260 |. 807B 05 33 cmp byte ptr [ebx+5], 33
00401264 |. 75 17 jnz short 0040127D
00401266 |. 807B 06 36 cmp byte ptr [ebx+6], 36
0040126A |. 75 11 jnz short 0040127D
0040126C |. 807B 07 38 cmp byte ptr [ebx+7], 38
00401270 |. 75 0B jnz short 0040127D
00401272 |. B8 01000000 mov eax, 1
00401277 |. 5A pop edx
00401278 |. 5B pop ebx
00401279 |. C9 leave
0040127A |. C2 0400 retn 4
0040127D |> 33C0 xor eax, eax
0040127F |. 5A pop edx
00401280 |. 5B pop ebx
00401281 |. C9 leave
00401282 \. C2 0400 retn 4
2.先不急于去分析汇编指令,弄清逻辑结构更为关键,仔细看下原来大致步骤是:将你输入的字符串的每个字符取出来,然后做移位和相加运算,最后得到一个结果,将这个结果与正确结果(Ox8DCAF368)作比较,如果相等则成功,否则失败。还是挺简单的吧!
3.理清了程序的结构,那么我们就要来好好分析下这个所谓的“移位和相加运算”,明眼人一看就知道00401221到0040122C是这个运算的核心过程,因为弄清了这个过程,我们的序列号就能轻松搞定了,为了便于讲解,我们不妨假设输入的字符串为ABCD(注:这里的ABCD是四个未知的字符,因为我们不知道正确的序列号,所以要逆求正解),那么我们就可以建立一个简单的数学模型,EDX 为累加器,无需关注,我们只要盯着每次都加上的EBX即可。
第一次要加上的EBX: 0 0 A 0
第二次要加上的EBX: 0 A B 0
第三次要加上的EBX: A B C 0
第四次要加上的EBX: B C D A
而正确结果我们是只知道的,所以此时我们只需要求解一个四元一次方程即可:
A + B= 8D ------------------------------------- (1)
A + B+ C = CA ------------------------------------- (2)
A + B+ C + D = F3 ------------------------------------- (3)
A =68 -------------------------------------(4)
联立(1)(2)(3)(4)得到一个解:h%=),快去试试吧,如图3
图3
其实看到这儿,应该就能明白为什么我要输入四个字符来讲解了,它的本质就是4个方程来解n元一次方程,如果n = 4,那么刚好可以得到唯一解;如果n > 4,那么解就可以是多个了。同时,如果你敏感的话,肯定很容易想到如果相加有进位呢?没错,这个留给爱思考的你。
你会发现如果不考虑进位:序列号的形式为XXXXXXXXXXXXX%=)
如果考虑到进位:序列号的一种形式为XXXXXXXXX$=)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三、后记
Q:这个CrackMe的算法很easy啊,你为啥还要写篇文章?
Ans:主要理由我觉得有两点:首先就是纯算法类型的CrackMe我也破解过一些,而且在大学时也搞过一段时间的ACM/ICPC,本身对算法就很热爱,自己真正分析出来的多少会有兴奋之情,如果只是一味地练习CrackMe,对我而言可能会失去热情,倒不如分享一下,写写自己的破解过程,保持对破解的一些新鲜感,这样会更好;其次就是我觉得“雁过留痕”,自己曾经做过的事总得留下些什么。