吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1476|回复: 9
收起左侧

[原创] 某iq drm v3 解密全流程

[复制链接]
我是不会改名的 发表于 2026-2-13 22:49
[ 本帖最后由 我是不会改名的 于 2026-2-13 22:51 编辑 ]

需要强调的是,本文的研究内容仅用于学术和教育目的。任何未经授权解密或分发受保护内容的行为可能违反版权法和相关法律法规。研究人员应当遵守相关法律,尊重内容创作者的知识产权。

iq drm v3解密全流程

很久之前写的了,最近git上开源了很多,都有现成的了,那就简单分享一下。

vf

某网站wasm md5简单分析https://www.52pojie.cn/thread-1836908-1-1.html  这篇文章已经提到一种方式,主要是js比较方便,app实现起来比较麻烦。

上面已经提到了,md5是标准的,只不过加了盐,所以需要把盐找到。

刚好写这篇文章的时候,奥本海默上映了,就想到原子弹的基本原理。只需要一个中子轰击U原子,就可以生成三个中子,中子在继续轰击,达到一定程度就可以在持续下去,最终释放巨大能量。

1.png

在看看MD5流程,以CryptoJS.md5为例,传入dash ,先初始化,然后将字符串转为word

2.png

其次填充数据

3.png

然后进行运算

4.png
在这个过程中,最小的运算单元是一个 word(4 个字节),而我们传入的数据长度往往不是 4 的倍数。
利用这个特性:第一次传入 4 个字节:运算区块包含 4 字节输入 + 4 字节盐 + 4 字节盐...第二次传入 6 个字节:运算区块包含 4 字节输入 + (2 字节输入+2 字节盐) + 4 字节盐.

def srt_2_dword(s: bytes):
    s += b"abcdefgh"
    d = []
    s += b'\x00' *(len(s) % 4)
    for i in range(0, len(s), 4):
        d.append(hex(struct.unpack('<I', s[i:i + 4])[0]))
    return d

if __name__ == '__main__':
    datas = b"1234"
    d = srt_2_dword(datas)
    print(d)
    datas2 = b"123456"
    d2 = srt_2_dword(datas2)
    print(d2)

5.png

通过这种错位,只需要 Hook 两次指令或内存读取,就能逆推出盐值。
以iq TV版为例  https://apkpure.com/cn/iqiyi-video-for-tv-dramas-movies/com.iqiyi.i18n.tv/download

在 里面 libmcto_media_player.so (其他 so里面也有,都是一样的)(APP TV IQ app都是一样的 只是需要注意TV的 "qd_v": "1" )

根据关键词vf=很容易定位到4D36E,大致长这样

6.png

简单hook 一下

var libmcto_media_player ="libmcto_media_player.so";
var libmcto_media_player_addr = Module.findBaseAddress(libmcto_media_player);
var addr = libmcto_media_player_addr.add(0x4D36E8+1);
Interceptor.attach(addr, {
    onEnter: function(args) {
        console.log("libmcto_media_player.so - string", args[0].readCString());
    },
    onLeave: function(retval) {
        console.log("libmcto_media_player.so - retval", retval.readCString());
    }
});

7.png

然后用unibdg跑一下

package com.iq;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Paths;

public class VF extends AbstractJni {
    private static AndroidEmulator emulator = null;
    private static VM vm = null;
    private static Module module = null;

    VF() {
        emulator = AndroidEmulatorBuilder.for32Bit()
                .build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File("unidbg-android/src/main/resources/android/apk/iQIYI Video.apk"));
        DalvikModule dm = vm.loadLibrary("mcto_media_player", false);
        module = dm.getModule();
        String traceWFile = "unidbg-android/src/test/java/com/iq/tracewrite.txt";
        PrintStream traceWStream;
        try {
            traceWStream = new PrintStream(Files.newOutputStream(Paths.get(traceWFile)), true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        emulator.traceWrite().setRedirect(traceWStream);
    }

    public static void main(String[] args) throws Exception {
        VF vf = new VF();
        System.out.println("getVf");
        vf.getVf("/dash?");//自行修改
    }

    public String getVf(String s) {
        Number pointer = module.callFunction(emulator, 0x4D36E8 + 1, s);
        UnidbgPointer ret = UnidbgPointer.pointer(emulator, pointer.intValue() & 0xffffffffL);
        String string = ret.getString(0);
        System.out.println("vf: " + string);
        return "getVm";
    }
}

8.png

直接就能跑,结果也能对上,那么直接修改输入,运行两次。

分别传入 /dashaaa /dasha

    vf.getVf("/dasha");
    vf.getVf("/dashaaa");
}

根据上面的推论开始实践

目前已知

['0x7361642f', '0x61616168']
['0x7361642f', '0x****6168']

那么直接搜索0x....6168,

9.png

现在就知道

['0x7361642f', '0x61616168','0x....316f']
['0x7361642f', '0x316f6168']

再在另一个文件里面搜索

10.png

又进一步,可以说和原子弹的原理,不能说一模一样,只能说是完全不一样

['0x7361642f', '0x61616168','0x6d63316f']
['0x7361642f', '0x316f6168','0x....6d63']

就这样一直推,直到搜不到,直接尝试搜索0x....,如果没有多半出错了

11.png

ticket解密

获取key就需要解密ticket,目前我看到有两个版本,3和5,响应里面有写"licenseVersion":"5|3",3和都能用,解密后结果都一样。

解密ticket同样在这里面,不过不建议分析这个so,在libmonalisa.so这里,函数monalisa_set_license        000052D8        

首先是新建结构体,然后解析

  v10 = license_alloc();
  v11 = license_parse(v10, license_base64ed, license_base64ed_len);

然后base64,解析结构体

  simple_base64_decode((unsigned __int8 *)dst, 0x1000, &olen, license_base64ed, license_base64ed_len);
  v4 = (unsigned __int16 *)calloc(1u, 0xCu);
  *(_DWORD *)v4 = 0xFFFFFFFF;
  *((_DWORD *)v4 + 1) = 0xFFFFFFFF;
  v5 = calloc(0x1000u, 1u);
  v6 = olen;
  v7 = v5;
  *((_DWORD *)v4 + 2) = v5;
  if ( v6 > 4 )

这里是自定义的 TLV (Type-Length-Value) 结构。用 Go 语言重写解析逻辑非常清晰:第 1 个字节是 Type,第 3、4 字节是 Data Length。

func licenseReadUnit(lunit *LicenseUnit, buf []byte, bufPos int, bufLen int) int {
    if bufLen <= 4 {
       return 0
    }

    lunit.Type = uint16(buf[bufPos])
    lunit.Index = uint16(buf[bufPos+1])
    dataSize := uint32(buf[bufPos+2])<<8 + uint32(buf[bufPos+3])
    lunit.DataSize = dataSize

    if bufLen < int(dataSize)+4 {
       return 0
    }

    lunit.Data = make([]byte, dataSize)
    copy(lunit.Data, buf[bufPos+4:bufPos+4+int(dataSize)])

    return int(dataSize) + 4
}

按照不同 Type 映射数据(提取 Version、Uid、CkeyCipherData、KekData 等):

for {
    unit = &LicenseUnit{}
    i := licenseReadUnit(unit, licenseBinary, licenseBinaryPos, certLength)
    if i <= 3 {
       break
    }
    typeVal = unit.Type
    switch typeVal {
    case 0:
       l.Version = unit.Data[0]
    case 1:
       l.UidData = append([]byte{}, unit.Data[1:unit.DataSize]...)
       l.UidSize = int(unit.DataSize - 1)
    case 2:
       data := unit.Data
       if data[0] == 1 {
          l.UidData = append([]byte{}, data[1:unit.DataSize]...)
          l.UidSize = int(unit.DataSize - 1)
       }
    case 3:
       data := unit.Data
       v10 := int(data[0])
       v11 := int(data[1])<<8 + int(data[2])
       v12 := int(data[v11+3])
       if v12 == 1 {
          l.CkeyCipherSize = v11
          l.CkeyCipherData = append([]byte{}, data[3:v11+3]...)
          if v10 != 1 {
             continue
          }
          l.CkeySize = 32
       } else if v12 == 2 {
          l.KekSize = v11
          l.KekData = append([]byte{}, data[3:v11+3]...)
       }
    case 255:
       v14 := unit.Data
       certLengtha := int(v14[1])
       v16 := int(v14[certLengtha+2])
       v17 := int(v14[certLengtha+3])

       if v14[0] == 1 {
          hashCalculated := sha256.Sum256(licenseBinary[:licenseBinaryPos])
          hashInLicense := append([]byte{}, v14[certLengtha+4:certLengtha+4+(v16<<8)+v17]...)
          v15 := bytesEqual(hashCalculated[:], hashInLicense)
          if !v15 {
             return 1, nil
          }
       }
    }
    licenseBinaryPos += i
    certLength -= i
}

然后aes解密

l.CkeyData, err = aesDec(l.CkeyCipherData, l.KekData)//        iv := "0000000000000000"

最核心的部分是基于 VMP(虚拟机保护)的白盒 AES (DCM)。这实际上是标准的 CTR 模式配合 XOR 操作。
这里选择的dcm,实际上就是解密ctr加异或 12.png
常规做法肯定直接dfa,但是呢他这里的ctr,key和iv是写死的,
根据ctr模式特点,只需要拿到一组明文和一组密文异或。再那密文和上述结果异或就拿到key了。
13.png
这里的key就是解密ts的key了
这里贴几张图感兴趣的自己分析

sub_9298

14.png

sub_8F08

15.png

sub_9988

16.png

vm_call_func_by_id(vm, func_id, argcount, args, rv, 0);

sub_9ADC

17.png

TS

对应函数

int __fastcall monalisa_decrypt_data(
void *monalisa_ctx,
unsigned __int8 *out_buf_ptr,
uint32_t *out_buf_size,
const unsigned __int8 *in_buf_ptr,
int in_buf_size,
int in_buf_format)

先解析ts,提取pes,sdt等
拿到其中的service_name

然后就是解析它
mdcm|s1:9:10|a0|vd70b0e7a262f4cc52b667901eb2e8b9d|e1|f497006|
按|分割
m 后面是加密模式
s 后面1:9:10 表示十次一轮,一次ctr,9次异或
a 后面0表示只加密关键帧
v 后面 1-25 + "00000001"ctr的iv
其余的没啥用,通样在一个so里面,函数如下

int __fastcall parse_cipher_mode(mtsEncArgs *decrypt_info, const unsigned __int8 *service_str, unsigned __int8 *key)
{
  if ( key )
    qmemcpy(decrypt_info->key, key, sizeof(decrypt_info->key));
  v5 = strlen((const char *)service_str);
  v6 = (char *)calloc(1u, v5 + 1);
  strcpy(v6, (const char *)service_str);
  v7 = 0;
  v31 = decrypt_info;
  if ( v5 >= 1 )
  {
    iv = decrypt_info->iv;
    v9 = 0;
    v10 = 0;
    dst = iv;
    v28 = service_str;
    while ( 1 )
    {
      while ( 1 )
      {
        v11 = (unsigned __int8)v6[v10] - 97;
        if ( (unsigned __int8)v11 <= 0x19u )
          break;
LABEL_15:
        v10 = strlen((const char *)service_str);
LABEL_7:
        if ( v5 <= v10 )
          goto LABEL_37;
      }
      v12 = &v6[v10];
      v13 = &v6[v10 + 1];
      if ( *v13 == 124 )
      {
        v14 = 2;
        v15 = &v6[v10 + 1];
      }
      else
      {
        v16 = 0;
        do
          v17 = &v12[v16++];
        while ( v17[2] != 124 );
        v15 = &v12[v16 + 1];
        v14 = v16 + 2;
      }
      *v15 = 0;
      v10 += v14;
      switch ( v11 )
      {
        case '\0':
          ++v9;
          v31->isKeyframesOnly = atoi(v13) != 0;
          if ( v5 > v10 )
            continue;
          goto LABEL_37;
        case '\x04':
          goto LABEL_7;
        case '\f':
          if ( !strcmp(v13, (const char *)sub_5948) )
          {
            v18 = 1;
          }
          else
          {
            if ( strcmp(v13, (const char *)&loc_594C) )
              goto LABEL_7;
            v18 = 0;
          }
          ++v9;
          v31->cryptoMode = v18;
          if ( v5 <= v10 )
            goto LABEL_37;
          continue;
        case '\x12':
          v30 = v9;
          v19 = strlen(v13);
          v20 = (char *)calloc(1u, v19 + 1);
          strcpy(v20, v13);
          v21 = strchr(v20, 58);
          if ( !v21 )
            goto LABEL_25;
          *v21 = 0;
          v22 = v21 + 1;
          v23 = atoi(v20);
          v31->dcmCryptoBlks = v23;
          if ( v23 <= 0 )
            goto LABEL_25;
          v24 = strchr(v22, 58);
          if ( v24
            && (*v24 = 0,
                v31->dcmXorBlks = atoi(v22),
                v25 = atoi(v24 + 1),
                v31->dcmTotalRounds = v25,
                (unsigned int)(v25 - 2) < 9) )
          {
            free(v20);
            service_str = v28;
            v9 = v30 + 1;
            if ( v5 <= v10 )
              goto LABEL_37;
          }
          else
          {
LABEL_25:
            free(v20);
            service_str = v28;
            v9 = v30;
            if ( v5 <= v10 )
              goto LABEL_37;
          }
          break;
        case '\x15':
          if ( ost_hexstring_to_binary(dst, 0x10u, (unsigned __int8 *)v13) == 16 )
            ++v9;
          if ( v5 > v10 )
            continue;
          goto LABEL_37;
        default:
          goto LABEL_15;
      }
    }
  }
  v9 = 0;
LABEL_37:
  free(v6);
  v26 = 0xFFFF;
  if ( !v31->cryptoMode )
    v26 = 3;
  if ( v31->cryptoMode == 1 )
    v26 = 4;
  if ( v9 != v26 )
    return -1;
  return v7;
}

然后就是提取出pes,判断是视频就解密,音频不用,然后遍历所有nal类型,目前是全加密了

提取出nal内容后
先nal_unescape
然后进行crc效验,最后两位用于效验

if (crc16(buffer, size - 2) != *(uint16_t *) (buffer + size - 2)) {
    return 0;
}

然后就是dcm解密

按16字节分组,根据前面解析出的进行轮换,目前全是1次ctr然后9次与计数器(iv)异或

int32_t dcm_block(uint8_t *in, int32_t len, uint8_t *iv, uint8_t *key,int32_t) {
    int32_t xorBlksInCycl = 9;
    int32_t numOfTotalRounds = 10;
    uint8_t state[0x10];
    uint8_t xord[0x10];
    uint8_t dec_len = 0;
    struct AVAESCTR *aes_ctr = av_aes_ctr_alloc();
    av_aes_ctr_init(aes_ctr, key);
    av_aes_ctr_set_full_iv(aes_ctr, iv);
    do {
        dec_len = len;
        if (len >= 0x10) {
            dec_len = 0x10;
        }
        if (len <= 0x10 || numOfTotalRounds > xorBlksInCycl) {
            av_aes_ctr_crypt(aes_ctr, in, in, dec_len);
        } else {
            memcpy(xord,aes_ctr->counter,0x10);
            for (int32_t i = 0; i < dec_len; i++) {
                xord[i] ^= in[i];
            }
            addOne(aes_ctr->counter,0x10);
            memcpy(in, xord, dec_len);
        }
        in += dec_len;
        len -= dec_len;
        numOfTotalRounds--;
        if (numOfTotalRounds == 0) {
            numOfTotalRounds = 10;
        }
    } while (len > 0);
    return 1;

}

然后效验解密后的nal

if (crc16(buffer, size - 2) != *(uint16_t *) (buffer + size - 2)) {
    return 0;
}
*pInt = size - 2;
memcpy(buffer1, buffer, *pInt);
int32_t ret2 = dcm_block(buffer1, *pInt, iv, key);
if (ret2 != 1) {
    return 0;
}
if (crc16(buffer1, *pInt-2) != *(uint16_t *) (buffer1 + *pInt - 2)) {
        return 0;
}
return 1;

然后重新组包就行了。

利用ffmpeg,重封装解密

将这套逻辑直接挂载到 FFmpeg 的底层处理流中是最高效的做法。参考 FFmpeg 官方示例 doc/examples/remuxing.c。直接在循环 av_read_frame(ifmt_ctx, pkt) 截获 pkt 即可拿到 PES 包:pkt->data -> 包含底层视频 ES 数据pkt->size -> 数据长度视频 ES 数据位于该 buffer 中,后续对其进行解密。
然后就是获取解密的iv,需要注意的是如果直接传的m3u8链接的话
ifmt_ctx指向的hls,而数据在ts里面,需要获取内部 TS 的 format context:

HLSContext
└── playlists[]
└── ifmt_ctx  (真实 TS)
HLSContext需要自己从ffmpeg的源码里面找,ffmpeg的版本不同,结构体可能也不太一样,自己找一下就行了。

    if (!strcmp(ifmt_ctx->iformat->name, "hls")) {
        HLSContext *avt = ifmt_ctx->priv_data;
        p_metadata(avt->playlists[0]->ifmt_ctx,c)
    }
    static int  p_metadata(AVFormatContext *ctx,DRMInfo *cenc_info) {
        if (ctx->nb_programs > 0) {
        for (int i = 0; i < ctx->nb_programs; i++) {
            AVProgram *p = ctx->programs[i];
            AVDictionaryEntry *tag;
            av_log(NULL, AV_LOG_INFO, "Program %d has %d streams\n", i, p->nb_stream_indexes);
            while ((tag = av_dict_get(p->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
                av_log(NULL, AV_LOG_INFO, " Program metadata: %s=%s\n", tag->key, tag->value);
                if (!strcmp(tag->key, "service_name")) {
                    if (strncmp(tag->value, "mdcm", 4) == 0) {
                        cenc_info->drm_type = DRM_TYPE_IQIYI;
                        char *service_name_copy = av_strdup(tag->value);
                        if (!service_name_copy) {;
                            av_log(NULL, AV_LOG_ERROR, "Failed to allocate memory for service_name_copy\n");
                            continue;
                        }
                        char *saveptr = NULL;
                        char *token = strtok_r(service_name_copy, "|", &saveptr);
                        while (token != NULL) {
                            if (token[0] == 'v' && strlen(token) == 33) {
                                char *key_str = token + 1;
                                if (cenc_info) {
                                    if (ff_hex_to_data(cenc_info->iv, key_str) >= 0) {
                                        memset(cenc_info->iv + 12, 0, 3);
                                        cenc_info->iv[15] = 1;
                                        cenc_info->has_key = 1;
                                        av_log(NULL, AV_LOG_DEBUG, "Extracted IQIYI IV: %s\n", key_str);
                                    }
                                }
                                break;
                            }
                            token = strtok_r(NULL, "|", &saveptr);
                        }
                        av_free(service_name_copy);
                    }
                }

            }
        }
    }

}

后面就很简单了,解密pkt->data就行了,还是上面的流程

┌─────────────────────────────────────────────────────────┐
│ 1. 校验入参 (cenc_info, data, size, iv 等)               │
│ 2. 分配临时缓冲区 temp_buf 存放最终数据                     │
└────────────────────────────┬────────────────────────────┘
                             │
                             ▼ (Loop 循环查找)
┌─────────────────────────────────────────────────────────┐
│ 3. 从 PES 中查找 NAL 单元 (cgts_find_nal_unit)             │
│    起始码: 00 00 01 / 00 00 00 01                       │
│    获取 NAL 起始与结束地址: nalu_start ~ nalu_end          │
└────────────────────────────┬────────────────────────────┘
                             │
          ┌──────────────────┴──────────────────┐
     未找到 NAL 单元                       找到 NAL 单元
          │                                     │
          ▼                                     ▼
┌──────────────────────┐  ┌──────────────────────────────────────────┐
│ 结束循环,将 temp_buf  │  │ 4. 根据编码类型 (H264/HEVC) 获取 NAL头长    │
│ 中的数据覆写回原 data   │  │ 5. 将 NAL Header 明文拷贝至 temp_buf       │
└──────────────────────┘  │    (NAL 头不参与解密)                      │
                          └─────────────────────┬────────────────────┘
                                                │
                                                ▼
                          ┌──────────────────────────────────────────┐
                          │ 6. 提取待解密的 Payload (减去头长度)        │
                          │    若 Payload 长度 <= 0 则直接跳到下个NAL   │
                          └─────────────────────┬────────────────────┘
                                                │
                                                ▼
                          ┌──────────────────────────────────────────┐
                          │ 7. 去除防竞争字节 00 00 03 (nalUnescape)   │
                          │    提取转义后的密文数据                     │
                          └─────────────────────┬────────────────────┘
                                                │
                                                ▼ (monalisa_decrypt_data)
                          ┌──────────────────────────────────────────┐
                          │ 8. 提取密文尾部 2 字节作为 Expected CRC     │
                          │    对剩余密文计算 CRC16 校验值              │
                          └─────────────────────┬────────────────────┘
                                                │
                                                ▼
                                      ┌──────────────────┐
                                      │  CRC16 是否匹配?  │
                                      └────┬────────┬────┘
                                           │        │
                                         否│        │是
                                           ▼        ▼
                                      ┌────────┐ ┌───────────────────────────┐
                                      │解密失败 │ │ 9. 分块解密 (dcm_block)     │
                                      └────────┘ │  - 10 轮为一个循环周期       │
                                                 │  - 交替使用 AES-CTR 与 XOR  │
                                                 └──────────┬────────────────┘
                                                            │
                                                            ▼
                                                 ┌───────────────────────────┐
                                                 │ 10. 解密后二次 CRC 校验     │
                                                 │     (长度 > 2 时触发)       │
                                                 └──────────┬────────────────┘
                                                            │
                                         ┌──────────────────┴──────────────────┐
                                       校验失败                              校验成功
                                         │                                     │
                                         ▼                                     ▼
                                      ┌────────┐                 ┌───────────────────────────┐
                                      │解密失败 │                 │ 11. 将解密后的 Payload 拷   │
                                      └────────┘                 │     贝并追加到 temp_buf     │
                                                                 └─────────────┬─────────────┘
                                                                               │
                                                                               ▼
                                                        (跳回步骤 3,更新 payload_pos 继续处理下一个 NAL)

免费评分

参与人数 9吾爱币 +9 热心值 +9 收起 理由
mtmatye + 1 + 1 我很赞同!
Kanou + 1 + 1 谢谢@Thanks!
落日余辉x + 1 + 1 谢谢@Thanks!,免去一个调so的了
dominjun + 1 + 1 谢谢@Thanks!
5omggx + 1 + 1 用心讨论,共获提升!
Anami + 1 + 1 我很赞同!
Willian + 2 + 1 谢谢@Thanks!
Niklaus + 1 + 1 我很赞同!
AG6 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

xixicoco 发表于 2026-2-13 23:31
大佬牛比,最喜欢大佬的文章了
paoxueyuan 发表于 2026-2-14 09:24
我感觉是小学生看中科院士的论文,字我能认全,组合在一起就不知道啥意思了,太强 了
Anami 发表于 2026-2-15 00:26
camiyada 发表于 2026-2-16 15:31
确实很牛  很厉害
lijian97 发表于 2026-2-18 08:49
完全看不懂
woailike7758 发表于 2026-2-18 13:33
膜拜大佬 我只会用现成的
1234ssz 发表于 2026-3-17 11:06
我用ffmpeg处理踩了坑,就是包缓存的问题,我当时被坑过,加密信息获取不及时,导致帧数据有一两帧花屏,不知道你用的版本有没有这个问题
UpCrawler 发表于 2026-3-17 21:53
1234ssz 发表于 2026-3-17 11:06
我用ffmpeg处理踩了坑,就是包缓存的问题,我当时被坑过,加密信息获取不及时,导致帧数据有一两帧花屏,不 ...

以前方案全是错的,至少多了4字节数据
UpCrawler 发表于 2026-3-17 21:56
请教一下大佬媒体文件处理的代码是怎么找到的,比如ts的解密逻辑
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-18 04:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表