首页
社区
课程
招聘
一个KeyGenMe的逆向工程(将exe还原成源代码)
发表于: 2006-5-27 17:12 25458

一个KeyGenMe的逆向工程(将exe还原成源代码)

2006-5-27 17:12
25458

前言:这是我近日以来一点小小的工作,由于字数太多一次写不完,先写
的部分分节发表在我的Blog上,这篇文章是根据Blog的内容整理出来的,
特此声明,以免抄袭之嫌。

又及:刚刚想要发这贴的时候,发现已经有一个贴分析了同一个
KeyGenMe:

http://bbs.pediy.com//showthread.php?threadid=25015

真是够郁闷的。这篇文章的内容每一个字都是我自己搞的,如果你觉得是
用其他方法得到的,我也能够理解,谁叫我落后一步呢。

KeyGenMe的下载地址:http://www.crackmes.de/users/haggar/keyme_no.4

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

第一节:去除花指令

  破解一般的共享软件时,都需要用PEID之类工具先检查文件类
型,以确定文件是否加了壳。但是这次不需要。这倒不是说CrackMe
就一定不会加壳,而是crackmes.de这个站点不允许提交加过壳的
CrackMe。以下用OllyDbg载入。

  分析开头的一段代码:

00401000 > $  50            push    eax
00401001   .  E8 03000000   call    00401009
00401006   .  EB            db      EB
00401007   >  EB 02         jmp     short 0040100B
00401009   $^ EB FC         jmp     short 00401007
0040100B   >  3E:8B0424     mov     eax, ds:[esp]                    
0040100F   .  83C0 09       add     eax, 9
00401012   >  EB 02         jmp     short 00401016
00401014   .  EB 06         jmp     short 0040101C
00401016   >  FFE0          jmp     eax
00401018   .  58            pop     eax
00401019   .  83C0 19       add     eax, 19
0040101C   >^ EB F4         jmp     short 00401012
0040101E    - E9 5833C050   jmp     5100437B
00401023   .  E8 03000000   call    0040102B
00401028   .^ EB EB         jmp     short 00401015
0040102A   ?  02EB          add     ch, bl
0040102C   ?  FC            cld
0040102D   >  3E:8B0424     mov     eax, ds:[esp]
00401031   .  83C0 09       add     eax, 9
00401034   >  EB 02         jmp     short 00401038
00401036   .  EB 06         jmp     short 0040103E
00401038   >  FFE0          jmp     eax
0040103A   .  58            pop     eax
0040103B   >  83C0 19       add     eax, 19
0040103E   >^ EB F4         jmp     short 00401034
00401040    - E9 5850E840   jmp     4128609D

401001这个call压进堆栈的地址是401006,而本身则转移到401009
处执行。经过两个jmp语句后,将刚压入的地址401006送回eax(但
不修改堆栈指针,与出栈不同)。在eax上加9以后,eax为40100F,
刚好等于add eax,9这一句的地址。接下来执行401016处的jmp eax
语句,又在eax上加9,于是eax现在是401018,jmp eax以后执行的
指令是pop eax,把call压进的401006弹出到eax。接下来在eax上
加19后变成40101F,再一次执行到jmp eax语句。但是且慢!从反
汇编的结果来看,地址40101F不是一条语句的开始呀?但是出现这
种情况,只能以实际运行的结果为准,姑且把40101E处的字节E9看
成字节数据,就可以看到正确的代码了:

0040101E   .  E9            db      E9
0040101F   .  58            pop     eax
00401020   .  33C0          xor     eax, eax
00401022   .  50            push    eax

也就是说,实际执行的指令是pop eax,将最初401000处保存的eax
值恢复,而40101E处的E9是一个垃圾字节,用来干扰反汇编程序的
识别。

  这些细小的地方姑且不去管他,程序自运行以来到执行40101F
处的pop eax为止,所涉及到的寄存器只有eax和esp,而现在push
和call两次入栈操作已经被两个pop eax平衡,eax也已经复原,那
么岂不是相当于什么都没做,这段程序究竟有什么用意呢?既然没
有用,那就用nop刷掉。(还可以省下一些CPU时间。)需要说明的
是,与之大同小异的花指令段在整个程序中实在是太多了,整个程
序的全部代码少说有三分之一是这种类型的花指令段。用相同的处
理方法处理。也就是说在文件中查找十六进制字节串:

50E803000000EBEB02EBFC3E8B042483C009EB02EB06FFE05883C019EBF4E958

(倒数第二个字节的E9有时也可是EB)然后全部改成:

9090909090909090909090909090909090909090909090909090909090909090

我想应该可以编个脚本专门做这种事情(我是用逐块替换的,感觉
还是有点多)。

  这样改完之后反汇编的代码中会有大块的nop指令,影响代码
的阅读。可以把修改过的文件用IDA打开,然后选择输出为汇编源文
件,这样一来,所有的地址引用都被替换成了对标号的引用,就可
以去掉这些nop指令,而不用担心指令引用错误地址的问题了。

  你或许会说,为什么能判断它是一段花指令,万一是正常的程
序功能指令,那不是就陷进去出不来了吗?判断一段程序是不是花
指令,没有一定的准则,只能靠个人感觉。以我个人来说,我是看
到这段程序里的jmp指令太多,有时连用好几个jmp。而正常的功能
指令段通常不会有很多的无条件跳转。还有call引用的地址跟它本
身的地址十分接近,这就可能是变形的jmp。

第二节:反向工程(1)还原界面代码

  在继续下去之前,我想阐明一下这里“反向工程”的涵义。一
般地,反向工程是把已经编译成机器指令的程序还原成源代码的形
式。而这个KeyGenMe原本是用汇编写的,那是不是说,把它的机器
指令用汇编的形式表示出来就算是反向了呢?如果这种说法是正确
的话,那随便一个会用电脑软件的人去找个象W32Dasm这样的软件,
把exe文件加载进去,出来的就是汇编代码了,这样的话,人人都
可以是反向工程专家了。

  反向工程还原的不仅是代码,更是程序的算法。创造性的反向
工程首先要详尽分析反汇编出来的代码,理解程序功能的算法,然
后自己编写代码实现相同或相仿的功能。即使是照抄反汇编出来的
代码,也要书写成让人能读懂的形式;对于那些与程序功能无关的
或者执行起来拖泥带水的冗余代码,该删的删,该改的改。还原出
来的代码,要在忠实于原程序功能的前提下,维持良好的可读性,
这才是反向工程的精神所在。

  废话打住,首先把IDA生成的汇编代码文件去掉那些大块的nop
指令,接着找到程序入口点的代码:

                public start
start                proc near
                xor        eax, eax
                push        eax                ; lpModuleName
                call        GetModuleHandleA
                mov        hInstance, eax
                push        0                ; dwInitParam
                push        offset DialogFunc ; lpDialogFunc
                push        0                ; hWndParent
                push        3E9h                ; lpTemplateName
                push        hInstance        ; hInstance
                call        DialogBoxParamA
                push        0                ; uExitCode
                call        ExitProcess
start                endp

由此可见,这是一个标准的使用对话框的程序:

    GetModuleHandle --> DialogBoxParam --> ExitProcess

lpTemplateName参数中需要提供对话框ID,通常这是在资源文件中
定义的,所以用Resource Hacker把它的资源提取出来辅助分析。
这里的3E9h就是1001,在资源脚本中有这么一句:

1001 DIALOGEX 0, 0, 463, 160

下文中还会出现在资源中定义的ID号,对此只要在源文件头部加上
一些诸如“DLG_MAIN equ 1001”的符号定义,就可以用DLG_MAIN
这些有意义的符号来引用资源了,所以都不再一一说明。

  使用对话框还必须有回调过程,上面程序中提供的回调过程是
DialogFunc,接下来看看它的代码:

; Attributes: bp-based frame

; BOOL __stdcall DialogFunc(HWND,UINT uMsg,WPARAM wParam,LPARAM)
DialogFunc        proc near                ; DATA XREF: start+8Fo

hDlg                = dword        ptr  8
uMsg                = dword        ptr  0Ch
wParam                = dword        ptr  10h

                push        ebp
                mov        ebp, esp
                cmp        [ebp+uMsg], WM_INITDIALOG
                jnz        short loc_4010DE
                push        7D1h                ; lpIconName
                push        hInstance        ; hInstance
                call        LoadIconA
                push        eax                ; lParam
                push        ICON_BIG        ; wParam
                push        WM_SETICON        ; Msg
                push        [ebp+hDlg]        ; hWnd
                call        SendMessageA
                jmp        loc_4012BC

loc_4010DE:                                ; CODE XREF: DialogFunc+Aj
                cmp        [ebp+uMsg], WM_COMMAND
                jnz        loc_4012AC
                cmp        [ebp+wParam], 3F6h
                jnz        short loc_401107
                push        0                ; lParam
                push        0                ; wParam
                push        WM_CLOSE        ; Msg
                push        [ebp+hDlg]        ; hWnd
                call        SendMessageA
                jmp        loc_4012BC

loc_401107:                                ; CODE XREF: DialogFunc+45j
                cmp        [ebp+wParam], 3F5h
                jnz        loc_4012BC
                push        10h                ; nMaxCount
                push        offset dword_4036C4 ; lpString
                push        3ECh                ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA
                cmp        eax, 4
                jz        short loc_401136
                call        sub_4012C2
                leave
                retn        10h

loc_401136:                                ; CODE XREF: DialogFunc+7Ej
                push        10h                ; nMaxCount
                push        offset dword_4036D4 ; lpString
                push        3EDh                ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA
                cmp        eax, 4
                jz        short loc_401178
                call        sub_4012C2
                leave
                retn        10h

loc_401178:                                ; CODE XREF: DialogFunc+C0j
                push        10h                ; nMaxCount
                push        offset dword_4036E4 ; lpString
                push        3EEh                ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA
                cmp        eax, 4
                jz        short loc_4011BA
                call        sub_4012C2
                leave
                retn        10h

loc_4011BA:                                ; CODE XREF: DialogFunc+102j
                push        10h                ; nMaxCount
                push        offset dword_4036F4 ; lpString
                push        3EFh                ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA
                cmp        eax, 4
                jz        short loc_4011FC
                call        sub_4012C2
                leave
                retn        10h

loc_4011FC:                                ; CODE XREF: DialogFunc+144j
                push        10h                ; nMaxCount
                push        offset dword_403704 ; lpString
                push        3F0h                ; nIDDlgItem
                push        [ebp+hDlg]        ; hDlg
                call        GetDlgItemTextA
                cmp        eax, 4
                jz        short loc_40123E
                call        sub_4012C2
                leave
                retn        10h

loc_40123E:                                ; CODE XREF: DialogFunc+186j
                call        _VerifyCDKey
                cmp        eax, 0
                jz        short loc_401281
                push        40h                ; uType
                push        offset Caption        ; "Information"
                push        offset Text        ; "The entered key appears to be valid.        Yo"...
                push        0                ; hWnd
                call        MessageBoxA
                xor        eax, eax
                leave
                retn        10h

loc_401281:                                ; CODE XREF: DialogFunc+1B9j
                call        sub_4012C2
                leave
                retn        10h

loc_4012AC:                                ; CODE XREF: DialogFunc+38j
                cmp        [ebp+uMsg], 10h
                jnz        short loc_4012BC
                push        0                ; nResult
                push        [ebp+hDlg]        ; hDlg
                call        EndDialog

loc_4012BC:                                ; CODE XREF: DialogFunc+2Cj
                                        ; DialogFunc+55j ...
                xor        eax, eax
                leave
                retn        10h
DialogFunc        endp

由于反汇编代码中的局部变量名是用ebp指针表示的,在此可赋予
它们一些有意义的名称,如uMsg,wParam等。stdcall调用方式是
参数自右向左入栈,所以参数表的第一个参数通常是[ebp+8],而
右边的参数放在高地址中。于是uMsg就是dword ptr [ebp+0C],
余类推。

  我们感兴趣的部分是在对WM_COMMAND(原为数值111h)消息的
处理上。程序运行界面有两个按钮:“Next >”和“Cancel”。按
下Cancel按钮,则调用SendMessage向对话框发送WM_CLOSE,界面
就关闭。而按下Next按钮,程序依次到五个文本框中取文本,只要
有其中一个文本框中取得的字符数不是4,就显示CDKey检验失败
(sub_4012C2是显示失败信息的例程)。也就是说,CDKey的每一
小节都应该是4个字符,满足这个条件才开始检验。取完文本后调
用一个过程_VerifyCDKey(这个过程名称是自定义的),如果返回
值是0,也检验失败。反之,则显示检验成功。至此,界面功能已
基本分析清楚,可以写出源代码的框架了。

第三节:反向工程(2)CDKey验证部分算法分析

  前面已经弄清了界面的工作原理,接下来该接触程序的核心算
法部分,也就是上面提到的_VerifyCDKey过程。

  算法分析的一个重要指导思想就是等效替代。如果一段代码能
够用较简单的指令序列实现同样的功能,那就应该用简单的指令序
列去替换复杂的指令序列。事实上,在去除花指令一节,就已经用
到了这种思想。现在再看一个例子:

                xor        eax, eax
                xor        edi, edi
                xor        esi, esi
                mov        ecx, 1
                mov        ebx, 0Dh

loc_401630:                                ; CODE XREF: _VerifyCDKey+35Fj
                movsx        eax, byte ptr dword_403714[esi]
                add        eax, ecx
                xor        edx, edx
                mov        ecx, 0FFF1h
                div        ecx
                mov        ecx, edx
                lea        eax, [ecx+edi]
                xor        edx, edx
                mov        edi, 0FFF1h
                div        edi
                inc        esi
                cmp        esi, ebx
                mov        edi, edx
                jl        short loc_401630

如果预先知道了dword_403714[esi]的内容是ASCII字符,那么符号
扩展就是零扩展,eax最多不超过7Fh。用它去除以0FFF1h,岂非就
相当于xchg eax, edx?循环中每次将eax的值累加送到ecx中,但
是只循环13次,13个字符的ASCII码加起来也远远达不到0FFF1h。
因此这句除法指令完全可以改成xchg eax, edx。其次,对各寄存
器的赋值情况作标记,删去那些只用来保存中间结果的寄存器。譬
如:

                add        eax, ecx
                xor        edx, edx
                xchg        eax, edx
                mov        ecx, edx

edx是保存中间结果的,下面并没有用到,那么就可以简化为:

                add        ecx, eax
                xor        eax, eax

同样还可以做其他一些简化。上面那段代码最终可以化为:

                xor        eax, eax
                xor        edi, edi
                xor        esi, esi
                mov        ecx, 1

loc_401630:                               
                movsx        eax, byte ptr dword_403714[esi]
                add        ecx, eax
                add        edi, ecx
                inc        esi
                cmp        esi, 0Dh
                jl        short loc_401630

当然,这段代码并非程序的关键代码,只是用来说明分析算法时使
用的一些简化技巧。设计算法和分析算法,从某种意义上来说是相
反的过程。设计算法时,算法越复杂,可能实现的功能越多。而分
析算法时,算法的表述越简单,就越容易理解。

  等效替代的思想,不仅可以应用在修改指令序列这样的微观方
面,同样可以应用在分析模块功能这样的宏观方面。譬如我们现在
要分析_VerifyCDKey过程,它有些什么功能?根据程序上下文,它
的作用是检验输入的CDKey,返回值代表检验结果:非0为成功,0
为失败。既然如此,我们的重点就应该放在CDKey是如何被校验的
方面,最后只要做出一个过程,采用相同方式校验CDKey,并且失
败返回0且成功返回非0(等效替代),就算达到目的了。

  基于这一思想,用OllyDbg载入程序,按要求(每4个字符一小
节)随便输入一个CDKey,然后定位到那些与注册码校验相关的代
码位置上。为了方便起见,这里还是使用IDA反汇编出来的代码。

                mov        dword_403674, 1010101h   ;这里是输入一个表
                mov        dword_403678, 1010101h   ;下文中将会用它来
                mov        dword_40367C, 1010101h   ;检验注册码中是否
                mov        dword_403680, 1010101h   ;存在K,P,Q,Y四字
                mov        dword_403684, 1010101h
                mov        dword_403688, 1010100h
                mov        dword_40368C, 1000001h
                mov        dword_403690, 1010101h
                mov        dword_403694, 1000101h
                mov        dword_403698, 1010101h
                mov        eax, dword_4036C4    ;将输入框中取得
                mov        dword_403714, eax        ;的字符连接到以
                mov        eax, dword_4036D4    ;403714为起始地
                mov        dword_403718, eax        ;址的内存单元中
                mov        eax, dword_4036E4
                mov        dword_40371C, eax
                mov        eax, dword_4036F4
                mov        dword_403720, eax
                mov        eax, dword_403704
                mov        dword_403724, eax
                mov        byte_403728, 0
                xor        ecx, ecx
                jmp        short loc_4014A3

loc_40144B:                                ; CODE XREF: _VerifyCDKey+1B0j
                mov        al, byte ptr dword_403714[ecx]
                mov        byte_403754, al
                cmp        byte_403754, 30h         ;0
                jb        short loc_401488
                cmp        byte_403754, 5Ah         ;Z
                jbe        short loc_40148D

loc_401488:                                ; CODE XREF: _VerifyCDKey+187j
                xor        eax, eax
                retn

loc_40148D:                                ; CODE XREF: _VerifyCDKey+190j
                cmp        byte_403754, 39h         ;9
                jbe        short loc_4014A2         
                cmp        byte_403754, 41h         ;A
                jnb        short loc_4014A2         ;这一段是检验输入码中是否
                xor        eax, eax                 ;只有数码及大写字母
                retn                             ;如包含其他字符,则为非法CDKey

                xor        ecx, ecx
                jmp        short loc_401516

loc_4014CC:                                ; CODE XREF: _VerifyCDKey+223j
                push        ecx
                mov        al, byte ptr dword_403714[ecx]
                xor        ecx, ecx
                jmp        short loc_40150F

loc_4014F7:                                ; CODE XREF: _VerifyCDKey+21Cj
                cmp        al, byte_4030FF[ecx]

;查看相应的内存地址可知:
;004030FF  30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46  0123456789ABCDEF
;0040310F  47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56  GHIJKLMNOPQRSTUV
;0040311F  57 5A 59 58                                      WZYX
;配合上面输入到地址403674的表,可知这段程序的作用是检验输入码中是否
;有K,P,Q,Y四字符,如果有,则为非法CDKey

                jnz        short loc_40150E
                cmp        byte ptr dword_403674[ecx], 1
                jz        short loc_40150E
                add        esp, 4
                xor        eax, eax
                retn

loc_40150E:                                ; CODE XREF: _VerifyCDKey+207j
                                        ; _VerifyCDKey+210j
                inc        ecx

loc_40150F:                                ; CODE XREF: _VerifyCDKey+1FFj
                cmp        ecx, 24h
                jb        short loc_4014F7
                pop        ecx
                inc        ecx

loc_401516:                                ; CODE XREF: _VerifyCDKey+1D4j
                cmp        ecx, 14h
                jb        short loc_4014CC
                xor        ecx, ecx
                jmp        short loc_40154C

loc_40153F:                                ; CODE XREF: _VerifyCDKey+259j
                mov        al, byte ptr dword_403714[ecx]
                mov        byte_403734[ecx], al
                inc        ecx             ;由于下面要对输入码做变换
                                        ;在403734地址处保存一份未变换的输入码
loc_40154C:                                ; CODE XREF: _VerifyCDKey+247j
                cmp        ecx, 14h
                jb        short loc_40153F
                lea        eax, dword_403714
                xor        edx, edx
                xor        ebx, ebx
                mov        ecx, 2
                mov        dl, [eax+ecx]   ;注册码的第3和第14两个字符交换位置
                mov        bl, [eax+0Dh]
                mov        [eax+ecx], bl
                mov        bl, [eax+0Eh]
                mov        [eax+0Dh], dl
                mov        edx, 4
                add        ecx, eax
                lea        ecx, [eax+edx]
                mov        dl, [ecx]
                mov        [ecx], bl
                mov        bl, [eax+0Fh]
                mov        [eax+0Eh], dl   ;第5和第15两个字符交换
                mov        ecx, 5
                mov        dl, [eax+ecx]
                mov        [eax+ecx], bl
                mov        bl, [eax+10h]
                mov        [eax+0Fh], dl   ;第6和第16两个字符交换
                mov        edx, 7
                add        ecx, eax
                lea        ecx, [eax+edx]
                mov        dl, [ecx]
                mov        [ecx], bl
                mov        bl, [eax+11h]
                mov        [eax+10h], dl   ;第8和第17两个字符交换
                mov        ecx, 0Ch
                mov        dl, [eax+ecx]
                mov        [eax+ecx], bl
                mov        bl, [eax+12h]
                add        ecx, eax
                mov        [eax+11h], dl   ;第13和第18两个字符交换
                mov        edx, 1
                lea        ecx, [eax+edx]
                mov        dl, [ecx]
                mov        [ecx], bl
                mov        bl, [eax+13h]
                mov        [eax+12h], dl   ;第2和第19两个字符交换
                mov        ecx, 3
                mov        dl, [eax+ecx]
                add        ecx, eax
                mov        [ecx], bl
                mov        [eax+13h], dl   ;第4和第20两个字符交换
                mov        byte ptr [eax+14h], 0

;注意上面一段的变换的逆变换就是其自身
;以下很长一段代码属于无关代码,兹从略
;执行到下面一句之前,ecx的值为16h

                lea        eax, byte_403755
                mov        dl, cl
                and        dl, 3
                shl        dl, 3
                mov        [eax+6], dl
                shr        ecx, 2
                mov        dl, cl
                and        dl, 1Fh
                mov        [eax+5], dl
                mov        edx, ecx
                shr        edx, 5
                and        dl, 1Fh
                mov        [eax+4], dl
                mov        edx, ecx
                shr        edx, 0Ah
                and        dl, 1Fh
                mov        [eax+3], dl
                mov        edx, ecx
                shr        edx, 0Fh
                and        dl, 1Fh
                mov        [eax+2], dl
                mov        edx, ecx
                shr        edx, 14h
                shr        ecx, 19h
                and        dl, 1Fh
                mov        [eax+1], dl
                and        cl, 1Fh
                mov        [eax], cl
                mov        dl, [eax+6]
                and        cl, 7         ;根据上文中给的ECX值
                or        dl, cl        ;执行完这一句时,403755开始写入的7个字节为
                mov        [eax+6], dl   ;0,0,0,0,0,5,10h
                movsx        edx, byte ptr [eax]
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+1]
                mov        [eax], cl
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+2]
                mov        [eax+1], cl
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+3]
                mov        [eax+2], cl
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+4]
                mov        [eax+3], cl
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+5]
                mov        [eax+4], cl
                mov        cl, byte_403145[edx]
                movsx        edx, byte ptr [eax+6]
                mov        [eax+5], cl
                mov        cl, byte_403145[edx]
                mov        [eax+6], cl
                mov        byte ptr [eax+7], 0

;上面用刚写入的7个字节做索引值在一张表中查找相应的项目并写回到原来的字节中
;查看相应的内存单元可知:
;00403145  32 33 34 35 36 37 38 39 41 42 43 44 45 46 47 48  23456789ABCDEFGH
;00403155  4A 4B 4C 4D 4E 51 51 52 53 54 55 56 57 58 59 59  JKLMNQQRSTUVWXYY
;为了验证这张表的内容是否与输入有关,在其上设置内存写入断点,没有发现
;程序往其中写入数据,也就是说这张表不依赖于输入
;于是,用0,0,0,0,0,5,10h做索引,写回的字符应该是'2','2','2','2','2','7','J'

                xor        ecx, ecx
                xor        eax, eax
                jmp        short loc_401946

loc_401939:                                ; CODE XREF: _VerifyCDKey+653j
                mov        al, byte_403755[ecx]
                mov        [ecx+403721h], al   ;用上面的7个字节
                inc        ecx                 ;改写注册码的后7个字符
loc_401946:                                ; CODE XREF: _VerifyCDKey+641j
                cmp        ecx, 7
                jb        short loc_401939

;执行逆变换,从略

                xor        eax, eax
                xor        ebx, ebx
                xor        ecx, ecx
                jmp        short loc_401A36

loc_401A22:                                ; CODE XREF: _VerifyCDKey+743j
                mov        bl, byte ptr dword_403714[ecx]
                mov        bh, byte_403734[ecx]
                cmp        bl, bh                 ;比较处理后的字符串是否与原输入码相同
                jz        short loc_401A35       ;不同,则此CDKey非法
                xor        eax, eax               ;反之则合法
                retn

loc_401A35:                                ; CODE XREF: _VerifyCDKey+73Aj
                inc        ecx

loc_401A36:                                ; CODE XREF: _VerifyCDKey+72Aj
                cmp        ecx, 14h
                jb        short loc_401A22
                mov        eax, 1
                retn

;根据执行逆变换前的结果,注册码的第14,15,16,17,18,19,20个字符分别为'2','2','2',
;'2','2','7','J',这就要求原输入码的第3,5,6,8,13,2,4个字符也为对应的值,方为合
;法CDKey

既然最后是通过一个比较来判定CDKey是否合法,那么我们所需要
关心的就只有与其相关的内存单元,以及在比较之前最后一次写入
的数据是什么。事实上,在上面写入'222227J'之前还写入过另外
一个字符串,这个字符串还是经过十分繁复的运算得出来的。但它
毕竟被覆盖了,那部分算法就犯不着关心。

  于是,一个CDKey是合法的当且仅当它有下面几个特征:
  ●由数码及大写字母构成;
  ●不含字符'K','P','Q','Y';
  ●第2,3,4,5,6,8,13个字符依次是'7','2','J','2','2','2',
      '2'。

根据这个算法描述重写的_VerifyCDKey过程,就比原来大大简化
了。

第四节:KeyGen

  首先做一张表,由全部数字以及除'K','P','Q','Y'的大写
字母组成。然后构造一个20字符的字符串模板,在规定的位置填入
已规定的字符('2'、'7'、'J'等),其他位置的字符就在上述那
张表中随机抽取并填入。但是我不知道产生随机数的API是什么,
所以暂时还写不出KeyGen程序。

上传文件的内容:

rev.asm 还原出来的KeyGenMe源程序
res.rc  资源脚本
Icon_1.ico, Bitmap_1.Bmp  图标及位图资源


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

上传的附件:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 433
活跃值: (176)
能力值: ( LV13,RANK:1250 )
在线值:
发帖
回帖
粉丝
2
不好意思,刚发现源程序中还有几个数据没有定义,现在修正了
2006-5-27 17:51
0
雪    币: 47147
活跃值: (20455)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
最初由 冲天剑 发布
不好意思,刚发现源程序中还有几个数据没有定义,现在修正了


文章不错,喜欢看这些逆向方面的
2006-5-27 18:09
0
雪    币: 181
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
牛啊!
学习 , 不过现在还看不懂
嘎嘎 顶!
2006-5-28 10:34
0
雪    币: 253
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
5
支持
2006-5-30 14:46
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢楼主,学习了
2006-5-31 12:23
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
好文章,有时间再细看
2006-6-1 08:56
0
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
8
最初由 冲天剑 发布
......其他位置的字符就在上述那
张表中随机抽取并填入。但是我不知道产生随机数的API是什么,
所以暂时还写不出KeyGen程序........


支持! 获得伪随机数可以用srand()做种,rand()取随机数。
2006-6-1 10:09
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
嘎嘎。。还没看懂。。。
2012-11-2 11:01
0
游客
登录 | 注册 方可回帖
返回
//