1596752.png

極彩花夢

GF  2024-04-20 15:49
(字幕とか儲からないし、翻訳とか適当でもいいよ)

关于如何解密Sokmil上的AV与里番

因为过程大差不差所以就不写太正式了,就当随手一写。
不公开完整代码,不公开最后程序,叙述结构混乱。

首先因为Sokmil解密有若干个网络请求,所以先直接写个通用header(如果某个请求需要不一样的header可以再另改)
登录一个Sokmil账号之后复制主页的请求,到curlconverter.com直接复制headers,稍微修改一下即可。
headers = {
    "Accept": "*/*",
    "Accept-Language": "ja",
    "Connection": "keep-alive",
    "Origin": "https://www.sokmil.com",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
    "content-type": "application/x-www-form-urlencoded",
    "sec-ch-ua": '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
}
有很多用不到的但是不重要,重要的其实主要是UA。

为了捋清解密的所有必要请求,可以直接倒着推。
最后的请求肯定就是widevine的license,https://license.candl.jp/sokmil/widevine/这个链接的请求。
这个请求的链接本身是固定的,data是CDM信息,那只有可能请求解密的关键讯息在headers里。
headers里也只有authorization中是一大串明显有讯息的内容,是Bearer eyXXXXX很长一串,明显后面ey开头的是编码后的讯息。
而这个信息在请求https://www.sokmil.com/purchase_auth/时出现,并且就是这个请求的内容,也就是先进行的这个(大概率为账号)认证再最后widevine解密。

而这个auth的headers中没什么疑似讯息的东西,重要的是data和cookies。
data里面是keyID,看名字就盲猜是widevine的KID,内容还是32长度的。
cookies肯定就是带账号信息的了,有非常多个字段,可以直接另写脚本模拟请求来排除不重要的字段,最后可以知道其中只有SCS是必要的。

SCS大概率是登录时返回的Set-Cookie,所以直接先试试模拟登录。
登录请求很简单就不说了,python里面设置请求时不允许重定向,然后获取响应的headers中的Set-Cookie就行。
——不过其实会获取到两个Set-Cookie,总之使用第二个就行。

然后是auth里的KID,大概率能从mpd文件中找到(说大概率是因为有些mpd文件可以不含KID),于是能找到唯一一个mpd请求。
里面能很容易找到default_KID="XXXX",去掉横线再转小写就是前面auth的keyID了。
另外pssh就可以直接自己生成,Sokmil的pssh有KID即可,在最后解密请求时用到。

那么mpd的链接是如何来的呢,再往前找可以看到有个https://www.sokmil.com/streaminginfo/?pid=XXXX&rid=XXXX的请求。
这个请求的内容是json,可以看到video_sources里面第一个就是mpd链接(但其实这里面也有m3u8链接和第三个忘了啥加密的链接)。

再接着,这个streaminginfo的pid和rid是怎么来的呢,pid倒是跟详情页的数字一样。
直接对视频详情页查看源码搜索rid就能搜到,所以streaminginfo的链接可以直接get详情页获取。

登录:
data = {
    "id": mail,
    "pw": password,
    "autologin": "on",
    "act": "",
    "repeat": "true",
    "encpw": "",
}
logger.info("Logging in...")
response = requests.post(
    "https://www.sokmil.com/member/login/",
    headers=headers,
    data=data,
    allow_redirects=False,
)
headers = response.headers
cookies = headers.get("Set-Cookie")
scs = re.findall(r"SCS=([^;]+);", cookies)[1]
logger.info("Login successful.")
logger.info("Retrieved SCS.")

然后写个通用Cookies:
cookies = {
    "AGEAUTH": "ok",
    "SCS": scs,
}

获取PID和RID:
url = input("Url of the title: ")
logger.info("Fetching PID & RID...")
response = requests.get(url, headers=headers, cookies=cookies)
data = response.text
match = re.search(
    r"https:\/\/www\.sokmil\.com\/streaminginfo\/\?pid=(\d+)&rid=(\d+)", data
)
pid = match.group(1)
rid = match.group(2)
logger.info("Retrieved PID & RID.")

获取mpd链接:
logger.info("Fetching mpd url...")
params = {
    "pid": pid,
    "rid": rid,
}
response = requests.get(
    "https://www.sokmil.com/streaminginfo/",
    params=params,
    cookies=cookies,
    headers=headers,
)
data = response.text
streaminginfo = json.loads(data)
try:
    mpd = streaminginfo["data"]["video_sources"][0]["src"]
except:
    response = requests.get(
        "https://www.sokmil.com/streaminginfo/",
        params=params,
        cookies=cookies,
        headers=headers,
    )
    data = response.text
    streaminginfo = json.loads(data)
    mpd = streaminginfo["data"]["video_sources"][0]["src"]
logger.info("Retrieved mpd url.")
关于为什么要写个try:因为有时候好像请求的结果有误,验证起来比较麻烦所以干脆错误之后再请求一次,第二次总之没错误过。

获取KID:
logger.info("Fetching KID...")
response = requests.get(mpd, headers=headers, cookies=cookies)
data = response.text
kid = re.search(r"default_KID=\"([^\"]+)\"", data).group(1).replace("-", "")
logger.info("Retrieved KID.")

获取token:
logger.info("Fetching token...")
data = {
    "keyId": kid.lower(),
}
response = requests.post(
    "https://www.sokmil.com/purchase_auth/", cookies=cookies, headers=headers, data=data
)
token = response.text
logger.info("Retrieved token.")

生成PSSH:
logger.info("Generating PSSH...")
kid = [base64.b16decode(kid.upper())]
boxes = []
pssh_data = pssh_box._generate_widevine_data(kid, None, None, None)
boxes.append(
    pssh_box.Pssh(
        0, base64.b16decode("EDEF8BA979D64ACEA3C827DCD51D21ED"), kid, pssh_data
    )
)
box_data = b"".join([x.binary_string() for x in boxes])
pssh = base64.b64encode(box_data).decode()
logger.info("Got PSSH.")

获取key:
logger.info("Obtaining keys...")
lic_url = "https://license.candl.jp/sokmil/widevine/"
def WV_Function(pssh, lic_url, cert_b64=None):
    wvdecrypt = WvDecrypt(
        init_data_b64=pssh,
        cert_data_b64=cert_b64,
        device={
            "name": "android_generic",
            "description": "android studio cdm",
            "security_level": 3,
            "session_id_type": "android",
            "private_key_available": True,
            "vmp": False,
            "send_key_control_nonce": True,
            "device_client_id_blob_filename": os.path.join(
                current_folder, "CDM", "device_client_id_blob"
            ),
            "device_private_key_filename": os.path.join(
                current_folder, "CDM", "device_private_key"
            ),
        },
    )
    widevine_license = requests.post(
        url=lic_url,
        data=wvdecrypt.get_challenge(),
        headers={
            "authorization": f"Bearer {token}",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
        },
    )
    license_b64 = json.loads(widevine_license.text)["license"]
    wvdecrypt.update_license(license_b64)
    Correct, keyswvdecrypt = wvdecrypt.start_process()
    if Correct:
        return Correct, keyswvdecrypt
_, _keys = WV_Function(pssh, lic_url)
if len(_keys) == 0:
    logger.error("Error in requesting key! Press Enter to exit.")
    input()
    raise
keys = ""
for key in _keys:
    keys = f"{keys}--key {key} "
logger.info("Got keys.")
这里最后模拟请求除了token之外还要设置一下UA。

获取到key就可以下载合并解密混流了。

761587.jpg

233311

B1F  2024-04-20 15:58
(加度盘表明来意,要不然不管)
虽然看不懂

xvid


梅单推人


梨花


子建


1650088.jpg

BigBigWolf

这网站居然不能用番号查询...

b375a372825c98eb6d99f.jpg

一天一个update

需要提前在sokmil购买资源吗

1596752.png

極彩花夢

B8F  2024-04-26 22:33
(字幕とか儲からないし、翻訳とか適当でもいいよ)

回 7楼(一天一个update) 的帖子

当然是的喵。

a10.gif

e3d533d7

GOOD