吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17363|回复: 32
收起左侧

[2018] 2018腾讯游戏安全竞赛第一轮PC标准版分析

  [复制链接]
额度更深刻 发表于 2018-4-27 16:30
本帖最后由 额度更深刻 于 2018-4-27 16:52 编辑

大三狗,水平有限,感觉要止步第二轮了。
第一题逆向没什么难度,主要是考察对算法的还原,最重要的是耐心
题目:

输入正确的UserName和RegCode,显示注册成功。

OD通过对GetDlgItem下断,找到按钮相应事件004026F0IDA分析
一.分析4026F0函数发现只要sub_405510函数返回值为1,就显示注册成功,那么显然sub_405510函数是关键
在上面我还发现了这样一行代码(传图麻烦,蓝色的文字就是IDA显示的伪代码)
v20 = SendMessageW(*((HWND *)v4 + 8), 0xF0u, 0, 0) == 1;// /v20为1 说明你选的标准版 为0选的是进阶版
用来标识标准版还是进阶版,暂且不管
二.分析405510函数主要如下:



405510函数进去首先对用户名做一个判断。起名为checkuser
if ( checkuser((int *)&str_name) )
1.分析checkuser函数
用OD发现这个函数在压参数入栈时,压的是我们输入的用户名,所以认为他是对用户名是否输入合法做一个判断
if ( a1[4] != 39 )                            // a1[4]是我们输入的用户名长度  
  return 0;首先是进行长度判断,长度不满足0x27,直接返回false

if ( v7 )                                    
{   
v9 = v7;                                                                                    // 每执行一次加一 执行39次                                 
                                                            // 通过toupper函数 把小写字符c转化为大写字母   
do    {      
           *((_BYTE *)v23 + v8) = toupper(*((char *)v6 + v8));     
           ++v8;   
         }   
          while ( v8 != v9 );   
            v1 = a1;
}
其次通过toupper函数将输入用户名中的小写字母转换成大写字母

sub_402A70(&v16, &unk_5AC430, 1u);            // &unk_5AC430是#
sub_404D70((int)v24, (int)v1, *(LPVOID *)&v16, v17, v18, v19, v20, v21);  
v10 = (_DWORD *)v24[0];
if ( (unsigned int)(v24[1] - v24[0] - 192) >= 0x18 )   
goto LABEL_30;// LABEL_30将会导致return 0
之后要求输入的用户名必须是XXXX#XXXX#XXXX#XXXX#XXXX#XXXX#XXXX#XXXX形式,X的范围必须在0~9,a~f或A~F之间对用户名合法性检查完毕,若正确,返回1
继续执行get5m((int)&str_name, &v26, &v27, &v28, (int *)&v29, &v30);// 根据用户名 获取五个64位整数 存在12EEA8~
发现这个函数根据输入的用户名得到了五个变量m1,m2,m3,m4,m5,之后会用到2.get5m分析没什么说的,用OD耐心的一步一步调试,发现他是把用户名分为了八段Seg1~Seg8        m1.QuadPart = 0;      
  m1.QuadPart += ((seg1[0] * seg2[0]) << 0x10);      
  m1.QuadPart += (seg3[1] ^ seg1[1]);      
  m1.QuadPart += (seg1[2] % (seg4[2] + 1)) + 1;      
  m1.QuadPart += (seg1[3] / (seg5[3]+1));             

  m2.QuadPart = 0;
      
  m2.QuadPart += ((seg2[0] ^ seg6[0]) << 0x10);   
  m2.QuadPart += (seg2[1] % (seg7[1] + 3));   
  m2.QuadPart += (seg2[2] / (seg8[2] + 1)) + 5;   
  m2.QuadPart += seg1[3] + seg2[3];        

  m3.QuadPart = 0;     
  m3.QuadPart += ((seg3[0] / (seg2[0] + 3)) << 0x10);   
  m3.QuadPart = m3.QuadPart ^ (seg3[1] % seg4[1]);     
  m3.QuadPart += 0xc + seg6[2] + seg3[2];   
  m3.QuadPart += seg8[3] + seg3[3];

  m4.QuadPart = 0;     
  m4.QuadPart = m4.QuadPart + (seg3[3] ^ seg1[1]);      
  m4.QuadPart = m4.QuadPart*(seg2[3] + seg4[1]);   
  m4.QuadPart = m4.QuadPart&(seg6[2] & seg5[2]);     
  m4.QuadPart = (seg8[3] * m4.QuadPart + m2.QuadPart)*seg7[0] * m1.QuadPart;   
  m4.QuadPart = m4.QuadPart - ((m4.QuadPart - m2.QuadPart) % (2 * m1.QuadPart));

  m5.QuadPart = 0;     
  m5.QuadPart += (seg4[0] ^ seg5[0]) << 0x10;     
  m5.QuadPart = m5.QuadPart*(seg4[1] % (seg5[1] + 2));   
  m5.QuadPart += 7 + (seg4[2] % (seg5[2] + 5));   
  m5.QuadPart += seg5[3] * seg4[3];
在计算m4的时候我以为要用到高32位和低32位,所以用到了结构体。后来发现不需要,定义M变量的时候直接用INT64定义就行了


3.对if(...)的分析
if ( sub_406080(v14)
&& ((v16 = HIDWORD(v31), v17 = (__m128i *)v31, a13)  || (v33 = xmmword_5AC470, sub_403010(HIDWORD(v31) - v31, v31, (char *)&v33, v15, v31)))
&& v16 - (_DWORD)v17 == 32
&& v17[1].m128i_i32[2] ==842019128
&& !v17[1].m128i_i32[3] )  // 406080函数就是对我们输入的key进行转换
这个判断看起来很复杂,我们先看Sub_406080(伪代码有点长,就不贴了)406080函数是对我们输入的RegCode进行转换得到三个INT64变量结合OD发现首先对我们输入的RegCode,与一个包含65个数的表做对比,我们输入的字符必须在这个表里存在表为:ZO6Kq79L&CPWvNopzQfghDRSG@di*kAB8rsFewxlm+/u5a^2YtTJUVEn0$HI34y#=所以我猜想这肯定跟BASE64有关
果然,之后406080函数把我们输入的RegCode 四个一组 转成三个
char ch1 = a ^ (a >> 3);                                   
char ch2 = b ^ (b >> 3);                  
char ch3 = c ^ (c >> 3);                                   
char ch4 = d ^ (d >> 3);                                     
char ret1 = 4 * ch1 | (ch2 >> 4) & 3;                                   
char ret2 = 16 * ch2 | (ch3 >> 2) & 0xF;         
char ret3 = (ch3 << 6) | ch4 & 0x3F;
将得到的
ret1,ret2,ret3存放到一个地方,后来发现后面的判断需要用到其中的一部分(其实前32位RegCode会最后组合成三个INT64的数a6,a7,a8)
也就是说必须内存中从19B748开始满足38 31 30 32 00 00 00 00如下才能过两个判断
而我选中的部分将作为参数压栈,到最终的check函数中去

三.分析最终的402F20check函数sub_402F20( v26, v27, v28,  v29, v30,  __PAIR__(_mm_cvtsi128_si32(_mm_srli_si128(v18, 4)), _mm_cvtsi128_si32(v18)),              __PAIR__(_mm_cvtsi128_si32(_mm_srli_si128(v18, 12)), _mm_cvtsi128_si32(_mm_srli_si128(v18, 8))),  *((__int64 *)&v33 + 1)) // 关键的check函数 返回1 说明我们输入的key与通过name计算的key相同

通过OD发现他将Get5m中得到的五个INT64变量 m1,m2,m3,m4,m5与406080函数得到的三个INT64变量a6,a7,a8做了这样的运算
bool __cdecl sub_402F20(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7, __int64 a8)
{
return a3 + (a2 + a1 * a6) * a6 == a7      && (a2 - a4) * (a2 - a4) == 4 * (a4 * a6 - (a2 + a1 * a6) * a6) * a1      && a3 + (a2 + a1 * a5 - a4) * a5 == a8;
}
将return后的语句化简得(其实第二句是一个一元二次方程)
a6 = (m4 - m2) / (2 * m1);      
a7 = m3 + (m2 + m1 * a6) * a6;      
a8 = m3 + (m2 + m1 * m5 - m4) * m5;
这是我们编写注册机的关键。先通过输入的合法用户名,得到五个变量m1,m2,m3,m4,m5再根据五个变量得到a6,a7,a8,具体算法在源代码里根据a6,a7,a8得到我们对应的RegCode(后面12位是固定的,有三种)有什么不妥之处,还望各位大牛指正

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 13威望 +1 吾爱币 +21 热心值 +13 收起 理由
胡一刀 + 1 + 1 我很赞同!
MakeDave + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
鱼无论次 + 1 + 1 我很赞同!
GNUBD + 1 + 1 谢谢@Thanks!
xuxudabai + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
evo1991 + 1 + 1 用心讨论,共获提升!
20151811337s + 1 + 1 我很赞同!
Hmily + 1 + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
liphily + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
HarunaPr + 1 + 1 我很赞同!
lookerJ + 1 热心回复!
挥汗如雨 + 2 + 1 真有耐心,当初分析安卓底层的时候看到这玩意儿就头大,做不下去
hi566 + 1 我很赞同!

查看全部评分

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

鱼无论次 发表于 2018-12-17 10:12
本帖最后由 鱼无论次 于 2018-12-17 10:24 编辑

兄弟你的a6 = (m4 - m2) / (2 * m1); 怎么计算出来的?能请教下吗?
这几句我没办法简化成a6 = (m4 - m2) / (2 * m1);,是不是我漏了什么吗?
bool __cdecl sub_402F20(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7, __int64 a8)
{
return a3 + (a2 + a1 * a6) * a6 == a7      && (a2 - a4) * (a2 - a4) == 4 * (a4 * a6 - (a2 + a1 * a6) * a6) * a1      && a3 + (a2 + a1 * a5 - a4) * a5 == a8;
}
wflb826 发表于 2019-1-19 23:36
a6 = (m4 - m2) / (2 * m1);      
a7 = m3 + (m2 + m1 * a6) * a6;      
a8 = m3 + (m2 + m1 * m5 - m4) * m5;

这个还是很经典的!
Ericky 发表于 2018-4-28 11:46
ssyy91 发表于 2018-4-28 12:13
啥也看不懂
 楼主| 额度更深刻 发表于 2018-4-28 12:26
Ericky 发表于 2018-4-28 11:46
和去年的题目好像额。。

看了一下去年的,发现逻辑上简直一模一样……
cz864259120 发表于 2018-4-28 14:11
厉害了我的哥
陌上寒烟薄雪 发表于 2018-4-28 23:04
大二的表示啥也看不懂
qiutianqin 发表于 2018-4-29 11:25

厉害了我的哥
南栀倾寒ysd 发表于 2018-5-4 14:42 来自手机
后悔没有好好学编程
zye 发表于 2018-5-4 21:10
逆向咋学啊,是要学汇编吗
 楼主| 额度更深刻 发表于 2018-5-4 21:13
zye 发表于 2018-5-4 21:10
逆向咋学啊,是要学汇编吗

汇编要学,看王爽的《汇编语言》,写的非常好
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-19 22:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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