吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 18481|回复: 63
收起左侧

[原创] Gslab游戏安全竞赛社会组WP(第二轮第一题)

  [复制链接]
loudy 发表于 2017-8-7 21:24
本帖最后由 loudy 于 2017-10-2 09:26 编辑

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

链接:

Gslab游戏安全竞赛社会组WP(第一轮进阶版)https://www.52pojie.cn/thread-633178-1-1.html
Gslab游戏安全竞赛社会组WP(第二轮第二题)https://www.52pojie.cn/thread-633181-1-1.html

一、整体轮廓

IDA载入,发现关键位置函数sub_401694(),如下,其中的注释已经很清楚。重点就是sub_4011D4、sub_4014B8和sub_401634三个函数。
1.png

2.png

其中sub_4011D4处理通过调用zapus_get得到的16字节数据。结果与如下16字节数据依次比较。
[C] 纯文本查看 复制代码
unsigned char fii[16] = {'G','S','L','a','b','1','7'};//对比字符串
unsigned int xy = GetCurrentProcessId();
unsigned int * fi1 = (unsigned int *)fii;
fi1[3] = xy;

sub_4014B8和sub_401634都是对zapus_dll.dll文件求hash,结果都与0x614C5347比较。

二、重点函数sub_4011D4的逆向
IDA进入该函数,
3.png

转化为C代码如下,其中fcode是固定值,具体见代码,inbuf是测试数据,v5是处理结果。
[C] 纯文本查看 复制代码
int getTheKey1(){
       int i,j,k;
       unsigned char inbuf[] = "0123456789abcdef";
       DWORD v8 = 0x1000193;
       DWORD v7 = 0x811C9DC5;
       for (i = 0; i < 0x800; ++i ){
              v7 *= v8;
              fcode ^= v7;
              v7 ^= fcode;
       }
       char v5[16] = {0}; 
       for (j = 0; j < 0x80; ++j ){
              unsigned char v3 = 0;
              for (k = 0; k < 0x80; ++k )   
                     v3 = ( ((signed int)fcode[16*j+k/8] >> k % 8) & ((signed int)inbuf[k/8]>> (7 - k % 8)) ^ v3) & 1;
              v5[j / 8] |= v3 << (7 - j % 8); 
       }
       int ret = 0;
       return ret;

分析易知,在已知处理结果的情况下求输入,实际上就是解异或方程组,代码如下。
[C] 纯文本查看 复制代码
unsigned char ut1[0x80][0x81]={0};//fcode2bit(j,k)
unsigned char ut2[0x80]={0};//inbuf2bit
unsigned char inb[0x10]={0};
  
//高斯消元法解异或方程
void Gauss ()
{
              int i,j,k;
              for (k=0;k<0x80;k++)
              {
                     //i=k;
                     for (i=k;i<0x80;i++)//对于k=0..N-1,找到一个M[k]不为0的行i 
                     {
                            if (ut1[k]==1) break;
                     }
  
                            for (j=0;j<=0x80;j++)  //把找到的第i行与第k行交换
                            {
                                   unsigned char tmp = ut1[k][j];
                                   ut1[k][j] = ut1[j];
                                   ut1[j] = tmp;
                            }
                             
                            for (i=0;i<0x80;i++)
                            {
                                   if (i!=k && ut1[k])
                                   {
                                          for (j=0;j<=0x80;j++)   // <=
                                                 ut1[j]=ut1[k][j]^ut1[j];
                                   }
                            }
              }
              for (i=0;i<0x80;i++)
              {
                     ut2=ut1[0x80];
                     inb[i/8] |= ut2<<(7-i%8);
              }
}
  
  
__declspec(dllexport) int zapus_get(char * c)
{
       int i,j,k;
  
       DWORD v8 = 0x1000193;//FNVHash常量
       DWORD v7 = 0x811C9DC5;
  
       unsigned char fii[16] = {'G','S','L','a','b','1','7'};//对比字符串
       unsigned int xy = GetCurrentProcessId();
       unsigned int * fi1 = (unsigned int *)fii;
       fi1[3] = xy;
  
       for (i = 0; i < 0x800; ++i )
       {
              v7 *= v8;
              fcode ^= v7;
              v7 ^= fcode;
       }
  
       for (j = 0; j < 0x80; ++j )//常量,转化为异或方程组的系数矩阵
       {
              for (k = 0; k < 0x80; ++k )   
              {
                     ut1[j][k] = (fcode[16*j+k/8]>>k%8) & 1;
              }
       }
  
       for(i=0;i<0x80;i++) //对比字符串转化为异或方程组的结果矩阵
       {
              ut1[0x80] = (fii[i/8]>> (7 - i % 8))&1;
              //            printf("%x",ut3);
       }
  
       Gauss();//高斯消元法解方程
  
       /*
       //此题如果不要求算法分析,则可在此处算好结果后,直接传回主程序32字节,直接传全0都可满足要求
       for (int j = 0; j < 0x80; ++j )
       {
              unsigned char v3 = 0;
              for (int k = 0; k < 0x80; ++k )      
                     v3 = ( ((signed int)fcode[16*j+k/8] >> k % 8) & ((signed int)inbuf[k/8]>> (7 - k % 8)) ^ v3) & 1;
              //v3 = (((signed int)(unsigned __int8)*(&fcode[16 * j] + k / 8) >> k % 8) & ((signed int)inbuf[k/8]>> (7 - k % 8)) ^ v3) & 1;
              inbuf[16+j / 8] |= v3 << (7 - j % 8); 
       }
       memcpy(c,inbuf,32);
       */
       memcpy(c,inb,16);//将解方程结果传回主程序。
       return 16;

然而,由于zapus_get返回值没有限制长度,且返回值紧贴预设的比较字符串,导致可以直接覆盖预设字符串达到目的,进而跳过此步验证。由于编写的zapus_get和题目预设的返回方式不一样,预设为“retn 4”返回(可能为delphi或者Borland C++),而VC++默认为“retn”返回,导致堆栈不平衡,出现错误。
4l.png

手动修改程序的返回方式为“retn 4”如下,即可完美实现该函数。
5.png

三、重点函数sub_4014B8的逆向
该函数实际就是crc32,核心如下,下图生成CRC32使用的表。
6.png

下图处理输入
7.png

因为表固定,不做重点分析,输入处理过程可化简为:
[C] 纯文本查看 复制代码
//CRC32编码
int getTheKey2(unsigned char * buf,int bufsize)
{
       DWORD ret = -1;
       DWORD * bb = (DWORD*)aa;
       for(int i=0;i<bufsize;i++)
       {
              int xt = (ret&0xff)^buf;
              ret = bb[1+xt]^(ret>>8);
       }
        
       return ~ret;
}

容易发现通过控制最后的4个字节,即可完全控制最终生成的CRC32结果,那么我们可以通过在文件后加4个字节来达到控制CRC32结果为0x614C5347的目的,求此4个字节的代码如下。
[C] 纯文本查看 复制代码
unsigned char buf[4]={0};
int get2(DWORD a)
{
       DWORD confirm1 = 0x9e; //0x9eb3acb8 == ~0x614C5347
       DWORD confirm2 = 0xb3;
       DWORD confirm3 = 0xac;
       DWORD confirm4 = 0xb8;
       DWORD tmp,x[4]={0};
       int i,y[4]={0};
       DWORD * bb = (DWORD*)aa;
       for(i=1;i<=0x100;i++){
              tmp = bb>>24;
              if(tmp==confirm1){
                     x[0] = bb;
                     y[0] = i;
                     break;
              }
       }
       tmp = x[0]>>16;
       tmp = tmp&0xff;
       confirm2 = confirm2^tmp;
       for(i=1;i<=0x100;i++){
              tmp = bb>>24;
              if(tmp==confirm2){
                     x[1] = bb;
                     y[1] = i;
                     break;
              }
       }
       tmp = x[0]>>8;
       tmp = tmp&0xff;
       confirm3 = confirm3^tmp;
       tmp = x[1]>>16;
       tmp = tmp&0xff;
       confirm3 = confirm3^tmp;
       for(i=1;i<=0x100;i++){
              tmp = bb>>24;
              if(tmp==confirm3){
                     x[2] = bb;
                     y[2] = i;
                     break;
              }
       }
  
       tmp = x[0];
       tmp = tmp&0xff;
       confirm4 = confirm4^tmp;
       tmp = x[1]>>8;
       tmp = tmp&0xff;
       confirm4 = confirm4^tmp;
       tmp = x[2]>>16;
       tmp = tmp&0xff;
       confirm4 = confirm4^tmp;
       for(i=1;i<=0x100;i++)
       {
              tmp = bb>>24;
              if(tmp==confirm4){
                     x[3] = bb;
                     y[3] = i;
                     break;
              }
       }
       DWORD ret = a;//0x32f38783;
       for(i=3;i>=0;i--){
              buf[3-i] = ((ret&0xff)^y-1);
              ret = x^(ret>>8);
       }
       return 0;
}

通过此函数还可以求出,在CRC32结果为0x614C5347的情况下,在文件后附加上整数倍的{0x5C, 0xA4, 0x88, 0xC9}CRC32结果保持不变。

四、重点函数sub_401634的逆向
该函数最简单,但逆向最麻烦,IDA中如下
8.png

化简为:
[C] 纯文本查看 复制代码
//FNV-1a Hash运算
DWORD getTheKey3(unsigned char * buf,int bufsize){
    DWORD ret = 0x811C9DC5;
       for(int i=0;i<bufsize;i++){
              DWORD xx = (DWORD)buf;
              ret = 0x1000193 * (ret^xx);
       }
       return ret;
}

此函数没有找到更好的破解办法,考虑枚举。枚举过程如下:
(1)将文件最后一个字节作为变量(0x00-0xff)。
(2)对每一个字节值,在文件后增加4字节使CRC32结果为0x614C5347。
(3)依次添加{0x5C, 0xA4, 0x88, 0xC9},保持CRC32不变,求FNv-1a的值,如果等于0x614C5347或者出现循环,则结束循环,转到第一步。
(4)代码如下:
[C] 纯文本查看 复制代码
int get3(DWORD a){
       unsigned char dd[4] = {0x5C, 0xA4 ,0x88 ,0xC9};
       DWORD ret = a;
       int i,j;
       for(i=0;;i++)//614C5347<-A19947FD<-CE19CA2F<-92F5E675<-F4659CD7<-0D33122D<-F32BF53F<-66263925<-7BDE6D67<-127F995D<-CDAA8F4F<-8379C0D5
       {
              for(j=0;j<4;j++)
              {
                     DWORD xx = (DWORD)dd[j];
                     ret = 0x1000193 * (ret^xx);//359C449B(1000193^-1)
              }
              if(ret ==0x614C5347  || ret == a)//0x614C5347
              {
                     break;
              }
               
       }
       if(ret ==0x614C5347)
       {
              return i;
       }
       else
       {
              return -1;
       }
}
  
       for(unsigned char i=0;i<0xff;i++)
       {
              bbuf[xs-1]=i;
        
              DWORD yy1 = getTheKey2(bbuf,xs);
              get2(yy1);
  
              bbuf[xs]= buf[0];
              bbuf[xs+1]= buf[1];
              bbuf[xs+2]= buf[2];
              bbuf[xs+3]= buf[3];
  
              //DWORD yy1 = sub_1244(bbuf, xs);
               
              DWORD yy2 = getTheKey3(bbuf,xs+4);
  
              int udd = get3(yy2);
              if(udd!=-1)
              {
                     printf("%02X %08X\n",i,udd);
              }
       }

对此DLL文件(和之前生成的DLL文件有关)得到结果如下:
9.png

当i=0x20时,需要加入的{0x5C, 0xA4, 0x88, 0xC9}串长度为0x2E3BCA2(已经足够小),即替换生成的dll文件的最后一个字节为0x20,然后编码如下:
[C] 纯文本查看 复制代码
   bbuf[xs-1]=0x20;
       DWORD yy1 = getTheKey2(bbuf,xs);
       get2(~yy1);
       bbuf[xs]= buf[0];
       bbuf[xs+1]= buf[1];
       bbuf[xs+2]= buf[2];
       bbuf[xs+3]= buf[3];      
       DWORD yy2 = getTheKey3(bbuf,xs+4);
       int udd = get3(yy2);
       unsigned char * memm = (unsigned char * )malloc(udd*4+8+xs);
       memcpy(memm,bbuf,xs+4);
       for(int i=0;i<=udd;i++)
       {
              memm[xs+4+i*4+0] = 0x5C;
              memm[xs+4+i*4+1] = 0xA4;
              memm[xs+4+i*4+2] = 0x88;
              memm[xs+4+i*4+3] = 0xC9;
       }
       fp = fopen("zapus_dll1.dll","wb");
       fwrite(memm,udd*4+8+xs,1,fp);
       fclose(fp);

生成新dll文件zapus_dll1.dll,将此文件重命名为zapus_dll.dll,放在zapus.exe同目录下,得到结果如下:
10.png

全文完。

src.zip

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

点评

这原谅绿真刺眼啊  发表于 2017-8-8 20:53
这code背景屎绿屎绿的,啊我的眼睛  发表于 2017-8-8 11:15

免费评分

参与人数 17吾爱币 +19 热心值 +16 收起 理由
chkds + 1 + 1 强!!学习
Nafoul + 1 + 1 我很赞同!
xyuetao + 1 + 1 用心讨论,共获提升!
52破解☆ + 1 + 1 我很赞同!
alienhh + 1 + 1 谢谢@Thanks!
soyiC + 2 + 1 用心讨论,共获提升!
adq_cq + 1 + 1 谢谢@Thanks!
lapop + 1 + 1 我很赞同!
这是追求不是梦 + 1 + 1 热心回复!
qwe159 + 1 谢谢@Thanks!
2864095098 + 1 + 1 热心回复!
qwerttqqaz + 1 + 1 我很赞同!
SomnusXZY + 1 + 1 用心讨论,共获提升!
凌乱的思绪 + 1 背景色太0.0
WYWZ + 2 + 1 谢谢@Thanks!
老虎爱吃素 + 2 + 1 热心回复!
a5606495 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

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

 楼主| loudy 发表于 2017-12-5 11:37
chkds 发表于 2017-12-4 22:56
fcode是0x40f0d0那里的数据吗?求解。。。sub_4044EC里做了什么操作?动调看到寄存器那边有一些大数{:1_908 ...

这里面没有大数吧,你对照IDA和OD调试一下,很容易看到的,我就不说了
 楼主| loudy 发表于 2017-10-2 16:49
感谢大家的支持!
最近新开了一个微信公众号:风云处(fyc1687),欢迎大家多多交流。其中我会逐步把我学习逆向的经验、实例、技巧分享出来。
zbnysjwsnd8 发表于 2017-8-7 21:43
 楼主| loudy 发表于 2017-8-7 21:57
zbnysjwsnd8 发表于 2017-8-7 21:43
你看看你的帖子
图片都GG了

感谢提醒
Seven_2017 发表于 2017-8-7 22:15
我的天,,好难好难  学习学习
czyuyu 发表于 2017-8-7 22:39 来自手机
学习了,支持下!
寒山远 发表于 2017-8-8 08:46
来学习一波
Tath 发表于 2017-8-8 11:33 来自手机
顶一下,
15589488520 发表于 2017-8-8 12:21
学习了,就是这颜色   太..........
xgh1128205 发表于 2017-8-9 08:47
好东西,学习一下!!!!!!!!!!!!!!!!
Hmily 发表于 2017-8-9 16:19
@loudy 文字那绿背景太恶心你了,忍不住给你编辑了,你这是从word中复制过来的?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

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

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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