1 今日目标
# 得物app
-采集首页推荐信息
# 版本选择
-4.74.5版本
2 绕过强制更新
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 8:01
@Description: Null
'''
import frida
import sys
rdev = frida.get_remote_device()
pid = rdev.spawn(["com.shizhuang.duapp"])
session = rdev.attach(pid)
scr = """
Java.perform(function () {
var AlertDialog = Java.use('androidx.appcompat.app.AlertDialog');
AlertDialog.show.implementation = function(){
console.log("执行了");
//this.show();
}
});
"""
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()
3 抓包(反代理)
如果没开SocksDroid,发现根本抓不到返回的结果
发现就是这个api返回的结果
# 1 该app进制了手机端代理,需要使用SocksDroid代理
-切记:一定要把之前配置的手机代理删除
# 2 打开charles,抓包
# 3 分析接口
-地址:https://app.dewu.com/sns-rec/v1/recommend/all/feed
-请求方式:get
-请求头:都可以删除,只剩 X-Auth-Toke
User-Agent duapp/4.74.5(android;11)
X-Auth-Toke # 不能删,需要携带
-请参数:
lastId # 空:第一页 或 1:继续加载
limit 20 # 每次显示多少条
newSign 178d802feef18d6a75caad8c048e10cf # 像加密的,测试可以删除,但是必须破,否则后续的完成不了
-----以下的都可以不携带-----
abType social_brand_strategy_v454
abValue 1
deliveryProjectId 0
abVideoCover 2
abRectagFengge 0
abRecReason 0
-请求体:无
# 4 分析完,我们破解目标
-newSign
-X-Auth-Token
4 破解newSign(难)
4.1 反编译app
# 1 把 得物app ,拖到 jadx中,搜索 "newSign
-发现 addQueryParameter比较像
-QueryParameter名字就是请求地址中问号后面的东西
-大胆猜测,就是它----》后面要做hook验证
# 2 分析host.addQueryParameter("newSign", RequestUtils.c(hashMap2, currentTimeMillis));
# 3 调用了RequestUtils.c,传了两个参数进去
# 4 写个hook脚本,测试是否走了这个c
-通过写hook脚本,确认走了这个c
4.2 hook-RequestUtils.c
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 10:01
@Description: Null
'''
import frida
import sys
# 连接手机设备
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
// 包.类
var RequestUtils = Java.use("com.shizhuang.duapp.common.utils.RequestUtils");
// Hook,替换
RequestUtils.c.implementation = function(map,j2){
console.log('--------------------------------')
// 执行原来的方法
console.log('1.参数字典为:',map); // 此处直接打印map,发现打印的是对象,我们需要转换一下
console.log('2.参数字典类型为:',JSON.stringify(map)); // 查看一下类型 :java.util.HashMap
// 以下是固定格式
var Map = Java.use('java.util.HashMap');
var obj = Java.cast(map, Map);
console.log('3.参数字典转成字符串类型为:',obj.toString());
console.log("传入的参数j2为:",j2);
var res = this.c(map,j2)
console.log("加密后为:",res);
return res;
}
});
"""
#367368
script = session.create_script(scr)
def on_message(message, data):
print(message, data)
script.on("message", on_message)
script.load()
sys.stdin.read()
'''
--------------------------------
1.参数字典为: [object Object]
2.参数字典类型为: "<instance: java.util.Map, $className: java.util.HashMap>"
3.参数字典转成字符串类型为: {abValue=1, deliveryProjectId=0, abRectagFengge=0, abType=social_brand_strategy_v454, limit=20, lastId=, abRecReason=0, abVideoCover=2}
传入的参数j2为: 1708847014908
加密后为: 8508b78f222c3cdd5d183d0033005db0
--------------------------------
1.参数字典为: [object Object]
2.参数字典类型为: "<instance: java.util.Map, $className: java.util.HashMap>"
3.参数字典转成字符串类型为: {type=1}
传入的参数j2为: 1708847014910
加密后为: 5e7d26e2b8ca0eeee312f744885c1052
'''
4.3 分析RequestUtils.c--java源码--干了5个事
public static synchronized String c(Map<String, String> map, long j2) throws UnsupportedEncodingException {
synchronized (RequestUtils.class) {
PatchProxyResult proxy = PatchProxy.proxy(new Object[]{map, new Long(j2)}, null, changeQuickRedirect, true, 6612, new Class[]{Map.class, Long.TYPE}, String.class);
if (proxy.isSupported) {
return (String) proxy.result;
} else if (map == null) {
return "";
} else {
//1 往map中放了很多 key-value
map.put("uuid", DuHttpConfig.d.getUUID());
map.put("platform", "android");
map.put("v", DuHttpConfig.d.getAppVersion());
map.put("loginToken", DuHttpConfig.d.getLoginToken());
map.put("timestamp", String.valueOf(j2));
//2 把上面的map放到arrayList中,做排序
ArrayList arrayList = new ArrayList(map.entrySet());
Collections.sort(arrayList, new Comparator<Map.Entry<String, String>>() {
public static ChangeQuickRedirect changeQuickRedirect;
@Override
public int compare(Map.Entry<String, String> entry, Map.Entry<String, String> entry2) {
PatchProxyResult proxy2 = PatchProxy.proxy(new Object[]{entry, entry2}, this, changeQuickRedirect, false, 6618, new Class[]{Map.Entry.class, Map.Entry.class}, Integer.TYPE);
return proxy2.isSupported ? ((Integer) proxy2.result).intValue() : entry.getKey().toString().compareTo(entry2.getKey());
}
});
// 3 把上面的arraylist中的内容,拼接到sb中,转成字符串
StringBuilder sb = new StringBuilder();
for (int i2 = 0; i2 < arrayList.size(); i2++) {
Map.Entry entry = (Map.Entry) arrayList.get(i2);
sb.append(((String) entry.getKey()) + ((String) entry.getValue()));
}
String sb2 = sb.toString();
DuHttpConfig.LogConfig logConfig = DuHttpConfig.f15800h;
String str = f16243a;
// 4 调用了AESEncrypt.encode,把sb2传入,做了加密
// 5 把加密后的结果,调用a(加密结果)--》返回结果返回了
return a(AESEncrypt.encode(DuHttpConfig.f15796c, sb2));
}
}
}
/*
// 1 参数拼接
// 把uuid,platform,v,loginToken,timestamp放入map中
map.put("uuid", DuHttpConfig.d.getUUID());
map.put("platform", "android");
map.put("v", DuHttpConfig.d.getAppVersion());
map.put("loginToken", DuHttpConfig.d.getLoginToken());
map.put("timestamp", String.valueOf(j2));
//2 把map转成ArrayList,并进行排序
ArrayList arrayList = new ArrayList(map.entrySet());
Collections.sort(arrayList, new Comparator<Map.Entry<String, String>>()
// 3 构建字符串
StringBuilder sb = new StringBuilder();
sb.append()
// 4 执行AESEncrypt.encode 加密
AESEncrypt.encode(DuHttpConfig.f15796c, sb2)
// 5 把返回结果当参数传入a中
a(AESEncrypt.encode(DuHttpConfig.f15796c, sb2));
*/
4.4 看a方法,干了什么事情
public static String a(String str) {
PatchProxyResult proxy = PatchProxy.proxy(new Object[]{str}, null, changeQuickRedirect, true, 6616, new Class[]{String.class}, String.class);
if (proxy.isSupported) {
return (String) proxy.result;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(str.getBytes());
byte[] digest = messageDigest.digest();
StringBuilder sb = new StringBuilder();
for (byte b2 : digest) {
String hexString = Integer.toHexString(b2 & 255);
while (hexString.length() < 2) {
hexString = "0" + hexString;
}
sb.append(hexString);
}
return sb.toString();
} catch (NoSuchAlgorithmException e2) {
e2.printStackTrace();
return "";
}
}
}
//发现是MD5方法,可以用python模拟
4.5再看AESEncrypt.encode方法
public class AESEncrypt {
static {
System.loadLibrary("JNIEncrypt"); // libJNIEncrypt.so
}
// 调用 java中的encode---》调用了c中jni开发的getByteValues,得到结果,java处理---》处理完后又调用了---》调用了c中jni开发的encodeByte
public static String encode(Object obj, String str) {
String byteValues = getByteValues();
StringBuilder sb = new StringBuilder(byteValues.length());
for (int i2 = 0; i2 < byteValues.length(); i2++) {
if (byteValues.charAt(i2) == '0') {
sb.append('1');
} else {
sb.append('0');
}
}
return encodeByte(str.getBytes(), sb.toString());
}
public static native String encodeByte(byte[] bArr, String str);
public static native String getByteValues();
}
/*
1 接下来:看getByteValues() 返回结果是什么
2 hook--》getByteValues看返回结果是什么
-返回了一堆0101010101
3 后续操作是对返回的一堆010101取反
*/
4.6 hook--查看getByteValues返回结果
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
var AESEncrypt = Java.use("com.duapp.aesjni.AESEncrypt");
AESEncrypt.getByteValues.implementation = function(){
var res = this.getByteValues();
console.log('getByteValues返回值是:',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()
'''
getByteValues返回值是: 101001011101110101101101111100111000110100010101010111010001000101100101010010010101110111010011101001011101110101100101001100110000110100011101010111011011001101001101011101010100001101000011
getByteValues返回值是: 101001011101110101101101111100111000110100010101010111010001000101100101010010010101110111010011101001011101110101100101001100110000110100011101010111011011001101001101011101010100001101000011
'''
4.7 hook--AESEncrypt.encode查看参数和返回值
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 10:21
@Description: Null
'''
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
var AESEncrypt = Java.use("com.duapp.aesjni.AESEncrypt"); // encode是重载的方法,has more than one overload
AESEncrypt.encode.overload('java.lang.Object', 'java.lang.String').implementation = function(obj,str){
console.log('传入参数:',str);
var res = this.encode(obj,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()
sys.stdin.read()
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 10:21
@Description: Null
'''
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
var AESEncrypt = Java.use("com.duapp.aesjni.AESEncrypt"); // encode是重载的方法,has more than one overload
AESEncrypt.encode.overload('java.lang.Object', 'java.lang.String').implementation = function(obj,str){
console.log('传入参数:',str);
var res = this.encode(obj,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()
sys.stdin.read()
'''
传入参数: abRecReason0abRectagFengge0abTypesocial_brand_strategy_v454abValue1abVideoCover2deliveryProjectId0lastIdlimit20loginTokenplatformandroidtimestamp1708847639357uuidbf2d0fb2366144d7v4.74.5
返回值是: VwGEkNCFuNTmwD6w19ogqRI5VLum7q2QP6Z4TaJLKn3cvS7y81d64hsABQvdu03vdj76OpM/O2awG8h9UpcJb7mTLuTReeaHVgQgd6kvRikIdeR4xJw0+TRbBSp2lMcQMGn2O4/9BKR+RkYgwOUZd4MFCaYlia6t5gNc3rbNTJjA6aJyja63+nAcybbrLaS0sDvYHP+MMqPt6v2G9n3m+a5cSovZRCdLbuc3vRl7ACqQFzAVbqOz6k0iDV5tBz02
'''
4.8 查看encodeByte(str.getBytes(), sb.toString())是如何实现
# 0 调用:encodeByte,传入了
str:loginTokenplatformandroidtimestamp1697201773755type1uuidaa39fda5dfb88d79v4.74.5
sb:010100101010101取反了,固定的
返回了加密
# 1 encodeByte是jni开发的,用c语言开发的---》需要去看它的c源码--》libJNIEncrypt.so
# 2 使用ida打开这个so文件--》libJNIEncrypt.so--》查看exports--》查看so文件中有哪些函数
-看32位的so文件
# 3 发现它是动态注册---》找JNI_OnLoad
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
# jclass clazz = (*env)->FindClass(env, "com/justin/s9day11/Dynamic");
v4 = (*(__int64 (__fastcall **)(__int64, const char *))(*(_QWORD *)v6[0] + 48LL))(
v6[0],
"com/duapp/aesjni/AESEncrypt");
#int res = (*env)->RegisterNatives(env, clazz, gMethods,2);
(*env)->RegisterNatives(v3, v4, off_15010, 8LL); # 动态注册的对应关系在off_15010函数中
return v2;
}
# 4 双击 off_15010 查看它的汇编,找到java中encodeByte对应c中encode,有两个参数(bytes,字符串),返回一个参数字符串
# 5 查看encode的代码---》双击--》按f5--》引入头文件--》扣出核心代码
v11[v9] = 0;
v18 = (const char *)AES_128_ECB_PKCS5Padding_Encrypt(v11, v8); # 得到了v18,要返回的字节数组
return a1->functions->NewStringUTF(a1, v18); # v18 要返回的字节数组
# 6 AES_128_ECB_PKCS5Padding_Encrypt
__int64 __fastcall AES_128_ECB_PKCS5Padding_Encrypt()
{
return AES_128_ECB_PKCS5Padding_Encrypt();
}
# 7 AES_128_ECB_PKCS5Padding_Encrypt--》核心代码
v24 = v10;
v25 = (char *)malloc(v10);
v26 = v25;
if ( v10 >= 16 )
{
v27 = (unsigned int)(v10 / 16);
v28 = v8;
v29 = v25;
do
{
AES128_ECB_encrypt(v28, a2, v29); # 循环执行AES128_ECB_encrypt,猜测是aes加密
v29 += 16;
--v27;
v28 += 16;
}
while ( v27 );
}
v30 = b64_encode(v26, v24); # 看到了把某个字节数组用base64编码,返回了
return v30;
# 8 我们看了代码后猜测是:aes加密---》结果使用base64编码
1 aes明文是什么:
2 秘钥是什么:
3 自己使用python实现---》跟hook得到的结果比较,如果一样,说明我们猜的是对的
4.9 hook--so文件中方法--AES_128_ECB_PKCS5Padding_Encrypt
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 12:09
@Description: Null
'''
# j_AES128_ECB_encrypt
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
//返回libJNIEncrypt.so中的AES_128_ECB_PKCS5Padding_Encrypt中的内存地址
var addr_func = Module.findExportByName("libJNIEncrypt.so", "AES_128_ECB_PKCS5Padding_Encrypt");
//使用Interceptor.attach调用,
Interceptor.attach(addr_func, {
onEnter: function(args){
console.log("--------------------------执行函数--------------------------");
console.log("参数1:", args[0].readUtf8String());
console.log("参数2:", args[1].readUtf8String());
},
onLeave: function(retValue){
console.log("返回值:", retValue.readUtf8String());
}
})
});
"""
script = session.create_script(scr)
def on_message(message, data):
print(message, data)
script.on("message", on_message)
script.load()
sys.stdin.read()
'''
参数1: loginTokenplatformandroidtimestamp1708849952350userId2019355096uuidbf2d0fb2366144d7v4.74.5
参数2: d245a0ba8d678a61
返回值: knGGXR0bR7LQn4eRCvJsdZ4D96wrRcYi2zPWWxLMOs2tQZ1W0zYDi25kxm0n1j1S012RG+Kra+swyA8o2aEROsp7PSiTH6a2Ctki7vO+3SWZLFVExv8GJs+lLgTW9h7o
'''
我们因此可以利用python来模拟AES加密
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 16:23
@Description: Null
'''
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64
key = "d245a0ba8d678a61"
str = "abRecReason0abRectagFengge0abTypesocial_brand_strategy_v454abValue1abVideoCover2deliveryProjectId0lastIdlimit20loginTokenplatformandroidtimestamp1708848941013uuidbf2d0fb2366144d7v4"
aes = AES.new(key.encode(), AES.MODE_ECB)
str2 = aes.encrypt(pad(str.encode(), 16))
print(base64.b64encode(str2).decode())
#VwGEkNCFuNTmwD6w19ogqRI5VLum7q2QP6Z4TaJLKn3cvS7y81d64hsABQvdu03vdj76OpM/O2awG8h9UpcJb7mTLuTReeaHVgQgd6kvRikIdeR4xJw0+TRbBSp2lMcQMGn2O4/9BKR+RkYgwOUZd4MFCaYlia6t5gNc3rbNTJjA6aJyja63+nAcybbrLaS0K+vd3ooZs8/ImnKJ4Fit865cSovZRCdLbuc3vRl7ACo8pthaMqeORkh+vB0HFF8B
发现咱猜测是正确的
也就是说sign值是通过aes+md5进行加密
loginTokenplatformandroidtimestamp1708849952350userId2019355096uuidbf2d0fb2366144d7v4.74.5
明文::
loginToken # 空的 platform android # 固定的 timestamp 708849952350# 时间戳
userId 2019355096#账户信息
uuid bf2d0fb2366144d7 # 不知道怎么来的 16进制的手机设备id v 4.74.5 # 固定的版本号
4.9 破解uuid
# 1 map.put("uuid", DuHttpConfig.d.getUUID());
# 2 DuHttpConfig.d.getUUID()--->PatchProxyResult-->看不懂
public String getUUID() {
PatchProxyResult proxy = PatchProxy.proxy(new Object[0], this, changeQuickRedirect, false, 5097, new Class[0], String.class);
return proxy.isSupported ? (String) proxy.result : "";
}
# 3 想办法
-1 固定uuid试试 bf2d0fb2366144d7-->是可以的
-2 项目uuid应该是一样的---》别的位置找到uuid的生成,拿过来用试试
# 4 后来 破 X-Auth-Token 发现也有uuid---》看那个uuid能看懂---》使用那个试试
-hashMap.put("uuid", HPDeviceInfo.b(BaseApplication.c()).a((Activity) null));
# 5 HPDeviceInfo.b(参数).a(参数)--》查看a
public String a(Activity activity) {
String str = this.f15203b; # 从内存中取出某个值---》重要项目不重启,这值是固定的
if (str != null) {
return str;
}
if (activity != null) {
final TelephonyManager telephonyManager = (TelephonyManager) activity.getApplication().getSystemService("phone");
new RxPermissions(activity).c("android.permission.READ_PHONE_STATE").subscribe(new Consumer() {
@Override // io.reactivex.functions.Consumer
public final void accept(Object obj) {
# 获取手机设备id号
HPDeviceInfo.this.a(telephonyManager, (Boolean) obj);
}
});
} else {
this.f15203b = a();
}
return this.f15203b;
}
### python 生成手机设备id号
def gen_imei():
# 这个不够标准
return "".join(random.choices('0123456789abcdef', k=15))
4.10 整合代码-得到newSign
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 16:55
@Description: Null
'''
import time
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
import random
import copy
def create_android_id():
data_list = []
for i in range(1, 9):
part = "".join(random.sample("0123456789ABCDEF", 2))
data_list.append(part)
return "".join(data_list).lower()
def md5(data_bytes):
hash_object = hashlib.md5()
hash_object.update(data_bytes)
return hash_object.hexdigest()
def aes_encrypt(data_string):
key = "d245a0ba8d678a61"
aes = AES.new(
key=key.encode('utf-8'),
mode=AES.MODE_ECB,
)
raw = pad(data_string.encode('utf-8'), 16)
return aes.encrypt(raw)
uid = create_android_id()
ctime = str(int(time.time() * 1000))
reply_param_dict = {
"lastId": "1",
"limit": "20",
}
new_dict = copy.deepcopy(reply_param_dict)
new_dict.update(
{"loginToken": "", "platform": "android", "timestamp": str(int(time.time() * 1000)), "uuid": uid, "v": "4.74.5"}
)
ordered_string = "".join(["{}{}".format(key, new_dict[key]) for key in sorted(new_dict.keys())])
aes_string = aes_encrypt(ordered_string)
aes_string = base64.encodebytes(aes_string)
aes_string = aes_string.replace(b"\n", b"")
sign_string = md5(aes_string)
print(sign_string)
5 破解 X-Auth-Token
# 1 反编译--搜索 X-Auth-Token
# 2 找到:hashMap.put("X-Auth-Token", ServiceManager.a().getJwtToken());
-看到了其他请求都,添加到map中了
# 3 ServiceManager.a().getJwtToken()
# 4 String getJwtToken(); 进行不下去了
# 5 x-auth-token到底是什么---》token串---》典型的三段式--》用来做前后端登录认证的 JWT
-即便不登录,也有token串,三段都是使用base64编码---》base64解码
Bearer
eyJhbGciOiJSUzI1NiJ9. # 头
eyJpYXQiOjE2OTcxOTg3MDYsImV4cCI6MTcyODczNDcwNiwiaXNzIjoiYWEzOWZkYTVkZmI4OGQ3OSIsInN1YiI6ImFhMzlmZGE1ZGZiODhkNzkiLCJ1dWlkIjoiYWEzOWZkYTVkZmI4OGQ3OSIsInVzZXJJZCI6MTkyMjE2ODYzOCwidXNlck5hbWUiOiLlvpfniallci1DMUkzUDdMNyIsImlzR3Vlc3QiOnRydWV9. # 荷载
Z-RMvetpgBdkh4TbOuEZQOBHsEKU3cYo1_18WzWij3VK_6waCY2fqMDrilMT2bQT3PFF5bCyg1gASO_Qw4-zzGvBKKrpyA6VKCPZe3QnhFd5dJ8WyD9-06AwBvpapuGgbfFel-lUCw0dK2Fq6mEh3zyVy9zgz-z2VMwwkqeh9aVxswqOK40gsD20L_ySVPOUg9QnjtrnK5P78HgR3gHmlQS2CNIUGDm1kPyniQlkdLI7yO1y0pD-r75amFcozvxpGLwWJd3B79y0XU37GSVDVNxuc0q9kjU0OSbtZ0GE4xiXCdE6_tIBdVDsayOdtRcLjdvGma_HjAK4TV7WvokzJw # 签名
# 6 token串都是后端生成,返回给前端的
一定是app一启动,向后端发送一个请求,获得到了这个token串
# 7 抓包,查看,那个请求获得token(清空app缓存,再抓)
请求地址:https://app.dewu.com/api/v1/app/user_core/users/getVisitorUserId
请求方式:post
请求头:
duv 4.74.5
duloginToken
dudeviceTrait Pixel+2+XL
dudeviceBrand google
timestamp 1697206819966
shumeiid 20231013222019b717ec769d0f04ac4f5207c9f95fe8e600dfeb39335ca0c2
oaid
User-Agent duapp/4.74.5(android;11)
请求参数:无
请求体:
{
"loginToken": "",
"newSign": "8b471539ad8ef3dbffa7b2ce4cd9b4d8",
"platform": "android",
"timestamp": "1697206819966",
"uuid": "aa39fda5dfb88d79",
"v": "4.74.5"
}
注意:
JWT绝对是后端返回的,去找接口时,必须要重新安装软件再重新抓包,不然抓不到包
5.1 使用python获取x-auth-token
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 17:49
@Description: Null
'''
import time
import requests
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from urllib.parse import quote_plus
import base64
import json
import random
def create_android_id():
data_list = []
for i in range(1, 9):
part = "".join(random.sample("0123456789ABCDEF", 2))
data_list.append(part)
return "".join(data_list).lower()
def md5(data_bytes):
hash_object = hashlib.md5()
hash_object.update(data_bytes)
return hash_object.hexdigest()
def aes_encrypt(data_string):
key = "d245a0ba8d678a61"
aes = AES.new(
key=key.encode('utf-8'),
mode=AES.MODE_ECB,
)
raw = pad(data_string.encode('utf-8'), 16)
return aes.encrypt(raw)
uid = create_android_id()
ctime = str(int(time.time() * 1000))
param_dict = {"loginToken": "", "platform": "android", "timestamp": ctime, "uuid": uid, "v": "4.74.5"}
ordered_string = "".join(["{}{}".format(key, param_dict[key]) for key in sorted(param_dict.keys())])
aes_string = aes_encrypt(ordered_string)
aes_string = base64.encodebytes(aes_string)
aes_string = aes_string.replace(b"\n", b"")
sign = md5(aes_string)
param_dict['newSign'] = sign
res = requests.post(
url="https://app.dewu.com/api/v1/app/user_core/users/getVisitorUserId",
headers={
"duuuid": uid,
"duimei": "",
"duplatform": "android",
"appId": "duapp",
"timestamp": ctime,
'duv': '4.74.5',
'duloginToken': '',
'dudeviceTrait': 'Pixel+2+XL',
'shumeiid': '2024022517352650cbee0674c1c8d9907d9ab7991b6ac400c6fc0b7d385acd',
'User-Agent': 'duapp/4.74.5(android;11)'
},
json=param_dict,
verify=False
)
print(res.headers)
x_auth_token = res.headers['X-Auth-Token']
print(x_auth_token)
'''
Bearer.
eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3MDg4NTM3MzYsImV4cCI6MTc0MDM4OTczNiwiaXNzIjoiYmYyZDBmYjIzNjYxNDRkNyIsInN1YiI6ImJmMmQwZmIyMzY2MTQ0ZDciLCJ1dWlkIjoiYmYyZDBmYjIzNjYxNDRkNyIsInVzZXJJZCI6MjAxOTM1NTA5NiwiaXNHdWVzdCI6dHJ1ZX0.
NhHQxf75X3g9yz2NJ880u2Rgs0zQII_a-AjiiRzWCoh8k-FF64K7VyX94mwGdvO8ewkF0c2O-gE6TZ0bdl2UHQf3ZGj92LsLW2QXQBFFPPkr23E0r_SXnuvOC2Z2S_5t2skXx9fjrU6Kt8TKESObbgV56jjhML_D_QSxZ_xR70C1V5M2NPpQ9OGMxAJFe_eiK6jCwnzFja7XHAtq2XmijgorYokUd7kkvSE6iwsVv8vi-aS2lfGLfMrutolTmrRheaw7ms181wRt7Ij4ocw96rSpXmciOxnHVyTe5C-Sd0DtpM_omwQFk5cRgyYs5mWhPpbEmb0jEVg7zQL9pRH7nA
'''
6 代码整合
# -*- coding: utf-8 -*-
'''
@IDE : PyCharm
@version : 3.9
@Auth : 恍惚
@time : 2024/2/25 17:49
@Description: Null
'''
import time
import requests
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
import random
import urllib3
import copy
urllib3.disable_warnings()
def create_android_id():
data_list = []
for i in range(1, 9):
part = "".join(random.sample("0123456789ABCDEF", 2))
data_list.append(part)
return "".join(data_list).lower()
def md5(data_bytes):
hash_object = hashlib.md5()
hash_object.update(data_bytes)
return hash_object.hexdigest()
def aes_encrypt(data_string):
key = "d245a0ba8d678a61"
aes = AES.new(
key=key.encode('utf-8'),
mode=AES.MODE_ECB,
)
raw = pad(data_string.encode('utf-8'), 16)
return aes.encrypt(raw)
uid = create_android_id()
ctime = str(int(time.time() * 1000))
param_dict = {"loginToken": "", "platform": "android", "timestamp": ctime, "uuid": uid, "v": "4.74.5"}
ordered_string = "".join(["{}{}".format(key, param_dict[key]) for key in sorted(param_dict.keys())])
aes_string = aes_encrypt(ordered_string)
aes_string = base64.encodebytes(aes_string)
aes_string = aes_string.replace(b"\n", b"")
sign = md5(aes_string)
param_dict['newSign'] = sign
res = requests.post(
url="https://app.dewu.com/api/v1/app/user_core/users/getVisitorUserId",
headers={
"duuuid": uid,
"duimei": "",
"duplatform": "android",
"appId": "duapp",
"timestamp": ctime,
'duv': '4.74.5',
'duloginToken': '',
'dudeviceTrait': 'Pixel+2+XL',
'shumeiid': '2024022517352650cbee0674c1c8d9907d9ab7991b6ac400c6fc0b7d385acd',
'User-Agent': 'duapp/4.74.5(android;11)'
},
json=param_dict,
verify=False
)
print(res.headers)
x_auth_token = res.headers['X-Auth-Token']
print(x_auth_token)
'''
Bearer.
eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3MDg4NTM3MzYsImV4cCI6MTc0MDM4OTczNiwiaXNzIjoiYmYyZDBmYjIzNjYxNDRkNyIsInN1YiI6ImJmMmQwZmIyMzY2MTQ0ZDciLCJ1dWlkIjoiYmYyZDBmYjIzNjYxNDRkNyIsInVzZXJJZCI6MjAxOTM1NTA5NiwiaXNHdWVzdCI6dHJ1ZX0.
NhHQxf75X3g9yz2NJ880u2Rgs0zQII_a-AjiiRzWCoh8k-FF64K7VyX94mwGdvO8ewkF0c2O-gE6TZ0bdl2UHQf3ZGj92LsLW2QXQBFFPPkr23E0r_SXnuvOC2Z2S_5t2skXx9fjrU6Kt8TKESObbgV56jjhML_D_QSxZ_xR70C1V5M2NPpQ9OGMxAJFe_eiK6jCwnzFja7XHAtq2XmijgorYokUd7kkvSE6iwsVv8vi-aS2lfGLfMrutolTmrRheaw7ms181wRt7Ij4ocw96rSpXmciOxnHVyTe5C-Sd0DtpM_omwQFk5cRgyYs5mWhPpbEmb0jEVg7zQL9pRH7nA
'''
reply_param_dict = {
"lastId": "1",
"limit": "20",
# "newSign": ""
}
new_dict = copy.deepcopy(reply_param_dict)
new_dict.update(
# {"loginToken": "", "platform": "android", "timestamp": str(int(time.time() * 1000)), "uuid": uid, "v": "4.84.0"})
{"loginToken": "", "platform": "android", "timestamp": str(int(time.time() * 1000)), "uuid": uid, "v": "4.74.5"})
ordered_string = "".join(["{}{}".format(key, new_dict[key]) for key in sorted(new_dict.keys())])
aes_string = aes_encrypt(ordered_string)
aes_string = base64.encodebytes(aes_string)
aes_string = aes_string.replace(b"\n", b"")
sign_string = md5(aes_string)
reply_param_dict['newSign'] = sign_string
res = requests.get(
url="https://app.dewu.com/sns-rec/v1/recommend/all/feed/",
params=reply_param_dict,
headers={
"X-Auth-Token": x_auth_token,
'User-Agent': 'duapp/4.74.5(android;11)'
},
verify=False
)
print(res.text)
运行结果