某订房软件算法分析还原

1,抓包

 2,定位关键位置,

 这次太简单了

frida hook 一下

Java.perform(function(){
        var header=Java.use("com.xxxx.xxxx.xxxx");
        console.log("start hook!!")
        header.a.overload('java.lang.String', 'java.lang.String', 'java.lang.String', 'java.lang.String', 'int', 'long').implementation=function(str1,str2,str3,str4,int1,long){
            console.log("\nstr1",str1,"\nstr2",str2,"\nstr3",str3,"\nstr4",str4,"\nint1",int1,"\nlong",long)
            var  ret=this.a(str1,str2,str3,str4,int1,long)
            //printStack()
            console.log("X-TGH :",ret)
            return ret;
        }

        header.encrypt.implementation=function(str1,str2,str3,str4,int1,long){
            console.log("\nstr1",str1,"\nstr2",str2,"\nstr3",str3,"\nstr4",str4,"\nint1",int1,"\nlong",long)
            var retval=this.encrypt(str1,str2,str3,str4,int1,long)
            console.log("X-TGH encrypt :",retval)
            return retval;
        }
    
    })

 打印结果

str1   null
str2 Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.P1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 tujia(hotel/263/263 mNet/wifi loc/zh_CN)  设备信息
str3 LON=null;LAT=null;AX=null;OY=null;CID=-1;LAC=-1;UID=c93a7674-d056-3b90-a9f9-ff195e6a27c8;OSVersion=8.1.0;AppVersion=263_263;DevType=2;DevModel=Pixel;Manufacturer=Google;;TJM=0;VersionName=8.40.1;    
str4 {"api_level":"263","platform":"1","res":null,"seed":651798267,"session_id":"c93a7674-d056-3b90-a9f9-ff195e6a27c8_1657895363857","timestamp":1657895780,"uid":"c93a7674-d056-3b90-a9f9-ff195e6a27c8"}
int1 196   str4.lenght
long 1657895838  time
X-TGH encrypt : 09b8b7593131ede78b53e91ae9d807684e068cfc
X-TGH : 09b8b7593131ede78b53e91ae9d807684e068cfc

 主动调用一下

function invoke(){
    Java.perform(function(){
        var Gundam = Java.use("com.xxxxxx");
        var StrCls = Java.use('java.lang.String');
        var arg0_str ="";
        var arg0 = StrCls.$new(arg0_str);
        var arg1_str = "Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.P1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 tujia(hotel/263/263 mNet/wifi loc/zh_CN)"
        var arg1 = StrCls.$new(arg1_str);
        var arg2_str = "LON=null;LAT=null;AX=null;OY=null;CID=-1;LAC=-1;UID=c93a7674-d056-3b90-a9f9-ff195e6a27c8;OSVersion=8.1.0;AppVersion=263_263;DevType=2;DevModel=Pixel;Manufacturer=Google;;TJM=0;VersionName=8.40.1;"
        var arg2 = StrCls.$new(arg2_str);
        var arg3_str = "{\"code\":null,\"parameter\":{\"abTests\":{\"T_login_831\":{\"s\":true,\"v\":\"A\"},\"searchhuojia\":{\"s\":true,\"v\":\"D\"},\"listfilter_227\":{\"s\":true,\"v\":\"D\"},\"T_renshu_292\":{\"s\":true,\"v\":\"D\"},\"Tlisttest_45664\":{\"s\":true,\"v\":\"C\"},\"Tlist_168\":{\"s\":true,\"v\":\"C\"},\"T_LIST27620\":{\"s\":true,\"v\":\"C\"}}},\"client\":{\"abTest\":{},\"abTests\":{}"
        var arg3 = StrCls.$new(arg3_str);
        var arg4 = arg3.getBytes().length;
        var arg5 = 1657895838;
        var ret = Gundam.encrypt(arg0,arg1,arg2,arg3,arg4,arg5);
        console.log("invoke encrypt ret:" + ret);

    })
}

3,so层分析。用下Unidbg

第一步先把框架搭起来,

package com.xxxx;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.LibraryResolver;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.PackageInfo;
import com.github.unidbg.linux.android.dvm.api.Signature;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import net.dongliu.apk.parser.bean.CertificateMeta;

import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class xxxx extends AbstractJni {

    private static final String APP_PACKAGE_NAME="xxxx";
    private final AndroidEmulator mEmulator;
    private final Memory mMemory;
    private final VM vm;
    private final Module dvmModule;

    xxxx(){
        //创建模拟器实列
        mEmulator = AndroidEmulatorBuilder.for32Bit().setProcessName(APP_PACKAGE_NAME).build();
        //emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(APP_PACKAGE_NAME).build();
        //获取模拟器内存接口
        mMemory = mEmulator.getMemory();
        //设置系统类库解析
        mMemory.setLibraryResolver(new AndroidResolver(23));
        //创建虚拟机,传入apk
        vm = mEmulator.createDalvikVM(new File("F:\\xxx.apk"));
        //加载so
        DalvikModule dvm = vm.loadLibrary(new File("F:\\libtujia_encrypt.so"), true);
        //获取本SO模块的句柄
        dvmModule = dvm.getModule();
        //设置jni
        vm.setJni(this);
        //打印日志
        vm.setVerbose(true);
        // 调用JNI OnLoad
        dvm.callJNI_OnLoad(mEmulator);
    }

    public static void main(String[] args) {
        xxxx xxxx = new xxxx();

    }

    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature){
            case "com/xxxx/xxxx/xxxxx->getInstance()Lcom/tujia/hotel/TuJiaApplication;":
                return vm.resolveClass("com/xxxx/xxxx/xxxxx").newObject(signature);
            case "java/security/MessageDigest->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;":{
                StringObject type = varArg.getObjectArg(0);
                System.out.println(type);
                String name = "";
                if ("\"SHA1\"".equals(type.toString())) {
                    name = "SHA1";
                } else {
                    name = type.toString();
                    System.out.println("else name: " + name);
                }
                try {
                    return vm.resolveClass("java/security/MessageDigest").newObject(MessageDigest.getInstance(name));
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature){
            case "com/xxxx/xxxx/xxxxxx->getPackageName()Ljava/lang/String;":{
                return new StringObject(vm,"com.tujia.hotel");
            }
            case "com/xxxx/xxxx/xxxxxx->getPackageManager()Landroid/content/pm/PackageManager;":{
                return vm.resolveClass("android/content/pm/PackageManager").newObject(signature);
            }
            case "java/security/MessageDigest->digest([B)[B":{
                MessageDigest messageDigest = (MessageDigest) dvmObject.getValue();
                byte[] input = (byte[]) varArg.getObjectArg(0).getValue();
                byte[] result = messageDigest.digest(input);
                return  new ByteArray(vm,result);
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        switch (signature){
            case "com/xxxx/xxxx/x->xxxxx:[Landroid/content/pm/Signature;":
                return vm.resolveClass("Landroid/content/pm/Signature").newObject(signature);
        }
        return super.getObjectField(vm, dvmObject, signature);
    }
}

补完环境跑一下

[main]D/Gundam: sign input 3 = 3687596384,~= 607370911
[main]D/Gundam: sign arr2 3 = 607370911,tempX= 607370911
[main]D/Gundam: sign input 4 = 699393680,~= 3595573615
[main]D/Gundam: sign arr2 4 = 3595573615,tempX= -699393681
[main]D/Gundam: result = 1
[main]D/Gundam: check result is ok
JNIEnv->RegisterNatives(com/xxxx/xxxx/xxxxx, RW@0x4001f000[libtujia_encrypt.so]0x1f000, 2) was called from RX@0x400040f3[libtujia_encrypt.so]0x40f3
RegisterNative(com/xxxx/xxxx/xxxxx, encrypt(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJ)Ljava/lang/String;, RX@0x400036a9[libtujia_encrypt.so]0x36a9)
RegisterNative(com/xxxx/xxxx/xxxxx, bodyEncrypt(Ljava/lang/String;JLjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;, RX@0x4000380d[libtujia_encrypt.so]0x380d)

成功跑出来了,现在主动调用一下encrypt();

public void invoke_encrypt() {
        //构建数组
        ArrayList<Object> args = new ArrayList<>();
        DvmClass dvmClass = vm.resolveClass("com/xxxxxx");
        DvmObject<?> dvmObject = dvmClass.newObject(null);
        Object arg0 = vm.addLocalObject(new StringObject(vm, ""));
        String arg1 ="Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.P1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 tujia(hotel/263/263 mNet/wifi loc/zh_CN)";
        Object args1_str=vm.addLocalObject(new StringObject(vm,arg1));
        String arg2 = "LON=null;LAT=null;AX=null;OY=null;CID=-1;LAC=-1;UID=c93a7674-d056-3b90-a9f9-ff195e6a27c8;OSVersion=8.1.0;AppVersion=263_263;DevType=2;DevModel=Pixel;Manufacturer=Google;;TJM=0;VersionName=8.40.1;";
        Object arg2_str=vm.addLocalObject(new StringObject(vm,arg2));
        String arg3 = "{\"code\":null,\"parameter\":{\"abTests\":{\"T_login_831\":{\"s\":true,\"v\":\"A\"},\"searchhuojia\":{\"s\":true,\"v\":\"D\"},\"listfilter_227\":{\"s\":true,\"v\":\"D\"},\"T_renshu_292\":{\"s\":true,\"v\":\"D\"},\"Tlisttest_45664\":{\"s\":true,\"v\":\"C\"},\"Tlist_168\":{\"s\":true,\"v\":\"C\"},\"T_LIST27620\":{\"s\":true,\"v\":\"C\"}}},\"client\":{\"abTest\":{},\"abTests\":{},\"adTest\":{\"m1\":\"Color OS V5.2.1\",\"m2\":\"\"}";
        Object arg3_str=vm.addLocalObject(new StringObject(vm,arg3));
        int arg4_len = arg3.getBytes(StandardCharsets.UTF_8).length;
        long arg5_long=1657895838L;
        args.add(vm.getJNIEnv());   //第一个参数是env
        args.add(0);                //第二个参数一般是jObject或者jClass 可以填写0,一般用不到
        args.add(arg0);
        args.add(args1_str);
        args.add(arg2_str);
        args.add(arg3_str);
        args.add(arg4_len);
        args.add(arg5_long);
        Number number = dvmModule.callFunction(mEmulator, 0x36a9, args.toArray());
        //System.out.println("number :"+number);
        String sign = vm.getObject(number.intValue()).getValue().toString();
        System.out.println("invoke_encrypt sign :"+sign);
    }

unidbg已经跑通了,用ida去看下so文件。

解析完成以后,G->跳转到0x36a9,就是encrypt()的函数地址

 

 能看到这么一个方法,进去瞅瞅

 里面有点乱 ,一点一点看

有两种,第一种,跟着传参看。第二种,从返回值看。

 看这种看起来类似算法常量,复制搜索一下。

接着往下看,

j_tjtxtutf8()   点进去,是base64,有两处调用的

再往下进入tjcreate()

 看这个常量是SHA1的K值,剩下的呢。

SHA1常量:0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,0xC3D2E1F0 

SHA K值:0x5A827999 ,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6

 这是在初始化 SHA1

 猜测一下,j_tjreset(),是运算。j_tjget()是 SHA1的结果

function hook_native(){
    Java.perform(function(){
        var encrypt_addr=Module.findBaseAddress("libxxxx.so")
        console.log("encrypt_addr :",encrypt_addr)
        var sha1_update=encrypt_addr.add(0x2C94+1)
        Interceptor.attach(sha1_update,{
            onEnter:function(args){
                console.log("sha1_update args[0] onEnter:\n",hexdump(args[0],{length:128}))
                this.r0=args[0]
                console.log("\nsha1_update args[1] onEnter:\n",hexdump( args[1], parseInt(args[2])))
            },onLeave:function(retval){
                console.log("\nsha1_update retval onLeave:\n",hexdump(this.r0,{length:128}))
            }
        })
    })
}

主动调用invoke()

 也可以用unidbg HOOK。

先试一下SHA1,

 和我们的sign对不上。

那就是魔改喽,返回到算法里看下。

我们知道 SHA1 只会和常量和K值表进行运算。

所以你算什么?

 测试一下

 好啦

大致流程已经知道了。等下 看下明文是个什么。

 对照咱们的传参比较一下;

1,#arg1_str#arg5#字符串#arg2_str#(arg3_str(排序))

2,字符串反转

3,Base64

4,xor 0x21

5,SHA1

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值