Shopee算法分析 - Unidbg模拟执行

博客地址: http://blog.androidcrack.com/index.php/archives/37/
感谢杨如画大佬分析的文章, 这个文章是复现大佬的文章. 动手了才是自己的.

抓包

  • Pixel3
  • Reqable

咱们的任务需求主要是搞定这几个算法.

image-20241213144552163

定位加密位置

1. 搜索法

我们将应用拖入JADX中,搜索关键词 x-sap-ri,发现无果. 所以搜索关键词的方法在此处显得较为无力. 在此案例中, 不适合使用. 但是并不意味着,搜索法不好. 这个要看实际的使用常见

2. 开发者思维

我们反编译虾皮的APK, 可以看到有okhttp3的主包, 那么我们就要思考. 我们应该如何给一个请求设置请求头?

https://square.github.io/okhttp/

使用addHeader, addHeader里面应该是用HashMap, 这里我没有去跟踪okhttp, 感兴趣的读者可以自己去跟踪一下.

最终可以写一个frida hook脚本.

💡对HashMap.put方法进行了拦截, 判断key是否为我们的目标, 如果为目标,则打印调用栈. 根据调用栈定位加密位置

Java.perform(function (){
   
    //     HashMap.put
    var hashMap = Java.use("java.util.HashMap");
    hashMap.put.implementation = function (a, b) {
   
        if(a!=null && a.equals("x-sap-ri")){
   
            console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
            console.log("hashMap.put: ", a, b);
        }
        return this.put(a, b);
    }
})

保证应用在前台, 我们开始Hook应用

frida -UF -l .\hook_shopee.js

堆栈非常的长, 我们看到又关键的方法com.shopee.shpssdk.SHPSSDK.uvwvvwvvw

java.lang.Throwable
        at java.util.HashMap.put(Native Method)
        at org.json.JSONObject.put(JSONObject.java:276)
        at org.json.JSONTokener.readObject(JSONTokener.java:394)
        at org.json.JSONTokener.nextValue(JSONTokener.java:104)
        at org.json.JSONObject.<init>(JSONObject.java:168)
        at org.json.JSONObject.<init>(JSONObject.java:185)
        at com.shopee.shpssdk.SHPSSDK.uvwvvwvvw(Unknown Source:58)
        at com.shopee.shpssdk.SHPSSDK.requestDefense(Unknown Source:95)
        at com.shopee.app.network.antifraud.b.intercept(SourceFile:47)
        at okhttp3.internal.http.RealInterceptorChain.proceed(SourceFile:19)
		....(省略)
hashMap.put:  x-sap-ri d3db5b67e52fdbdec805c31d01850c676f03f49ab5458cc0533f

在jadx中, 我们找到对应的位置, 怪不得我们在前文中,用搜索法无法定位到函数位置. 原来是因为该应用的字符串加密导致的.

💡以后在逆向工作中, 大家可以思考下, 如果我是开发者,我会怎么做? 有时候可能会带来意想不到的效果!

那么生成我们需要的x-sap-ri参数的函数就是vuwuuwvw方法

public Map<String, String> requestDefense(String str, byte[] bArr) {
   
    if (!ShPerfC.checkNotNull(perfEntry) || !ShPerfC.on(new Object[]{
   str, bArr}, this, perfEntry, false, 35729, new Class[]{
   String.class, byte[].class}, Map.class)) {
   
        Hashtable hashtable = new Hashtable();
        if (str == null) {
   
            return hashtable;
        }
        try {
   
            try {
   
                return uvwvvwvvw(wvvvuwwu.vuwuuwvw(str.getBytes(), bArr)); // vuwuuwvw方法生成结果
            } catch (Throwable unused) {
   
                hashtable.put(vvuwvuuuu.wwvvvwvwu("0F580414075A131E0F1A10", "wuwuwwuww"), vvuwvuuuu.wwvvvwvwu("130E3F013E1F1A123E1B261C3A34341C2F243F413C3232063C363D023E1F19103C1C3F4E", "vwuvwuuuw"));
                return hashtable;
            }
        } catch (Throwable unused2) {
   
        }
    } else {
   
        return (Map) ShPerfC.perf(new Object[]{
   str, bArr}, this, perfEntry, false, 35729, new Class[]{
   String.class, byte[].class}, Map.class);
    }
}

wwvvvwvwu 方法是一个native方法. 需要去so中分析了.

public static native String vuwuuwvw(byte[] bArr, byte[] bArr2);

但是目前, 我们已经成功的通过开发者思维定位到函数加密的位置.

frida主动调用与unidbg模拟

1. 确定目标SO

在同个文件下搜索loadLibrary发现, 不是太容易, 因为导入的SO字符串也被处理了. 所以,我们可以hook libart

public static /* synthetic */ void access$000(String str) {
   
    IAFz3z iAFz3z = perfEntry;
    if (iAFz3z == null || !((Boolean) ShPerfB.perf(new Object[]{
   str}, null, iAFz3z, true, 39835, new Class[]{
   String.class}, Void.TYPE)[0]).booleanValue()) {
   
        System.loadLibrary(str);
    }
}

hook registernative脚本

function hook_hashmap() {
   
    Java.perform(function () {
   
        //     HashMap.put
        var hashMap = Java.use("java.util.HashMap");
        hashMap.put.implementation = function (a, b) {
   
            if (a != null && a.equals("x-sap-ri")) {
   
                console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
                console.log("hashMap.put: ", a, b);
            }
            return this.put(a, b);
        }
    })
}

function find_RegisterNatives(params) {
   
    let symbols = Module.enumerateSymbolsSync("libart.so");
    let addrRegister
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值