|   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); 也就是入口了   | 
 
 
 
入口为: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.Contextstr:也就是 functionIdstr2:是 body 字符串str3:一直是 f9f40175bf4c6710, 猜测可能是设备码或者 secretKey 之类的东西吧(搜索抓包发现其为一个 uuid, 那就是设备码之类的 )str4:设备类型,我的是 androidstr5:看起来是版本号result:就是加密好的 sign 和时间戳以及一个 sv, 所以猜测加密用上了这些
 
 
入口为: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;      };   })   | 
 
i2:101, 固定的数字objArr objArr[0]:uri 转为 byte 数组objArr[1]:一串和设备相关的字符串objArr[2]:eid 还是什么东西objArr[3]:1.0objArr[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"}   | 
 
 
这边提供一个通用的模板,可以自己填充
 
|   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();      }    }   | 
 
 
先套用一下模板,然后运行看看
 
|   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 了!
 
 

 
 
|   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.Contextstr:也就是 functionIdstr2:是 body 字符串str3:一直是 f9f40175bf4c6710, 猜测可能是设备码或者 secretKey 之类的东西吧(搜索抓包发现其为一个 uuid, 那就是设备码之类的 )str4:设备类型,我的是 androidstr5:看起来是版本号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;      };   })   | 
 
i2:101, 固定的数字objArr objArr[0]:uri 转为 byte 数组objArr[1]:一串和设备相关的字符串objArr[2]:eid 还是什么东西objArr[3]:1.0objArr[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 的时候不知道为什么会报这样一个错误,并且也没有堆栈,因为还是小白,暂时不知道如何解决
 目前会导致获取到的结果拿不到,以后有思路了再来完善这一块!希望有大佬能指导一下,万分感谢!!
 
