pk8900 发表于 2017-12-15 13:19

[反汇编练习] 160个CrackMe之047(DueList.2.exe)算法分析及注册机编写

本帖最后由 pk8900 于 2017-12-15 13:22 编辑

   
   昨天晚上,继续研究 了【适合破解新手的160个crackme练手】,本文内容为第47个CrackMe: DueList.2.exe,论坛里搜索了一下关于这个CrackMe的帖子,没找到,所以写了这篇帖子,分享一下我的分析过程及注册机编写方法。
【crackme简介】
       下载地址:http://pan.baidu.com/share/link?shareid=541269&uk=4146939145
       MASM32 / TASM32编写,无壳,是一个Keyfile验证方式,打开后只有一个提示框,大意是说:你的试用时间已到期,请把KEY文件放到目录下进行注册。
      分析工具:X64dbg,OD
      这个CRACKME和034有些像,可以参照帖子:https://www.52pojie.cn/thread-673404-1-1.html
【crackme截图】

【算法分析过程】
关键代码位置如下:
00401057   .A3 AF214000   mov dword ptr ds:,eax          ; |kernel32.BaseThreadInitThunk
0040105C   .6A 00         push 0x0                                 ; |/hTemplateFile = NULL
0040105E   .68 6F214000   push DueList_.0040216F                   ; ||Attributes = READONLY|HIDDEN|SYSTEM|ARCHIVE|TEMPORARY|402048
00401063   .6A 03         push 0x3                                 ; ||Mode = OPEN_EXISTING
00401065   .6A 00         push 0x0                                 ; ||pSecurity = NULL
00401067   .6A 03         push 0x3                                 ; ||ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401069   .68 000000C0   push 0xC0000000                        ; ||Access = GENERIC_READ|GENERIC_WRITE
0040106E   .68 79204000   push DueList_.00402079                   ; ||FileName = "due-cm2.dat"
00401073   .E8 0B020000   call <jmp.&KERNEL32.CreateFileA>         ; |\CreateFileA
00401078   .83F8 FF       cmp eax,-0x1                           ; |
0040107B   .75 1D         jnz short DueList_.0040109A            ; |
0040107D   .6A 00         push 0x0                                 ; |/Style = MB_OK|MB_APPLMODAL
0040107F   .68 01204000   push DueList_.00402001                   ; ||Title = "Duelist's Crackme #2"
00401084   .68 17204000   push DueList_.00402017                   ; ||Text = "Your time-trial has ended... Please register and copy the keyfile sent to you to this directory!"
00401089   .6A 00         push 0x0                                 ; ||hOwner = NULL
0040108B   .E8 D7020000   call <jmp.&USER32.MessageBoxA>         ; |\MessageBoxA
00401090   .E8 24020000   call <jmp.&KERNEL32.ExitProcess>         ; \ExitProcess
00401095   .E9 28010000   jmp DueList_.004011C2
0040109A   >6A 00         push 0x0                                 ; /pOverlapped = NULL
0040109C   .68 73214000   push DueList_.00402173                   ; |pBytesRead = DueList_.00402173
004010A1   .6A 46         push 0x46                              ; |BytesToRead = 46 (70.)
004010A3   .68 1A214000   push DueList_.0040211A                   ; |Buffer = DueList_.0040211A
004010A8   .50            push eax                                 ; |hFile = 75753358
004010A9   .E8 2F020000   call <jmp.&KERNEL32.ReadFile>            ; \ReadFile
004010AE   .85C0          test eax,eax                           ;kernel32.BaseThreadInitThunk
004010B0   .75 02         jnz short DueList_.004010B4
004010B2   .EB 43         jmp short DueList_.004010F7
004010B4   >33DB          xor ebx,ebx
004010B6   .33F6          xor esi,esi
004010B8   .833D 73214000>cmp dword ptr ds:,0x12         ;读取长度小于 0x12 失败
004010BF   .7C 36         jl short DueList_.004010F7
004010C1   >8A83 1A214000 mov al,byte ptr ds:         ;第一段代码
004010C7   .3C 00         cmp al,0x0                               ;key为零结循环
004010C9   .74 08         je short DueList_.004010D3
004010CB   .3C 01         cmp al,0x1
004010CD   .75 01         jnz short DueList_.004010D0            ;key等于0x01则累加ESI计数
004010CF   .46            inc esi
004010D0   >43            inc ebx
004010D1   .^ EB EE         jmp short DueList_.004010C1
004010D3   >83FE 02       cmp esi,0x2                              ;第一条件:至少有两个0x01
004010D6   .7C 1F         jl short DueList_.004010F7
以上代码部分分析结果如下:
       1、CreateFileA参数Mode = OPEN_EXISTING,打开已存在的文件,若文件不存在,函数返回-1,是用来验证Key文件是否存在的。
       2、ReadFile函数读取文件内容,参数pBytesRead = Cruehead.004021A0是读取文件返回的字节数,接下来一句代码cmp dword ptr ds:[0x402173],0x12,如果读取的字节数少于0x12(18)个,则跳至失败处,BytesToRead = 46 (70.),预读取字节为70个。
       3、FileName = "due-cm2.dat",所以我们在CrackMe目录下新建一个文件due-cm2.dat,并写入18个字节以上的数据,内容随意。
      再次载入程序,在ReadFile后代码下断点,进行分析。读入的字节我们分别用Key~Key来表示,第一段循环代码,从Key开始,一直遇到\0空字符结束,统计出Key~Key中0x01字节的个数,最少应为2个,少于2个则跳至KEY文件无效提示。
004010DA   .33DB          xor ebx,ebx                              ;第二段代码循环
004010DC   >8A83 1A214000 mov al,byte ptr ds:
004010E2   .3C 00         cmp al,0x0
004010E4   .74 09         je short DueList_.004010EF
004010E6   .3C 01         cmp al,0x1
004010E8   .74 05         je short DueList_.004010EF
004010EA   .03F0          add esi,eax                              ;kernel32.BaseThreadInitThunk
004010EC   .43            inc ebx
004010ED   .^ EB ED         jmp short DueList_.004010DC
004010EF   >81FE D5010000 cmp esi,0x1D5                            ;第一个01前,累加值为0x1D5
004010F5   .74 1D         je short DueList_.00401114
  上面的第二段代码循环是:
  从Key开始,一直到第一个0x01字节,累加各字节值为0x1D5,不含0x01字节,则成功。
00401114   > \33F6          xor esi,esi                              ;第三段代码
00401116   >43            inc ebx
00401117   .8A83 1A214000 mov al,byte ptr ds:
0040111D   .3C 00         cmp al,0x0
0040111F   .74 18         je short DueList_.00401139
00401121   .3C 01         cmp al,0x1
00401123   .74 14         je short DueList_.00401139
00401125   .83FE 0F       cmp esi,0xF                              ;>=0xF个字符结束
00401128   .73 0F         jnb short DueList_.00401139
0040112A   .3286 1A214000 xor al,byte ptr ds:      ;与第一字答异或,依次存入402160地址
00401130   .8986 60214000 mov dword ptr ds:,eax      ;kernel32.BaseThreadInitThunk
00401136   .46            inc esi
00401137   .^ EB DD         jmp short DueList_.00401116
00401139   >43            inc ebx                                  ;遇到01或到第0xf个字符结束
0040113A   .33F6          xor esi,esi
上面的第三段代码循环是:
从第一个0x01字节后的字节开始,分别与Key~Key异或,异或结果存入402160地址,一直到第二个0x01字节或到Key结束,同样不含最后的那个字节,402160地址异或后的字节将成为最终注册成功显示的用户名。
0040113A   .33F6          xor esi,esi                              ;第四部分循环代码
0040113C   >8A83 1A214000 mov al,byte ptr ds:
00401142   .3C 00         cmp al,0x0
00401144   .74 09         je short DueList_.0040114F
00401146   .3C 01         cmp al,0x1
00401148   .^ 74 F2         je short DueList_.0040113C
0040114A   .03F0          add esi,eax                              ;kernel32.BaseThreadInitThunk
0040114C   .43            inc ebx
0040114D   .^ EB ED         jmp short DueList_.0040113C
0040114F   >81FE B2010000 cmp esi,0x1B2                            ;到00字符前累加值为0x1B2,不累加0x01
00401155   .^ 75 A0         jnz short DueList_.004010F7
上面的第四段代码循环是:累加第三部分之后的字节值,一直到0x00字节,其中的0x01字节不累加在内,若累加值为0x1B2则成功验证完成,在文本框内显示402160地址的用户名,上面有已注册的英文提示。
【注册机编写】
第一个0x01前的累加值为:1D5,所以前三个字节可以为0xEA,0xEB,0x01,这样从第四个开始可以留给用户使用,一直到第16个,所以能注册的用户长度最长为13个字符。
用户名部分将想要注册显示的用户名依次序从Key开始异或,存入文件中。后面的部分就比较简单了,长度不限,只要到空字符之前的累加值为0x1B2,并写入第二个0x01就行。
C++代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <windows.h>
using namespace std;
int main()
{
      char * path = "E:\\crackme\\due-cm2.dat";
      FILE * keyfile;
      char * youkey = new char;         //输入用户名
      memset(youkey, 0, 40);
      unsigned char * writekey = new unsigned char;
      memset(writekey, 0, 40);             //存入文件的内容
      cout << "Enter your name:";
      gets(youkey);
      if (strlen(youkey) >= 8)
                cout << "用户名最多支持13位,其余部分将被截断!" << endl;
      int now = 0;
      
      writekey = 0xEA;
      writekey = 0xEB;
      writekey = 0x01;
      for (unsigned int x = 0; x < 13 && x<strlen(youkey); x++)
      {
                writekey = youkey^writekey;
      }
      if (strlen(youkey) >= 13)
                now = 16;
      else
                now = strlen(youkey) + 3;

      writekey = 0x01;
      writekey = 0xD9;
      writekey = 0xD9;
      errno_t err = fopen_s(&keyfile, path, "w+b");
      if (err != 0)
      {
                cout << "file open or create failed!" << endl;
                system("pause");
                return -1;
      }
      rewind(keyfile);
      fwrite(writekey, sizeof(byte), 40, keyfile);
      fclose(keyfile);
      cout << "write file over!" << endl;
      delete[] youkey;
      delete[] writekey;
      system("pause");
      return 1;
}
附注册成功后截图:

说明:后来想了一下,上面的注册机中异或后的用户名字符部分有可能含有0x01字符,这部分未加入判断,失败也是有概率的。
分析的对不对,欢迎大家回贴交流。

13025796343 发表于 2017-12-15 13:35

支持一下下

shanhuyi 发表于 2017-12-15 14:09

支持,支持

异客CO 发表于 2018-12-17 21:54

谢谢你的分享,给后来者指了路。

zyj66 发表于 2018-12-18 10:31

谢谢分享,注册机不容易编写

zq7695zq 发表于 2018-12-18 16:53

请问这个crackme合集在哪里可以下载??
页: [1]
查看完整版本: [反汇编练习] 160个CrackMe之047(DueList.2.exe)算法分析及注册机编写