吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 10229|回复: 69

[原创逆向图文] ZD Soft Screen Recorder v9.8 算法逆向 + Keygen

  [复制链接]
LYQingYe 发表于 2016-8-1 10:28 | 显示全部楼层 |阅读模式

[软件名称]  :  ZD SoftScreen Recorder v9.8
[编译类型]  :  VC++
[是否有壳]  :  无壳
[注册类型]  :  注册码注册
[作者信息]  :  LYQingYe

//核心算法CALL查找方法,MFC按钮事件定位 -> 算法CALL ->sub_0042C640 , 如下图. 0x727B6350 跟下去就是核心算法

//MFC按钮事件查找 (PS:查找时,程序禁止被调试)

//浅述,验证方式.
//采用 MD5加密 用户输入的 E-mail , 然后计算拼装成注册码,为了防止一个 E-mail 只固定一个死码,作者采用多重加密,生成多个Key,加密次数最高为 0x3e8 , 次数来源  sub_0042C640 ,参数 push 0x3e8 .
//分为两个阶段,第一阶段MD5加密,与简单计算,第二阶段,拼装注册码  ,具体算法看分析.
//本次测试 Name = LYQingYe  E-mail = xuepojie   Key =0123456789


//进入核心算法CALL


//准备工作,检查参数,合并输入的E-mail 然后初次加密,拷贝我们输入的Key到新的缓冲区,以便以后的比较
[Asm] 纯文本查看 复制代码
0042C640  />sub esp,0x270
0042C646  |>xor eax,eax
0042C648  |>push ebx                                  
0042C649  |>push ebp                                  
0042C64A  |>push esi
0042C64B  |>mov esi,dword ptr ss:[esp+0x280]           ; 获取输入的参数->"Software\ZD Soft\Screen Recorder"
0042C652  |>xor ebx,ebx                                
0042C654  |>push edi
0042C655  |>cmp esi,ebx                                
0042C657  |>mov ebp,ecx                                ;  检查传入参数是否非法->"Software\ZD Soft\Screen Recorder"
0042C659  |>jle ScnRec.0042C7E2
0042C65F  |>mov ecx,0x8
0042C664  |>lea edi,dword ptr ss:[esp+0x55]
0042C668  |>mov byte ptr ss:[esp+0x54],al
0042C66C  |>rep stos dword ptr es:[edi]                ;  将传入的参数字符串合并,路径 + E-mail
0042C66E  |>mov eax,dword ptr ss:[esp+0x288]           ;  UNICODE "Software\ZD Soft\Screen Recorder\xuepojie"
0042C675  |>lea ecx,dword ptr ss:[esp+0x54]
0042C679  |>push eax                                   ;  传入要加密的数据 "Software\ZD Soft\Screen Recorder\xuepojie"
0042C67A  |>push ecx
0042C67B  |>mov ecx,ebp                               
0042C67D  |>call ScnRec.0042C480                       ;  MD5加密
0042C682  |>test eax,eax                               ;  加密后的数据 ASCII "971E2087B3DFED4279B6E06949A5F157"
0042C684  |>jnz short ScnRec.0042C696                  ;  加密失败则返回
0042C686  |>pop edi                                   
0042C687  |>pop esi                                    
0042C688  |>pop ebp                                   
0042C689  |>or eax,-0x1
0042C68C  |>pop ebx                                    
0042C68D  |>add esp,0x270
0042C693  |>retn 0x8
0042C696  |>mov ecx,0x10
0042C69B  |>xor eax,eax
0042C69D  |>lea edi,dword ptr ss:[esp+0x12]
0042C6A1  |>mov word ptr ss:[esp+0x10],bx
0042C6A6  |>rep stos dword ptr es:[edi]
0042C6A8  |>mov ecx,0x81
0042C6AD  |>lea edi,dword ptr ss:[esp+0x7A]
0042C6B1  |>mov word ptr ss:[esp+0x78],bx
0042C6B6  |>lea edx,dword ptr ss:[esp+0x78]
0042C6BA  |>rep stos dword ptr es:[edi]
0042C6BC  |>stos word ptr es:[edi]
0042C6BE  |>lea eax,dword ptr ss:[ebp+0x2A64]            ;获得参数,我们输入的假码
0042C6C4  |>push eax                                   ; /假码 - >01234-56789-
0042C6C5  |>push edx                                   ; |dest = 00000011
0042C6C6  |>call dword ptr ds:[<&MSVCRT.wcscpy>]       ; \将输入的假码拷贝到新的缓冲区,以便后面的比较

//循环加密,并且计算,EDI作为加密循环计数器,ESI作为字符串计算的索引,每次取两个字节,进行计算。

//加密一次后将字符串转换为UNICODE,然后计算.

[Asm] 纯文本查看 复制代码
0042C6CC  |.>add esp,0x8
0042C6CF  |.>xor edi,edi                                ;  初始化EDI作为循环计数器
0042C6D1  |.>cmp esi,ebx                                ;  kernel32.MultiByteToWideChar
0042C6D3  |.>jle ScnRec.0042C7DF
0042C6D9  |.>mov ebx,dword ptr ds:[<&KERNEL32.MultiByte>;  kernel32.MultiByteToWideChar
0042C6DF  |>>/lea eax,dword ptr ss:[esp+0x10]
0042C6E3  |.>|push 0x21
0042C6E5  |.>|push eax
0042C6E6  |.>|lea ecx,dword ptr ss:[esp+0x5C]           ;  ECX - >"971E2087B3DFED4279B6E06949A5F157"
0042C6EA  |.>|push -0x1
0042C6EC  |.>|push ecx                                     ;压入加密数据"971E2087B3DFED4279B6E06949A5F157"
0042C6ED  |.>|push 0x0
0042C6EF  |.>|push 0x0
0042C6F1  |.>|call ebx                                  ;  将加密后的数据转换为 UNICODE ,以便计算
0042C6F3  |.>|lea edx,dword ptr ss:[esp+0x10]
0042C6F7  |.>|lea eax,dword ptr ss:[esp+0x54]
0042C6FB  |.>|push edx                                  ;  压入第一次加密的数据 "971E2087B3DFED4279B6E06949A5F157"
0042C6FC  |.>|push eax
0042C6FD  |.>|mov ecx,ebp                               ;  ScnRec.004A7DE8
0042C6FF  |.>|call ScnRec.0042C480                      ;  MD5再次加密
0042C704  |.>|xor esi,esi                               ;  清空ESI 当作 字符串的索引,每次取两个字节
0042C706  |>>|/mov cx,word ptr ss:[esp+esi*2+0x10]      ;  这里取的是第一次加密数据,也就是前一次,每次取两个字节
0042C70B  |.>||push ecx                                 ; /w
0042C70C  |.>||call dword ptr ds:[<&MSVCRT.iswalpha>]   ; \判断是否为字母
0042C712  |.>||add esp,0x4
0042C715  |.>||test eax,eax
0042C717  |.>||jnz ScnRec.0042C7A0                      ;  不是字母则不计算,跳到尾不,继续循环
0042C71D  |.>||mov eax,esi
0042C71F  |.>||and eax,0x80000001                       ;  取 索引ESI 判断 索引是奇数还是偶数
0042C724  |.>||jns short ScnRec.0042C72B                     ;判断是否为负数
0042C726  |.>||dec eax                                     ;为负数则进行补码操作        
0042C727  |.>||or eax,-0x2                                     ;再判断是奇数还是偶数
0042C72A  |.>||inc eax
0042C72B  |>>||je short ScnRec.0042C743
0042C72D  |.>||xor edx,edx
0042C72F  |.>||mov dx,word ptr ss:[esp+esi*2+0x10]     ;获得加密后的数据
0042C734  |.>||and edx,0x80000001                            ;同上判断是奇数还是偶数
0042C73A  |.>||jns short ScnRec.0042C741
0042C73C  |.>||dec edx
0042C73D  |.>||or edx,-0x2
0042C740  |.>||inc edx
0042C741  |>>||jnz short ScnRec.0042C7A0
0042C743  |>>||test eax,eax
0042C745  |.>||jnz short ScnRec.0042C75A
0042C747  |.>||mov ax,word ptr ss:[esp+esi*2+0x10]      ;  取获得的加密后的数据,每次两个字节
0042C74C  |.>||and eax,0x80000001
0042C751  |.>||jns short ScnRec.0042C758                ;  判断是奇数还是偶数
0042C753  |.>||dec eax
0042C754  |.>||or eax,-0x2
0042C757  |.>||inc eax
0042C758  |>>||je short ScnRec.0042C7A0
0042C75A  |>>||xor eax,eax                              ;  若 ESI 为奇数 以及 加密数据为偶数 ,或者 ESI 为偶数 加密数据为奇数,才跳到这进行计算
0042C75C  |.>||mov ecx,0x14                                     ;ECX = 0x14,作为除数
0042C761  |.>||mov ax,word ptr ss:[esp+esi*2+0x10]      ;  获得加密数据
0042C766  |.>||add eax,esi                              ;  加密数据加上索引
0042C768  |.>||add eax,edi                              ;  加密数据加上循环记数
0042C76A  |.>||cdq                                             ;  把EDX的所有位都设成EAX最高位的值
0042C76B  |.>||idiv ecx                                 ;  将计算后的值 / 0x14
0042C76D  |.>||lea eax,dword ptr ds:[edx+0x47]
0042C770  |.>||cmp ax,0x4F                              ;  判断计算后的值是否为0x4f
0042C774  |.>||mov word ptr ss:[esp+esi*2+0x10],ax
0042C779  |.>||jnz short ScnRec.0042C782
0042C77B  |.>||mov word ptr ss:[esp+esi*2+0x10],0x30    ;  若为0x4f 则写入0x30
0042C782  |>>||cmp word ptr ss:[esp+esi*2+0x10],0x49    ;  判断计算后的值是否为0x49
0042C788  |.>||jnz short ScnRec.0042C791
0042C78A  |.>||mov word ptr ss:[esp+esi*2+0x10],0x31    ;  若为49 ,则写入0x31
0042C791  |>>||cmp word ptr ss:[esp+esi*2+0x10],0x5A    ;  判断计算后的值是否为0x5a
0042C797  |.>||jnz short ScnRec.0042C7A0
0042C799  |.>||mov word ptr ss:[esp+esi*2+0x10],0x32    ;  若为5A则写入0x32
0042C7A0  |>>||inc esi
0042C7A1  |.>||cmp esi,0x20                             ;  加密后的数据长度作为循环次数
0042C7A4  |.>|\jl ScnRec.0042C706                       ;  继续循环计算

[align=left]

//最后一个阶段,拼装字符串,与比较我们输入的key若相等则跳出循环

[Asm] 纯文本查看 复制代码
0042C7AA  |>|lea edx,dword ptr ss:[esp+0x10]
0042C7AE  |>|mov ecx,ebp                               ;  ScnRec.004A7DE8
0042C7B0  |>|push edx
0042C7B1  |>|call ScnRec.0042C1B0                      ;  这里为第二阶段,注册码拼装
0042C7B6  |>|lea eax,dword ptr ss:[esp+0x78]
0042C7BA  |>|push eax                                  ; /wstr2 = 00000058 ???
0042C7BB  |>|lea eax,dword ptr ss:[ebp+0x2A64]         ; |
0042C7C1  |>|push eax                                  ; |wstr1 = 00000058 ???
0042C7C2  |>|call dword ptr ds:[<&MSVCRT.wcscmp>]      ; \注册码与我们输入的假码比较
0042C7C8  |>|add esp,0x8
0042C7CB  |>|test eax,eax
0042C7CD  |>|je short ScnRec.0042C7DF                  ;  成功则跳出循环
0042C7CF  |>|mov eax,dword ptr ss:[esp+0x284]
0042C7D6  |>|inc edi
0042C7D7  |>|cmp edi,eax
0042C7D9  |>\jl ScnRec.0042C6DF                        ;  循环MD5加密和计算

//第一阶段算法解读,有一个寄存器EDI作为for循环的计数器,另外一个寄存器ESI作为读取加密数据字符串的索引,这两个值都用到计算上.

//若 ESI 为奇数 以及 加密数据为偶数 ,或者 ESI 为偶数 加密数据为奇数,才会进行计算,否则不计算,计算算法 很简单
[Asm] 纯文本查看 复制代码
0042C75C  |.>||mov ecx,0x14                                     ;ECX = 0x14,作为除数
0042C761  |.>||mov ax,word ptr ss:[esp+esi*2+0x10]      ;  获得加密数据
0042C766  |.>||add eax,esi                              ;  加密数据加上索引
0042C768  |.>||add eax,edi                              ;  加密数据加上循环记数
0042C76A  |.>||cdq                                             ;  把EDX的所有位都设成EAX最高位的值
0042C76B  |.>||idiv ecx                                 ;  将计算后的值 / 0x14
设加密数据为 Code 所以 ->   calccode = (Code + ESI + EDI) % 0x14 ,然后判断 calccode 是否等于0x4f 0x49 0x5a ,然后写入相应数据.然后组装注册码,进行和输入的假码比较,若想等则结束,否则继续加密循环

//简单总结算法过程 ,具体注释可以看下面的逆向还原注释。
//检查参数,将 Software\ZDSoft\Screen Recorder 与我们输入的E-main字符串链接
//然后进行第一次加密,接着就是计算,计算后拼装注册码,再与我们输入的Key进行比较,若相等则结束,否则继续加密.
//加密次数最大值为0x3e8,这就避免了一个E-mail 只能对应 一个Key 他可以对应 0X3E8个key,校验时,要循环0X3E8次 进行校验,直到相等或者不相等


//两个阶段函数逆向 -> 核心算法CALL 逆向,与 字符串拼装函数逆向

[C] 纯文本查看 复制代码
 //
//计算注册码
//
char * Keygen(char * PathEmali)
{
        char szDigest[16];//加密后的Hex数值
        char *encrypt; //要加密的数据
        char *Key; //加密后的字符串
        wchar_t *Key2; //加密后的字符串
        
        wchar_t * FuckKey;//最终计算的Key

        //循环次数
        ULONG loop = 0x3E8 ; //0042C869  |.  68 E8030000   push 0x3E8

        encrypt = PathEmali;

        Key2 = (WCHAR *)malloc(33);
        

        signed int Index;
        bool IsEven; 
        unsigned int bitflag; 
        bool IsEven2;
        unsigned int bitflag2; 
        int code; 



        //第一次加密,对传入的参数MD5加密

        MD5Digest(encrypt, strlen(encrypt), szDigest);
        //转为字符串
        Key = (char*)HexToAscii((DWORD)&szDigest, 16, TRUE);

        //为最终计算后的Key分配内存
        FuckKey = (wchar_t *)malloc(60);
        memset(FuckKey, 0x0, 60);

        for (int i = 0; i < loop; ++i)
        {

                //转换为宽字符
                MultiByteToWideChar(0, 0, Key, -1, Key2, 33);

                //再次加密数据
                MD5Digest(Key, strlen(Key), szDigest);
                Key = (char*)HexToAscii((DWORD)&szDigest, 16, TRUE);

                Index = 0;

                //
                //这个算法只处理 Index为奇数,code为偶数,或者Index为偶数,code为奇数 。
                //


                do
                {
                        if (!iswalpha(*(Key2 + Index))) //字节递进
                        {

                                if ((Index % 2)) 
                                {
                                        //
                                        //Index为奇数
                                        //


                                        //获取keycode的最高位和最低位
                                        bitflag = *(Key2 + Index) & 0x80000001;

                                        IsEven = bitflag == 0;

                                        //判断最高位是否为0->判断是否为负数
                                        if ((bitflag & 0x80000000) != 0)
                                        {
                                                //负数,则判断是否为偶数
                                                IsEven = (((BYTE)bitflag - 1) | 0xFFFFFFFE) == -1;
                                        }
                                                
                                        if (IsEven)
                                        {
                                        
                                                //keycode为偶数

                                                code = (i + Index + *(Key2 + Index)) % 20;
                                                *(Key2 + Index) = code + 71;
                                                if ((WORD)code == 8)
                                                                *(Key2 + Index) = 48;
                                                if (*(Key2 + Index) == 73)
                                                                *(Key2 + Index) = 49;
                                                        if (*(Key2 + Index) == 90)
                                                                *(Key2 + Index) = 50;

                                        }

                                }
                                else{

                                        //
                                        //Index为偶数
                                        //

                                                //判断最高位是否为0->判断是否为负数
                                                bitflag2 = *(Key2 + Index) & 0x80000001;

                                                IsEven2 = bitflag2 == 0;

                                                //判断最高位是否为0->判断是否为负数
                                                if ((bitflag2 & 0x80000000) != 0)
                                                {
                                                        //判断奇偶
                                                        IsEven2 = (((BYTE)bitflag2 - 1) | 0xFFFFFFFE) == -1;
                                                }
                                                        

                                                
                                                if (!IsEven2)
                                                {
                                                        //Code为奇数

                                                        code = (i + Index + *(Key2 + Index)) % 20;
                                                        *(Key2 + Index) = code + 71;
                                                        if ((WORD)code == 8)
                                                                *(Key2 + Index) = 48;
                                                        if (*(Key2 + Index) == 73)
                                                                *(Key2 + Index) = 49;
                                                        if (*(Key2 + Index) == 90)
                                                                *(Key2 + Index) = 50;
                                                }
                                        
                                }
                                
                        }
                        ++Index;
                } while (Index < 32);


                //组装注册码_> 0042C1B0

                memset(FuckKey, 0X0, 60);
                LinkKey(FuckKey, Key2);


                //在这可以随机输出Key,一个用户名生成的Key可以有0x3e8组

                printf("%ws \n", FuckKey);
        }
        return NULL;
}

游客,如果您要查看本帖隐藏内容请回复



//
//附上测试图,以及keygen
//





点评

m0bil3_xT”点评说:
我擦,一年不见变的如此之牛逼了啊  发表于 2017-2-22 17:39

评分

参与人数 42威望 +1 HB +88 THX +24 收起 理由
xiaoxixpj + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
longge188 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
小声点我布隆 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
24567 + 1
我是小天 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
Jawon + 2
虚心学习 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
太阳神 + 2 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
zxjzzh + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
sjtkxy + 1
冷亦飞 + 1
yexing + 1
禽大师 + 1
temp + 1 + 1
kkk1l + 1
weiran324 + 2 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
胶带 + 1
ldljlzw + 1
我是好人 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
liugu0hai + 1 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
jaunic + 2
成丰羽 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
jsntsjg + 1
消逝的过去 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
bnjzzheng + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
lies + 1
gaoyouren0000 + 1 [快捷评语]--吃水不忘打井人,给个评分懂感恩!
lukun456lx@163 + 1 + 1 [快捷评语]--你将受到所有人的崇拜!
macolma + 1 [快捷评语]--积极评分,从我做起。感谢分享!
gzshaoming + 1 + 1 [快捷评语] - 评分=感恩!简单却充满爱!感谢您的作品!
lovevery + 1 + 1 [快捷评语] - 吃水不忘打井人,给个评分懂感恩!
DDK4282 + 2 + 1 [快捷评语] - 2018,祝您运气旺旺旺!
剑不会断的 + 3 + 1 吃水不忘打井人,给个评分懂感恩!
ronle + 1 这个很强力!
wswwj + 2 + 1 吃水不忘打井人,给个评分懂感恩!
Bu弃 + 5 + 1 表哥威武
菜鸟中的菜鸟 + 6 + 1 待我看懂之日,必是高手之时。
duanbin109 + 1 + 1 分享精神,是最值得尊敬的!
爱我你怕了吗 + 3 + 1 吃水不忘打井人,给个评分懂感恩!
小久久 + 3 + 1 附件形式上传教程,为后来学习者保证了资源稳定性,额外+10HB,感谢!
xuenii + 5 + 1 分享精神,是最值得尊敬的!
Shark恒 + 1 + 30 + 1 评分=感恩!简单却充满爱!感谢您的作品!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Shark恒 发表于 2016-8-1 12:32 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
xuenii 发表于 2016-8-1 13:52 | 显示全部楼层

略微高端,前排仰望拜膜一下~~
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
爱我你怕了吗 发表于 2016-8-1 14:15 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
神马既是浮云 发表于 2016-8-6 17:13 | 显示全部楼层

感谢LYQingYe分享,这玩意只适用于9.8版本吗
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
vov369 发表于 2016-8-7 21:15 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Bu弃 发表于 2016-8-9 20:36 | 显示全部楼层

感谢算法牛表哥
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
woaini 发表于 2016-8-9 21:49 | 显示全部楼层

谢谢了我喜欢
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
fresharplite 发表于 2016-8-9 22:02 | 显示全部楼层

向算法大神学习了
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
hua5551 发表于 2016-8-11 14:11 | 显示全部楼层

大神的世界不懂。。
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表