一 目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 目标:唯品会搜索功能
目标:搜索
版本:v7.83.3.apk

## 破解步骤
【第1步】device_reg,注册设备 ----》完成
-devices_token:uuid
-skey:固定的
-authorization:sha1加密,有盐,加了两次密
【第2步】getTokenByFP,返回值中获取vcspToken
【第3步】generate_token,返回did
- 发送请求需要利用 vcspToken
- 返回值:did
【第4步】执行搜索

二 getTokenByFP

image-20240517181844274

1
2
3
4
5
6
7
8
9
10
11
12
# 请求地址:
https://vcsp-api.vip.com/token/getTokenByFP
# 请求方式:
get
# 请求参数:
vcspKey=4d9e524ad536c03ff203787cf0dfcd29
# 请求头:
vcspauthorization vcspSign=05a68135d2bfd322e3a22f95bbc25a24c777f387

# 需要破解的:
-vcspKey
-vcspauthorization实际上是vcspSign

2.1 vcspKey破解

2.1.1 固定值

1
#1 上节课在hook  KeyInfo.getNavInfo  打印出了vcspKey是固定的
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
# 现象:hook就闪退(反调试) 删除 `libmsaoaidsec.so`
import frida
import sys

rdev = frida.get_remote_device()
pid = rdev.spawn(["com.achievo.vipshop"])
session = rdev.attach(pid)

scr = """
Java.perform(function () {
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");

KeyInfo.getNavInfo.implementation = function (ctx, str) {
console.log("-----------------");
console.log("参数==>", str);
var res = this.getNavInfo(ctx, str);
console.log("返回的值==>", res);
return res;
}
});
"""


script = session.create_script(scr)


def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()
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
设备1
-----------------
参数==> app_name
返回的值==> shop_android
-----------------
参数==> vcsp_key
返回的值==> 4d9e524ad536c03ff203787cf0dfcd29
-----------------
参数==> api_key
返回的值==> 23e7f28019e8407b98b84cd05b5aef2c

-----------------
参数==> skey
返回的值==> 6692c461c3810ab150c9a980d0c275ec
-----------------
参数==> skey
返回的值==> 6692c461c3810ab150c9a980d0c275ec

设备2
-----------------
参数==> app_name
返回的值==> shop_android
-----------------
参数==> vcsp_key
返回的值==> 4d9e524ad536c03ff203787cf0dfcd29
-----------------
参数==> api_key
返回的值==> 23e7f28019e8407b98b84cd05b5aef2c
-----------------
参数==> skey
返回的值==> 6692c461c3810ab150c9a980d0c275ec
-----------------
参数==> skey
返回的值==> 6692c461c3810ab150c9a980d0c275ec

skey可以不传,传了也是个固定的值。

1
skey = 6692c461c3810ab150c9a980d0c275ec

2.1.2 搜索

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
# 1 搜索:vcspKey
# 2 搜索结果两个,都是一样的,看第一个
# 3 查看 VCSPCommonsConfig.getAppKey()
public static String getAppKey() {
return appKey;
}
# 4 查找在哪里赋值
public static void setAppKey(String str) {
appKey = str;
}

# 5 查找用例,一个结果
VCSPCommonsConfig.setAppKey(KeyInfoFetcher.getInfo(VCSPCommonsConfig.getContext(), "vcsp_key"));

#6 执行KeyInfoFetcher.getInfo 得到---》又是熟悉的反射
public static String getInfo(Context context, String str) {
try {
if (clazz == null || object == null || method == null) {
int i10 = KeyInfo.f69594a;
clazz = KeyInfo.class;
object = KeyInfo.class.newInstance();
method = clazz.getMethod("getInfo", Context.class, String.class);
}
return (String) method.invoke(object, context, str);
} catch (Exception e10) {
VCSPMyLog.error(KeyInfoFetcher.class, e10);
return "";
}
}
# 7 查看KeyInfo的getInfo--》跟上面破解 skey一样 传入的参数不同,结果是固定的
public static String getInfo(Context context, String str) {
try {
try {
return getNavInfo(context, str);
} catch (Throwable th2) {
return "KI gi: " + th2.getMessage();
}
} catch (Throwable unused) {
SoLoader.load(context, LibName);
return getNavInfo(context, str);
}
}

image-20231103175909225

2.2 vcspSign破解

2.2.1 固定值

1
2
3
4
# 这个其实固定就好。
多次请求发现是固定 + 在请求中参数中只有固定的vcspKey(固定),所以,得到的结果理应固定。

# 05a68135d2bfd322e3a22f95bbc25a24c777f387

2.2.2 搜索

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
# 1 搜索 vcspauthorization
# 2 一个位置,就是我们要找的
# 3 str的生成位置
public Map<String, String> getApiSign(VCSPNetworkParam vCSPNetworkParam) {
String str;
try {
BaseSDK.getSecurityBasicService();
str = VCSPSecurityBasicService.apiSignVcspToken(VCSPCommonsUtils.getUrlParams(getContext(), vCSPNetworkParam.url), VCSPCommonsConfig.getIAppInfo().getUserTokenSecret());
} catch (Exception e10) {
VCSPMyLog.error(TokenNetworkServiceConfig.class, e10);
str = "";
}
HashMap hashMap = new HashMap(1);
hashMap.put("VCSPAuthorization", "vcspSign=" + str);
return hashMap;
}
# 4 VCSPSecurityBasicService.apiSignVcspToken()
public static String apiSignVcspToken(TreeMap<String, String> treeMap, String str) throws Exception {
return VCSPSecurityConfig.getMapParamsSign(VCSPCommonsConfig.getContext(), treeMap, str, true);
}

#5 VCSPSecurityConfig.getMapParamsSign
public static String getMapParamsSign(Context context, TreeMap<String, String> treeMap, String str, boolean z10) {
String str2 = null;
if (treeMap != null) {
boolean z11 = false;
Set<Map.Entry<String, String>> entrySet = treeMap.entrySet();
if (entrySet != null) {
Iterator<Map.Entry<String, String>> it = entrySet.iterator();
while (true) {
if (it == null || !it.hasNext()) {
break;
}
Map.Entry<String, String> next = it.next();
if (next != null && next.getKey() != null && ApiConfig.USER_TOKEN.equals(next.getKey()) && !TextUtils.isEmpty(next.getValue())) {
z11 = true;
break;
}
}
}
if (z11) {
if (TextUtils.isEmpty(str)) {
str = VCSPCommonsConfig.getTokenSecret();
}
str2 = str;
}
return getSignHash(context, treeMap, str2, z10);
}
return null;
}
# 6 getSignHash
public static String getSignHash(Context context, Map<String, String> map, String str, boolean z10) {
try {
return gs(context.getApplicationContext(), map, str, z10);
} catch (Throwable th2) {
VCSPMyLog.error(clazz, th2);
return "error! params invalid";
}
}
# 7 最终找到 gs的位置,我们之前看过,加密方式跟authorization一样,但是加密明文是固定的,所有结果固定

# 8 hook--getByteHash

image-20240517181912030

2.2.2.1 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
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
function do_hook() {

var addr = Module.findExportByName("libkeyinfo.so", "getByteHash");
console.log(addr); //0xb696387d


Interceptor.attach(addr, {
onEnter: function (args) {
this.x1 = args[2];
},
onLeave: function (retval) {
console.log("--------------------")
console.log(Memory.readCString(this.x1));
console.log(Memory.readCString(retval));
}

})

}

function load_so_and_hook() {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
// console.log("[dlopen:]", path);
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf("libkeyinfo.so") !== -1) {
console.log("[dlopen:]", this.path);
do_hook();

}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();

this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf("libkeyinfo.so") !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
do_hook();

}
}
});
}

load_so_and_hook();

// frida -U -f com.achievo.vipshop -l delay_hook.js

// 根据抓包抓到的数据,去搜索

/*
--------------------
da19a1b93059ff3609fc1ed2e04b0141vcspKey=4d9e524ad536c03ff203787cf0dfcd29
5a7c831821536f5a9d5244b99af681226dc8a277
--------------------
da19a1b93059ff3609fc1ed2e04b01415a7c831821536f5a9d5244b99af681226dc8a277
05a68135d2bfd322e3a22f95bbc25a24c777f387
--------------------
*/

2.2.2.2 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import hashlib

data_string ="da19a1b93059ff3609fc1ed2e04b0141vcspKey=4d9e524ad536c03ff203787cf0dfcd29"

# sha1加密
hash_object = hashlib.sha1()
hash_object.update(data_string.encode('utf-8'))
arg7 = hash_object.hexdigest()
print(arg7) # 5a7c831821536f5a9d5244b99af681226dc8a277

x = "da19a1b93059ff3609fc1ed2e04b0141"+arg7
# sha1加密
hash_object = hashlib.sha1()
hash_object.update(x.encode('utf-8'))
arg7 = hash_object.hexdigest()
print(arg7)#05a68135d2bfd322e3a22f95bbc25a24c777f387

2.3 代码整合

1
2
3
4
5
6
7
8
9
10
import requests

res = requests.get(
url="https://vcsp-api.vip.com/token/getTokenByFP?vcspKey=4d9e524ad536c03ff203787cf0dfcd29",
headers={
"vcspauthorization": "vcspSign=05a68135d2bfd322e3a22f95bbc25a24c777f387"
}
)

print(res.text)

三 generate_token

3.1 抓包

1
2
3
4
5
6
7
8
9
10
11
12
13
# 请求地址:
https://mapi.appvipshop.com/vips-mobile/rest/device/generate_token
# 请求方式:
POST
# 请求头
authorization OAuth api_sign=eef0dd9d6ca762441659024e00e3bdf92574a9dd
# 请求体:
api_key 23e7f28019e8407b98b84cd05b5aef2c
did
edata # 只有它是变化的
eversion 0
skey 6692c461c3810ab150c9a980d0c275ec
timestamp 1692699853

image-20240517181926257

3.2 搜索分析

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
# 1 反编译,搜索  "edata"
# 2 找到edata是一个常量,查找 EDATA这个常量的用例
# 3 找到GobalConfig.encodeStr(context, sb2.toString())比较多,我们点第二个进去看
public static String encodeStr(Context context, String str, String str2, String str3, int i10) {
try {
d.f(GobalConfig.class, "SecurityConfig encodeStr");
return VCSPSecurityConfig.encodeStr(getApplicationContext(context), str, i10);
} catch (Throwable th2) {
d.d(GobalConfig.class, th2);
return null;
}
}

# 4 查看 VCSPSecurityConfig.encodeStr
public static String encodeStr(Context context, String str, int i10) {
try {
VCSPMyLog.info(VCSPSecurityConfig.class, "VCSPSecurityConfig encodeStr");
return es(context.getApplicationContext(), str, null, null, i10);
} catch (Throwable th2) {
VCSPMyLog.error(VCSPSecurityConfig.class, th2);
return null;
}
}

# 5 查看es---》又看到熟悉的样子---》通过反射,执行可KeyInfo中的es方法
private static String es(Context context, String str, String str2, String str3, int i10) {
try {
if (clazz == null || object == null) {
synchronized (lock) {
initInstance();
}
}
if (esMethod == null) {
esMethod = clazz.getMethod("es", Context.class, String.class, String.class, String.class, Integer.TYPE);
}
return (String) esMethod.invoke(object, context, str, str2, str3, Integer.valueOf(i10));
} catch (Exception e10) {
e10.printStackTrace();
return "Exception es: " + e10.getMessage();
} catch (Throwable th2) {
th2.printStackTrace();
return "Throwable es: " + th2.getMessage();
}
}
# 6 查看KeyInfo中的es方法
public class KeyInfo {
private static final String LibName = "keyinfo";
static {
System.loadLibrary(LibName);
}
public static String es(Context context, String str, String str2, String str3, int i10) {
try {
try {
return esNav(context, str, str2, str3, i10);
} catch (Throwable th2) {
return "KI es: " + th2.getMessage();
}
} catch (Throwable unused) {
SoLoader.load(context, LibName);
return esNav(context, str, str2, str3, i10);
}
}

private static native String esNav(Context context, String str, String str2, String str3, int i10);

}

# 7 核心是:private static native String esNav---》执行了jni的方法得到的加密串

# 8 Hook esNav查看参数和返回值

# 9 反编译libkeyinfo.so 找 esNav 方法读源码

image-20240517181942679

image-20240517181950203

image-20240517181958181

image-20240517182007180

image-20240517182019306

image-20240517182027767

image-20240517182037636

image-20240517182045846

3.3 hook–esNav查看参数和返回值

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
# 清除数据,重新运行
import frida
import sys

rdev = frida.get_remote_device()

session = rdev.attach("唯品会")

scr = """
Java.perform(function () {
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");

KeyInfo.esNav.implementation = function (ctx, str, str2,str3, i10) {
console.log("-----------------gsNav-----------------");
console.log("参数==>", str);
console.log("参数==>", str2);
console.log("参数==>", str3);
console.log("参数==>", i10);
var res = this.esNav(ctx, str, str2,str3, i10);
console.log("返回的值==>", res);
return res;
}
});
"""
script = session.create_script(scr)

def on_message(message, data):
print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()


'''跟抓包得到的是一样的

-----------------gsNav-----------------
参数==> app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_1692701399867&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1MjkzMzM5fHx8.1fcc47f53744a470d205788e5e2991d1
参数==> null
参数==> null
参数==> 0
返回的值==> YWM2ZGNlNWE2MDQ3Nzg2MqBCOPIUaF8TUJgfmd3LW9JTzQrwc3vTmGfX8Nz6y7XQWPd0py0yv8KTR1qlpMnvxFZjphKOiko7yiJN5R9UYsIbwIjfsTdUtp5V8hxTqJpllOI6Y80XX00QbbzLN4ehYqd6v9lslVQzcjM13tG5ZqhmicHk1+TzICmhGViR1mzHVZcjDL7ZZqUWcpGG4d1wu9YvzOQKSQGdDVBX0inATZc9P3T9e+x4wg1rCy/tHidu+wvXzPQgUd+Ah2zgGP/kdYI8dQQ0W/7BVjchOV8fYh+IjrQtHcQL8KmxF8mqjmL4Yz1ceY0z7bGK7itabbSLakDaPNJtfIN7zHllOEnzUNgq1fXd64dMZb+rDZlqhVsjct7bumVnvlmaZ1MQdQ0tGrYl4q2ysb8lfCv96IL414t/OWRd6x7XlX0tqRe7/6rAumohB3WGGrvM6tZX2tBUcGjU5eIeD64MXM1GDQTJi+HHSl9TkPBmMPnXb5hiWdy+GWhi38lAkH+6WWteq3wk6Uzxq/lyk9aLKeQxBzzvUiSOOFibtYEV+91+j9gq0yteIYtUEoqEKIrKOxdJOVPDda3a7P6iFz4opjirlozqwapxeb/4pEV7a2BkrXhCkAQKtcSlFLxNjPyc5ra11LuZKs90Z8I/msB5uAvxEAX0kFu3tR2to85zdNH9tjRjgt4m21PLeyXsxMulTaFxe95mEQfX2/sy04YiqMz0y4ah3cZ5pnTfGbgsuao8Am1TnAhnRyV82ZHzyMrLHPXEA3IG6gYV9fJiMiOHbH4k3zHAA6EkV5x31wXUE59I6fN4+bCk25jPWyxS5gaGt8FICmRz41pPj8zqDykb6VUdxTLEnWJi8siBQrTYUen6eOFkqbz6ZZrfBApeo85DGnK22D+kN2N0pPekx8P63Ccme3qrcQTKwz4hX/LIYySvemGSvUvbI7o7X4MqEJSQLMQ2jIrjv0esYcPctD+BwlvagdICRKd6ooDMhOCUQmnbotOIOe5IBL/DCf+vl2CQQMkWn3LdXEjecnQIcjSJhYmixN0ukS4/KxseIH7LY5bmxJ7Lycv3zSAKBavg3e1dMOkMJK45VQehGCg4cCSPUo701tT+rgT4MILyVkXBAgabPTuqZC8bdphuERsqpf/15gwUg6wKSQQzn+LIJ32AUAOn+GZbJEyfCOZSdwk3CFwI4hTeYLH/FX3Y5S4u+UvUO83AU/Bbow==

'''

image-20240517182134296

3.3 分析so–在libkeyinfo.so找esNav

image-20240517182147710

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# 1 查看Java_com_vip_vcsp_KeyInfo_esNav
int __fastcall Java_com_vip_vcsp_KeyInfo_esNav(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
{
int v9; // r5
int v11; // [sp+4h] [bp-Ch]

v11 = a4;
if ( j_Utils_ima(a1, a2, a3) )
v9 = j_Functions_es(a1, a4, a5, a6, a7, v11); # 调用了j_Functions_es得到v9
else
v9 = 0;
j_Utils_checkJniException(a1);
return v9;
}

# 2 查看j_Functions_es
int __fastcall j_Functions_es(int a1, int a2, int a3, int a4, int a5, int a6)
{
return Functions_es(a1, a2, a3, a4, a5);
}

# 3 查看Functions_es
jstring __fastcall Functions_es(JNIEnv_ *a1, int a2, int a3, int a4, int a5){
#####很多代码########,在调用java的方法
v22 = a1->functions->FindClass(a1, "javax/crypto/Cipher");
v23 = a1->functions->GetStaticMethodID(a1, v22, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
v24 = a1->functions->NewStringUTF(a1, "AES/CBC/PKCS5Padding");
v57 = a1->functions->CallStaticObjectMethod((JNIEnv *)a1, v22, v23, v24);
if ( v20 )
{
v25 = a1->functions->NewStringUTF(a1, "AES");
v26 = a1->functions->FindClass(a1, "javax/crypto/spec/SecretKeySpec");
v27 = a1->functions->GetMethodID(a1, v26, "<init>", "([BLjava/lang/String;)V");
v21 = a1->functions->NewObject((JNIEnv *)a1, v26, v27, v20, v25);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v20);
}
# #####很多代码########,在在使用bashe64
if ( !v50 )
{
qmemcpy(v39, v58, v46);
qmemcpy((char *)v39 + v46, v48, v47);
v45 = 0;
*((_BYTE *)v39 + v47 + v46) = 0;
a1->functions->ReleaseByteArrayElements((JNIEnv *)a1, v44, v48, 0);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v44);
v52 = (char *)j_base64_encode((char *)v39);
free(v39);
if ( v52 )
{
v45 = a1->functions->NewStringUTF(a1, v52);
free(v52);
}
}
a1->functions->DeleteLocalRef((JNIEnv *)a1, v56);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v57);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v54);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v55);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v53);
}
return v45;

}


#4 根据代码分析出:大致逻辑
-把一些明文字符串通过调用java的aes加密,得到结果后使用base64转码

# 6 所以我们要找出aes的
明文
key
iv

# 7 分析代码
v22 = a1->functions->FindClass(a1, "javax/crypto/Cipher");
v23 = a1->functions->GetStaticMethodID(a1, v22, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
v24 = a1->functions->NewStringUTF(a1, "AES/CBC/PKCS5Padding");
v57 = a1->functions->CallStaticObjectMethod((JNIEnv *)a1, v22, v23, v24);
###### 代码等同于
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding")


# 8 继续分析下面的代码
if ( v20 )
{
v25 = a1->functions->NewStringUTF(a1, "AES");
v26 = a1->functions->FindClass(a1, "javax/crypto/spec/SecretKeySpec");
v27 = a1->functions->GetMethodID(a1, v26, "<init>", "([BLjava/lang/String;)V");
v21 = a1->functions->NewObject((JNIEnv *)a1, v26, v27, v20, v25);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v20);
}
###### 代码等同于
SecretKeySpec spec =new SecretKeySpec(第一个参数:v20,第二个参数:v25 "AES")

# 9 查找v20是什么--》或者直接找key和iv,因为key和iv是不变的--》直接hook--SecretKeySpec构造方法---》读取第一个参数,直接得到key

# 10 继续分析iv
v34 = a1->functions->FindClass(a1, "javax/crypto/spec/IvParameterSpec");
v35 = a1->functions->GetMethodID(a1, v34, "<init>", "([B)V");
v37 = a1->functions->NewObject((JNIEnv *)a1, v36, v35, v32);
### 代码等同于,传入v32,v32就是iv
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());

# 11 上述代码分析完,核心逻辑如下
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding")
SecretKeySpec spec =new SecretKeySpec(第一个参数:v20,第二个参数:v25 "AES")
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
cipher.init(1, spec, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(byteContent); #传入明文字节数组,返回密文字节数组

# 12 所以 加密后密文的字节数组是:v44,明文的字节数组是 v41

image-20240517182205883

1
2
3
4
5
6
7
8
9
10
public static String encryptData(String key, String iv, String content) throws Exception {
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKeySpec, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(byteContent);
return Base64.encodeBase64String(encryptedBytes);
}

3.4 破解aes的key

3.4.0 分析过程

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1 上面我们分析到 aes的key是 v20,我们找v20怎么来的
v20 = a1->functions->NewByteArray(a1, 16); # 生成一个空的16位的数组
a1->functions->SetByteArrayRegion((JNIEnv *)a1, v20, 0, 16, v19); # 调用它把数据放入,就是对字节数组进行赋值,把v19的的前16个字节放入

# 2 继续查找v19怎么来的--》通过 md5加密得到--》s通过md5加密得到返回值是aes的key
v18 = strlen(s);# s是字符串,v18是字符串的长度
v19 = (const jbyte *)j_getMD516(v60, s, v18);


# 3 所以我们做如下操作
c层hook:j_getMD516-->实际是:getMD516(a1, a2, a3)
java层hook:SecretKeySpec spec =new SecretKeySpec(第一个参数:v20,第二个参数:v25 "AES")

3.4.1 c层hook–getMD516–需要延迟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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function do_hook() {
var getMD516 = Module.findExportByName("libkeyinfo.so", "getMD516");
console.log('getMD516 addr = ', getMD516);
if (getMD516) {
Interceptor.attach(getMD516, {
onEnter: function (args) {
// args 传进三个参数:v60,s, v18,s是字符串,所以拿了:args[1],v18是数字,是字符串的长度
//hexdump('字符串',数字)---》拿这个字符串的数字长度的16进制
console.log('2.getMD5参数:', hexdump(args[1], {length: args[2].toInt32()}), "\n");
// console.log('2.getMD5 参数:', hexdump(args[1]), "\n");
},
onLeave: function (retval) {
// console.log('3.getMD5 返回值:', hexdump(retval), "\n");
console.log('3.getMD5=key 返回值:', hexdump(retval, {length: 16}), "\n");
}
});
}

}

function delay_hook(so_name, hook_func) {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
// console.log("[dlopen:]", path);
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("[dlopen:]", this.path);
hook_func();

}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
this.path = path;
},
onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
hook_func();
}
}
});
}
// 返回值是aes的key

3.4.2 java层hook–SecretKeySpec

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
// 这个可能app内部使用aes加密,会有很多地方调用,会输出很多
// 所以我们可以结合前面hook了KeyInfo的esNav,它执行之后再hook此代码
function java_hook() {
Java.perform(function () {
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("-----------------------SecretKeySpec---------------------------");
console.log("aes 的 key bytes=", JSON.stringify(key));
console.log("aes 的 key hex =", ByteString.of(key).hex());
console.log("aes 的 key 字符串", ByteString.of(key).utf8());
}
var res = this.$init(key, name);
return res;
}
});
}

// 1 hook java 的传入的参数,必须要跟hook的c的 返回值一样,才ok

// 2 hook SecretKeySpec 可能很多地方在用,会输出很多东西
// 3 hook getMD516 也可能很多地方在用,会输出很多
// 4 在c 中 getMD516执行完了---》调用了java的SecretKeySpec完成实例化---》这堆操作,都在com.vip.vcsp.KeyInfo里的esNav 内部执行的
-代码执行流程:java的KeyInfo里的esNav方法执行---》getMD516得到aes的key---》调用java的SecretKeySpec完成实例化
-由于上面hook SecretKeySpec 或 getMD516 会有很多输出
-我们这样做:
hook---》 KeyInfo里的esNav,在它开始 打印开始,在它结束打印结束
====>开始
getMD516
SecretKeySpec
====>结束

// 5 hook 3个东西
hook---》 KeyInfo里的esNav
hook--》getMD516
hook--》SecretKeySpec


// 6 清除数据启动app---》去hook--》hook时机很重要---》如果慢了,执行完了---》如果快了,so文件还没加载
---》getMD516找不到---》需要延迟hook

3.4.3 c层和java层一起hook–在esNav中间的

3.4.3.1 先hook–KeyInfo的esNav和SecretKeySpec

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
// hook 不到,要卸载app再重装

Java.perform(function () {
//1 hookesNav--我们只看 开始和结束之间的hook输出即可
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("====================开始================", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("====================结束================>", res);
return res;
}

//2 hook-- java层--SecretKeySpec
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("----------------SecretKeySpec--------------------");
console.log("java key hex =", ByteString.of(key).hex());
}
var res = this.$init(key, name);
return res;
}



});



// frida -U -f com.achievo.vipshop -l 2.hook.js



/*
====================开始================ app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%222%2C%22ah3%22%3A%22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8
ca-cbe24c03979d_shop_android_1692869734529&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBk kZmNkMjl8fHwxNjk1NDYxNjE2fHx8.3869a5558ebdb2e1df72774a22b213be
-----------------------SecretKeySpec---------------------------
java key hex = cdd17ab29b84b32552ddcfbb4abf0225
====================结束================>
*/

// java层hook到的 key
java key hex = cdd17ab29b84b32552ddcfbb4abf0225

3.4.3.2 先hook–KeyInfo的esNav和SecretKeySpec和c层的getMD516

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
// 有时候会hook不到,因为so文件没加载,内存中还没有

Java.perform(function () {
//1 hookesNav--我们只看 开始和结束之间的hook输出即可
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("====================开始================", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("====================结束================>", res);
return res;
}

//2 hook-- java层--SecretKeySpec
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("----------------SecretKeySpec--------------------");
console.log("java key hex =", ByteString.of(key).hex());
}
var res = this.$init(key, name);
return res;
}


// 3 hook c中的 getMD516
var getMD516 = Module.findExportByName("libkeyinfo.so", "getMD516");
console.log('getMD516 addr = ', getMD516);
if (getMD516) {
Interceptor.attach(getMD516, {
onEnter: function (args) {
console.log('getMD5参数:', hexdump(args[1], {length: args[2].toInt32()}), "\n");
},
onLeave: function (retval) {
console.log('getMD5=key 返回值:', hexdump(retval, {length: 16}), "\n");
}
});
}

});

3.4.3.3 先hook–KeyInfo的esNav和SecretKeySpec和c层的getMD516–延迟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
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
function java_hook() {
Java.perform(function () {
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");

KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("-----------------gsNav-----------------");
console.log("开始================>", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("结束================>", res);
return res;
}

var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("-----------------------SecretKeySpec---------------------------");
//console.log("4.key bytes=", JSON.stringify(key));
console.log("java key hex =", ByteString.of(key).hex());
//console.log("4.key", ByteString.of(key).utf8());
}
var res = this.$init(key, name);
return res;
}
});

}

function delay_hook(so_name, hook_func) {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
// console.log("[dlopen:]", path);
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("[dlopen:]", this.path);
hook_func();
}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
this.path = path;
},
onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
hook_func();
}
}
});
}

function so_hook() {
var getMD516 = Module.findExportByName("libkeyinfo.so", "getMD516");
console.log('getMD516 addr = ', getMD516);

if (getMD516) {
Interceptor.attach(getMD516, {
onEnter: function (args) {
console.log('getMD5参数:', hexdump(args[1], {length: args[2].toInt32()}), "\n");
// console.log('2.getMD5 参数:', hexdump(args[1]), "\n");
},
onLeave: function (retval) {
// console.log('3.getMD5 返回值:', hexdump(retval), "\n");
console.log('getMD5=key 返回值:', hexdump(retval, {length: 16}), "\n");
}
});
}
}

delay_hook("libkeyinfo.so", so_hook)
java_hook()

//frida -U -f com.achievo.vipshop -l 4.hook.js


/*


开始================>
app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%
22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+
2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as
3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-
36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_s
hop_android_1692870589136&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDYyNTI5fHx8.ca75da1fb1e7a26c192d29ce71e62e47


getMD5参数: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
bf14a030 61 65 65 34 63 34 32 35 64 62 62 32 32 38 38 62 aee4c425dbb2288b
bf14a040 38 30 63 37 31 33 34 37 63 63 33 37 64 30 34 62 80c71347cc37d04b


getMD5=key 返回值: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
bf149fb8 cd d1 7a b2 9b 84 b3 25 52 dd cf bb 4a bf 02 25 ..z....%R...J..%
-----------------------SecretKeySpec---------------------------
java key hex = cdd17ab29b84b32552ddcfbb4abf0225


结束================>

*/

3.4.4 最终hook得到key为固定的

1
java key hex = cdd17ab29b84b32552ddcfbb4abf0225

3.5 破解aes的iv

3.5.0 分析过程

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
# 1 分析代码
v31 = j_Utils_getStringByteArray(a1, v28);
v32 = (struct _jobject *)v31;

v34 = a1->functions->FindClass(a1, "javax/crypto/spec/IvParameterSpec");
v35 = a1->functions->GetMethodID(a1, v34, "<init>", "([B)V");
# v32就是iv的值,v32又是v31赋值的,v31通过执行j_Utils_getStringByteArray得到
v37 = a1->functions->NewObject((JNIEnv *)a1, v36, v35, v32);
a1->functions->DeleteLocalRef((JNIEnv *)a1, v38);

# 2 看 v28的产生过程--》通过v59产生
v28 = a1->functions->NewStringUTF(a1, v59);


# 3 查看 v59 的产生过程
# v59是个字节数组,一开始全是空
v59[0] = 0LL;
v59[1] = 0LL;
v59[2] = 0LL;
v59[3] = 0LL;
# 执行了j_rand16Str 变得有值了,我们只需要hook--》j_rand16Str--》函数执行完,把传入的参数打印出来查看即可
j_rand16Str();

# 4 所以我们hook--j_rand16Str

# 5 hook完,发现每次iv都不一样
-iv是动态的,不是固定

# 6 iv不固定,实际原理是
-安卓端生成iv,携带iv到后端,解密,本app就是这种方案
-所以发给后端的数据是 iv+密文
-iv是随机生成16个字节的字符串

3.5.1 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
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
// hook  KeyInfo的esNav---》内部执行了rand16Str---》又执行了java的IvParameterSpec
function java_hook() {
Java.perform(function () {
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("开始================>", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("结束================>", res);
return res;
}

var IvParameterSpec = Java.use("javax.crypto.spec.IvParameterSpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
IvParameterSpec.$init.overload('[B').implementation = function (iv) {
console.log("-----------------------IvParameterSpec---------------------------");
//console.log("iv str=", ByteString.of(iv).utf8());
console.log("java iv hex=", ByteString.of(iv).hex());
//console.log("iv byte", JSON.stringify(iv));
var res = this.$init(iv);
return res;
}

});

}

function delay_hook(so_name, hook_func) {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("[dlopen:]", this.path);
hook_func();
}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
this.path = path;
},
onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
hook_func();
}
}
});
}

function so_hook() {
var rand16Str = Module.findExportByName("libkeyinfo.so", "rand16Str");
console.log('rand16Str addr = ', rand16Str);
if (rand16Str) {
Interceptor.attach(rand16Str, {
onEnter: function (args) {
this.x1 = args[0]
},
onLeave: function (retval) {
// console.log('5.rand16Str 返回值:', hexdump(this.x1), "\n");
console.log('so rand16Str=iv 返回值:', hexdump(this.x1, {length: 16}), "\n");
}
});
}
}

delay_hook("libkeyinfo.so", so_hook)
java_hook()

//frida -U -f com.achievo.vipshop -l 5.hook.js

3.5.2 总结iv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# iv不固定,实际原理是
-安卓端生成iv,携带iv到后端,解密,本app就是这种方案
-所以发给后端的数据是 iv+密文
-iv是随机生成16个字节的字符串

#随机生成 ,拼接在edata中传到后端


# aes key固定,iv变化
-正常:key+iv 对数据进行加密---》传递到后端---》后端通过key+iv解密
-不正常情况:
key 和iv 都变化,对数据进行加密--》传到后端(携带key和iv)--》后端通过key+iv解密
-咱们这个app 其实就是
key不变 和iv 变化,对数据进行加密--》传到后端(携带iv)--》后端通过key+iv解密

3.6 加密明文破解

3.6.0 分析过程

1
2
3
4
5
6
# 1 我们猜测,aes的明文,就是 上面hook到,传入的参数
# 2 验证我们的猜测
-看代码过程
明文是v41---》v41又是v40---》v40又是传入的参数a2执行j_Utils_getStringByteArray得到---》本质就是传入参数的字符串变成字节
-hook--doFinal,查看传入的参数,就是明文

3.6.1 hook–doFinal和java层esNav比较

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
function java_hook() {
Java.perform(function () {
// 1 hook esNav 传入的参数和后面doFinal参数比较,如果一样,说明它没有改动,就是用这个参数加密的
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("-----------------gsNav-----------------");
console.log("开始================>", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("结束================>", res);
return res;
}


var Cipher = Java.use("javax.crypto.Cipher");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");

Cipher.doFinal.overload('[B').implementation = function (data) {
console.log("-----------------------AES加密---------------------------");
console.log("AES明文:", ByteString.of(data).utf8());
var res = this.doFinal(data);
console.log('AES密文:', ByteString.of(res).hex());
return res;
}

});

}

java_hook()

//frida -U -f com.achievo.vipshop -l 6.hook.js


/*

开始================>

// java层hook到的
app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%
22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+
2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as
3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-
36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_s
hop_android_1692874116699&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2MDU2fHx8.5bed87eb0a0bab17a07cc313e9f06235

// c层hook到的,完全一样
-----------------------AES加密---------------------------
AES明文: app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22a
h4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%2
2ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%2
2%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cb
e24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_
1692874116699&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2MDU2fHx8.5bed87eb0a0bab17a07cc313e9f06235
AES密文: badd250b574b5ebf70dff81f938a1f87bb4cf732baf801faeaa2cd2438c9a0ef1e7e1a002341ebbf88cf1e5107e10e8afc42db4d73d728189e8cb5974af2fc447f5793954
067f506b6b9af84af39b107379e15b6d3a1811aaa584e2ec771189d4ce58050959f86aa5a0f684da7b64a6d3184a534dce5eb2bde3d94e103ee20a97933e1d3418889cdbfda126da920
21ee1845bf4918d36c538743e210290f8c5fb172e3ff54aa409455a266ea3b3e8952b739285ba6b34a23e084bb7e42b8de712985c0a423a5e9fad4f0f25364c29160d5aa9a3e57b9d86
34727c782fe0d2fd5dfcc89e5c0ebe622f0fdf96784c22ad6e31c646d5a747db5df8e2fb5153a1205b904a2f2aa5a735b8281156c8cfc12f767caa21bcb970372b8ebedbe7a7e6ec22b
3f0498f57c336feb2354496f3abee9ed2501dbb6254c6f85e5ffd9352ede84f60d8a1c1799404388819f68cd1e70bb5f610cedcdd12e6bdab5d2f1d1e95499e14d97b1c44ea27d9b44c
a4f208c25efe3aa2941691881cfbc88dedaa2aa6e067e89db4eba2930e7f4171b089c93c5bca15788e3f1716bed5245a4c5cff79f958c689d7a6d5df2976f83081f80a0a599527b3846
7eae930ed7f99f152a8731660222f26bb40b87b79a58fb664830b8f447cdf68de5ff8bfebb1a8a9a5231b949d01d1562fc691af2f08b50f26600c26c9185965b889eb40beb26ccd85f6
d04495e5d8a734f38d6cc8be1db86eb54a7db9bac8a683f0d7247aeb8a1c7ee5f7fccba5f0d8da8b4506bbc825228b7229da0369d1ea618e52210f72a6e342bdcffca31cf1ab21de0e4
d510b9c400462fc1c4a16bac235c6c01ad5074b3eb656dfc20a717f17d0a32d64c893637b35d8f17c96c5d5cf5ecf4a5759da0c2c1c0a5fac3aadc3867bd3956ae36e564305c35fa773
syL4duG61Sn25usimg/DXJHrrihx+5ff8y6Xw2NqLRQa7yCUii3Ip2gNp0ephjlIhD3Km40K9z/yjHPGrId4OTVELnEAEYvwcSha6wjXGwBrVB0s+tlbfwgpxfxfQoy1kyJNjezXY8XyWxdXPXs9KV1naDCwcCl+sOq3DhnvTlWrjblZDBcNfp3M9M7AbOzska7kIpLOlh3DCfFX+Twbc6PyrKU7zaJ1mMEBer2J7iPvIamLHA771fE4ndNxuYkCZr2y1VKGq85Tb2fsWU6ELGTFEQ3ylQRtNgFVR2le5XcT1XctxUnMuz/OLkfTK/QwaYcGob9EG7r0r5Jf0JFGp0gGzmfxgYqAlksMVonR9gxThydwyZkl+yWQzjivXnZglYe6QECNZnfjxDKRXtPc2J+URvhM2cEgJ4jblZCzl1Fdrh4GCiyKKZbnWitqpcYA6IZwVUYEgD66xKJqy/58TuUK+FRKywFzDpxpYnJ7F4rngHC6vryQiy/P32uA96iNXMadySYa7V5WA==


*/

3.6.3 明文如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from urllib.parse import unquote_plus
data = "app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_1692874116699&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2MDU2fHx8.5bed87eb0a0bab17a07cc313e9f06235"
res = unquote_plus(data)
print(res)


#### 结果如下,我们看一下有什么没破解的参数
app_name=shop_android&
app_version=7.83.3&
client_type=android&
dinfo={"ah1":"","ah2":"","ah3":"","ah4":"wifi","ah5":"1440_2712","ah6":1900800,"ah7":8,"ah8":3839954944,"ah9":"Pixel 2 XL","ah10":"","ah11":"","ah12":"","ah13":"","as1":"11","as2":"","as3":"","as4":"cff15d2b8c13bf22","as5":"","as6":"","as7":"30","ac1":"a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d"}&
mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&
phone_model=Pixel 2 XL&
session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_1692874116699&
sys_version=30&
vcspKey=4d9e524ad536c03ff203787cf0dfcd29&
vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2MDU2fHx8.5bed87eb0a0bab17a07cc313e9f06235

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dinfo={"ah1":"",
"ah2":"",
"ah3":"",
"ah4":"wifi",
"ah5":"1440_2712",
"ah6":1900800, # cpu最大工作频率
"ah7":8, # cpu核数
"ah8":3839954944, # 内存大小
"ah9":"Pixel 2 XL", # 手机型号
"ah10":"",
"ah11":"",
"ah12":"",
"ah13":"",
"as1":"11", # 安卓版本
"as2":"",
"as3":"",
"as4":"cff15d2b8c13bf22", # 手机 android_id = 随机生成
"as5":"",
"as6":"",
"as7":"30", # 安卓SDK版本
"ac1":"a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d"# 注册设备时的device_token =随机uuid
}

image-20240517182246855

image-20240517182253924

3.7 整体hook–key-iv-明文

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
84
85
86
87
88
89
90
91
92
93
94
function java_hook() {
Java.perform(function () {
// 1 hook 明文
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("-----------------gsNav-----------------");
console.log("开始================>", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("结束================>", res);
return res;
}
//2 hook aes 的key
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("-----------------------SecretKeySpec---------------------------");
//console.log("4.key bytes=", JSON.stringify(key));
console.log("java key hex =", ByteString.of(key).hex());
//console.log("4.key", ByteString.of(key).utf8());
}
var res = this.$init(key, name);
return res;
}

//3 hook aes 的iv
var IvParameterSpec = Java.use("javax.crypto.spec.IvParameterSpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
IvParameterSpec.$init.overload('[B').implementation = function (iv) {
console.log("-----------------------IvParameterSpec---------------------------");
//console.log("iv str=", ByteString.of(iv).utf8());
console.log("java iv hex=", ByteString.of(iv).hex());
//console.log("iv byte", JSON.stringify(iv));
var res = this.$init(iv);
return res;
}
// 4 hook 加密后的数据
var Cipher = Java.use("javax.crypto.Cipher");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");

Cipher.doFinal.overload('[B').implementation = function (data) {
console.log("-----------------------AES加密---------------------------");
console.log("AES明文:", ByteString.of(data).utf8());
var res = this.doFinal(data);
console.log('AES密文:', ByteString.of(res).hex());
return res;
}


});

}

java_hook()

//frida -U -f com.achievo.vipshop -l 7.hook.js


/*
开始================>
####### 明文
app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%
22%22%2C%22ah4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+
2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as
3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-
36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_s
hop_android_1692874944471&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2ODg0fHx8.0ce84673dab328ac28af1ca51170da70
-----------------------SecretKeySpec---------------------------
java key hex = cdd17ab29b84b32552ddcfbb4abf0225
-----------------------IvParameterSpec---------------------------
java iv hex= 34353539666663663138323461386336
-----------------------AES加密---------------------------
AES明文: app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22a
h4%22%3A%22wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%2
2ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%2
2%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cb
e24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_
1692874944471&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY2ODg0fHx8.0ce84673dab328ac28af1ca51170da70

#### 密文

AES密文: 7aa3767e3cf5e4b2d391f073ccb1a53f84fb2b3e630a6d0f700317c8dc88f80ffb160f8d90a3be14a92a8ca6c8adde8d482ce0f5d7f5353b1833781adc9f1a2c1e5a8843c
0291d090c6fbe7e4924ba7b5bf0af71b663c6a4b15bd103fbfa585ced7c43790e956a4f44f70262d94ebe1709806d8d3fd56372c8d0faa1a7812d1de546163d3dbc35e2db91208617b8
b70ed7e0762c44558f045a1f8aa249a5f14df672081cfa388d0fafa675ad7fa61b1b41c41e33ac238190fa3efb2e916b43fd98623c232d55e85e6d1ff91f67feda3fed4f52a55329a32
19cacea152e22e47d8bbdec13337bc0eb13b0205c3c55fe9233574376d3e15160e3d82961c82ef98219b36b86eec6924b46064c1fef58e580705ab5fd4d809764be2f09794b83902ed7
6c1c11235723187d4509fb0b11a99a7646241e8aa35a9a64ff6c7ebf9cb4baf71d80c91b4b5f12d72934b5fdf9db54bcd4195588e3f7f3b57eab7d3f767d8cc5938fa35ef0b144b683c
27bd110aa228f4ea828fd6525230cbb28b6dc5e0ea5801199aa1cc4f0fb7febf8d5acbfe61e67618908f33b622a67230ec031966ec436616889cc64ae27a128a09864a8aae6e3f8f5e0
Lp7W/CvcbZjxqSxW9ED+/pYXO18Q3kOlWpPRPcCYtlOvhcJgG2NP9VjcsjQ+qGngS0d5UYWPT28NeLbkSCGF7i3DtfgdixEVY8EWh+Kokml8U32cggc+jiND6+mda1/phsbQcQeM6wjgZD6PvsukWtD/ZhiPCMtVehebR/5H2f+2j/tT1KlUymjIZys6hUuIuR9i73sEzN7wOsTsCBcPFX+kjNXQ3bT4VFg49gpYcgu+YIZs2uG7saSS0YGTB/vWOWAcFq1/U2Al2S+Lwl5S4OQLtdsHBEjVyMYfUUJ+wsRqZp2RiQeiqNammT/bH6/nLS69x2AyRtLXxLXKTS1/fnbVLzUGVWI4/fztX6rfT92fYzFk4+jXvCxRLaDwnvREKoij06oKP1lJSMMuyi23F4OpYARmaocxPD7f+v41ay/5h5nYYkI8ztiKmcjDsAxlm7ENmFoicxkriehKKCYZKiq5uP49eA7qhCE/9Sv9kRR7SvVmaxSfa603p56rSCzH5rvTW1+w57bwI6KPOWEyi8ZCdLPO3FsfdMfZ/uj3akmRkhkc4x1jb9g6SdFshbsyW5n028FgefQ7q/VmCRDaJ0vLFuekOt87QNjGa8S7fVp54RMbjGJNnxU6uiUUQ7zUX2zsWOamJMM9eLBd1QtLnK1VgKOSuTSUedbQJZp8GcduqoYW6OLJKvA5v71WN7k4gIUhd86aY2r1eEgaJQXuBdGX5CQXp4X09WEZ6fKEbqfBLfhsQ93XH8//Ho6G02UusBlRW+KMDmN64uOXNO+Mq+bdUBeT5vTs0440DqMePVF6qhND5wEo6wrUbFFPNy7Yvfq5lPSm6gpZCpOSNdDKtgE4cO4NpAFKFerYqup2IDe9oRxsIxvMTe2Erp/dJVTbmJRqX1txuJH3i0UwQmbX5RZ+zUUvK14FybRw/wU+DL4/5bGKs+4XnMhKSZRADJlwNJbrfdnVjsyuuotNVnVOGFh8pxNthdFoB2HJjJ/4Hfzd+1AcqJCY66tFc7phECqVM5Giz1v6VHZPoR1s2PpB9j6/YcAEsTM3AkN/BU+VP0pfkISdnEq7rKOFN47qhhDuWlJ/sAw76gcbhVIh4gAczWrBg==




*/

3.8 hook-key-iv-明文–base64

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
function java_hook() {
Java.perform(function () {
// 1 hook 明文
var KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo.esNav.implementation = function (ctx, str, str2, str3, i10) {
console.log("-----------------gsNav-----------------");
console.log("开始================>", str);
var res = this.esNav(ctx, str, str2, str3, i10);
console.log("结束================>", res);
return res;
}
// 2 hook aes 的key
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (key, name) {
if (name === 'AES') {
console.log("-----------------------SecretKeySpec---------------------------");
//console.log("4.key bytes=", JSON.stringify(key));
console.log("java key hex =", ByteString.of(key).hex());
//console.log("4.key", ByteString.of(key).utf8());
}
var res = this.$init(key, name);
return res;
}
// 3 hook iv
var IvParameterSpec = Java.use("javax.crypto.spec.IvParameterSpec");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");
IvParameterSpec.$init.overload('[B').implementation = function (iv) {
console.log("-----------------------IvParameterSpec---------------------------");
//console.log("iv str=", ByteString.of(iv).utf8());
console.log("java iv hex=", ByteString.of(iv).hex());
//console.log("iv byte", JSON.stringify(iv));
var res = this.$init(iv);
return res;
}
// 4 hook 密文
var Cipher = Java.use("javax.crypto.Cipher");
var ByteString = Java.use("com.android.okhttp.okio.ByteString");

Cipher.doFinal.overload('[B').implementation = function (data) {
console.log("-----------------------AES加密---------------------------");
console.log("AES明文:", ByteString.of(data).utf8());
var res = this.doFinal(data);
console.log('AES密文:', ByteString.of(res).hex());
return res;
}


});

}

function delay_hook(so_name, hook_func) {
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
// console.log("[dlopen:]", path);
this.path = path;
}, onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("[dlopen:]", this.path);
hook_func();
}
}
});

Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
this.path = path;
},
onLeave: function (retval) {
if (this.path.indexOf(so_name) !== -1) {
console.log("\nandroid_dlopen_ext加载:", this.path);
hook_func();
}
}
});
}

function so_hook() {
// 5 hook base64 编码
var base64_encode = Module.findExportByName("libkeyinfo.so", "base64_encode");
console.log('base64_encode = ', base64_encode)
if (base64_encode) {
Interceptor.attach(base64_encode, {
onEnter: function (args) {
console.log("-----------------------base64---------------------------");
console.log('base64参数1:', hexdump(args[0], {length: args[1].toInt32()}), "\n");
console.log('base64参数2:', parseInt(args[1]), "\n");
},
onLeave: function (retval) {
// console.log('5.rand16Str 返回值:', hexdump(this.x1), "\n");
console.log('base64返回值:', Memory.readCString(retval), "\n");

}
});
}
}

delay_hook("libkeyinfo.so", so_hook)

java_hook()


/*
开始================> app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22ah4%22%3A%22
wifi%22%2C%22ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%
22%22%2C%22ah12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%
22%2C%22as6%22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel
+2+XL&session_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_1692875431421&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY3MzcxfHx8.4f176037624c6f9736751183730c2ba5
-----------------------SecretKeySpec---------------------------
java key hex = cdd17ab29b84b32552ddcfbb4abf0225
-----------------------IvParameterSpec---------------------------
java iv hex= 62303463366162323833313731336239
-----------------------AES加密---------------------------
AES明文: app_name=shop_android&app_version=7.83.3&client_type=android&dinfo=%7B%22ah1%22%3A%22%22%2C%22ah2%22%3A%22%22%2C%22ah3%22%3A%22%22%2C%22ah4%22%3A%22wifi%22%2C%2
2ah5%22%3A%221440_2712%22%2C%22ah6%22%3A1900800%2C%22ah7%22%3A8%2C%22ah8%22%3A3839954944%2C%22ah9%22%3A%22Pixel+2+XL%22%2C%22ah10%22%3A%22%22%2C%22ah11%22%3A%22%22%2C%22a
h12%22%3A%22%22%2C%22ah13%22%3A%22%22%2C%22as1%22%3A%2211%22%2C%22as2%22%3A%22%22%2C%22as3%22%3A%22%22%2C%22as4%22%3A%22cff15d2b8c13bf22%22%2C%22as5%22%3A%22%22%2C%22as6%
22%3A%22%22%2C%22as7%22%3A%2230%22%2C%22ac1%22%3A%22a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d%22%7D&mars_cid=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d&phone_model=Pixel+2+XL&sessio
n_id=a9d1a2b9-2a79-36fd-a8ca-cbe24c03979d_shop_android_1692875431421&sys_version=30&vcspKey=4d9e524ad536c03ff203787cf0dfcd29&vcspToken=NGQ5ZTUyNGFkNTM2YzAzZmYyMDM3ODdjZjBkZmNkMjl8fHwxNjk1NDY3MzcxfHx8.4f176037624c6f9736751183730c2ba5
AES密文: 352c4847afdb711716f7ea89dfde20d9f44ab3d7534ff57067e04fc8030c4ce864e66eb23e4735460f359e59ebdd7e2262e3c23d26f3ebd30a6a737e24c5418b154f4d9ddf0c3b64ed2204bd16cbf4f3
24c162f4278a9e54f24c1479988f4b329cb3663a21ef47ff2b062d5aefbf98458a67ae1ee1ddf9bac7df3e87eb2554de4ade4656f304fc9f6c1ffc752e14eb03091ccf52f11554db51ca8babe536d6af7cc5c1b68e
202fb8a38b7419bb6fd2d900742fc2c207f5b390901294aa38489db536ee626392a98b8e38131b65192d387be1ae235836dfd87233ec678bf8ea0e794e9f3b65837eb7a9f30dc5d44130b5e8df837cdf9ce0ee4517
c6c42113916c6472296530aafbdb942e93cfafa31f8560cfa4983e2962dd123991dd0a4b7d8679520b6e685a56779590cea4d7e0be0573a6306262bc02313edd30486776498ac646f059c282c3b308227cc5d82581
82d9ab241e55bd383d8feb5cbb9fa9c02ac568ab86c2ce092c942eeba26c06a06c630b1a5dc1d5d1096050f43b74f57886bf66c1add12511d13670b2cafebf615373a76025711bbb612515cc689228b3e53f40b598
630c2be2ea2212c935c292667ff97ec65999ccc12916cd46a38edf5165e512250e8eef67867406cdd1b5eda98025bc3ec21c2f49acbed109c9be13ba89fba854cc66f9358051a9b8c541195254d4e751f44e56b3f7
5730acee461a6a25fff7db74cd5cee2d745b2b2c3310935c4b9981861830bb831b59e323c711bf31683805f4825c852b930ea37114cc4189e6bfd9e7690005d288014f241b8d2bde59f49c2a3a965d566ab403acf9
fc26000712670120b1ed4f8c8fcbd2188a96c391491cdf5f6633e39ad1dddc48024fb1fa076dc700d4ef6975ae539a34c25fc0e554cba18d2e42e202f73af98d32e7a2061d4f2107b3d58585b7ed40e7af40733b08
8d8efa6570842714c8086906dfc492c1aa60bf29cf97d148eab5c001618a940efe2250c1e2f0d56f52b0c49b4cbc477c415bf857183142feae24622d7234434ab869b2690243bce50cf6d7eb37e984fad76c21456e
913c53141d416114cce60854e7901a930ac13d8f2a4797360a5b7917595e78e2ece2fea1395be7d9174444536666958457a47e7816acceedf3eabe7eaf002f6534e34f13bdaf627b067a6abebc181d17cb03516355bc8d24919e1ab976bcb7ed44c6c7cfe62bccb44c12953e81fa08b02da61519b8255547fae1d55ef2a2da98fadb2d2fafeccf8e5ec737bc20705ab4560d0b8f75e1882c
-----------------------base64---------------------------
base64参数1: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
edc51810 62 30 34 63 36 61 62 32 38 33 31 37 31 33 62 39 b04c6ab2831713b9
edc51820 35 2c 48 47 af db 71 17 16 f7 ea 89 df de 20 d9 5,HG..q....... .
edc51830 f4 4a b3 d7 53 4f f5 70 67 e0 4f c8 03 0c 4c e8 .J..SO.pg.O...L.
edc51840 64 e6 6e b2 3e 47 35 46 0f 35 9e 59 eb dd 7e 22 d.n.>G5F.5.Y..~"
edc51850 62 e3 c2 3d 26 f3 eb d3 0a 6a 73 7e 24 c5 41 8b b..=&....js~$.A.
edc51860 15 4f 4d 9d df 0c 3b 64 ed 22 04 bd 16 cb f4 f3 .OM...;d."......
edc51870 24 c1 62 f4 27 8a 9e 54 f2 4c 14 79 98 8f 4b 32 $.b.'..T.L.y..K2
edc51880 9c b3 66 3a 21 ef 47 ff 2b 06 2d 5a ef bf 98 45 ..f:!.G.+.-Z...E
edc51890 8a 67 ae 1e e1 dd f9 ba c7 df 3e 87 eb 25 54 de .g........>..%T.
edc518a0 4a de 46 56 f3 04 fc 9f 6c 1f fc 75 2e 14 eb 03 J.FV....l..u....
edc518b0 09 1c cf 52 f1 15 54 db 51 ca 8b ab e5 36 d6 af ...R..T.Q....6..
edc518c0 7c c5 c1 b6 8e 20 2f b8 a3 8b 74 19 bb 6f d2 d9 |.... /...t..o..
edc518d0 00 74 2f c2 c2 07 f5 b3 90 90 12 94 aa 38 48 9d .t/..........8H.
edc518e0 b5 36 ee 62 63 92 a9 8b 8e 38 13 1b 65 19 2d 38 .6.bc....8..e.-8
edc518f0 7b e1 ae 23 58 36 df d8 72 33 ec 67 8b f8 ea 0e {..#X6..r3.g....
edc51900 79 4e 9f 3b 65 83 7e b7 a9 f3 0d c5 d4 41 30 b5 yN.;e.~......A0.
edc51910 e8 df 83 7c df 9c e0 ee 45 17 c6 c4 21 13 91 6c ...|....E...!..l
edc51920 64 72 29 65 30 aa fb db 94 2e 93 cf af a3 1f 85 dr)e0...........
edc51930 60 cf a4 98 3e 29 62 dd 12 39 91 dd 0a 4b 7d 86 `...>)b..9...K}.
edc51940 79 52 0b 6e 68 5a 56 77 95 90 ce a4 d7 e0 be 05 yR.nhZVw........
edc51950 73 a6 30 62 62 bc 02 31 3e dd 30 48 67 76 49 8a s.0bb..1>.0HgvI.
edc51960 c6 46 f0 59 c2 82 c3 b3 08 22 7c c5 d8 25 81 82 .F.Y....."|..%..
edc51970 d9 ab 24 1e 55 bd 38 3d 8f eb 5c bb 9f a9 c0 2a ..$.U.8=..\....*
edc51980 c5 68 ab 86 c2 ce 09 2c 94 2e eb a2 6c 06 a0 6c .h.....,....l..l
edc51990 63 0b 1a 5d c1 d5 d1 09 60 50 f4 3b 74 f5 78 86 c..]....`P.;t.x.
edc519a0 bf 66 c1 ad d1 25 11 d1 36 70 b2 ca fe bf 61 53 .f...%..6p....aS
edc519b0 73 a7 60 25 71 1b bb 61 25 15 cc 68 92 28 b3 e5 s.`%q..a%..h.(..
edc519c0 3f 40 b5 98 63 0c 2b e2 ea 22 12 c9 35 c2 92 66 [email protected].+.."..5..f
edc519d0 7f f9 7e c6 59 99 cc c1 29 16 cd 46 a3 8e df 51 ..~.Y...)..F...Q
edc519e0 65 e5 12 25 0e 8e ef 67 86 74 06 cd d1 b5 ed a9 e..%...g.t......
edc519f0 80 25 bc 3e c2 1c 2f 49 ac be d1 09 c9 be 13 ba .%.>../I........
edc51a00 89 fb a8 54 cc 66 f9 35 80 51 a9 b8 c5 41 19 52 ...T.f.5.Q...A.R
edc51a10 54 d4 e7 51 f4 4e 56 b3 f7 57 30 ac ee 46 1a 6a T..Q.NV..W0..F.j
edc51a20 25 ff f7 db 74 cd 5c ee 2d 74 5b 2b 2c 33 10 93 %...t.\.-t[+,3..
edc51a30 5c 4b 99 81 86 18 30 bb 83 1b 59 e3 23 c7 11 bf \K....0...Y.#...
edc51a40 31 68 38 05 f4 82 5c 85 2b 93 0e a3 71 14 cc 41 1h8...\.+...q..A
edc51a50 89 e6 bf d9 e7 69 00 05 d2 88 01 4f 24 1b 8d 2b .....i.....O$..+
edc51a60 de 59 f4 9c 2a 3a 96 5d 56 6a b4 03 ac f9 fc 26 .Y..*:.]Vj.....&
edc51a70 00 07 12 67 01 20 b1 ed 4f 8c 8f cb d2 18 8a 96 ...g. ..O.......
edc51a80 c3 91 49 1c df 5f 66 33 e3 9a d1 dd dc 48 02 4f ..I.._f3.....H.O
edc51a90 b1 fa 07 6d c7 00 d4 ef 69 75 ae 53 9a 34 c2 5f ...m....iu.S.4._
edc51aa0 c0 e5 54 cb a1 8d 2e 42 e2 02 f7 3a f9 8d 32 e7 ..T....B...:..2.
edc51ab0 a2 06 1d 4f 21 07 b3 d5 85 85 b7 ed 40 e7 af 40 ...O!.......@..@
edc51ac0 73 3b 08 8d 8e fa 65 70 84 27 14 c8 08 69 06 df s;....ep.'...i..
edc51ad0 c4 92 c1 aa 60 bf 29 cf 97 d1 48 ea b5 c0 01 61 ....`.)...H....a
edc51ae0 8a 94 0e fe 22 50 c1 e2 f0 d5 6f 52 b0 c4 9b 4c ...."P....oR...L
edc51af0 bc 47 7c 41 5b f8 57 18 31 42 fe ae 24 62 2d 72 .G|A[.W.1B..$b-r
edc51b00 34 43 4a b8 69 b2 69 02 43 bc e5 0c f6 d7 eb 37 4CJ.i.i.C......7
edc51b10 e9 84 fa d7 6c 21 45 6e 91 3c 53 14 1d 41 61 14 ....l!En.<S..Aa.
edc51b20 cc e6 08 54 e7 90 1a 93 0a c1 3d 8f 2a 47 97 36 ...T......=.*G.6
edc51b30 0a 5b 79 17 59 5e 78 e2 ec e2 fe a1 39 5b e7 d9 .[y.Y^x.....9[..
edc51b40 17 44 44 53 66 66 95 84 57 a4 7e 78 16 ac ce ed .DDSff..W.~x....
edc51b50 f3 ea be 7e af 00 2f 65 34 e3 4f 13 bd af 62 7b ...~../e4.O...b{
edc51b60 06 7a 6a be bc 18 1d 17 cb 03 51 63 55 bc 8d 24 .zj.......QcU..$
edc51b70 91 9e 1a b9 76 bc b7 ed 44 c6 c7 cf e6 2b cc b4 ....v...D....+..
edc51b80 4c 12 95 3e 81 fa 08 b0 2d a6 15 19 b8 25 55 47 L..>....-....%UG
edc51b90 fa e1 d5 5e f2 a2 da 98 fa db 2d 2f af ec cf 8e ...^......-/....
edc51ba0 5e c7 37 bc 20 70 5a b4 56 0d 0b 8f 75 e1 88 2c ^.7. pZ.V...u..,

base64参数2: 928

base64返回值: YjA0YzZhYjI4MzE3MTNiOTUsSEev23EXFvfqid/eINn0SrPXU0/1cGfgT8gDDEzoZOZusj5HNUYPNZ5Z691+ImLjwj0m8+vTCmpzfiTFQYsVT02d3ww7ZO0iBL0Wy/TzJMFi9CeKnlTyTBR5mI9LMpyzZjo
h70f/KwYtWu+/mEWKZ64e4d35usffPofrJVTeSt5GVvME/J9sH/x1LhTrAwkcz1LxFVTbUcqLq+U21q98xcG2jiAvuKOLdBm7b9LZAHQvwsIH9bOQkBKUqjhInbU27mJjkqmLjjgTG2UZLTh74a4jWDbf2HIz7GeL+OoOeU6fO
2WDfrep8w3F1EEwtejfg3zfnODuRRfGxCETkWxkcillMKr725Quk8+vox+FYM+kmD4pYt0SOZHdCkt9hnlSC25oWlZ3lZDOpNfgvgVzpjBiYrwCMT7dMEhndkmKxkbwWcKCw7MIInzF2CWBgtmrJB5VvTg9j+tcu5+pwCrFaKu
Gws4JLJQu66JsBqBsYwsaXcHV0QlgUPQ7dPV4hr9mwa3RJRHRNnCyyv6/YVNzp2AlcRu7YSUVzGiSKLPlP0C1mGMMK+LqIhLJNcKSZn/5fsZZmczBKRbNRqOO31Fl5RIlDo7vZ4Z0Bs3Rte2pgCW8PsIcL0msvtEJyb4Tuon7q
FTMZvk1gFGpuMVBGVJU1OdR9E5Ws/dXMKzuRhpqJf/323TNXO4tdFsrLDMQk1xLmYGGGDC7gxtZ4yPHEb8xaDgF9IJchSuTDqNxFMxBiea/2edpAAXSiAFPJBuNK95Z9JwqOpZdVmq0A6z5/CYABxJnASCx7U+Mj8vSGIqWw5F
JHN9fZjPjmtHd3EgCT7H6B23HANTvaXWuU5o0wl/A5VTLoY0uQuIC9zr5jTLnogYdTyEHs9WFhbftQOevQHM7CI2O+mVwhCcUyAhpBt/EksGqYL8pz5fRSOq1wAFhipQO/iJQweLw1W9SsMSbTLxHfEFb+FcYMUL+riRiLXI0Q
0q4abJpAkO85Qz21+s36YT612whRW6RPFMUHUFhFMzmCFTnkBqTCsE9jypHlzYKW3kXWV544uzi/qE5W+fZF0REU2ZmlYRXpH54FqzO7fPqvn6vAC9lNONPE72vYnsGemq+vBgdF8sDUWNVvI0kkZ4auXa8t+1ExsfP5ivMtEwSlT6B+giwLaYVGbglVUf64dVe8qLamPrbLS+v7M+OXsc3vCBwWrRWDQuPdeGILA==

结束================>
*/

3.9 代码请求generate_token

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import random
import time
import uuid
from base64 import b64encode
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import hashlib
from urllib.parse import quote_plus
import requests
import hashlib

requests.packages.urllib3.disable_warnings() # https 会有红色警告,去掉

# ====================== 注册设备 ======================
mars_cid = device_token = str(uuid.uuid4())
android_id = ''.join(["%x" % random.randint(1, 15) for i in range(16)])
build_model = "Pixel 2 XL"
ctime = str(int(time.time()))


def sha1(data_string):
# sha1加密
hash_object = hashlib.sha1()
hash_object.update(data_string.encode('utf-8'))
arg7 = hash_object.hexdigest()
return arg7

device_token = str(uuid.uuid4())
param_dict = {
'app_name': 'achievo_ad',
'app_version': '7.83.3',
'device_token': device_token,
'status': 1,
'warehouse': 'null',
'manufacturer': 'Google',
'device': build_model,
'os_version': '30',
'channel': 'oziq7dxw:::',
'vipruid': '',
'regPlat': '0',
'regid': '',
'rom': 'Dalvik/2.1.0 (Linux; U; Android 11; Pixel 2 XL Build/RP1A.201005.004.A1)',
'skey': '6692c461c3810ab150c9a980d0c275ec',

}

ordered_string = "&".join(["{}={}".format(key, param_dict[key]) for key in sorted(param_dict.keys())])

salt = "aee4c425dbb2288b80c71347cc37d04b"
tmp = sha1(f"{salt}{ordered_string}")
api_sign = sha1(f"{salt}{tmp}")

res = requests.get(
url="https://mp.appvipshop.com/apns/device_reg",
params=param_dict,
headers={
"Authorization": "OAuth api_sign={}".format(api_sign)
},verify=False
)
print("1.注册设备", res.text)


# ====================== getTokenByFP ======================
res = requests.get(
url="https://vcsp-api.vip.com/token/getTokenByFP?vcspKey=4d9e524ad536c03ff203787cf0dfcd29",
headers={
"vcspauthorization": "vcspSign=05a68135d2bfd322e3a22f95bbc25a24c777f387"
},verify=False
)

print(res.text)
vsp_dict = res.json()['data']

# ====================== generate_token ======================

dinfo = '{"ah1":"","ah2":"","ah3":"","ah4":"wifi","ah5":"1080_2189","ah6":1804800,"ah7":8,"ah8":7742595072,"ah9":"%s","ah10":"","ah11":"","ah12":"","ah13":"","as1":"10","as2":"","as3":"","as4":"%s","as5":"","as6":"","as7":"29","ac1":"%s"}' % (
build_model, android_id, device_token)

data_dict = {
"app_name": "shop_android",
"app_version": "7.83.3",
"client_type": "android",
"dinfo": quote_plus(dinfo),
"mars_cid": mars_cid,
"phone_model": build_model,
"session_id": "{}_shop_android_{}".format(mars_cid, ctime),
"sys_version": "29",
"vcspKey": "4d9e524ad536c03ff203787cf0dfcd29",
"vcspToken": vsp_dict['vcspToken']
}

data = "&".join(["{}={}".format(key, data_dict[key]) for key in sorted(data_dict.keys())])
iv = ''.join(["%x" % random.randint(1, 15) for i in range(16)])

obj = hashlib.md5()
obj.update(b'aee4c425dbb2288b80c71347cc37d04b')
key = obj.digest()

aes = AES.new(
key=key,
mode=AES.MODE_CBC,
iv=iv.encode('utf-8')
)
raw = pad(data.encode('utf-8'), 16)
encrypt_bytes = aes.encrypt(raw)
total_bytes = iv.encode('utf-8') + encrypt_bytes
edata = b64encode(total_bytes).decode('utf-8')

body_dict = {
'api_key': "23e7f28019e8407b98b84cd05b5aef2c",
'did': "",
'edata': edata,
'eversion': "0",
'skey': "6692c461c3810ab150c9a980d0c275ec",
'timestamp': int(time.time())
}

body_string = "&".join(["{}={}".format(key, body_dict[key]) for key in sorted(body_dict.keys())])
# print(body_string)
salt = "aee4c425dbb2288b80c71347cc37d04b"
tmp = sha1(f"{salt}{body_string}")
api_sign = sha1(f"{salt}{tmp}")

res = requests.post(
url="https://mapi.appvipshop.com/vips-mobile/rest/device/generate_token",
data=body_dict,
headers={
"Authorization": "OAuth api_sign={}".format(api_sign)
},
verify=False
)
print("3.token", res.text)

4 搜索功能实现

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import random
import time
import uuid
from base64 import b64encode
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import hashlib
from urllib.parse import quote_plus
import requests
import hashlib

requests.packages.urllib3.disable_warnings() # https 会有红色警告,去掉

# ====================== 注册设备 ======================
api_key = "23e7f28019e8407b98b84cd05b5aef2c"
skey = '6692c461c3810ab150c9a980d0c275ec'

mars_cid = device_token = str(uuid.uuid4())
android_id = ''.join(["%x" % random.randint(1, 15) for i in range(16)])
build_model = "Pixel 2 XL"
ctime = str(int(time.time()))
session_id = "{}_shop_android_1669730552506".format(mars_cid, ctime)


def sha1(data_string):
# sha1加密
hash_object = hashlib.sha1()
hash_object.update(data_string.encode('utf-8'))
arg7 = hash_object.hexdigest()
return arg7

device_token = str(uuid.uuid4())
param_dict = {
'app_name': 'achievo_ad',
'app_version': '7.83.3',
'device_token': device_token,
'status': 1,
'warehouse': 'null',
'manufacturer': 'Google',
'device': build_model,
'os_version': '30',
'channel': 'oziq7dxw:::',
'vipruid': '',
'regPlat': '0',
'regid': '',
'rom': 'Dalvik/2.1.0 (Linux; U; Android 11; Pixel 2 XL Build/RP1A.201005.004.A1)',
'skey': '6692c461c3810ab150c9a980d0c275ec',

}

ordered_string = "&".join(["{}={}".format(key, param_dict[key]) for key in sorted(param_dict.keys())])

salt = "aee4c425dbb2288b80c71347cc37d04b"
tmp = sha1(f"{salt}{ordered_string}")
api_sign = sha1(f"{salt}{tmp}")

res = requests.get(
url="https://mp.appvipshop.com/apns/device_reg",
params=param_dict,
headers={
"Authorization": "OAuth api_sign={}".format(api_sign)
},verify=False
)
print("1.注册设备", res.text)


# ====================== getTokenByFP ======================
res = requests.get(
url="https://vcsp-api.vip.com/token/getTokenByFP?vcspKey=4d9e524ad536c03ff203787cf0dfcd29",
headers={
"vcspauthorization": "vcspSign=05a68135d2bfd322e3a22f95bbc25a24c777f387"
},verify=False
)

print(res.text)
vsp_dict = res.json()['data']

# ====================== generate_token ======================

dinfo = '{"ah1":"","ah2":"","ah3":"","ah4":"wifi","ah5":"1080_2189","ah6":1804800,"ah7":8,"ah8":7742595072,"ah9":"%s","ah10":"","ah11":"","ah12":"","ah13":"","as1":"10","as2":"","as3":"","as4":"%s","as5":"","as6":"","as7":"29","ac1":"%s"}' % (
build_model, android_id, device_token)

data_dict = {
"app_name": "shop_android",
"app_version": "7.83.3",
"client_type": "android",
"dinfo": quote_plus(dinfo),
"mars_cid": mars_cid,
"phone_model": build_model,
"session_id": "{}_shop_android_{}".format(mars_cid, ctime),
"sys_version": "29",
"vcspKey": "4d9e524ad536c03ff203787cf0dfcd29",
"vcspToken": vsp_dict['vcspToken']
}

data = "&".join(["{}={}".format(key, data_dict[key]) for key in sorted(data_dict.keys())])
iv = ''.join(["%x" % random.randint(1, 15) for i in range(16)])

obj = hashlib.md5()
obj.update(b'aee4c425dbb2288b80c71347cc37d04b')
key = obj.digest()

aes = AES.new(
key=key,
mode=AES.MODE_CBC,
iv=iv.encode('utf-8')
)
raw = pad(data.encode('utf-8'), 16)
encrypt_bytes = aes.encrypt(raw)
total_bytes = iv.encode('utf-8') + encrypt_bytes
edata = b64encode(total_bytes).decode('utf-8')

body_dict = {
'api_key': "23e7f28019e8407b98b84cd05b5aef2c",
'did': "",
'edata': edata,
'eversion': "0",
'skey': "6692c461c3810ab150c9a980d0c275ec",
'timestamp': int(time.time())
}

body_string = "&".join(["{}={}".format(key, body_dict[key]) for key in sorted(body_dict.keys())])
# print(body_string)
salt = "aee4c425dbb2288b80c71347cc37d04b"
tmp = sha1(f"{salt}{body_string}")
api_sign = sha1(f"{salt}{tmp}")

res = requests.post(
url="https://mapi.appvipshop.com/vips-mobile/rest/device/generate_token",
data=body_dict,
headers={
"Authorization": "OAuth api_sign={}".format(api_sign)
},
verify=False
)
print("3.token", res.text)
did = res.json()['data']['token']['did']

# ====================== 搜索 ======================

key_word = "行李箱"

search_dict = {
"api_key": api_key,
"app_name": "shop_android",
"app_version": "7.83.3",
"bigSaleTagIds": "",
"brandIds": "",
"brandStoreSns": "",
"categoryId": "",
"channelId": "1",
"channel_flag": "0_1",
"client": "android",
"client_type": "android",
"darkmode": "0",
"deeplink_cps": "",
"device_model": "".format(build_model),
"did": did,
"elder": "0",
"extParams": '{"priceVer":"2","mclabel":"1","cmpStyle":"1","statusVer":"2","ic2label":"1","video":"2","preheatTipsVer":"4","floatwin":"1","superHot":"1","exclusivePrice":"1","router":"1","coupons":"1","needVideoExplain":"1","rank":"2","needVideoGive":"1","bigBrand":"1","couponVer":"v2","videoExplainUrl":"1","live":"1","sellpoint":"1","reco":"1","vreimg":"1","search_tag":"2","tpl":"1","stdSizeVids":"","labelVer":"2"}',
"fdc_area_id": "101103104123",
"functions": "RTRecomm,flagshipInfo,feedback,otdAds,zoneCode,slotOp,survey,hasTabs,floaterParams",
"harmony_app": "0",
"harmony_os": "0",
"headTabType": "all",
"height": "2189",
"isMultiTab": "0",
"keyword": key_word,
"lastPageProperty": '{"isBgToFront":"0","suggest_text":"%s","scene_entry_id":"-99","refer_page_id":"page_te_globle_classify_search_1669733882852","text":"%s","tag":"1","module_name":"com.achievo.vipshop.search","type":"all","typename":"全部","is_back_page":"0"}' %(key_word,key_word),
"maker": "REDMI",
"mars_cid": mars_cid,
"mobile_channel": "oziq7dxw:::",
"mobile_platform": "3",
"net": "WIFI",
"operator": "中国电信",
"os": "Android",
"osv": "10",
"otddid": "",
"other_cps": "",
"page_id": "page_te_commodity_search_{}".format(int(time.time() * 1000) - 200),
"phone_model": build_model,
"priceMax": "",
"priceMin": "",
"props": "",
"province_id": "101103",
"referer": "com.achievo.vipshop.search.activity.TabSearchProductListActivity",
"rom": "Dalvik/2.1.0 (Linux; U; Android 10; M2007J17C MIUI/V12.0.11.0.QJSCNXM)",
"sd_tuijian": "0",
"service_provider": "46011",
"session_id": session_id,
"skey": skey,
"sort": "0",
"source": "app",
"source_app": "android",
"standby_id": "oziq7dxw:::",
"sys_version": "29",
"timestamp": ctime,
"union_mark": "blank&_&blank&_&oziq7dxw:::&_&blank&_&blank",
"vipService": "",
"warehouse": "VIP_BJ",
"width": "1080"
}

search_string = "&".join(["{}={}".format(key, search_dict[key]) for key in sorted(search_dict.keys())])
# print(body_string)
salt = "aee4c425dbb2288b80c71347cc37d04b"
tmp = sha1(f"{salt}{search_string}")
api_sign = sha1(f"{salt}{tmp}")

res = requests.post(
url="https://mapi.appvipshop.com/vips-mobile/rest/shopping/search/product/list/v1",
data=search_dict,
headers={
"Authorization": "OAuth api_sign={}".format(api_sign)
},
verify=False
)
print(res.text)

__END__