吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1279|回复: 15
上一主题 下一主题
收起左侧

[Android 原创] 某店的逆向

[复制链接]
跳转到指定楼层
楼主
yanmf 发表于 2026-3-30 22:43 回帖奖励

0x01开发环境

  • windows 10
  • 小米MIX(刷机到android 10,root)
  • mumu模拟器(root android 12)
    • 用于抓包
  • 某店APK com.koudai.weidian.buyer 某店-7.4.1.apk
  • conda 25.11.0 (Anaconda3)
    • 主要集成了py3还有很多常用的包,不需要每个都pip下载安装。
  • adb Version 34.0.3-10161052
  • Charles 4.6.4
    • 抓包工具
  • frida frida-server 16.1.11
    • 逆向动态分析hook工具
  • jadx-gui 1.4.7
    • 逆向反编译apk工具
  • ghidra 9.2.4
    • 反编译so工具
  • scrcpy-win64 3.3.4
    • adb手机投屏电脑工具

0x02搭建环境

Charles安装跟注册

  • 下载地址 https://www.charlesproxy.com/download/
  • 需要注册一下,不然会<font style="color:rgb(25, 27, 31);">30分钟会自动关闭,弹出一个弹窗。启动10秒延迟,这个是需要付费导致的</font>
  • <font style="color:rgb(25, 27, 31);">这里非常感谢zhile大佬们的分享</font>
  • <font style="color:rgb(25, 27, 31);">解决方案如下</font>

Charles解决方式,使用该账号填写注册即可。

Help -> Register Charles,填写以下信息重启即可

Registered Name: https://zhile.io

License Key: 48891cf209c6d32bf4

不同设备抓包

  1. windows抓包,安装证书到受信任的根证书颁发机构
  2. mumu模拟器抓包,MuMu模拟器Charles抓包教程_MuMu模拟器_安卓模拟器
  3. 小米Mix android10抓包
    • 过程坎坷,放到末尾再讲。
    • android7以上的设备抓包证书无法安装到系统目录下所以导致抓包艰难。

<!-- 这是一张图片,ocr 内容为: -->


安装Anaconda3

C:\Users\>python -V
Python 3.13.9

C:\Users\>conda -V
conda 25.11.0

C:\Users\>pip -V
pip 25.3 from C:\ProgramData\anaconda3\Lib\site-packages\pip (python 3.13)

安装frida

  1. 这里版本推荐16.1.11,比较稳定。frida-server兼容性非常好。
  2. 其他版本也可以不过一定保证frida跟frida-server的版本保持一致。
  3. 安装frida跟tools

<u>pip install frida==16.1.11 frida-tools==12.1.1</u>

  1. 下载frida-server Releases · frida/frida
C:\Users\>frida --version
16.1.11
  1. 把下载好的frida-server推送到设备上并运行
adb push frida-server /data/local/tmp/
cd /data/local/tmp/
chmod +x frida-server
# 运行
./frida-server &
  1. 在电脑上测试连接

<font style="color:rgb(31, 35, 40);">frida-ps -Ua</font>


  1. 转发frida-server端口设置(可选择)
    • <font style="color:rgb(31, 35, 40);">frida-server跑在Android端,frida需要通过连接frida-server。上一步使用adb的方式连接,frida认为是USB模式,需要</font><font style="color:rgb(31, 35, 40);background-color:rgba(129, 139, 152, 0.12);">-U</font><font style="color:rgb(31, 35, 40);">命令。frida也支持依赖端口的远程连接模式,在某些场景下更加灵活。可以通过端口转发的方式实现此功能。</font>
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

其它工具下载

该说不说github真是太强大了


0x03逆向分析

抓包以及静态分析

主要是用过Charles跟mumu模拟器安装某店后进行抓包

<!-- 这是一张图片,ocr 内容为: -->

  • 搜索login,发现有一条请求。这个肯定就是某店的登录消息了。可是发现都是加密的,我们该怎么办呢?我们多看几个thor.weidian.com的请求。发现他们格式是固定的。参数都是param v context appkey timestamp sign。
  • 此时我们没有头绪,那就在jadx中全局搜索这几个字段名看看。可以先从sign开始。

<!-- 这是一张图片,ocr 内容为: -->

  • 发现了com.vdian.android.lib.protocol.thor.e这个类,有所有的字段名,我们顺势看调用。

<!-- 这是一张图片,ocr 内容为: -->

  • 果然我们逐个分析后发现了签名函数,还不确定是不是我们来通过frida进行hook查看下。

private static String sign(Map<String, String> map, String str) {
    if (map == null) {
        return null;
    }
    map.remove(com.vdian.android.lib.protocol.thor.e.z);
    StringBuilder sb = new StringBuilder();
    String str2 = "";
    if (!map.isEmpty()) {
        ArrayList<String> arrayList = new ArrayList(map.keySet());
        Collections.sort(arrayList);
        for (String str3 : arrayList) {
            String str4 = map.get(str3);
            if (str4 == null) {
                str4 = "";
            }
            sb.append((Object) str4);
        }
    }
    sb.append(str);
    String sb2 = sb.toString();
    try {
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        messageDigest.update(sb2.getBytes(q.b));
        byte[] digest = messageDigest.digest("".getBytes(q.b));
        for (int i = 0; i < digest.length; i++) {
            str2 = String.valueOf(str2) + Integer.toHexString((digest[i] & 255) | (-256)).substring(6);
        }
        return str2.toUpperCase();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    } catch (Exception e2) {
        e2.printStackTrace();
        return null;
    }
}
  • 这里提一嘴jadx真好用,选择函数右键就可以生成frida代码

/*
    # 附加到运行中的进程
    frida -U -l hook.js com.koudai.weidian.buyer
    frida -U -l .\hook.js  com.koudai.weidian.buyer  2>&1 | tee output.log 

    # 或者启动应用
    frida -U -f com.koudai.weidian.buyer -l hook.js --no-pause

    frida -U -l .\hook.js -f com.koudai.weidian.buyer  2>&1 | tee output.log 
*/

setTimeout(function() {
  Java.perform(function() {
    let UploadClient = Java.use("com.qiyukf.module.log.UploadClient");
    UploadClient["sign"].implementation = function (map, str) {
      console.log(`UploadClient.sign is called: map=${map}, str=${str}`);
      let result = this["sign"](map, str);
      console.log(`UploadClient.sign result=${result}`);
      return result;
    };
  })
}, 5000);

<!-- 这是一张图片,ocr 内容为: -->

  • 很显然什么日志都没有。我们再看一眼jadx发现这个函数居然没有调用。我们这时候怎么办呢?既然静态分析看不出来个啥,我们就动态分析,所以尝试了一下进行hook http的请求,能hook到就能看到堆栈。其余的就好办了。
  • 让Deepseek来生成一个hook 所有http请求的代码。(AI真的太香了)

/*
    # 附加到运行中的进程
    frida -U -l hook.js com.koudai.weidian.buyer
    frida -U -l .\hook.js  com.koudai.weidian.buyer  2>&1 | tee output.log 

    # 或者启动应用
    frida -U -f com.koudai.weidian.buyer -l hook.js --no-pause

    frida -U -l .\hook.js -f com.koudai.weidian.buyer  2>&1 | tee output.log 
*/

function printStackTrace() {
    var Exception = Java.use('java.lang.Exception');
    var exception = Exception.$new();
    var stackTrace = exception.getStackTrace();
    console.log(" Stack Trace:");
    for (var i = 0; i < stackTrace.length; i++) {
        console.log("\t" + stackTrace[i].toString());
    }
    exception.$dispose();
}
Java.perform(function() {
    // 只Hook POST请求,忽略GET
    try {
        var HttpURLConnection = Java.use('java.net.HttpURLConnection');

        // Hook 设置请求方法
        HttpURLConnection.setRequestMethod.overload('java.lang.String').implementation = function(method) {
            if (method === 'POST') {
                var urlStr = this.getURL().toString();
                if (urlStr.toLowerCase().includes("thor.weidian.com")){

                    console.log("\n" + "=".repeat(50));
                    console.log("📤 捕获到POST请求");
                    console.log("🔗 URL: " + this.getURL());
                    console.log("📝 请求方法: " + method);

                    console.log("\n📍 相关堆栈:");
                    printStackTrace();
                    console.log("=".repeat(50));
                }
            }
            return this.setRequestMethod(method);
        };
    } catch(e) {
        console.log("可能不是HttpURLConnection请求");
    }
});
  • 我们成功hook到了确认订单的请求,通过堆栈发现com.vdian.android.lib.protocol.thor.s.a这个函数是某店请求加密签名的函数。

<!-- 这是一张图片,ocr 内容为: -->

<!-- 这是一张图片,ocr 内容为: -->

<!-- 这是一张图片,ocr 内容为: -->

  • 我们通过看代码发现了加密解密方法跟签名方法。签名他们的字段都是e.xxxx怪不到我没搜索到,不过问题不大,条条大路通南北。
  • 我们观察到所有的加密解密,签名,压缩函数都在AdapteeManager 类里面。然后发现了一个某店单例com.vdian.android.lib.protocol.thor.ThorManager,可以直接在frida命令行进行调用查看了。

var ThorManager = Java.use("com.vdian.android.lib.protocol.thor.ThorManager");

加密类

ThorManager.getInstance().getAdapteeManager().getEncryptor()

解密类

ThorManager.getInstance().getAdapteeManager().getDecryptor()

签名类

ThorManager.getInstance().getAdapteeManager().getSigner()

压缩类

ThorManager.getInstance().getAdapteeManager().getZip()

解压类

ThorManager.getInstance().getAdapteeManager().getUnzip()

[MI MIX::com.koudai.weidian.buyer ]-> var thorMgr = Java.use("com.vdian.android.lib.protocol.thor.ThorManager");
var ThorManager = Java.use("com.vdian.android.lib.protocol.thor.ThorManager"); 
[MI MIX::com.koudai.weidian.buyer ]-> var ThorManager = Java.use("com.vdian.android.lib.protocol.thor.ThorManager");
ThorManager.getInstance().getAdapteeManager().getEncryptor() 
[MI MIX::com.koudai.weidian.buyer ]-> ThorManager.getInstance().getAdapteeManager().getEncryptor() 
"<instance: com.vdian.android.lib.adaptee.Encryptor, $className: com.vdian.android.lib.protocol.thor.m>"
ThorManager.getInstance().getAdapteeManager().getDecryptor() 
[MI MIX::com.koudai.weidian.buyer ]-> ThorManager.getInstance().getAdapteeManager().getDecryptor() 
"<instance: com.vdian.android.lib.adaptee.Decryptor, $className: com.vdian.android.lib.protocol.thor.g>"
ThorManager.getInstance().getAdapteeManager().getSigner() 
[MI MIX::com.koudai.weidian.buyer ]-> ThorManager.getInstance().getAdapteeManager().getSigner() 
"<instance: com.vdian.android.lib.adaptee.Signer, $className: com.vdian.android.lib.protocol.thor.ab>"
ThorManager.getInstance().getAdapteeManager().getZip() 
[MI MIX::com.koudai.weidian.buyer ]-> ThorManager.getInstance().getAdapteeManager().getZip() 
"<instance: com.vdian.android.lib.adaptee.Zip, $className: com.vdian.android.lib.adapter.n>"
ThorManager.getInstance().getAdapteeManager().getUnzip()
[MI MIX::com.koudai.weidian.buyer ]-> ThorManager.getInstance().getAdapteeManager().getUnzip()
"<instance: com.vdian.android.lib.adaptee.UnZip, $className: com.vdian.android.lib.adapter.v>"
    public b(Context context, String str) {
        this.checksum = new com.vdian.android.lib.adapter.j();
        this.zip = new com.vdian.android.lib.adapter.n();
        this.unzip = new com.vdian.android.lib.adapter.v();
        this.encoder = new com.vdian.android.lib.adapter.g();
        this.decoder = new VDBase64Decoder();
        if (str != null && str.length() > 0) {
            this.encryptor = new ah(context);
            this.decryptor = new ag(context);
            this.signer = new ai(context);
        } else {
            this.encryptor = new m(context);
            this.decryptor = new g(context);
            this.signer = new ab(context);
        }
        this.statisticsProvider = new com.vdian.android.lib.adapter.u();
        this.crashReporter = new com.vdian.android.lib.adapter.i();
    }
==================================================================================
package com.vdian.android.lib.protocol.thor;

import android.content.Context;
import android.util.Log;
import java.util.Map;

/* loaded from: classes4.dex */
public final class ThorNativeBridge {
    private static native String native_abi();

    private static native byte[] native_d(Context context, String str, byte[] bArr);

    private static native byte[] native_e(Context context, String str, byte[] bArr);

    private static native String native_s(Context context, String str, Map<String, String> map);

    private static native String native_s_a(Context context, String str);

    private static native byte[] native_s_d(Context context, String str, byte[] bArr);

    private static native byte[] native_s_e(Context context, String str, byte[] bArr);

    private static native String native_s_s(Context context, String str, Map<String, String> map);

    private static native String native_v();
  • 我们首先处理压缩函数,因为在请求第一步对参数进行压缩,我们需要看下压缩之前是什么字符串。同时处理各种秘钥。

/*
    # 附加到运行中的进程
    frida -U -l hook.js com.koudai.weidian.buyer
    frida -U -l .\hook.js  com.koudai.weidian.buyer  2>&1 | tee output.log 

    # 或者启动应用
    frida -U -f com.koudai.weidian.buyer -l hook.js --no-pause

    frida -U -l .\hook.js -f com.koudai.weidian.buyer  2>&1 | tee output.log 
*/

// byte[] 转字符串的通用函数
function byteArrayToString(byteArray, encoding) {
    if (!byteArray) return "";

    encoding = encoding || "UTF-8";

    try {
        var String = Java.use('java.lang.String');
        return String.$new(byteArray, encoding).toString();
    } catch(e) {
        // 如果转换失败,返回十六进制
        return bytesToHex(byteArray);
    }
}

// byte[] 转十六进制字符串
function bytesToHex(byteArray) {
    var result = "";
    var bytes = Java.array('byte', byteArray);

    for (var i = 0; i < bytes.length; i++) {
        var hex = (bytes[i] & 0xFF).toString(16);
        if (hex.length === 1) hex = "0" + hex;
        result += hex;
        if ((i + 1) % 2 === 0) result += " "; // 每两个字节加空格
    }
    return result.toUpperCase();
}

function printStackTrace() {
    var Exception = Java.use('java.lang.Exception');
    var exception = Exception.$new();
    var stackTrace = exception.getStackTrace();
    console.log(" Stack Trace:");
    for (var i = 0; i < stackTrace.length; i++) {
        console.log("\t" + stackTrace[i].toString());
    }
    exception.$dispose();
}

function hookHttpPost(){
    // 只Hook POST请求,忽略GET
    try {
        var HttpURLConnection = Java.use('java.net.HttpURLConnection');

        // Hook 设置请求方法
        HttpURLConnection.setRequestMethod.overload('java.lang.String').implementation = function(method) {
            if (method === 'POST') {
                var urlStr = this.getURL().toString();
                if (urlStr.toLowerCase().includes("thor.weidian.com")){

                    // console.log("\n" + "=".repeat(50));
                    // console.log("📤 捕获到POST请求");
                    console.log("hook http post URL: " + this.getURL());
                    // console.log("📝 请求方法: " + method);

                    // console.log("\n📍 相关堆栈:");
                    // printStackTrace();
                    // console.log("=".repeat(50));
                }
            }
            return this.setRequestMethod(method);
        };
    } catch(e) {
        console.log("可能不是HttpURLConnection请求");
    }
}

Java.perform(function() {
    hookHttpPost();
    let n = Java.use("com.vdian.android.lib.adapter.n");
    n["zip"].implementation = function (bArr) {
        // hook压缩
        console.log(`n.zip is called: bArr=${Java.use('java.lang.String').$new(bArr)}`);
        let result = this["zip"](bArr);
        console.log(`n.zip result=${bytesToHex(result)}`);
        return result;
    };

    let ThorNativeBridge = Java.use("com.vdian.android.lib.protocol.thor.ThorNativeBridge");
    ThorNativeBridge["s"].implementation = function (context, str, map) {
        // hook 签名
        console.log(`hook签名成功,签名秘钥: ${str}`);
        let result = this["s"](context, str, map);
        // console.log(`ThorNativeBridge.s result=${result}`);
        return result;
    };

    ThorNativeBridge["e"].implementation = function (context, str, bArr) {
        console.log(`hook加密成功,加密秘钥: ${str}`);
        let result = this["e"](context, str, bArr);
        // console.log(`ThorNativeBridge.e result=${result}`);
        return result;
    };

    ThorNativeBridge["d"].implementation = function (context, str, bArr) {
        console.log(`hook解密成功,解密秘钥: ${str}`);
        // console.log(`ThorNativeBridge.d is called: context=${context}, str=${str}, bArr=${bArr}`);
        let result = this["d"](context, str, bArr);
        // console.log(`ThorNativeBridge.d result=${result}`);
        return result;
    };
});
  • 手机上操作了下请求订单,至此我们已经获取到了请求订单的param跟context。通过hook的数据,我们写代码测试发现是gzip的压缩解压方式。
[MI MIX::com.koudai.weidian.buyer ]-> n.zip is called: bArr={"alt":"","android_id":"440a1a6ec3e025cf","app_status":"active","appid":"com.koudai.weidian.buyer","appv":"7.4.1","brand":"Xiaomi","build":"20231218102011","channel":"1042n","cuid":"d9e5e97dc6c4adcf0ff0de06e771aa47","disk_capacity":"108.73GB/112.41GB","duid":"1823334179","feature":"E|F,H|F,P|F,R|T,W|F","h":"2004","iccid":"","imei":"","imsi":"","is_login":"1","lat":"","login_token":"_EwWqqVIQYjjzet1vcrFL5Ws3Qa_bg-HqhhYDD7r3qoT053j56LiU2F7FVMewu54u_OW5gmHFXp0wp73VzQ2TFXGXiGBXvmZqfu5M3IGZ5nlMOws1Aw-JBKSpfFi3v8ymBwYpZiRcfDn-Yr3Ty6Eund6PCw4jOqGA_PtBmCrBHPTW0UnY3Wwe7p9ZUNMGLj-c4gPpS5whn5PF02mAFnCYNhdsGNkohJE4kme3RfsVjpLtNp1jBwUHrlb_w5KuCRsm88_fovNF","lon":"","mac":"00:00:00:00:00:00","machine_model":"aarch64","memory":"1306M/3739M","mid":"MI_MIX","mobile_station":"0","net_subtype":"0_","network":"WIFI","oaid":"","os":"30","platform":"android","serial_num":"","shop_id":"1794871849","suid":"d9e5e97dc6c4adcf0ff0de06e771aa47","uid":"1823334179","w":"1080","wmac":"02:00:00:00:00:00","wssid":"<unknown ssid>"}
n.zip result=1F8B 0800 0000 0000 0000 8D53 5B6F DA30 14FE 2B13 CF85 DA71 2E50 4D93 1A4A 026D 4329 E5FE 1219 C721 86C4 0EB9 60D1 75FF 7D76 D23E 4CDB C3A4 583A DF77 6ECE 399F 7F76 705A 75EE 3A9D 9B0E E651 2158 14B2 4861 D304 1862 9B12 4481 6191 58FB F33C 2C2B 5CD5 A5F2 6352 B10B 6DD9 2681 88AC 7712 7584 594F 5216 31CC 7BFB FA4A 8B36 E4A2 229C 9ED9 830A EE0B D549 E10D C322 639A A859 AA09 0318 081A B00F 8101 A08E 2409 E69C A6CA 0581 6970 CDD4 4DB3 6840 2D3A 7022 6213 1347 2406 710C 220A 6CEA 3810 63D3 5191 112B 4F21 C139 26AC BA36 15FA 3D07 F9EE 2D84 46CF 84BE AB63 DA6A B06F 2084 4CE8 0C14 1753 F587 0555 F4E8 C3BB 19AB 3353 67FE B1B8 597F 78CA 9F34 1705 A632 1921 4DBE 3633 CABE ACF2 CB2A C354 1C18 D71D 144C F1E7 9C1B 32AC C489 6A57 3892 EBF3 7935 79DD 1E8F EFB4 8217 5278 CFD6 BA44 AF38 DC1F BAE3 7392 6C1F 1E9C 029D C502 58E8 68D9 CF6C 6978 8EB7 0AA8 AC2D B30E 5FD6 D621 1B7B 9B1C C8DC 41AB F757 63E1 6DFC 0DF3 DDCD 25DB 9DE3 
DA0A D0C4 DF59 3C0D 5E64 09EF 65F7 D17D 7ACB 638F A14B FF9A B972 9BEF D89C C40F BCBB 2DD0 E26A 8F6A 1ED9 B3A1 348F 2F67 FF3E 9C55 6E36 2CDC F16C B106 4BBE 456B 499D 7CB0 5B4E 03FF F9D8 25E6 6196 BF59 32E1 D6CC 0346 76EF F1E1 769A 44A5 3F3D 89E4 7164 9E32 8AE6 71B9 3AE6 CFD5 3487 4757 2EC7 45BA 0FA5 F554 0FE7 65D6 EF87 B1B8 4CBD 6632 BC1D 5186 8932 00B8 FBF3 6B3D 09E3 34CC 44D4 0803 E382 24B6 DE46 4633 5134 9B46 C00E 6E91 8306 81A6 9B15 0593 3098 6C34 147B 96D2 46C7 ACE9 A68B 725A 8565 BDAF AEB9 DE3B 085B 4A8A E2A4 E07A E24D 1421 F0D7 AE85 D63F D279 B9DA 692C 8A4C DFA3 7D3C 8A2C 69C1 701A F23A 6BC3 CB44 E4ED 9B52 0233 FB0E EC9B 5A67 E57F 2BF9 1F22 95AD A0F5 1DE4 E7AC 8CBF 6725 CBB2 49FD 5EF3 1317 927F D3F8 47E7 D76F C9AE 7445 F403 0000
hook加密成功,加密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
n.zip is called: bArr={"shopItemInfos":[{"groupId":"1751062078","isOverseasSupplier":0,"itemInfos":[{"itemId":"7626627049","itemImg":"https://si.geilicdn.com/ch1743699958-38a10000019af371c6780a8133cc_1600_2133.jpg?w=110&h=110&cp=1","point":"0","price":"86.5","skuId":"127538921761"},{"itemId":"7626627049","itemImg":"https://si.geilicdn.com/ch1743699958-38a10000019af371c6780a8133cc_1600_2133.jpg?w=110&h=110&cp=1","point":"0","price":"71.5","skuId":"127539139762"}],"sellerId":"1751062078"}]}
n.zip result=1F8B 0800 0000 0000 0000 CD51 BB6E C330 0CFC 170E 9D52 9B94 623D 0C04 9D3D 75E8 5804 81A1 28B6 52DB 1224 BB1D 02FF 7BE5 B44B DB1F 2807 E2EE 4882 47F0 06A9 F7A1 99ED D84C 179F A07E BD41 17FD 129A 33D4 40B2 2214 0CA5 821D B8F4 FC6E 63B2 6D7A 5942 189C 8D50 6396 7FCC DED9 362A 0513 8249 DC6B F8EE 19BB 2CF7 F31C 525D 96C9 159D 7583 33E7 A930 7E2C 4D4F 72CF 85D6 BA52 8F5C B584 5B90 6E2F 5C92 1152 61AB 8873 634E 2410 4F2C E3E2 1ABA A78F 0311 3EF4 F76C C281 F2B2 E0DD 34E7 55B8 E1E8 8CCD 5889 A2CA 34BD 2D5F 7731 5971 A519 4941 B0EE FEAD 6D49 7F6D 6BE2 3ABB 84F5 980B 7618 6CFC FDAA F5B8 7E02 03F5 687B D701 0000
hook加密成功,加密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
hook签名成功,签名秘钥: 2a7d5d1d3c0a4df08bfbacd58fb0748d
hook http post URL: https://thor.weidian.com/vcart/separateBuy/1.0
hook解密成功,解密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
n.zip is called: bArr={"alt":"","android_id":"440a1a6ec3e025cf","app_status":"active","appid":"com.koudai.weidian.buyer","appv":"7.4.1","brand":"Xiaomi","build":"20231218102011","channel":"1042n","cuid":"d9e5e97dc6c4adcf0ff0de06e771aa47","disk_capacity":"108.73GB/112.41GB","duid":"1823334179","feature":"E|F,H|F,P|F,R|T,W|F","h":"2004","iccid":"","imei":"","imsi":"","is_login":"1","lat":"","login_token":"_EwWqqVIQYjjzet1vcrFL5Ws3Qa_bg-HqhhYDD7r3qoT053j56LiU2F7FVMewu54u_OW5gmHFXp0wp73VzQ2TFXGXiGBXvmZqfu5M3IGZ5nlMOws1Aw-JBKSpfFi3v8ymBwYpZiRcfDn-Yr3Ty6Eund6PCw4jOqGA_PtBmCrBHPTW0UnY3Wwe7p9ZUNMGLj-c4gPpS5whn5PF02mAFnCYNhdsGNkohJE4kme3RfsVjpLtNp1jBwUHrlb_w5KuCRsm88_fovNF","lon":"","mac":"00:00:00:00:00:00","machine_model":"aarch64","memory":"1304M/3739M","mid":"MI_MIX","mobile_station":"0","net_subtype":"0_","network":"WIFI","oaid":"","os":"30","platform":"android","serial_num":"","shop_id":"1794871849","suid":"d9e5e97dc6c4adcf0ff0de06e771aa47","uid":"1823334179","w":"1080","wmac":"02:00:00:00:00:00","wssid":"<unknown ssid>"}
n.zip result=1F8B 0800 0000 0000 0000 8D53 5B6F 9B30 14FE 2B53 9E9B D4C6 1048 354D 2A69 2069 4B9A A6B9 BF20 C798 E004 6CC2 2556 BAEE BFCF 86F6 61DA 1E26 61E9 7CDF B999 733E FFEC E0B4 EADC 753A 371D CCA3 42B0 2864 91C2 A609 30C4 7D4A 1005 8645 62ED CFF3 B0AC 7055 97CA 8F49 C52E B465 9B04 22B2 DE49 D411 663D 4959 C430 EFED EB2B 2DDA 908B 8AB0 7B66 0F2A B82F 5427 8537 0C8B 8C69 A266 A926 0C60 2068 4007 0203 401D 4912 CC39 4D95 0B02 D3E0 9AA9 9B66 D180 5A74 6047 A44F 4C1C 9118 C431 8828 E853 DB86 189B B68A 8C58 790A 09CE 3161 D5B5 A9E0 F46C E4BB B710 1A3D 13FA AE8E 69AB 41C7 4008 99D0 1E28 2EA6 EA0F 0BAA E8D1 8777 3356 67A6 CEFC 6371 B3FE F094 3F69 2E0A 4C65 3242 9A7C 6D66 947D 59E5 9755 86A9 3830 AE3B 2898 E2CF 3937 6458 8913 D5AE 7024 D7E7 F36A F2BA 3D1E DF69 052F A4F0 9EAD 7589 5E71 B83F 74C7 E724 D93E 3CD8 053A 8B05 B0D0 D1EA 3FB3 A5E1 D9DE 2AA0 B2B6 CC3A 7C59 5B87 6CEC 6D72 2073 1BAD DE5F 8D85 B7F1 37CC 7737 976C 778E 
6B2B 4013 7F67 F134 7891 25BC 97DD 47F7 E92D 8F3D 862E CE35 73E5 36DF B139 891F 7877 5BA0 C5B5 3FAA 79D4 9F0D A579 7C39 FBF7 E1AC 72B3 61E1 8E67 8B35 58F2 2D5A 4B6A E783 DD72 1AF8 CFC7 2E31 0FB3 FCCD 9209 B766 1E30 B27B 8F0F B7D3 242A FDE9 4924 8F23 F394 5134 8FCB D531 7FAE A639 3CBA 7239 2ED2 7D28 ADA7 7A38 2F33 C709 6371 997A CD64 783B A20C 1365 0070 F7E7 D77A 12C6 6998 89A8 1106 C605 49FA 7A1B 19CD 44D1 6C1A 0133 B845 361A 049A 6E56 144C C260 B2D1 50EC 594A 1B1D B3A6 9B2E CA69 1596 F5BE BAE6 7AEF 206C 2929 8A93 82EB 8937 5184 C05F BB16 5AFF 48E7 E56A A7B1 2832 7D8F F6F1 28B2 A405 C369 C8EB AC0D 2F13 91B7 6F4A 09CC 746C E898 5A67 E57F 2BF9 1F22 95AD A0F5 1DE4 E7AC 8CBF 6725 CBB2 49FD 5EF3 1317 927F D3F8 47E7 D76F FF0A 82BF F403 0000
hook加密成功,加密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
n.zip is called: bArr={"channel":"maijiaban","item_list":[{"item_id":"7626627049","item_sku_id":"127538921761","item_type":"0","price_type":"1","quantity":2,"use_installment":1},{"item_id":"7626627049","item_sku_id":"127539139762","item_type":"0","price_type":"1","quantity":1,"use_installment":1}],"sdk_version":"5.8.7","source_id":"5774937da7853cd3860c022c7fb13f84","wfr":""}
n.zip result=1F8B 0800 0000 0000 0000 958D 410E 8230 1045 EF32 6B62 680B 9D96 AB18 436A 2971 142A D2A2 31C6 BB3B 6AD8 B971 F9E7 BF3F EF01 FEE0 620C 0334 303A 3A92 DBBB 0805 500E 633B 50CA D06C 1FDF 441D 33A8 A5D6 12CB CAAE 503A 2DDF 4A48 AC95 B152 A016 6B99 EF53 E0AA E43C CDE4 C37A 7803 97C5 C54C F90E 8D2C 6049 A1A5 98B2 1B86 3144 B68A 67F1 8FD7 0A65 99F9 CF2B 7E7A 7705 A4EE D45E C39C E81C 7954 6FCC 0679 98CE CBCC AF3E D21A B1B2 0A3B 87A6 56BE 5346 97BE 94D2 63BF 17AA 3715 E3B7 7E66 109E 2F9D 784D 2263 0100 00
hook加密成功,加密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
hook签名成功,签名秘钥: 2a7d5d1d3c0a4df08bfbacd58fb0748d
hook http post URL: https://thor.weidian.com/vbuy/ConfirmOrder/1.0
hook解密成功,解密秘钥: 6cec4a8b268b8749dfa0bd409effcd08
加密解密:6cec4a8b268b8749dfa0bd409effcd08
签名秘钥:2a7d5d1d3c0a4df08bfbacd58fb0748d
  • 至此我们只需要分析加密解密方式还有签名方式,这些逻辑搞定就可以实现独立脚本抢购。发现这些所有逻辑都在native里面,我们需要进行反编译so文件了。

反编译libthor.so文件

  • 解压apk,在~\lib\arm64-v8a\libthor.so找到so文件,使用ghidra工具打开。
  • Language选择AARCH64 这个对应arm64-v8a。
Format: ELF (Executable and Linking Format) for Linux/Android

Language: ARM:LE:32:v8 (如果是32位)
或
Language: AARCH64:LE:64:v8A (如果是64位)
  • Ctrl+T进行搜索函数名。
F5: 反编译当前函数
G: 跳转到地址
Ctrl+G: 转到地址对话框
Ctrl+Shift+F: 搜索文本
Ctrl+E: 导出数据
Ctrl+Shift+E: 符号表
Ctrl+L: 创建标签
Ctrl+R: 重命名
;: 添加注释

<!-- 这是一张图片,ocr 内容为: -->

  • 找到函数后跳转到函数,按F5进行反编译查看伪代码。生成的伪代码我们肯定是看不懂的,这时候就得使用AI了。把代码复制给Deepseek让他进行解读,尝试算法还原。这个过程很痛苦,再试错误。好在功夫不负有心人宗宇获取到了加密方式
AES加密算法还原
确定算法参数:
算法: AES-128-ECB
密钥长度: 16字节(32字符十六进制字符串) 6cec4a8b268b8749dfa0bd409effcd08
填充: PKCS7
工作模式: ECB

<!-- 这是一张图片,ocr 内容为: -->

  • 复原签名函数,签名函数复原过程过于曲折。因为是反编译出来的伪代码,拼接字符串逻辑比较复杂,少一个多一个符号md5就不一致。一直翻译不正确签名前的字符串。后来通过hook md5的函数才发现了字符串的真面目。
    • 首先我们hook native_s查看参数跟返回值。
    • 然后我们查看反编译so的伪代码,发现使用的是mbedtls_md5。我们直接hook看番数

function hookMd5(){
  // Hook mbedtls_md5函数
  var mbedtls_md5_addr = Module.findExportByName("libthor.so", "mbedtls_md5");
  if (mbedtls_md5_addr) {
    console.log("[+] Found mbedtls_md5 at: " + mbedtls_md5_addr);

    Interceptor.attach(mbedtls_md5_addr, {
      onEnter: function(args) {
        console.log("\n=== mbedtls_md5 called ===");
        console.log("param_1 (data pointer): " + args[0]);
        console.log("param_2 (data length): " + args[1]);
        console.log("param_3 (output buffer): " + args[2]);

        // 保存参数供onLeave使用
        this.dataPtr = args[0];
        this.dataLen = args[1].toInt32();
        this.outputPtr = args[2];

        // 打印输入数据
        if (this.dataPtr && this.dataLen > 0) {
          var inputData = ptr(this.dataPtr).readByteArray(this.dataLen);
          // console.log("Input data (hex): " + Array.from(new Uint8Array(inputData)).map(b => b.toString(16).padStart(2, '0')).join(''));
          console.log("Input data (string): " + Memory.readUtf8String(this.dataPtr, this.dataLen));
        }
      },
      onLeave: function(retval) {
        console.log("\n=== mbedtls_md5 result ===");

        // 读取MD5结果(16字节)
        if (this.outputPtr) {
          var md5Result = ptr(this.outputPtr).readByteArray(16);
          if (md5Result) {
            var hexResult = Array.from(new Uint8Array(md5Result)).map(b => b.toString(16).padStart(2, '0')).join('');
            console.log("MD5 Result: " + hexResult);
          }
        }

        console.log("=== mbedtls_md5 end ===\n");
      }
    });
  } else {
    console.log("[-] mbedtls_md5 not found!");
  }
}

还原login加密参数

  • 我们抓包得知login账号密码登录,密码是加密进行发送的,原本以为是简单的md5后来发现还挺复杂。
{
    "buyerPassword": "14a2f7d48c81ecd92cd17852ec2f6d81",
    "countryCode": "86",
    "phone": "18435110264",
    "sellerPassword": "dbcffe8ee683c53feb4e657c0555d195"
}
  • 下面是jadx反编译的代码
public class ACModifyPwdRequest extends ACAbsRequest {
    public String action;
    public String code;
    public String phone;
    public String pwd;
    public String smsCode;

    @Override // com.vdian.android.lib.wdaccount.core.request.ACAbsRequest
    public Class<? extends ACAbsResponse> getResponse() {
        if (WDBUploadAgent.SCOPE_LOGIN.equals(this.action)) {
            return ACLoginRelationResponse.class;
        }
        return ACVoidResponse.class;
    }

    @Override // com.vdian.android.lib.wdaccount.core.request.ACAbsRequest
    public HashMap<String, String> getParam() {
        this.param.put("phone", this.phone);
        this.param.put("countryCode", this.code);
        this.param.put("buyerPassword", d.b(this.pwd));
        HashMap<String, String> hashMap = this.param;
        hashMap.put("sellerPassword", d.c(c.h + this.pwd));
        this.param.put("vcode", this.smsCode);
        this.param.put("action", this.action);
        return this.param;
    }
}
    public static String b(String str) {
        if (str == null || str.length() == 0) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        String b = b.b(str);
        int length = b.length();
        for (int i = 0; i < length; i++) {
            sb.append((char) (b.charAt(i) + (i % 3)));
        }
        sb.append(c.i);
        String c2 = c(sb.toString());
        return c2 == null ? str : c2;
    }

    public static String c(String str) {
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.reset();
            messageDigest.update(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            a().e("ACUtil getMD5Str error", e);
        } catch (NoSuchAlgorithmException e2) {
            a().e("ACUtil getMD5Str error", e2);
            System.exit(-1);
        }
        byte[] digest = messageDigest.digest();
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < digest.length; i++) {
            if (Integer.toHexString(digest[i] & 255).length() == 1) {
                stringBuffer.append("0");
                stringBuffer.append(Integer.toHexString(digest[i] & 255));
            } else {
                stringBuffer.append(Integer.toHexString(digest[i] & 255));
            }
        }
        return stringBuffer.toString();
    }
  • 直接交给ai进行代码生成。
  • 至此我们压缩解压,加密解密,签名都搞定。剩下就是复原代码,写逻辑了。

0x04踩坑经历

抓包坑

  • android 7以上无法抓包。TODO:后续再完善。
  • android 9一下root后面具无法安装LSP插件。

frida坑

  • 版本不匹配,架构使用不对坑
  • 使用Module.findExportByName("libthor.so", "xxx");无法找到函数。也是架构问题虚拟机都是x86_64,但是某店只有arm64架构的,所以转译相关问题目前还没解决。只能用arm64的手机

签名md5字符串问题

  • 一直无法获取到正确的md5加密签的字符串

创建订单有个风控参数risk_sign始终找不到代码

private RiskControl x() {
    RiskControl riskControl = new RiskControl();
    if (TextUtils.isEmpty(this.ay)) {
        StringBuilder sb = new StringBuilder();
        sb.append("KD");
        sb.append(new Date().getTime());
        sb.append("_");
        sb.append(("" + Math.random()).substring(2));
        riskControl.setRiskSign(sb.toString());
    } else {
        riskControl.setRiskSign(this.ay);
    }
    return riskControl;
}
  • 发现还有订单代码也找不到,很多业务逻辑代码都找不到。
  • 经查询发现这些都属于动态加载模块,可以再手机的根目录下进行搜索。把搜索出来的apk都加到jadx中就能看到完整代码了。
lithium:/ # find /data/data/com.koudai.weidian.buyer -name *.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.im/7.3.9.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.compat/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.main/7.3.2.1/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.webview/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.shop/7.2.5.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.flutterwrap/7.4.1.3/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.qrcode/7.3.9.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.payment/7.3.2.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.weex/6.9.8.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.order/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.share/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.splash/7.1.7.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.homepage/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.follow/7.2.2.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.push/7.3.0.2/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.goods/7.3.6.1/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.talk/7.3.6.1/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.search/7.4.1.1/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.rxkotlin/7.2.8.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.transaction/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.vdlive/7.3.0.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.my/7.4.1.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.update/7.1.7.0/bundle.apk
/data/data/com.koudai.weidian.buyer/app_plugins/com.vdian.android.wdb.vdtrick/7.2.7.0/bundle.apk

彩蛋

  • 加密秘钥,签名秘钥都是在jadx中常量保存的可以直接搜到。

0x05总结

<font style="color:rgb(31, 35, 40);">本文完整的记录了对于Apk与服务器交互API的解析过程,包括环境搭建、抓包、反编译静态分析、frida hook动态分析。本文算是第一次深入且成功的用动态+静态分析结合的方式,借助神器frida+jadx,成功破解Apk,因此本文的记录也较为细致的记录了的思考过程,可以提供参考。</font>

<font style="color:rgb(31, 35, 40);">本文也有一些不足之处,如无法使用模拟器进行hook,高版本安卓抓包痛点。对于这些问题,如果未来有时间,会再回来填坑。</font>

<font style="color:rgb(31, 35, 40);"></font>

0x06代码

hook代码


/*
    # 附加到运行中的进程
    frida -U -l hook.js com.koudai.weidian.buyer
    frida -U -l .\hook.js  com.koudai.weidian.buyer  2>&1 | tee output.log 

    # 或者启动应用
    frida -U -f com.koudai.weidian.buyer -l hook.js --no-pause

    frida -U -l .\hook.js -f com.koudai.weidian.buyer  2>&1 | tee output.log 
*/

// byte[] 转字符串的通用函数
function byteArrayToString(byteArray, encoding) {
  if (!byteArray) return "";

  encoding = encoding || "UTF-8";

  try {
    var String = Java.use('java.lang.String');
    return String.$new(byteArray, encoding).toString();
  } catch(e) {
    // 如果转换失败,返回十六进制
    return bytesToHex(byteArray);
  }
}

// byte[] 转十六进制字符串
function bytesToHex(byteArray) {
  var result = "";
  var bytes = Java.array('byte', byteArray);

  for (var i = 0; i < bytes.length; i++) {
    var hex = (bytes[i] & 0xFF).toString(16);
    if (hex.length === 1) hex = "0" + hex;
    result += hex;
    if ((i + 1) % 2 === 0) result += " "; // 每两个字节加空格
  }
  return result.toUpperCase();
}

function mapToJsonWithJSONObject(jmap) {
  try {
    // 使用Android自带的JSONObject
    var JSONObject = Java.use('org.json.JSONObject');

    // 将Map转换为JSONObject
    var jsonObj = JSONObject.$new(jmap);
    var jsonStr = jsonObj.toString();

    // 格式化输出
    return jsonStr;
    // return JSON.stringify(JSON.parse(jsonStr), null, 2);
  } catch(e) {
    console.log("使用JSONObject失败: " + e);
    return null;
  }
}

function printStackTrace() {
  var Exception = Java.use('java.lang.Exception');
  var exception = Exception.$new();
  var stackTrace = exception.getStackTrace();
  console.log(" Stack Trace:");
  for (var i = 0; i < stackTrace.length; i++) {
    console.log("\t" + stackTrace[i].toString());
  }
  exception.$dispose();
}

function hookHttpPost(){
  // 只Hook POST请求,忽略GET
  try {
    var HttpURLConnection = Java.use('java.net.HttpURLConnection');

    // Hook 设置请求方法
    HttpURLConnection.setRequestMethod.overload('java.lang.String').implementation = function(method) {
      if (method === 'POST') {
        var urlStr = this.getURL().toString();
        if (urlStr.toLowerCase().includes("thor.weidian.com")){

          // console.log("\n" + "=".repeat(50));
          // console.log("📤 捕获到POST请求");
          console.log("hook http post URL: " + this.getURL());
          // console.log("📝 请求方法: " + method);

          // console.log("\n📍 相关堆栈:");
          // printStackTrace();
          // console.log("=".repeat(50));
        }
      }
      return this.setRequestMethod(method);
    };
  } catch(e) {
    console.log("可能不是HttpURLConnection请求");
  }
}

function HookNative(){
  hookHttpPost();
  let n = Java.use("com.vdian.android.lib.adapter.n");
    n["zip"].implementation = function (bArr) {
        // hook压缩
        console.log(`n.zip is called: bArr=${Java.use('java.lang.String').$new(bArr)}`);
        let result = this["zip"](bArr);
        console.log(`n.zip result=${bytesToHex(result)}`);
        return result;
    };

    let ThorNativeBridge = Java.use("com.vdian.android.lib.protocol.thor.ThorNativeBridge");
    ThorNativeBridge["s"].implementation = function (context, str, map) {
        // hook 签名
        console.log(`hook签名成功,签名秘钥: ${str}`);
        var mapStr = mapToJsonWithJSONObject(map);
        console.log(`hook签名成功,map: ${mapStr}`);
        let result = this["s"](context, str, map);
        // console.log(`ThorNativeBridge.s result=${result}`);
        return result;
    };

    ThorNativeBridge["e"].implementation = function (context, str, bArr) {
        console.log(`hook加密成功,加密秘钥: ${str}`);
        let result = this["e"](context, str, bArr);
        // console.log(`ThorNativeBridge.e result=${result}`);
        return result;
    };

    ThorNativeBridge["d"].implementation = function (context, str, bArr) {
        console.log(`hook解密成功,解密秘钥: ${str}`);
        // console.log(`ThorNativeBridge.d is called: context=${context}, str=${str}, bArr=${bArr}`);
        let result = this["d"](context, str, bArr);
        // console.log(`ThorNativeBridge.d result=${result}`);
        return result;
    };
}

// 智能读取字符串数据
function readStringData(pointer, length) {
    if (pointer.isNull()) return null;

    try {
        // 先尝试读取C字符串
        var cStr = pointer.readCString();

        // 如果读取到的C字符串长度小于指定长度,可能字符串中有\0
        if (cStr && cStr.length < length) {
            return cStr + " [truncated at null byte]";
        }

        return cStr;
    } catch(e) {
        // 如果读取C字符串失败,尝试按指定长度读取
        try {
            return pointer.readUtf8String(Math.min(length, 1024)); // 限制最大长度
        } catch(e2) {
            // 如果还是失败,返回十六进制
            var data = Memory.readByteArray(pointer, Math.min(length, 64));
            return "[Non-string data] " + bytesToHex(data);
        }
    }
}

function hookMd5(){
    // Hook mbedtls_md5函数
    var mbedtls_md5_addr = Module.findExportByName("libthor.so", "mbedtls_md5");
    if (mbedtls_md5_addr) {
        console.log("[+] Found mbedtls_md5 at: " + mbedtls_md5_addr);

        Interceptor.attach(mbedtls_md5_addr, {
            onEnter: function(args) {
                console.log("\n=== mbedtls_md5 called ===");
                console.log("param_1 (data pointer): " + args[0]);
                console.log("param_2 (data length): " + args[1]);
                console.log("param_3 (output buffer): " + args[2]);

                // 保存参数供onLeave使用
                this.dataPtr = args[0];
                this.dataLen = args[1].toInt32();
                this.outputPtr = args[2];

                // 打印输入数据
                if (this.dataPtr && this.dataLen > 0) {
                    var inputData = ptr(this.dataPtr).readByteArray(this.dataLen);
                    // console.log("Input data (hex): " + Array.from(new Uint8Array(inputData)).map(b => b.toString(16).padStart(2, '0')).join(''));
                    console.log("Input data (string): " + Memory.readUtf8String(this.dataPtr, this.dataLen));
                }
            },
            onLeave: function(retval) {
                console.log("\n=== mbedtls_md5 result ===");

                // 读取MD5结果(16字节)
                if (this.outputPtr) {
                    var md5Result = ptr(this.outputPtr).readByteArray(16);
                    if (md5Result) {
                        var hexResult = Array.from(new Uint8Array(md5Result)).map(b => b.toString(16).padStart(2, '0')).join('');
                        console.log("MD5 Result: " + hexResult);
                    }
                }

                console.log("=== mbedtls_md5 end ===\n");
            }
        });
    } else {
        console.log("[-] mbedtls_md5 not found!");
    }
}

Java.perform(function() {
    hookMd5();    
});

// 附加脚本到进程
console.log("Frida script loaded successfully!");

抢购脚本

https://github.com/yanmf/thor_seckill.git

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
allspark + 1 + 1 用心讨论,共获提升!
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
xoyi 发表于 2026-3-31 12:55
git地址404了
3#
kll545012 发表于 2026-3-31 14:54
<!-- 这是一张图片,ocr 内容为: -->帖子中的这个信息,是图挂了?
4#
xiugou 发表于 2026-3-31 15:57
5#
cymtslx 发表于 2026-3-31 16:36
真大佬,厉害
6#
zzp28218 发表于 2026-3-31 17:50

可以,很详细
7#
fast123 发表于 2026-3-31 17:51
不错的教程很好
8#
cadd 发表于 2026-3-31 19:20
好帖,感谢分享
9#
fzlte0 发表于 2026-3-31 21:00
从java到so,学习了。
10#
Ligengxi0709 发表于 2026-3-31 23:21
教程清晰,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-12 04:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表