[反汇编练习] 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字符,这部分未加入判断,失败也是有概率的。
分析的对不对,欢迎大家回贴交流。 支持一下下 支持,支持 谢谢你的分享,给后来者指了路。 谢谢分享,注册机不容易编写 请问这个crackme合集在哪里可以下载??
页:
[1]