首页
社区
课程
招聘
[原创]Triangle CrackMe
发表于: 2013-7-25 10:59 7736

[原创]Triangle CrackMe

2013-7-25 10:59
7736
名字:
Triangle KeyGenMe
下载地址:
见附件
界面:

加壳情况:

定位注册算法位置:
下GetDlgItemTextA断点,返回到Alt+F9程序领空
输入的信息
aaaaaa
bbbbbb
分析算法:
004012B0  push    32                               ; /Count = 32 (50.)
004012B2  push    0040315C                         ; |Buffer = KeyGenMe.0040315C
004012B7  push    0FA1                             ; |ControlID = FA1 (4001.)
004012BC  push    dword ptr [ebp+8]                ; |hWnd
004012BF  call    <jmp.&user32.GetDlgItemTextA>    ; \GetDlgItemTextA
004012C4  push    12                               ; /Count = 12 (18.)
004012C6  push    004031D2                         ; |Buffer = KeyGenMe.004031D2
004012CB  push    0FA2                             ; |ControlID = FA2 (4002.)
004012D0  push    dword ptr [ebp+8]                ; |hWnd
004012D3  call    <jmp.&user32.GetDlgItemTextA>    ; \GetDlgItemTextA
004012D8  cmp     eax, 10
004012DB  jnz     00401371


获取账号密码,分别放在[0040315C]和[004031D2]
密码必须等于16个字符,所以要将输入的信息更换如下
aaaaaa
bbbbbbbbbbbbbbbb

004012E1  push    0040315C                         ;  ASCII "aaaaaa"
004012E6  call    004013EB
{
004013EB  push    ebp
004013EC  mov     ebp, esp
004013EE  pushad
004013EF  push    dword ptr [ebp+8]
004013F2  call    004010F3                          ;  计算用户名的长度
004013F7  mov     ecx, 63
004013FC  sub     ecx, eax                          ;  63-6 in this case
004013FE  mov     edi, dword ptr [ebp+8]
00401401  add     edi, eax
00401403  inc     edi
00401404  /stos    byte ptr es:[edi]
00401405  |inc     al
00401407  \loopd   short 00401404                   ;  这个循环初始化一个表
00401409  finit                                     ;  初始化浮点数寄存器
0040140B  push    dword ptr [ebp+8]
0040140E  call    004010F3                          ;  计算用户名的长度
00401413  mov     dword ptr [4031E3], eax
00401418  fild    dword ptr [4031E3]                ;  st(0)=用户名长度=6
0040141E  fst     qword ptr [4031E7]                ;  [4031E7]=st(0)
00401424  fst     qword ptr [4031F7]                ;  [4031F7]=st(0)
0040142A  fstp    qword ptr [4031FF]                ;  [4031FF]=st(0) 注意fstp会出栈
00401430  mov     eax, 2
00401435  mov     dword ptr [4031E3], eax
0040143A  fild    dword ptr [4031E3]
00401440  fstp    qword ptr [40323F]                ;  即[40323F]=[4031E3]=eax=2
00401446  xor     eax, eax
00401448  mov     dword ptr [4031E3], eax
0040144D  fild    dword ptr [4031E3]
00401453  fst     qword ptr [403237]                ;  [403237]=eax=0
00401459  fstp    qword ptr [40324F]                ;  [40324F]=eax=0 注意fstp会出栈
0040145F  xor     ecx, ecx
00401461  mov     ebx, dword ptr [ebp+8]            ;  ebx->用户名 "aaaaaa"
00401464  /cmp     ecx, 63
00401467  |jnb     00401556                         ;  不小于63
0040146D  |movzx   eax, byte ptr [ebx]
00401470  |mov     dword ptr [4031E3], eax
00401475  |fild    dword ptr [4031E3]
0040147B  |fsqrt
0040147D  |fst     qword ptr [4031EF]               ;  [4031EF]=用户名第一个字母开平方
00401483  |fstp    qword ptr [403217]               ;  [403217]=用户名第一个字母开平方 出栈
00401489  |inc     ebx
0040148A  |movzx   eax, byte ptr [ebx]
0040148D  |mov     dword ptr [4031E3], eax
00401492  |fild    dword ptr [4031E3]
00401498  |fsqrt
0040149A  |fst     qword ptr [403207]               ;  [403207]=用户名第二个字母开平方
004014A0  |fstp    qword ptr [40320F]               ;  [40320F]=用户名第二个字母开平方
004014A6  |fld     qword ptr [403217]               ;  第一个字母开平方
004014AC  |fld     qword ptr [4031F7]               ;  用户名长度
004014B2  |faddp   st(1), st                        ;  两个加起来放到st(0) [随便起个名字:长方]
004014B4  |fld     qword ptr [40320F]
004014BA  |fld     qword ptr [4031EF]
004014C0  |fsubp   st(1), st                        ;  1,2个字母的平方相减
004014C2  |fmulp   st(1), st                        ;  长方(就是现在的st(1))*st(0)
004014C4  |fdiv    qword ptr [40323F]               ;  除以2
004014CA  |fstp    qword ptr [403247]               ;  [403247]=st(0)/2
004014D0  |fld     qword ptr [403207]               ;  第二个字母平方
004014D6  |fld     qword ptr [403217]               ;  第一个字母平方
004014DC  |faddp   st(1), st                        ;  加起来
004014DE  |fld     qword ptr [4031FF]               ;  用户名长度
004014E4  |fld     qword ptr [40320F]               ;  第二个字母开方
004014EA  |fsubp   st(1), st
004014EC  |fmulp   st(1), st                        ;  相减再相乘
004014EE  |fdiv    qword ptr [40323F]               ;  除以2
004014F4  |fld     qword ptr [403247]               ;  装入之前算的[403247]
004014FA  |fadd    st, st(1)
004014FC  |fstp    qword ptr [403247]               ;  加起来又放回[403247]
00401502  |ffree   st                               ;  invalidate st(0)
00401504  |fld     qword ptr [403207]               ;  第二个字母平方
0040150A  |fld     qword ptr [4031F7]               ;  用户名长度
00401510  |faddp   st(1), st                        ;  加起来
00401512  |fld     qword ptr [4031FF]               ;  用户名长度
00401518  |fld     qword ptr [4031EF]               ;  第一个字母
0040151E  |fsubp   st(1), st
00401520  |fmulp   st(1), st
00401522  |fdiv    qword ptr [40323F]               ;  相减,相乘 再除以2
00401528  |fld     qword ptr [403247]               ;  装入之前算的[403247]
0040152E  |fsub    st, st(1)                        ;  相减
00401530  |fcom    qword ptr [403237]               ;  st(0)跟[403237]比较,影响对应标记位
00401536  |fstsw   ax                               ;  复制浮点标记寄存器(应该是这么叫吧)到ax
00401538  |test    ax, 100
0040153C  |je      short 00401540
0040153E  |fchs                                     ;  不等于则取相反值
00401540  |fld     qword ptr [40324F]               ;  载入0
00401546  |faddp   st(1), st
00401548  |fstp    qword ptr [40324F]               ;  相加放回[40324F],可以看出这个call主要就是计算这个地址放的东西啦
0040154E  |ffree   st
00401550  |inc     ecx
00401551  \jmp     00401464
00401556  popad
00401557  leave
00401558  retn    4
}

吓我一跳,都是浮点操作的汇编指令 照着书看就行了。。

004012EB  push    1
004012ED  push    8
004012EF  push    0040324F
004012F4  push    004031C1                          ;  ASCII "A376957E38A17340"
004012F9  call    004010B1
{
004010B1  push    ebp                               ;  很简单,就是查表,弄一条字符串出来放到4031C1
004010B2  mov     ebp, esp
004010B4  pushad
004010B5  xor     eax, eax
004010B7  mov     ecx, dword ptr [ebp+10]
004010BA  mov     ebx, dword ptr [ebp+8]
004010BD  mov     edx, dword ptr [ebp+C]
004010C0  cmp     dword ptr [ebp+14], 1
004010C4  jnz     short 004010CD
004010C6  mov     esi, 00403000                     ;  ASCII "0123456789ABCDEF0123456789abcdef+-Need at least "
004010CB  jmp     short 004010D2
004010CD  mov     esi, 00403010                     ;  ASCII "0123456789abcdef+-Need at least "
004010D2  /mov     al, byte ptr [edx]
004010D4  |shr     al, 4
004010D7  |mov     al, byte ptr [eax+esi]
004010DA  |mov     byte ptr [ebx], al
004010DC  |inc     ebx
004010DD  |mov     al, byte ptr [edx]
004010DF  |and     al, 0F
004010E1  |mov     al, byte ptr [eax+esi]
004010E4  |mov     byte ptr [ebx], al
004010E6  |inc     ebx
004010E7  |inc     edx
004010E8  \loopd   short 004010D2
004010EA  xor     al, al
004010EC  mov     byte ptr [ebx], al
004010EE  popad
004010EF  leave
004010F0  retn    10
}

004012FE  push    004031D2                          ;  ASCII "bbbbbbbbbbbbbbbb"
00401303  push    004031C1                          ;  ASCII "A376957E38A17340"
00401308  call    00401378							;  这个是关键call
{
00401378  push    ebp                              ;  比较神奇的校验用户和密码的方法。。
00401379  mov     ebp, esp
0040137B  pushad
0040137C  mov     ecx, 10
00401381  xor     edx, edx
00401383  xor     ebx, ebx
00401385  mov     esi, dword ptr [ebp+8]           ;  User Magic String
00401388  mov     edi, dword ptr [ebp+C]           ;  password
0040138B  mov     ah, byte ptr [ebx+esi]
0040138E  mov     al, byte ptr [ebx+edi]
00401391  cmp     ah, 39
00401394  jnz     short 0040139A
00401396  jmp     short 004013C7
00401398  jmp     short 004013C5
0040139A  cmp     ah, 46
0040139D  jnz     short 004013A3
0040139F  jmp     short 004013C7                   ;  异或重来
004013A1  jmp     short 004013C5                   ;  减一异或重来
004013A3  cmp     al, 30
004013A5  jb      short 004013C3
004013A7  cmp     al, 39
004013A9  ja      short 004013AF
004013AB  jmp     short 004013C5                   ;  减一异或重来
004013AD  jmp     short 004013C5                   ;  减一异或重来
004013AF  cmp     al, 41
004013B1  jb      short 004013BF
004013B3  cmp     al, 46
004013B5  ja      short 004013BB
004013B7  jmp     short 004013C5                   ;  减一异或重来
004013B9  jmp     short 004013C5                   ;  减一异或重来
004013BB  jmp     short 004013D7                   ;  错误
004013BD  jmp     short 004013C5                   ;  减一异或重来
004013BF  jmp     short 004013D7                   ;  错误
004013C1  jmp     short 004013C5                   ;  减一异或重来
004013C3  jmp     short 004013D7                   ;  错误
004013C5  dec     al
004013C7  xor     al, ah
004013C9  jnz     short 004013CC                   ;  al-1必须等于ah
004013CB  inc     edx
004013CC  inc     ebx
004013CD  dec     ecx
004013CE  jnz     short 0040138B
004013D0  cmp     edx, 10
004013D3  jnz     short 004013D7                   ;  edx必须等于16
004013D5  jmp     short 004013E1                   ;  正确
004013D7  popad
004013D8  mov     eax, 0
004013DD  leave
004013DE  retn    8
004013E1  popad
004013E2  mov     eax, 1
004013E7  leave
004013E8  retn    8
}


重上面看下来没看出什么端倪,但是从最后面回溯可以发现edx必须等于16(十进制),进而推出al-1必须等于ah,所以我们根据A376957E38A17340,把密码改成这样:B487@68F49B28451
即每个字母的ASCII都减1。
现在再运行,发现到最后edx竟然是F,就是还差一个. 调试发现是因为”A376957E38A17340”中第五个字符’9’使得地址00401396处的jmp得以执行,跳过了dec al这步,所以,当”A376957E38A17340”中字符’9’出现的时候,密码处也应该有一个相同的字符’9’
最终密码为B487968F49B28451

0040130D  cmp     eax, 1
00401310  jnz     short 00401328
00401312  push    40                                ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401314  push    004030D3                          ; |Title = "Goodboy"
00401319  push    0040306C                          ; |Text = "Yes! You made it! I hope you didn't patch anything and you'll write a nice tutorial for crackmes.de ;)"
0040131E  push    dword ptr [ebp+8]                 ; |hOwner
00401321  call    <jmp.&user32.MessageBoxA>         ; \MessageBoxA


这里是比较了,很简单。

注册机编写:
将这两个CALL(call    004013EB和call    004010B1)摘下来,再自己写一个函数将生成的字符串的ASCII全部减1(当然了,遇到字符’9’就不需要减1了)

CrackMe流程总结:


经验积累:
如果从上到下无法得出什么结论,可以从后面向前面回溯得出正确密码所需的特征

备注:后来看了下老外的solution,其实遇到字符'F'的时候ASCII也会减一

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
It just sank and sank, this poor little thing.
2013-7-25 19:41
0
雪    币: 95
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
偶也下载看看!
2013-7-25 22:37
0
雪    币: 95
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢楼主分享!我是初学者,也破了一下,感觉难度不小!
2013-7-26 12:31
0
雪    币: 45
活跃值: (55)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
成功了否,觉得只是最后一个call有一点点纠结而已
2013-7-26 14:14
0
游客
登录 | 注册 方可回帖
返回