分析流程 #
这个软件呢,其实分析主要就是想要看他拉人获得vip的操作
抓包可以找到一个api
api/user/traveler
这之前token还没有得到,参数
{"deviceId": device, "dev": 随机3字符, "code": '{"p":"PDTVPD"}'}
这个code参数其实开始我也不知道是啥,走了一遍拉人的流程,发现app给我们的邀请链接有p参数,就是邀请码
https://z6mhir31k.xyz?p=PDTVPD
在进到网页中点击下载软件会将邀请码复制到我们的粘贴板上
{"p":"PDTVPD"}
当你进入app时,读取粘贴板的第一条数据
public static String u() {
ClipData v0 = FragmentAnim.f.getSystemService("clipboard").getPrimaryClip();
String v1 = "";
if(v0 != null && v0.getItemCount() > 0) {
CharSequence v0_1 = v0.getItemAt(0).getText();
if(TextUtils.isEmpty(v0_1)) {
return v1;
}
else {
return v0_1.toString();
}
}
return v1;
}
deviceId #
顺着上去就发现了deviceId的生成
public final void C() {
String v0_2;
String v3_1;
String v1 = "everb";
String v2 = "";
if(a.a.exists()) {
try {
ObjectInputStream v0_1 = new ObjectInputStream(new FileInputStream(a.a));
Object v3 = v0_1.readObject();
try {
v0_1.close();
goto label_21;
}
catch(Exception v0) {
}
}
catch(Exception v0) {
v3_1 = v2;
}
Log.d(v1, v0.toString());
}
else {
v3_1 = v2;
}
label_21:
if(v3_1 == null) {
Log.d(v1, "data == null");
v3_1 = v2;
}
if(TextUtils.isEmpty(((CharSequence)v3_1))) {
v0_2 = String.valueOf(System.currentTimeMillis() + UUID.randomUUID().getLeastSignificantBits());
try {
byte[] v0_4 = MessageDigest.getInstance("MD5").digest(v0_2.getBytes());
StringBuilder v1_1 = new StringBuilder();
int v5;
for(v5 = 0; v5 < v0_4.length; ++v5) {
String v6 = Integer.toHexString(v0_4[v5] & 0xFF);
if(v6.length() < 2) {
v1_1.append(0);
}
v1_1.append(v6);
}
v3_1 = v1_1.toString();
}
他会打开是个名为.CAD的文件,把deviceid写进去,如果像要看一遍生成新device,记得删除这个文件
但是我却没有搞懂这个deviceID的鉴别方法,你自己随便改一两位他都不会返回,看不懂
下面是用python模拟生成的deviceid
import uuid,hashlib,unpack
t = int(time.time()*1000)
uuid_bytes = uuid.uuid1().bytes
leastbit = struct.unpack('>q', uuid_bytes[8:])[0]
print(leastbit)
v4 = hashlib.md5(str(t+leastbit).encode()).digest()
v2 = []
for i in v4:
v1 = hex(i & 0xFF)[2:]
if len(v1) < 2:
v1 = "0"+v1
v2.append(v1)
device = ''.join(v2)
这个几把最低64位真的搞得烦,没见过
里面其实还有个chcode不知道是啥
public JSONObject c(String arg5, String arg6) {
this.a("deviceId", arg5);
Random v5 = new Random();
StringBuffer v0 = new StringBuffer();
int v1;
for(v1 = 0; v1 < 3; ++v1) {
v0.append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt(v5.nextInt(62)));
}
this.a("dev", v0.toString());
if(arg6 != null) {
this.a("code", arg6);
}
try {
arg5 = FragmentAnim.f.getPackageManager().getApplicationInfo(FragmentAnim.f.getPackageName(), 0x80).metaData.getString("UMENG_CHANNEL");
}
catch(PackageManager$NameNotFoundException ) {
arg5 = "";
}
StringBuilder v6 = new StringBuilder();
v6.append("-------------->");
v6.append(arg5);
String v0_1 = "渠道号:";
Log.e(v0_1, v6.toString());
if(!TextUtils.isEmpty(((CharSequence)arg5)) && !arg5.equals(FragmentAnim.A())) {
this.a("chCode", arg5);
Log.e(v0_1, "chCode-------------->" + arg5);
}
return b.b;
}
header #
在上面那个api中header中的s和t
import time,hashlin
t=str(int(time.time()*1000))
s=hsahlib.md5(t[3:8].encode()).hesdigest()
到最后我发现妈的这软件好像也不加头,我的vip啊,也有可能是我哪里遗漏了些什么
注意ua,没带或者ua有问题都不会返回token,我在网上找的爬虫ua就不行,用模拟器自己生成的ua就行
个人里面那个填写invitecode的地方好像也是摆设
encData #
在后面返回数据里encData是有加密的,aes/cbc/pkcs7,key和iv都是一样的
JhbGciOiJIUzI1Ni
m3u8解密 #
m3u8文件解密,得到的m3u8文件里的key是假的
看了几个关于m3u8的类
会找到getkeyPath这个方法
key是在本地文件kdbc/kb里,把key copy出来
配合N_m3u8DL-CLI可以下载m3u8文件
打印N_m3u8DL-CLI的命令,视频存放在Videos目录
def download():
id = '1570'
# 143811
# 24323
t = str(int(time.time()*1000))
header = {'deviceid': 'ac6819db96c1892587d63f353bd9a51c',
't': t,
's': hashlib.md5(t[3:8].encode()).hexdigest(),
'authorization': 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzMzNjQwNyIsImlzcyI6IiIsImlhdCI6MTY3MTI3NTY5NCwibmJmIjoxNjcxMjc1Njk0LCJleHAiOjE4Mjg5NTU2OTR9.766BQyjE6ZO5ZPC5Y6J0i5BZKmezUk7CXYo-cStNp_w'
}
res = requests.get(
f'https://mhapp.vxxsred.xyz/api/video/getVideoById?videoId={id}', headers=header).json()
# print(res)
data = res['encData']
data = AES_decrypt(data, 'JhbGciOiJIUzI1Ni', 'JhbGciOiJIUzI1Ni')
print(data)
data = json.loads(data)
print(data['videoUrl'])
print('"https://nwretbns.yongchengge.cn/' +
data['videoUrl']+r'" --workDir "C:\Users\xx\Videos" --useKeyFile "C:\Users\xx\Desktop\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG\key\kb"')
# https://videossy.tsk12.top/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8/
# lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8
# "https://nwretbns.yongchengge.cn/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8" --workDir "E:\tools\other\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG" --useKeyFile "C:\Users\jinchuan\Desktop\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG\key\kb"
# https://nwretbns.yongchengge.cn/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8
全部代码
import hashlib
import time
import requests
from Crypto.Cipher import AES
import base64
import json
import struct
import uuid
import random
import string
def add(text, L=16):
l = len(text)
if l < L:
text = text+'\0'*(L-l)
elif l > L:
text = text+'\0'*(L-l % L)
return text.encode()
def AES_decrypt(cipher, key, iv):
aes = AES.new(add(key), AES.MODE_CBC, add(iv))
return aes.decrypt(base64.b64decode(cipher.encode())).decode().replace('\x08', '').replace('\x0f', '')
def download():
id = '1570'
# 143811
# 24323
t = str(int(time.time()*1000))
header = {'deviceid': 'ac6819db96c1892587d63f353bd9a51c',
't': t,
's': hashlib.md5(t[3:8].encode()).hexdigest(),
'authorization': 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzMzNjQwNyIsImlzcyI6IiIsImlhdCI6MTY3MTI3NTY5NCwibmJmIjoxNjcxMjc1Njk0LCJleHAiOjE4Mjg5NTU2OTR9.766BQyjE6ZO5ZPC5Y6J0i5BZKmezUk7CXYo-cStNp_w'
}
res = requests.get(
f'https://mhapp.vxxsred.xyz/api/video/getVideoById?videoId={id}', headers=header).json()
# print(res)
data = res['encData']
data = AES_decrypt(data, 'JhbGciOiJIUzI1Ni', 'JhbGciOiJIUzI1Ni')
print(data)
data = json.loads(data)
print(data['videoUrl'])
print('"https://nwretbns.yongchengge.cn/' +
data['videoUrl']+r'" --workDir "C:\Users\jinchuan\Videos" --useKeyFile "C:\Users\jinchuan\Desktop\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG\key\kb"')
# https://videossy.tsk12.top/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8/
# lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8
# "https://nwretbns.yongchengge.cn/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8" --workDir "E:\tools\other\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG" --useKeyFile "C:\Users\jinchuan\Desktop\N_m3u8DL-CLI_v3.0.2_with_ffmpeg_and_SimpleG\key\kb"
# https://nwretbns.yongchengge.cn/lfb/5c/5h/8n/rv/fb43cc643014465380e4c222aa60fb30.m3u8
def r(n):
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(n))
ua='Mozilla/5.0 (Linux; Android 7.1.2; HD1910 Build/N2G48H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.70 Mobile Safari/537.36;SuiRui/mh/ver=1.1.5'
def com(url):
result=requests.get(url,proxies={'https':'127.0.0.1:7890'},headers={'user-agent':ua})
def traveler():
api = 'https://mhapp.vxerfxs.shop/api/user/traveler'
t = int(time.time()*1000)
uuid_bytes = uuid.uuid1().bytes
leastbit = struct.unpack('>q', uuid_bytes[8:])[0]
print(leastbit)
v4 = hashlib.md5(str(t+leastbit).encode()).digest()
v2 = []
for i in v4:
v1 = hex(i & 0xFF)[2:]
if len(v1) < 2:
v1 = "0"+v1
v2.append(v1)
device = ''.join(v2)
data = {"deviceId": device, "dev": r(3), "code": '{"p":"PDTVPD"}'}
print(data)
t=int(time.time()*1000)
header = {
'deviceid': device, 't': str(t),
's': hashlib.md5(str(t)[3:8].encode()).hexdigest(),
'user-agent':ua
}
result = requests.post(api, json=data, headers=header,proxies={'https':'127.0.0.1:7890'})
print(result.status_code, result.text)
com('https://z6mhir31k.xyz?p=PDTVPD')
traveler()
# https://z6mhir31k.xyz?p=LVMC5Y