吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6444|回复: 18
收起左侧

[CTF] Gslab游戏安全竞赛社会组WP(第一轮进阶版)

[复制链接]
loudy 发表于 2017-8-7 21:22
本帖最后由 loudy 于 2017-10-17 08:35 编辑

最近新开了一个微信公众号:风云处(fyc1687),欢迎大家多多交流。其中我会逐步把我学习逆向的经验、实例、技巧分享出来。

一、整体流程IDA载入,简单分析发现,主要处理流程在sub_403B70处,如下图,函数不长,但是很复杂。
5.png
0.png
1.png

   进阶版引进了RSA思想。上图sub_4033A0检查userName的合法性;sub_4034E0从userName生成4个64位整数,具体算法后面分析;sub_403860和sub_405FB0两个函数从输入的userName生成了RSA的N和E;sub_405CA0对输入的RegCode进行处理,得到16字节长数组(得不到则失败),即2个64位整数;接着sub_403160检测之前生成的6个64位整数是否满足其中公式。

二、关键函数剖析
(1)sub_4033A0如下图,IDA中已经标注,key长度为0x27,其中连接符“-”共7个,有效位0x20个,分成8段,每段4byte,每个有效byte均为0-9或a-f或A-F(转化为a-f)。
2.png
3.png
4.png

(2)sub_4034E0该函数经过调试化简,可以与以下函数等价,key为输入,param为a1、a2、a3、a6。

[C] 纯文本查看 复制代码
int get_formula_param(unsigned __int64 * param,char * key)
{
     if(strlen(key)!=0x27)
     {
         return -1;
     }
     if(key[4]!='-' ||key[9]!='-' ||key[14]!='-' ||key[19]!='-' ||key[24]!='-' ||key[29]!='-' ||key[34]!='-')
     {
         return -2;
     }
     for(int i=0;i<0x27;i++)
     {
         if(i!=4 && i!=9 && i!=14 && i!=19 && i!=24 && i!=29 && i!=34)
         {
              if(!(key>='0'&&key<='9'||key>='a'&&key<='f'))
              {
                   return -3;
              }
         }
     }
     unsigned int s0[4],s1[4],s2[4],s3[4],s4[4],s5[4],s6[4],s7[4];
     for(int i=0;i<4;i++)
     {
         s0 = (unsigned int)key;
         s1 = (unsigned int)key[5+i];
         s2 = (unsigned int)key[10+i];
         s3 = (unsigned int)key[15+i];
         s7 = (unsigned int)key[20+i];
         s6 = (unsigned int)key[25+i];
         s5 = (unsigned int)key[30+i];
         s4 = (unsigned int)key[35+i];
     }
     unsigned x0 = s0[0]*s4[0];
     x0 = x0<<0x10;
     param[0] = x0 + s0[1]^s4[1] + s0[2]%(s4[2]+1)+1 + s0[3]/(s4[3]+1);
     x0 = s1[0]^s5[0];
     x0 = x0<<0x10;
     param[1] = x0 + s1[1]%(s5[1]+3)+s1[2]/(s5[2]+1)+5 +s1[3]+s5[3];
     x0 = s2[0]/(s6[0]+3);
     x0 = x0<<0x10;
     param[2] = x0^(s2[1]*s6[1])+s2[2]%(s6[2]+7)+ 5 + s2[3]+s6[3];
  
     x0 = s3[0]+s7[0];
     x0 = x0<<0x10;
     param[3] = x0*(s3[1]/(s7[1]+2)) + (s3[2]%(s7[2]+5))+7 + s3[3]*s7[3];
}


(3)sub_403860和sub_405FB0涉及到大数的运算以及随机值生成算法,过程比较复杂,应该用到了某大数库,但没找到对应的,解题过程中,直接调用了原程序,得到RSA的N和E(附件程序中使用了Miracl库)。
(4)sub_405CA0该函数比较复杂,首先是对RegCode进行base64解码,如下图:
0.png
此base64解码做了变形,首先常量从“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/”变成了“OPWvYny#Nopz0$HI34QRSG@dJKq7fghD9Zi*kAB8rsFu56L&Ca^2tTUVEewxlm+/”,如下图。
1.png
其次,下图红线处在基本base64上加了异或操作。
0.png
因此将base64编解码做变形如下:

[C] 纯文本查看 复制代码
const char * base64char = "OPWvYny#Nopz0$HI34QRSG@dJKq7fghD9Zi*kAB8rsFu56L&Ca^2tTUVEewxlm+/";
  
char * base64_encode( const unsigned char * bindata, char * base64, int binlength )
{
    int i, j;
    unsigned char current;
     unsigned char k;
  
    for ( i = 0, j = 0 ; i < binlength ; i += 3 )
    {
        current = (bindata >> 2) ;
        current &= (unsigned char)0x3F;
         for(k=0;k<64;k++)
         {
              unsigned h = k>>4;
              h = k^h;
               
              if(h == current)
              {
                  current = k;
                   break;
              }
         }
        base64[j++] = base64char[(int)current];
  
        current = ( (unsigned char)(bindata << 4 ) ) & ( (unsigned char)0x30 ) ;
  
        if ( i + 1 >= binlength )
        {
            for(k=0;k<64;k++)
              {
                   unsigned h = k>>4;
                   h = k^h;
                    
                   if(h == current)
                   {
                       current = k;
                       break;
                   }
              }
              base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            base64[j++] = '=';
            break;
        }
        current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
         for(k=0;k<64;k++)
         {
              unsigned h = k>>4;
              h = k^h;
               
              if(h == current)
              {
                   current = k;
                   break;
              }
         }
  
        base64[j++] = base64char[(int)current];
  
        current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
          
        if ( i + 2 >= binlength )
        {
            for(k=0;k<64;k++)
              {
                   unsigned h = k>>4;
                   h = k^h;
                    
                   if(h == current)
                   {
                       current = k;
                       break;
                   }
              }
              base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            break;
        }
        current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
         for(k=0;k<64;k++)
         {
              unsigned h = k>>4;
              h = k^h;
               
              if(h == current)
              {
                   current = k;
                   break;
              }
         }
        base64[j++] = base64char[(int)current];
  
        current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
         for(k=0;k<64;k++)
         {
              unsigned h = k>>4;
              h = k^h;
               
              if(h == current)
              {
                   current = k;
                   break;
              }
         }
        base64[j++] = base64char[(int)current];
    }
    base64[j] = '\0';
    return base64;
}
  
int base64_decode( const char * base64, unsigned char * bindata )
{
    int i, j;
    unsigned char k;
    unsigned char temp[4];
    for ( i = 0, j = 0; base64 != '\0' ; i += 4 )
    {
        memset( temp, 0xFF, sizeof(temp) );
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64 )
                temp[0]= k ^ (k>>4);
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+1] )
                temp[1]= k ^ (k>>4);
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+2] )
                temp[2]= k ^ (k>>4);
        }
        for ( k = 0 ; k < 64 ; k ++ )
        {
            if ( base64char[k] == base64[i+3] )
                temp[3]= k ^ (k>>4);
        }
  
        bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) |
                ((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
        if ( base64[i+2] == '=' )
            break;
  
        bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) |
                ((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
        if ( base64[i+3] == '=' )
            break;
  
        bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) |
                ((unsigned char)(temp[3]&0x3F));
    }
    return j;
}


此变形base64对RegCode解码后,每个byte都不能大于0x31,如下:
0.png
然后转化为16段,每段分别转化为一个大数(十进制),共16个大数,转化方式为:
X(1byte,不相关)
大小y(2byte)
数据(y字节)

  其中每段第一个字节不参与运算,可以任意,故同一个userName可以有很多RegCode。然后利用之前生成的D和N对每个大数进行RSA加密(幂模运算),取运算结果16进制的低2位作为一字节。
0.png
共16字节,前8byte作为a4,后8byte为a5。
(5)sub_403160该函数看起来简单,但因为在32位下实现64位计算,分析起来也难。
0.png

经过分析可以发现,前一个if比较(a6 * a1 + a2) * a6 + a3和a4*a6 + a5,后一个if比较(a2-a4)*(a2-a4)和4*a1*(a3-a5),均相等则注册成功。由于a1、a2、a3、a6和key有关,相当于已知量,a4、a5和code有关相当于未知量,则由
0.png
三、注册机编写
(1)从UserName得到a1、a2、a3、a6(编码实现),和大数E、N(直接从原程序CrackMe.exe中提取)。
(2)从a1、a2、a3、a6计算a4、a5。
(3)从a4、a5得到RSA加密运算结果(本文认为解密结果就是2位16进制,当然可以在满足低位不变得情况下,任意扩展加密结果,得到不同的RegCode)。
(4)将大整数N分解为P和Q(修改ppsiqs.exe程序进行)。
(5)从P、Q得到私钥D。
(6)用D和N对第(3)步的结果解密,得到16个不同的大数。
(7)对大数按如下规则编排,得到base64解码结果。
X(1byte,不相关)
大小y(2byte)
数据(y字节)

(8)进行base64编码,得到RegCode。

四、使用说明
(1)CrackMe.exe、ppsiqs2.exe、dbg.exe三个文件放在同一目录运行。
(2)CrackMe.exe(在偏移0x403c88手动加了断点,方便提取内存数据,且决定标准还是进阶的跳转改为只有进阶版)和ppsiqs2.exe(删除了循环,输入从控制台改为文件)均经过手动修改。
(3)dbg.exe是附件代码生成的的程序。
(4)使用时,运行dbg.exe,在跟随运行起来的CrackMe.exe的UserName处填上正确的userName,点击按钮“go”,在dbg.exe的控制台界面可以看到正确的RegCode(另外在当前目录下还会生成out.txt文件,内容即为RegCode)。
0.png
输入该RegCode,成功注册。
0.png

结束,代码见附件中main.cpp。该版本利用了调试器原理,没有完全编码。
后来把生成RSA参数的过程看了一下,得到另一版见附件2,其中有源码和可执行文件,分析过程免去,无太多价值。
附件(http://bbs.pediy.com/attach-download-128525.htm)

附件2.zip

26.05 KB, 下载次数: 17, 下载积分: 吾爱币 -1 CB

点评

给大佬倒一杯卡布奇诺  发表于 2017-8-9 08:35

免费评分

参与人数 7威望 +1 吾爱币 +14 热心值 +7 收起 理由
WYWZ + 1 用心讨论,共获提升!
枫恋蓝点 + 2 + 1 谢谢@Thanks!
Vision、猎龙 + 1 + 1 谢谢@Thanks!
老虎爱吃素 + 1 + 1 热心回复!
Sound + 1 + 8 + 1 已经处理,感谢您对吾爱破解论坛的支持!
1539173762 + 1 + 1 我很赞同!
shiaixiaomin + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2017-8-10 16:08
loudy 发表于 2017-8-10 15:30
谢谢,没经常发帖,不太会用

你可以在源代码格式下复制进去,再编辑好点,代码用论坛的代码框,也可以用markdown插件写。
 楼主| loudy 发表于 2017-8-10 22:35
Hmily 发表于 2017-8-10 16:08
你可以在源代码格式下复制进去,再编辑好点,代码用论坛的代码框,也可以用markdown插件写。

ok,感谢指点
WHO 发表于 2017-8-7 21:34
抢我所爱的你 发表于 2017-8-7 22:00
有没有跟我一样看不懂的
演员风 发表于 2017-8-7 22:47
+1 ...
海之帆 发表于 2017-8-7 22:58
我是水手,基本看不懂的
luckyangman 发表于 2017-8-8 01:22
没有看懂,够难呀。
dad9 发表于 2017-8-8 08:41
666666 终于有人放出来了,表示当时看了没做出来
孤妄啊 发表于 2017-8-8 08:54
感觉好难的样子,我还是算了
lyq1732 发表于 2017-8-8 10:14
厉害啊!666
946885280 发表于 2017-8-8 12:43
萌新默默的研究中....
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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