209 lines
9.3 KiB
Python
209 lines
9.3 KiB
Python
import json
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from contextlib import redirect_stdout
|
|
from io import StringIO
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from tools import extract_app_login_fields
|
|
from tools.extract_app_login_fields import extract_fields
|
|
|
|
|
|
class ExtractAppLoginFieldsTests(unittest.TestCase):
|
|
def test_cli_preserves_cached_weapon_kaw_when_new_log_has_none(self):
|
|
"""只抓到登录字段的新日志不能清空版本级 KAW 缓存。"""
|
|
|
|
event = {
|
|
"tag": "REQUEST_BUILD",
|
|
"method": "POST",
|
|
"url": (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/login/mobileVerifyCode?"
|
|
"did=ANDROID_APP_DID&sig=OLD"
|
|
),
|
|
"body": "code=123456&mobile=LOGIN_ENC&passport_account_image=VIMG_LOGIN",
|
|
}
|
|
with tempfile.TemporaryDirectory() as td:
|
|
root = Path(td)
|
|
log = root / "probe_login.log"
|
|
out = root / "app_login_fields_latest.json"
|
|
log.write_text("@@LOGIN " + json.dumps(event), encoding="utf-8")
|
|
out.write_text(
|
|
json.dumps({"weapon_kaw": "CACHED_VERSION_KAW", "source_log": "older.log"}),
|
|
encoding="utf-8",
|
|
)
|
|
argv = [
|
|
"extract_app_login_fields.py",
|
|
"--log",
|
|
str(log),
|
|
"--out",
|
|
str(out),
|
|
]
|
|
with patch.object(sys, "argv", argv), redirect_stdout(StringIO()):
|
|
self.assertEqual(extract_app_login_fields.main(), 0)
|
|
|
|
fields = json.loads(out.read_text(encoding="utf-8"))
|
|
|
|
self.assertEqual(fields["weapon_kaw"], "CACHED_VERSION_KAW")
|
|
|
|
def test_extract_fields_keeps_mobile_checker_fields(self):
|
|
"""登录日志里有 mobile/checker 时,提取 checker 专用密文和票据。"""
|
|
|
|
checker_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/mobile/checker?"
|
|
"did=ANDROID_APP_DID&sig=OLD&__NS_sig3=OLD3&__NS_xfalcon=OLDX"
|
|
)
|
|
request_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/requestMobileCode?"
|
|
"did=ANDROID_APP_DID&sig=OLD&__NS_sig3=OLD3&__NS_xfalcon=OLDX"
|
|
)
|
|
login_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/login/mobileVerifyCode?"
|
|
"did=ANDROID_APP_DID&sig=OLD&__NS_sig3=OLD3&__NS_xfalcon=OLDX"
|
|
)
|
|
events = [
|
|
{
|
|
"tag": "LOGINHELPER_B_PHONE",
|
|
"in_value": "13800000000",
|
|
"out_value": "CHECKER+ENC==",
|
|
},
|
|
{
|
|
"tag": "REQUEST_BUILD",
|
|
"method": "POST",
|
|
"url": checker_url,
|
|
"body": (
|
|
"mobileCountryCode=+86&mobile=CHECKER+ENC==&cs=false&client_key=2ac2a76d"
|
|
"&videoModelCrowdTag=&os=android&uQaTag=&passport_account_image=VIMG_CHECKER$AI_1111"
|
|
),
|
|
},
|
|
{
|
|
"tag": "REQUEST_BUILD",
|
|
"method": "POST",
|
|
"url": request_url,
|
|
"body": (
|
|
"mobileCountryCode=+86&mobile=REQUEST+ENC==&type=27&useVoice=false"
|
|
"&needCheck=false&prefetchPhoneNumber=&requestSource=1"
|
|
"&passport_account_image=VIMG_REQUEST$AI_2222"
|
|
),
|
|
},
|
|
{
|
|
"tag": "REQUEST_BUILD",
|
|
"method": "POST",
|
|
"url": login_url,
|
|
"body": (
|
|
"isDegraded=false&code=123456&mobileCountryCode=+86&deviceMode=OnePlus(PJZ110)"
|
|
"&mobile=LOGIN+ENC==&prefetchPhoneNumber=&raw=1785044999746"
|
|
"&publicKey=PUBLIC&secret=SECRET&type=27&deviceName=OnePlus(PJZ110)"
|
|
"&passport_account_image=VIMG_LOGIN$AI_3333"
|
|
),
|
|
},
|
|
]
|
|
with tempfile.TemporaryDirectory() as td:
|
|
log = Path(td) / "probe.log"
|
|
log.write_text(
|
|
"\n".join("@@LOGIN " + json.dumps(event, ensure_ascii=False) for event in events),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
fields = extract_fields(log)
|
|
|
|
self.assertEqual(fields["checker_host"], "az2-api.ksapisrv.com")
|
|
self.assertEqual(fields["checker_path"], "/rest/nebula/user/mobile/checker")
|
|
self.assertEqual(fields["query_params"], {"did": "ANDROID_APP_DID"})
|
|
self.assertEqual(fields["source_mobile"], "13800000000")
|
|
self.assertEqual(fields["checker_encrypted_mobile"], "CHECKER+ENC==")
|
|
self.assertEqual(fields["request_encrypted_mobile"], "REQUEST+ENC==")
|
|
self.assertEqual(fields["encrypted_mobile"], "LOGIN+ENC==")
|
|
self.assertEqual(fields["checker_passport_account_image"], "VIMG_CHECKER$AI_1111")
|
|
self.assertEqual(fields["request_passport_account_image"], "VIMG_REQUEST$AI_2222")
|
|
self.assertEqual(fields["passport_account_image"], "VIMG_LOGIN$AI_3333")
|
|
self.assertEqual(
|
|
fields["checker_body_keys"],
|
|
[
|
|
"mobileCountryCode",
|
|
"mobile",
|
|
"cs",
|
|
"client_key",
|
|
"videoModelCrowdTag",
|
|
"os",
|
|
"uQaTag",
|
|
"passport_account_image",
|
|
],
|
|
)
|
|
|
|
|
|
|
|
def test_extract_fields_from_all_layers_begin_chunk_log(self):
|
|
"""probe_nebula_all_layers 的 BEGIN/CHUNK 日志也能提取三段字段。"""
|
|
|
|
checker_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/mobile/checker?"
|
|
"did=ANDROID_APP_DID&sig=OLD&__NS_sig3=OLD3"
|
|
)
|
|
request_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/requestMobileCode?"
|
|
"did=ANDROID_APP_DID&sig=OLD&__NS_xfalcon=OLDX"
|
|
)
|
|
login_url = (
|
|
"https://az2-api.ksapisrv.com/rest/nebula/user/login/mobileVerifyCode?"
|
|
"did=ANDROID_APP_DID&sig=OLD"
|
|
)
|
|
|
|
def begin(seq, tag, payload):
|
|
return f"@@BEGIN\t{seq}\t{tag}\t" + json.dumps(payload, ensure_ascii=False)
|
|
|
|
def chunk(seq, name, value):
|
|
return f"@@CHUNK\t{seq}\t{name}\t0\t" + json.dumps(value, ensure_ascii=False)
|
|
|
|
lines = [
|
|
begin(10, "OKHTTP_CHAIN_BEFORE_REQ", {"method": "POST", "url": checker_url, "tid": 7}),
|
|
begin(11, "OKHTTP_CHAIN_BEFORE_REQ_BODY", {"tid": 7}),
|
|
chunk(11, "utf8", "mobileCountryCode=%2B86&mobile=CHECKER%2BENC%3D%3D&passport_account_image=VIMG_CHECKER%24AI_1111"),
|
|
begin(20, "OKHTTP_CHAIN_BEFORE_REQ", {"method": "POST", "url": request_url, "tid": 7}),
|
|
begin(21, "OKHTTP_CHAIN_BEFORE_REQ_BODY", {"tid": 7}),
|
|
chunk(21, "utf8", "mobileCountryCode=%2B86&mobile=REQUEST%2BENC%3D%3D&type=27&prefetchPhoneNumber=&passport_account_image=VIMG_REQUEST%24AI_2222"),
|
|
begin(30, "OKHTTP_CHAIN_BEFORE_REQ", {"method": "POST", "url": login_url, "tid": 7}),
|
|
begin(31, "OKHTTP_CHAIN_BEFORE_REQ_BODY", {"tid": 7}),
|
|
chunk(31, "utf8", "code=123456&mobileCountryCode=%2B86&mobile=LOGIN%2BENC%3D%3D&prefetchPhoneNumber=&deviceName=OnePlus(PJZ110)&passport_account_image=VIMG_LOGIN%24AI_3333"),
|
|
begin(32, "CRONET_ADD_HEADER", {"name": "kaw", "tid": 7}),
|
|
chunk(32, "value", "KAW_VERSION_CONFIG"),
|
|
]
|
|
with tempfile.TemporaryDirectory() as td:
|
|
log = Path(td) / "probe_login_stable.log"
|
|
log.write_text("\n".join(lines), encoding="utf-8")
|
|
|
|
fields = extract_fields(log)
|
|
|
|
self.assertEqual(fields["checker_host"], "az2-api.ksapisrv.com")
|
|
self.assertEqual(fields["checker_path"], "/rest/nebula/user/mobile/checker")
|
|
self.assertEqual(fields["query_params"], {"did": "ANDROID_APP_DID"})
|
|
self.assertEqual(fields["checker_encrypted_mobile"], "CHECKER+ENC==")
|
|
self.assertEqual(fields["request_encrypted_mobile"], "REQUEST+ENC==")
|
|
self.assertEqual(fields["encrypted_mobile"], "LOGIN+ENC==")
|
|
self.assertEqual(fields["checker_passport_account_image"], "VIMG_CHECKER$AI_1111")
|
|
self.assertEqual(fields["request_passport_account_image"], "VIMG_REQUEST$AI_2222")
|
|
self.assertEqual(fields["passport_account_image"], "VIMG_LOGIN$AI_3333")
|
|
self.assertEqual(fields["weapon_kaw"], "KAW_VERSION_CONFIG")
|
|
|
|
def test_extract_fields_reads_mixed_utf8_utf16le_all_layers_log(self):
|
|
"""Start-Job/Tee 混合编码时仍能识别 BEGIN/CHUNK。"""
|
|
|
|
url = "https://az2-api.ksapisrv.com/rest/nebula/user/login/mobileVerifyCode?did=ANDROID_APP_DID&sig=OLD"
|
|
line1 = "@@BEGIN\t1\tOKHTTP_CHAIN_BEFORE_REQ\t" + json.dumps({"method": "POST", "url": url, "tid": 9})
|
|
line2 = "@@BEGIN\t2\tOKHTTP_CHAIN_BEFORE_REQ_BODY\t" + json.dumps({"tid": 9})
|
|
line3 = "@@CHUNK\t2\tutf8\t0\t" + json.dumps("mobileCountryCode=%2B86&mobile=LOGIN%2BENC%3D%3D&passport_account_image=VIMG_LOGIN%24AI_3333")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
log = Path(td) / "probe_mixed.log"
|
|
body = "\r\n".join([line1, line2, line3]) + "\r\n"
|
|
log.write_bytes(b"\xef\xbb\xbfJOB_BEGIN pid=1\r\n" + body.encode("utf-16-le"))
|
|
|
|
fields = extract_fields(log)
|
|
|
|
self.assertEqual(fields["encrypted_mobile"], "LOGIN+ENC==")
|
|
self.assertEqual(fields["passport_account_image"], "VIMG_LOGIN$AI_3333")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|