测试环境 系统: 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)