1.qingyou
内置了vm指令集,难度太高,AI分析了分析一下,放弃了
2.台服
没有成功
思路,跳窗口,但是hook登录抽离了游戏逻辑,不走登录 游戏崩溃
两个so
1.一个运行时解密
没分析
2.一个原SO
动态
这家伙全是花指令
用Frida hook了so
dex解密了
请求逆向
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
微验 API 测试工具 - Python版
"""
import hashlib
import requests
import time
import random
import json
# ============================================================
# RC4 加解密
# ============================================================
def rc4_crypt(key: str, data: bytes) -> bytes:
"""RC4 加解密 (加解密相同)"""
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) & 0xFF
s[i], s[j] = s[j], s[i]
i = j = 0
result = bytearray()
for byte in data:
i = (i + 1) & 0xFF
j = (j + s[i]) & 0xFF
s[i], s[j] = s[j], s[i]
result.append(byte ^ s[(s[i] + s[j]) & 0xFF])
return bytes(result)
class WeiYan:
"""微验 API 客户端"""
def __init__(self, host: str, appid: str, appkey: str, rc4key: str,
kami: str = "", imei: str = ""):
self.host = host
self.appid = appid
self.appkey = appkey
self.rc4key = rc4key
self.kami = kami
self.imei = imei
def _encrypt(self, data: str) -> str:
"""RC4 加密 data,返回 hex 字符串"""
raw = rc4_crypt(self.rc4key, data.encode())
return raw.hex()
def _decrypt(self, data: bytes) -> str:
"""RC4 解密响应"""
return rc4_crypt(self.rc4key, data).decode(errors='replace')
def _post(self, path: str, body: str) -> bytes:
"""发送 POST 请求,返回解码后的原始字节"""
url = f"http://{self.host}/{path}"
resp = requests.post(url, data=body.encode(),
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=10)
text = resp.text.strip()
# 服务器返回的是 hex 编码文本,先 hex 解码
try:
raw = bytes.fromhex(text)
return raw
except ValueError:
# 不是 hex,可能是直接二进制或错误消息
return resp.content
# ========== 公开接口 ==========
def login(self, endpoint: str = "kmlogon",
kami: str = None, imei: str = None) -> dict:
"""
登录请求
endpoint: kmlogon(实际抓到的) / kmlogin(源码模板)
返回: {raw_hex, raw_len, decrypted, json}
"""
if kami is None: kami = self.kami
if imei is None: imei = self.imei
ts = int(time.time())
rand_val = f"{ts}{random.randint(1000, 9999)}"
# 签名: MD5("kami=...&markcode=...&t=...&appkey")
sign_raw = f"kami={kami}&markcode={imei}&t={ts}&{self.appkey}"
sign_md5 = hashlib.md5(sign_raw.encode()).hexdigest()
# 数据拼接
data_raw = f"kami={kami}&markcode={imei}&t={ts}&sign={sign_md5}&value={rand_val}"
data_enc = self._encrypt(data_raw)
body = f"data={data_enc}"
path = f"api/?id={endpoint}&app={self.appid}"
print(f"\n{'='*60}")
print(f"[登录] {self.host}/{path}")
print(f"[签名原文] {sign_raw}")
print(f"[MD5签名] {sign_md5}")
print(f"[加密前] {data_raw}")
print(f"[加密后] {data_enc[:40]}...")
print(f"[请求体] data={data_enc[:40]}...")
path = f"api/?id={endpoint}&app={self.appid}"
url = f"http://{self.host}/{path}"
resp = requests.post(url, data=body.encode(),
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=10)
text = resp.text.strip()
print(f"[HTTP状态] {resp.status_code}")
print(f"[响应原文] {text[:100]}")
# hex 解码 → RC4 解密
raw_bytes = bytes.fromhex(text) if all(c in '0123456789abcdefABCDEF' for c in text) else text.encode()
print(f"[Hex解码后] {raw_bytes.hex()[:60]}...")
print(f"[Hex后长度] {len(raw_bytes)} bytes")
decrypted = self._decrypt(raw_bytes)
try:
j = json.loads(decrypted)
print(f"[解密JSON] {json.dumps(j, indent=2, ensure_ascii=False)}")
except:
print(f"[解密文本] {decrypted[:500]}")
j = None
return {'de_json': j, 'decrypted': decrypted,
'raw_hex': raw_bytes.hex(), 'raw_len': len(raw_bytes)}
def notice(self) -> dict:
"""获取公告 - 请求明文,响应加密"""
body = f"app={self.appid}" # 源码中 _ggUrl 是明文!
url = f"http://{self.host}/api/?id=notice"
resp = requests.post(url, data=body.encode(),
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=10)
text = resp.text.strip()
print(f"\n[公告] 响应原文: {text[:100]}")
# hex 解码 → RC4 解密
if all(c in '0123456789abcdefABCDEF' for c in text):
raw_bytes = bytes.fromhex(text)
decrypted = self._decrypt(raw_bytes)
print(f"[公告] Hex解码: {len(raw_bytes)} bytes")
else:
decrypted = self._decrypt(text.encode())
print(f"[公告] 解密: {decrypted[:500]}")
try:
j = json.loads(decrypted)
print(json.dumps(j, indent=2, ensure_ascii=False))
return j
except:
return {'raw': decrypted}
def custom(self, path: str, body: str) -> bytes:
"""自定义请求"""
url = f"http://{self.host}/{path}"
resp = requests.post(url, data=body.encode(),
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=10)
text = resp.text.strip()
raw_bytes = bytes.fromhex(text) if all(c in '0123456789abcdefABCDEF' for c in text) else text.encode()
print(f"[自定义] {path}")
print(f"[响应] {len(raw_bytes)} bytes: {raw_bytes[:200]}")
return raw_bytes
def analyze_response(response):
"""分析微验响应结构"""
print("\n" + "="*60)
print("响应分析:")
print("="*60)
if 'de_json' in response and response['de_json']:
j = response['de_json']
print(f"状态码: {j.get('code', 'N/A')}")
print(f"时间戳: {j.get('time', 'N/A')}")
print(f"Check值: {j.get('check', 'N/A')}")
if 'msg' in j:
msg = j['msg']
print("\n消息内容:")
print(f" ID: {msg.get('id', 'N/A')}")
print(f" Token: {msg.get('token', 'N/A')}")
print(f" 卡密类型: {msg.get('kmtype', 'N/A')}")
print(f" Key类型: {msg.get('ktype', 'N/A')}")
print(f" VIP: {msg.get('vip', 'N/A')}")
print(f" 备注: {msg.get('note', 'N/A')}")
print(f"\n响应原始长度: {response.get('raw_len', 'N/A')} bytes")
# 检查 check 字段的格式
check = j.get('check', '') if j else ''
if check:
print(f"\nCheck 字段分析:")
print(f" 长度: {len(check)} 字符")
print(f" 类型: {'MD5' if len(check) == 32 else 'SHA1' if len(check) == 40 else '未知'}")
# 尝试分析 check 的生成方式
if len(check) == 32:
# 可能是 MD5
import hashlib
msg = j.get('msg', {})
test_strings = [
f"{APPID}{APPKEY}",
f"{APPKEY}{APPID}",
f"{j.get('time', '')}{APPKEY}",
f"{j.get('time', '')}{APPID}",
f"{APPID}{j.get('time', '')}{APPKEY}",
f"{KAMI}{IMEI}{j.get('time', '')}",
# 包含响应中的字段
f"{APPKEY}{j.get('time', '')}",
f"{j.get('time', '')}{APPKEY}{APPID}",
f"{APPID}{KAMI}{IMEI}",
f"{KAMI}{APPKEY}",
f"{IMEI}{APPKEY}",
# 包含 msg 字段
f"{msg.get('id', '')}{APPKEY}",
f"{msg.get('token', '')}{APPKEY}",
f"{msg.get('id', '')}{msg.get('token', '')}",
f"{j.get('time', '')}{msg.get('id', '')}{msg.get('token', '')}",
f"{APPKEY}{j.get('time', '')}{msg.get('id', '')}",
f"{APPID}{j.get('time', '')}{msg.get('id', '')}{APPKEY}",
# 带分隔符
f"{APPID}&{APPKEY}&{j.get('time', '')}",
f"{j.get('time', '')}|{APPKEY}|{APPID}",
# 小写
f"{APPID}{APPKEY}".lower(),
f"{APPKEY}{j.get('time', '')}".lower(),
]
print("\n 尝试匹配 check 值:")
for s in test_strings:
md5 = hashlib.md5(s.encode()).hexdigest()
if md5 == check:
print(f" ✓ 匹配: {s}")
else:
print(f" ✗ {s}")
# 尝试组合多个字段
print("\n 尝试更复杂的组合:")
complex_tests = [
# 响应字段组合
f"{msg.get('id', '')}{msg.get('token', '')}{msg.get('kmtype', '')}{msg.get('ktype', '')}",
f"{j.get('code', '')}{j.get('time', '')}{msg.get('id', '')}{msg.get('token', '')}",
f"{KAMI}{IMEI}{msg.get('id', '')}{j.get('time', '')}",
# 包含所有信息
f"{APPID}{KAMI}{IMEI}{j.get('time', '')}{APPKEY}",
f"{KAMI}{IMEI}{APPKEY}{j.get('time', '')}{msg.get('id', '')}",
# 包含 code 的组合
f"{j.get('code', '')}{APPKEY}",
f"{APPKEY}{j.get('code', '')}",
f"{j.get('code', '')}{j.get('time', '')}{APPKEY}",
f"{APPKEY}{j.get('code', '')}{j.get('time', '')}",
f"{APPID}{j.get('code', '')}{APPKEY}",
f"{j.get('code', '')}{APPID}{APPKEY}",
f"{msg.get('id', '')}{j.get('code', '')}{APPKEY}",
f"{j.get('code', '')}{msg.get('id', '')}{msg.get('token', '')}{APPKEY}",
# 完整响应 JSON 序列化后 MD5
]
for s in complex_tests:
md5 = hashlib.md5(s.encode()).hexdigest()
if md5 == check:
print(f" ✓ 匹配: {s}")
else:
print(f" ✗ {s}")
# 尝试完整JSON的MD5
print("\n 尝试完整响应JSON的MD5:")
full_json = json.dumps(j, separators=(',', ':'))
md5_full = hashlib.md5(full_json.encode()).hexdigest()
if md5_full == check:
print(f" ✓ 匹配: full_json")
else:
print(f" ✗ full_json: {md5_full}")
# 尝试去掉check字段后的JSON MD5
j_no_check = j.copy()
if 'check' in j_no_check:
del j_no_check['check']
full_json_no_check = json.dumps(j_no_check, separators=(',', ':'))
md5_no_check = hashlib.md5(full_json_no_check.encode()).hexdigest()
if md5_no_check == check:
print(f" ✓ 匹配: full_json_no_check")
else:
print(f" ✗ full_json_no_check: {md5_no_check}")
# 尝试包含原始响应数据
if 'decrypted' in response:
md5_raw = hashlib.md5(response['decrypted'].encode()).hexdigest()
if md5_raw == check:
print(f" ✓ 匹配: decrypted_response")
else:
print(f" ✗ decrypted_response: {md5_raw}")
if __name__ == "__main__":
import sys
# 默认配置 (从 SO 逆向获取)
HOST = "wy.llua.cn"
APPID = " "
APPKEY = "" # 填入从 SO 逆出的 AppKey
RC4KEY = "" # 填入从 SO 逆出的 RC4 Key
KAMI = "" # 填入卡密
IMEI = "" # 填入机器码
wy = WeiYan(HOST, APPID, APPKEY, RC4KEY, KAMI, IMEI)
if len(sys.argv) > 1:
cmd = sys.argv[1]
if cmd == "login":
ep = sys.argv[2] if len(sys.argv) > 2 else "kmlogon"
result = wy.login(ep)
analyze_response(result)
elif cmd == "notice":
wy.notice()
elif cmd == "custom":
path = sys.argv[2]
body = sys.argv[3] if len(sys.argv) > 3 else ""
wy.custom(path, body)
elif cmd == "analyze":
# 模拟分析成功响应
mock_response = {
'de_json': {
'code': 2100,
'msg': {
'id': 11102437,
'kmtype': 'year',
'token': '72UZW',
'note': '',
'ktype': 'code',
'vip': 1812197817
},
'time': 1780663631,
'check': 'f200d0dfb83b3e2da14afc39e325ef07'
},
'raw_len': 169
}
analyze_response(mock_response)
else:
print(f"未知命令: {cmd}")
else:
# 交互模式
print("=" * 60)
print(" 微验 API 测试工具 (Python)")
print("=" * 60)
print(f" Host: {HOST}")
print(f" AppID: {APPID}")
print()
while True:
try:
cmd = input(">>> ").strip()
if not cmd:
result = wy.login()
analyze_response(result)
elif cmd == "q":
break
elif cmd == "login":
result = wy.login()
analyze_response(result)
elif cmd == "kmlogon":
result = wy.login("kmlogon")
analyze_response(result)
elif cmd == "kmlogin":
result = wy.login("kmlogin")
analyze_response(result)
elif cmd == "notice":
wy.notice()
elif cmd.startswith("custom "):
parts = cmd.split(None, 2)
path = parts[1]
body = parts[2] if len(parts) > 2 else ""
wy.custom(path, body)
elif cmd == "analyze":
mock_response = {
'de_json': {
'code': 2100,
'msg': {
'id': 11102437,
'kmtype': 'year',
'token': '72UZW',
'note': '',
'ktype': 'code',
'vip': 1812197817
},
'time': 1780663631,
'check': 'f200d0dfb83b3e2da14afc39e325ef07'
},
'raw_len': 169
}
analyze_response(mock_response)
elif cmd == "help":
print(" login / kmlogon / kmlogin - 登录")
print(" notice - 公告")
print(" custom <path> [body] - 自定义")
print(" analyze - 分析响应结构")
print(" q - 退出")
else:
print(" 输入 help 查看命令")
except KeyboardInterrupt:
break
except Exception as e:
print(f" 错误: {e}")

难度SO加密了
appid只能frida修改
(软件有防VPN) 就用的这个软件

问题来了,Code再so里面,并且静态hook不到,frida也没特征地点,于是,只能放弃
但是可以通过so静态找到偏移地点,然后frida进行修改
例如 直接修改appid
获取code
就可以了