2018腾讯游戏安全技术竞赛Android 组资格赛 Round1

“工欲善其事,必先利其器”

——《论语·魏灵公》

        最近参加了腾讯游戏安全技术竞赛,话说这个比赛我已经参加了3年了,第一年,什么都没看懂;第二年,题目做出来一半;第三年,我花了5天做完了最简单的题目。自己虽然自信满满,觉得可以做到高级赛题,可是比赛的时候并不顺利,遇到了层层阻隔。最终可能是由于提交时间过晚,没有晋级决赛吧。我想把这些写下来和大家分享,或者说,提醒自己更合适。

一、赛题解析

        那么,我们首先来说说这次的题目吧。刚拿到赛题的时候,我觉得似曾相识,仔细看下去,发现和去年的题目大同小异。

拿到Apk之后,首先拖到jadx,如我所料,java代码里面没有什么有价值的内容,主要还是native代码。将libnative-lib.so拖入IDA,定位到检查key和code的函数:Java_com_qq_gslab_regme_MainActivity_checkReg,代码将接受到的key和code传入sub_51B0进行检查,主要的检查逻辑也在此处。

0x01 key的合法性?

首先遇到的是sub_4864,跟进去读IDA生成的伪代码发现是检查字符串长度,key的长度应该为39。

之后又对字符串进行了一系列操作。


         因此我们得到了完整的key格式约束:长度应该为39,每四个字符以“#”分隔,字符范围应该在“0123456789ABCDEF”,不区分大小写,但是计算code时使用大写字母进行计算。为了方便叙述,将key以#分隔,分为8个段,以segment0~segment7来表示。即XXXX#XXXX#XXXX#XXXX#XXXX#XXXX#XXXX#XXXX。

0x02 key的5个校验值

在key的合法性验证通过之后,继续使用key生成5个校验数,这个校验数在之后会用到。这5个校验数计算方式如下:


I1 = 0L;
I1 += (segment0[0] * segment1[0]) <<16;
I1 += segment0[1] ^ segment2[1];
I1 += (segment0[2] % (segment3[2] + 1)) +1;
I1 += segment0[3] / (segment4[3] +1);

I2 = 0L;
I2 += (segment1[0] ^ segment5[0])<<16;
I2 += (segment1[1] % (segment6[1] + 3));
I2 += (segment1[2] / (segment7[2] + 1)) +5;
I2 += segment1[3] + segment0[3];

I3 = 0L;
I3 += (segment2[0] / (segment1[0] +3)) <<16;
I3 = (segment2[1] % segment3[1]) ^ I3;
I3 += segment2[2] + segment5[2] + 12;
I3 += segment2[3] + segment7[3];

I4 = 0L;
I4 += segment0[1] ^ segment2[3];
I4 *= segment1[3] + segment3[1];
I4 = (segment4[2] & segment5[2]) & (I4 & 0xFFFFFFFF);
I4 *= segment7[3];
I4 += I2;
I4 *= segment6[0] * I1;
I4 -= (I4 - I2 )% (I1 *2);

I5 = 0L;
I5 += (segment3[0] ^ segment4[0]) <<16;
I5 *= segment3[1] % (segment4[1] +2);
I5 += (segment3[2] % (segment4[2] +5 )) + 7;
I5 += segment3[3] * segment4[3];

0x03 code解码

到此为止,我们的key就没有使用价值了,接下来都是对code 的检查。首先是sub_7114对code进行一个base64解码。但是这个base64解码和常规的有些不同,字符表被替换为

ZO6Kq79L&CPWvNopzQfghDRSG@di*kAB8rsFewxlm+/u5a^2YtTJUVEn0$HI34y#

对于生成的每个十进制码x,要做一个x^(x>>3)的变换,并以此为索引,替换在字符集中的相应字符。

0x04 分水岭!

接下来的就是进阶和标准的分界点了。由于我此次做出的标准等级。所以接下来的流程比较简单。

首先是code解码后长度检查,题目中要求解码后的code 长度应该为32字节。每8个字节为一组,记为Group1,Group2,Group3,Group4。将Group1,Group2,Group3和之前计算出的五个校验值,视为64bit长的数字,并应满足如下方程:

令Group1 = x, Group2 = y, Group3 = z;
I1* x^2 + I2* y + I3 – y = 0
I4* x + I3 - y = (I2-I4)^2/ (4*I1)

求解这个方程组得到:

x = (I2-I4)/(2*I1)
y = (I1*x + I2)*x+I3

z的方程比较简单:

z = ( I1*I5 + I2)*I5 +I3

最后使用java写的注册机,做成了一个apk。提供一组成功的值。代码自下

Key:1111#1111#1111#1111#1111#1111#1111#1111

Code:ZZZpxEZZZZ6tNCl4ZZk0Fs0x4DG0$zDZNQeUNKq#EZZ= 

二、我踩到的坑


1.工欲善其事,必先利其器。这次的比赛,并没有做好充足的准备,首先是自己觉得之前参加过比赛,有了比赛经验之后,没有去参考其他比赛的内容,导致最开始上手的时候,折腾了好久汇编。其次呢,就是没有找好调试机器,自己想当然的觉得肯定没问题,可是等到真的开始调试的的时候,又遇到了超级多的bug,比如64位的机器能不能动态调试so库,什么样的手机才能调试,自己如何寻找到能够调试的方法。这些都是问题。如果能够早点进行准备的话, 就不会在调试机器上面耽误1天时间,能够节省下来看其他的代码。第三,就是自己明知道是有加密算法的,但是却没有准备相关的逆向知识,导致高级算法根本就看不出来是什么。虽然这些加密算法都有一个独特的特征,但是如何能够识别却还是需要经验的。最后就是不要太相信自己的能力,虽然直接看汇编进行逆向固然好,可是有现成的工具又为什么不用呢?最后两个方程,自己看了几个小时,还不及IDA7.0 F5一下。一句“无他,唯手熟尔”包含着多少默默付出。

2.根据自己的需求,选择好最合适的语言再进行开发。这次比赛我首先使用的是python语言。为什么?自己最熟悉而且可以省略很多底层的操作(由于知道算法中有大数运算),就是这一点让自己吃了大亏。后面底层的操作,本来取低64位的操作可以进行&0xFFFFFFFFFFFFFFFF或者进行mod 2**64,但是无论如何都得不到想要的结果,而且Python使用bin的时候正数很正常,但是负数的话是正数的bin前面加上负号,也就是-5表示成 -101。但是如果换做Java就没有这些问题,而且还有大数库可以很好地操作64位乘法。所以啊,有些事情不是看看就会的,还是要动手调试。“绝知此事要躬行”眼高手低要不得。

3.缺少“撸起袖子加油干”的精神。嗯。。。这也是自己一直以来的一个通病吧,就是干活的时候喜欢听音乐、听相声、听视频,应该说是因为自己比较偷懒,想一直看视频、看相声,自己从骨子里就是有股惰性,而虽然我表面上在干活,可是内心里却已经放飞。这种隐藏摸鱼的手法,虽然自己觉着可能工作中比较轻松,但是却大大增加了开发的周期,影响了效率。正好借着耳机坏了的机会,把自己的这个毛病好好扳一扳。“空谈误国,实干兴邦”,自己不知何时变得浮躁,还是要踏踏实实的干下去。

4.无论何时,编程能力都是重中之重。编程可谓是安全人员的工作基石,可是自己的编程能力不过关。别的姑且不论,就拿大端小端来说,弄了那么久都没有弄对,究其本质,还是自己的基础知识不过关。自己经常说记性不好,可是记性不好并不是失败的借口,接下来,努力强化记忆吧。

5."I always do what I can not do ,so that I can know how to do."说起来比较有意思,这句话是从基友衣服上面看到的,有点意思,谨记吧。

那么我自己的优点呢我觉得还是有一些的。
1.耐心好,能够耐着性子看完汇编
2.联想能力比较好,快速的联想到了去年的比赛,从而加速了开发的进程。

3.已经掌握了较多的技巧,知道如何去动态,如何去寻找工具。

这并不是一件坏事,在这个找工作和做毕设的紧要关头,这一个比赛暴露了自己的缺点,改正就行了。希望来年,不要再写出这几点缺点,争取做完全部内容。同时,表达能力啊喂,自己多看一看文章,多写一些内容,干得多也要说得多。我这个人记打不记吃,而且比较喜欢和别人比较找出自己的弱点,并且一定要改正。已经尝试戒掉听音乐3天,工作效率有所提高,想渐渐改掉摸鱼的习惯。

猜你喜欢

转载自blog.csdn.net/zhuzhuzhu22/article/details/80114334