注意:本文从旧博客迁移过来,涉及内容可能已经过时,请选择性阅读。
中秋,跟同事一起聚会玩到午夜,准备上火兔嘀咕几句,发现有人问我“图嘀怎么不能发图了?”(注:图嘀,以前做的一个小工具,用来突破限制发图)。当时心里就犯了嘀咕,火兔把我辛辛苦苦做的图嘀和谐了?
马上登陆图嘀验证,果然发送图片失败。某客户端也随之更新,以前的图滴版本无法使用。一连串的事实证明,图嘀确实被和谐了。火兔做了加密处理,但是我这人就是喜欢较劲。于是熬夜奋战,解决了问题。
遇到被封锁的问题,习惯性的想到火兔一定在API上做了手脚,果不其然,抓包看究竟。如图所示:
在发送嘀咕的时候程序会事先向LoveDiguVal 发送一个请求。然后会得到一个字符串:
看到这字符串,有点莫名其妙,于是邪恶的连续刷新好几次,每次得到的字符窜都是不规则的字符窜(包括长度)。 看到这里,心里开始嘀咕,未必遇到加密高手了?放弃是不可能的,于是连续抓了几个包开始分析,准备从获取的字符窜中入手,以获取一些有用的信息。
以下是抓取的4组字符串:
228 vxi 738 bkof 1296 an 696 6510 emb(456 b 738 mcr 1134 sio 928 8680qg 1769 xgm)
228 opxc 738 1296 z 696 fplxxh 6499 eeho (456 irckph 738 1296 pd 609 enkwdsar 8665 comr 717 mtg)
228 bt 738 zv 1296 rl 696 nbu 3028 heg(456 mpvqknkks 738 vnkdsrnri 1134 psetp 928 kxhyeldouw294pxra)
228 ntr 738 q 1296 ofc 696 vguk 616 kbs (456 cnrhky 738 nlvt 1134 nqyxr 928 ww 1902 gpsuopt)
空格是我特意分开的,从以上的数组中不难发现一个规律:前四组数字的值都是一样的,唯独最后一位的数字的值是变化的。看到这一组数据,开始纳闷了,这不像是流行的加密算法啊?未必火兔的程序员自己写了一个算法?
上面每组数据会有两组字符串,前面的是获取的,后面的是发送出去的。初步下了得出:客户端首先获取字符串,然后根据获取的字符串来进行处理,接着发送出去另外一个字符串。这个过程中,客户端可能会涉及到一个加密算法。
当然这时候,我也只是推测。做事情有始有终,既然在客户端会有处理,那么弄清楚它的算法不就可以看个究竟了?于是习惯性的,尝试反编译它的客户端,它的客户端是基于Adobe Air技术,以前还没试过反编译这玩意,这将会是一个吃螃蟹的过程。
经过一番努力,反编译成功,获得类似字节码的IL语言。开始分析代码,由于没有配置环境没法调试。直接从代码段开始入手分析,众里寻它千百度,暮然回首发现一个可疑的加密以及解密的函数,上图:
这么敏感的字符居然没被混淆,哦也。Encry顾名思义加密的意思。那么它加密的什么呢?看代码(代码中注释的部分也就是我分析的过程):
static function decryptIp(String):Array /*disp_id 4*/ //解密函数,返回是一个数组
{
// local_count=3 max_scope=1 max_stack=4 code_len=80
0 getlocal0
1 pushscope
2 getlocal1
3 getlex RegExp
6 pushstring "[A-Za-z]+" //这里它用到了正则?这里为什么要用到正则呢?未必是处理 请求的那个字符窜的?(现在仅仅是猜测),继续往后走。
9 construct (1)
11 callproperty http://adobe.com/AS3/2006/builtin::split (1) //这里它用到了 分割? 难道是把那个字符窜分割成数组?经验证确实如此。
15 coerce Array
17 setlocal2
18 getlocal2
19 pushbyte0
21 getlocal2
22 pushbyte 0 //这里指向数组 索引为 0的数
24 getproperty null
27 pushbyte 2 //这里 它进行了 除 2的处理。
29 divide //除法的意思。。英语很重要啊。呵呵
30 setproperty null
33 getlocal2
34 pushbyte 1
36 getlocal2
37 pushbyte 1 //这里指向数组 索引为 1的数
39 getproperty null
42 pushbyte 3 //这里 它进行了 除 2的处理。
44 divide
45 setproperty null
48 getlocal2
49 pushbyte 2
51 getlocal2
52 pushbyte 2 //这里指向数组 索引为 2的数
54 getproperty null
57 pushbyte 8 //这里 它进行了 除 2的处理。
59 divide
60 setproperty null
63 getlocal2
64 pushbyte 3
66 getlocal2
67 pushbyte 3 //这里指向数组 索引为 2的数
69 getproperty null
72 pushbyte 6 //这里 它进行了 除 2的处理。
74 divide
75 setproperty null
78 getlocal2
79 returnvalue
}
这里我有点奇怪,为什么只有四组? 前面我们从函数的名字我们可以判断出,这组数据是跟IP有关联的(现在只是假设)。我把获取的字符窜里面的数字按早这个规则进行运算,得出的结果正好是我的IP地址?事情开始明朗了,好,我们继续往后走。
static function encryptIp(Array):String /* disp_id 3*/
{
activation {
var myPak::getRandomStr:Function /* slot_id 2 */
var myPak::r:String /* slot_id 3 */
var myPak::ip:Array /* slot_id 1 */
}
// local_count=3 max_scope=2 max_stack=5 code_len=230
0 getlocal0
1 pushscope
2 newactivation
3 dup
4 setlocal2
5 pushscope
6 getscopeobject 1
8 getlocal1
9 setslot 1
11 getscopeobject 1
13 newfunction var undefined():String /* disp_id 0*/
16 coerce Function
18 setslot 2
20 getscopeobject 1
22 pushstring ""
24 setslot 3
26 getscopeobject 1
28 getlex Math
31 getscopeobject 1
33 getslot 1
35 pushbyte 0
37 getproperty null
40 pushbyte 4 //这里它把解密后的数值进行乘以4的处理?
42 multiply
43 callproperty round (1)
47 callproperty http://adobe.com/AS3/2006/builtin::toString (0)
51 getscopeobject 1
53 getslot 2
55 getglobalscope
56 call (0)
58 add
59 coerce_s
60 setslot 3
62 getscopeobject 1
64 getscopeobject 1
66 getslot 3
68 getlex Math
71 getscopeobject 1
73 getslot 1
75 pushbyte 1
77 getproperty null
80 pushbyte 3 //这里它把解密后的数值进行乘以3的处理?
82 multiply
83 callproperty round (1)
87 callproperty http://adobe.com/AS3/2006/builtin::toString (0)
91 add
92 getscopeobject 1
94 getslot 2
96 getglobalscope
97 call (0)
99 add
100 coerce_s
101 setslot 3
103 getscopeobject 1
105 getscopeobject 1
107 getslot 3
109 getlex Math
112 getscopeobject 1
114 getslot 1
116 pushbyte 2
118 getproperty null
121 pushbyte 7 //这里它把解密后的数值进行乘以7的处理?
123 multiply
124 callproperty round (1)
128 callproperty http://adobe.com/AS3/2006/builtin::toString (0)
132 add
133 getscopeobject 1
135 getslot 2
137 getglobalscope
138 call (0)
140 add
141 coerce_s
142 setslot 3
144 getscopeobject 1
146 getscopeobject 1
148 getslot 3
150 getlex Math
153 getscopeobject 1
155 getslot 1
157 pushbyte 3
159 getproperty null
162 pushbyte 8 //这里它把解密后的数值进行乘以8的处理?
164 multiply
165 callproperty round (1)
169 callproperty http://adobe.com/AS3/2006/builtin::toString (0)
173 add
174 getscopeobject 1
176 getslot 2
178 getglobalscope
179 call (0)
181 add
182 coerce_s
183 setslot 3
185 getscopeobject 1
187 getscopeobject 1
189 getslot 3
191 getlex Math
194 getlex Math
197 callproperty random (0) //NND 原来最后组数是随机数?难怪前面的都是有规律的,唯独最后一个数字没规律。
201 pushshort 2500 //它在0~250 范围内任意取值。
204 multiply
205 callproperty round (1)
209 callproperty http://adobe.com/AS3/2006/builtin::toString (0)
213 add
214 getscopeobject 1
216 getslot 2
218 getglobalscope
219 call (0)
221 add
222 coerce_s
223 setslot 3
225 getscopeobject 1
227 getslot 3
229 returnvalue
}
在以上的代码中,一个明朗的思路渐现:客户端申请一个字符串,然后对字符串解密并二次加密发送数据。
我开始有点纳闷,它根据IP加密的,为啥要生成一个字符串然后解密再加密?让人摸不着头脑,后来证明那加密完全是多余,也就是说把火兔的程序员想的好一点:他用来混淆试听的,或者是起到一个类似于验证码的功能。
最后写了一个小DEMO 去测试是否验证成功,截图:
测试发图成功,如图:
注意:仅供参考。之所以拿火兔开刀,是因为觉得它做的还是不错,就是限制的厉害,正好我最讨厌被限制,于是有了图嘀的出现。
破解过程有点跳跃性,在真正的破解中,这是很漫长的过程,大概花了我4个小时。由于实在太累,所以就写了关键的部分,不懂的可以邮件问我。最后说一句“道高一尺魔高一丈,安全是个永恒的话题”。