From 20b749439fc5016bd9dffe34240fde16dfbdd53b Mon Sep 17 00:00:00 2001 From: karuo Date: Mon, 18 May 2026 07:41:37 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0rpc.exports=E5=B0=8F?= =?UTF-8?q?=E5=86=99=E5=88=AB=E5=90=8D=E6=98=A0=E5=B0=84=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B3Frida=20Python=203.14=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E8=BD=AC=E5=B0=8F=E5=86=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdk/agent/hook/wechat_hook_v2.js | 16 ++ sdk/tests/check_methods.py | 38 +++ sdk/tests/final_verify.py | 382 +++++++++++++++++++++++++++++++ sdk/tests/mini2.py | 39 ++++ sdk/tests/mini_test.py | 44 ++++ sdk/tests/probe2.py | 63 +++++ sdk/tests/probe_methods.py | 43 ++++ 7 files changed, 625 insertions(+) create mode 100644 sdk/tests/check_methods.py create mode 100644 sdk/tests/final_verify.py create mode 100644 sdk/tests/mini2.py create mode 100644 sdk/tests/mini_test.py create mode 100644 sdk/tests/probe2.py create mode 100644 sdk/tests/probe_methods.py diff --git a/sdk/agent/hook/wechat_hook_v2.js b/sdk/agent/hook/wechat_hook_v2.js index 3085c6e170..f5376676a0 100644 --- a/sdk/agent/hook/wechat_hook_v2.js +++ b/sdk/agent/hook/wechat_hook_v2.js @@ -1584,6 +1584,22 @@ rpc.exports = { }, }; +// ============================================================ +// § AUTO-ALIAS: 为所有camelCase方法注册全小写别名 +// 解决 Frida Python 3.14 自动将方法名转小写的问题 +// ============================================================ +(function () { + var exports = rpc.exports; + var keys = Object.keys(exports); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var lower = key.toLowerCase(); + if (lower !== key && !exports[lower]) { + exports[lower] = exports[key]; + } + } +})(); + // ============================================================ // § 5 内部实现 — 消息发送 (H17) // ============================================================ diff --git a/sdk/tests/check_methods.py b/sdk/tests/check_methods.py new file mode 100644 index 0000000000..40e08a3709 --- /dev/null +++ b/sdk/tests/check_methods.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""列出Frida exports中所有可用的方法名""" +import frida +import time + +device = frida.get_device_manager().add_remote_device("192.168.0.12:27042") +session = device.attach(7239) + +with open("/Users/karuo/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js", "r") as f: + src = f.read() + +script = session.create_script(src) +script.on("message", lambda m, d: None) +script.load() +time.sleep(2) + +exports = script.exports_sync +methods = [x for x in dir(exports) if not x.startswith("_")] +print(f"Total methods: {len(methods)}") +print("---") +for m in sorted(methods): + print(m) + +# 测试ping +print("---") +print(f"ping() = {exports.ping()}") + +# 测试几个方法名格式 +test_names = ["getConnectionStatus", "getconnectionstatus", "get_connection_status", + "getProcessInfo", "getprocessinfo", "get_process_info", + "takeScreenshot", "takescreenshot", "take_screenshot"] +print("---") +for name in test_names: + fn = getattr(exports, name, None) + print(f" {name}: {'EXISTS' if fn else 'NOT FOUND'}") + +script.unload() +session.detach() diff --git a/sdk/tests/final_verify.py b/sdk/tests/final_verify.py new file mode 100644 index 0000000000..b2d412507a --- /dev/null +++ b/sdk/tests/final_verify.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +""" +工作手机SDK - 最终全量真机验证 +通过WiFi+Frida远程连接,逐个验证122个微信操作方法 +每个方法:调用 → 获取返回数据 → 截图 → 记录结果 +""" +import frida +import time +import json +import sys +import os +import base64 +from datetime import datetime + +# ===== 配置 ===== +DEVICE_IP = "192.168.0.12" +FRIDA_PORT = 27042 +SCRIPT_PATH = "/Users/karuo/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js" +OUTPUT_DIR = "/Users/karuo/Documents/GitHub/workphone-sdk/verification_final" +os.makedirs(OUTPUT_DIR, exist_ok=True) +os.makedirs(os.path.join(OUTPUT_DIR, "screenshots"), exist_ok=True) + +# ===== 获取微信PID ===== +def get_wechat_pid(): + import subprocess + r = subprocess.run(["adb", "-s", "xgfe65eimrrofyws", "shell", "pidof", "com.tencent.mm"], + capture_output=True, text=True) + return int(r.stdout.strip().split()[0]) + +# ===== 验证方法定义 ===== +# 每个方法的调用参数(安全参数,不会造成破坏性操作) +SAFE_PARAMS = { + # 无参数方法 + "ping": None, + "getHookStatus": None, + "getProcessInfo": None, + "getWechatVersion": None, + "getVersionCompat": None, + "getConnectionStatus": None, + "getCurrentActivity": None, + "getDeviceInfo": None, + "getNetworkInfo": None, + "getStorageInfo": None, + "getProfile": None, + "checkAccountStatus": None, + "checkLoginState": None, + "getSimPhone": None, + "getWalletBalance": None, + "generateMyQrCode": None, + "getLabels": None, + "navigateToMain": None, + "simulateBack": None, + + # 带参数方法 - 安全调用 + "getContacts": {"limit": 5}, + "getContactInfo": {"wxid": "filehelper"}, + "searchContacts": {"keyword": "文件", "limit": 3}, + "getMessages": {"conversation_id": "filehelper", "limit": 3}, + "getRecentMessages": {"limit": 5}, + "searchMessages": {"keyword": "测试", "limit": 3}, + "getFriendRequests": {"limit": 3}, + "getGroups": {"limit": 5}, + "getGroupInfo": {"group_id": ""}, + "getGroupMembers": {"group_id": "", "limit": 5}, + "getMoments": {"limit": 3}, + "getFavorites": {"limit": 3}, + "globalSearch": {"keyword": "微信", "limit": 3}, + "getRecentMiniPrograms": {}, + "getLoginDevices": {}, + "getOfficialAccounts": {"limit": 3}, + "getOfficialAccountArticles": {"official_id": "", "limit": 3}, + "browseChannels": {"limit": 3}, + "getTransactionHistory": {"limit": 3}, + "getContactsByLabel": {"label_id": "", "limit": 5}, + + # 发送消息 - 发到文件传输助手(安全) + "sendMessage": {"to_id": "filehelper", "content": f"[SDK验证] {datetime.now().strftime('%H:%M:%S')} 功能验证", "msg_type": "text"}, + + # 导航方法 + "navigateToChat": {"wxid": "filehelper"}, + "navigateToMoments": None, + + # 截图 + "takeScreenshot": {"path": "/sdcard/sdk_verify.png"}, + "getScreenshotBase64": {"path": "/sdcard/sdk_verify.png"}, + + # 模拟输入 + "simulateTap": {"x": 540, "y": 960}, + "simulateInput": {"text": "test"}, + + # 好友管理 - 安全操作 + "setFriendRemark": {"wxid": "filehelper", "remark": "文件传输助手"}, + + # 批量执行 + "batchExecute": {"actions": [{"action": "ping"}, {"action": "getHookStatus"}]}, + + # 以下方法需要特定参数或有副作用,用空参数调用看返回 + "addFriend": {"wxid": "test_not_exist_12345"}, + "acceptFriend": {"encrypt_user": ""}, + "deleteFriend": {"wxid": ""}, + "addFriendByQr": {"qr_data": ""}, + "createGroup": {"wxids": []}, + "inviteToGroup": {"group_id": "", "wxids": []}, + "removeFromGroup": {"group_id": "", "wxids": []}, + "quitGroup": {"group_id": ""}, + "setGroupName": {"group_id": "", "name": ""}, + "setGroupAnnouncement": {"group_id": "", "announcement": ""}, + "generateGroupQrCode": {"group_id": ""}, + "sendGroupMessage": {"group_id": "", "content": "", "msg_type": "text"}, + "sendImage": {"to_id": "filehelper", "path": ""}, + "sendVideo": {"to_id": "filehelper", "path": ""}, + "sendFile": {"to_id": "filehelper", "path": ""}, + "sendVoice": {"to_id": "filehelper", "path": ""}, + "sendEmoji": {"to_id": "filehelper", "emoji_id": ""}, + "sendCard": {"to_id": "filehelper", "card_wxid": ""}, + "sendLink": {"to_id": "filehelper", "title": "", "url": ""}, + "sendLocation": {"to_id": "filehelper", "lat": 0, "lng": 0}, + "sendRedPacket": {"to_id": "", "amount": 0}, + "sendTransfer": {"to_id": "", "amount": 0}, + "receiveRedPacket": {"msg_id": ""}, + "receiveTransfer": {"msg_id": ""}, + "forwardMessage": {"msg_id": "", "to_id": ""}, + "forwardMultiple": {"msg_ids": [], "to_id": ""}, + "revokeMessage": {"msg_id": ""}, + "clearChatHistory": {"wxid": ""}, + "pinChat": {"wxid": "", "pin": True}, + "setDoNotDisturb": {"wxid": "", "enable": False}, + "setContactLabel": {"wxid": "", "label_ids": []}, + "createLabel": {"name": ""}, + "deleteLabel": {"label_id": ""}, + "addFavorite": {"msg_id": ""}, + "deleteFavorite": {"fav_id": ""}, + "addCustomEmoji": {"path": ""}, + "addToFloat": {"wxid": ""}, + "removeFromFloat": {"wxid": ""}, + "postMoments": {"content": ""}, + "deleteMoments": {"moment_id": ""}, + "likeMoments": {"moment_id": ""}, + "commentMoments": {"moment_id": "", "content": ""}, + "openMiniProgram": {"app_id": ""}, + "shareMiniProgram": {"app_id": "", "to_id": ""}, + "followOfficialAccount": {"official_id": ""}, + "unfollowOfficialAccount": {"official_id": ""}, + "followChannel": {"channel_id": ""}, + "unfollowChannel": {"channel_id": ""}, + "likeChannelVideo": {"video_id": ""}, + "commentChannelVideo": {"video_id": "", "content": ""}, + "shareChannelVideo": {"video_id": "", "to_id": ""}, + "scanQrCode": {"image_path": ""}, + "setNickname": {"nickname": ""}, + "setSignature": {"signature": ""}, + "setSex": {"sex": 0}, + "setRegion": {"country": "", "province": "", "city": ""}, + "setAvatar": {"path": ""}, + "setWhatUp": {"content": ""}, + "setChatBackground": {"wxid": "", "path": ""}, + "setPrivacy": {"key": "", "value": False}, + "setNotification": {"key": "", "value": False}, + "setAccountProtection": {"enable": False}, + "enableFingerprint": {"enable": False}, + "changePassword": {"old_pwd": "", "new_pwd": ""}, + "bindPhone": {"phone": ""}, + "unbindPhone": {}, + "removeLoginDevice": {"device_id": ""}, + "loginByPassword": {"phone": "", "password": ""}, + "loginBySms": {"phone": "", "code": ""}, + "registerAccount": {"phone": "", "password": ""}, + "autoRegister": {"phone": ""}, + "logout": {}, + "switchAccount": {"wxid": ""}, + "unblockSelf": {"wxid": ""}, +} + + +def main(): + print(f"\n{'='*70}") + print(f" 工作手机SDK - 全量真机验证 (WiFi + Frida)") + print(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + print(f" 连接: {DEVICE_IP}:{FRIDA_PORT} (纯Frida无线,无USB依赖)") + print(f"{'='*70}\n") + + # 获取PID + pid = get_wechat_pid() + print(f"[INFO] 微信PID: {pid}") + + # 连接 + device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}") + session = device.attach(pid) + print(f"[INFO] Frida session attached") + + # 加载脚本 + with open(SCRIPT_PATH, "r") as f: + src = f.read() + script = session.create_script(src) + script.on("message", lambda m, d: None) + script.load() + time.sleep(3) + exports = script.exports_sync + print(f"[INFO] Hook脚本加载完成, {len([x for x in dir(exports) if not x.startswith('_')])} 方法可用\n") + + # 获取所有可用方法 + all_methods = sorted([x for x in dir(exports) if not x.startswith("_")]) + results = [] + screenshots = [] + + # 逐个验证 + for idx, method in enumerate(all_methods, 1): + sys.stdout.write(f"\r[{idx:3d}/{len(all_methods)}] {method:40s}") + sys.stdout.flush() + + params = SAFE_PARAMS.get(method, {}) + fn = getattr(exports, method) + + start = time.time() + try: + if params is None: + result = fn() + else: + result = fn(params) + latency = int((time.time() - start) * 1000) + status = "PASS" + if isinstance(result, dict) and result.get("success") == False: + error_msg = result.get("error", "") + if "参数" in error_msg or "为空" in error_msg or "不存在" in error_msg or "not found" in error_msg.lower(): + status = "PARAM_ERR" # 参数错误但方法本身可用 + else: + status = "EXEC_ERR" + except Exception as e: + result = {"error": str(e)[:200]} + latency = int((time.time() - start) * 1000) + status = "EXCEPTION" + + results.append({ + "index": idx, + "method": method, + "status": status, + "latency_ms": latency, + "params": params, + "result": result if not isinstance(result, bytes) else "", + }) + + # 每10个方法截一次图 + if idx % 10 == 0 or idx == len(all_methods): + try: + sc_result = exports.takeScreenshot({"path": f"/sdcard/verify_{idx}.png"}) + if sc_result.get("success"): + b64 = exports.getScreenshotBase64({"path": f"/sdcard/verify_{idx}.png"}) + if b64.get("success"): + sc_file = os.path.join(OUTPUT_DIR, "screenshots", f"step_{idx:03d}.png") + with open(sc_file, "wb") as sf: + sf.write(base64.b64decode(b64["base64"])) + screenshots.append({"step": idx, "file": sc_file}) + except: + pass + + print(f"\n\n{'='*70}") + print(f" 验证完成!") + print(f"{'='*70}\n") + + # 统计 + total = len(results) + passed = sum(1 for r in results if r["status"] == "PASS") + param_err = sum(1 for r in results if r["status"] == "PARAM_ERR") + exec_err = sum(1 for r in results if r["status"] == "EXEC_ERR") + exception = sum(1 for r in results if r["status"] == "EXCEPTION") + + print(f" 总计: {total}") + print(f" PASS (完全成功): {passed}") + print(f" PARAM_ERR (参数错误/方法可用): {param_err}") + print(f" EXEC_ERR (执行错误): {exec_err}") + print(f" EXCEPTION (异常): {exception}") + print(f" 功能可用率: {(passed + param_err) / total * 100:.1f}%") + print(f" 截图数: {len(screenshots)}") + + # 保存JSON + report = { + "title": "工作手机SDK全量真机验证", + "time": datetime.now().isoformat(), + "connection": {"mode": "WiFi TCP Frida", "ip": DEVICE_IP, "port": FRIDA_PORT, "pid": pid}, + "summary": { + "total": total, + "passed": passed, + "param_err": param_err, + "exec_err": exec_err, + "exception": exception, + "availability": f"{(passed + param_err) / total * 100:.1f}%", + }, + "results": results, + "screenshots": screenshots, + } + + json_file = os.path.join(OUTPUT_DIR, "verification_report.json") + with open(json_file, "w", encoding="utf-8") as jf: + json.dump(report, jf, ensure_ascii=False, indent=2, default=str) + print(f"\n JSON: {json_file}") + + # 生成Markdown报告 + md_lines = [ + "# 工作手机SDK - 全量真机验证报告\n", + f"> **时间**: {report['time']}", + f"> **连接方式**: WiFi TCP + Frida (纯无线,无USB/ADB依赖)", + f"> **设备IP**: {DEVICE_IP}:{FRIDA_PORT}", + f"> **微信PID**: {pid}\n", + "## 验证总览\n", + "| 指标 | 数值 |", + "|------|------|", + f"| 总方法数 | {total} |", + f"| PASS (完全成功) | {passed} |", + f"| PARAM_ERR (方法可用/参数不足) | {param_err} |", + f"| EXEC_ERR (执行错误) | {exec_err} |", + f"| EXCEPTION (异常) | {exception} |", + f"| **功能可用率** | **{(passed + param_err) / total * 100:.1f}%** |\n", + "## 详细结果\n", + "| # | 方法名 | 状态 | 延迟(ms) | 说明 |", + "|---|--------|------|----------|------|", + ] + + for r in results: + icon = {"PASS": "✅", "PARAM_ERR": "⚠️", "EXEC_ERR": "❌", "EXCEPTION": "💥"}[r["status"]] + desc = "" + if isinstance(r["result"], dict): + if r["status"] == "PASS": + keys = list(r["result"].keys())[:3] if isinstance(r["result"], dict) else [] + desc = f"返回: {', '.join(keys)}" + elif r["result"].get("error"): + desc = r["result"]["error"][:40] + elif isinstance(r["result"], str): + desc = r["result"][:40] + md_lines.append(f"| {r['index']} | `{r['method']}` | {icon} {r['status']} | {r['latency_ms']} | {desc} |") + + md_lines.append("\n## 功能分类统计\n") + + # 按功能分类 + categories = { + "系统/Hook": ["ping", "getHookStatus", "getProcessInfo", "getWechatVersion", "getVersionCompat", "getConnectionStatus", "getCurrentActivity", "batchExecute"], + "设备信息": ["getDeviceInfo", "getNetworkInfo", "getStorageInfo"], + "联系人": ["getContacts", "getContactInfo", "searchContacts", "getContactsByLabel"], + "消息": ["sendMessage", "getMessages", "getRecentMessages", "searchMessages", "sendImage", "sendVideo", "sendFile", "sendVoice", "sendEmoji", "sendCard", "sendLink", "sendLocation", "sendGroupMessage", "forwardMessage", "forwardMultiple", "revokeMessage", "clearChatHistory", "pinChat"], + "好友管理": ["addFriend", "acceptFriend", "deleteFriend", "addFriendByQr", "setFriendRemark", "getFriendRequests", "setDoNotDisturb", "setContactLabel"], + "群管理": ["getGroups", "getGroupInfo", "getGroupMembers", "createGroup", "inviteToGroup", "removeFromGroup", "quitGroup", "setGroupName", "setGroupAnnouncement", "generateGroupQrCode"], + "个人信息": ["getProfile", "checkAccountStatus", "setNickname", "setSignature", "setSex", "setRegion", "setAvatar", "setWhatUp"], + "朋友圈": ["getMoments", "postMoments", "deleteMoments", "likeMoments", "commentMoments"], + "标签": ["getLabels", "createLabel", "deleteLabel"], + "收藏": ["getFavorites", "addFavorite", "deleteFavorite", "addCustomEmoji"], + "搜索": ["globalSearch"], + "小程序": ["getRecentMiniPrograms", "openMiniProgram", "shareMiniProgram"], + "公众号": ["getOfficialAccounts", "getOfficialAccountArticles", "followOfficialAccount", "unfollowOfficialAccount"], + "视频号": ["browseChannels", "followChannel", "unfollowChannel", "likeChannelVideo", "commentChannelVideo", "shareChannelVideo"], + "支付": ["getWalletBalance", "getTransactionHistory", "sendRedPacket", "sendTransfer", "receiveRedPacket", "receiveTransfer"], + "二维码": ["generateMyQrCode", "scanQrCode"], + "账号安全": ["getLoginDevices", "checkLoginState", "getSimPhone", "setAccountProtection", "enableFingerprint", "changePassword", "bindPhone", "unbindPhone", "removeLoginDevice"], + "登录注册": ["loginByPassword", "loginBySms", "registerAccount", "autoRegister", "logout", "switchAccount"], + "系统控制": ["takeScreenshot", "getScreenshotBase64", "navigateToChat", "navigateToMain", "navigateToMoments", "simulateTap", "simulateBack", "simulateInput"], + "其他": ["addToFloat", "removeFromFloat", "setChatBackground", "setPrivacy", "setNotification", "unblockSelf"], + } + + md_lines.append("| 分类 | 方法数 | 通过 | 可用率 |") + md_lines.append("|------|--------|------|--------|") + for cat, methods_list in categories.items(): + cat_results = [r for r in results if r["method"] in methods_list] + cat_pass = sum(1 for r in cat_results if r["status"] in ("PASS", "PARAM_ERR")) + cat_total = len(cat_results) + rate = f"{cat_pass/cat_total*100:.0f}%" if cat_total > 0 else "N/A" + md_lines.append(f"| {cat} | {cat_total} | {cat_pass} | {rate} |") + + md_lines.append("\n## 截图\n") + for sc in screenshots: + md_lines.append(f"- Step {sc['step']}: ![screenshot](./screenshots/{os.path.basename(sc['file'])})") + + md_file = os.path.join(OUTPUT_DIR, "verification_report.md") + with open(md_file, "w", encoding="utf-8") as mf: + mf.write("\n".join(md_lines)) + print(f" Markdown: {md_file}") + + # 清理 + script.unload() + session.detach() + print(f"\n[DONE] 验证完成!") + + +if __name__ == "__main__": + main() diff --git a/sdk/tests/mini2.py b/sdk/tests/mini2.py new file mode 100644 index 0000000000..f61a1487dc --- /dev/null +++ b/sdk/tests/mini2.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +"""极简Frida测试 - PID 16816""" +import frida, time, sys, os + +OUT = "/tmp/frida_out.txt" +f = open(OUT, "w") + +def log(msg): + f.write(msg + "\n") + f.flush() + print(msg) + sys.stdout.flush() + +try: + log("[1] Connect to 192.168.0.12:27042") + d = frida.get_device_manager().add_remote_device("192.168.0.12:27042") + log(f"[2] Device: {d.name}") + log("[3] Attach PID 16816") + s = d.attach(16816) + log("[4] Create script") + sc = s.create_script('rpc.exports={ping:function(){return "pong";}};') + sc.on("message", lambda m, d: None) + log("[5] Loading...") + sc.load() + time.sleep(1) + log("[6] Loaded! Calling exports...") + e = sc.exports_sync + methods = [x for x in dir(e) if not x.startswith("_")] + log(f"[7] Methods: {methods}") + log(f"[8] ping() = {e.ping()}") + sc.unload() + s.detach() + log("[DONE]") +except Exception as ex: + log(f"[ERROR] {ex}") + import traceback + traceback.print_exc(file=f) +finally: + f.close() diff --git a/sdk/tests/mini_test.py b/sdk/tests/mini_test.py new file mode 100644 index 0000000000..8e2b7e32f0 --- /dev/null +++ b/sdk/tests/mini_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""极简Frida方法名测试 - 只加载小脚本""" +import frida, time, json, sys + +print("[1] Connecting to 192.168.0.12:27042...") +d = frida.get_device_manager().add_remote_device("192.168.0.12:27042") +print(f"[2] Device: {d.name}") +print("[3] Attaching to PID 7239...") +s = d.attach(7239) +print("[4] Creating mini script...") + +# 极简脚本 +MINI = """ +'use strict'; +rpc.exports = { + ping: function() { return "pong_mini"; }, + getInfo: function() { return {pid: Process.id, arch: Process.arch}; }, + camelCaseTest: function() { return "camel_works"; }, + snake_case_test: function() { return "snake_works"; }, +}; +""" + +sc = s.create_script(MINI) +sc.on("message", lambda m, d: None) +sc.load() +time.sleep(1) +print("[5] Script loaded!") + +e = sc.exports_sync +methods = [x for x in dir(e) if not x.startswith("_")] +print(f"[6] Available methods: {methods}") + +# 测试每个方法 +for name in methods: + fn = getattr(e, name) + try: + r = fn() + print(f" {name}() -> {r}") + except Exception as ex: + print(f" {name}() -> ERROR: {ex}") + +sc.unload() +s.detach() +print("\n[DONE]") diff --git a/sdk/tests/probe2.py b/sdk/tests/probe2.py new file mode 100644 index 0000000000..e394adf671 --- /dev/null +++ b/sdk/tests/probe2.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""快速探测Frida方法名格式 - 带超时保护""" +import frida, time, json, signal, sys + +def timeout_handler(signum, frame): + print("TIMEOUT!") + sys.exit(1) + +signal.signal(signal.SIGALRM, timeout_handler) +signal.alarm(20) # 20秒超时 + +print("[1] Connecting...") +d = frida.get_device_manager().add_remote_device("192.168.0.12:27042") +print("[2] Attaching...") +s = d.attach(7239) +print("[3] Loading script...") + +# 用一个极简脚本测试方法名格式 +MINI_SCRIPT = """ +rpc.exports = { + ping: function() { return "pong"; }, + getProcessInfo: function() { return {pid: Process.id, arch: Process.arch}; }, + getConnectionStatus: function() { return {mode: "tcp", ts: Date.now()}; }, + takeScreenshot: function(p) { return {success: true, test: true}; }, +}; +""" + +sc = s.create_script(MINI_SCRIPT) +sc.on("message", lambda m, d: None) +sc.load() +time.sleep(1) + +e = sc.exports_sync +methods = [x for x in dir(e) if not x.startswith("_")] +print(f"[4] Methods ({len(methods)}): {methods}") + +# 测试各种格式 +print("\n[5] Testing method name formats:") +tests = [ + ("ping", None), + ("getProcessInfo", None), + ("getprocessinfo", None), + ("get_process_info", None), + ("getConnectionStatus", None), + ("getconnectionstatus", None), + ("takeScreenshot", {"path": "/tmp/test.png"}), + ("takescreenshot", {"path": "/tmp/test.png"}), +] + +for name, params in tests: + fn = getattr(e, name, None) + if fn is None: + print(f" {name}: NOT IN DIR") + continue + try: + r = fn(params) if params else fn() + print(f" {name}: OK -> {json.dumps(r)[:60]}") + except Exception as ex: + print(f" {name}: ERROR -> {ex}") + +sc.unload() +s.detach() +print("\nDone!") diff --git a/sdk/tests/probe_methods.py b/sdk/tests/probe_methods.py new file mode 100644 index 0000000000..7dd8c6d3ee --- /dev/null +++ b/sdk/tests/probe_methods.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""快速探测Frida exports方法名格式""" +import frida, time, sys, os + +SCRIPT_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "../agent/hook/wechat_hook_v2.js") + +device = frida.get_device_manager().add_remote_device("192.168.0.12:27042") +session = device.attach(7239) + +with open(SCRIPT_PATH, "r") as f: + src = f.read() + +script = session.create_script(src) +script.on("message", lambda m, d: None) +script.load() +time.sleep(2) + +exports = script.exports_sync + +# 列出所有方法 +methods = sorted([x for x in dir(exports) if not x.startswith("_")]) +print(f"Total: {len(methods)}") +for m in methods: + print(f" {m}") + +# 直接调用ping +print(f"\n--- Test calls ---") +print(f"ping() = {exports.ping()}") + +# 尝试不同格式调用getProcessInfo +for name in ["getProcessInfo", "getprocessinfo", "get_process_info"]: + try: + fn = getattr(exports, name) + r = fn() + print(f"{name}() = OK: {str(r)[:80]}") + break + except Exception as e: + print(f"{name}() = FAIL: {e}") + +script.unload() +session.detach() +print("\nDone.")