luren-dc / QQMusicApi

Python QQ音乐Api封装库
MIT License
48 stars 15 forks source link

[提问] qrc解码函数报错 #69

Open aurora0x27 opened 1 week ago

aurora0x27 commented 1 week ago

Python 版本: 3.12.6

模块版本: 0.1.8

运行环境: ArchLinux, x86-64

调试utils.qrc_decrypt()函数时收到报错.

测试代码:

import qqmusic_api.utils.utils

with open("./test.qrc", "rb") as f:
    raw = f.read()
    f.close()

dat = qqmusic_api.utils.utils.qrc_decrypt(raw)

with open("./test.qrc.decode", "w+") as f:
    f.write(dat)
    f.close()

测试文件(经过压缩打包):

test.zip

报错信息:

Traceback (most recent call last):
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/utils.py", line 98, in qrc_decrypt
    data += tripledes_crypt(encrypted_qrc[i:], schedule)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/tripledes.py", line 599, in tripledes_crypt
    data = crypt(data, key[i])
           ^^^^^^^^^^^^^^^^^^^
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/tripledes.py", line 418, in crypt
    s0, s1 = initial_permutation(input_data)  # 初始置换
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/tripledes.py", line 134, in initial_permutation
    bitnum(input_data, 57, 31)
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/tripledes.py", line 79, in bitnum
    return ((a[(b // 32) * 4 + 3 - (b % 32) // 8] >> (7 - b % 8)) & 1) << c
             ~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IndexError: bytearray index out of range

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/main.py", line 7, in <module>
    dat = qqmusic_api.utils.utils.qrc_decrypt(raw)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/aurora/Desktop/projects/debug/qrc-decoder/python/decoder/lib/python3.12/site-packages/qqmusic_api/utils/utils.py", line 104, in qrc_decrypt
    raise ValueError(f"解密失败: {e}")
ValueError: 解密失败: bytearray index out of range
luren-dc commented 4 days ago

此处代码来源 https://github.com/chenmozhijin/LDDC

aurora0x27 commented 4 days ago

https://github.com/chenmozhijin/LDDC 仓库中backend/decrypt/__init__.py的代码提及qrc文件的"local"和"cloud"需要经过不同的处理

 if qrc_type == QrcType.LOCAL:
            qmc1_decrypt(encrypted_text_byte)
            encrypted_text_byte = encrypted_text_byte[11:]

        data = bytearray()
        schedule = tripledes_key_setup(QRC_KEY, DECRYPT)

以8字节为单位迭代的范围也不相同

LDDC代码:

# 以 8 字节为单位迭代 encrypted_text_byte
        for i in range(0, len(encrypted_text_byte), 8):
            data += tripledes_crypt(encrypted_text_byte[i:], schedule)

此仓库代码:

        # 分块解密数据
        # 以 8 字节为单位迭代 encrypted_qrc
        for i in range(0, len(encrypted_qrc), 8):
            data += tripledes_crypt(encrypted_qrc[i : i + 8], schedule)

我比较疑惑是不是和此处相关

luren-dc commented 4 days ago

事实上对于这个API来说,对获取的qrc进行解密,有没有i+8解密都能成功

async def get_lyric(
    *,
    mid: Optional[str] = None,
    id: Optional[int] = None,
    qrc: bool = False,
    trans: bool = False,
    roma: bool = False,
) -> dict[str, str]:
    """获取歌词

    Note:
        歌曲 mid 和 id,两者至少提供一个

    Args:
        mid:   歌曲 mid
        id:    歌曲 id
        qrc:   是否返回逐字歌词
        trans: 是否返回翻译歌词
        roma:  是否返回罗马歌词

    Returns:
        {"lyric": 歌词或逐字歌词, "trans": 翻译歌词, "roma": 罗马歌词}
    """
    if mid is None and id is None:
        raise ValueError("mid or id must be provided")

    params = {
        "crypt": 1,
        "ct": 11,
        "cv": 13020508,
        "lrc_t": 0,
        "qrc": qrc,
        "qrc_t": 0,
        "roma": roma,
        "roma_t": 0,
        "songId": id,
        "songMid": mid,
        "trans": trans,
        "trans_t": 0,
        "type": 1,
    }
    res = await Api(**API["info"]).update_params(**params).result

    lyric = qrc_decrypt(res["lyric"])

    if lyric and qrc:
        try:
            root = ET.fromstring(lyric)
            lyric_info = root[1]
            lyric = lyric_info[0].attrib.get("LyricContent", lyric)
        except Exception:
            pass

    return {
        "lyric": lyric,
        "trans": qrc_decrypt(res["trans"]),
        "roma": qrc_decrypt(res["roma"]),
    }
luren-dc commented 4 days ago

c++ 实现的 qrc 解密 https://github.com/MeoProject/lx-music-api-server/tree/main/deps/pyqdes