A-A+

某 APP 数据传输 内部加密签名方法分析

2020年09月17日 12:17 学习笔记 某 APP 数据传输 内部加密签名方法分析已关闭评论 阅读 94 views 次

【注意:此文章为博主原创文章!转载需注意,请带原文链接,至少也要是txt格式!】

问题的重点在某APP进行了数据包签名加密的验证,然后就是没办法更改数据包,没办法重放数据包。那针对如何加密的过程分析如下:

 

首先针对此款APP进行数据抓包,当然了,正常你是没办法抓包的,所以想看抓包过程请参考:Frida  Burp抓包APP ,下面是抓取的数据包,请仔细看图,如图分析:

burp 数据包对比分析

既然这样,那么就收集到了几个关键字

kbappkwle8K1Mhlc、kbcts、kbck、kbsv

既然这样已经收集这4个关键字,我们反编译APP,然后解包其中最重要的文件:classes.dex、classes2.dex、classes3.dex  解包之后我们搜索这几个关键词。

关键词kbappkwle8K1Mhlc 搜索结果

通过这里呢,发现了APP的核心配置文件,里面有很多关键的东西呢。

路径是:com.hp.smartmobile.config.ServiceConfig

我们再次搜索其它关键词,就发现了重要的传输过程中的加密方法及函数。如下图,请仔细看图:

关键词 kbcts 搜索结果详细分析 (所有图片点击可查看大图)

所有的加密函数,传输方法都在com.smartmobilevpay.android.http.HttpTools 这个路径下的文件中,全局的传输方法是doHttpGetPost、doHttpGetPost,而在这个传输方法中,使用了加密验证的方式,下面我们拿出重点的加密方法函数如下,

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
//这里是Get方法的加密函数
    public static Map<String, String> getSignGetMap(final String s, JSONObject jsonObject) {
        final HashMap<String, String> hashMap = new HashMap<String, String>();
        JSONObject jsonObject2;
        final Object o = jsonObject2 = (JSONObject)"";
        if (jsonObject != null) {
            TreeMap<String, String> treeMap;
            try {
                final Iterator keys = jsonObject.keys();
                treeMap = new TreeMap<String, String>();
                while (keys.hasNext()) {
                    final String s2 = keys.next();
                    treeMap.put(s2, jsonObject.getString(s2));
                }
            }
            catch (Exception ex) {
                ex.printStackTrace();
                return hashMap;
            }
            final Iterator<String> iterator = treeMap.keySet().iterator();
            jsonObject = (JSONObject)o;
            while (true) {
                jsonObject2 = jsonObject;
                if (!iterator.hasNext()) {
                    break;
                }
                final String s3 = iterator.next();
                try {
                    final String s4 = treeMap.get(s3);
                    if (((String)jsonObject).equals("")) {
                        jsonObject = (JSONObject)(s3 + "=" + s4);
                    }
                    else {
                        jsonObject = (JSONObject)((String)jsonObject + "&" + s3 + "=" + s4);
                    }
                }
                catch (Exception ex2) {
                    ex2.printStackTrace();
                }
            }
        }
        final long time = new Date().getTime();
        hashMap.put("kbck", SmartSdkManager.getInstance().getClientKey());
        hashMap.put("kbcts", time + "");
        hashMap.put("kbsv", EncryptUtils.stringToMD5(EncryptUtils.signatureGetCode(time, s, (String)jsonObject2)));
        return hashMap;
    }
 
    private void removeRequest(final long n) {
        synchronized (this) {
            if (this.mRequestIds == null) {
                this.mRequestIds = new HashSet<Long>();
            }
            this.mRequestIds.remove(n);
        }
    }
 
//这里是POST方式签名加密
    public Map<String, String> getSignPostMap(final String s, final JSONObject jsonObject) {
        final HashMap<String, String> hashMap = new HashMap<String, String>();
        try {
            final String string = jsonObject.toString();
            final long time = new Date().getTime();
            hashMap.put("kbck", SmartSdkManager.getInstance().getClientKey());
            hashMap.put("kbcts", time + "");
            hashMap.put("kbsv", EncryptUtils.stringToMD5(EncryptUtils.signaturePostCode(time, s, string)));
            return hashMap;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return hashMap;
        }
    }

到这里我们就明白了很多,首先kbck的值仅仅是一个配置文件的字符串,kbcts是当前时间的时间戳,重点的就是kbsv。
kbsv这里首先是执行 EncryptUtils.signaturePostCode(l, paramString, str) l是时间戳,其它两个还需要继续看。先看一下

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
//这里是Get方法的kbsv加密
public static String signatureGetCode(long paramLong, String paramString1, String paramString2) {
    paramString1 = SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + paramString2;
    if (SmartSdkManager.getInstance().isOpenLog())
      Log.i("applog", "------forMD5," + paramString1); 
    return paramString1;
  }
 
//这里是Post方法的kbsv加密  注意上面的函数调用的就是这里signaturePostCode
public static String signaturePostCode(long paramLong, String paramString1, String paramString2) {
    paramString1 = SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + '\t' + paramString2;
    if (SmartSdkManager.getInstance().isOpenLog())
      Log.i("applog", "------forMD5," + paramString1); 
    return paramString1;
  }
 
//这里是最外层的MD5加密EncryptUtils.stringToMD5(EncryptUtils.signaturePostCode(l, paramString, str)))
public static String stringToMD5(String paramString) {
    try {
      MessageDigest messageDigest = MessageDigest.getInstance("MD5");
      try {
        byte[] arrayOfByte = messageDigest.digest(paramString.getBytes("UTF-8"));
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i &lt; arrayOfByte.length; i++) {
          int j = arrayOfByte[i] &amp; 0xFF;
          if (j &lt; 16)
            stringBuffer.append("0"); 
          stringBuffer.append(Integer.toHexString(j));
        } 
        if (SmartSdkManager.getInstance().isOpenLog())
          Log.i("applog", "------MD5iS," + stringBuffer.toString()); 
        return stringBuffer.toString();
      } catch (Exception exception) {
        exception.printStackTrace();
        return "";
      } 
    } catch (Exception exception) {
      exception.printStackTrace();
      return "";
    } 
  }
}

 

其实走到这里就可以看出来了,最重要的就是:SmartSdkManager.getInstance().getClientKey() + '\t' + SmartSdkManager.getInstance().getClientSec() + '\t' + paramLong + '\t' + paramString1 + '\t' + paramString2;

通过刚刚的核心配置文件我们知道了

SmartSdkManager.getInstance().getClientKey() 值是 kbappkwle8K1Mhlc

SmartSdkManager.getInstance().getClientSec() 值是 WYjEbpFholuphDuO

paramLong 值是 当前时间戳

那么把上面的解密就是:

kbappkwle8K1Mhlc + '\t' + WYjEbpFholuphDuO + '\t' + 当前时间戳 + '\t' + paramString1 + '\t' + paramString2;

下一步,只要我们弄清楚paramString1paramString2就完全弄清楚这个传输方式过程中kbsv的加密方式。我们就可以通过修改数据包然后来重新加密,重新发包。

既然想弄清paramString1paramString2那么我们就要梳理整个加密解密逻辑关系,什么地方传入这两个参数。

我们先看下图,找到他们俩的来源

paramString1、paramString2参数来源

paramString1、paramString2参数来源

其实到这里基本就可以明白了。下图是更详细的 paramString1、paramString2参数来源

全局POST入口 参数详解

全局POST入口 参数详解

还有更多详细的分析需要梳理,这里就不梳理的那么仔细了。

 

paramString1 = 提交路径

例如  http://seo.baidu.com/Web/Page/Mainframe/Mainframe.aspx

paramString1 就是:/Web/Page/Mainframe/Mainframe.aspx   具体可能有所不同,我看到有的API接口是取部分值,有可能还是/Mainframe/Mainframe.aspx

 

paramString2 = 提交参数

例如 某GET请求是  https://woj.app/post.php?post=6731&action=edit

paramString2 就是 post=6731&action=edit

 

例如 某POST请求 https://seo.baidu.com/validOvertimeVoucher

DATA: {"customerId":null,"userUniqueId":"d020434d-0c0f-4357-b2b8-e834a4390009"}

paramString2 就是 {"customerId":null,"userUniqueId":"d020434d-0c0f-4357-b2b8-e834a4390009"}

 

那所有的全部都梳理出来拉,整体的就是:

1
2
3
4
5
6
def do_POST_signatureCode(n, s, s2):
    return 'kbappkwle8K1Mhlc' + '\t' + 'WYjEbpFholuphDuO' + '\t' + str(时间戳) + '\t' + paramString1 + '\t' + '\t' + paramString2 
 
 
def do_GET_signatureCode(n, s, s2):
    return 'kbappkwle8K1Mhlc' + '\t' + 'WYjEbpFholuphDuO' + '\t' + str(时间戳) + '\t' + paramString1 + '\t' + paramString2

就这么简单哦。
最后赠送一个神秘接口https://authlogin.baidu.com.cn/api/user/token?token=10232d845aa0fd70_1123123123a_1749ae5894a_w123123w 就可以查询这个用户的信息。

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

评论已关闭!