菜鸟 学注册机编写之 “MD5”
测试环境
系统: xp sp3
调试器 :od 1.10
sc_office_2003_pro
高手不要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!
一:定位关键CALL
1. 因为该软件是word插件,所以用OD载入word.exe,F9运行,运行后如下图:
2. 点击按钮”Save As PDF”出现如下界面
3. 点击”Register”随便输入用户名与注册码,如下图:
4.点击 “暂停” 点击 “K” ,来到如下图的地方
5.选择”MessageBoxExW” 右键 “显示调用”,然后在函数尾下好断点,F9运行,点”确定”,就会被断下
6.接下来就是一直F8单步走,来到如下图的地方
7.往上找就能找到关键跳与关键CALL,如下图
8.在关键CALL下好断点后重新载入OD, F9运行,随便输入用户名与注册码,接着就是分析算法了
二:算法分析
1.首先将注册码中含有小写字母转换成大写
1010EC3B 8BC6 mov eax,esi 1010EC3D 66:391E cmp word ptr ds:[esi],bx ; ---将字母转换成大写 1010EC40 74 1B je short doc2pdf.1010EC5D 1010EC42 0FB708 movzx ecx,word ptr ds:[eax] ; 判断并取字符 1010EC45 83F9 61 cmp ecx,0x61 ; ECX小于a 就跳 1010EC48 72 0B jb short doc2pdf.1010EC55 1010EC4A 83F9 7A cmp ecx,0x7A ; ECX大于z 就跳 1010EC4D 77 06 ja short doc2pdf.1010EC55 1010EC4F 83C1 E0 add ecx,-0x20 ; 满足条件就将ECX and -0x20 1010EC52 66:8908 mov word ptr ds:[eax],cx ; 存放 1010EC55 83C0 02 add eax,0x2 1010EC58 66:3918 cmp word ptr ds:[eax],bx 1010EC5B ^ 75 E5 jnz short doc2pdf.1010EC42 ; 判断是否完成 1010EC5D 33C0 xor eax,eax 1010EC5F E9 E2000000 jmp doc2pdf.1010ED46
2.分析关键 call doc2pdf.100182D0 , F7跟进
10018376 E8 25F0FFFF call <doc2pdf.strcat> ;用户名与固定字符串拼接 1001741A E8 F1F3FEFF call <doc2pdf.MtoB> ; 将用户名中字母转换成大写 1010EC3D 66:391E cmp word ptr ds:[esi],bx ; ---将字母转换成大写 1010EC40 74 1B je short doc2pdf.1010EC5D 1010EC42 0FB708 movzx ecx,word ptr ds:[eax] ; 判断并取字符 1010EC45 83F9 61 cmp ecx,0x61 ; ECX小于a 就跳 1010EC48 72 0B jb short doc2pdf.1010EC55 1010EC4A 83F9 7A cmp ecx,0x7A ; ECX大于z 就跳 1010EC4D 77 06 ja short doc2pdf.1010EC55 1010EC4F 83C1 E0 add ecx,-0x20 ; 满足条件就将ECX and -0x20 1010EC52 66:8908 mov word ptr ds:[eax],cx ; 存放 1010EC55 83C0 02 add eax,0x2 1010EC58 66:3918 cmp word ptr ds:[eax],bx 1010EC5B ^ 75 E5 jnz short doc2pdf.1010EC42 ; 判断是否完成 1010EC5D 33C0 xor eax,eax 1010EC5F E9 E2000000 jmp doc2pdf.1010ED46 1001744B E8 A0280000 call <doc2pdf.WideCharToMultiByte> ; 将用户名宽字符转换成单字符 1001745E E8 FD280000 call <doc2pdf.Remove_digital> ; 去掉用户输入用户名中包含的数字 10019D60 <doc2pd> 55 push ebp 10019D61 8BEC mov ebp,esp 10019D63 56 push esi 10019D64 8B75 08 mov esi,dword ptr ss:[ebp+0x8] 10019D67 85F6 test esi,esi 10019D69 74 37 je short doc2pdf.10019DA2 10019D6B 8B55 0C mov edx,dword ptr ss:[ebp+0xC] 10019D6E 85D2 test edx,edx 10019D70 74 30 je short doc2pdf.10019DA2 10019D72 8A0A mov cl,byte ptr ds:[edx] 10019D74 8BC6 mov eax,esi 10019D76 84C9 test cl,cl 10019D78 74 25 je short doc2pdf.10019D9F 10019D7A 8D9B 00000000 lea ebx,dword ptr ds:[ebx] 10019D80 80F9 61 cmp cl,0x61 ; 判断字符a 10019D83 7C 05 jl short doc2pdf.10019D8A 10019D85 80F9 7A cmp cl,0x7A ; 判断字符z 10019D88 7E 0A jle short doc2pdf.10019D94 10019D8A 80F9 41 cmp cl,0x41 ; 字符A 10019D8D 7C 08 jl short doc2pdf.10019D97 10019D8F 80F9 5A cmp cl,0x5A ; 字符Z 10019D92 7F 03 jg short doc2pdf.10019D97 10019D94 880E mov byte ptr ds:[esi],cl 10019D96 46 inc esi 10019D97 8A4A 01 mov cl,byte ptr ds:[edx+0x1] 10019D9A 42 inc edx 10019D9B 84C9 test cl,cl 10019D9D ^ 75 E1 jnz short doc2pdf.10019D80 10019D9F 5E pop esi 10019DA0 5D pop ebp 10019DA1 C3 retn 1001746D E8 7E280000 call <doc2pdf.WideCharToMultiByte> ; 将字串"f4hguNAg"转换成单字符 10017485 E8 5D8F0F00 call <doc2pdf.strcat> ; 用户名去掉数字与固定字符串连接 101103E7 <doc2pd> 8BFF mov edi,edi 101103E9 55 push ebp 101103EA 8BEC mov ebp,esp 101103EC 8B4D 08 mov ecx,dword ptr ss:[ebp+0x8] 101103EF 56 push esi 101103F0 57 push edi 101103F1 85C9 test ecx,ecx 101103F3 74 07 je short doc2pdf.101103FC 101103F5 8B7D 0C mov edi,dword ptr ss:[ebp+0xC] 101103F8 85FF test edi,edi 101103FA 75 13 jnz short doc2pdf.1011040F 101103FC E8 B00F0000 call doc2pdf.101113B1 10110401 6A 16 push 0x16 10110403 5E pop esi 10110404 8930 mov dword ptr ds:[eax],esi 10110406 E8 B4490000 call doc2pdf.10114DBF 1011040B 8BC6 mov eax,esi 1011040D EB 41 jmp short doc2pdf.10110450 1011040F 8B55 10 mov edx,dword ptr ss:[ebp+0x10] 10110412 85D2 test edx,edx 10110414 75 05 jnz short doc2pdf.1011041B 10110416 C601 00 mov byte ptr ds:[ecx],0x0 10110419 ^ EB E1 jmp short doc2pdf.101103FC 1011041B 8BF1 mov esi,ecx 1011041D 803E 00 cmp byte ptr ds:[esi],0x0 ; 判断用户是否为0 10110420 74 04 je short doc2pdf.10110426 10110422 46 inc esi 10110423 4F dec edi 10110424 ^ 75 F7 jnz short doc2pdf.1011041D 10110426 85FF test edi,edi 10110428 ^ 74 EC je short doc2pdf.10110416 1011042A 2BF2 sub esi,edx 1011042C 8A02 mov al,byte ptr ds:[edx] 1011042E 880416 mov byte ptr ds:[esi+edx],al ; ----字符串f4hguNAg与用户名连接 10110431 42 inc edx 10110432 84C0 test al,al 10110434 74 03 je short doc2pdf.10110439 10110436 4F dec edi 10110437 ^ 75 F3 jnz short doc2pdf.1011042C 10110439 85FF test edi,edi 1011043B 75 11 jnz short doc2pdf.1011044E 1011043D C601 00 mov byte ptr ds:[ecx],0x0 10110440 E8 6C0F0000 call doc2pdf.101113B1 10110445 6A 22 push 0x22 10110447 59 pop ecx 10110448 8908 mov dword ptr ds:[eax],ecx 1011044A 8BF1 mov esi,ecx 1011044C ^ EB B8 jmp short doc2pdf.10110406 1011044E 33C0 xor eax,eax 10110450 5F pop edi 10110451 5E pop esi 10110452 5D pop ebp 10110453 C3 retn
拼接完成后得到字符串 “TESTf4hguNAg”
将拼接完成后的字符传入函数进行计算, F7跟进,观察函数发下如下图所示特征
貌似MD5计算,我们直接让该函数走完,看它会出什么结果,发现函数执行完后与我们用工具直接对字符串” TESTf4hguNAg”进行MD5的结果是一样的。如下图
然后将计算得到的MD5值转换成字符,并且将小写字母转换成大写,如下图
3.查找并替换MD5中的字符
100183E2 6A 5A push 0x5A ; Z 100183E4 6A 31 push 0x31 ; 1 100183E6 8D8D 44FEFFFF lea ecx,dword ptr ss:[ebp-0x1BC] 100183EC E8 6FE3FEFF call <doc2pdf.Find_and_Replace> ; 查找第1个参数,找到则用第2个替换
1替换成Z
2替换成W
0替换成K
3替换成T
8替换成P
7替换成S
10006760 <doc2pd> 55 push ebp 10006761 8BEC mov ebp,esp 10006763 83EC 08 sub esp,0x8 10006766 66:8B45 08 mov ax,word ptr ss:[ebp+0x8] 1000676A 53 push ebx 1000676B 57 push edi 1000676C 33FF xor edi,edi 1000676E 8BD9 mov ebx,ecx 10006770 897D F8 mov dword ptr ss:[ebp-0x8],edi 10006773 66:3B45 0C cmp ax,word ptr ss:[ebp+0xC] 10006777 74 72 je short doc2pdf.100067EB 10006779 8B03 mov eax,dword ptr ds:[ebx] 1000677B 56 push esi 1000677C 8B70 F4 mov esi,dword ptr ds:[eax-0xC] 1000677F 32C9 xor cl,cl 10006781 3BF7 cmp esi,edi 10006783 7E 65 jle short doc2pdf.100067EA 10006785 66:8B55 08 mov dx,word ptr ss:[ebp+0x8] 10006789 66:391478 cmp word ptr ds:[eax+edi*2],dx ; 比较MD5值中是否有传进的第1个参数 1000678D 75 37 jnz short doc2pdf.100067C6 1000678F 84C9 test cl,cl 10006791 75 28 jnz short doc2pdf.100067BB 10006793 C645 FF 01 mov byte ptr ss:[ebp-0x1],0x1 10006797 85F6 test esi,esi 10006799 78 5B js short doc2pdf.100067F6 1000679B 8B03 mov eax,dword ptr ds:[ebx] ; ----MD5值地址 1000679D 8B50 F8 mov edx,dword ptr ds:[eax-0x8] 100067A0 B9 01000000 mov ecx,0x1 100067A5 2B48 FC sub ecx,dword ptr ds:[eax-0x4] 100067A8 2BD6 sub edx,esi 100067AA 0BCA or ecx,edx ; 0xA 100067AC 7D 08 jge short doc2pdf.100067B6 100067AE 56 push esi 100067AF 8BCB mov ecx,ebx 100067B1 E8 BAEDFFFF call doc2pdf.10005570 100067B6 8B03 mov eax,dword ptr ds:[ebx] ; ---MD5值地址 100067B8 8A4D FF mov cl,byte ptr ss:[ebp-0x1] 100067BB 66:8B55 0C mov dx,word ptr ss:[ebp+0xC] ; ---函数传进来的第2个参数 100067BF FF45 F8 inc dword ptr ss:[ebp-0x8] 100067C2 66:891478 mov word ptr ds:[eax+edi*2],dx ; 将传进的第2个参数替换掉MD5中查到的值 100067C6 8D7C3F 02 lea edi,dword ptr ds:[edi+edi+0x2] 100067CA D1FF sar edi,1 100067CC 3BFE cmp edi,esi ; 判断是否查找结束 100067CE ^ 7C B5 jl short doc2pdf.10006785 100067D0 84C9 test cl,cl 100067D2 74 16 je short doc2pdf.100067EA 100067D4 85F6 test esi,esi 100067D6 78 1E js short doc2pdf.100067F6 100067D8 8B03 mov eax,dword ptr ds:[ebx] 100067DA 3B70 F8 cmp esi,dword ptr ds:[eax-0x8] 100067DD 7F 17 jg short doc2pdf.100067F6 100067DF 8970 F4 mov dword ptr ds:[eax-0xC],esi 100067E2 8B03 mov eax,dword ptr ds:[ebx] 100067E4 33C9 xor ecx,ecx 100067E6 66:890C70 mov word ptr ds:[eax+esi*2],cx 100067EA 5E pop esi 100067EB 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] 100067EE 5F pop edi 100067EF 5B pop ebx 100067F0 8BE5 mov esp,ebp 100067F2 5D pop ebp 100067F3 C2 0800 retn 0x8 //--获得注册码 1001844B E8 90D9FEFF call <doc2pdf.Get12Char> ; --获取MD5替换后前12字节 10018450 6A 0C push 0xC 10018452 8D8D 38FEFFFF lea ecx,dword ptr ss:[ebp-0x1C8] 10018458 51 push ecx 10018459 8BCB mov ecx,ebx 1001845B C645 FC 02 mov byte ptr ss:[ebp-0x4],0x2 1001845F E8 7CD9FEFF call <doc2pdf.Get12Char> ; --获取用户输入注册码12字节 10018464 8B00 mov eax,dword ptr ds:[eax]
4.比较注册码,如下图
1010EB5D 8BFF mov edi,edi 1010EB5F 55 push ebp 1010EB60 8BEC mov ebp,esp 1010EB62 833D 78EA1810 0>cmp dword ptr ds:[0x1018EA78],0x0 1010EB69 56 push esi 1010EB6A 75 69 jnz short doc2pdf.1010EBD5 1010EB6C 8B75 08 mov esi,dword ptr ss:[ebp+0x8] 1010EB6F 85F6 test esi,esi 1010EB71 75 17 jnz short doc2pdf.1010EB8A 1010EB73 E8 39280000 call doc2pdf.101113B1 1010EB78 C700 16000000 mov dword ptr ds:[eax],0x16 1010EB7E E8 3C620000 call doc2pdf.10114DBF 1010EB83 B8 FFFFFF7F mov eax,0x7FFFFFFF 1010EB88 EB 5B jmp short doc2pdf.1010EBE5 1010EB8A 8B4D 0C mov ecx,dword ptr ss:[ebp+0xC] 1010EB8D 85C9 test ecx,ecx 1010EB8F ^ 74 E2 je short doc2pdf.1010EB73 1010EB91 2BF1 sub esi,ecx 1010EB93 0FB7040E movzx eax,word ptr ds:[esi+ecx] ; 取MD5后生成的注册码 1010EB97 83F8 41 cmp eax,0x41 ; A 1010EB9A 72 0D jb short doc2pdf.1010EBA9 1010EB9C 83F8 5A cmp eax,0x5A ; Z 1010EB9F 77 08 ja short doc2pdf.1010EBA9 1010EBA1 83C0 20 add eax,0x20 ; 转换成小写 1010EBA4 0FB7D0 movzx edx,ax ; 给值dx 1010EBA7 EB 02 jmp short doc2pdf.1010EBAB 1010EBA9 8BD0 mov edx,eax 1010EBAB 0FB701 movzx eax,word ptr ds:[ecx] ; 取用户输入的注册码 1010EBAE 83F8 41 cmp eax,0x41 ; A 1010EBB1 72 0B jb short doc2pdf.1010EBBE 1010EBB3 83F8 5A cmp eax,0x5A ; Z 1010EBB6 77 06 ja short doc2pdf.1010EBBE 1010EBB8 83C0 20 add eax,0x20 ; 转换成小写 1010EBBB 0FB7C0 movzx eax,ax 1010EBBE 83C1 02 add ecx,0x2 1010EBC1 66:85D2 test dx,dx ; 判断是否为零 1010EBC4 74 05 je short doc2pdf.1010EBCB 1010EBC6 66:3BD0 cmp dx,ax ; 比较注册码 1010EBC9 ^ 74 C8 je short doc2pdf.1010EB93 1010EBCB 0FB7C8 movzx ecx,ax 1010EBCE 0FB7C2 movzx eax,dx 1010EBD1 2BC1 sub eax,ecx 1010EBD3 EB 10 jmp short doc2pdf.1010EBE5 1010EBD5 6A 00 push 0x0 1010EBD7 FF75 0C push dword ptr ss:[ebp+0xC] 1010EBDA FF75 08 push dword ptr ss:[ebp+0x8] 1010EBDD E8 78FEFFFF call doc2pdf.1010EA5A 1010EBE2 83C4 0C add esp,0xC 1010EBE5 5E pop esi 1010EBE6 5D pop ebp 1010EBE7 C3 retn
总结
1. 将用户输入的注册码中小写字母转换成大写
2. 将用户名中数字去掉并将字母转换成大写
3. 将用户名与固定字符拼接
4. 将拼接后的字符进行MD5计算并且将值转换成字符
5. 查找替换MD5字符
6. 取MD5前12字节做注册码
注册机编写
#include <stdio.h> #include <windows.h> #include <string.h> #include "md5.h" /**************************************************************************** 函数名称: hex_to_str 函数功能: 十六进制转字符串 输入参数: ptr 字符串 buf 十六进制 len 十六进制字符串的长度。 输出参数: 无 *****************************************************************************/ void hex_to_str(char *ptr,unsigned char *buf,int len) { int i=0; for(i = 0; i < len; i++) { sprintf(ptr, "%02x",buf[i]); ptr += 2; } } /* MD5: 转换成大写的MD5值 Val: 要查找到值 RpVal: 要替换的值 */ void FindVal_Replace(char* MD5, char Val, char RpVal) { int i = 0; if (NULL == MD5 || 0 == Val || 0 == RpVal) { return; } for (i=0; i<strlen(MD5); i++) { if ( Val == MD5[i] ) { MD5[i] = RpVal; } } } int main(int argc, char* argv[]) { char UserName[256] = {0}; char License[256] = {0}; char BigStrName[256] = {0}; char MD5[256] = {0}; //与用户名拼凑 char *ConstString = "f4hguNAg"; MD5_CTX context; long dtLength; char szHash[256] = {0}; TCHAR szBuffer[256] = {0}; int i = 0; int n = 0; char j = 0; printf("---------doc2pdf2 V5.0注册机-------\n\n"); printf("请输入用户名: "); scanf("%s",UserName); if ( strlen(UserName) >= 240 ) { printf("用户名不能超过240位\n"); return -1; } for (i=0; i<strlen(UserName); i++) { //--是数字 if (isdigit(UserName[i]) != 0) { continue; } else { //--是字母 if ( isalpha(UserName[i])!=0) { //--判断是大小写并转换成大写 if(UserName[i]>='a'&& UserName[i]<='z') { BigStrName[i] = UserName[i] - 32; continue; } } } } //--用户名与固定字符拼凑并计算MD5值 strcat(BigStrName,ConstString); // strncpy(BigStrName+strlen(BigStrName), ConstString, strlen(ConstString)); //计算MD5值 MD5Init(&context); MD5Update(&context, BigStrName, strlen(BigStrName)); MD5Final(szHash, &context); //--将MD5值转换成字符 hex_to_str(MD5, szHash, strlen(szHash)); //--将MD5字符串转换成大写 for (n=0; n<strlen(MD5); n++) { //--判断是大小写并转换成大写 if(MD5[n]>='a'&& MD5[n]<='z') { MD5[n] -= 32; continue; } } //--查找并替换掉MD5字符 FindVal_Replace(MD5, '1', 'Z');//1替换成Z FindVal_Replace(MD5, '2', 'W');//2替换成W FindVal_Replace(MD5, '0', 'K');//0替换成K FindVal_Replace(MD5, '3', 'T');//3替换成T FindVal_Replace(MD5, '8', 'P');//8替换成P FindVal_Replace(MD5, '7', 'S');//7替换成S //-拷贝注册码 strncpy(License, MD5, 12); printf("注册码:%s\n",License); system("pause"); return 0; }
测试注册机
输入用户名 test 生成注册码
注册成功
样本及注册机源码下载
http://yunpan.cn/cAFbdqsBEYEyr (提取码:2967)