#!/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()