Files

75 lines
3.4 KiB
Python
Raw Permalink Normal View History

2024-05-04 15:58:59 +08:00
import asyncio
import logging
2024-05-04 15:58:59 +08:00
from loguru import logger
2024-05-22 13:46:31 +08:00
from tenacity import retry, retry_if_exception_type, stop_after_attempt, before_sleep_log
2024-05-04 15:58:59 +08:00
2024-05-22 13:46:31 +08:00
from src.adb import Device, HyperDecryptDevice
2024-05-21 22:33:41 +08:00
from src.exceptions import DecryptException, RetryableDecryptException
2024-05-04 15:58:59 +08:00
from src.models.song_data import Datum
from src.mp4 import SongInfo, SampleInfo
from src.types import defaultId, prefetchKey
2024-05-22 13:46:31 +08:00
from src.utils import timeit
2024-05-04 15:58:59 +08:00
2024-05-21 22:33:41 +08:00
retry_count = {}
2024-05-04 15:58:59 +08:00
2024-05-21 22:33:41 +08:00
@retry(retry=retry_if_exception_type(RetryableDecryptException), stop=stop_after_attempt(3),
before_sleep=before_sleep_log(logger, logging.WARN))
2024-05-22 13:46:31 +08:00
@timeit
async def decrypt(info: SongInfo, keys: list[str], manifest: Datum, device: Device | HyperDecryptDevice) -> bytes:
2024-05-04 15:58:59 +08:00
async with device.decryptLock:
2024-05-22 13:46:31 +08:00
if isinstance(device, HyperDecryptDevice):
logger.info(f"Using hyperDecryptDevice {device.serial} to decrypt song: {manifest.attributes.artistName} - {manifest.attributes.name}")
else:
logger.info(f"Using device {device.serial} to decrypt song: {manifest.attributes.artistName} - {manifest.attributes.name}")
try:
2024-05-28 18:17:06 +08:00
reader, writer = await asyncio.open_connection(device.host, device.fridaPort, limit=2**14)
except ConnectionRefusedError:
2024-05-22 13:46:31 +08:00
logger.warning(f"Failed to connect to device {device.serial}, re-injecting")
device.restart_inject_frida()
raise RetryableDecryptException
2024-05-28 18:17:06 +08:00
decrypted = []
2024-05-04 15:58:59 +08:00
last_index = 255
for sample in info.samples:
if last_index != sample.descIndex:
if len(decrypted) != 0:
writer.write(bytes([0, 0, 0, 0]))
key_uri = keys[sample.descIndex]
track_id = manifest.id
if key_uri == prefetchKey:
track_id = defaultId
writer.write(bytes([len(track_id)]))
writer.write(track_id.encode("utf-8"))
writer.write(bytes([len(key_uri)]))
writer.write(key_uri.encode("utf-8"))
last_index = sample.descIndex
try:
result = await decrypt_sample(writer, reader, sample)
2024-05-21 22:33:41 +08:00
except RetryableDecryptException as e:
2024-05-22 13:46:31 +08:00
if 0 <= retry_count.get(device.serial, 0) < 3 or 4 <= retry_count.get(device.serial, 0) < 6:
2024-05-21 22:33:41 +08:00
logger.warning(f"Failed to decrypt song: {manifest.attributes.artistName} - {manifest.attributes.name}, retrying")
writer.write(bytes([0, 0, 0, 0]))
writer.close()
raise e
elif retry_count == 3:
logger.warning(f"Failed to decrypt song: {manifest.attributes.artistName} - {manifest.attributes.name}, re-injecting")
device.restart_inject_frida()
raise e
else:
logger.error(f"Failed to decrypt song: {manifest.attributes.artistName} - {manifest.attributes.name}")
raise DecryptException
2024-05-28 18:17:06 +08:00
decrypted.append(result)
2024-05-04 15:58:59 +08:00
writer.write(bytes([0, 0, 0, 0]))
writer.close()
2024-05-28 18:17:06 +08:00
return bytes().join(decrypted)
2024-05-04 15:58:59 +08:00
async def decrypt_sample(writer: asyncio.StreamWriter, reader: asyncio.StreamReader, sample: SampleInfo) -> bytes:
writer.write(len(sample.data).to_bytes(4, byteorder="little", signed=False))
writer.write(sample.data)
result = await reader.read(len(sample.data))
if not result:
2024-05-21 22:33:41 +08:00
raise RetryableDecryptException
2024-05-04 15:58:59 +08:00
return result