meishenm 发表于 2010-5-24 20:05:28

Thigo's keygenme简单分析

标 题: Thigo's keygenme简单分析
作 者: ikki
时 间: 2006-08-10,12:05:41
链 接: http://bbs.pediy.com/showthread.php?t=30337

【文章标题】: Thigo's keygenme简单分析
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
呵呵,这个keygenMe有点意思.

让我们开始吧,运行一下CrackMe,有如下提示信息:
You should put a file named key.dat in this dir...
哦,听作者的话吧,把CrackMe关掉,然后在同一个目录下建一个名字叫key.dat的文件
里面随便输点东西,我写的信息是字符串"12345678".
再次打开CrackMe,看到提示信息变成了:
Are u sure it's a good keyfile ??
呀,还是错的呢.


W32Dasm反汇编,看看有没有什么有用的信息.

W32DASM String Data References
------------
" ?"
""
""
"?"
"]_^[?]?L$髁"
"Are u sure it's a good keyfile "
"key.dat"
"U?? -"
"You should put a file named key.dat "
"竣??NK4?8?2?8?5C泪B?LE?G?H:?8?0R?
------------
似乎都是提示keyfile的信息,没成功的提示信息.


双击"Are u sure it's a good keyfile "这一行字符串,来到这里

:004011EA 3DF48B2A00            cmp eax, 002A8BF4
:004011EF 7407                  je 004011F8

* Possible StringData Ref from Data Obj ->"Are u sure it's a good keyfile "
                                          ->"??"
                                    |
:004011F1 68E4514000            push 004051E4
:004011F6 EB60                  jmp 00401258


一直往上看

:00401142 50                      push eax
:00401143 56                      push esi

* Reference To: KERNEL32.ReadFile, Ord:0218h
                                    |
:00401144 FF1518404000             call dword ptr ds:[<&KERNEL32.ReadFile>]    ; 读取keyfile文件

:0040114A 56                      push esi

* Reference To: KERNEL32.CloseHandle, Ord:001Bh
                                    |
:0040114B FF151C404000            call dword ptr ds:[<&KERNEL32.CloseHandle>] ; 关闭文件句炳
:00401151 8B4DFC                  mov ecx, dword ptr
:00401154 33C0                  xor eax, eax
:00401156 3BCB                  cmp ecx, ebx
:00401158 760C                  jbe 00401166


就从这里下手吧,用OD载入CrackME,在00401144处下个断点,Let's go

0040115A    308C05 A8FEFFFF       xor byte ptr ss:,cl            ; ---------
00401161    40                  inc eax                                     ; key.dat内容逐字节异或文件的长度
00401162    3BC1                  cmp eax,ecx
00401164^ 72 F4               jb short crackme.0040115A                   ; ---------
00401166    80B5 A8FEFFFF 54      xor byte ptr ss:,54                ; 文件第1字节XOR 0x54
0040116D    80B5 A9FEFFFF 4D      xor byte ptr ss:,4D                ; 文件第2字节XOR 0x4D
00401174    80B5 AAFEFFFF 47      xor byte ptr ss:,47                ; 文件第3字节XOR 0x47
0040117B    3BCF                  cmp ecx,edi                                 ; /
0040117D    8BF7                  mov esi,edi                                 ; key.dat文件长度<=3则跳转
0040117F    76 27               jbe short crackme.004011A8                  ; \
00401181    8A95 A8FEFFFF         mov dl,byte ptr ss:                ; /
00401187    8D8435 A9FEFFFF       lea eax,dword ptr ss:          ; n为循环计数器,n > 0
0040118E    03F7                  add esi,edi
00401190    3050 FF               xor byte ptr ds:,dl                  ; 第1位XOR第3n+1位->第3n+1位
00401193    8A95 A9FEFFFF         mov dl,byte ptr ss:
00401199    3010                  xor byte ptr ds:,dl                  ; 第2位XOR第3n+2位->第3n+2位
0040119B    8A95 AAFEFFFF         mov dl,byte ptr ss:
004011A1    3050 01               xor byte ptr ds:,dl                  ; 第3位XOR第3(n+1)位->第3(n+1)位
004011A4    3BF1                  cmp esi,ecx                                 ; 循环直到3(n+1) > length
004011A6^ 72 D9               jb short crackme.00401181                   ; \
004011A8    BE 30504000         mov esi,crackme.00405030                   ; 要处理的数据开始地址(0405030)放到ESI
004011AD    889C0D A8FEFFFF       mov byte ptr ss:,bl         ; 把0写入上面最后一次循环的3(n+1)位
004011B4    33FF                  xor edi,edi
004011B6    8BC6                  mov eax,esi
004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:         ; /
004011BF    3008                  xor byte ptr ds:,cl
004011C1    40                  inc eax                                    ; 用前面对key.dat内容变换后得到的数据
004011C2    47                  inc edi
004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
004011C6    75 02               jnz short crackme.004011CA
004011C8    33FF                  xor edi,edi                              ; 直到碰到要处理数据段的结束标志0xFF
004011CA    8038 FF               cmp byte ptr ds:,0FF
004011CD^ 75 E9               jnz short crackme.004011B8               ; \
004011CF    0FB605 32504000       movzx eax,byte ptr ds:             ; /
004011D6    0FB60D 31504000       movzx ecx,byte ptr ds:
004011DD    0FAFC1                imul eax,ecx
004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
004011E7    0FAFC1                imul eax,ecx
004011EA    3D F48B2A00         cmp eax,2A8BF4                           ; 如不相等则显示下面的错误信息
004011EF    74 07               je short crackme.004011F8                  ; \
004011F1    68 E4514000         push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"
004011F6    EB 60               jmp short crackme.00401258
004011F8    8A8D A8FEFFFF         mov cl,byte ptr ss:
004011FE    33C0                  xor eax,eax
00401200    884C05 D0             mov byte ptr ss:,cl            ; /
00401204    8A8C05 A9FEFFFF       mov cl,byte ptr ss:
0040120B    40                  inc eax                                    ; 在处理后的key.dat中查找第一个空格字符(0x20)
0040120C    80F9 20               cmp cl,20
0040120F^ 75 EF               jnz short crackme.00401200               ; \
00401211    885C05 D0             mov byte ptr ss:,bl            ;
00401215    40                  inc eax
00401216    33D2                  xor edx,edx
00401218    8D8C05 A8FEFFFF       lea ecx,dword ptr ss:
0040121F    8A8405 A8FEFFFF       mov al,byte ptr ss:
00401226    41                  inc ecx                                    ; /---------A-------
00401227    884415 A8             mov byte ptr ss:,al            ; 将key.dat文件中第一个空格字符
0040122B    42                  inc edx                                    ; 后1位起一直到末尾的数据
0040122C    8A01                  mov al,byte ptr ds:                   ; COPY到某处内存
0040122E    3AC3                  cmp al,bl                                  ; 这段数据最后要用到
00401230^ 75 F4               jnz short crackme.00401226               ; \---------A-------
00401232    8D45 F8               lea eax,dword ptr ss:
00401235    885C15 A8             mov byte ptr ss:,bl
00401239    50                  push eax
0040123A    6A 40               push 40
0040123C    68 7F010000         push 17F
00401241    56                  push esi
00401242    FF15 68404000         call dword ptr ds:[<&KERNEL32.VirtualProte>; kernel32.VirtualProtect

0012FA88   00401248      /CALL 到 VirtualProtect 来自 crackme.00401242
0012FA8C   00405030      |Address = crackme.00405030
0012FA90   0000017F      |Size = 17F (383.)
0012FA94   00000040      |NewProtect = PAGE_EXECUTE_READWRITE
0012FA98   0012FBF8      \pOldProtect = 0012FBF8

修改地址为0403050,长度为0x17F内存区域的属性,从这里可以猜测到前面做变换的使用的数据段的长度


00401248    8D45 D0               lea eax,dword ptr ss:
0040124B    50                  push eax
0040124C    8D45 A8               lea eax,dword ptr ss:
0040124F    50                  push eax
00401250    FFD6                  call esi                                 ; /*整个CrackMe的精彩部分*/ESI=403050
00401252    59                  pop ecx
00401253    8D45 D0               lea eax,dword ptr ss:
00401256    59                  pop ecx
00401257    50                  push eax
00401258    68 EC030000         push 3EC
0040125D    FF75 08               push dword ptr ss:
00401260    FF15 C0404000         call dword ptr ds:[<&USER32.SetDlgItemText>; 根据CALL ESI的结果设置提示信息
00401266    5F                  pop edi
00401267    5E                  pop esi
00401268    5B                  pop ebx
00401269    C9                  leave




我是用内容为字符串"12345678"的keyfile,通过把前面的两处跳转
1:
004011EA    3D F48B2A00         cmp eax,2A8BF4                           ;
004011EF    74 07               je short crackme.004011F8                  ;这里要修改

2:
0040120C    80F9 20               cmp cl,20
0040120F^ 75 EF               jnz short crackme.00401200               ; 这里要修改


修改后来到这里的:
程序运行到这里的时候ESI=0403050
00401250    FFD6                  call esi

进入CALL

00405030- 73 C8               jnb short crackme.00404FFA
00405032    DE77 84               fidiv word ptr ds:
00405035^ 77 DA               ja short crackme.00405011
00405037    43                  inc ebx
00405038    3226                  xor ah,byte ptr ds:
0040503A    43                  inc ebx
0040503B    B9 634FF426         mov ecx,26F44F63
......
......

一大段的乱码,呵呵,这是怎么回事呀?

这里正是整个CrackMe最精彩的部分,慢慢分析一下:
地址0403050开始的这段数据在我们前面的分析中曾出现两次
1:
004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:         ; /
004011BF    3008                  xor byte ptr ds:,cl
004011C1    40                  inc eax                                    ; 用前面对key.dat内容变换后得到的数据
004011C2    47                  inc edi
004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
004011C6    75 02               jnz short crackme.004011CA
004011C8    33FF                  xor edi,edi                              ; 直到碰到要处理数据段的结束标志0xFF
004011CA    8038 FF               cmp byte ptr ds:,0FF
004011CD^ 75 E9               jnz short crackme.004011B8               ; \


2:
004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
004011E7    0FAFC1                imul eax,ecx
004011EA    3D F48B2A00         cmp eax,2A8BF4                           ; 如不相等则显示下面的错误信息
004011EF    74 07               je short crackme.004011F8                  ; \
004011F1    68 E4514000         push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"


而现在它又是一段CALL的代码,这里面一定有作者的某种提示.

让我们看看一般的CALL开头是什么样的:
kernel32.GetModuleHandleA

7C80B529 >8BFF                  mov edi,edi
7C80B52B    55                  push ebp                                 ; ******
7C80B52C    8BEC                  mov ebp,esp                              ; ******
7C80B52E    837D 08 00            cmp dword ptr ss:,0
7C80B532    74 18               je short kernel32.7C80B54C
7C80B534    FF75 08               push dword ptr ss:
7C80B537    E8 682D0000         call kernel32.7C80E2A4
7C80B53C    85C0                  test eax,eax
7C80B53E    74 08               je short kernel32.7C80B548
7C80B540    FF70 04               push dword ptr ds:
7C80B543    E8 F4300000         call kernel32.GetModuleHandleW
7C80B548    5D                  pop ebp
7C80B549    C2 0400               retn 4



kernel32.TerminateProcess


7C801E16 >8BFF                  mov edi,edi
7C801E18    55                  push ebp                                 ; ******
7C801E19    8BEC                  mov ebp,esp                              ; ******
7C801E1B    837D 08 00            cmp dword ptr ss:,0
7C801E1F    75 09               jnz short kernel32.7C801E2A
7C801E21    6A 06               push 6
7C801E23    E8 98740000         call kernel32.7C8092C0
7C801E28    EB 1B               jmp short kernel32.7C801E45
7C801E2A    FF75 0C               push dword ptr ss:
7C801E2D    FF75 08               push dword ptr ss:
7C801E30    FF15 FC13807C         call dword ptr ds:[<&ntdll.NtTerminateProc>; ntdll.ZwTerminateProcess
7C801E36    85C0                  test eax,eax
7C801E38    7C 05               jl short kernel32.7C801E3F
7C801E3A    33C0                  xor eax,eax
7C801E3C    40                  inc eax
7C801E3D    EB 08               jmp short kernel32.7C801E47
7C801E3F    50                  push eax
7C801E40    E8 36750000         call kernel32.7C80937B
7C801E45    33C0                  xor eax,eax
7C801E47    5D                  pop ebp
7C801E48    C2 0800               retn 8

注意打了'*'号的语句

0x55 * 0x8B * 0xEC = 0x2A8BF4

结合:
1:
004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:         ; /
004011BF    3008                  xor byte ptr ds:,cl
004011C1    40                  inc eax                                    ; 用前面对key.dat内容变换后得到的数据
004011C2    47                  inc edi
004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
004011C6    75 02               jnz short crackme.004011CA
004011C8    33FF                  xor edi,edi                              ; 直到碰到要处理数据段的结束标志0xFF
004011CA    8038 FF               cmp byte ptr ds:,0FF
004011CD^ 75 E9               jnz short crackme.004011B8               ; \


2:
004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
004011E7    0FAFC1                imul eax,ecx
004011EA    3D F48B2A00         cmp eax,2A8BF4                           ; 如不相等则显示下面的错误信息
004011EF    74 07               je short crackme.004011F8                  ; \
004011F1    68 E4514000         push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"

原来作者要告诉我们的就是:

地址0403050开始的这段数据最后变换得到的结果应该是:
7C801E18    55                  push ebp                                 
7C801E19    8BEC                  mov ebp,esp                              
这样的形式.

因为异或运算满足:
A XOR B = C
A XOR C = B

从而可以推导出前面key.dat变换后得到的前3位的结果,




0403050处的原始数据(程序内置数据):
---------------------------
004050301E BF A2 1A F3 0B B7 34 4E 4B 34 C5 0E 38 88 4B竣??NK4?8?
0040504032 C5 06 38 88 0A 35 43 C0 61 42 8D 76 4C 45 BF2?8?5C泪B?LE
.....
.....
0040519035 C7 0E C8 C5 06 3C 4D 06 C8 41 F5 25 CB 99 415?扰<M攘??A
004051A097 C0 71 42 48 71 B2 8D 74 41 4B BF AB 16 F7 FF?qBHq?tAK揩?


0x1E XOR 0x55 = 0x4B

0xBF XOR 0x8B = 0x34

0xA2 XOR 0xEC = 0x4E

把得到的结果代替
004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:
处的key.dat数据,就可以把0403050处的原始数据还原成正确的指令代码了:

还原后的指令:
00405030    55                  push ebp
00405031    8BEC                  mov ebp,esp
00405033    51                  push ecx
00405034    C745 FC 00000000      mov dword ptr ss:,0
0040503B    8B45 0C               mov eax,dword ptr ss:
0040503E    C600 06               mov byte ptr ds:,6

......

......

前面一大段的指令做的只是很简单的变换:
把固定的数据还原成以'\0'结尾的字符串:
registed to:


0040517A    8B45 0C               mov eax,dword ptr ss:               ; /
0040517D    0345 FC               add eax,dword ptr ss:
00405180    8B4D 08               mov ecx,dword ptr ss:               ; 把前面标记为A出得到的数据
00405183    034D FC               add ecx,dword ptr ss:
00405186    8A11                  mov dl,byte ptr ds:
00405188    8850 0F               mov byte ptr ds:,dl
0040518B    8B45 FC               mov eax,dword ptr ss:               ; 连接在字符串"registed to:"后面
0040518E    83C0 01               add eax,1
00405191    8945 FC               mov dword ptr ss:,eax
00405194    8B4D 08               mov ecx,dword ptr ss:
00405197    034D FC               add ecx,dword ptr ss:               ; 得到registed to:XXXXX形势的字符串
0040519A    0FBE11                movsx edx,byte ptr ds:
0040519D    85D2                  test edx,edx                               ; 做为提示信息
0040519F^ 75 D9               jnz short crackme.0040517A               ; \

这个CrackMe总的来说还是比较简单的,因为这个CrackMe本来就是for newbies,专门为我们菜鸟写的嘛:).

算法大体过程如上所叙,根据用户名逆推回去就可以得到正确的keyFile了,说的不清楚的部分,请参看注册机代码(C语言).




#include <iostream>

#include <string>

#include <fstream>

using namespace std;

int main()
{
    string userName;

    string tmp;


    userName.push_back(0x4B ^ 0x54);

    userName.push_back(0x34 ^ 0x4D);
   
    userName.push_back(0x4E ^ 0x47);

    userName.push_back(0x4B ^ 0x20);

    userName.push_back(0x34 ^ 0x20);

    userName.push_back(0x4E ^ 0x20);


    cout << "Please Enter your name:" << endl;

    cin >> tmp;

    userName += tmp;

    int len = userName.length();

    if((userName.length() % 3)!= 0)
    {
      int i=3 * (userName.length() / 3 + 1);

      for(;i > len; i--)
      {
      userName.push_back(0x0);
      
      }
    }

    for(int i = 2;i < userName.length() / 3; i ++)
    {
      userName ^= 0x4B;

      userName ^= 0x34;

      userName ^= 0x4E;
    }

    for(int i = 0; i < userName.length(); i++)
    {
      userName ^= userName.length();
    }



    ofstream outFile;

    outFile.open("key.dat",ios_base::out | ios_base::binary);

    outFile << userName;

    outFile.close();


    system("pause");

    return 0;
}




--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年08月10日 12:01:57

zibeike 发表于 2010-5-24 20:11:19

学习中~~~~~

假面泰山 发表于 2010-5-25 16:16:52

:loveliness:虚心看下。辛苦了。多多发帖。

carson 发表于 2010-5-25 16:50:30

看到这种验证方式,心虚……:sleepy:主要技术太烂

剑哥 发表于 2010-5-25 18:00:36

又可以学习了

miyuecao 发表于 2010-5-26 23:02:20

呵呵 支持支持 楼主好强啊 学习了
页: [1]
查看完整版本: Thigo's keygenme简单分析