[Android]APK反编译之so库加密函数调用

    现在,绝大多数apk在数据签名验证方面,采用了jni+so的方式增加安全性。

    上次遇到一个app,360加固+native签名算法函数,这里不对脱壳做过多的描述。

    用Charles对app进行抓包,多次抓包发现FuncTag是每个事件处理函数的一个标识,用于区分请求的模块功能。

    zhuabao

    发现有一个参数sv,应该是数据的签名字段,应该就是我们的目标。

     

    在Android Killer打开工程,搜索40000020的16进制0x2625A14 定位到该处。

    aktag

     

    计算sv的区域,包名已经除去。

        new-instance v0, Ljava/lang/StringBuilder;
    
        invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
    
        const-string v1, "a:"
    
        invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        move-result-object v0
    
        sget v1, Lcom/xxxx/xxxx/a/g;->d:I
    
        invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
    
        move-result-object v0
    
        const-string v1, "c"
    
        invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        move-result-object v0
    
        const-string v1, ":"
    
        invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        move-result-object v0
    
        .line 4636
        invoke-static {}, Lcom/xxxx/xxxx/y;->d()Lcom/xxxx/xxxx/y;
    
        move-result-object v1
    
        invoke-virtual {v1}, Lcom/xxxx/xxxx/y;->ap()Ljava/lang/String;
    
        move-result-object v1
    
        invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        move-result-object v0
    
        invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
    
        move-result-object v0
    
        /*
        省略部分
        */
        .line 4643
        new-instance v1, Ljava/lang/StringBuilder;
    
        invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
    
        const-string v2, ""
    
        invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        move-result-object v1
    
        invoke-static {}, Lcom/xxxx/xxxx/y;->d()Lcom/xxxx/xxxx/y;
    
        move-result-object v2
    
        invoke-virtual {v2}, Lcom/xxxx/xxxx/y;->aB()J
    
        move-result-wide v2
    
        invoke-virtual {v1, v2, v3}, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;
    
        move-result-object v1
    
        invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
    
        move-result-object v1
    
        invoke-static {v0, v1}, Lcom/xxxx/xxxx/utils/EncodeString;->EncodeMD5(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    
        move-result-object v1

    在这里可以看出,sv的计算方法是把每个参数按照一定的顺序排列拼接起来,再进行EncodeMD5。

     

    转换成java代码验证一下,没有错。

        Object localObject = "a:" + com.xxxx.xxxx.a.g.d + "c" + ":" + com.xxxx.xxxx.y.d().ap();
        localObject = (String)localObject + "FuncTag" + ":" + 40000020;
        localObject = (String)localObject + "ir" + ":" + paramInt2;
        localObject = (String)localObject + "phoneNum" + ":" + paramString;
        localObject = (String)localObject + "platform" + ":" + 2;
        localObject = (String)localObject + "smsType" + ":" + paramInt1;
        String str = EncodeString.EncodeMD5((String)localObject + "userId" + ":", "" + com.xxxx.xxxx.y.d().aB());

    str 就是我们要的sv值,他是通过EncodeString类里的EncodeMD5方法来加密的,跟过去看看。

     

    发现都是static native的方法。

    .method public static native EncodeMD5(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    .end method
    
    .method public static native EncodeMD5ByByte([BLjava/lang/String;)Ljava/lang/String;
    .end method
    
    .method public static native EncodeUserNameAndPassword(Ljava/lang/String;)Ljava/lang/String;
    .end method

    继续找下,看下是加载哪个so库。

     

    搜索关键字 loadLibrary ,发现在app初始化时加载了两个动态库,很明显,EncodeString类应该在encode这个动态库里面。

      private static void b(Context paramContext)
      {
        int i = Build.VERSION.SDK_INT;
        y.a(d, "loadSo , SDK Version: " + i);
        System.loadLibrary("encode");
        System.loadLibrary("game");
      }
      

     

    找到so文件拖入IDA,搜索 encode。

    ida

     

    看了很久不是很明白他的算法到底是怎么实现的,他修改了MD5的算法,并不是一般的(数据+key)再进行原生的MD5加密。

     

    在导出表里面发现这几个导出函数,想到了一个办法。

    ida2

     

    找到了函数注册到java里的包名,这里就跟上面在java代码里面看到的包名路径是一样的。

    ida3

     

    打开Android Studio,新建一个项目。

    as1

     

    在main下新建jniLibs目录,把上面的so库放入armeabi中

    as2

     

    在上面jni映射过来的方法在com.xxxx.engine.utils中,这里新建一个包,包名路径相同。

    as3

     

    写一个EncodeString类,函数申明在上面反编译出来的代码里面已经有了,直接照搬过来。

    在函数前装载so库

        static {
            System.loadLibrary("encode");
        }

    as4

     

    在Oncreate下面调用EncodeMD5方法,debug打印出来。

    运行一遍,看调试器里面,so已经加载成功。as5

     String str = EncodeString.EncodeMD5((String)localObject + "userId" + ":", "" + com.xxxx.xxxx.y.d().aB());
    

     

    最后按照原来的参数打印出来sv。其实这样相当于是曲线救国,可以把一台手机用于签名,连接电脑进行生产工作。

    但是这样的效率相对来说低一点,对模拟器的兼容也不太好。

    所以只能当做下下策来使用。

    本博客所有文章如无特别注明均为原创。
    复制或转载请以超链接形式注明转自n0elle's Blog,原文地址《[Android]APK反编译之so库加密函数调用
    标签:
    喜欢 | 1
    分享:

还没有人抢沙发呢~