当前位置: 首页 > news >正文

京东协议算法最新版

环境准备

1

com.jingdong.app.mall==11.6.4

入口定位

逆向分析,发现 params 里面有一个 sign 以及请求头里面有一个 jdgs

首先我们发现京东的 sign 是 32 位的,猜测其可能是 md5 之类的 hash 算法,既然是 hash 算法,那么就大概率会用到 getBytes 方法,我们首先 hook 一下 java.lang.String 的 getBytes 方法,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 获取 Java 的字符串

    const str = Java.use('java.lang.String');

    // 重载  getBytes 方法 (因为常用的 Hash 算法一般都会调用字符串的 getBytes 方法)

    str.getBytes.overload().implementation = function () {

        var response = this.getBytes()

        var str1 = this.toString();

        // 如果 string 里面包含了 functionId (其他关键字也可以,需要自己尝试)

        if (str1.indexOf("functionId") >= 0) {

            // 输出找到了字符串

            console.log("find string:", str1);

            // 打印一下堆栈

                console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

        }

        return response;

    }

使用 firda 加载脚本后进入详情页就发现出现了以下堆栈

1

2

3

4

5

6

7

8

9

10

11

12

13

14

java.lang.Throwable

        at java.lang.String.getBytes(Native Method)

        at com.jd.security.jdguard.a.e.o(SourceFile:15)

        at com.jd.security.jdguard.a.e.i(SourceFile:3)

        at com.jd.security.jdguard.a.c.a(SourceFile:10)

        at com.jd.security.jdguard.a.c.b(SourceFile:4)

        at com.jingdong.common.guard.JDGuardHelper$1.genSign(SourceFile:1)

        at com.jingdong.jdsdk.network.toolbox.HttpSettingTool.doSignUsingJdGuard(SourceFile:10)

        at com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.setupParams(SourceFile:23)

        at com.jingdong.jdsdk.network.toolbox.HttpSettingTool.setupParams(SourceFile:2)

        at com.jingdong.jdsdk.network.toolbox.HttpGroupAdapter$RequestTask.run(SourceFile:14)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)

        at java.lang.Thread.run(Thread.java:919)

通过以上堆栈我们发现了疑似入口为:com.jingdong.common.guard.JDGuardHelper$1.genSign, jadx 源码如下:

1

2

3

public Map<String, String> genSign(URI uri, byte[] bArr, String str, String str2, boolean z) {

    return c.b(uri, bArr, str, str2, z);

}

因此我们再次 hook 验证,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

const jString = Java.use("java.lang.String");

let IJDGuardPlugin = Java.use("com.jingdong.common.guard.JDGuardHelper$1");

IJDGuardPlugin["genSign"].implementation = function (uri, bArr, str, str2, z) {

    let result = this["genSign"](uri, bArr, str, str2, z);

    let mapStr = JSONObject.$new(result).toString()

    console.log("======== IJDGuardPlugin.genSign is called ==========")

    console.log(`uri = ${uri}`)

    console.log(`bArr string = ${jString.$new(bArr)}`)

    console.log(`str  = ${str}`)

    console.log(`str2  = ${str2}`)

    console.log(`z  = ${z}`)

    console.log(`mapStr  = ${mapStr}\n\n`)

    return result;

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

uri = https://api.m.jd.com/client.action?functionId=getLegoWareDetailComment&lmt=0&clientVersion=11.6.4&build=98704&client=android&partner=tencent&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv&sdkVersion=29&lang=zh_CN&harmonyOs=0&networkType=wifi&uemps=2-2-2&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&ef=1&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&st=1678346289851&sign=4bcf4d55a72e4023eebcf2d80c0bac2c&sv=111

bArr string = {"category":"12218;12221;13554","commentNum":3,"isNew":"0","newTitle":null,"shadowMainSku":"0","shieldCurrentComment":"1","shopId":null,"shopType":"0","sku":"10064651465940","venderId":"12715063","wareType":"0"}

str  = application/x-www-form-urlencoded; charset=UTF-8

str2  = Post

z  = true

mapStr  = {"jdgs":"{\"b1\":\"a4574221-958f-4752-ba5b-40e53bed1c17\",\"b2\":\"3.1.4-grey_0\",\"b3\":\"2.0\",\"b4\":\"eDIaHBTaPBKuc86zzA+aq9kltjVpsfYK9uL9uBZTn64tmAtrIPjdnnSaRU5oYnqGZrpwAfVY8PxsVG6T0BM07qPisYg5ffIXkrDYnM9W1bnZyY9uOnXSCSgwMt369HcipuC56Bh3OoP2\/8dGTfb1IJTRCj8s5o12Js+W4id6RKGen4q52iF74F+bl3fA5Zsl2Z3fg96JTVf7nAAIDvXiJBacMEMzWVBblzJMwjNENwZs2SvOB\/b6XSr5lrdlAtHgSytyL7ME5ftn+flvL2nJwH3CQ7AanNGaKFCaZPzjV0sU3+Q29NONh1SBjFDMGX\/PgA9QZmrQj14raPf7w7t\/QsdjjBPix+jbGr5KuPGOVaVBWSKhzh0s3Wkqhe5PqkjkYOUM3DAFGb7d1fNTXlZJUsI8Dsqin8a\/iXLb4RU=\",\"b5\":\"a49356adf69f2937aa764b661c2982d202ec6003\",\"b7\":\"1678346289856\",\"b6\":\"3e5485b2ee5c1e3a55e6ab35606728264d787a0f\"}"}

使用 firda 重新加载 js 脚本后发现其返回值居然是请求头里面的 jdgs,并不是我们需要的 sign,并且里面的参数,也就是 uri 里面包含了我们需要的 sign 参数,所以 sign 应该是在获取 jdgs 之前就生成了!

图片描述

图片描述

那么我们尝试定位上一级堆栈,也就是 com.jingdong.jdsdk.network.toolbox.HttpSettingTool.doSignUsingJdGuard ,源码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public static void doSignUsingJdGuard(HttpSetting httpSetting, String str) {

    URI uri;

    if (TextUtils.isEmpty(httpSetting.getFunctionId()) || JDHttpTookit.getEngine().getJdGuardPlugin() == null || !JDHttpTookit.getEngine().getJdGuardPlugin().isEnable() || !JDHttpTookit.getEngine().getJdGuardPlugin().isInWhiteList(httpSetting.getFunctionId())) {

        return;

    }

    String str2 = httpSetting.isPost() ? UrlRequest.HTTP_METHOD_POST : UrlRequest.HTTP_METHOD_GET;

    boolean isPost = httpSetting.isPost();

    Map<String, String> map = null;

    String str3 = isPost ? ParamEncodeUtil.DEFAULT_CONTENT_TYPE : null;

    try {

        uri = new URI(httpSetting.getUrl());

    } catch (URISyntaxException unused) {

        uri = null;

    }

    try {

        map = JDHttpTookit.getEngine().getJdGuardPlugin().genSign(uri, !TextUtils.isEmpty(str) ? str.getBytes() : null, str3, str2, isPost);

    } catch (Throwable unused2) {

    }

    if (map == null || map.isEmpty()) {

        return;

    }

    Map<String, String> headerMap = httpSetting.getHeaderMap();

    if (headerMap == null || headerMap.isEmpty()) {

        headerMap = new HashMap<>();

    }

    headerMap.putAll(map);

    httpSetting.setHeaderMap(headerMap);

}

我们可以发现此时的 uri 里面就有的 sign ,因此我们继续定位上层 com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.setupParams ,源码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public static void setupParams(HttpRequest httpRequest) {

        String str;

        HttpSetting httpSetting = HttpSettingTool.setupBaseParams(httpRequest);

        HttpSettingTool.addGuardVerifyLmtCode(httpSetting);

        if (httpSetting.getCustomMapParam() == null || !httpSetting.getCustomMapParam().containsKey("uuid")) {

            str = "";

        else {

            str = httpSetting.getCustomMapParam().get("uuid");

            httpSetting.getCustomMapParam().remove("uuid");

        }

        if (TextUtils.isEmpty(str) && httpSetting.getCustomEncryptMapParam() != null && httpSetting.getCustomEncryptMapParam().containsKey("uuid")) {

            str = httpSetting.getCustomEncryptMapParam().get("uuid");

            httpSetting.getCustomEncryptMapParam().remove("uuid");

        }

        if (TextUtils.isEmpty(str)) {

            str = JDHttpTookit.getEngine().getStatInfoConfigImpl().getDeviceUUID(httpSetting.getFunctionId(), httpSetting.isEnableEncryptTransmission());

        }

        if (TextUtils.isEmpty(str)) {

            str = "unknow";

        }

        if (OKLog.D) {

            String str2 = TAG;

            OKLog.d(str2, "id:" + httpSetting.getId() + "- uuid -->> " + str);

        }

        String bodyParam = HttpSettingTool.getBodyParam(httpSetting);

        if (httpSetting.getType() == 6000) {

            if (RuntimeConfigHelper.advertiseStatDataEnable()) {

                HttpSettingTool.addStatQueryParam(httpSetting, bodyParam, str);

            }

        else {

            HttpSettingTool.addStatQueryParam(httpSetting, bodyParam, str);

        }

        HttpSettingTool.addCustomQueryParam(httpSetting);

        if (JDHttpTookit.getEngine().isNeedVerifySignature()) {

            JDHttpTookit.getEngine().getSignatureHandlerImpl().networkSettingsPreSignature();

            signature(httpSetting, bodyParam, str);

        }

        HttpSettingTool.doSignUsingJdGuard(httpSetting, bodyParam);

        JDHttpTookit.getEngine().getExternalDebugConfigImpl().addMockerIdName(httpSetting);

    }

发现这个函数是设置 httpSetting 的 参数的,貌似就是我们需要的!我们看一下有没有关键的代码!发现了这么一行代码:signature(httpSetting, bodyParam, str); ,很可疑,我们先来尝试 hook 验证加密入口是否正确,先尝试 hook 一下 com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.signature 这个函数吧,因为这个函数是没有返回值的,所以我们尝试输出一下调用前与调用后的 httpSetting 的 url 不就知道他有没有给 url 加上 sign 了吗?代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

Java.perform(() => {

    let ParamBuilderForJDMall = Java.use("com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall");

    ParamBuilderForJDMall["signature"].implementation = function (httpSetting, str, str2) {

        console.log("========= ParamBuilderForJDMall.signature =========")

        console.log(`before httpSetting=${httpSetting}`)

        console.log(`before httpSetting uri=${httpSetting.getUrl()}`)

        console.log(`str=${str}`)

        console.log(`str2=${str2}`)

        this["signature"](httpSetting, str, str2);

        console.log(`after httpSetting=${httpSetting}`)

        console.log(`after httpSetting uri=${httpSetting.getUrl()`)

    };

})

图片描述

果然发现这就是关键入口,因为之前的 uri 里面是没有 sign 这个参数的,但是后面却有了,因此我们就追进去看看,发现了这么一行代码:String signature = JDHttpTookit.getEngine().getSignatureHandlerImpl().signature(JDHttpTookit.getEngine().getApplicationContext(), functionId, str, str2, property, versionName); ,继续跟进到 signature 里面去发现他只是一个接口,所以肯定有实现或者重载的地方!(这种情况,我们一般 hook 不到,因为位置不正确,实际调用的是其实现类的那个函数,不信你可以试试!)

那么我们应该如何找这个接口的实现呢?这边提供一个思路,因为实现的话肯定要使用同样的函数名,返回值以及参数,所以可以尝试直接搜索,例如当前函数定义为 String signature(Context context, String str, String str2, String str3, String str4, String str5);,那么我们就直接将这个作为关键字进行搜索(当然你也可以尝试查找用例,不过应该找不到)

通过搜索我们找到了如下结果:

图片描述

一个个去找就能发现真正的加密入口了:

1

2

3

4

@Override // com.jingdong.jdsdk.network.dependency.ISignatureHandler

public String signature(Context context, String str, String str2, String str3, String str4, String str5) {

    return BitmapkitUtils.getSignFromJni(context, str, str2, str3, str4, str5);

}

再继续跟进去就能发现这个是一个 native 函数 (其实从名字就能看出来),所以 sign 的 jni 入口为:com.jingdong.common.utils.BitmapkitUtils.getSignFromJni

环境准备

1

com.jingdong.app.mall==11.6.4

入口定位

逆向分析,发现 params 里面有一个 sign 以及请求头里面有一个 jdgs

首先我们发现京东的 sign 是 32 位的,猜测其可能是 md5 之类的 hash 算法,既然是 hash 算法,那么就大概率会用到 getBytes 方法,我们首先 hook 一下 java.lang.String 的 getBytes 方法,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 获取 Java 的字符串

    const str = Java.use('java.lang.String');

    // 重载  getBytes 方法 (因为常用的 Hash 算法一般都会调用字符串的 getBytes 方法)

    str.getBytes.overload().implementation = function () {

        var response = this.getBytes()

        var str1 = this.toString();

        // 如果 string 里面包含了 functionId (其他关键字也可以,需要自己尝试)

        if (str1.indexOf("functionId") >= 0) {

            // 输出找到了字符串

            console.log("find string:", str1);

            // 打印一下堆栈

                console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

        }

        return response;

    }

使用 firda 加载脚本后进入详情页就发现出现了以下堆栈

1

2

3

4

5

6

7

8

9

10

11

12

13

14

java.lang.Throwable

        at java.lang.String.getBytes(Native Method)

        at com.jd.security.jdguard.a.e.o(SourceFile:15)

        at com.jd.security.jdguard.a.e.i(SourceFile:3)

        at com.jd.security.jdguard.a.c.a(SourceFile:10)

        at com.jd.security.jdguard.a.c.b(SourceFile:4)

        at com.jingdong.common.guard.JDGuardHelper$1.genSign(SourceFile:1)

        at com.jingdong.jdsdk.network.toolbox.HttpSettingTool.doSignUsingJdGuard(SourceFile:10)

        at com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.setupParams(SourceFile:23)

        at com.jingdong.jdsdk.network.toolbox.HttpSettingTool.setupParams(SourceFile:2)

        at com.jingdong.jdsdk.network.toolbox.HttpGroupAdapter$RequestTask.run(SourceFile:14)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)

        at java.lang.Thread.run(Thread.java:919)

通过以上堆栈我们发现了疑似入口为:com.jingdong.common.guard.JDGuardHelper$1.genSign, jadx 源码如下:

1

2

3

public Map<String, String> genSign(URI uri, byte[] bArr, String str, String str2, boolean z) {

    return c.b(uri, bArr, str, str2, z);

}

因此我们再次 hook 验证,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

const jString = Java.use("java.lang.String");

let IJDGuardPlugin = Java.use("com.jingdong.common.guard.JDGuardHelper$1");

IJDGuardPlugin["genSign"].implementation = function (uri, bArr, str, str2, z) {

    let result = this["genSign"](uri, bArr, str, str2, z);

    let mapStr = JSONObject.$new(result).toString()

    console.log("======== IJDGuardPlugin.genSign is called ==========")

    console.log(`uri = ${uri}`)

    console.log(`bArr string = ${jString.$new(bArr)}`)

    console.log(`str  = ${str}`)

    console.log(`str2  = ${str2}`)

    console.log(`z  = ${z}`)

    console.log(`mapStr  = ${mapStr}\n\n`)

    return result;

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

uri = https://api.m.jd.com/client.action?functionId=getLegoWareDetailComment&lmt=0&clientVersion=11.6.4&build=98704&client=android&partner=tencent&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv&sdkVersion=29&lang=zh_CN&harmonyOs=0&networkType=wifi&uemps=2-2-2&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&ef=1&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&st=1678346289851&sign=4bcf4d55a72e4023eebcf2d80c0bac2c&sv=111

bArr string = {"category":"12218;12221;13554","commentNum":3,"isNew":"0","newTitle":null,"shadowMainSku":"0","shieldCurrentComment":"1","shopId":null,"shopType":"0","sku":"10064651465940","venderId":"12715063","wareType":"0"}

str  = application/x-www-form-urlencoded; charset=UTF-8

str2  = Post

z  = true

mapStr  = {"jdgs":"{\"b1\":\"a4574221-958f-4752-ba5b-40e53bed1c17\",\"b2\":\"3.1.4-grey_0\",\"b3\":\"2.0\",\"b4\":\"eDIaHBTaPBKuc86zzA+aq9kltjVpsfYK9uL9uBZTn64tmAtrIPjdnnSaRU5oYnqGZrpwAfVY8PxsVG6T0BM07qPisYg5ffIXkrDYnM9W1bnZyY9uOnXSCSgwMt369HcipuC56Bh3OoP2\/8dGTfb1IJTRCj8s5o12Js+W4id6RKGen4q52iF74F+bl3fA5Zsl2Z3fg96JTVf7nAAIDvXiJBacMEMzWVBblzJMwjNENwZs2SvOB\/b6XSr5lrdlAtHgSytyL7ME5ftn+flvL2nJwH3CQ7AanNGaKFCaZPzjV0sU3+Q29NONh1SBjFDMGX\/PgA9QZmrQj14raPf7w7t\/QsdjjBPix+jbGr5KuPGOVaVBWSKhzh0s3Wkqhe5PqkjkYOUM3DAFGb7d1fNTXlZJUsI8Dsqin8a\/iXLb4RU=\",\"b5\":\"a49356adf69f2937aa764b661c2982d202ec6003\",\"b7\":\"1678346289856\",\"b6\":\"3e5485b2ee5c1e3a55e6ab35606728264d787a0f\"}"}

使用 firda 重新加载 js 脚本后发现其返回值居然是请求头里面的 jdgs,并不是我们需要的 sign,并且里面的参数,也就是 uri 里面包含了我们需要的 sign 参数,所以 sign 应该是在获取 jdgs 之前就生成了!

图片描述

图片描述

那么我们尝试定位上一级堆栈,也就是 com.jingdong.jdsdk.network.toolbox.HttpSettingTool.doSignUsingJdGuard ,源码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public static void doSignUsingJdGuard(HttpSetting httpSetting, String str) {

    URI uri;

    if (TextUtils.isEmpty(httpSetting.getFunctionId()) || JDHttpTookit.getEngine().getJdGuardPlugin() == null || !JDHttpTookit.getEngine().getJdGuardPlugin().isEnable() || !JDHttpTookit.getEngine().getJdGuardPlugin().isInWhiteList(httpSetting.getFunctionId())) {

        return;

    }

    String str2 = httpSetting.isPost() ? UrlRequest.HTTP_METHOD_POST : UrlRequest.HTTP_METHOD_GET;

    boolean isPost = httpSetting.isPost();

    Map<String, String> map = null;

    String str3 = isPost ? ParamEncodeUtil.DEFAULT_CONTENT_TYPE : null;

    try {

        uri = new URI(httpSetting.getUrl());

    } catch (URISyntaxException unused) {

        uri = null;

    }

    try {

        map = JDHttpTookit.getEngine().getJdGuardPlugin().genSign(uri, !TextUtils.isEmpty(str) ? str.getBytes() : null, str3, str2, isPost);

    } catch (Throwable unused2) {

    }

    if (map == null || map.isEmpty()) {

        return;

    }

    Map<String, String> headerMap = httpSetting.getHeaderMap();

    if (headerMap == null || headerMap.isEmpty()) {

        headerMap = new HashMap<>();

    }

    headerMap.putAll(map);

    httpSetting.setHeaderMap(headerMap);

}

我们可以发现此时的 uri 里面就有的 sign ,因此我们继续定位上层 com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.setupParams ,源码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public static void setupParams(HttpRequest httpRequest) {

        String str;

        HttpSetting httpSetting = HttpSettingTool.setupBaseParams(httpRequest);

        HttpSettingTool.addGuardVerifyLmtCode(httpSetting);

        if (httpSetting.getCustomMapParam() == null || !httpSetting.getCustomMapParam().containsKey("uuid")) {

            str = "";

        else {

            str = httpSetting.getCustomMapParam().get("uuid");

            httpSetting.getCustomMapParam().remove("uuid");

        }

        if (TextUtils.isEmpty(str) && httpSetting.getCustomEncryptMapParam() != null && httpSetting.getCustomEncryptMapParam().containsKey("uuid")) {

            str = httpSetting.getCustomEncryptMapParam().get("uuid");

            httpSetting.getCustomEncryptMapParam().remove("uuid");

        }

        if (TextUtils.isEmpty(str)) {

            str = JDHttpTookit.getEngine().getStatInfoConfigImpl().getDeviceUUID(httpSetting.getFunctionId(), httpSetting.isEnableEncryptTransmission());

        }

        if (TextUtils.isEmpty(str)) {

            str = "unknow";

        }

        if (OKLog.D) {

            String str2 = TAG;

            OKLog.d(str2, "id:" + httpSetting.getId() + "- uuid -->> " + str);

        }

        String bodyParam = HttpSettingTool.getBodyParam(httpSetting);

        if (httpSetting.getType() == 6000) {

            if (RuntimeConfigHelper.advertiseStatDataEnable()) {

                HttpSettingTool.addStatQueryParam(httpSetting, bodyParam, str);

            }

        else {

            HttpSettingTool.addStatQueryParam(httpSetting, bodyParam, str);

        }

        HttpSettingTool.addCustomQueryParam(httpSetting);

        if (JDHttpTookit.getEngine().isNeedVerifySignature()) {

            JDHttpTookit.getEngine().getSignatureHandlerImpl().networkSettingsPreSignature();

            signature(httpSetting, bodyParam, str);

        }

        HttpSettingTool.doSignUsingJdGuard(httpSetting, bodyParam);

        JDHttpTookit.getEngine().getExternalDebugConfigImpl().addMockerIdName(httpSetting);

    }

发现这个函数是设置 httpSetting 的 参数的,貌似就是我们需要的!我们看一下有没有关键的代码!发现了这么一行代码:signature(httpSetting, bodyParam, str); ,很可疑,我们先来尝试 hook 验证加密入口是否正确,先尝试 hook 一下 com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall.signature 这个函数吧,因为这个函数是没有返回值的,所以我们尝试输出一下调用前与调用后的 httpSetting 的 url 不就知道他有没有给 url 加上 sign 了吗?代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

Java.perform(() => {

    let ParamBuilderForJDMall = Java.use("com.jingdong.jdsdk.network.toolbox.ParamBuilderForJDMall");

    ParamBuilderForJDMall["signature"].implementation = function (httpSetting, str, str2) {

        console.log("========= ParamBuilderForJDMall.signature =========")

        console.log(`before httpSetting=${httpSetting}`)

        console.log(`before httpSetting uri=${httpSetting.getUrl()}`)

        console.log(`str=${str}`)

        console.log(`str2=${str2}`)

        this["signature"](httpSetting, str, str2);

        console.log(`after httpSetting=${httpSetting}`)

        console.log(`after httpSetting uri=${httpSetting.getUrl()`)

    };

})

图片描述

果然发现这就是关键入口,因为之前的 uri 里面是没有 sign 这个参数的,但是后面却有了,因此我们就追进去看看,发现了这么一行代码:String signature = JDHttpTookit.getEngine().getSignatureHandlerImpl().signature(JDHttpTookit.getEngine().getApplicationContext(), functionId, str, str2, property, versionName); ,继续跟进到 signature 里面去发现他只是一个接口,所以肯定有实现或者重载的地方!(这种情况,我们一般 hook 不到,因为位置不正确,实际调用的是其实现类的那个函数,不信你可以试试!)

那么我们应该如何找这个接口的实现呢?这边提供一个思路,因为实现的话肯定要使用同样的函数名,返回值以及参数,所以可以尝试直接搜索,例如当前函数定义为 String signature(Context context, String str, String str2, String str3, String str4, String str5);,那么我们就直接将这个作为关键字进行搜索(当然你也可以尝试查找用例,不过应该找不到)

通过搜索我们找到了如下结果:

图片描述

一个个去找就能发现真正的加密入口了:

1

2

3

4

@Override // com.jingdong.jdsdk.network.dependency.ISignatureHandler

public String signature(Context context, String str, String str2, String str3, String str4, String str5) {

    return BitmapkitUtils.getSignFromJni(context, str, str2, str3, str4, str5);

}

再继续跟进去就能发现这个是一个 native 函数 (其实从名字就能看出来),所以 sign 的 jni 入口为:com.jingdong.common.utils.BitmapkitUtils.getSignFromJni

图片描述

我们回到 jdgs 加密入口处,继续跟进代码就能发现其实际也是一个 native 函数,简单讲一下步骤

1

2

3

4

5

6

1. 首先定位的入口为:com.jingdong.common.guard.JDGuardHelper$1.genSign

2. 进入 c.b,关键代码为 eVar.i();

3. 进入 eVar.i,发现 jdgs 就是 而 o 是 o(h())

4. 进入 o 函数,发现了 com.jd.security.jdguard.b.d().A(bArr);

5. 进入 com.jd.security.jdguard.b.d().A 发现了 Bridge.main(101, new Object[]{bArr, u(), s(), w(), q()});

6. 进入 main 发现 public static native Object[] main(int i2, Object[] objArr); 也就是入口了

接口分析

sign

入口为:com.jingdong.common.utils.BitmapkitUtils.getSignFromJni

使用通用的办法找到 so 文件和函数签名:so 文件 libjdbitmapkit.so, 函数签名:getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; , hook 一下分析一下参数吧!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Java.perform(() => {

    let BitmapkitUtils = Java.use("com.jingdong.common.utils.BitmapkitUtils");

    BitmapkitUtils["getSignFromJni"].implementation = function (context, str, str2, str3, str4, str5) {

        console.log("========= getSignFromJni =========")

        console.log(` context=${context}`)

        console.log(` str=${str}`)

        console.log(` str2=${str2}`)

        console.log(` str3=${str3}`)

        console.log(` str4=${str4}`)

        console.log(` str5=${str5}`)

        let result = this["getSignFromJni"](context, str, str2, str3, str4, str5);

        console.log(` result=${result}`)

        return result;

    };

})

图片描述

  • context:就是 android.context.Context
  • str:也就是 functionId
  • str2:是 body 字符串
  • str3:一直是 f9f40175bf4c6710, 猜测可能是设备码或者 secretKey 之类的东西吧(搜索抓包发现其为一个 uuid, 那就是设备码之类的 )
  • str4:设备类型,我的是 android
  • str5:看起来是版本号
  • result:就是加密好的 sign 和时间戳以及一个 sv, 所以猜测加密用上了这些

jdgs

入口为:com.jd.security.jdguard.core.Bridge.main

使用通用的办法找到 so 文件和函数签名:so 文件 libjdg.so, 函数签名:main(I[Ljava/lang/Object;)[Ljava/lang/Object; , hook 一下分析一下参数吧!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

Java.perform(() => {

    let Bridge = Java.use("com.jd.security.jdguard.core.Bridge");

    Bridge["main"].implementation = function (i2, objArr) {

        console.log("======== Bridge.main ========")

        console.log(`i2=${i2}`)

        console.log(`objArr=${objArr}`)

        let result = this["main"](i2, objArr);

        console.log(`result=${result}`)

        return result;

    };

})

  • i2101, 固定的数字
  • objArr
    • objArr[0]uri 转为 byte 数组
    • objArr[1]:一串和设备相关的字符串
    • objArr[2]eid 还是什么东西
    • objArr[3]:1.0
    • objArr[4]:83
  • result:请求头里面的 jdgs

这个参数暂时先不分析了,我们本文主要分析 sign ,如果有需要后续再补充分布步骤,先记录一下参数示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

i2: 101

objArr[0]: POST /client.action bef=1&bfa06501b64b4672e3b8645fd0ad54401e4786bd9efebc784fa483cf99bec2fd=&build=98704&client=android&clientVersion=11.6.4&ef=1&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF%2FmunBEHRDWPW5sYgYPelPhS+vYueanoeJv&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&functionId=wareBusiness&harmonyOs=0&lang=zh_CN&lmt=0&networkType=wifi&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&partner=tencent&scval=10062561918391&sdkVersion=29&sign=68049b09fa2cb6111f9ab0209ea9412e&st=1678348533650&sv=100&uemps=2-2-2 转为 byte 数组

objArr[1]: sdm845|-|OnePlus6|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§0|1§§-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§-|115717931008§§-|-|1.0|-§§0|1|-|-|-|-|-|-|-|-|-§§OnePlus/OnePlus6/OnePlus6:10/QKQ1.190716.003/2107162030:user/release-keys/|-|-|1678344292938|-§§-|-|-|-|-|-|-

objArr[2]: eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv

objArr[3]: 1.0

objArr[4]: 83

返回值:{"b1":"a4574221-958f-4752-ba5b-40e53bed1c17","b2":"3.1.4-grey_0","b3":"2.0","b4":"poW6/4yQ4JTEcYoerj5XATijmNYCBHcNjt++g5Q4O+l6+FjqoOp+0969nSqdVuQ6OYidCZIgcjC5HDYYgMu7jfPc+9qv092hwdGQxZK4GlClVizD6jznVAOvOvMYNkLQ3FjogO+BAMTJQvk8EPD7vWXJm8uvve8RVv1HkFs5RZ3zXSBNp3M2OyVX1hIZ9/CRBsu49MSJLIjUmz1fWvi47BZ+IlWC78KJMHb33i9I9YwzGtGnZCtIA3Sj+SzBiIIAS7B9dNfqYkjHf3FzDMcx3F0QznG+VyIw/qw727A7tulxd+5kky8tIG73Yb59KDmoEXrMlzDFEh9YGutpUKhBz3bTFp/uMp9MM1I5oKMoURMMAf7BK4ZWocY/wEWwq6Chh+rrlbZYpwJ/zCg3nxk/xmdIgvJ85+Ses0BYy+49","b5":"231497376118012d822698e78658dc91d6fa9a63","b7":"1678348533659","b6":"eeb029f8f6aadee6c235acd9eaf8447e6825f478"}

Unidbg

这边提供一个通用的模板,可以自己填充

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

package 这个写当前包名;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;

import com.github.unidbg.linux.android.AndroidResolver;

import com.github.unidbg.linux.android.dvm.AbstractJni;

import com.github.unidbg.linux.android.dvm.DalvikModule;

import com.github.unidbg.linux.android.dvm.DvmClass;

import com.github.unidbg.linux.android.dvm.VM;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class className extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "包名";

    // apk 地址

    private final String packagePath = "apk 地址";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "so 名称, 要去掉 lib 和  .so";

    // jni 类名

    private final String jniClassName = "jni 类名";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass Module;

    public className() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        Module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        className ins = new className();

    }

}

sign

先套用一下模板,然后运行看看

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

package com.jingdong.app.mall;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;

import com.github.unidbg.linux.android.AndroidResolver;

import com.github.unidbg.linux.android.dvm.AbstractJni;

import com.github.unidbg.linux.android.dvm.DalvikModule;

import com.github.unidbg.linux.android.dvm.DvmClass;

import com.github.unidbg.linux.android.dvm.VM;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class signUtils extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "com.com.jingdong.app.mall";

    // apk 地址

    private final String packagePath = "unidbg-android/src/test/resources/jd.apk";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "jdbitmapkit";

    // jni 类名

    private final String jniClassName = "com.jingdong.common.utils.BitmapkitUtils";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass Module;

    public signUtils() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        Module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        signUtils ins = new signUtils();

    }

}

  • com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;

    at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)

发现报错了,是因为缺少环境问题,可以看到最底层的报错堆栈在 com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField,大概意思就是要找com/jingdong/common/utils/BitmapkitUtils这个类的 a 字段,但是找不到,所以报了上述的错误,那么我们就需要重写 getStaticObjectField 这个方法,将环境补充完毕,步骤如下:

首先我们先点进源码里面看看作者自己的实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

@Override

public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {

    switch (signature) {

        case "android/content/Context->TELEPHONY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.TELEPHONY_SERVICE);

        case "android/content/Context->WIFI_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.WIFI_SERVICE);

        case "android/content/Context->CONNECTIVITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.CONNECTIVITY_SERVICE);

        case "android/content/Context->ACCESSIBILITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.ACCESSIBILITY_SERVICE);

        case "android/content/Context->KEYGUARD_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.KEYGUARD_SERVICE);

        case "android/content/Context->ACTIVITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.ACTIVITY_SERVICE);

        case "android/content/Context->LOCATION_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.LOCATION_SERVICE);

        case "android/content/Context->WINDOW_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.WINDOW_SERVICE);

        case "android/content/Context->SENSOR_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.SENSOR_SERVICE);

        case "android/content/Context->UI_MODE_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.UI_MODE_SERVICE);

        case "android/content/Context->DISPLAY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.DISPLAY_SERVICE);

        case "android/content/Context->AUDIO_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.AUDIO_SERVICE);

        case "java/lang/Void->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Void");

        case "java/lang/Boolean->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Boolean");

        case "java/lang/Byte->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Byte");

        case "java/lang/Character->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Character");

        case "java/lang/Short->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Short");

        case "java/lang/Integer->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Integer");

        case "java/lang/Long->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Long");

        case "java/lang/Float->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Float");

        case "java/lang/Double->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Double");

    }

    throw new UnsupportedOperationException(signature);

}

重写这个方法,补充实现,我们查看 jadx 发现 a 这个字段是一个 android.app.Application (你也可以通过签名看出来),所以我们需要在 unidbg 里面构造出来。

在 unidbg 里面,resolveClass是一个可变参数方法,它的第二个参数是可变参数,用于进一步描述我们所构造的这个DvmClass的父类和接口。

拿目前需要补充的 android.app.Application 为例,通过搜索资料可知,Application 继承与 ContextWrapper, 而 ContextWrapper 又继承与 Context,所以在 unidbg 里面需要实现一个 Application,那么可以使用以下嵌套代码:

1

2

3

DvmClass Context = vm.resolveClass("android/content/Context");

DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", Context);

DvmClass Application = vm.resolveClass("android/app/Application", ContextWrapper);

补环境代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@Override

    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {

        switch (signature) {

            case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {

                DvmClass Context = vm.resolveClass("android/content/Context");

                DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", Context);

                // 使用 android/app/Application 发现会报错: com.github.unidbg.arm.backend.BackendException

                // 百度了一下发现这个错误是找不到 methodID

                // 因此可以使用 android/app/Activity 代替, 因为它也可以获取 Application 对象

                DvmClass Application = vm.resolveClass("android/app/Activity", ContextWrapper);

                return Application.newObject(signature);

            }

        }

        return super.getStaticObjectField(vm, dvmClass, signature);

    }

  • android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)

取 ApplicationInfo 也就是 apk 的存放位置 可以使用 adb 进手机查看一下

图片描述

所以 app 在这里: /data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk

1

2

3

4

5

6

7

8

9

@Override

public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {

    switch (signature) {

        case "android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;": {

            return new StringObject(vm, "/data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk");

        }

    }

    return super.getObjectField(vm, dvmObject, signature);

}

  • com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B

    at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)

这个补的时候有一个参数,可以先输出一下参数,看看都是什么

1

2

3

arg0--  /data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk

arg1--  META-INF/

arg2--  .RSA

所以他找的好像是 apk 里面 META-INF 文件夹里面的 xxx.RSA , 我们只要使用压缩文件打开 apk 就可以看到一个叫做 JINGDONG.RSA 的文件,补全一下即可!

图片描述

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

@Override

public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {

    switch (signature) {

        case "com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B": {

            StringObject apkPath = varArg.getObjectArg(0);

            StringObject directory = varArg.getObjectArg(1);

            StringObject filename = varArg.getObjectArg(2);

            if (

                    "/data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk".equals(apkPath.getValue())

                            && "META-INF/".equals(directory.getValue())

                            && ".RSA".equals(filename.getValue())

            ) {

                byte[] data = vm.unzip("META-INF/JINGDONG.RSA");

                return new ByteArray(vm, data);

            }

        }

    }

    return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);

}

  • sun/security/pkcs/PKCS7-><init>([B)V

1

2

java.lang.UnsupportedOperationException: sun/security/pkcs/PKCS7-><init>([B)V

    at com.github.unidbg.linux.android.dvm.AbstractJni.newObject(AbstractJni.java:741)

图片描述

写完之后提示要捕捉异常,因此就捕捉一下,虽然我也不懂为什么

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@Override

public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {

    switch (signature) {

        case "sun/security/pkcs/PKCS7-><init>([B)V": {

            PKCS7 pkcs7 = null;

            try {

                byte[] bArray = (byte[]) varArg.getObjectArg(0).getValue();

                pkcs7 = new PKCS7(bArray);

            } catch (ParsingException e) {

                e.printStackTrace();

            }

            DvmClass pkcs7Class = vm.resolveClass("sun/security/pkcs/PKCS7");

            return pkcs7Class.newObject(pkcs7);

        }

    }

    return super.newObject(vm, dvmClass, signature, varArg);

}

  • sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;

1

2

java.lang.UnsupportedOperationException: sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:921)

1

2

3

4

5

6

7

8

9

10

11

12

@Override

public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {

    switch (signature) {

        case "sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;": {

            PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();

            X509Certificate[] x509Certificates = pkcs7.getCertificates();

            return ProxyDvmObject.createObject(vm, x509Certificates);

        }

    }

    return super.callObjectMethod(vm, dvmObject, signature, varArg);

}

  • com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B

    at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)

发现他找的是一个 objectToBytes 方法,但是找不到,所以我们直接去 jadx 里面搜索一下,便得到了结果

图片描述

图片描述

可以直接复制出来使用,去除报错,导入一些包就可以了!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public static byte[] objectToBytes(Object obj) {

    try {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

        objectOutputStream.writeObject(obj);

        objectOutputStream.flush();

        byte[] byteArray = byteArrayOutputStream.toByteArray();

        objectOutputStream.close();

        byteArrayOutputStream.close();

        return byteArray;

    } catch (IOException e2) {

        return null;

    }

}

然后再继续补环境

1

2

3

case "com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B": {

    return new ByteArray(vm, objectToBytes(varArg.getObjectArg(0).getValue()));

}

接着运行发现居然不报错了,因此我们就写一个获取 sign 的函数尝试调用一下!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public void getSign() {

    DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);

    String arg1 = "getLegoWareDetailComment";

    String arg2 = "{\"category\":\"12473;12480;12515\",\"commentNum\":3,\"isNew\":\"0\",\"newTitle\":null,\"shadowMainSku\":\"0\",\"shieldCurrentComment\":\"1\",\"shopId\":null,\"shopType\":\"0\",\"sku\":\"11653586880\",\"venderId\":\"644185\",\"wareType\":\"0\"}";

    String arg3 = "f9f40175bf4c6710";

    String arg4 = "android";

    String arg5 = "11.6.4";

    DvmObject<?> ret = module.callStaticJniMethodObject(emulator, "getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", vm.addLocalObject(context), arg1, arg2, arg3, arg4, arg5

    );

    System.out.println("================================================");

    System.out.println(ret.getValue());

    System.out.println("================================================");

}

图片描述

好的,又开始报错了,咱继续补环境

  • java/lang/StringBuffer-><init>()V

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer-><init>()V

    at com.github.unidbg.linux.android.dvm.AbstractJni.newObjectV(AbstractJni.java:791)

1

2

3

4

5

6

7

8

9

10

11

12

@Override

public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {

    switch (signature) {

        case "java/lang/StringBuffer-><init>()V": {

            StringBuffer stringBuffer = new StringBuffer();

            return vm.resolveClass("java/lang/StringBuffer").newObject(stringBuffer);

        }

    }

    return super.newObjectV(vm, dvmClass, signature, vaList);

}

  • java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

6

7

8

9

10

11

12

13

@Override

public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {

    switch (signature) {

        case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {

            StringBuffer stringBuffer = (java.lang.StringBuffer) dvmObject.getValue();

            stringBuffer.append(vaList.getObjectArg(0).getValue());

            DvmClass StringBuffer = vm.resolveClass("java/lang/StringBuffer");

            return StringBuffer.newObject(stringBuffer);

        }

    }

    return super.callObjectMethodV(vm, dvmObject, signature, vaList);

}

  • java/lang/Integer->toString()Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: java/lang/Integer->toString()Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

6

case "java/lang/Integer->toString()Ljava/lang/String;": {

    Integer value = (Integer) dvmObject.getValue();

    return vm.resolveClass("java/lang/String").newObject(value.toString());

}

  • java/lang/StringBuffer->toString()Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer->toString()Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

case "java/lang/StringBuffer->toString()Ljava/lang/String;": {

    StringBuffer value = (StringBuffer) dvmObject.getValue();

    return vm.resolveClass("java/lang/String").newObject(value.toString());

}

最后发现环境补完了,可以直接得到 sign 了!

图片描述

jdgs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

package com.jingdong.app.mall;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

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.jni.ProxyDvmObject;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class jdgs extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "com.com.jingdong.app.mall";

    // apk 地址

    private final String packagePath = "unidbg-android/src/test/resources/jd.apk";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "jdg";

    // jni 类名

    private final String jniClassName = "com.jd.security.jdguard.core.Bridge";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass module;

    public jdgs() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        jdgs ins = new jdgs();

        ins.getJdgs();

    }

    public void getJdgs() {

        Integer i2 = 101;

        String arr1 = "/client.action bef=1&bfa06501b64b4672e3b8645fd0ad54401e4786bd9efebc784fa483cf99bec2fd=&build=98704&client=android&clientVersion=11.6.4&ef=1&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF%2FmunBEHRDWPW5sYgYPelPhS+vYueanoeJv&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&functionId=wareBusiness&harmonyOs=0&lang=zh_CN&lmt=0&networkType=wifi&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&partner=tencent&scval=10062561918391&sdkVersion=29&sign=68049b09fa2cb6111f9ab0209ea9412e&st=1678348533650&sv=100&uemps=2-2-2";

        String arr2 = "sdm845|-|OnePlus6|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§0|1§§-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§-|115717931008§§-|-|1.0|-§§0|1|-|-|-|-|-|-|-|-|-§§OnePlus/OnePlus6/OnePlus6:10/QKQ1.190716.003/2107162030:user/release-keys/|-|-|1678344292938|-§§-|-|-|-|-|-|-";

        String arr3 = "eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv";

        String arr4 = "1.0";

        String arr5 = "83";

        Object[] arr = new Object[]{arr1.getBytes(), arr2, arr3, arr4, arr5};

        DvmObject<?> ret = module.callStaticJniMethodObject(emulator, "main(I[Ljava/lang/Object;)[Ljava/lang/Object;", i2, ProxyDvmObject.createObject(vm, arr));

        System.out.println("================================================");

        System.out.println(ret.getValue());

        System.out.println("================================================");

    }

}

模拟 jdgs 的时候不知道为什么会报这样一个错误,并且也没有堆栈,因为还是小白,暂时不知道如何解决
目前会导致获取到的结果拿不到,以后有思路了再来完善这一块!希望有大佬能指导一下,万分感谢!!

图片描述

我们回到 jdgs 加密入口处,继续跟进代码就能发现其实际也是一个 native 函数,简单讲一下步骤

1

2

3

4

5

6

1. 首先定位的入口为:com.jingdong.common.guard.JDGuardHelper$1.genSign

2. 进入 c.b,关键代码为 eVar.i();

3. 进入 eVar.i,发现 jdgs 就是 而 o 是 o(h())

4. 进入 o 函数,发现了 com.jd.security.jdguard.b.d().A(bArr);

5. 进入 com.jd.security.jdguard.b.d().A 发现了 Bridge.main(101, new Object[]{bArr, u(), s(), w(), q()});

6. 进入 main 发现 public static native Object[] main(int i2, Object[] objArr); 也就是入口了

接口分析

sign

入口为:com.jingdong.common.utils.BitmapkitUtils.getSignFromJni

使用通用的办法找到 so 文件和函数签名:so 文件 libjdbitmapkit.so, 函数签名:getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; , hook 一下分析一下参数吧!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Java.perform(() => {

    let BitmapkitUtils = Java.use("com.jingdong.common.utils.BitmapkitUtils");

    BitmapkitUtils["getSignFromJni"].implementation = function (context, str, str2, str3, str4, str5) {

        console.log("========= getSignFromJni =========")

        console.log(` context=${context}`)

        console.log(` str=${str}`)

        console.log(` str2=${str2}`)

        console.log(` str3=${str3}`)

        console.log(` str4=${str4}`)

        console.log(` str5=${str5}`)

        let result = this["getSignFromJni"](context, str, str2, str3, str4, str5);

        console.log(` result=${result}`)

        return result;

    };

})

图片描述

  • context:就是 android.context.Context
  • str:也就是 functionId
  • str2:是 body 字符串
  • str3:一直是 f9f40175bf4c6710, 猜测可能是设备码或者 secretKey 之类的东西吧(搜索抓包发现其为一个 uuid, 那就是设备码之类的 )
  • str4:设备类型,我的是 android
  • str5:看起来是版本号
  • result:就是加密好的 sign 和时间戳以及一个 sv, 所以猜测加密用上了这些

jdgs

入口为:com.jd.security.jdguard.core.Bridge.main

使用通用的办法找到 so 文件和函数签名:so 文件 libjdg.so, 函数签名:main(I[Ljava/lang/Object;)[Ljava/lang/Object; , hook 一下分析一下参数吧!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

Java.perform(() => {

    let Bridge = Java.use("com.jd.security.jdguard.core.Bridge");

    Bridge["main"].implementation = function (i2, objArr) {

        console.log("======== Bridge.main ========")

        console.log(`i2=${i2}`)

        console.log(`objArr=${objArr}`)

        let result = this["main"](i2, objArr);

        console.log(`result=${result}`)

        return result;

    };

})

  • i2101, 固定的数字
  • objArr
    • objArr[0]uri 转为 byte 数组
    • objArr[1]:一串和设备相关的字符串
    • objArr[2]eid 还是什么东西
    • objArr[3]:1.0
    • objArr[4]:83
  • result:请求头里面的 jdgs

这个参数暂时先不分析了,我们本文主要分析 sign ,如果有需要后续再补充分布步骤,先记录一下参数示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

i2: 101

objArr[0]: POST /client.action bef=1&bfa06501b64b4672e3b8645fd0ad54401e4786bd9efebc784fa483cf99bec2fd=&build=98704&client=android&clientVersion=11.6.4&ef=1&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF%2FmunBEHRDWPW5sYgYPelPhS+vYueanoeJv&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&functionId=wareBusiness&harmonyOs=0&lang=zh_CN&lmt=0&networkType=wifi&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&partner=tencent&scval=10062561918391&sdkVersion=29&sign=68049b09fa2cb6111f9ab0209ea9412e&st=1678348533650&sv=100&uemps=2-2-2 转为 byte 数组

objArr[1]: sdm845|-|OnePlus6|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§0|1§§-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§-|115717931008§§-|-|1.0|-§§0|1|-|-|-|-|-|-|-|-|-§§OnePlus/OnePlus6/OnePlus6:10/QKQ1.190716.003/2107162030:user/release-keys/|-|-|1678344292938|-§§-|-|-|-|-|-|-

objArr[2]: eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv

objArr[3]: 1.0

objArr[4]: 83

返回值:{"b1":"a4574221-958f-4752-ba5b-40e53bed1c17","b2":"3.1.4-grey_0","b3":"2.0","b4":"poW6/4yQ4JTEcYoerj5XATijmNYCBHcNjt++g5Q4O+l6+FjqoOp+0969nSqdVuQ6OYidCZIgcjC5HDYYgMu7jfPc+9qv092hwdGQxZK4GlClVizD6jznVAOvOvMYNkLQ3FjogO+BAMTJQvk8EPD7vWXJm8uvve8RVv1HkFs5RZ3zXSBNp3M2OyVX1hIZ9/CRBsu49MSJLIjUmz1fWvi47BZ+IlWC78KJMHb33i9I9YwzGtGnZCtIA3Sj+SzBiIIAS7B9dNfqYkjHf3FzDMcx3F0QznG+VyIw/qw727A7tulxd+5kky8tIG73Yb59KDmoEXrMlzDFEh9YGutpUKhBz3bTFp/uMp9MM1I5oKMoURMMAf7BK4ZWocY/wEWwq6Chh+rrlbZYpwJ/zCg3nxk/xmdIgvJ85+Ses0BYy+49","b5":"231497376118012d822698e78658dc91d6fa9a63","b7":"1678348533659","b6":"eeb029f8f6aadee6c235acd9eaf8447e6825f478"}

Unidbg

这边提供一个通用的模板,可以自己填充

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

package 这个写当前包名;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;

import com.github.unidbg.linux.android.AndroidResolver;

import com.github.unidbg.linux.android.dvm.AbstractJni;

import com.github.unidbg.linux.android.dvm.DalvikModule;

import com.github.unidbg.linux.android.dvm.DvmClass;

import com.github.unidbg.linux.android.dvm.VM;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class className extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "包名";

    // apk 地址

    private final String packagePath = "apk 地址";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "so 名称, 要去掉 lib 和  .so";

    // jni 类名

    private final String jniClassName = "jni 类名";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass Module;

    public className() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        Module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        className ins = new className();

    }

}

sign

先套用一下模板,然后运行看看

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

package com.jingdong.app.mall;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;

import com.github.unidbg.linux.android.AndroidResolver;

import com.github.unidbg.linux.android.dvm.AbstractJni;

import com.github.unidbg.linux.android.dvm.DalvikModule;

import com.github.unidbg.linux.android.dvm.DvmClass;

import com.github.unidbg.linux.android.dvm.VM;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class signUtils extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "com.com.jingdong.app.mall";

    // apk 地址

    private final String packagePath = "unidbg-android/src/test/resources/jd.apk";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "jdbitmapkit";

    // jni 类名

    private final String jniClassName = "com.jingdong.common.utils.BitmapkitUtils";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass Module;

    public signUtils() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        Module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        signUtils ins = new signUtils();

    }

}

  • com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;

    at com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField(AbstractJni.java:103)

发现报错了,是因为缺少环境问题,可以看到最底层的报错堆栈在 com.github.unidbg.linux.android.dvm.AbstractJni.getStaticObjectField,大概意思就是要找com/jingdong/common/utils/BitmapkitUtils这个类的 a 字段,但是找不到,所以报了上述的错误,那么我们就需要重写 getStaticObjectField 这个方法,将环境补充完毕,步骤如下:

首先我们先点进源码里面看看作者自己的实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

@Override

public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {

    switch (signature) {

        case "android/content/Context->TELEPHONY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.TELEPHONY_SERVICE);

        case "android/content/Context->WIFI_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.WIFI_SERVICE);

        case "android/content/Context->CONNECTIVITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.CONNECTIVITY_SERVICE);

        case "android/content/Context->ACCESSIBILITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.ACCESSIBILITY_SERVICE);

        case "android/content/Context->KEYGUARD_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.KEYGUARD_SERVICE);

        case "android/content/Context->ACTIVITY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.ACTIVITY_SERVICE);

        case "android/content/Context->LOCATION_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.LOCATION_SERVICE);

        case "android/content/Context->WINDOW_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.WINDOW_SERVICE);

        case "android/content/Context->SENSOR_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.SENSOR_SERVICE);

        case "android/content/Context->UI_MODE_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.UI_MODE_SERVICE);

        case "android/content/Context->DISPLAY_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.DISPLAY_SERVICE);

        case "android/content/Context->AUDIO_SERVICE:Ljava/lang/String;":

            return new StringObject(vm, SystemService.AUDIO_SERVICE);

        case "java/lang/Void->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Void");

        case "java/lang/Boolean->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Boolean");

        case "java/lang/Byte->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Byte");

        case "java/lang/Character->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Character");

        case "java/lang/Short->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Short");

        case "java/lang/Integer->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Integer");

        case "java/lang/Long->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Long");

        case "java/lang/Float->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Float");

        case "java/lang/Double->TYPE:Ljava/lang/Class;":

            return vm.resolveClass("java/lang/Double");

    }

    throw new UnsupportedOperationException(signature);

}

重写这个方法,补充实现,我们查看 jadx 发现 a 这个字段是一个 android.app.Application (你也可以通过签名看出来),所以我们需要在 unidbg 里面构造出来。

在 unidbg 里面,resolveClass是一个可变参数方法,它的第二个参数是可变参数,用于进一步描述我们所构造的这个DvmClass的父类和接口。

拿目前需要补充的 android.app.Application 为例,通过搜索资料可知,Application 继承与 ContextWrapper, 而 ContextWrapper 又继承与 Context,所以在 unidbg 里面需要实现一个 Application,那么可以使用以下嵌套代码:

1

2

3

DvmClass Context = vm.resolveClass("android/content/Context");

DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", Context);

DvmClass Application = vm.resolveClass("android/app/Application", ContextWrapper);

补环境代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@Override

    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {

        switch (signature) {

            case "com/jingdong/common/utils/BitmapkitUtils->a:Landroid/app/Application;": {

                DvmClass Context = vm.resolveClass("android/content/Context");

                DvmClass ContextWrapper = vm.resolveClass("android/content/ContextWrapper", Context);

                // 使用 android/app/Application 发现会报错: com.github.unidbg.arm.backend.BackendException

                // 百度了一下发现这个错误是找不到 methodID

                // 因此可以使用 android/app/Activity 代替, 因为它也可以获取 Application 对象

                DvmClass Application = vm.resolveClass("android/app/Activity", ContextWrapper);

                return Application.newObject(signature);

            }

        }

        return super.getStaticObjectField(vm, dvmClass, signature);

    }

  • android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.getObjectField(AbstractJni.java:171)

取 ApplicationInfo 也就是 apk 的存放位置 可以使用 adb 进手机查看一下

图片描述

所以 app 在这里: /data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk

1

2

3

4

5

6

7

8

9

@Override

public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {

    switch (signature) {

        case "android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;": {

            return new StringObject(vm, "/data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk");

        }

    }

    return super.getObjectField(vm, dvmObject, signature);

}

  • com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B

    at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)

这个补的时候有一个参数,可以先输出一下参数,看看都是什么

1

2

3

arg0--  /data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk

arg1--  META-INF/

arg2--  .RSA

所以他找的好像是 apk 里面 META-INF 文件夹里面的 xxx.RSA , 我们只要使用压缩文件打开 apk 就可以看到一个叫做 JINGDONG.RSA 的文件,补全一下即可!

图片描述

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

@Override

public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {

    switch (signature) {

        case "com/jingdong/common/utils/BitmapkitZip->unZip(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[B": {

            StringObject apkPath = varArg.getObjectArg(0);

            StringObject directory = varArg.getObjectArg(1);

            StringObject filename = varArg.getObjectArg(2);

            if (

                    "/data/app/com.jingdong.app.mall-Icn20e0xHLzrVHPBwOirRA==/bask.apk".equals(apkPath.getValue())

                            && "META-INF/".equals(directory.getValue())

                            && ".RSA".equals(filename.getValue())

            ) {

                byte[] data = vm.unzip("META-INF/JINGDONG.RSA");

                return new ByteArray(vm, data);

            }

        }

    }

    return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);

}

  • sun/security/pkcs/PKCS7-><init>([B)V

1

2

java.lang.UnsupportedOperationException: sun/security/pkcs/PKCS7-><init>([B)V

    at com.github.unidbg.linux.android.dvm.AbstractJni.newObject(AbstractJni.java:741)

图片描述

写完之后提示要捕捉异常,因此就捕捉一下,虽然我也不懂为什么

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

@Override

public DvmObject<?> newObject(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {

    switch (signature) {

        case "sun/security/pkcs/PKCS7-><init>([B)V": {

            PKCS7 pkcs7 = null;

            try {

                byte[] bArray = (byte[]) varArg.getObjectArg(0).getValue();

                pkcs7 = new PKCS7(bArray);

            } catch (ParsingException e) {

                e.printStackTrace();

            }

            DvmClass pkcs7Class = vm.resolveClass("sun/security/pkcs/PKCS7");

            return pkcs7Class.newObject(pkcs7);

        }

    }

    return super.newObject(vm, dvmClass, signature, varArg);

}

  • sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;

1

2

java.lang.UnsupportedOperationException: sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethod(AbstractJni.java:921)

1

2

3

4

5

6

7

8

9

10

11

12

@Override

public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {

    switch (signature) {

        case "sun/security/pkcs/PKCS7->getCertificates()[Ljava/security/cert/X509Certificate;": {

            PKCS7 pkcs7 = (PKCS7) dvmObject.getValue();

            X509Certificate[] x509Certificates = pkcs7.getCertificates();

            return ProxyDvmObject.createObject(vm, x509Certificates);

        }

    }

    return super.callObjectMethod(vm, dvmObject, signature, varArg);

}

  • com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B

1

2

java.lang.UnsupportedOperationException: com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B

    at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethod(AbstractJni.java:432)

发现他找的是一个 objectToBytes 方法,但是找不到,所以我们直接去 jadx 里面搜索一下,便得到了结果

图片描述

图片描述

可以直接复制出来使用,去除报错,导入一些包就可以了!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public static byte[] objectToBytes(Object obj) {

    try {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

        objectOutputStream.writeObject(obj);

        objectOutputStream.flush();

        byte[] byteArray = byteArrayOutputStream.toByteArray();

        objectOutputStream.close();

        byteArrayOutputStream.close();

        return byteArray;

    } catch (IOException e2) {

        return null;

    }

}

然后再继续补环境

1

2

3

case "com/jingdong/common/utils/BitmapkitZip->objectToBytes(Ljava/lang/Object;)[B": {

    return new ByteArray(vm, objectToBytes(varArg.getObjectArg(0).getValue()));

}

接着运行发现居然不报错了,因此我们就写一个获取 sign 的函数尝试调用一下!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public void getSign() {

    DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);

    String arg1 = "getLegoWareDetailComment";

    String arg2 = "{\"category\":\"12473;12480;12515\",\"commentNum\":3,\"isNew\":\"0\",\"newTitle\":null,\"shadowMainSku\":\"0\",\"shieldCurrentComment\":\"1\",\"shopId\":null,\"shopType\":\"0\",\"sku\":\"11653586880\",\"venderId\":\"644185\",\"wareType\":\"0\"}";

    String arg3 = "f9f40175bf4c6710";

    String arg4 = "android";

    String arg5 = "11.6.4";

    DvmObject<?> ret = module.callStaticJniMethodObject(emulator, "getSignFromJni(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", vm.addLocalObject(context), arg1, arg2, arg3, arg4, arg5

    );

    System.out.println("================================================");

    System.out.println(ret.getValue());

    System.out.println("================================================");

}

图片描述

好的,又开始报错了,咱继续补环境

  • java/lang/StringBuffer-><init>()V

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer-><init>()V

    at com.github.unidbg.linux.android.dvm.AbstractJni.newObjectV(AbstractJni.java:791)

1

2

3

4

5

6

7

8

9

10

11

12

@Override

public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {

    switch (signature) {

        case "java/lang/StringBuffer-><init>()V": {

            StringBuffer stringBuffer = new StringBuffer();

            return vm.resolveClass("java/lang/StringBuffer").newObject(stringBuffer);

        }

    }

    return super.newObjectV(vm, dvmClass, signature, vaList);

}

  • java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

6

7

8

9

10

11

12

13

@Override

public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {

    switch (signature) {

        case "java/lang/StringBuffer->append(Ljava/lang/String;)Ljava/lang/StringBuffer;": {

            StringBuffer stringBuffer = (java.lang.StringBuffer) dvmObject.getValue();

            stringBuffer.append(vaList.getObjectArg(0).getValue());

            DvmClass StringBuffer = vm.resolveClass("java/lang/StringBuffer");

            return StringBuffer.newObject(stringBuffer);

        }

    }

    return super.callObjectMethodV(vm, dvmObject, signature, vaList);

}

  • java/lang/Integer->toString()Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: java/lang/Integer->toString()Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

6

case "java/lang/Integer->toString()Ljava/lang/String;": {

    Integer value = (Integer) dvmObject.getValue();

    return vm.resolveClass("java/lang/String").newObject(value.toString());

}

  • java/lang/StringBuffer->toString()Ljava/lang/String;

1

2

java.lang.UnsupportedOperationException: java/lang/StringBuffer->toString()Ljava/lang/String;

    at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)

1

2

3

4

5

case "java/lang/StringBuffer->toString()Ljava/lang/String;": {

    StringBuffer value = (StringBuffer) dvmObject.getValue();

    return vm.resolveClass("java/lang/String").newObject(value.toString());

}

最后发现环境补完了,可以直接得到 sign 了!

图片描述

jdgs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

package com.jingdong.app.mall;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.arm.backend.Unicorn2Factory;

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.jni.ProxyDvmObject;

import com.github.unidbg.memory.Memory;

import java.io.File;

public class jdgs extends AbstractJni {

    private final AndroidEmulator emulator;

    private final VM vm;

    // 包名

    private final String packageName = "com.com.jingdong.app.mall";

    // apk 地址

    private final String packagePath = "unidbg-android/src/test/resources/jd.apk";

    // so 名称, 要去掉 lib 和  .so

    private final String libraryName = "jdg";

    // jni 类名

    private final String jniClassName = "com.jd.security.jdguard.core.Bridge";

    // 调试信息

    private final Boolean verbose = true;

    // jni 模块

    private final DvmClass module;

    public jdgs() {

        // 实例化一个模拟器

        emulator = AndroidEmulatorBuilder

                .for32Bit()

                .addBackendFactory(new Unicorn2Factory(true))

                .setProcessName(packageName)

                .build();

        Memory memory = emulator.getMemory();

        memory.setLibraryResolver(new AndroidResolver(23));

        vm = emulator.createDalvikVM(new File(packagePath));

        vm.setJni(this);

        vm.setVerbose(verbose);

        DalvikModule dm = vm.loadLibrary(libraryName, true);

        module = vm.resolveClass(jniClassName);

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) {

        jdgs ins = new jdgs();

        ins.getJdgs();

    }

    public void getJdgs() {

        Integer i2 = 101;

        String arr1 = "/client.action bef=1&bfa06501b64b4672e3b8645fd0ad54401e4786bd9efebc784fa483cf99bec2fd=&build=98704&client=android&clientVersion=11.6.4&ef=1&eid=eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF%2FmunBEHRDWPW5sYgYPelPhS+vYueanoeJv&ep=%7B%22hdid%22%3A%22JM9F1ywUPwflvMIpYPok0tt5k9kW4ArJEU3lfLhxBqw%3D%22%2C%22ts%22%3A1678345151525%2C%22ridx%22%3A-1%2C%22cipher%22%3A%7B%22area%22%3A%22CV83Cv8yDzu5XzK%3D%22%2C%22d_model%22%3A%22J05PUOnVU0O2CNKm%22%2C%22wifiBssid%22%3A%22dW5hbw93bq%3D%3D%22%2C%22osVersion%22%3A%22CJK%3D%22%2C%22d_brand%22%3A%22J25vUQn1cm%3D%3D%22%2C%22screen%22%3A%22CtO1EIenCNqm%22%2C%22uuid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22aid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%2C%22openudid%22%3A%22ZtvwDNKnDzVsZtHtDtcnCK%3D%3D%22%7D%2C%22ciphertype%22%3A5%2C%22version%22%3A%221.2.0%22%2C%22appname%22%3A%22com.jingdong.app.mall%22%7D&ext=%7B%22prstate%22%3A%220%22%2C%22pvcStu%22%3A%221%22%2C%22cfgExt%22%3A%22%7B%5C%22privacyOffline%5C%22%3A%5C%220%5C%22%7D%22%7D&functionId=wareBusiness&harmonyOs=0&lang=zh_CN&lmt=0&networkType=wifi&oaid=1B4220DEA49487D7AF8DE6AF4C0F1F60F05819E2ADCC3CD2ACDF3C07D81BEDC6&partner=tencent&scval=10062561918391&sdkVersion=29&sign=68049b09fa2cb6111f9ab0209ea9412e&st=1678348533650&sv=100&uemps=2-2-2";

        String arr2 = "sdm845|-|OnePlus6|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§0|1§§-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-§§-|115717931008§§-|-|1.0|-§§0|1|-|-|-|-|-|-|-|-|-§§OnePlus/OnePlus6/OnePlus6:10/QKQ1.190716.003/2107162030:user/release-keys/|-|-|1678344292938|-§§-|-|-|-|-|-|-";

        String arr3 = "eidAf5088122acsfbxEYf1ulTs2pUn38cGRLh28RGgYgyPwz80gu9s7yfbXAvbZ1R70WQm1PENZaDGd6EF/munBEHRDWPW5sYgYPelPhS+vYueanoeJv";

        String arr4 = "1.0";

        String arr5 = "83";

        Object[] arr = new Object[]{arr1.getBytes(), arr2, arr3, arr4, arr5};

        DvmObject<?> ret = module.callStaticJniMethodObject(emulator, "main(I[Ljava/lang/Object;)[Ljava/lang/Object;", i2, ProxyDvmObject.createObject(vm, arr));

        System.out.println("================================================");

        System.out.println(ret.getValue());

        System.out.println("================================================");

    }

}

模拟 jdgs 的时候不知道为什么会报这样一个错误,并且也没有堆栈,因为还是小白,暂时不知道如何解决
目前会导致获取到的结果拿不到,以后有思路了再来完善这一块!希望有大佬能指导一下,万分感谢!!

图片描述

相关文章:

京东协议算法最新版

环境准备 1 com.jingdong.app.mall11.6.4 入口定位 逆向分析&#xff0c;发现 params 里面有一个 sign 以及请求头里面有一个 jdgs 首先我们发现京东的 sign 是 32 位的&#xff0c;猜测其可能是 md5 之类的 hash 算法&#xff0c;既然是 hash 算法&#xff0c;那么就大概率…...

软考系统架构设计师案例分析知识汇总

软件架构风格 △△△ 软件架构风格是描述某一类特定应用领域中软件系统组织方式和惯用方式。组织方式描述了系统的组成构件和这些构件的组织方式,惯用模式则反映众多系统共有的结构和语义。 面向对象架构风格的特征是将数据表示和基本操作封装在对象中。这种模式的构件是对象…...

MyBatis-plus 代码生成器

具体代码 application.yaml server:port: 8081 #自定义端口号spring:datasource:url: jdbc:mysql://localhost:3306/itcast?useUnicodetrue&characterEncodingutf-8&serverTimezoneGMT%2B8username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver…...

运维常识——网络

内网&#xff0c;公网IP 内网IP为专网IP 因为网络资源&#xff08;IP地址不够&#xff0c;所以引出来了内网IP和IPv6&#xff09; 内网IP和公网IP之分是为了减缓IP地址不够使用的情况 一般设置代理服务器 设置两张网卡 一张对外一张对内 内部主机将数据转发到内网卡&#…...

《研发效能(DevOps)工程师》课程简介(一)丨IDCF

为贯彻落实《关于深化人才发展体制机制改革的意见》&#xff0c;推动实施人才强国战略&#xff0c;促进专业技术人员提升职业素养、补充新知识新技能&#xff0c;实现人力资源深度开发&#xff0c;推动经济社会全面发展&#xff0c;根据《中华人民共和国劳动法》有关规定&#…...

OMV 介绍及安装

# Time: 2023/11/02 #Author: Xiaohong # 运行电脑: Lenovo X201I (Intel(R) Core(TM) i3 CPU M 370 2.40GHz) # 功能: OMV 介绍及安装 导图 若OMV6 安装Extras 插件失败&#xff0c;可以参考 OMV6 安装Extras 插件失败的解决方法...

JAVA 实现PDF转图片(spire.pdf.free版)

1.引入jar包 导入方法1&#xff1a; 手动引入。将Free Spire.PDF for Java下载到本地&#xff0c;解压&#xff0c;找到lib文件夹下的Spire.PDF.jar文件。在IDEA中打开如下界面&#xff0c;将本地路径中的jar文件引入Java程序&#xff1a; 导入方法2&#xff1a;如果您想通过…...

高效学习工具之AnkiMobile新手入门指南(ios端,包括ipad、ihpone设备)————创建、使用、备份、设置参数、相关资料

文章目录 0 背景0.1 闭环学习0.2 什么是anki0.3 anki践行者经验分享 1 开始使用1.1 导入1.2 创建空白组1.3 创建卡片1.3.1 利用anki创建卡片的两种方法1.3.2 复习材料分类 1.4 筛选&#xff08;做减法&#xff0c;拆分学习&#xff08;做子卡牌集合&#xff09;&#xff09;&am…...

LiveMeida视频接入网关

一、产品简介 视频接入网关主要部署在视频存储节点或视频汇聚节点&#xff0c;面向不同用户&#xff0c;主要用于对接不同厂家、不同型号的摄像机设备&#xff0c;获取摄像机视频后&#xff0c;以统一标准的视频格式和传输协议&#xff0c;将视频推送至上层联网/应用平台。可广…...

我和云栖有个约会

文章目录 云栖大会体验与感受大模型的体验感受 对大会的期待 云栖大会 云栖大会是是阿里巴巴集团主办的年度技术盛会&#xff0c;是云计算、大数据、人工智能等前沿技术产业发展的见证者、参与者和推动者。2023年的云栖大会于10月31日在杭州开幕&#xff0c;吸引了全球的技术专…...

模拟官网编写自定义Grafana Dashboard

前言 我们想编写自定义的Dashboard&#xff0c;类似于官网那样下载的Dashboard&#xff0c;并且能移值到机器主机&#xff0c;如何实现了&#xff1f; ## 官网dashboard https://grafana.com/grafana/dashboards/ 编写 先在虚拟机写好Dashboard 然后下载。json文件如下: {…...

组件局部注册和全局注册

普通组件的注册使用-局部注册 1.特点&#xff1a; 只能在注册的组件内使用 2.实现效果 3.步骤&#xff1a; 创建.vue文件&#xff08;三个组成部分&#xff09;在使用的组件内先导入再注册&#xff0c;最后使用 4.使用方式&#xff1a; 当成html标签使用即可 <组件名&…...

【数据结构】模拟实现stack

namespace my_stack {//适配器模式/配接器template <class T,class Containervector<T>>class stack {public:void push(const T& val){_con.push_back(val);}void pop(){_con.pop_back();}const T& top(){return _con.back();}size_t size(){return _con.…...

Hive创建分区表并插入数据

业务中经常会遇到这种需求&#xff1a;数据每天全量更新&#xff0c;但是要求月底将数据单独保存一份以供后期查询某月节点的信息。这时就要考虑用到Hive的分区表实现&#xff0c;即按照月份创建分区表&#xff0c;相当于新的月份数据保存在新表&#xff0c;进而实现保存了历史…...

虚拟机防火墙关闭教程

虚拟机防火墙关闭教程 连接数据库请求超时 教程...

《研发效能(DevOps)工程师》课程简介(二)丨IDCF

为贯彻落实《关于深化人才发展体制机制改革的意见》&#xff0c;推动实施人才强国战略&#xff0c;促进专业技术人员提升职业素养、补充新知识新技能&#xff0c;实现人力资源深度开发&#xff0c;推动经济社会全面发展&#xff0c;根据《中华人民共和国劳动法》有关规定&#…...

EViews| 基础操作 备战下周机考

目录 一、创建工作文件 1、非时间序列数据 2、时间序列数据 二、导入数据 1、导入数据 2、保存数据组合或方程结果 三、估计回归模型 1、估计回归模型 2、回归结果名词解读 四、检验模型设定错误 1、检验是否遗漏变量 2-1、检验是否加入了不相干变量 2-2、惩罚新增…...

Web安全系列——敏感信息泄露与加密机制

一、前言 数字化时代&#xff0c;越来越多的数据正在被传输到Web应用程序中&#xff0c;这其中不乏个人或机构的敏感信息。 如果Web应用程序未采取正确的加密机制&#xff0c;这些信息可能会遭到窃取或篡改&#xff0c;从而使用户数据或机构的财产受到威胁。 二、加密机制失…...

【kubernetes】k8s对象

文章目录 1、什么是kubernetes对象2、如何描述kubernetes对象3、服务器端字段验证 1、什么是kubernetes对象 在k8s系统中&#xff0c;kubernetes 对象是持久化的实体&#xff0c;kubernetes使用这些实体去表示 整个集群的状态,具体而言&#xff0c;他们描述了以下信息&#xf…...

关注云栖大会的感受:从工业大脑到全面AI时代的进化

前言 自2009年的地方网站峰会到如今的云栖大会&#xff0c;这个盛大的科技盛事已经走过了一个多十年的漫长历程。这个会议见证了中国科技行业的崛起&#xff0c;也记录了技术的不断演化。而对我来说&#xff0c;首次接触云栖大会是在2020年&#xff0c;当年大会迁移到线上&…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一&#xff1a;HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二&#xff1a;Floyd 快慢指针法&#xff08;…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...