任我小行,说行就行
一个简单的CrackMe的两种破解思路以及算法分析
上一篇 / 下一篇 2007-08-16 09:35:28 / 个人分类:CRACKER
今天这个CrackMe我自己都不记得是在哪弄来的,在硬盘里放了很久,突然看到,觉得简单,特写下此文。本人水平有限,权当抛砖引玉,让有兴趣的朋友对破解有个了解的机会。
好了,我们言归正传
首先运行一次CrackMe,输入用户名和假码提示信息为you have enter a wrong serial, please try again 。我们把这句话记下来。
用PEID查壳,无壳,入口点地址为401000。
打开动态调试器OD,载入此CrackMe。来到这里,程序的入口点401000处
00401000 >/$ 6A 00 PUSH 0 ; /(初始 cpu 选择)
00401002|.E8 23040000CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401007 |. A3 F0344000 MOV DWORD PTR DS:[4034F0],EAX
'j'
在OD的反汇编区,点鼠标右键,选择查找超级字串参考的ASCII,看到我们刚记下来的you have enter a wrong serial, please try again这句话。在此字串上双击,来到程序的此段代码中。
00401334 |. E8 4A000000 CALL Key-Crac.00401383 ;关键CALL,我们在此下断
00401339 |. 5E POP ESI
0040133A |. 3BC6 CMP EAX,ESI ;比较真假注册码
0040133C |. 75 15 JNZ SHORT Key-Crac.00401353 ;关键跳转,如果上面的CMP不相等,则跳转至显示出错信息。
0040133E |. 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
00401340 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
00401345 |. 68 B8344000 PUSH Key-Crac.004034B8 ; | good job, i wish you the very best
0040134A |. 6A 00 PUSH 0 ; |hOwner = NULL
0040134C |. E8 9D000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401351 |. EB 13 JMP SHORT Key-Crac.00401366
00401353 |> 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL;来自0040133C的跳转
00401355 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
0040135A |. 68 86344000 PUSH Key-Crac.00403486 ; | you have enter a wrong serial, please try again ;输入错误的序列号后的错误提示
0040135F |. 6A 00 PUSH 0 ; |hOwner = NULL
00401361 |. E8 88000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA;显示错误信息
我们分析一下,当输入不正确的注册码的时候,程序弹出的错误信息是从0040133C跳转过来的。所以,我们在跳转之前的CALL上下断。F9运行程序,弹出程序主窗口,我们输入以下的假注册信息,用户名“lansechenxi”,注册码“54321”。点“CHECK”后程序断下,OD自动激活。断在了00401334处,(反汇编代码可参看上面)我们这里暂时先不关心注册码的算法,因为此CrackMe是简单的明码比较。按F8两次后,单步执行程序到CMP EAX,ESI处,在OD的信息区看到ESI=0001E5C2;EAX=0000D431。呵呵,到了这里大家也许已经想到,0000D431不就是10进制的“54321”的16进制表示形式吗?那么ESI寄存器中的001E5C2应该就是正确的注册码了吧?确实如此,我们用系统自带的计算器把001E5C2换算成10进制为124354。(OD本身就带有换算功能)
我们试试,哈哈成功了,是不是很简单?

这个时候有一个新的问题,假如程序当输入假码时没有错误提示信息怎么办?那不就查找不到相应的字串了吗?这时候,我们可以碰碰运气,用OD查找字串参考时,看能不能找到当输入正确注册码时,程序给出的信息,如本例中的“good job, i wish you the very best”这句话。从这入手,用上面的方法,也能达到破解的目的。但是,如果程序压根就把这些重要信息加了密的话,那这种查找字串的手法,就没辙了。我们只能另外想办法。我们要用的就是在API函数上下断的方法。重要的函数有,USER32.DLL中的GetWindowsTextA和GetDlgItemTextA,我们今天就要用到其中之一。这两个函数的作用分别是,复制窗口中的字符到缓冲区和返回对话框中某一个窗口的标题或文字。
还是用此CrackMe为例说明
我们用OD重新载入程序,来到程序的入口点。在反汇编区,点鼠标右键---查找---当前模块中的名称(对应的快捷键为ctrl+N),看到本程序用到了GetDlgItemTextA函数。光标移到此函数处点鼠标右键,选择查找输入函数参考。会弹出一个新的窗口,列出此CrackMe程序中用到的所有GetDlgItemTextA函数。这里我们看到有两个,不难理解,因为此CrackMe是要输入用户名和注册码两种信息的。我们现在分不清哪个函数对哪个信息,所以干脆两个上面都下断。F9运行程序,弹出程序主窗口,我们还是输入以下的假注册信息,用户名“lansechenxi”,注册码“54321”。点“CHECK”后程序断下,OD自动激活。取下这么一段反汇编代码分析。
004012BD |. E8 08010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA;程序中断在此处,取得用户名窗口所输入数值
004012C2 |. 83F8 00 CMP EAX,0 ;检查是否输入用户名
004012C5 |. 74 18 JE SHORT Key-Crac.004012DF ;如未输入用户名则跳转至004012DF
004012C7 |. 6A 40 PUSH 40 ; /Count = 40 (64.)
004012C9 |. 68 38314000 PUSH Key-Crac.00403138 ; |Buffer = Key-Crac.00403138
004012CE |. 6A 6B PUSH 6B ; |ControlID = 6B (107.)
004012D0 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004012D3 |. E8 F2000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA;取得注册码窗口所输入数值
004012D8 |. 83F8 00 CMP EAX,0 ;检查是否输入注册码
004012DB |. 74 02 JE SHORT Key-Crac.004012DF ;如未输入注册码则跳转至004012DF
004012DD |. EB 17 JMP SHORT Key-Crac.004012F6
004012DF |> 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
004012E1 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
004012E6 |. 68 00304000 PUSH Key-Crac.00403000 ; |please fill in 1 more char!!
004012EB |. 6A 00 PUSH 0 ; |hOwner = NULL
004012ED |. E8 FC000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012F2 |. C9 LEAVE
004012F3 |. C2 1000 RETN 10
004012F6 |> 68 38304000 PUSH Key-Crac.00403038 ; /String = "lansechenxi"
004012FB |. E8 30010000 CALL <JMP.&KERNEL32.lstrlenA> ; \lstrlenA
00401300 |. 33F6 XOR ESI,ESI ;ESI清0
00401302 |. 8BC8 MOV ECX,EAX
00401304 |. B8 01000000 MOV EAX,1 ;EAX=1
00401309 |> 8B15 38304000 /MOV EDX,DWORD PTR DS:[403038] ;
0040130F |. 8A90 37304000 |MOV DL,BYTE PTR DS:[EAX+403037] ;第EAX位用户名 EAX=EAX+1
00401315 |. 81E2 FF000000 |AND EDX,0FF ;EDX和OFF与运算,保留第EAX字母
0040131B |. 8BDA |MOV EBX,EDX ;EBX=第EAX字母0040131D |. 0FAFDA |IMUL EBX,EDX ;第EAX位字母的ASCII的平方
00401320 |. 03F3 |ADD ESI,EBX ;ESI=ESI+第EAX位字母的ASCII的平方
00401322 |. 8BDA |MOV EBX,EDX ;EBX=第EAX字母
00401324 |. D1FB |SAR EBX,1 ;EBX右移1位
00401326 |. 03F3 |ADD ESI,EBX ;
00401328 |. 2BF2 |SUB ESI,EDX ;
0040132A |. 40 |INC EAX ;EAX=1+1=2
0040132B |. 49 |DEC ECX ;ECX=B-1=A(我输入的是“lansechenxi”所以为11位)
0040132C |.^ 75 DB \JNZ SHORT Key-Crac.00401309 ;不为0则跳
0040132E |. 56 PUSH ESI
0040132F |. 68 38314000 PUSH Key-Crac.00403138
00401334 |. E8 4A000000 CALL Key-Crac.00401383
00401339 |. 5E POP ESI
0040133A |. 3BC6 CMP EAX,ESI
0040133C |. 75 15 JNZ SHORT Key-Crac.00401353
0040133E |. 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
00401340 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
00401345 |. 68 B8344000 PUSH Key-Crac.004034B8 ; | good job, i wish you the very best
0040134A |. 6A 00 PUSH 0 ; |hOwner = NULL
0040134C |. E8 9D000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401351 |. EB 13 JMP SHORT Key-Crac.00401366
程序先取下两个窗口所输入的数值,判断是否有效。然后根据用户名算出正确的注册码和我们输入的注册码进行比较。相同则提示注册成功,不同则提示相应的错误信息。
好了,我们一直按F8,让程序运行到0040133A处的CMP EAX,ESI,我们按照上面说的方法,也取得了正确的注册码。
我们现在来试着分析注册码的算法,大家看上面反汇编代码的红色部分,是此CrackMe的注册码算法,我已经标注了每条语句的基本功能。此算法是这样的,依次取用户名的一位字符,换成16进制ASCII后开平方得到值,加上此ASCII做算术右移1位的值,再减去此ASCII码。最后把每一位的结果相加,这就是正确的注册码。
我们来再做一次实验,看这种算法对不对。
我们假设用户名输入的是“lscx”,四个字符的转换成16进制分别为6C、73、63、78。
开始计算:
第一位“l”:6C*6C=2D90,6C右移1位为36,2D90+36-3C=2D5A
同样的方法,依次算得s-336F;c-2617;x-3804。累加得BEE4,换成10进制是48868。这也是正确的注册码。
好了,就说到这里,有兴趣的朋友,可以试着写出注册机。
---===本帖最后由 蓝色晨曦 于 2007-4-1 10:45 编辑===---
好了,我们言归正传
首先运行一次CrackMe,输入用户名和假码提示信息为you have enter a wrong serial, please try again 。我们把这句话记下来。
用PEID查壳,无壳,入口点地址为401000。
打开动态调试器OD,载入此CrackMe。来到这里,程序的入口点401000处
00401000 >/$ 6A 00 PUSH 0 ; /(初始 cpu 选择)
00401002|.E8 23040000CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401007 |. A3 F0344000 MOV DWORD PTR DS:[4034F0],EAX
'j'
在OD的反汇编区,点鼠标右键,选择查找超级字串参考的ASCII,看到我们刚记下来的you have enter a wrong serial, please try again这句话。在此字串上双击,来到程序的此段代码中。
00401334 |. E8 4A000000 CALL Key-Crac.00401383 ;关键CALL,我们在此下断
00401339 |. 5E POP ESI
0040133A |. 3BC6 CMP EAX,ESI ;比较真假注册码
0040133C |. 75 15 JNZ SHORT Key-Crac.00401353 ;关键跳转,如果上面的CMP不相等,则跳转至显示出错信息。
0040133E |. 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
00401340 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
00401345 |. 68 B8344000 PUSH Key-Crac.004034B8 ; | good job, i wish you the very best
0040134A |. 6A 00 PUSH 0 ; |hOwner = NULL
0040134C |. E8 9D000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401351 |. EB 13 JMP SHORT Key-Crac.00401366
00401353 |> 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL;来自0040133C的跳转
00401355 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
0040135A |. 68 86344000 PUSH Key-Crac.00403486 ; | you have enter a wrong serial, please try again ;输入错误的序列号后的错误提示
0040135F |. 6A 00 PUSH 0 ; |hOwner = NULL
00401361 |. E8 88000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA;显示错误信息
我们分析一下,当输入不正确的注册码的时候,程序弹出的错误信息是从0040133C跳转过来的。所以,我们在跳转之前的CALL上下断。F9运行程序,弹出程序主窗口,我们输入以下的假注册信息,用户名“lansechenxi”,注册码“54321”。点“CHECK”后程序断下,OD自动激活。断在了00401334处,(反汇编代码可参看上面)我们这里暂时先不关心注册码的算法,因为此CrackMe是简单的明码比较。按F8两次后,单步执行程序到CMP EAX,ESI处,在OD的信息区看到ESI=0001E5C2;EAX=0000D431。呵呵,到了这里大家也许已经想到,0000D431不就是10进制的“54321”的16进制表示形式吗?那么ESI寄存器中的001E5C2应该就是正确的注册码了吧?确实如此,我们用系统自带的计算器把001E5C2换算成10进制为124354。(OD本身就带有换算功能)
我们试试,哈哈成功了,是不是很简单?

这个时候有一个新的问题,假如程序当输入假码时没有错误提示信息怎么办?那不就查找不到相应的字串了吗?这时候,我们可以碰碰运气,用OD查找字串参考时,看能不能找到当输入正确注册码时,程序给出的信息,如本例中的“good job, i wish you the very best”这句话。从这入手,用上面的方法,也能达到破解的目的。但是,如果程序压根就把这些重要信息加了密的话,那这种查找字串的手法,就没辙了。我们只能另外想办法。我们要用的就是在API函数上下断的方法。重要的函数有,USER32.DLL中的GetWindowsTextA和GetDlgItemTextA,我们今天就要用到其中之一。这两个函数的作用分别是,复制窗口中的字符到缓冲区和返回对话框中某一个窗口的标题或文字。
还是用此CrackMe为例说明
我们用OD重新载入程序,来到程序的入口点。在反汇编区,点鼠标右键---查找---当前模块中的名称(对应的快捷键为ctrl+N),看到本程序用到了GetDlgItemTextA函数。光标移到此函数处点鼠标右键,选择查找输入函数参考。会弹出一个新的窗口,列出此CrackMe程序中用到的所有GetDlgItemTextA函数。这里我们看到有两个,不难理解,因为此CrackMe是要输入用户名和注册码两种信息的。我们现在分不清哪个函数对哪个信息,所以干脆两个上面都下断。F9运行程序,弹出程序主窗口,我们还是输入以下的假注册信息,用户名“lansechenxi”,注册码“54321”。点“CHECK”后程序断下,OD自动激活。取下这么一段反汇编代码分析。
004012BD |. E8 08010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA;程序中断在此处,取得用户名窗口所输入数值
004012C2 |. 83F8 00 CMP EAX,0 ;检查是否输入用户名
004012C5 |. 74 18 JE SHORT Key-Crac.004012DF ;如未输入用户名则跳转至004012DF
004012C7 |. 6A 40 PUSH 40 ; /Count = 40 (64.)
004012C9 |. 68 38314000 PUSH Key-Crac.00403138 ; |Buffer = Key-Crac.00403138
004012CE |. 6A 6B PUSH 6B ; |ControlID = 6B (107.)
004012D0 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004012D3 |. E8 F2000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA;取得注册码窗口所输入数值
004012D8 |. 83F8 00 CMP EAX,0 ;检查是否输入注册码
004012DB |. 74 02 JE SHORT Key-Crac.004012DF ;如未输入注册码则跳转至004012DF
004012DD |. EB 17 JMP SHORT Key-Crac.004012F6
004012DF |> 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
004012E1 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
004012E6 |. 68 00304000 PUSH Key-Crac.00403000 ; |please fill in 1 more char!!
004012EB |. 6A 00 PUSH 0 ; |hOwner = NULL
004012ED |. E8 FC000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012F2 |. C9 LEAVE
004012F3 |. C2 1000 RETN 10
004012F6 |> 68 38304000 PUSH Key-Crac.00403038 ; /String = "lansechenxi"
004012FB |. E8 30010000 CALL <JMP.&KERNEL32.lstrlenA> ; \lstrlenA
00401300 |. 33F6 XOR ESI,ESI ;ESI清0
00401302 |. 8BC8 MOV ECX,EAX
00401304 |. B8 01000000 MOV EAX,1 ;EAX=1
00401309 |> 8B15 38304000 /MOV EDX,DWORD PTR DS:[403038] ;
0040130F |. 8A90 37304000 |MOV DL,BYTE PTR DS:[EAX+403037] ;第EAX位用户名 EAX=EAX+1
00401315 |. 81E2 FF000000 |AND EDX,0FF ;EDX和OFF与运算,保留第EAX字母
0040131B |. 8BDA |MOV EBX,EDX ;EBX=第EAX字母0040131D |. 0FAFDA |IMUL EBX,EDX ;第EAX位字母的ASCII的平方
00401320 |. 03F3 |ADD ESI,EBX ;ESI=ESI+第EAX位字母的ASCII的平方
00401322 |. 8BDA |MOV EBX,EDX ;EBX=第EAX字母
00401324 |. D1FB |SAR EBX,1 ;EBX右移1位
00401326 |. 03F3 |ADD ESI,EBX ;
00401328 |. 2BF2 |SUB ESI,EDX ;
0040132A |. 40 |INC EAX ;EAX=1+1=2
0040132B |. 49 |DEC ECX ;ECX=B-1=A(我输入的是“lansechenxi”所以为11位)
0040132C |.^ 75 DB \JNZ SHORT Key-Crac.00401309 ;不为0则跳
0040132E |. 56 PUSH ESI
0040132F |. 68 38314000 PUSH Key-Crac.00403138
00401334 |. E8 4A000000 CALL Key-Crac.00401383
00401339 |. 5E POP ESI
0040133A |. 3BC6 CMP EAX,ESI
0040133C |. 75 15 JNZ SHORT Key-Crac.00401353
0040133E |. 6A 00 PUSH 0 ; /Style. = MB_OK|MB_APPLMODAL
00401340 |. 68 62344000 PUSH Key-Crac.00403462 ; |key/crackme #2
00401345 |. 68 B8344000 PUSH Key-Crac.004034B8 ; | good job, i wish you the very best
0040134A |. 6A 00 PUSH 0 ; |hOwner = NULL
0040134C |. E8 9D000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401351 |. EB 13 JMP SHORT Key-Crac.00401366
程序先取下两个窗口所输入的数值,判断是否有效。然后根据用户名算出正确的注册码和我们输入的注册码进行比较。相同则提示注册成功,不同则提示相应的错误信息。
好了,我们一直按F8,让程序运行到0040133A处的CMP EAX,ESI,我们按照上面说的方法,也取得了正确的注册码。
我们现在来试着分析注册码的算法,大家看上面反汇编代码的红色部分,是此CrackMe的注册码算法,我已经标注了每条语句的基本功能。此算法是这样的,依次取用户名的一位字符,换成16进制ASCII后开平方得到值,加上此ASCII做算术右移1位的值,再减去此ASCII码。最后把每一位的结果相加,这就是正确的注册码。
我们来再做一次实验,看这种算法对不对。
我们假设用户名输入的是“lscx”,四个字符的转换成16进制分别为6C、73、63、78。
开始计算:
第一位“l”:6C*6C=2D90,6C右移1位为36,2D90+36-3C=2D5A
同样的方法,依次算得s-336F;c-2617;x-3804。累加得BEE4,换成10进制是48868。这也是正确的注册码。
好了,就说到这里,有兴趣的朋友,可以试着写出注册机。
---===本帖最后由 蓝色晨曦 于 2007-4-1 10:45 编辑===---
导入论坛 引用链接 收藏 分享给好友 推荐到圈子 管理 举报
TAG:



