【文章标题】: CPP_Crackme1的破解和算法分析
【文章作者】: jdxyw
【软件名称】: CPP_Crackme1
【下载地址】: 自己搜索下载
【加壳方式】: 无
【编写语言】: C/C++
【使用工具】: OD peid
【操作平台】: XP
【作者声明】: 如果先前有高手已经有破过,请告诉我,我会把附件删掉,给论坛省点空间。
--------------------------------------------------------------------------------
【详细过程】
peid查壳,无壳
点击运行,随便输入一个用户名和序列号,这里我的用户名是yutou 序列号随便
运行od,将crack载入,F9运行,输入用户名和序列号
CTRL+N 在GetWindowText下断点,一共有三个同样的函数,只要下其中的两个就可以了
点击check后来到004016C6 F8运行几部后来到下面
00401747 |> 8D95 50FFFFFF /LEA EDX,DWORD PTR SS:[EBP-B0]
0040174D |. 52 |PUSH EDX
0040174E |. FFD6 |CALL ESI
00401750 |. 3BD8 |CMP EBX,EAX
00401752 |. 7D 16 |JGE SHORT CPP_Crac.0040176A
00401754 |. 8A8C1D 50FFFF>|MOV CL,BYTE PTR SS:[EBP+EBX-B0] 依次取用户名的字节
0040175B |. 8AC3 |MOV AL,BL
0040175D |. FEC0 |INC AL
0040175F |. 32C8 |XOR CL,AL
00401761 |. 888C1D 70FFFF>|MOV BYTE PTR SS:[EBP+EBX-90],CL 计算结果保存
00401768 |. EB 19 |JMP SHORT CPP_Crac.00401783
0040176A |> 8B07 |MOV EAX,DWORD PTR DS:[EDI] 依次取内存表中的双字
0040176C |. B9 39000000 |MOV ECX,39
00401771 |. 83F0 5A |XOR EAX,5A
00401774 |. 03C3 |ADD EAX,EBX
00401776 |. 99 |CDQ edx=0
00401777 |. F7F9 |IDIV ECX
00401779 |. 80C2 41 |ADD DL,41
0040177C |. 88941D 70FFFF>|MOV BYTE PTR SS:[EBP+EBX-90],DL 结果保存
00401783 |> 83C7 04 |ADD EDI,4
00401786 |. 43 |INC EBX
00401787 |. 81FF A8704000 |CMP EDI,CPP_Crac.004070A8
0040178D |.^ 7C B8 \JL SHORT CPP_Crac.00401747
上面这个循环一共循环30次,前五次循环是依次取用户名的一个字节,将此字节和循环次异或,再将异或的结果放到以0013FB68开始的
堆栈。后二十五次的循环是,依次取出原本就在内存中一个表中DS:[EDI]的字到EAX中,使得((eax xor 5ah)+ebx)/39h
所得的余数存入dl ,再将dl+41,所得存入堆栈中。这是通过用户计算所得的第一个数据
0040178F |. 8D55 90 LEA EDX,DWORD PTR SS:[EBP-70]
00401792 |. 6A 1E PUSH 1E
00401794 |. 8D85 70FFFFFF LEA EAX,DWORD PTR SS:[EBP-90]
0040179A |. 52 PUSH EDX
0040179B |. 50 PUSH EAX
0040179C |. C645 8E 00 MOV BYTE PTR SS:[EBP-72],0
004017A0 |. E8 EA050000 CALL CPP_Crac.00401D8F 这个函数的调用是将上个循环算的数据复制一遍,
所不同的是,没两个数据的位置是交换的,保存在0013fb88
004017A5 |. 83C4 0C ADD ESP,0C
004017A8 |. C645 AE 00 MOV BYTE PTR SS:[EBP-52],0
004017AC |. 33C0 XOR EAX,EAX
004017AE |. 33C9 XOR ECX,ECX
004017B0 |> 8A540D 90 /MOV DL,BYTE PTR SS:[EBP+ECX-70]
004017B4 |. 8855 FF |MOV BYTE PTR SS:[EBP-1],DL
004017B7 |. 8A45 FF |MOV AL,BYTE PTR SS:[EBP-1]
004017BA |. D1C8 |ROR EAX,1
004017BC |. 8845 FF |MOV BYTE PTR SS:[EBP-1],AL
004017BF |. 8A45 FF |MOV AL,BYTE PTR SS:[EBP-1]
004017C2 |. 88440D 90 |MOV BYTE PTR SS:[EBP+ECX-70],AL
004017C6 |. 41 |INC ECX
004017C7 |. 83F9 1E |CMP ECX,1E
这个循环同样循环30次,先是从堆栈0013fb88处依次取一个字节到AL处,右移动一位,再将算得的数据的AL送返回到原来堆栈处0013fb88
004017CA |.^ 7C E4 \JL SHORT CPP_Crac.004017B0
004017CC |. 33C9 XOR ECX,ECX
004017CE |. 8DBD D8FCFFFF LEA EDI,DWORD PTR SS:[EBP-328]
004017D4 |> 0FBE840D 70FF>/MOVSX EAX,BYTE PTR SS:[EBP+ECX-90] 依次 取0013fb68的一个字节
004017DC |. 0FBE540D 90 |MOVSX EDX,BYTE PTR SS:[EBP+ECX-70] 依次 取0013fb88的一个字节
004017E1 |. 8BF0 |MOV ESI,EAX
004017E3 |. 0FAFC2 |IMUL EAX,EDX
004017E6 |. 33F2 |XOR ESI,EDX
004017E8 |. 83C7 04 |ADD EDI,4
004017EB |. 03F0 |ADD ESI,EAX
004017ED |. 41 |INC ECX
004017EE |. 8977 FC |MOV DWORD PTR DS:[EDI-4],ESI
004017F1 |. 83F9 1E |CMP ECX,1E
这个循环同样循环30次,依次取0013fb68和0013fb88的一个字节分别到eax和edx,做如下运算 (EAX*edx)+(eax xor edx) 把计算双字所得存入堆栈
0013f8d0
004017F4 |.^ 7C DE \JL SHORT CPP_Crac.004017D4
004017F6 |. 8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
004017FC |. B9 0F000000 MOV ECX,0F
00401801 |> 8B50 3C /MOV EDX,DWORD PTR DS:[EAX+3C] 依次 取0013f90c的一个双字
00401804 |. 8B38 |MOV EDI,DWORD PTR DS:[EAX] 依次 取0013f8d0的一个双字
00401806 |. 03FA |ADD EDI,EDX
00401808 |. 8938 |MOV DWORD PTR DS:[EAX],EDI
0040180A |. 83C0 04 |ADD EAX,4
0040180D |. 49 |DEC ECX
0040180E |.^ 75 F1 \JNZ SHORT CPP_Crac.00401801
这个循环是15次,分别从0013f8d0和0013f90c处依次取双字到eax 和edx 做如下计算 eax+edx 计算所得存入0013f8d0
00401810 |. 33C0 XOR EAX,EAX
00401812 |. 33C9 XOR ECX,ECX
00401814 |. 8DBD D8FCFFFF LEA EDI,DWORD PTR SS:[EBP-328]
0040181A |> 8B07 /MOV EAX,DWORD PTR DS:[EDI] 依次从0013f8d0取一个双字到eax
0040181C |. 8945 B0 |MOV DWORD PTR SS:[EBP-50],EAX
0040181F |. 8B45 B0 |MOV EAX,DWORD PTR SS:[EBP-50]
00401822 |. 8845 B4 |MOV BYTE PTR SS:[EBP-4C],AL
00401825 |. 8B45 B4 |MOV EAX,DWORD PTR SS:[EBP-4C] 取其中的al到eax
00401828 |. BE 09000000 |MOV ESI,9
0040182D |. 25 FF000000 |AND EAX,0FF
00401832 |. 83C7 04 |ADD EDI,4
00401835 |. 99 |CDQ edx=0
00401836 |. F7FE |IDIV ESI
00401838 |. 80C2 30 |ADD DL,30
0040183B |. 88540D D4 |MOV BYTE PTR SS:[EBP+ECX-2C],DL
0040183F |. 41 |INC ECX
00401840 |. 83F9 0F |CMP ECX,0F
00401843 |.^ 7C D5 \JL SHORT CPP_Crac.0040181A
这个循环是15次,依次从0013f8d0取一个双字到eax,取其中的al到eax,做以下运算,(eax/9),所得余数加上30h,所得存入0013fbcc
00401845 |. B9 06000000 MOV ECX,6
0040184A |. 33C0 XOR EAX,EAX
0040184C |. 8D7D E4 LEA EDI,DWORD PTR SS:[EBP-1C]
0040184F |. F3:AB REP STOS DWORD PTR ES:[EDI]
00401851 |. 8B0D 30704000 MOV ECX,DWORD PTR DS:[407030]
00401857 |. 83F1 63 XOR ECX,63
0040185A |. 51 PUSH ECX
0040185B |. AA STOS BYTE PTR ES:[EDI]
0040185C |. E8 62040000 CALL CPP_Crac.00401CC3 此函数以上是生成加上一个字符‘U’
00401861 |. 8B15 34704000 MOV EDX,DWORD PTR DS:[407034]
00401867 |. 8845 E4 MOV BYTE PTR SS:[EBP-1C],AL
0040186A |. 83F2 63 XOR EDX,63
0040186D |. 52 PUSH EDX
0040186E |. E8 50040000 CALL CPP_Crac.00401CC3 此函数以上是生成加上一个字符‘L’
00401873 |. 8845 E5 MOV BYTE PTR SS:[EBP-1B],AL
00401876 |. A1 38704000 MOV EAX,DWORD PTR DS:[407038]
0040187B |. 83F0 63 XOR EAX,63
0040187E |. 50 PUSH EAX
0040187F |. E8 3F040000 CALL CPP_Crac.00401CC3 此函数是将0013fbcc处得数据转为字符
00401884 |. 8D4D D4 LEA ECX,DWORD PTR SS:[EBP-2C]
00401887 |. 6A 0F PUSH 0F
00401889 |. 8D55 E4 LEA EDX,DWORD PTR SS:[EBP-1C]
0040188C |. B3 2D MOV BL,2D
0040188E |. 51 PUSH ECX
0040188F |. 52 PUSH EDX
00401890 |. 8845 E6 MOV BYTE PTR SS:[EBP-1A],AL 加上一个字符'T'
00401893 |. 885D E7 MOV BYTE PTR SS:[EBP-19],BL 加上一个字符‘-’
00401896 |. E8 05030000 CALL CPP_Crac.00401BA0
0040189B |. 8B0D 50974000 MOV ECX,DWORD PTR DS:[409750]
004018A1 |. 83C4 18 ADD ESP,18
004018A4 |. 8D45 B8 LEA EAX,DWORD PTR SS:[EBP-48]
004018A7 |. 885D F7 MOV BYTE PTR SS:[EBP-9],BL
004018AA |. 6A 19 PUSH 19 ; /Count = 19 (25.)
004018AC |. 50 PUSH EAX ; |Buffer
004018AD |. 51 PUSH ECX ; |hWnd => NULL
004018AE |. C645 F8 32 MOV BYTE PTR SS:[EBP-8],32 ; | 这里以下的四句分别在序列号后加上四个字符
这在下面将造成很大的迷惑
004018B2 |. C645 F9 34 MOV BYTE PTR SS:[EBP-7],34 ; |
004018B6 |. C645 FA 31 MOV BYTE PTR SS:[EBP-6],31 ; |
004018BA |. C645 FB 33 MOV BYTE PTR SS:[EBP-5],33 ; |
004018BE |. FF15 C8604000 CALL DWORD PTR DS:[<&USER32.GetWindowTex>; \GetWindowTextA
004018C4 |. 83F8 01 CMP EAX,1
004018C7 |. 5B POP EBX
004018C8 |. 7D 68 JGE SHORT CPP_Crac.00401932
004018CA |. 6A 18 PUSH 18
004018CC |. 68 18714000 PUSH CPP_Crac.00407118
F8几步后,将到一个call处,
004019BC |. E8 5F010000 CALL CPP_Crac.00401B20
这个call可以跟进,你会发现一些问题
00401B20 /$ 56 PUSH ESI
00401B21 |. 57 PUSH EDI
00401B22 |. B9 3F000000 MOV ECX,3F
00401B27 |. 33C0 XOR EAX,EAX
00401B29 |. BF 54974000 MOV EDI,CPP_Crac.00409754
00401B2E |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10]
00401B32 |. F3:AB REP STOS DWORD PTR ES:[EDI]
00401B34 |. 66:AB STOS WORD PTR ES:[EDI]
00401B36 |. AA STOS BYTE PTR ES:[EDI]
00401B37 |. 33C0 XOR EAX,EAX
00401B39 |. 85F6 TEST ESI,ESI
00401B3B |. 7E 17 JLE SHORT CPP_Crac.00401B54
00401B3D |. 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
00401B41 |> 8A11 /MOV DL,BYTE PTR DS:[ECX]
00401B43 |. 83C1 04 |ADD ECX,4
00401B46 |. 80F2 63 |XOR DL,63
00401B49 |. 8890 54974000 |MOV BYTE PTR DS:[EAX+409754],DL
00401B4F |. 40 |INC EAX
00401B50 |. 3BC6 |CMP EAX,ESI
00401B52 |.^ 7C ED \JL SHORT CPP_Crac.00401B41
00401B54 |> 5F POP EDI
00401B55 |. 5E POP ESI
00401B56 \. C3 RETN
从这个循环你会发现,这个循环只循环20次,而序列号是要求24个的,最后4个,也就是上面最终加上的四个,也就是我们在内存中
可以看到的似乎是真序列号的最后四位,是不在这里比较的。我们也可以看处,无论我们的用户名是什么,最后四位似乎都是一样的。
继续,可以来到一个比较最后四位的地方
00401A1C |> 8A4D CC MOV CL,BYTE PTR SS:[EBP-34]
00401A1F |. 80F1 63 XOR CL,63
00401A22 |. 80F9 53 CMP CL,53
00401A25 |. 0F85 86000000 JNZ CPP_Crac.00401AB1
00401A2B |. 8A55 CD MOV DL,BYTE PTR SS:[EBP-33]
00401A2E |. 80F2 63 XOR DL,63
00401A31 |. 80FA 52 CMP DL,52
00401A34 |. 75 7B JNZ SHORT CPP_Crac.00401AB1
00401A36 |. 8A45 CE MOV AL,BYTE PTR SS:[EBP-32]
00401A39 |. 34 63 XOR AL,63
00401A3B |. 3C 57 CMP AL,57
00401A3D |. 75 72 JNZ SHORT CPP_Crac.00401AB1
00401A3F |. 8A4D CF MOV CL,BYTE PTR SS:[EBP-31]
00401A42 |. 80F1 63 XOR CL,63
00401A45 |. 3ACA CMP CL,DL
00401A47 |. 75 68 JNZ SHORT CPP_Crac.00401AB1
简单反向计算,最后四位应该是0141
算法总结
第一个循环,前五次循环是依次取用户名的一个字节,将此字节和循环次异或,再将异或的结果放到以0013FB68开始的
堆栈。后二十五次的循环是,依次取出原本就在内存中一个表中DS:[EDI]的字到EAX中,使得((eax xor 5ah)+ebx)/39h
所得的余数存入dl ,再将dl+41,所得存入堆栈中。这是通过用户计算所得的第一个数据。
将上个循环算的数据复制一遍,所不同的是,没两个数据的位置是交换的,保存在0013fb88
先是从堆栈0013fb88处依次取一个字节到AL处,右移动一位,再将算得的数据的AL送返回到原来堆栈处0013fb88
依次取0013fb68和0013fb88的一个字节分别到eax和edx,做如下运算 (EAX*edx)+(eax xor edx) 把计算双字所得存入堆栈
依次从0013f8d0取一个双字到eax,取其中的al到eax,做以下运算,(eax/9),所得余数加上30h,所得存入0013fbcc
将存在0013FBCC处的数据转为字符,在前面加上“ULT-”在后面加上“-0141”
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年08月21日 1:27:46
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!