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

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 19831|回复: 61
收起左侧

[Android 原创] 2017腾讯游戏安全技术竞赛 Round 1 标准及进阶题解分析

  [复制链接]
Ericky 发表于 2017-12-1 20:37
PS:抱歉,很久没冒泡了。最近太忙了,发几篇之前参加比赛的笔记,分享一下。

前言

为了学习死磕的精神,我也来做题。我选择了APK的题目,题目有两种难度,分别对应标准级别和进阶级别,标准级别是100分,进阶级别是200分。我先看的标准级别,然后看的进阶级别,由于自己对一些算法的熟练程度不够,逆向东西也逆得慢,所以花了不少时间。再说一点题外话,这个进阶题目和之前360移动安全竞赛的APK cm比较相似,都是RSA算法,name和RSA中的e 、n、 d参数绑定,不同之处是360的那道题目的难点在于用了ollvm来进行混淆,增大分析难度,而这一道题目的难点是需要写一个完整的注册机,这个是两道题的不同之处,好了不扯了,下面开始正题。

0x1REG_CHECK函数
Java层比较简单,只是一个reg_check函数,传入输入的key和code,然后进行验证。这个函数是整个程序的核心,reg_check函数如下:
[C++] 纯文本查看 复制代码
int __fastcall CRegCheck::reg_check(int a1, int a2, _DWORD *a3, int a4)
{
  int v4; // r4@1
  char **v5; // r8@1
  int v6; // r6@1
  int v7; // r5@1
  int v8; // r0@2
  unsigned int v9; // r4@5
  int v10; // r4@5
  void *v11; // r4@6
  int v13; // [sp+20h] [bp-CD4h]@5
  void *ptr; // [sp+24h] [bp-CD0h]@2
  int v15; // [sp+28h] [bp-CCCh]@2
  int v16; // [sp+2Ch] [bp-CC8h]@2
  __int64 v17; // [sp+30h] [bp-CC4h]@2
  __int64 v18; // [sp+38h] [bp-CBCh]@7
  __int64 v19; // [sp+40h] [bp-CB4h]@7
  __int64 v20; // [sp+48h] [bp-CACh]@7
  __int64 v21; // [sp+58h] [bp-C9Ch]@5
  __int64 v22; // [sp+60h] [bp-C94h]@7
  int v23; // [sp+68h] [bp-C8Ch]@7
  int v24; // [sp+CE4h] [bp-10h]@1
 
  v4 = a4;
  v5 = (char **)a3;
  v6 = a2;
  v24 = _stack_chk_guard;
  v7 = 0;
  if ( CRegCheck::is_user_name_legal(_stack_chk_guard, (int *)a2) != 1 )
    goto LABEL_13;
  v8 = CRegCheck::get_formula_param(&v17, v6);
  ptr = 0;
  v15 = 0;
  v16 = 0;
  if ( v4 == 1 )
  {
    if ( !CBase64::base64_decode(*v5, (int)&ptr) )
      goto LABEL_8;
  }
  else
  {
    v9 = CRegCheck::get_rsa_key_seed(v8, v6);
    RSA::RSA((RSA *)&v21);
    RSA::RSAKey((RSA *)&v21, v9);
    sub_33440(&v13, (int *)v5);
    v10 = RSA::tdecrypto((int)&v21, (int)&v13, (int)&ptr);
    sub_332FC(v13 - 12);
    RSA::~RSA((RSA *)&v21);
    if ( !v10 )
    {
LABEL_8:
      v7 = 0;
      goto LABEL_9;
    }
  }
  v11 = ptr;
  if ( v15 - (_DWORD)ptr == 16 )
  {
    _aeabi_memclr(&v23, 0);
    _aeabi_memcpy(&v21, v11, 16);
    v7 = is_tangent(v20, v19, v18, v21, v22, v17);
LABEL_9:
    v11 = ptr;
    goto LABEL_11;
  }
  v7 = 0;
LABEL_11:
  if ( v11 )
    operator delete(v11);
LABEL_13:
  if ( _stack_chk_guard != v24 )
    _stack_chk_fail(_stack_chk_guard - v24);
  return v7;
}


0x2 is_user_name_legal函数

在check函数的开端,遇见的是is_user_name_legal函数,它的作用是校验了一下name的长度是否是39位,格式需要如下:
xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx (一共39位,暂且称之为8个段,在之后用segment1~segment8来表示)
x需要在 ‘0’-‘9’ 、 ‘a’-‘f’之间,否则key的合法性就不予通过。

0x3 get_formula_param函数

在get_formula_param函数中,根据输入的name 初始化了4个参数,每2个段生成一个参数,这些参数将在最后的is_tangent函数验证中用到。
具体参数代码如下:
[C++] 纯文本查看 复制代码
a1 = 0;
a1 += (segment1[0] * segment8[0]) << 16 ;
a1 += segment1[1] ^ segment8[1];
a1 += segment1[2] % (segment8[2]+1)+ 1;
a1 += segment1[3] / (segment8[3]+1);
 
a2 =0;
a2 += (segment2[0] ^ segment7[0]) << 16 ;
a2 += (segment2[1] % (segment7[1]+3));
a2 += (segment2[2] / (segment7[2]+1)) + 5;
a2 += (segment2[3] + (segment7[3])) ;
a3 = 0;
a3 += (segment3[0] / (3 + segment6[0]) )<< 16 ;
a3 += (segment3[1] * segment6[1]);
a3 +=  segment3[2] %(segment6[2] + 7) +5;
a3 +=  segment3[3] + segment6[3];
a6 = 0;
a6 += (segment4[0] + segment5[0]) << 16;
a6 *= segment4[1]/(segment5[1]+2);
a6 += segment4[2] %(segment5[2] +5) +7;
a6 += segment4[3] * segment5[3];

好了,这就是最后校验用到的4个参数。

0x4 分水岭 (标准OR进阶?)

检查完输入key的合法性和初始化4个重要的参数之后,这个时候判断模式,判断做题的人是选择的什么难度,如果是选择标准难度,将会对输入的code进行base64解码,再进行计算,判断是否成功。我打算重点说说进阶难度题目的事情,所以这里就给出一组正确的key,其他的过程需要读者自行跟踪一下。
我算出来的一组key:
key:0234-5678-90ab-00ef-0f87-6543-21fe-0cba
code:A6Kf2FVPOOvERSOoIf@5l2==
如果是选择的进阶的题目,将会进入到另外一番天地,先贴一段代码,如下:
[C++] 纯文本查看 复制代码
v9 = CRegCheck::get_rsa_key_seed(v8, v6);
    RSA::RSA((RSA *)&v21);
    RSA::RSAKey((RSA *)&v21, v9);
    sub_33440(&v13, (int *)v5);
    v10 = RSA::tdecrypto((int)&v21, (int)&v13, (int)&ptr);
    sub_332FC(v13 - 12);
    RSA::~RSA((RSA *)&v21);


从这里可以看出来,进阶版这里用的是RSA算法对输入的key和code进行验证。

0x5 get_rsa_key_seed函数

虽然这个名字叫做get_rsa_key_seed,但是其实里面的功能是对输入的key进行4次hash的计算,分别使用的是RSHash、JSHash、PJWHash、以及最后一个自定义的hash值计算的方法,具体的hash 函数代码在这里,请戳这里:
http://www.partow.net/programming/hashfunctions/index.html
对name 做了hash后,会将生成的hash值传入下一个函数,这个函数是RSAKey。

0x6 RSAKey函数

原函数代码如下:
[C++] 纯文本查看 复制代码
bool RSAKey()  
{  
    for(i=0;i<MAX;i++)  
        m[i]=p[i]=q[i]=n[i]=d[i]=e[i]=0;
    prime_random(p,q);
    mul(p,q,n);  
    mov(p,p1);  
    p1[0]--;        
    mov(q,q1);  
    q1[0]--;      /*/q-1;*/  
    mul(p1,q1,m);//m=(p-1)*(q-1)  
    erand(e,m);  
    rsad(e,m,d);  
    return true;  
}


看代码可以知道,这个RSAKey函数的主要作用是来生成n、e 、d的,逆向跟一下会发现,如果name不变,n、e、d也是不变的,也就是说name 和 n、e、d三个参数存在某种关系。what?因为我们知道n是p、q的乘积,而p与q这两个素数是用prime_random生成的,居然不变?点进去看看它prime_random函数的实现,发现set_rand_seed、gen_rand这两个函数,再点进去一看,都是类似这样的函数,贴其中的一个如下:
[C++] 纯文本查看 复制代码
void *__fastcall CGenRand::set_rand_seed(CGenRand *this, unsigned int a2)
{
  void *result; // r0@1
 
  __asm
  {
    VMOV            S0, R0
    VCVT.F64.U32    D0, S0
  }
  result = &CGenRand::seed_;
  __asm { VSTR            D0, [R0] }
  return result;

这是什么鬼,感觉好慌。这里卡了很久,被坑了一波。最后发现APK中有x86的lib,打开x86的so看了一下,里面有正确的c代码,是一个伪随机数,算法如下:
[C++] 纯文本查看 复制代码
hash_seed  = (double)(signed int)(0.00000095367431640625 * (hash_seed* 17.0 + 139.0)) * (-1048576.0)+ hash_seed * 17.0+ 139.0;
return hash_seed/10;


其中,hash_seed是一个全局变量,其实也就是get_rsa_key_seed函数的返回值,这就很明朗了,印证了固定的name对应固定e d n。同样的,在后面的erand函数也是用类似的方法生成了值。
都初始化好了之后开始了最后的解密函数tdecrypto。

0x7 tdecrypto函数

开头用了标准阶段的base64进行解密,然后所有解密的字符不能大于0x31,接着所有解密的字符会进入RSA解密阶段,充当大数的各个位,tdecrypto函数代码如下:
[C++] 纯文本查看 复制代码
string tdecrypto(int d[MAX], int n[MAX], string text)  
{  
    struct slink *h,*p1,*p2;  
    char ch;  
    int i,j,k,c,count,temp;  
    i=0;  
    j=3;  
    count=0;  
    h=p1=p2=(struct slink * )malloc(LEN);  
    int kk;  
    for (kk = 0; kk < text.length(); kk++)  
    {    
        ch = text.at(kk);  
        c=ch;        
        if(j==3)  
        {  
            p1->bignum[MAX-2]=c-48;  
            j--;  
        }  
        else if(j==2)  
        {  
            temp=c-48;  
            j--;  
        }  
        else if(j==1)  
        {  
            p1->bignum[MAX-1]=temp*10+c-48;  
            j--;  
        }  
        else if (j==0)  
        {  
            p1->bignum[i]=c-48;  
            i++;  
            if(i==p1->bignum[MAX-1])  
            {   
                i=0;  
                j=3;  
                count++;  
                if (count==1)  
                    h=p1;  
                else p2->next=p1;  
                p2=p1;  
                p1=(struct slink * )malloc(LEN);  
            }  
        }  
    }  
    p2->next=NULL;   
  
    p2=(struct slink * )malloc(LEN);  
    p1=h;  
    k=0;  
    string res;  
    if(h!=NULL)
        do  
        {  
            for(i=0;i<MAX;i++)  
                p2->bignum[i]=0;  
            expmod( p1->bignum , d ,n ,p2->bignum);            
            temp=p2->bignum[0] + p2->bignum[1]*10 + p2->bignum[2]*100;  
            if (( p2->bignum[MAX-2])=='0')  
            {  
                temp=0-temp;  
            } 
            ch=temp;/*  str[k]--->ch */  
printf("0x%02x ",(unsigned char)ch);
            res += ch;  // char a4_a5[16] ={0x9e,0xf6,0x1d,0xc2,0x8d,0x01,0x00,0x00,0xfb,0x49,0x50,0x09,0x3d,0xd5,0xee,0xff};
            k++;  
            p1=p1->next;  
            p2=(struct slink * )malloc(LEN);  
        }while (p1!=NULL);  
        return res;  
}


0x8最后一战is_tangent函数

然后解密的byte数组要为16位,这点和标准阶段的一样,接着进入最后的比较,is_tangent函数中,如果看到这个函数的名字去搜索一下,会发现可能这个函数和圆的相切,圆心距离有关系。然而这只是一个陷阱,你相信了你就输了,废话不多说,仔细看看代码,你发现了什么?
(a4-a2)^2 ? 4*params1*params2?
像不像delta?没错,这就是一个简单的一元二次方程组。
只要花点耐心,把2个方程整理一下,就会发现,a6其实就是方程中的x。
而题目的意思就是说这个方程有且只有一个根的话,你就是tangent的!
所以,直接上代码,a4直接算:
a4 = 2 * a1 * a6 + a2;
a5可能会溢出(这里被卡了,标记一波)用的是gmp大数库,可以看我的上篇文章:
[C++] 纯文本查看 复制代码
mpz_t mpz_a1, mpz_a2, mpz_a3, mpz_a4, mpz_a5, tmp, four, ten, ds;
    mpz_init_set_str( four, "4", 0x10 );
    mpz_init_set_str( ten, "10", 0x10 );
    mpz_init_set_str( ds, "1", 0x10 );
    mpz_init_set_str( mpz_a1, buf_a1, 0x10 );
    mpz_init_set_str( mpz_a2, buf_a2, 0x10 );
    mpz_init_set_str( mpz_a3, buf_a3, 0x10 );
    mpz_init_set_str( mpz_a4, buf_a4, 0x10 );
    mpz_init( mpz_a5 );
    mpz_init( tmp );
    mpz_sub( tmp, mpz_a4, mpz_a2 );
    mpz_mul( tmp, tmp, tmp );
    mpz_divexact( tmp, tmp, four );
    mpz_divexact( tmp, tmp, mpz_a1 );
    mpz_sub( mpz_a5, mpz_a3, tmp );

这样,整个过程就明晰了,然后就是编写注册机了,最后给出我算的几组解如下:
(去掉-):
key:
11111111111111111111111111111111
code:
04NPO44NP4iPWOYPP4gPO4iNWON#W4YP0ONPO4KOPiOPWOByO4NvWOgnP4gvO4NP0ONPP4KyP4GvO2NPPiGPO2GOPiiyP2YP04NOWOgoPi0OWOBPWO0NO4YYO20vWOY2OiOoW4NPW40oOiOPW4iWO2BPW4GYPQYOO4O^OOYO04OPOvOWO4KnP2B#PiBnO2OPO2YOP4YyO24vOQYWO44yO2OvPO4yO20yOiKYO4NoO4YYOQYWOOiWP4BYPOivO2KNPOiWPigNW4Gn0ONPP4GNO24WO2iOO2ByWOYvW4O#PO0P04NPPii#OOBnP2ivPOBYOOgnW4ByW40P0ONOPi4yPiinP2GyO40OP2OnWOYvO2G2OiOvWO4WO2YyPiKYOi4nOi0OPO0PPvOOO4Y=

key:
00000000000000000000000000000000
code:
0OYoOONWO2NPOOBvP4GPPiYOOO4POZOWOOKvO24nP4GPO4gnPOOPOiONP2YY04NOPOYWP4gvP2KOW40vW4KoP4OWP202OiOvWOOoO2GnW4OnW4OnP2YOPiKvWQOWOO4oPi4PPO4NWOKvPOKyOOgPWOKN04OPOvYOO4O^OOYO0ONPPiB#OOgYPiKnO2YPO4YnOOgYPiGP04NOW4OPPO0NW44oO4ONOiiYP2iYP4i2OiOPPOGvP24vOO4PO24oOON#PiNnPQOWOOGPOiGyWOKvO2NWO2K#P4gOP2g#0ONOPiBWW4OvOOiNWO0yO40WOiB#O4i^OiOYO4NnP20#PiOoO20oPiBnOON#OCOWOO4POiG#O2gyOOBvO2ByW4GOOigv0OOPO3==

key:
48671974307316312876431870418788
code:
04NPP24YOONoO4gvPig#P2BWP4YNO40v0ONPP4N#WOYPP4BoWOioOiYWO4GNO4gv04NPOiBoOi0YPOiNWOiNP4NPOOgvP4gv04NPO4ByPi4OO4OOWOYnP20yP24YOiiW0ONPP4OPWOgPPiNyWOGYWO4oP4KYO4GP04OPOvYOO4O^OOYO04NPO4GnW4OWP20YPOGYPOgyPiiOP4OP04NPPO0YPOY#OOKyP44#WONWWOBnO2iP04NPOiGWPOBYW40OP4GWWO0WOiGPW4iW0ONPP4gWOOivW44yWOY#W4YWOO4yO4gP0ONPWOg#OOYoO2KNPOBoP2BoOOK#O44P04NPP4NYP2YOOiYvO4OOOiB#OiYYWO4W0ONPOiYWO4g#PiOOO2BvO4NyOiKWP2KW0OOPO3==
谢谢观看,如有错误之处请大侠们斧正!

By Ericky
2017.7.8

免费评分

参与人数 44吾爱币 +46 热心值 +44 收起 理由
隔壁家刘大爷 + 1 + 1 我很赞同!
y1u2i + 1 + 1 谢谢@Thanks!
umauwu + 1 + 1 我很赞同!
暗舞呀 + 1 + 1 热心回复!
醒醒吧 + 1 + 1 我很赞同!
oncitybb + 1 + 1 我很赞同!
人生海海 + 1 + 1 我很赞同!
181842 + 2 + 1 你负责发牛逼的帖,哥负责加牛逼的分!
amtian + 1 + 1 我很赞同!
爱学习的小仙女 + 2 + 1 我很赞同!
Chinamkn + 1 + 1 已答复!
SomnusXZY + 1 + 1 热心回复!
siuhoapdou + 1 + 1 谢谢@Thanks!
maoxuechuen + 1 + 1 谢谢@Thanks!
Prider + 1 + 1 谢谢@Thanks!
52破解☆ + 1 + 1 我很赞同!
行走的麦克 + 1 + 1 我上号就是为了赞你~~~膜拜
很快再相见123 + 1 + 1 我很赞同!
只虾米一个人 + 1 + 1 谢谢@Thanks!
aipanpann + 1 + 1 已答复!
mincai + 1 + 1 谢谢@Thanks!
qilelejia + 1 + 1 谢谢@Thanks!
土川鼠 + 1 + 1 谢谢@Thanks!
no_one + 1 + 1 谢谢@Thanks!
w20064360 + 1 热心回复!
无痕软件 + 1 + 1 thanks
sunnylds7 + 1 + 1 谢谢@Thanks!
LASTandGAME + 1 + 1 我很赞同!
d_apple + 1 + 1 谢谢@Thanks!
sotyoyo + 1 + 1 鼓励转贴优秀软件安全工具和文档!
cbn6998 + 1 + 1 很牛逼,我什么都看不懂
糖0穿 + 1 + 1 谢谢@Thanks!
Allyn0303 + 1 + 1 没基础看不懂
阿豪0719 + 1 + 1 厉害了 虽然现在还看不懂 ~
ayoma + 1 + 1 用心讨论,共获提升!
圣华炼 + 1 + 1 我很赞同!
小巷清幽 + 1 + 1 我很赞同!
zhiyi1120 + 1 + 1 谢谢@Thanks!
www.52pojie.cn + 2 + 1 你负责发牛逼的帖,哥负责加牛逼的分!
都同学 + 1 + 1 谢谢@Thanks!
a952779523 + 1 + 1 6
无影寒冬 + 1 + 1 谢谢@Thanks!
落尘之木 + 1 + 1 热心回复!
鞠宝宝萌萌哒~ + 1 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

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

鱼塘小猎人 发表于 2017-12-14 00:09 来自手机
路过五次提笔写作业
甜筒先生 发表于 2017-12-1 21:58
很酷的一个寒 发表于 2017-12-1 20:42
惟爱你灬love 发表于 2017-12-1 20:52
膜拜大牛
落魄狂人 发表于 2017-12-1 21:01
仰望下 离大师还远 慢慢学习
cnmb123666 发表于 2017-12-1 21:10
就冲我看不懂 顶一个
q5627 发表于 2017-12-1 22:10
看着这么一大堆就头疼
RZCL9585 发表于 2017-12-1 22:19
看的我头疼
shian1988 发表于 2017-12-2 10:10
谢谢楼主分享经验,很不错,进来学习一下。。
宋仁投 发表于 2017-12-2 10:21
膜拜大佬666

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
295ec + 1 + 1 已答复!

查看全部评分

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

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

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

GMT+8, 2024-4-20 07:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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