首页
社区
课程
招聘
Taliesin's KeyGenMe #1之逆向
发表于: 2006-7-11 23:13 6286

Taliesin's KeyGenMe #1之逆向

2006-7-11 23:13
6286

下载地址:http://www.crackmes.de/users/taliesin/kgm1tal

使用工具:
反编译及调试工具:IDA,OllyDbg
程序开发包:MASM32

1. 界面的生成

  这是一个汇编写的SDK程序,用IDA载入,找到入口点,可以轻易发现
GetModuleHandle,ExitProcess这些函数。出人意料的是这个程序竟然没
有静态资源,加载到内存才用自己的代码动态创建资源,因而Resource
Hacker这些工具无法派上用场。主界面是在资源创建完毕以后用
DialogBoxIndirectParam(好复杂的名字@_@)建立的对话框。这就是
说,我们能看到的资源全部是二进制形式的。如果知道资源的二进制与脚
本形式的对应关系,应该可以根据这些二进制资源重建一个资源脚本,以
便能够使用习惯的DialogBoxParam来建立界面。可惜鄙人目前还不会做这
种手工反编译资源的工作,创建资源这部分代码只好让它保持原样了。这
些代码在原程序中从地址401017开始到40122F结束。

  对话框的回调过程位于原程序中地址401230到401296,其中处理了
WM_COMMAND,WM_INITDIALOG,WM_CLOSE三种消息。由于后两种消息的处
理比较常规,分析起来也很简单,这里就不详述了。我们的重点是放在
WM_COMMAND的处理上,这涉及到程序的核心算法,也就是下一小节将要叙
述的内容。

2. 注册算法分析

  现在定位到对话框回调过程中的WM_COMMAND分支上:

===================================================
loc_40125A:                                ; CODE XREF: DialogFunc+Aj
                cmp        [ebp+uMsg], WM_COMMAND
                jnz        short loc_401280
                cmp        [ebp+wParam], ID_REGISTER ; 按下Register按钮
                jnz        short loc_401290
                call        _Detect_Int3_1        ; 检测过程401296中的Int3断点
                call        _Detect_Int3_2        ; 检测GetDlgItemTextA起始处的Int3断点
                push        [ebp+hDlg]        ; hDlg
                call        sub_401296
                jmp        short loc_401290
===================================================

其中_Detect_Int3_1和_Detect_Int3_2是自定义的名字,这是由于这两个
过程中采用检查代码字节是否为0CCh的方法检测Int3断点。如果在调试器
中对所检测区域内的位置下断,可能会立即跳到验证失败的分支。但是采
用OD的F4键“执行到此处”功能则可以轻易避开这一检测,因为这个功能
本质是基于SingleStep而不是断点中断。以下跟进过程sub_401296中:

===================================================
; Attributes: bp-based frame

; int __stdcall        sub_401296(HWND        hDlg)
sub_401296        proc near                ; CODE XREF: DialogFunc+49p
                                        ; DATA XREF: _Detect_Int3_1o

hDlg                = dword        ptr  8

                push        ebp
                mov        ebp, esp
                pusha
                mov        esi, offset loc_4012FE
                push        esi
                push        large dword ptr        fs:0 ; uType
                mov        large fs:0, esp
                push        nMaxCharName        ; nMaxCount
                push        offset szNameString ; lpString
                push        EDT_NAME        ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA        ; Name取30字符
                push        nMaxCharPwd        ; nMaxCount
                push        offset szPwdString ; lpString
                push        EDT_PASSWORD        ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA        ; Password取20字符
                call        _1stTestOfPwd
                push        offset szTestTable ; "ZWATRQLCGHPSXYENVBJDFKMU"
                call        sub_4013B6
                call        sub_4014CE
                push        0                ; nResult
                push        [ebp+hDlg]        ; hDlg
                call        EndDialog
                jmp        short loc_401324

loc_4012FE:                                ; DATA XREF: sub_401296+4o
                mov        esp, [esp+8]
                pop        large dword ptr        fs:0
                add        esp, 4
                popa
                push        MB_OK
                push        offset aSehInAssembly ;        "SEH in        Assembly"
                push        offset aExceptionHasBe ; "Exception has        been handled !!\r\nPress OK"...
                push        NULL                ; hWnd
                call        MessageBoxA
                jmp        short locret_40132E

loc_401324:                                ; CODE XREF: sub_401296+66j
                pop        large dword ptr        fs:0
                add        esp, 24h

locret_40132E:                                ; CODE XREF: sub_401296+8Aj
                                        ; sub_401296+8Cj
                leave
                retn        4
sub_401296        endp
===================================================

这个过程包含了获取输入的用户名及Password、以及验证的整个流程,正
常情况下应该在调用EndDialog以后结束。还有一段SEH,由于有地方没看
懂,在还原的时候把它删掉了(关键在于按照SEH的设置,产生异常的时
候流程转到loc_4012FE处执行,但为什么产生异常时的[esp+8]中存储的
恰好就是SEH设置完毕的esp值呢?)。其中EDT_NAME,EDT_PASSWORD这些
常数的等值定义都是根据创建控件资源的代码猜出来的。由此得知输入的
用户名只取前30字符,而密码只取前20字符。

  过程_1stTestOfPwd主要是对输入的密码格式作简单的前期合法性检
测,其代码如下:

===================================================
_1stTestOfPwd        proc near                ; CODE XREF: sub_401296+48p

                xor        eax, eax
                mov        ecx, 0
                mov        esi, offset szPwdString
                mov        al, [esi]
                jmp        short loc_401352

loc_401342:                                ; CODE XREF: _1stTestOfPwd+22j
                movzx        eax, al
                cmp        byte_403150[eax], 2 ; 实质是检测eax中是否大写字母
                jnz        short loc_401358 ; 不是则跳
                inc        ecx
                mov        al, [ecx+esi]

loc_401352:                                ; CODE XREF: _1stTestOfPwd+Ej
                cmp        al, 0                ; 已达串尾否
                ja        short loc_401342
                jmp        short loc_40135F

loc_401358:                                ; CODE XREF: _1stTestOfPwd+1Aj
                mov        LegalFlagOfPwd,        40h ; 置密码非法标志

loc_40135F:                                ; CODE XREF: _1stTestOfPwd+24j
                mov        esi, offset szNameString
                xor        ecx, ecx
                mov        eax, 1
                xor        edx, edx
                mov        byteChkSumName,        0

loc_401377:                                ; CODE XREF: _1stTestOfPwd+59j
                mov        ecx, 0
                mov        cl, [edx+esi]
                cmp        cl, 0
                jz        short loc_40138D
                inc        edx
                add        byte ptr byteChkSumName, cl
                jmp        short loc_401377

loc_40138D:                                ; CODE XREF: _1stTestOfPwd+50j
                mov        eax, byteChkSumName
                mov        ecx, 18h
                cdq
                idiv        ecx
                mov        ChkSumNameMod24, dl
                mov        cl, LegalFlagOfPwd
                cmp        cl, 40h
                jnz        short loc_4013B0
                jmp        loc_4014F5        ; 如果Password中有非大写字母则为非法

loc_4013B0:                                ; CODE XREF: _1stTestOfPwd+77j
                jmp        loc_401480

loc_401480:                                ; CODE XREF: _1stTestOfPwd:loc_4013B0j
                                        ; DATA XREF: _1stTestOfPwd+155o
                                        ; .text:004014C7o
                call        sub_401510      ; 检测Password第二个字符是否为'E'
                xor        ebx, ebx
                mov        edi, offset loc_401480
                sub        edi, 60h
                mov        eax, 0DEh
                xor        eax, 12h        ; 检测关键比较处的Int3断点
                mov        ecx, 59h
                repne scasb
                test        ecx, ecx
                jz        short locret_4014A8
                pop        esi
                xor        esi, esi
                push        edi
                jmp        short loc_4014F5

locret_4014A8:                                ; CODE XREF: _1stTestOfPwd+16Ej
                retn
===================================================

  这个过程有三个功能:第一,检验密码是否全由大写字母组成,以及
第二个字符是否为'E',过滤不满足这些条件的输入(地址403150起始的
地方有一个256字节的表,表中只有索引为65到90的字节等于2,正好是大
写字母ASCII码的范围);第二,计算出用户名字符串的字节校验和模24
的值(注意不是用户名各字符之和模24,要先模256再模24),该值存放
于变量ChkSumNameMod24中;第三,检测关键比较处的Int3断点。

  前期检测通过后,接下来就是关键比较了。该段代码在过程
sub_4013B6中:

===================================================
sub_4013B6        proc near                ; CODE XREF: sub_401296+52p

arg_0                = dword        ptr  8

                push        ebp
                mov        ebp, esp
                push        offset szPwdString
                call        sub_401540        ; 求长度的过程
                cmp        eax, 0Ah
                jnz        loc_4014F5        ; Password必须为10个字符
                mov        esi, offset szPwdString
                mov        eax, 0
                mov        ebx, 0
                xor        ecx, ecx
                jmp        short loc_4013E5

loc_4013DF:                                ; CODE XREF: sub_4013B6+32j
                mov        cl, [eax+esi]
                add        ebx, ecx        ; 将password前9字符之和累加放到ebx
                inc        eax

loc_4013E5:                                ; CODE XREF: sub_4013B6+27j
                cmp        eax, 9
                jb        short loc_4013DF
                mov        eax, ebx
                mov        ecx, 9
                cdq
                idiv        ecx
                mov        SumPwdDiv9, eax
                mov        edi, [ebp+arg_0] ; edi---->"ZWATRQLCGHPSXYENVBJDFKMU"
                mov        dl, ChkSumNameMod24
                mov        al, dl
                cmp        al, 18h
                jbe        short loc_40140A
                sub        al, 18h

loc_40140A:                                ; CODE XREF: sub_4013B6+50j
                mov        byte_40304E, al
                xor        eax, eax
                mov        al, byte_40304E
                mov        ah, [eax+edi]
                mov        dh, [esi]        ; esi(password)的第一个字符必须等于以chksumNameMod24做索引找到的字符
                cmp        ah, dh
                jnz        loc_4014F5
                sub        dh, 'A'
                mov        dh, dl                ; 这一句是否打错?
                mov        ah, 0
                mov        byte_40304E, al
                xor        eax, eax
                mov        al, byte_40304E
                add        al, dl
                cmp        al, 18h
                jbe        short loc_40143E
                sub        al, 18h

loc_40143E:                                ; CODE XREF: sub_4013B6+84j
                mov        ecx, 2
                mov        ah, [eax+edi]        ; esi(password)的第三个字符必须等于以chksumNameMod24*2做索引找到的字符
                mov        dh, [ecx+esi]
                cmp        ah, dh
                jnz        loc_4014F5
                jmp        short loc_401477

loc_401453:                                ; CODE XREF: sub_4013B6+C4j
                mov        byte_40304E, al
                xor        eax, eax
                mov        al, byte_40304E
                sub        dh, 'A'         ; 取得此字母在正规字母表中的索引
                mov        dl, dh
                inc        ecx
                add        al, dl                ; 加到刚才查表的索引
                cmp        al, 18h
                jbe        short loc_40146D
                sub        al, 18h

loc_40146D:                                ; CODE XREF: sub_4013B6+B3j
                mov        ah, [eax+edi]
                mov        dh, [ecx+esi]        ; 继续实施比较
                cmp        ah, dh
                jnz        short loc_4014F5

loc_401477:                                ; CODE XREF: sub_4013B6+9Bj
                cmp        ecx, 8                ; 直到比较完password的前9个字符
                jb        short loc_401453
                leave
                retn        4
sub_4013B6        endp
===================================================

根据这段程序,有效的password必须是10字符长。而确定password前9个
字符的算法可总结如下:

预先给定一个字符表szTestTable = "ZWATRQLCGHPSXYENVBJDFKMU"
根据用户名字节检验和模24的值a = ChkSumNameMod24
在该表中找到szTestTable[a]作为密码的第一个字符
密码的第二个字符必须为'E',如上一段所述。
a = (a + a) mod 24
找到szTestTable[a]作为密码的第三个字符
然后取得该字符在顺序字母表中的索引,加到a的现行值上,
作为新的索引(当然要模24)
也就是说:
a = (a + szTestTable[a] - 'A') mod 24
找到szTestTable[a]作为密码的第四个字符
a = (a + szTestTable[a] - 'A') mod 24
找到szTestTable[a]作为密码的第五个字符
……
依次类推直到确定前九个字符。

确定密码最后一个字符的代码在过程sub_4014CE中,兹从略。最后一个字
符等于前9个字符之和除以9的商。

例:假设用户名为PEDIY

计算byteChkSumName = ('P'+'E'+'D'+'I'+'Y') mod 256 = 123

ChkSumNameMod24 = 123 mod 24 = 3

TestTable[3] = 'T'

Password的第二个字符必须为'E'

3 + 3 = 6

TestTable[6] = 'L', 'L' - 'A' = 11, 6 + 11 = 17

TestTable[17] = 'B', 'B' - 'A' = 1, 17 + 1 = 18

TestTable[18] = 'J', 'J' - 'A' = 9, 18 + 9 - 24 = 3

TestTable[3] = 'T', 'T' - 'A' = 19, 3 + 19 = 22

TestTable[22] = 'M', 'M' - 'A' = 12, 22 + 12 - 24 = 10

TestTable[10] = 'P', 'P' - 'A' = 15, 10 + 15 - 24 = 1

TestTable[1] = 'W'

'T' + 'E' + 'L' + 'B' + 'J' + 'T' + 'M' + 'P' + 'W' = 697

697/9 = 77 = 'M'

password为TELBJTMPWM

  至此可以还原出程序大部分功能的源代码。程序中对Int3断点的检测
涉及到具体机器码地址范围,不可能精确还原;而SEH的部分由于笔者水
平有限而不得不删掉了。在还原的代码中,把比较方式改成了明码,以便
书写生成真码的过程以及制作注册机。

  上传文件内容:Noname6.asm 还原的MASM源码(含注册机条件汇编)


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 47147
活跃值: (20455)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
这种学习方法好。
顺便将CrackMe转份本地收藏
上传的附件:
2006-7-12 09:22
0
雪    币: 110
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这种文章要顶的,强
2006-7-12 12:19
0
雪    币: 97697
活跃值: (200834)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
4
sustain.
2006-7-12 21:05
0
雪    币: 220
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
好像它检测不到IDA Pro。但能检测到OllyDGB。
2006-7-21 02:22
0
游客
登录 | 注册 方可回帖
返回
//