云闪付逆向笔记

云闪付逆向笔记

流程:

  1. 获取券号couponId

  2. 初始化验证码(/youhui-web/restlet/frog/initCaptcha),获取验证码的必要参数,请求参数与响应结果均加密。请求参数为billId, 是couponId加密后的结果。响应结果解密后包含dfpSessionId,sessionId参数。

    1
    2
    3
    4
    5
    6
    encrypt('3102022051863931')
    encrypt result: 0F94DD568A40DAE4D669A1F55A786D95

    resp = 'a97faad1644995b73ed282dee397aa132c453a978cc46a29653691a62b111a8d86b1d1fee21fe4f85c8527c2b9ba17661fb8589624bf17d027ac0f26195f6742feeaa25bc0be0212e1405e8d1686f20a59153713cb8c91dcf3f1ca221370c164fb89362107127be29c286ef73f8602414c7bd9780b1842f3875c2d4f5c4223241a653ac94b5208ae9686b5172b9cbf08ae4b49313fcb425a833c13f09ae9443688edfc16b7bd035d67345e7ec21403a1ab6b9e23644005595f23cb3c96aa0034'
    derypt(resp)
    decrypt result: {"msg":"success","resp":"00","cmd":"frog/initCaptcha","params":{"hexStr":"","dfpSessionId":"110005D001L0VAdpjbl1TKOIPPiX11653287554405","id":"","sessionId":"7c7488a80884414b8459da411b2880af"}}
  3. 获取验证码capId

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    GET /session/initcap?callback=jsonpCallbackyyyy0523164902432_wHG4P7d7w910Rdj&v=1653295742429&cType=3&cVersion=1.0.0&sesId=4cdd249449f14eb5b4eef41086bd7f61 HTTP/1.1
    Host: captcha.95516.com

    ---------------

    HTTP/1.1 200 OK
    Date: Mon, 23 May 2022 08:49:02 GMT
    Content-Type: application/json;charset=UTF-8
    Connection: close
    Server: nginx
    X-Frame-Options: SAMEORIGIN
    X-Via: 1.1 xin151:5 (Cdn Cache Server V2.0)
    X-Ws-Request-Id: 628b4a7e_xin151_20427-6065
    Content-Length: 218

    jsonpCallbackyyyy0523164902432_wHG4P7d7w910Rdj({"resCode":"0000","resMsg":"成功","resData":{"dVersion":"2.0.0","capInfo":"27.0","capType":"4","capId":"e1fed7b26f764a94b37767aeb2bf22a7"},"extensions":{},"dealTime":2})
  4. 使用capId下载验证码图片

    1
    2
    https://captcha.95516.com/media?mediaId=me1fed7b26f764a94b37767aeb2bf22a7.png
    https://captcha.95516.com/media?mediaId=se1fed7b26f764a94b37767aeb2bf22a7.png
  5. 提交验证结果,获取token,sign

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    https://captcha.95516.com/session/vfy?callback=jsonpCallback20220523170812896_OvDXVwf96RZaNwS&v=1653296892896&sesId=ac078fdd86e4459da107ad1bb4f3bdaa&passTime=88&value=169.05537459283386&bhv=E(!!H(!!()


    HTTP/1.1 200 OK
    Date: Mon, 23 May 2022 09:08:34 GMT
    Content-Type: application/json;charset=UTF-8
    Connection: close
    Server: nginx
    X-Frame-Options: SAMEORIGIN
    X-Via: 1.1 dedxin32:7 (Cdn Cache Server V2.0)
    X-Ws-Request-Id: 628b4f12_dedxin33_5004-51280
    Content-Length: 210

    jsonpCallback20220523170834203_JYAuk4D3QGsASX6({"resCode":"0000","resMsg":"成功","resData":{"sign":"c6f01502ec59311a373c6d076e05c156","token":"361ee9dc0fb143198b6a9006c8805c8e"},"extensions":{},"dealTime":5})

  6. 提交抢券请求。

    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
    POST /youhui-web/restlet/frog/download HTTP/1.1
    requestId: ff70a604db084df7a0b04b37dc370650
    X-Tingyun-Id: 4Nl_NnGbjwY;c=2;r=1198351094;u=f6deeb5ff2e3dcaf1ec602c68c59b166a809eedc49e41d523e747935ab486eba4bf1714ff5a5c5606bf66ff5d7b5a07b::A8B29C5F57C4A663
    X-Tingyun: c=A|CAtzD3av_OQ;
    cityCd: 510100
    nextdataDeviceId: 202111292255244eee4bfd2433484fe8c57cef421c9ea20194f1689290cc87
    gpsLongitude: 104.071315
    Accept: */*
    User-Agent: Android CHSP
    dcTagList: PY:AB,BS:AB,CT:AB,LF:AB,TV:AB,OP:AB,AD:AB,HP:AB,BL:AB,AC:AB,FD:AB,ID:AB,MB:AB
    language: zh_CN
    locale: zh-CN
    key_session_id:
    nonce: 491f056b-0201-4139-9c91-d7bafbc091d5
    urid: 153044286065308
    sid: 4f3a3b7983634f7d9a3a4243906b27b7a
    timeStamp: 1653296914705
    dfpSessionId: 110005D001k0kOPiNTF0pzlzb9KWP1653296888029
    gray: 0
    dcTag:
    requestId: db81bc8c-6e5c-4146-ba5e-0f52e90a983e
    digest: FA1287DC731274F5325E4C4E4121AB88465EBF5296C0E4EA1550E0DAB984AABC
    gpsLatitude: 30.538696
    Content-Type: application/json; charset=utf-8
    Host: youhui.95516.com
    Connection: close
    Accept-Encoding: gzip, deflate
    Content-Length: 448

    A54CB6F38CCE2BDE2BA466F5E1DC14B6485648ED408B09C807848E80C1F74A9FC05AD76AF7E940BC03294275F0E3115B54AC1289C14215B96B295AC24129D4FFE8EE4FC6DCF6EA46B6058F33C6FD883AB111F19B13AD44B377082AAAC506C37CB8A2677ECBB2EE7747386CC70E39CB4AB5202347C97338E79574C02B53BDCFDF23B9D845E85E14A184DF2DA89B327F3A0A4B46C399F2E4A64650ADF026D38EA570B759A47075F74C6791488C6238887F545FF82E4D9A31FAE469F1DFB66266FC4D2CEBDA435294B525E0D09F3E3E885587585D98CC658B94C167B9B8872CE9CF

    -----------------------------
    HTTP/1.1 200 OK
    Date: Mon, 23 May 2022 09:08:35 GMT
    Content-Type: application/json
    Connection: close
    Server: nginx
    X-Frame-Options: SAMEORIGIN
    Access-Control-Allow-Origin: “”
    Access-Control-Allow-Methods: “”
    Access-Control-Allow-Headers: X-PINGOTHER, accept, content-type, isOutput, h5-user-agent, sid, urid, dfpSessionId, cityCd, gray,dcTag,nextdataDeviceId,gpsLongitude,gpsLatitude
    Access-Control-Expose-Headers: date
    Access-Control-Max-Age: 1728000
    Access-Control-Allow-Credentials: true
    X-Via: 1.1 PS-KWE-01Ber55:5 (Cdn Cache Server V2.0)
    X-Ws-Request-Id: 628b4f12_PS-KWE-015vJ56_23355-42496
    Content-Length: 208

    1de6a613988fe573f7b73c0c41754e179c80706f59f307e55cd29e1b42507f72a53589c37ec3b0fa5d6939ae658dbb43062f28b5e1c9bf82a01807f61a1726354e9b8146ac17599831dd09c6d7f731217059a3729350ab5107ef975ebdd1909538f900d8739bba6e

    解密:

    encrypt:
    arg0: {"verifyValue":"","billId":"3102022051863931","captchaTp":"2","verifyCode":"","token":"361ee9dc0fb143198b6a9006c8805c8e","cityCd":"510100","sesId":"ac078fdd86e4459da107ad1bb4f3bdaa","sign":"c6f01502ec59311a373c6d076e05c156"}

    encrypt result:
    A54CB6F38CCE2BDE2BA466F5E1DC14B6485648ED408B09C807848E80C1F74A9FC05AD76AF7E940BC03294275F0E3115B54AC1289C14215B96B295AC24129D4FFE8EE4FC6DCF6EA46B6058F33C6FD883AB111F19B13AD44B377082AAAC506C37CB8A2677ECBB2EE7747386CC70E39CB4AB5202347C97338E79574C02B53BDCFDF23B9D845E85E14A184DF2DA89B327F3A0A4B46C399F2E4A64650ADF026D38EA570B759A47075F74C6791488C6238887F545FF82E4D9A31FAE469F1DFB66266FC4D2CEBDA435294B525E0D09F3E3E885587585D98CC658B94C167B9B8872CE9CF



    decrypt:
    arg0: 1de6a613988fe573f7b73c0c41754e179c80706f59f307e55cd29e1b42507f72a53589c37ec3b0fa5d6939ae658dbb43062f28b5e1c9bf82a01807f61a1726354e9b8146ac17599831dd09c6d7f731217059a3729350ab5107ef975ebdd1909538f900d8739bba6e

    decrypt result: {"msg":"验证码非秒杀时间内生成,请重试","resp":"E11","cmd":"frog/download","params":{}}
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
POST /app/inApp/sys/init HTTP/1.1
cityCd: 510100
nextdataDeviceId: 202111292255244eee4bfd2433484fe8c57cef421c9ea20194f1689290cc87
Accept: */*
User-Agent: Android CHSP
dcTagList: PY:AB,BS:AB,CT:AB,LF:AB,TV:AB,OP:AB,AD:AB,HP:AB,BL:AB,AC:AB,FD:AB,ID:AB,MB:AB
language: zh_CN
locale: zh-CN
key_session_id:
nonce: 53c994a1-c0f9-45fc-9d71-015ff8fe2538
urid: 153044286065308
timeStamp: 1653387196739
dfpSessionId: 110005D001A08q3LSX56G3ri6WdQu1653387196628
dcTag: AB
requestId: 52528db6-941a-4ef7-820b-ff63e814acae
Content-Type: application/json; charset=utf-8
Host: base.95516.com
Connection: close
Accept-Encoding: gzip, deflate
Content-Length: 693

{"clientVersion":"267","currentKeyNo":"2","encryptedTk":"71E76318B5558B6D48C19C29322B29D275B6AA50E3B8D09E453379AB41E8BD3DC3C9EC185D5E224FD5B238859663A3016148B79AF34B03ECCF9EBD0B89231BD74A6E0EE5580D9927EE2D9653FDDDE90D31E7FC9F445FCA3174237081E73435CB3A7CD842AC89DB2EB31BA65EC7BE056E274E8AB56E8D554DD61AA989CF98C963F087C4A644598F7E9AA4A77053F34D0076A5B7B35EB34CFFCBC81C3B1888DC57DF9A3612D085813605296BF53226048889986FC533323766D227A864DB6BBF28007CA03F5B6ACABA2CB9E6BEB679BAEC36CD365A98585BF6673F86F7290591FAB82FB3EFA7B5E6B72E43BCBEB4FF2089FE83ECC3ACF46ED6480C8D0E741BCC98","secretKeyType":"INT","encryptedVid":"6FECA3B000BE953789C95671641E30562585B38EF0E98253","maxKeyNo":"3","osName":"Android"}

----------------------------------------
resp:
{"resp":"00","msg":"成功","params":{"encryptedVid":"0ede0efa754ea97f879b1f7358b3d33a1cbccabe23cffa32","encryptedCk":"fecc176553941f9cb22083c725d04670808496e7318a682d7e73220b652587fefecc176553941f9cb22083c725d04670","sid":"6660ea5529394a33931e9a2c89f34619a","gray":"0","timestamp":"1653403246719","respCode":"00","respMsg":"成功"}}
1
2
SessionKey = makeSessionKey()
891FBD68BB212C95D573DB92D6EB0D0D891FBD68BB212C95
1
2
3
encryptSessionKey(SessionKey)
# encryptedTk
71E76318B5558B6D48C19C29322B29D275B6AA50E3B8D09E453379AB41E8BD3DC3C9EC185D5E224FD5B238859663A3016148B79AF34B03ECCF9EBD0B89231BD74A6E0EE5580D9927EE2D9653FDDDE90D31E7FC9F445FCA3174237081E73435CB3A7CD842AC89DB2EB31BA65EC7BE056E274E8AB56E8D554DD61AA989CF98C963F087C4A644598F7E9AA4A77053F34D0076A5B7B35EB34CFFCBC81C3B1888DC57DF9A3612D085813605296BF53226048889986FC533323766D227A864DB6BBF28007CA03F5B6ACABA2CB9E6BEB679BAEC36CD365A98585BF6673F86F7290591FAB82FB3EFA7B5E6B72E43BCBEB4FF2089FE83ECC3ACF46ED6480C8D0E741BCC98
1
2
3
4
# encryptedVid
encryptDataWithKey('ceEZ489xTUWYXSb+bhEQeA==', SessionKey)
# ceEZ489xTUWYXSb+bhEQeA== ? random?
6FECA3B000BE953789C95671641E30562585B38EF0E98253

1
2
refreshSessionKey(resp.get('encryptedCk'))
fecc176553941f9cb22083c725d04670808496e7318a682d7e73220b652587fefecc176553941f9cb22083c725d04670
1
2
3
4
decryptMsg(resp.get('encryptedVid'))
decryptMsg('0ede0efa754ea97f879b1f7358b3d33a1cbccabe23cffa32')

ceEZ489xTUWYXSb+bhEQeA==
1
2
"ceEZ489xTUWYXSb+bhEQeA==".equals("ceEZ489xTUWYXSb+bhEQeA==")
# 握手成功
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
package com.unionpay.network.model.req;

import com.google.gson.annotations.SerializedName;
import com.unionpay.network.EncryptValue.Encrypt;
import com.unionpay.network.ae;
import com.unionpay.network.g;
import com.unionpay.network.u;

@g(a = false, b = Encrypt.NONE, c = 1)
public class UPInitParam extends UPWalletReqParam {
@SerializedName("clientVersion")
private String mClientVersion;
@SerializedName("currentKeyNo")
private String mCurrentKeyNumber;
@SerializedName("encryptedTk")
private String mEncryptTK;
@SerializedName("secretKeyType")
private String mEncryptType;
@SerializedName("encryptedVid")
private String mEncryptedVid;
@SerializedName("maxKeyNo")
private String mMaxKeyNo;
@SerializedName("osName")
private String mOSName;
private static final long serialVersionUID;

public UPInitParam() {
String v0 = ae.b().a(); // makeSessionKey
this.mEncryptTK = ae.b().d(v0); // encryptSessionKey
this.mCurrentKeyNumber = ae.b().b();
this.mMaxKeyNo = ae.b().c() + "";
this.mEncryptedVid = ae.b().h(v0); // encryptDataWithKey
this.mOSName = "Android";
this.mClientVersion = com.unionpay.utils.ae.f();
if(u.a) {
this.mEncryptType = "NAT";
return;
}

this.mEncryptType = "INT";
}

public String getCurrentKeyNumber() {
return this.mCurrentKeyNumber;
}
}

image-20220526091131466

digest

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
Map v0_2 = arg9.headers;
if(v0_2 != null && v0_2.get("digest") != null) {
String v0_3 = (String)v0_2.get("digest");
UPLog.v("parseNetworkResponse digst = " + v0_3);
String encryptedVid = com.unionpay.data.e.a(null).c("encryptedVid", 1);
String cKWithSM3 = com.unionpay.utils.IJniInterface.g();
String v5 = y.a(cKWithSM3, ae.b().e(), encryptedVid, body); // sid =ae.b().e();
UPLog.v("responseSM3 = " + v5);
if(!v0_3.equals(v5)) {
arg9.statusCode = 423;
try {
z.a(new String[]{"event_id", "vid", "cksm3", "source"}, new String[]{"ErrorHttpCode423", encryptedVid, cKWithSM3, "0"});
}
catch(Exception v0_4) {
v0_4.printStackTrace();
}

return m.a(new k(arg9));
}
}

public static String a(String cKWithSM3, String sid, String encryptedVid, String body) {
int v1 = 0;
String v0 = cKWithSM3 + body;
if(TextUtils.isEmpty(v0)) {
v0 = "";
}

int v0_1 = v0.length() % 7;
if(v0_1 == 0) {
v0_1 = 7;
}

String[] v2 = new String[]{cKWithSM3, sid, encryptedVid, body};
StringBuilder v3 = new StringBuilder();
while(v1 < v2.length) {
String v5 = v2[v1];
if(!TextUtils.isEmpty(v5)) {
v3.append(v5);
}

v3.append(UPUtils.getStringFirstLength(v5, v0_1));
++v1;
}

return IJniInterface.sm3NSADigestData(v3.toString());
}

HOOK方式获取密钥

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

/**
*
makeSessionKey: 生成3-DES临时密钥
891FBD68BB212C95D573DB92D6EB0D0D891FBD68BB212C95
然后将这个SessionKey使用非对称加密(encryptedTk)发到服务器,服务器返回一个encryptedCk
req:
https://base.95516.com/app/inApp/sys/init
{"clientVersion":"267","currentKeyNo":"2","encryptedTk":"71E76318B5558B6D48C19C29322B29D275B6AA50E3B8D09E453379AB41E8BD3DC3C9EC185D5E224FD5B238859663A3016148B79AF34B03ECCF9EBD0B89231BD74A6E0EE5580D9927EE2D9653FDDDE90D31E7FC9F445FCA3174237081E73435CB3A7CD842AC89DB2EB31BA65EC7BE056E274E8AB56E8D554DD61AA989CF98C963F087C4A644598F7E9AA4A77053F34D0076A5B7B35EB34CFFCBC81C3B1888DC57DF9A3612D085813605296BF53226048889986FC533323766D227A864DB6BBF28007CA03F5B6ACABA2CB9E6BEB679BAEC36CD365A98585BF6673F86F7290591FAB82FB3EFA7B5E6B72E43BCBEB4FF2089FE83ECC3ACF46ED6480C8D0E741BCC98","secretKeyType":"INT","encryptedVid":"6FECA3B000BE953789C95671641E30562585B38EF0E98253","maxKeyNo":"3","osName":"Android"}

resp:
{"resp":"00","msg":"成功","params":{"encryptedVid":"0ede0efa754ea97f879b1f7358b3d33a1cbccabe23cffa32","encryptedCk":"fecc176553941f9cb22083c725d04670808496e7318a682d7e73220b652587fefecc176553941f9cb22083c725d04670","sid":"6660ea5529394a33931e9a2c89f34619a","gray":"0","timestamp":"1653403246719","respCode":"00","respMsg":"成功"}}

encryptedCk使用3-DES临时密钥解密后就是后续通讯使用的密钥

des3_decrypt(key='891FBD68BB212C95D573DB92D6EB0D0D891FBD68BB212C95', data='fecc176553941f9cb22083c725d04670808496e7318a682d7e73220b652587fefecc176553941f9cb22083c725d04670')
output: 0ede0efa754ea97f879b1f7358b3d33a1cbccabe23cffa32
*/
Java.perform(function () {
console.log("start...");
var IJniInterface = Java.use("com.unionpay.utils.IJniInterface");
IJniInterface.f.overload().implementation = function() {
var retval = this.f()
console.log("makeSessionKey: " + retval)
return retval;
}

IJniInterface.b.overload('java.lang.String').implementation = function(arg_0) {
console.log("refreshSessionKey: " + arg_0);
this.b(arg_0);

}



});

Frida内存搜索、调用

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
75
76
77
78
79
80
81
82
83
// getkey.js

console.log('get des3key...')
// 从内存中搜索des3密钥
/**
* 3-DES(Triple DES)密钥为24字节
* 云闪付的3-DES密钥有特殊格式,前8字节和最后8字节完全相同,因此可以利用这个特征在内存中搜索密钥
*
*/
var memory_ranges =Process.enumerateRanges('rw-')
for(var i=0; i<memory_ranges.length; ++i){
if(memory_ranges[i].file != undefined && memory_ranges[i].file.path.indexOf('libencrypt')>-1 && memory_ranges[i].protection=='rw-'){

var base = memory_ranges[i].base
var size = memory_ranges[i].size
base = parseInt(base)

if(size <= 4096){
continue
}
// send(memory_ranges[i])
for(var addr = base; addr < base+size - 0x20 ; addr+=0x10){
var a = ptr(addr).readU32()
var b = ptr(addr+0x20).readU32()
if(a!=0 && a==b){
var c = ptr(addr+0x4).readU32()
var d = ptr(addr+0x20+0x4).readU32()

var e = ptr(addr+0x8).readU32()
var f = ptr(addr+0x20+0x8).readU32()

var g = ptr(addr+0xc).readU32()
var h = ptr(addr+0x20+0xc).readU32()

if(c==d && e==f && g==h){
console.log(ptr(addr).readCString())
}

}
}


}
}
console.log('done')

Java.perform(function () {

// set proxy
var URL = Java.use('java.net.URL')
URL.openConnection.overload().implementation = function(){
var Proxy = Java.use('java.net.Proxy');
var InetSocketAddress = Java.use("java.net.InetSocketAddress");
var ProxyType = Java.use('java.net.Proxy$Type');
// !!!!!! 修改此处的代理IP、端口
var proxy = Proxy.$new(ProxyType.HTTP.value, InetSocketAddress.createUnresolved("192.168.191.1", 8888));
return this.openConnection(proxy)
}

// disable sslpinning
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {

// Skip all the logic and just return the chain again :P
return untrustedChain;
}

var k = Java.use("com.unionpay.network.k");
k.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(arg_0, arg_1) {
}

k.checkClientTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(arg_0, arg_1) {

}

// get encryptedVid
var e = Java.use("com.unionpay.data.e");
var encryptedVid = e.a(null).c('encryptedVid', 1)
console.log('encryptedVid: ', encryptedVid)

});


Python抢券

image-20220527112441736

滑动验证码“漏洞”

请求滑动验证码数据时,返回了需要滑动的距离(capAnswer)。

image-20220601140851904

2023.04.10 更新

老版本(v9.0.6)被强制升级,更新了目前google play上的最新版本,和iOS一样使用了SM4-CTR代替了3DES加密。

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
import binascii
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend


def sm4_ctr_decrypt(key, iv, data):
if not isinstance(key, bytes):
key = key.encode()
if not isinstance(iv, bytes):
iv = iv.encode()
if not isinstance(data, bytes):
data = data.encode()
cipher = Cipher(algorithms.SM4(key), modes.CTR(iv), backend=default_backend())
result = cipher.decryptor().update(data)
return result.decode()

def sm4_ctr_encrypt(key, iv, data):
if not isinstance(key, bytes):
key = key.encode()
if not isinstance(iv, bytes):
iv = iv.encode()
if not isinstance(data, bytes):
data = data.encode()
cipher = Cipher(algorithms.SM4(key), modes.CTR(iv), backend=default_backend())
result = cipher.encryptor().update(data)
return binascii.hexlify(result).decode().upper()

if __name__ == '__main__':
key = binascii.unhexlify("2513D8EA5B8DB0EB70B0BFFDCED98F03")
iv = key
s = '030f80478267b92ede56f9f355bbb7d57db150c5fe9c039158809cf65403f977'
enc_data = binascii.unhexlify(s)
dec_data = sm4_ctr_decrypt(key, iv, enc_data)
print("sm4_key:", dec_data)
1
sm4_key: 7efef285cf73c5b01c1838ab286b28ff
1
2
IJniInterface->makeNSASessionKey (retType: java.lang.String): 2513D8EA5B8DB0EB70B0BFFDCED98F03
IJniInterface->refreshNSASessionKey (argType: java.lang.String): 030f80478267b92ede56f9f355bbb7d57db150c5fe9c039158809cf65403f977

内存搜索sm4_key:

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
function find_key() {
var memory_ranges = Process.enumerateRanges('rw-')
for (var i = 0; i < memory_ranges.length; ++i) {
if (memory_ranges[i].file != undefined && memory_ranges[i].file.path.indexOf('libencrypt') > -1 && memory_ranges[i].protection == 'rw-') {

var base = memory_ranges[i].base
var size = memory_ranges[i].size
base = parseInt(base)

if (size <= 4096) {
continue
}
for (var addr = base + size - 0x20; addr > base + 0x20; addr -= 1) {
var flag = true
for (var j = 0; j < 32; ++j) {
var byte = ptr(addr + j).readU8()
if (byte < 0x30 || byte > 0x7a) {
flag = false
break
}
}
if (flag) {
console.log('success', ptr(addr).readCString())
break
}

}

}
}
console.log('done')
}

请求签名

签名这部分没有变化,依旧是SM3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def calc_digest(CKWithSM3,sid,encryptedVid,timeStamp, nonce, req_data, dcTag=None):
t = CKWithSM3 + req_data
n = len(t)%7
if n == 0:
n = 7
CKWithSM3_ = expand(CKWithSM3, n)
req_data_ = expand(req_data, n)
timeStamp_ = expand(timeStamp, n)
nonce_ = expand(nonce, n)
encryptedVid_ = expand(encryptedVid, n)
sid_ = expand(sid, n)
if dcTag:
dcTag_ = expand(dcTag, n)
data = f'{CKWithSM3_}{sid_}{encryptedVid_}{req_data_}{nonce_}{timeStamp_}{dcTag_}'
else:
data = f'{CKWithSM3_}{sid_}{encryptedVid_}{req_data_}{nonce_}{timeStamp_}'
#print(data)
return get_digest(data)
1
2
3
4
5
6
7
8
9
CKWithSM3_: EB80BF4CB90C9E345931D831FD30BF1ABDDC989C4438896A48920BE709EB9251

CKWithSM3_: EB80BF4CB90C9E345931D831FD30BF1ABDDC989C4438896A48920BE709EB9251 E
sid_: ee67374459bb40689a9f96366a2594adaa000000 e
encryptedVid_: eIoDmZtWRWSuIWRW0P0jtw== e
req_data_: E4CEA210F9584CA5DCCEC92D3E09EDD0E6C704262DC3D9FA4968F344E98DAA02949A6CDAAD355B23989E34BB4CFFC1141098CF9A21272BE2CA88EB99C770E821B3887F7A8F0C56841B55C10C516D93D613D0CEDCA9CC22C36D54A8408A1CD647EBC1D64806A6D3370A6EB646E920CB68EBE601A5A06B9C4D53A9082FA7B4253B0303B413BAEA9EC4266D607DF0D19D93E808E6B8BD4EE670BCD1F71832210D8CA33B2AD4D4302295675E8AC1CB06BC65C4007461AB79D2FC68D2F841749CC1A8BFC6C02BC57EF6C62131D51FCA26DF315F9F61D56C662B8E49C843D667CCA8DB2E7A3E913914DAF4CC20AB99271CC0A4E6E9974B0A9A5288816A0DA0 E
nonce_: a71c3e11-3337-4aa5-ab58-1712b3215b4f a
timeStamp_: 1681103776138 1
dcTag_ AB A

2023.06.21 更新获取encryptedVid与请求头

获取encryptedVid

image-20230621115648984

image-20230621121650634

image-20230621121726342

获取请求头

image-20230621121431921

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
var e = Java.use("com.unionpay.data.e");
var e_instance = e._a.value
var encryptedVid = e_instance.c('encryptedVid', 1)
console.log(encryptedVid)

var sid = null
var z = Java.use("com.unionpay.network.z");
z.i.overload().implementation = function () {
var retval = this.i()
var map = retval;
var header_data = {}
console.log('------------------')
if (map != null) {
var keyset = map.keySet();
var it = keyset.iterator();
while (it.hasNext()) {
var keystr = it.next().toString();
var valuestr = ''
if (map.get(keystr) != null) {
valuestr = map.get(keystr).toString();
} else {
valuestr = 'null'
}
header_data[keystr] = valuestr
}
}

if(map.get('sid')!==null && sid !== map.get('sid').toString()){
var req_header = JSON.stringify(header_data)
console.log(req_header)
}
console.log('------------------')
return retval;
}