【逆向】午夜归来玩破解-火兔破解附解密过程

注意:本文从旧博客迁移过来,涉及内容可能已经过时,请选择性阅读。

中秋,跟同事一起聚会玩到午夜,准备上火兔嘀咕几句,发现有人问我“图嘀怎么不能发图了?”(注:图嘀,以前做的一个小工具,用来突破限制发图)。当时心里就犯了嘀咕,火兔把我辛辛苦苦做的图嘀和谐了?

马上登陆图嘀验证,果然发送图片失败。某客户端也随之更新,以前的图滴版本无法使用。一连串的事实证明,图嘀确实被和谐了。火兔做了加密处理,但是我这人就是喜欢较劲。于是熬夜奋战,解决了问题。

遇到被封锁的问题,习惯性的想到火兔一定在API上做了手脚,果不其然,抓包看究竟。如图所示:

在发送嘀咕的时候程序会事先向LoveDiguVal 发送一个请求。然后会得到一个字符串:

看到这字符串,有点莫名其妙,于是邪恶的连续刷新好几次,每次得到的字符窜都是不规则的字符窜(包括长度)。 看到这里,心里开始嘀咕,未必遇到加密高手了?放弃是不可能的,于是连续抓了几个包开始分析,准备从获取的字符窜中入手,以获取一些有用的信息。

以下是抓取的4组字符串:

  1. 228  vxi  738  bkof  1296  an 696  6510 emb(456  b  738  mcr  1134  sio  928  8680qg 1769 xgm)

  2. 228  opxc  738  1296  z   696  fplxxh 6499 eeho (456  irckph 738 1296  pd 609 enkwdsar 8665 comr 717 mtg)

  3. 228  bt  738  zv  1296  rl  696  nbu 3028 heg(456  mpvqknkks  738  vnkdsrnri  1134  psetp  928  kxhyeldouw294pxra)

  4. 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个小时。由于实在太累,所以就写了关键的部分,不懂的可以邮件问我。最后说一句“道高一尺魔高一丈,安全是个永恒的话题”。