首页
社区
课程
招聘
[原创]题目为“Splish,Splash”的crackme的算法分析(菜鸟第一次)
发表于: 2006-6-17 16:18 9362

[原创]题目为“Splish,Splash”的crackme的算法分析(菜鸟第一次)

2006-6-17 16:18
9362

【破解作者】 wildbug
【作者邮箱】 :)
【作者主页】 :)
【使用工具】 OD, PEiD,ASCII码转换,windows计算器
【破解平台】 Win98
【软件名称】 题目为“Splish,Splash”的crackme
【下载地址】 看雪论坛精华5-序列号-splish
【软件简介】 一个crackme,有3个输入框,第一个为Hard Coded,输入,下面有Check HardCoded按钮,第二、三是输入Name和

Serial的,下面有Name/Serial Check按钮。
【软件大小】 232KB
【加壳方式】 无
【破解声明】 献给和我一样看到无明码就想爆破的兄弟:)
--------------------------------------------------------------------------------
【破解内容】

初步分析:
此Crackme分2步,第一步是输入“HardCoded”,第2步是输入注册名和注册码。用 PEiD查壳,为MASM32 / TASM32,
嘿嘿,无壳好啊。实验下,随便在“HardCoded”输入1234,有提示“sorry,please try again”,在Name/Serial处
输入Name:wildbug,Serial:987654321,也有同样提示:“sorry,please try again”,嘿嘿,有提示就好办了。

破解过程:
用od载入,超级字符串查找“sorry,please try again”。在004013D9和004016E9各有1个,双击004013D9,往上找
到这里:
00401353 |. 48 61 72 64 4>ASCII "HardCoded",0
0040135D |> 6A 20 PUSH 20 ; /Count = 20 (32.)
0040135F |. 68 15324000 PUSH SPLISH.00403215 ; |Buffer = SPLISH.00403215
00401364 |. FF35 90344000 PUSH DWORD PTR DS:[403490] ; |hWnd = 00000D1C

(class='Edit',wndproc=8057DB36,parent=00000D7C)
0040136A |. E8 BB030000 CALL <JMP.&USER32.GetWindowTextA> ; \GetWindowTextA
0040136F |. 8D05 53134000 LEA EAX,DWORD PTR DS:[401353]-----------------F2下断
00401375 |. 8D1D 15324000 LEA EBX,DWORD PTR DS:[403215]

在GetWindowTextA下面的0040136F下断,运行程序,在HardCoded处输入“1234”,点Check HardCoded按钮,在
od中断下,
0040136F |. 8D05 53134000 LEA EAX,DWORD PTR DS:[401353]----断在这里,把401353处字符放到eax
00401375 |. 8D1D 15324000 LEA EBX,DWORD PTR DS:[403215] 把403215中的输入的“1234”放到ebx
0040137B |> 8038 00 /CMP BYTE PTR DS:[EAX],0 比较完了吗?
0040137E |. 74 0C |JE SHORT SPLISH.0040138C 完了就跳转,也就是胜利了
00401380 |. 8A08 |MOV CL,BYTE PTR DS:[EAX] eax中第一个字符放到CL,
00401382 |. 8A13 |MOV DL,BYTE PTR DS:[EBX] ebx中第一个字符放到DL
00401384 |. 38D1 |CMP CL,DL 比较是否一样
00401386 |. 75 4A |JNZ SHORT SPLISH.004013D2 不一样就跳到失败
00401388 |. 40 |INC EAX eax中的下一个字符
00401389 |. 43 |INC EBX ebx中的下一个字符
0040138A |.^ EB EF \JMP SHORT SPLISH.0040137B 循环
0040138C |> EB 2F JMP SHORT SPLISH.004013BD 到胜利处
0040138E |. 43 6F 6E 67 7>ASCII "Congratulations,"
0040139E |. 20 79 6F 75 2>ASCII " you got the har"
004013AE |. 64 20 63 6F 6>ASCII "d coded serial",0
004013BD |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004013BF |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
004013C4 |. 68 8E134000 PUSH SPLISH.0040138E ; |congratulations, you got the hard coded

serial
004013C9 |. 6A 00 PUSH 0 ; |hOwner = NULL
004013CB |. E8 78030000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004013D0 |. EB 13 JMP SHORT SPLISH.004013E5
004013D2 |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004013D4 |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
004013D9 |. 68 67304000 PUSH SPLISH.00403067 ; |sorry, please try again.
004013DE |. 6A 00 PUSH 0 ; |hOwner = NULL
004013E0 |. E8 63030000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA

我很菜的,只要看到算法分析,没有明码的第一感觉就是“爆”,这次呢,看到这里,有了点信心,感觉就是输入
的字符和“HardCoded”比较么,我试下,输入“HardCoded”,果然成功了。嘿嘿,对往下继续有了点期待。
在字符串参考中的004016E9处双击,往上找,有2处连在一起的GetWindowTextA,嘿嘿,就是这里了,在第2处下面
下断,输入Name:wildbug,Serial:987654321,点Name/Serial Check按钮,断在这里
0040160D |. E8 18010000 CALL <JMP.&USER32.GetWindowTextA> ; \GetWindowTextA
00401612 |. 85C0 TEST EAX,EAX --F2下断,断下,测试name输入没,
00401614 |. 74 68 JE SHORT SPLISH.0040167E 没有就跳走
00401616 |. A3 63344000 MOV DWORD PTR DS:[403463],EAX
0040161B |. 33C9 XOR ECX,ECX
0040161D |. 33DB XOR EBX,EBX
0040161F |. 33D2 XOR EDX,EDX
00401621 |. 8D35 36324000 LEA ESI,DWORD PTR DS:[403236] 把输入的name放到esi
00401627 |. 8D3D 58324000 LEA EDI,DWORD PTR DS:[403258]
0040162D |. B9 0A000000 MOV ECX,0A
00401632 |> 0FBE041E /MOVSX EAX,BYTE PTR DS:[ESI+EBX]name第一位转成16进制,放到eax
00401636 |. 99 |CDQ 扩展
00401637 |. F7F9 |IDIV ECX eax除以ecx,dl=余数=edx
00401639 |. 33D3 |XOR EDX,EBX 余数和当前位数-1进行xor
0040163B |. 83C2 02 |ADD EDX,2 edx加2
0040163E |. 80FA 0A |CMP DL,0A 比较dl和0a
00401641 |. 7C 03 |JL SHORT SPLISH.00401646 比oa小的话就跳转
00401643 |. 80EA 0A |SUB DL,0A 比oa大的话就减去0a
00401646 |> 88141F |MOV BYTE PTR DS:[EDI+EBX],DL 把算出的DL放到EDI+EBX,第一个是403258
00401649 |. 43 |INC EBX ebx加1,算出一个就加1
0040164A |. 3B1D 63344000 |CMP EBX,DWORD PTR DS:[403463] 403463放的是name的长度,和已经计算过的比较
00401650 |.^ 75 E0 \JNZ SHORT SPLISH.00401632 没有计算完就继续计算。
00401652 |. 33C9 XOR ECX,ECX
00401654 |. 33DB XOR EBX,EBX
00401656 |. 33D2 XOR EDX,EDX
00401658 |. 8D35 42324000 LEA ESI,DWORD PTR DS:[403242] 把输入注册码放到esi
0040165E |. 8D3D 4D324000 LEA EDI,DWORD PTR DS:[40324D]
00401664 |. B9 0A000000 MOV ECX,0A
00401669 |> 0FBE041E /MOVSX EAX,BYTE PTR DS:[ESI+EBX]把注册码的第一位转成16进制
0040166D |. 99 |CDQ 扩展
0040166E |. F7F9 |IDIV ECX 除以ecx
00401670 |. 88141F |MOV BYTE PTR DS:[EDI+EBX],DL 把dl放到EDI+EBX 第一位是40324d
00401673 |. 43 |INC EBX ebx加1记数
00401674 |. 3B1D 67344000 |CMP EBX,DWORD PTR DS:[403467] 注册码计算完了吗?
0040167A |.^ 75 ED \JNZ SHORT SPLISH.00401669 没有就继续计算
0040167C |. EB 2A JMP SHORT SPLISH.004016A8 算好了就跳转
0040167E |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401680 |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
00401685 |. 68 A0304000 PUSH SPLISH.004030A0 ; |please enter your name.
0040168A |. 6A 00 PUSH 0 ; |hOwner = NULL
0040168C |. E8 B7000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401691 |. EB 62 JMP SHORT SPLISH.004016F5
00401693 |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401695 |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
0040169A |. 68 B8304000 PUSH SPLISH.004030B8 ; |please enter your serial number.
0040169F |. 6A 00 PUSH 0 ; |hOwner = NULL
004016A1 |. E8 A2000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004016A6 |. EB 4D JMP SHORT SPLISH.004016F5
004016A8 |> 8D35 4D324000 LEA ESI,DWORD PTR DS:[40324D] 跳到这里,把注册码算好的数放到esi
004016AE |. 8D3D 58324000 LEA EDI,DWORD PTR DS:[403258] 把用name算好的数放到edi
004016B4 |. 33DB XOR EBX,EBX
004016B6 |> 3B1D 63344000 /CMP EBX,DWORD PTR DS:[403463] 比较几次?和name的位数相同,这里是7次。
004016BC |. 74 0F |JE SHORT SPLISH.004016CD 比较完了就成功了
004016BE |. 0FBE041F |MOVSX EAX,BYTE PTR DS:[EDI+EBX]把注册码算好的数的第一位放到eax
004016C2 |. 0FBE0C1E |MOVSX ECX,BYTE PTR DS:[ESI+EBX]把用name算好的数的第一位放到ecx
004016C6 |. 3BC1 |CMP EAX,ECX 一位一位比较
004016C8 |. 75 18 |JNZ SHORT SPLISH.004016E2 不相等就结束,失败。
004016CA |. 43 |INC EBX 下一位。
004016CB |.^ EB E9 \JMP SHORT SPLISH.004016B6 循环。
004016CD |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004016CF |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
004016D4 |. 68 42304000 PUSH SPLISH.00403042 ; |good job, now keygen it.
004016D9 |. 6A 00 PUSH 0 ; |hOwner = NULL
004016DB |. E8 68000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004016E0 |. EB 13 JMP SHORT SPLISH.004016F5
004016E2 |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004016E4 |. 68 0A304000 PUSH SPLISH.0040300A ; |splish, splash
004016E9 |. 68 67304000 PUSH SPLISH.00403067 ; |sorry, please try again.
004016EE |. 6A 00 PUSH 0 ; |hOwner = NULL
004016F0 |. E8 53000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004016F5 |> C9 LEAVE
004016F6 \. C2 0800 RETN 8

晕,第一次搞算法,头都晕了,还好可以看内存窗口做参考:
0040324D 04050607
00403251 00010203 .
00403255 01000009 ...
00403259 04050206
0040325D 00000704 ..

这里就是比较的地方和比较的数字。

这个Crackme的算法是这样的,
1.把输入的name每一位转换成16进制,再除以0a,余数和位数xor,再加2,如果比0a大,就减去0a。
2.再把输入的注册码的每一位转换成16进制,再除以0a。
以输入的name的长度为比较次数,把1和2的计算结果一位一位的比较,有一位不相等就失败,达到比较次数就成功了。

比如第一位为w,ASCII为77,77 IDIV 0a,余数为09,09 xor 0,得09,09 add 2,得0b,0b大于0a,0b sub 0a,得01
所以算出的结果为01。

ASCII码表可输入数字从30-39(即0-9),32(即2)除以0A,余数为0,我用这个32做为基数来计算。

所以余数为1的是32+1=33,即相对的注册码数字为3。

同样可以算出:i算出的结果为06,相对的注册码为32+6=38,就是数字8。
......
g算出的结果为07,相对的注册码为32+7=39,就是数字9。

w i l d b u g ------注册的name
77 69 6C 64 62 75 67 ------转换的16进制
0 1 2 3 4 5 6 ------转换后的所在位数
01 06 02 05 04 04 07 ------注册name计算的结果
------------------------------全加上32(原因见上文)
33 38 34 37 36 36 39 ------注册Serial的16进制
3 8 4 7 6 6 9 ------呵呵,这就是注册Serial

一个可以用的注册码:
HardCoded:HardCoded
Name:wildbug
Serial:3847669

谢谢斑竹鼓励,呵呵 我可以上传附件了。
--------------------------------------------------------------------------------
【破解总结】

感觉这个crackme写的太好了,非常适合我们这些看到算法就想爆破的菜鸟学习,如果不是第一部分的破解给了我信心的话,我也

不会去继续分析第二部分的算法。建议每个想从爆破、找明码提高到分析算法,最终写出注册机的兄弟都去破下这个crackme。
--------------------------------------------------------------------------------
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (6)
雪    币: 370
活跃值: (78)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
2
新手第一次,鼓励一下。
2006-6-17 23:06
0
雪    币: 217
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
清楚明了,不错。
2006-6-18 13:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
确实很好。
我拿到软件,一般都是爆破了事,没有耐心去分析算法,写注册机。
看了这位仁兄的做饭,深表钦佩,学习ing...
我也要好好学习,从爆破手升级到拆雷专家。
2006-6-22 10:04
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
加油!!!!!!
2006-7-6 14:16
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢楼主,对我这样的材料很有帮助!
2009-10-18 17:07
0
雪    币: 347
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
本人感觉,这个CM写的不怎样,对用户名和序列号都没有长度和数据类型的判断,而且还有一个非常大的BUG:

只要序列号特别的长(也不用太长),也同样的可以验证过去,上图,用户名空,序列号我打了十几个空格
注册算法貌似不并不是LZ说的那样,有一些误差,不是用户名转化为十六进制然后进去算法部分的,真正计算的是ASCII,
用LZ的用户名+改变数字数组为全部字符测试一下:

注册机流程:
用户名:asmc------->ASCII------->除0xA,得到余数edx------->xor 各个位数------->如果大于0xA,则减0xA------->对0~9组成的数组与0xA进行求余,得到余数,如果和
前一步得到的结果相同,把现在的ASCII保存到目的数组中
其实这里,我只是建立一个静态的0到9的数组,这样每次轮训比较哪些数字和0xA的余是和算法中的结果相同的,如果相同就是序列号中的其中一位,这个数组还是可以把字符,特殊字符
也可以加进去的,因为只要满足ASCII与0xA余数和算法中的余数相同都可以作为序列号,CM没做判断导致的
用LZ的用户名+改数字数组得到的序列号是:

注册机写完:
case IDC_BTN_GEN:
  {
   char strName[MAX_PATH] = {0};
   char strPW[260] = {0};
   char strPW1[260] = {0};
   static char strHardCode[] = {"HardCoded"};
   static char strRand[] = {"0123456789"};
 
   int uLen = GetDlgItemText(hDlg, IDC_EDT_ID, strName, MAX_PATH);
 
   if (uLen!=0)
   {
    __asm
    {
      pushad
      xor     ecx, ecx
      xor     ebx, ebx
      xor     edx, edx
      lea     esi, dword ptr [strName]
      lea     edi, dword ptr [strPW]
      mov     ecx, 0xA
CORE:
      movsx   eax, byte ptr [esi+ebx]
      cdq
      idiv    ecx
      xor     edx, ebx
      add     edx, 2
      cmp     dl, 0xA
      jl      short LA
      sub     dl, 0xA
LA:
      push    esi
      push    ebx
      push    edx
      xor     ebx, ebx
      lea     esi, dword ptr [strRand]
LB: 
      movsx   eax, byte ptr [esi+ebx]
      cdq
      idiv    ecx
      inc     ebx
      cmp     dl, byte ptr [esp]
      jnz     short LB
      mul     cl
      add     al, dl
      pop     edx
      pop     ebx
      pop     esi
      mov     byte ptr [edi+ebx], al
      inc     ebx
      cmp     ebx, uLen
      jnz     short CORE
      popad
    }
    wsprintf(strPW1, TEXT("%s"),strPW);
    SetDlgItemText(hDlg, IDC_EDT_PW, strPW1);
    SetDlgItemText(hDlg, IDC_EDT_PW2, strHardCode);
   }
   else
   {
    MessageBox(hDlg,"阿噢,用户名必须是1~11位","",MB_OK);
   }


总结:
不知道LZ如何评价这个CM“太好了",我是一点都没看出来,哪里好了,可能也就是算法部分还可以,一个用户名可以对应几个序列号
KeyGen.zip
上传的附件:
2009-10-19 15:24
0
游客
登录 | 注册 方可回帖
返回
//