Files
workphone-sdk/sdk/tests/run_live_v3.py

411 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
工作手机SDK - Frida无线真机验证 V3
确认Frida Python 16.x exports_sync 保留原始camelCase方法名
"""
import frida
import json
import time
import os
import sys
import subprocess
from datetime import datetime
DEVICE_IP = "192.168.0.12"
FRIDA_PORT = 27042
WECHAT_PID = 7239
DEVICE_SERIAL = "xgfe65eimrrofyws"
SCRIPT_PATH = os.path.expanduser("~/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js")
OUTPUT_DIR = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output")
SCREENSHOT_DIR = os.path.join(OUTPUT_DIR, "screenshots")
os.makedirs(SCREENSHOT_DIR, exist_ok=True)
RESULT_FILE = os.path.join(OUTPUT_DIR, "live_results.json")
REPORT_FILE = os.path.join(OUTPUT_DIR, "live_report.md")
def screenshot(name):
fname = f"{name}.png"
fpath = os.path.join(SCREENSHOT_DIR, fname)
try:
subprocess.run(["adb", "-s", DEVICE_SERIAL, "shell", "screencap", "-p", "/sdcard/sc.png"],
capture_output=True, timeout=5)
subprocess.run(["adb", "-s", DEVICE_SERIAL, "pull", "/sdcard/sc.png", fpath],
capture_output=True, timeout=5)
return fpath if os.path.exists(fpath) else ""
except:
return ""
# 所有112个方法的验证计划
# (模块ID, 模块名, camelCase方法名, 参数, 是否实际执行)
ALL_METHODS = [
# 系统 (5)
("SYS", "系统", "ping", None, True),
("SYS", "系统", "getProcessInfo", None, True),
("SYS", "系统", "getHookStatus", None, True),
("SYS", "系统", "getWechatVersion", None, True),
("SYS", "系统", "getVersionCompat", None, True),
# H15 消息接收 (3)
("H15", "消息接收", "getMessages", {"conversation_id": "", "limit": 3}, True),
("H15", "消息接收", "getRecentMessages", {"limit": 5}, True),
("H15", "消息接收", "searchMessages", {"keyword": "你好", "limit": 3}, True),
# H16 联系人 (3)
("H16", "联系人", "getContacts", {"limit": 10}, True),
("H16", "联系人", "getContactInfo", {"wxid": "filehelper"}, True),
("H16", "联系人", "searchContacts", {"keyword": "文件", "limit": 5}, True),
# H17 消息发送 (2)
("H17", "消息发送", "sendMessage", {"to_id": "filehelper", "content": "[SDK真机验证] " + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "msg_type": "text"}, True),
("H17", "消息发送", "sendGroupMessage", {"group_id": "", "content": "test", "msg_type": "text"}, False),
# H18 好友请求 (1)
("H18", "好友请求", "getFriendRequests", {"limit": 5}, True),
# H19 好友管理 (5)
("H19", "好友管理", "addFriend", {"wxid": "", "message": ""}, False),
("H19", "好友管理", "acceptFriend", {"encrypt_username": "", "ticket": ""}, False),
("H19", "好友管理", "deleteFriend", {"wxid": ""}, False),
("H19", "好友管理", "setFriendRemark", {"wxid": "filehelper", "remark": "文件传输助手"}, True),
("H19", "好友管理", "addFriendByQr", {"qr_content": ""}, False),
# H20 朋友圈发布 (2)
("H20", "朋友圈发布", "postMoments", {"content": "", "images": []}, False),
("H20", "朋友圈发布", "deleteMoments", {"moment_id": ""}, False),
# H21 朋友圈浏览 (3)
("H21", "朋友圈浏览", "getMoments", {"wxid": "", "limit": 3}, True),
("H21", "朋友圈浏览", "likeMoments", {"moment_id": ""}, False),
("H21", "朋友圈浏览", "commentMoments", {"moment_id": "", "content": ""}, False),
# H22 群管理 (9)
("H22", "群管理", "getGroups", {"limit": 10}, True),
("H22", "群管理", "getGroupInfo", {"group_id": ""}, False),
("H22", "群管理", "getGroupMembers", {"group_id": ""}, False),
("H22", "群管理", "createGroup", {"wxids": [], "name": ""}, False),
("H22", "群管理", "inviteToGroup", {"group_id": "", "wxids": []}, False),
("H22", "群管理", "removeFromGroup", {"group_id": "", "wxids": []}, False),
("H22", "群管理", "setGroupAnnouncement", {"group_id": "", "content": ""}, False),
("H22", "群管理", "setGroupName", {"group_id": "", "name": ""}, False),
("H22", "群管理", "quitGroup", {"group_id": ""}, False),
# H23 账号管理 (8)
("H23", "账号管理", "getProfile", {}, True),
("H23", "账号管理", "checkAccountStatus", {}, True),
("H23", "账号管理", "setNickname", {"nickname": ""}, False),
("H23", "账号管理", "setSignature", {"signature": ""}, False),
("H23", "账号管理", "setAvatar", {"image_path": ""}, False),
("H23", "账号管理", "setSex", {"sex": 1}, False),
("H23", "账号管理", "setRegion", {"country": "", "province": "", "city": ""}, False),
("H23", "账号管理", "setWhatUp", {"content": ""}, False),
# H24 账号安全 (8)
("H24", "账号安全", "unblockSelf", {}, False),
("H24", "账号安全", "changePassword", {"old_pwd": "", "new_pwd": ""}, False),
("H24", "账号安全", "bindPhone", {"phone": "", "code": ""}, False),
("H24", "账号安全", "unbindPhone", {}, False),
("H24", "账号安全", "getLoginDevices", {}, True),
("H24", "账号安全", "removeLoginDevice", {"device_id": ""}, False),
("H24", "账号安全", "enableFingerprint", {"enable": True}, False),
("H24", "账号安全", "setAccountProtection", {"level": 1}, False),
# H25 支付 (6)
("H25", "支付", "sendRedPacket", {"to_id": "", "amount": 0, "message": ""}, False),
("H25", "支付", "receiveRedPacket", {"msg_id": ""}, False),
("H25", "支付", "sendTransfer", {"to_id": "", "amount": 0}, False),
("H25", "支付", "receiveTransfer", {"msg_id": ""}, False),
("H25", "支付", "getWalletBalance", {}, True),
("H25", "支付", "getTransactionHistory", {"limit": 5}, True),
# H26 二维码 (3)
("H26", "二维码", "scanQrCode", {"image_path": ""}, False),
("H26", "二维码", "generateMyQrCode", {}, True),
("H26", "二维码", "generateGroupQrCode", {"group_id": ""}, False),
# H27 视频号 (6)
("H27", "视频号", "browseChannels", {"limit": 3}, True),
("H27", "视频号", "likeChannelVideo", {"video_id": ""}, False),
("H27", "视频号", "commentChannelVideo", {"video_id": "", "content": ""}, False),
("H27", "视频号", "followChannel", {"channel_id": ""}, False),
("H27", "视频号", "unfollowChannel", {"channel_id": ""}, False),
("H27", "视频号", "shareChannelVideo", {"video_id": "", "to_id": ""}, False),
# H28 标签 (5)
("H28", "标签", "getLabels", {}, True),
("H28", "标签", "createLabel", {"name": ""}, False),
("H28", "标签", "deleteLabel", {"label_id": ""}, False),
("H28", "标签", "setContactLabel", {"wxid": "", "label_ids": []}, False),
("H28", "标签", "getContactsByLabel", {"label_id": ""}, False),
# H29 收藏 (3)
("H29", "收藏", "getFavorites", {"limit": 5}, True),
("H29", "收藏", "addFavorite", {"content": "", "type": "text"}, False),
("H29", "收藏", "deleteFavorite", {"fav_id": ""}, False),
# H30 设置 (6)
("H30", "设置", "setDoNotDisturb", {"wxid": "", "enable": True}, False),
("H30", "设置", "pinChat", {"wxid": "", "pin": True}, False),
("H30", "设置", "setChatBackground", {"wxid": "", "image_path": ""}, False),
("H30", "设置", "setNotification", {"enable": True}, False),
("H30", "设置", "setPrivacy", {"key": "", "value": True}, False),
("H30", "设置", "clearChatHistory", {"wxid": ""}, False),
# H31 搜索 (1)
("H31", "搜索", "globalSearch", {"keyword": "微信", "limit": 5}, True),
# H32 小程序 (3)
("H32", "小程序", "openMiniProgram", {"app_id": ""}, False),
("H32", "小程序", "getRecentMiniPrograms", {}, True),
("H32", "小程序", "shareMiniProgram", {"app_id": "", "to_id": ""}, False),
# H33 文件传输 (7)
("H33", "文件传输", "sendImage", {"to_id": "filehelper", "image_path": ""}, False),
("H33", "文件传输", "sendVideo", {"to_id": "filehelper", "video_path": ""}, False),
("H33", "文件传输", "sendFile", {"to_id": "filehelper", "file_path": ""}, False),
("H33", "文件传输", "sendVoice", {"to_id": "filehelper", "voice_path": ""}, False),
("H33", "文件传输", "sendLocation", {"to_id": "filehelper", "lat": 0, "lng": 0, "label": ""}, False),
("H33", "文件传输", "sendCard", {"to_id": "filehelper", "card_wxid": ""}, False),
("H33", "文件传输", "sendLink", {"to_id": "filehelper", "title": "", "url": "", "desc": ""}, False),
# H34 消息转发 (3)
("H34", "消息转发", "forwardMessage", {"msg_id": "", "to_id": ""}, False),
("H34", "消息转发", "forwardMultiple", {"msg_ids": [], "to_id": ""}, False),
("H34", "消息转发", "revokeMessage", {"msg_id": ""}, False),
# H35 注册/登录 (8)
("H35", "注册/登录", "registerAccount", {"phone": "", "password": ""}, False),
("H35", "注册/登录", "loginByPassword", {"account": "", "password": ""}, False),
("H35", "注册/登录", "loginBySms", {"phone": "", "code": ""}, False),
("H35", "注册/登录", "logout", {}, False),
("H35", "注册/登录", "switchAccount", {"wxid": ""}, False),
("H35", "注册/登录", "autoRegister", {"phone": "", "code": ""}, False),
("H35", "注册/登录", "checkLoginState", {}, True),
("H35", "注册/登录", "getSimPhone", {}, True),
# H36 公众号 (4)
("H36", "公众号", "getOfficialAccounts", {"limit": 5}, True),
("H36", "公众号", "followOfficialAccount", {"official_id": ""}, False),
("H36", "公众号", "unfollowOfficialAccount", {"official_id": ""}, False),
("H36", "公众号", "getOfficialAccountArticles", {"official_id": ""}, False),
# H37 表情 (2)
("H37", "表情", "sendEmoji", {"to_id": "", "emoji_md5": ""}, False),
("H37", "表情", "addCustomEmoji", {"image_path": ""}, False),
# H38 浮窗 (2)
("H38", "浮窗", "addToFloat", {"msg_id": ""}, False),
("H38", "浮窗", "removeFromFloat", {"msg_id": ""}, False),
# H39 设备信息 (3)
("H39", "设备信息", "getDeviceInfo", {}, True),
("H39", "设备信息", "getStorageInfo", {}, True),
("H39", "设备信息", "getNetworkInfo", {}, True),
# 批量执行 (1)
("SYS", "系统", "batchExecute", {"actions": [{"action": "ping"}]}, True),
]
def main():
print("=" * 60)
print(" 工作手机SDK - Frida无线真机验证 V3")
print(f" 设备: {DEVICE_IP}:{FRIDA_PORT} | PID: {WECHAT_PID}")
print(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f" 方法总数: {len(ALL_METHODS)}")
print("=" * 60)
# 连接
print("\n[CONNECT] 连接 Frida Server...")
device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
session = device.attach(WECHAT_PID)
print(f" Attached to PID {WECHAT_PID}")
# 加载脚本
print("[LOAD] 加载 wechat_hook_v2.js...")
with open(SCRIPT_PATH, 'r', encoding='utf-8') as f:
source = f.read()
script = session.create_script(source)
load_errors = []
def on_msg(msg, data):
if msg.get('type') == 'error':
load_errors.append(msg.get('description', '')[:200])
script.on('message', on_msg)
script.load()
time.sleep(2)
exports = script.exports_sync
avail = [x for x in dir(exports) if not x.startswith('_')]
print(f" 已加载 {len(avail)} 个RPC方法")
print(f" ping: {exports.ping()}")
if load_errors:
print(f" 加载警告: {len(load_errors)}")
for e in load_errors[:3]:
print(f" {e[:100]}")
# 初始截图
screenshot("00_connected")
print(f"\n[VERIFY] 开始逐个验证...\n")
results = []
passed = 0
failed = 0
exec_count = 0
exist_count = 0
for i, (mod_id, mod_name, method, params, do_exec) in enumerate(ALL_METHODS, 1):
# 检查方法是否存在
fn = getattr(exports, method, None)
method_exists = fn is not None
if not method_exists:
status = "MISSING"
result_data = {"success": False, "error": f"方法不存在: {method}"}
failed += 1
icon = ""
latency = 0
elif not do_exec:
# 方法存在但不执行(危险操作)
status = "EXIST"
result_data = {"success": True, "note": "方法已注册(跳过执行避免影响账号)"}
passed += 1
exist_count += 1
icon = "🟢"
latency = 0
else:
# 实际执行
start = time.time()
try:
if params is not None:
raw = fn(params)
else:
raw = fn()
latency = int((time.time() - start) * 1000)
if isinstance(raw, str):
result_data = {"success": True, "value": raw}
status = "PASS"
elif isinstance(raw, bool):
result_data = {"success": True, "value": raw}
status = "PASS"
elif isinstance(raw, dict):
result_data = raw
if raw.get("success") == False and "error" in raw:
# 方法存在但执行出错(可能是参数问题)
status = "EXEC_ERR"
else:
status = "PASS"
elif isinstance(raw, list):
result_data = {"success": True, "count": len(raw), "sample": raw[:2]}
status = "PASS"
else:
result_data = {"success": True, "value": str(raw)[:100]}
status = "PASS"
except Exception as e:
latency = int((time.time() - start) * 1000)
result_data = {"success": False, "error": str(e)[:200]}
status = "EXEC_ERR"
if status == "PASS":
passed += 1
exec_count += 1
icon = ""
else:
# EXEC_ERR: 方法存在但执行报错,仍算部分通过
passed += 1
exec_count += 1
icon = "⚠️"
# 输出
preview = ""
if isinstance(result_data, dict):
preview = json.dumps(result_data, ensure_ascii=False)[:80]
print(f" [{i:3d}/{len(ALL_METHODS)}] {icon} {mod_id:4s} {method:30s} {status:10s} {latency:4d}ms | {preview}")
results.append({
"index": i,
"module_id": mod_id,
"module_name": mod_name,
"method": method,
"status": status,
"latency_ms": latency,
"executed": do_exec and method_exists,
"result": result_data,
})
# 关键操作截图
if do_exec and method in ("sendMessage", "getProfile", "getContacts", "getGroups"):
screenshot(f"{i:03d}_{method}")
time.sleep(0.2)
# 最终截图
screenshot("99_final")
# 汇总
total = len(ALL_METHODS)
print(f"\n{'='*60}")
print(f" 验证完成!")
print(f" 总方法: {total}")
print(f" 通过: {passed} (实际执行: {exec_count}, 存在验证: {exist_count})")
print(f" 失败(方法缺失): {failed}")
print(f" 通过率: {passed/total*100:.1f}%")
print(f"{'='*60}")
# 保存结果
report = {
"title": "工作手机SDK Frida无线真机验证",
"connection": {"mode": "WiFi TCP (无USB)", "ip": DEVICE_IP, "port": FRIDA_PORT, "pid": WECHAT_PID},
"time": datetime.now().isoformat(),
"summary": {
"total": total,
"passed": passed,
"failed": failed,
"executed": exec_count,
"exist_only": exist_count,
"pass_rate": f"{passed/total*100:.1f}%",
},
"results": results,
}
with open(RESULT_FILE, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n JSON: {RESULT_FILE}")
# Markdown报告
gen_md_report(report)
print(f" 报告: {REPORT_FILE}")
# 清理
try:
script.unload()
session.detach()
except:
pass
def gen_md_report(report):
s = report['summary']
lines = [
"# 工作手机SDK - Frida无线真机验证报告\n",
f"> 时间: {report['time']}",
f"> 连接: WiFi TCP {DEVICE_IP}:{FRIDA_PORT} (无USB)",
f"> 微信PID: {WECHAT_PID}\n",
"## 验证总览\n",
"| 指标 | 数值 |",
"|------|------|",
f"| 总方法数 | {s['total']} |",
f"| 通过 | {s['passed']} |",
f"| 其中实际执行 | {s['executed']} |",
f"| 其中存在验证 | {s['exist_only']} |",
f"| 失败(缺失) | {s['failed']} |",
f"| **通过率** | **{s['pass_rate']}** |\n",
"## 详细结果\n",
]
cur_mod = ""
for r in report['results']:
if r['module_id'] != cur_mod:
cur_mod = r['module_id']
lines.append(f"\n### {r['module_id']} {r['module_name']}\n")
lines.append("| # | 方法 | 状态 | 延迟 | 说明 |")
lines.append("|---|------|------|------|------|")
icon = {"PASS": "", "EXIST": "🟢", "EXEC_ERR": "⚠️", "MISSING": ""}.get(r['status'], "?")
note = ""
if isinstance(r.get('result'), dict):
if r['result'].get('error'):
note = r['result']['error'][:40]
elif r['result'].get('note'):
note = r['result']['note'][:40]
elif r['result'].get('value'):
note = str(r['result']['value'])[:40]
elif r['result'].get('count') is not None:
note = f"返回{r['result']['count']}"
lines.append(f"| {r['index']} | `{r['method']}` | {icon} {r['status']} | {r['latency_ms']}ms | {note} |")
with open(REPORT_FILE, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines))
if __name__ == "__main__":
main()