GDAblog-中国首款交互式Android反编译器

GDA脚本批量解密APP字符串向导

 

一些APK尤其是Android恶意代码会将自身的一些重要的字符串进行加密,加大逆向分析的难度,这类字符串往往数量比较大,很难一个一个进行处理。本文将介绍一种方法来解密这种字符串,并将解密的字符串应用于反编译器中。本次演示的案例来自于一款恶意代码(为了防止风险,不提供恶意代码样本),恶意代码信息如下:

[File Base Info]

File Name: C:\Users\**\fish.apk

Package Name: fdsvcc.bcxa.bvdwq

Main Activity: fdsvcc.bcxa.bvdwq.MainActivity

File Size: 2631444 bytes

MD5: 026d079284beaddf6ffda550bc9e3668

Packed: Not Packed

Min SDK: 15

Target SDK: 28

 

python解密脚本代码:方法一 方法二

java解密脚本代码:方法一、方法二

 

一、使用python脚本解密

 

1、通过GDA->String我们可以看到,该APP的大量字符串做了加密处理。如图:

其中加密字符串上千个,因此需要对其进行自动化批量解密。光标放在[]按下X(字符串交叉引用)我们可以清楚的看到解密函数,其中一个解密函数类似于如下红色矩形框中的函数:

以下我们将以该函数为例,利用该函数来解密所有与其相关的字符串。

双击函数Request.ALLATORIxDEMO来到函数的反编译代码:

可以看出,该解密函数从字符串尾部开始分别与0x270x65进行异或计算,因此实现起来也比较简单。

2、我们的目标是将该解密函数所能解密的所有字符串进行解密处理。因此我们的脚本可以通过如下步骤对所有字符串进行解密:

 

首先使用python重新实现 ALLATORIxDEMO(String p0)

接下来需要定位appALLATORIxDEMO函数的位置,并找到所有引用该函数的调用者函数。

在调用者函数中提取出加密的字符串,然后解密并写回APP(不修改原始apk文件)

 

1) 解密函数python实现

 

使用python重新实现解密算法很简单,如下:

def decodeString(gda,idx):

    rawstr=gda.GetStringById(idx)

    if rawstr==None:

        return ''

    stri=rawstr

    ret=list(stri)

    i=len(stri)-1

    xx=''

    while i>= 0:

        ret[i]=chr(ord(stri[i])^39)//0x27

        if i <= 0:

            break

        i=i-1

        ret[i]=chr(ord(stri[i])^101)//0x65

        i=i-1

    xx = ''.join(ret)   

return xx

 

2) 定位解密函数

 

首先我们需要找到解密函数及其调动者。在反编译的ALLATORIxDEMO函数处按F5编译成smali代码如下图:

 

 该函数的method index(该indexdex文件中方法的索引)为004fc9,然后我们通过如下代码定位到该函数。

 

def GDA_MAIN(gda_obj):

    gda=gda_obj//GDA对象

    Dex0=gda.DexList[0]//app的第一个DEX

    midx=0x4fc9//method idx

method=Dex0.MethodTable[str(midx)]//通过methodTable快速查找方法。

这里method对象(类型为MethodInfo)就是我们要找的解密函数。method对象的callorIdxList(具体见GdaImport.py文件)属性便是该方法的所有调用者。通过遍历callorIdxList便可以访问所有的调用者。

 

3) 定位字符串并解密

 

调用者找到后,我们还需要定位解密函数在调用者函数中的位置,以方便我们进一步定位字符串。我们回到GDA上看看调用者调用解密函数的上线文。

我们看到,字符串出现在解密函数ALLATORIxDEMO被调用上面,因此,我们可以对调用者进行反汇编(通过GetSmaliCodeById)得到smali,然后从解密函数被调用的行开始反向定位字符串,并将字符串的index提取出来解密,代码如下(原始代码中加入了字典callorTable={}strIdxTable={},防止重复操作)。

clist=method.callorIdxList

destr=''

for idx in clist:

        smalicode=gda.GetSmaliCodeById(idx)

        splitstr=smalicode.split('\r\n')

        i=0

        for sstr in splitstr:

            if '@004fc9' in sstr:

                line=splitstr[i-1]

                if 'string@' in line:

                    pos=line.find('ing@')+4

                    strIdx=line[pos:pos+4]

                    dstr=decodeString(gda,int(strIdx,16))

                    gda.SetStringById(int(strIdx,16),dstr)

                    destr+="[string@"

                    destr+=strIdx

                    destr+="] "

                    destr+=dstr

                    destr+='\n' 

            i=i+1   

代码注解:首先遍历callorIdxList,这个列表里存放的是调用者函数的idx,我们可以通过GDAAPI GetSmaliCodeById来反汇编该函数,接下来,将反汇编的代码按行进行分割,首先通过'@004fc9'来定位解密函数所在的行,然后反向搜索需要解密的字符串,提取出字符串的index最后调用解密函数解密并通过API SetStringById写回到字符串中。代码中为了方便操作我加了字符串交互标志[string@xxxx].解密后如下:

 

光标放在[]中按X可以查看调用处的代码如下:

可以看到所有的解密后的字符串都直接反应在了反编译的代码中。

上面的方法简单易懂,但是稍微会慢些,第二种方法速度会更快,其基本不同在于,采用字节码的数据来定位字符串。具体见代码。

代码中使用API DumpHexData来提取调用者方法的字节码(以十六进制字符串的形式表示,返回字符串流),然后通过字节码特征定位要解密的字符串idx

如何定位特征?通过上面的方法定位到其中一个调用者函数,然后编译成为smali, 鼠标右键->show bytecode分析解密字符串的定位特征:

首先找到“c94f”(0x4fc9),然后向前找到加密字符串idx.

 

二、java脚本

 

GDA提供给java使用的接口与python是相同的,具体分析不再累述。但是需要注意以下两点。

1.使用java处理字符串解密问题时,如果解密函数只使用了java基本库,可以直接使用反编译的代码进行解密,不需要额外写解密函数。

2.由于java虚拟机加载到进程后,无法动态释放和重载,所以在一个GDA进程里不能重复运行相同类名的java代码。