某H视频-mls

某H视频-mls

分析流程 #

这个软件呢,其实分析主要就是想要看他拉人获得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;
    }

在上面那个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