CRACKME1.0 算法分析(下载地址:http://www.fradfire.com/crackme1.0.exe)
这个是我在看雪论坛发的第一个破文,因为技术比较菜,所以写的不是很好,希望大侠们不要见笑,实际上这个CRACKME是《加密与解密第2版》中的一
个练习题,后面有答案,但是答案上是用SOFTICE 来破解的,提示的很简短,在这里我主要对这个CRACKME的算法做详细的分析,好了不说废话了
开始吧
****************************************************************************************************************************
1)运行PEID探测无壳
2)运行这个CRACKME,输入任意的注册码和序列号后有错误提示----Wrong Serial Number !
3) 用IDA载入这个程序,搜索这个错误提示字符串,很快就找到了这个错误提示的入口
CODE:00427B44 ; 〓〓〓〓〓〓〓〓〓〓〓〓 S U B R O U T I N E 〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓
CODE:00427B44
CODE:00427B44 ; Attributes: bp-based frame
CODE:00427B44
CODE:00427B44 sub_427B44 proc near ; DATA XREF: CODE:004279A1o
CODE:00427B44
CODE:00427B44 var_8 = dword ptr -8
CODE:00427B44 var_4 = dword ptr -4
CODE:00427B44
CODE:00427B44 push ebp
CODE:00427B45 mov ebp, esp
CODE:00427B47 push 0
CODE:00427B49 push 0
CODE:00427B4B push ebx
CODE:00427B4C mov ebx, eax
CODE:00427B4E xor eax, eax
CODE:00427B50 push ebp
CODE:00427B51 push offset loc_427BFA
CODE:00427B56 push dword ptr fs:[eax]
CODE:00427B59 mov fs:[eax], esp
CODE:00427B5C lea edx, [ebp+var_4]
CODE:00427B5F mov eax, [ebx+1DCh]
CODE:00427B65 call sub_415D90
CODE:00427B6A mov eax, [ebp+var_4] ;在这个地方ebp+var_4这个局部变量保存了我们输入的UserName,并将其值传送到EAX中
CODE:00427B6D call sub_4037B0
CODE:00427B72 dec eax
CODE:00427B73 jl short loc_427BA5
CODE:00427B75 lea edx, [ebp+var_4]
CODE:00427B78 mov eax, [ebx+1ECh]
CODE:00427B7E call sub_415D90
CODE:00427B83 mov eax, [ebp+var_4] ;此处[ebp+var_4]这个局部变量保存了我们输入的Serial
CODE:00427B86 push eax ;输入的Serial入栈
CODE:00427B87 lea edx, [ebp+var_8]
CODE:00427B8A mov eax, [ebx+1DCh]
CODE:00427B90 call sub_415D90
CODE:00427B95 mov eax, [ebp+var_8]
CODE:00427B98 pop edx
CODE:00427B99 call sub_427A20 ;此处是关键CALL 跟进去就是算法的核心
CODE:00427B9E cmp eax, 0BC614Eh
CODE:00427BA3 jge short loc_427BC3 >=就跳转到成功
CODE:00427BA5
CODE:00427BA5 loc_427BA5: ; CODE XREF: sub_427B44+2Fj
CODE:00427BA5 push 0
CODE:00427BA7 push offset aError_0 ; "ERROR"
CODE:00427BAC push offset aWrongSerialNum ; "Wrong Serial Number !" 注册错误的提示
CODE:00427BB1 mov eax, ds:dword_429744
CODE:00427BB6 call sub_4199FC
CODE:00427BBB push eax
CODE:00427BBC call MessageBoxA_0 ;调用此函数之后将产生错误提示
CODE:00427BC1 jmp short loc_427BDF
CODE:00427BC3 ; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CODE:00427BC3
CODE:00427BC3 loc_427BC3: ; CODE XREF: sub_427B44+5Fj
CODE:00427BC3 push 0
CODE:00427BC5 push offset aSuccess ; "Success"
CODE:00427BCA push offset aCongratulation ; "Congratulation ! You've Did It.\rMail Us"... 注册成功的提示
CODE:00427BCF mov eax, ds:dword_429744
CODE:00427BD4 call sub_4199FC
CODE:00427BD9 push eax
CODE:00427BDA call MessageBoxA_0
4)用OD载入来到00427B44这个地址,并在此处设断.然后F9运行,并在UserName处输入123456789,在Serial处输入123456 点击OK后便中断在00427B44这个地方
然后取消断点,单步向下运行,注意观察寄存器窗口各个寄存器的值,看看哪一句代码,读入了我们输入的UserName和Serial,不久便会发现
00427B6A |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 在这个地方EBP-4这个局部变量保存了我们输入的UserName,并将其值传送到EAX中
00427B83 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 此处EBP-4这个局部变量保存了我们输入的Serial
一直向下跟直到
CODE:00427B99 call sub_427A20 很明显此处是关键CALL 必须跟进去
****************************************************************************************************************************
5)核心算法分析
00427A20 /$ 55 PUSH EBP ;此时EAX和EDX中分别保存的是UserName和123456
00427A21 |. 8BEC MOV EBP,ESP
00427A23 |. 83C4 F0 ADD ESP,-10 ;为变量和参数分配空间
00427A26 |. 53 PUSH EBX
00427A27 |. 56 PUSH ESI
00427A28 |. 33C9 XOR ECX,ECX ;ECX寄存器清0
00427A2A |. 894D F0 MOV DWORD PTR SS:[EBP-10],ECX ;EBP-10 这个局部变量被赋值为0
00427A2D |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX ;EBP-C 这个局部变量也被赋值为0
00427A30 |. 8955 F8 MOV DWORD PTR SS:[EBP-8],EDX ;EBP-8 这个局部变量保存了我们输入的SERIAL
00427A33 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX ;EBP-4 这个局部变量保存了我们输入的USERNMAE
00427A36 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ;USENAME 再次写入到EAX中 EAX的值为123456789
00427A39 |. E8 26BFFDFF CALL crackme1.00403964 ;这个函数我没有跟进去,有兴趣的可以跟进去看看
00427A3E |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8] ;SERIAL写入到EAX中 EAX的值为123456
00427A41 |. E8 1EBFFDFF CALL crackme1.00403964 ;再次调用这个函数
00427A46 |. 33C0 XOR EAX,EAX ;EAX清0
00427A48 |. 55 PUSH EBP
00427A49 |. 68 2A7B4200 PUSH crackme1.00427B2A
00427A4E |. 64:FF30 PUSH DWORD PTR FS:[EAX]
00427A51 |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
00427A54 |. 33DB XOR EBX,EBX ;EBX清0
00427A56 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ;USERNAME送入EAX EAX=123456789
00427A59 |. E8 52BDFDFF CALL crackme1.004037B0 ;调用这个函数后,返回的是我们输入的USERNAME的位数,其值放在EAX中,此时EAX=9
00427A5E |. 8BF0 MOV ESI,EAX
00427A60 |. 85F6 TEST ESI,ESI
00427A62 |. 7E 3C JLE SHORT crackme1.00427AA0 ;<=则跳,很显然跳转不成功
00427A64 |. B8 01000000 MOV EAX,1 ;EAX置1
----------------------------第一个循环-------------------------------------------------------------------------------------
00427A69 |> 8BD0 /MOV EDX,EAX ;EDX置1
00427A6B |. 8B4D FC |MOV ECX,DWORD PTR SS:[EBP-4] ;ECX保存了USERNAME
00427A6E |. 0FB64C11 FF |MOVZX ECX,BYTE PTR DS:[ECX+EDX-1];传送USERNAME中的第一个位到ECX中,因为是字节传送,此时ECX=00000031(1的16进制ASCLL码)
00427A73 |. 03D9 |ADD EBX,ECX ;EBX+ECX=00000031
00427A75 |. 71 05 |JNO SHORT crackme1.00427A7C ;OF标志位为0就跳(未溢出就跳)
00427A77 |. E8 B4AFFDFF |CALL crackme1.00402A30
00427A7C |> C1E3 08 |SHL EBX,8 ;逻辑左移,EBX=00003100
00427A7F |. 8B0D 80884200 |MOV ECX,DWORD PTR DS:[428880] ;ECX保存了428880这个存储单元的值--LANNYDIBANDINGINANAKEKHYANGNGENT
00427A85 |. 0FB65411 FF |MOVZX EDX,BYTE PTR DS:[ECX+EDX-1] ;将ECX中的第一位4C(L的16进制ASCII)送入EDX中
00427A8A |. 0BDA |OR EBX,EDX ;EBX和EDX进行OR运算 EBX=0000314C
00427A8C |. 85DB |TEST EBX,EBX
00427A8E |. 7D 0C |JGE SHORT crackme1.00427A9C ;>=则跳 第五次循环和最后一次这个条件将不会满足
00427A90 |. 6BD3 FF |IMUL EDX,EBX,-1
00427A93 |. 71 05 |JNO SHORT crackme1.00427A9A
00427A95 |. E8 96AFFDFF |CALL crackme1.00402A30
00427A9A |> 8BDA |MOV EBX,EDX
00427A9C |> 40 |INC EAX ;EAX+1
00427A9D |. 4E |DEC ESI :ESI-1
00427A9E |.^ 75 C9 \JNZ SHORT crackme1.00427A69 ;循环的条件(很明显将循环9次)每次的循环将会导致EAX的值的变化--EDX将变化....每次都产生不同的结果
经过9次循环后 EBX的值将是本次循环的结果EBX=0C7E84BF
----------------------------------------------------------------------------------------------------------------------------
00427AA0 |> 81F3 78563412 XOR EBX,12345678 ;OR运算后 EBX=1E4AD27C
00427AA6 |. 8D55 F0 LEA EDX,DWORD PTR SS:[EBP-10]
00427AA9 |. 8BC3 MOV EAX,EBX
00427AAB |. E8 44E9FDFF CALL crackme1.004063F4
00427AB0 |. 8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10]
00427AB3 |. E8 F8BCFDFF CALL crackme1.004037B0
00427AB8 |. 8BF0 MOV ESI,EAX
00427ABA |. 85F6 TEST ESI,ESI
00427ABC |. 7E 38 JLE SHORT crackme1.00427AF6
----------------------------第2个循环---------------------------------------------------------------------------------------
00427ABE |> 8BC3 /MOV EAX,EBX
00427AC0 |. B9 0A000000 |MOV ECX,0A
00427AC5 |. 99 |CDQ ;将EAX寄存器中的双字+0扩展成为4字 并保存在EDX:EAX中
00427AC6 |. F7F9 |IDIV ECX ;EAX:EDX/ECX 商保存在EAX中 余数保存在EDX中
00427AC8 |. 6215 3C7B4200 |BOUND EDX,QWORD PTR DS:[427B3C]
00427ACE |. 8A92 84884200 |MOV DL,BYTE PTR DS:[EDX+428884] ;用DD 00428884查看会看到LANNY5646521这个字符串,取该字符串的一位到DL中,取哪一位和所的的余数有关
00427AD4 |. 8D45 F0 |LEA EAX,DWORD PTR SS:[EBP-10]
00427AD7 |. E8 FCBBFDFF |CALL crackme1.004036D8
00427ADC |. 8B55 F0 |MOV EDX,DWORD PTR SS:[EBP-10]
00427ADF |. 8D45 F4 |LEA EAX,DWORD PTR SS:[EBP-C]
00427AE2 |. E8 D1BCFDFF |CALL crackme1.004037B8
00427AE7 |. 8BC3 |MOV EAX,EBX
00427AE9 |. B9 0A000000 |MOV ECX,0A
00427AEE |. 99 |CDQ
00427AEF |. F7F9 |IDIV ECX ;再次做除法运算
00427AF1 |. 8BD8 |MOV EBX,EAX
00427AF3 |. 4E |DEC ESI ;ESI-1 第一次ESI中保存的仍然是USERNAME的位数9
00427AF4 |.^ 75 C8 \JNZ SHORT crackme1.00427ABE ;很明显将循环9次
----------------------------------------------------------------------------------------------------------------------------
00427AF6 |> 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C] ;送入EAX中的值就是正确的Serial
00427AF9 |. 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8] ;错误的serial
00427AFC |. E8 BFBDFDFF CALL crackme1.004038C0 ;对两个serial进行一系列的运算
00427B01 |. 75 07 JNZ SHORT crackme1.00427B0A ;要想注册成功就必须让这个条件不成立
00427B03 |. BB 4E61BC00 MOV EBX,0BC614E ;如果能运行到此处 那么 那么他将最终跳向成功
00427B08 |. EB 05 JMP SHORT crackme1.00427B0F
00427B0A |> BB 91D61200 MOV EBX,12D691
00427B0F |> 33C0 XOR EAX,EAX
00427B11 |. 5A POP EDX
00427B12 |. 59 POP ECX
00427B13 |. 59 POP ECX
00427B14 |. 64:8910 MOV DWORD PTR FS:[EAX],EDX
00427B17 |. 68 317B4200 PUSH crackme1.00427B31
00427B1C |> 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]
00427B1F |. BA 04000000 MOV EDX,4
00427B24 |. E8 2FBAFDFF CALL crackme1.00403558
00427B29 \. C3 RETN ;返回到00427B31
00427B2A .^ E9 A9B4FDFF JMP crackme1.00402FD8
00427B2F .^ EB EB JMP SHORT crackme1.00427B1C
00427B31 . 8BC3 MOV EAX,EBX
00427B33 . 5E POP ESI
00427B34 . 5B POP EBX
00427B35 . 8BE5 MOV ESP,EBP
00427B37 . 5D POP EBP
00427B38 . C3 RETN ;返回到00427B9E 因为EAX被赋予了错误的值,所以注册失败
好了,不说了,不好意思说了很多的废话,主要是为了让象我一样的菜鸟都能看懂,下面看下这个算法的运算过程
****************************************************************************************************************************
运算过程:
username: 1 2 3 4 5 6 7 8 9
16进制ASCII形式:31 32 33 34 35 36 37 38 39
M 字符串: LANNYDIBANDINGINANAKEKHYANGNGENT
16进制ASCII形式:4C 41 4E 4E 59 44 49 42 41 4E 44 49 4E 47 49 4E 41 4E 41 4B 45 4B 48 59 41 4E 47 4E 47 45 4E 54
N 字符串:LANNY5646521
第一个循环:
第1次循环:1)取 username的第一位31和0想加 00000031+0=00000031
2)00000031 逻辑左移一个单位 =====00003100
3)00003100与字符串M的第一位做OR运算 //00003100 OR 0000004C=0000314C
第2次循环:1)取usename 的第2位和0000314C想加 0000314C+00000032=0000317E
2)0000317E 逻辑左移一个单位 =====00317E00
3)00317E00与字符串M的第2位做OR运算 //00317E00 OR 00000041=00317E41
..........................中间的过程省略(注意第5次循环和最后一次的时候.00427A8E 这个条件将不会满足,他将会去做乘法运算).....................................
经过第一个循环:得到结果EBX=0C7E84BF
在进行这样一次运算:0C7E84BF XOR 12345678=1E4AD2C7
第二个循环:
第1次循环:1)1E4AD2C7 扩展为000000001E4AD2C7
2)000000001E4AD2C7除以0A======03077B7A(商).......00000003(余数)
4)取N字符串LANNY5646521的第4(00000003+1)个字符 该字符是正确serial的第一位
第2次循环:1)03077B7A扩展为0000000003077B7A
2)0000000003077B7A除以0A=======004D8C59(商)........00000000(余数)
3)取N字符串LANNY5646521的第1(0000000+1)个字符 该字符是正确serial的第二位
第3次循环:1)004D8C59扩展为00000000004D8C59
2)00000000004D8C59除以0A======0007C13C(商).......00000001(余数)
3)取N字符串LANNY5646521的第2(00000001+1)个字符 该字符是正确serial的第三位
...........就这样经过9次循环后便得到正确的serial为(NLALNN6L5)...................
*************************************************************************************************************************
总结:其实,这个CRACKME 要得到他的serial并不难,因为它是明码保存的,保护机制很弱,经过这两个循环之后在OD的堆栈窗口就看到
正确的serial,所以要单纯的找到他的 serial其实是很简单的,当然我在这里也只是分析他的算法.
由于第一次 写破文,加之水平有限,错误之处,请大家见谅,欢迎大家指出.
最后祝愿大家都能在看雪学到更高的解密技术...
jipo400
2006.3.5
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)