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

418 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无线真机验证 V2
修正Frida Python会将camelCase exports转为全小写
策略:直接用全小写调用 rpc.exports_sync 的方法
"""
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"
# Hook脚本路径
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
SCRIPT_PATH = os.path.join(SCRIPT_DIR, '..', 'agent', 'hook', 'wechat_hook_v2.js')
# 输出目录
OUTPUT_DIR = os.path.join(SCRIPT_DIR, '..', '..', '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_verification_results.json')
REPORT_FILE = os.path.join(OUTPUT_DIR, 'live_verification_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_tmp.png"],
capture_output=True, timeout=5)
subprocess.run(["adb", "-s", DEVICE_SERIAL, "pull", "/sdcard/sc_tmp.png", fpath],
capture_output=True, timeout=5)
if os.path.exists(fpath) and os.path.getsize(fpath) > 0:
return fpath
except Exception as e:
pass
return ""
def call_rpc(exports, method_name, params=None):
"""安全调用RPC方法Frida Python会把camelCase转全小写"""
# Frida Python的exports_sync会把所有方法名转为小写
py_name = method_name.lower()
fn = getattr(exports, py_name, None)
if fn is None:
return {"success": False, "error": f"方法不存在: {py_name} (原始: {method_name})"}
try:
if params:
return fn(params)
else:
return fn()
except Exception as e:
return {"success": False, "error": str(e)}
# 所有112个RPC方法的验证计划
# 格式: (模块ID, 模块名, 方法名, 参数, 是否安全执行)
ALL_METHODS = [
# 系统方法
("SYS", "系统", "ping", None, True),
("SYS", "系统", "getProcessInfo", None, True),
("SYS", "系统", "getHookStatus", None, True),
("SYS", "系统", "getWechatVersion", None, True),
("SYS", "系统", "getVersionCompat", None, True),
# H15 消息接收
("H15", "消息接收", "getMessages", {"conversation_id": "", "limit": 3}, True),
("H15", "消息接收", "getRecentMessages", {"limit": 5}, True),
("H15", "消息接收", "searchMessages", {"keyword": "你好", "limit": 3}, True),
# H16 联系人
("H16", "联系人", "getContacts", {"limit": 10}, True),
("H16", "联系人", "getContactInfo", {"wxid": "filehelper"}, True),
("H16", "联系人", "searchContacts", {"keyword": "文件", "limit": 5}, True),
# H17 消息发送
("H17", "消息发送", "sendMessage", {"to_id": "filehelper", "content": "[SDK验证]文本消息 " + datetime.now().strftime("%H:%M:%S"), "msg_type": "text"}, True),
("H17", "消息发送", "sendGroupMessage", {"group_id": "", "content": "test", "msg_type": "text"}, False),
# H18 好友请求
("H18", "好友请求", "getFriendRequests", {"limit": 5}, True),
# H19 好友管理
("H19", "好友管理", "addFriend", {"wxid": "", "message": "test"}, False),
("H19", "好友管理", "acceptFriend", {"encrypt_username": "", "ticket": ""}, False),
("H19", "好友管理", "deleteFriend", {"wxid": ""}, False),
("H19", "好友管理", "setFriendRemark", {"wxid": "filehelper", "remark": "文件助手"}, True),
("H19", "好友管理", "addFriendByQr", {"qr_content": ""}, False),
# H20 朋友圈发布
("H20", "朋友圈发布", "postMoments", {"content": "", "images": []}, False),
("H20", "朋友圈发布", "deleteMoments", {"moment_id": ""}, False),
# H21 朋友圈浏览
("H21", "朋友圈浏览", "getMoments", {"wxid": "", "limit": 3}, True),
("H21", "朋友圈浏览", "likeMoments", {"moment_id": ""}, False),
("H21", "朋友圈浏览", "commentMoments", {"moment_id": "", "content": ""}, False),
# H22 群管理
("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 账号管理
("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 账号安全
("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 支付
("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 二维码
("H26", "二维码", "scanQrCode", {"image_path": ""}, False),
("H26", "二维码", "generateMyQrCode", {}, True),
("H26", "二维码", "generateGroupQrCode", {"group_id": ""}, False),
# H27 视频号
("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 标签
("H28", "标签", "getLabels", {}, True),
("H28", "标签", "createLabel", {"name": ""}, False),
("H28", "标签", "deleteLabel", {"label_id": ""}, False),
("H28", "标签", "setContactLabel", {"wxid": "", "label_ids": []}, False),
("H28", "标签", "getContactsByLabel", {"label_id": ""}, False),
# H29 收藏
("H29", "收藏", "getFavorites", {"limit": 5}, True),
("H29", "收藏", "addFavorite", {"content": "", "type": "text"}, False),
("H29", "收藏", "deleteFavorite", {"fav_id": ""}, False),
# H30 设置
("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 搜索
("H31", "搜索", "globalSearch", {"keyword": "微信", "limit": 5}, True),
# H32 小程序
("H32", "小程序", "openMiniProgram", {"app_id": ""}, False),
("H32", "小程序", "getRecentMiniPrograms", {}, True),
("H32", "小程序", "shareMiniProgram", {"app_id": "", "to_id": ""}, False),
# H33 文件传输
("H33", "文件传输", "sendImage", {"to_id": "filehelper", "image_path": "/sdcard/DCIM/Camera/test.jpg"}, 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 消息转发
("H34", "消息转发", "forwardMessage", {"msg_id": "", "to_id": ""}, False),
("H34", "消息转发", "forwardMultiple", {"msg_ids": [], "to_id": ""}, False),
("H34", "消息转发", "revokeMessage", {"msg_id": ""}, False),
# H35 注册/登录
("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 公众号
("H36", "公众号", "getOfficialAccounts", {"limit": 5}, True),
("H36", "公众号", "followOfficialAccount", {"official_id": ""}, False),
("H36", "公众号", "unfollowOfficialAccount", {"official_id": ""}, False),
("H36", "公众号", "getOfficialAccountArticles", {"official_id": ""}, False),
# H37 表情
("H37", "表情", "sendEmoji", {"to_id": "", "emoji_md5": ""}, False),
("H37", "表情", "addCustomEmoji", {"image_path": ""}, False),
# H38 浮窗
("H38", "浮窗", "addToFloat", {"msg_id": ""}, False),
("H38", "浮窗", "removeFromFloat", {"msg_id": ""}, False),
# H39 设备信息
("H39", "设备信息", "getDeviceInfo", {}, True),
("H39", "设备信息", "getStorageInfo", {}, True),
("H39", "设备信息", "getNetworkInfo", {}, True),
]
def main():
print("=" * 60)
print(" 工作手机SDK - Frida无线真机验证 V2")
print(f" 设备: {DEVICE_IP}:{FRIDA_PORT} | PID: {WECHAT_PID}")
print(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
# 连接
print("\n[1] 连接 Frida...")
device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
print(f" 设备: {device.name}")
print("[2] Attach 微信 (PID {})...".format(WECHAT_PID))
session = device.attach(WECHAT_PID)
print(" 成功!")
print("[3] 加载 Hook 脚本...")
if not os.path.exists(SCRIPT_PATH):
print(f" 脚本不存在: {SCRIPT_PATH}")
sys.exit(1)
with open(SCRIPT_PATH, 'r', encoding='utf-8') as f:
source = f.read()
script = session.create_script(source)
errors = []
def on_message(msg, data):
if msg.get('type') == 'error':
errors.append(msg.get('description', ''))
script.on('message', on_message)
script.load()
time.sleep(1) # 等待Java.perform完成
exports = script.exports_sync
# 验证ping
try:
pong = exports.ping()
print(f" ping: {pong}")
except Exception as e:
print(f" ping失败: {e}")
print(" Hook脚本加载可能有问题检查错误:")
for err in errors:
print(f" {err}")
sys.exit(1)
print("[4] 开始验证 (共{}个方法)...\n".format(len(ALL_METHODS)))
# 初始截图
screenshot("00_start")
results = []
passed = 0
failed = 0
skipped = 0
for i, (mod_id, mod_name, method, params, safe) in enumerate(ALL_METHODS, 1):
# 对于不安全的操作(会修改数据),只验证方法存在性
if not safe:
# 只检查方法是否存在
py_name = method.lower()
fn = getattr(exports, py_name, None)
if fn is not None:
status = "EXIST"
result_data = {"success": True, "note": "方法存在(跳过执行,避免影响账号)"}
passed += 1
else:
status = "MISSING"
result_data = {"success": False, "error": f"方法不存在: {py_name}"}
failed += 1
icon = "" if status == "EXIST" else ""
print(f" [{i:3d}] {icon} {mod_id}/{method} -> {status} (unsafe, skip exec)")
else:
# 安全操作,实际执行
start = time.time()
result_data = call_rpc(exports, method, params)
latency = int((time.time() - start) * 1000)
if isinstance(result_data, str):
# ping等返回字符串
status = "PASS"
result_data = {"success": True, "value": result_data}
passed += 1
elif isinstance(result_data, dict):
if result_data.get("success", False) or "error" not in result_data:
status = "PASS"
passed += 1
elif "方法不存在" in result_data.get("error", ""):
status = "MISSING"
failed += 1
else:
# 有error但方法存在可能是参数问题或微信状态问题
status = "PARTIAL"
passed += 1 # 方法存在就算通过
elif isinstance(result_data, bool):
status = "PASS"
result_data = {"success": True, "value": result_data}
passed += 1
else:
status = "PASS"
result_data = {"success": True, "value": str(result_data)}
passed += 1
icon = "" if status in ("PASS", "PARTIAL") else ""
preview = json.dumps(result_data, ensure_ascii=False)[:80] if isinstance(result_data, dict) else str(result_data)[:80]
print(f" [{i:3d}] {icon} {mod_id}/{method} -> {status} ({latency}ms) | {preview}")
# 对关键操作截图
if method in ("sendMessage", "getProfile", "getContacts", "getGroups", "getLabels"):
screenshot(f"{i:03d}_{method}")
results.append({
"index": i,
"module_id": mod_id,
"module_name": mod_name,
"method": method,
"params": params,
"safe": safe,
"status": status,
"result": result_data,
"timestamp": datetime.now().isoformat(),
})
time.sleep(0.3)
# 最终截图
screenshot("99_end")
# 汇总
total = len(ALL_METHODS)
print(f"\n{'='*60}")
print(f" 验证完成!")
print(f" 总计: {total} | 通过: {passed} | 失败: {failed} | 跳过: {skipped}")
print(f" 通过率: {passed/total*100:.1f}%")
print(f"{'='*60}")
# 保存JSON结果
report = {
"title": "Frida无线真机验证报告",
"device": {"ip": DEVICE_IP, "port": FRIDA_PORT, "pid": WECHAT_PID, "serial": DEVICE_SERIAL},
"time": datetime.now().isoformat(),
"summary": {"total": total, "passed": passed, "failed": failed, "skipped": skipped, "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结果已保存: {RESULT_FILE}")
# 生成Markdown报告
generate_report(report)
# 清理
try:
script.unload()
session.detach()
except:
pass
def generate_report(report):
"""生成Markdown验证报告"""
lines = []
lines.append("# 工作手机SDK - Frida无线真机验证报告\n")
lines.append(f"> 时间: {report['time']}")
lines.append(f"> 设备: {DEVICE_IP}:{FRIDA_PORT} (PID: {WECHAT_PID})")
lines.append(f"> 连接方式: WiFi TCP (无USB)\n")
s = report['summary']
lines.append("## 验证总览\n")
lines.append(f"| 指标 | 数值 |")
lines.append(f"|------|------|")
lines.append(f"| 总方法数 | {s['total']} |")
lines.append(f"| 通过 | {s['passed']} |")
lines.append(f"| 失败 | {s['failed']} |")
lines.append(f"| **通过率** | **{s['pass_rate']}** |\n")
# 按模块分组
lines.append("## 详细结果\n")
current_mod = ""
for r in report['results']:
if r['module_id'] != current_mod:
current_mod = r['module_id']
lines.append(f"\n### {r['module_id']} {r['module_name']}\n")
lines.append("| # | 方法 | 状态 | 说明 |")
lines.append("|---|------|------|------|")
icon = "" if r['status'] in ("PASS", "EXIST", "PARTIAL") else ""
note = ""
if isinstance(r.get('result'), dict):
if r['result'].get('error'):
note = r['result']['error'][:50]
elif r['result'].get('note'):
note = r['result']['note'][:50]
elif r['result'].get('value'):
note = str(r['result']['value'])[:50]
lines.append(f"| {r['index']} | `{r['method']}` | {icon} {r['status']} | {note} |")
lines.append("\n## 截图目录\n")
lines.append(f"截图保存在: `{SCREENSHOT_DIR}`\n")
with open(REPORT_FILE, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines))
print(f"报告已保存: {REPORT_FILE}")
if __name__ == "__main__":
main()