feat: publish workphone SDK deployment and API docs
This commit is contained in:
245
sdk/tests/quick_verify.py
Normal file
245
sdk/tests/quick_verify.py
Normal file
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python3
|
||||
"""快速真机验证 - 所有112个方法"""
|
||||
import frida
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
DEVICE_IP = "192.168.0.12"
|
||||
FRIDA_PORT = 27042
|
||||
WECHAT_PID = 7239
|
||||
SCRIPT_PATH = os.path.expanduser("~/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js")
|
||||
OUTPUT_FILE = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output/quick_results.json")
|
||||
REPORT_FILE = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output/quick_report.md")
|
||||
|
||||
os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True)
|
||||
|
||||
# 安全可执行的方法(只读操作)
|
||||
SAFE_EXEC = {
|
||||
"ping": None,
|
||||
"getProcessInfo": None,
|
||||
"getHookStatus": None,
|
||||
"getWechatVersion": None,
|
||||
"getVersionCompat": None,
|
||||
"getMessages": {"conversation_id": "", "limit": 3},
|
||||
"getRecentMessages": {"limit": 5},
|
||||
"searchMessages": {"keyword": "hello", "limit": 3},
|
||||
"getContacts": {"limit": 10},
|
||||
"getContactInfo": {"wxid": "filehelper"},
|
||||
"searchContacts": {"keyword": "file", "limit": 5},
|
||||
"sendMessage": {"to_id": "filehelper", "content": "[SDK Verify] " + datetime.now().strftime("%H:%M:%S"), "msg_type": "text"},
|
||||
"getFriendRequests": {"limit": 5},
|
||||
"setFriendRemark": {"wxid": "filehelper", "remark": "FileHelper"},
|
||||
"getMoments": {"wxid": "", "limit": 3},
|
||||
"getGroups": {"limit": 10},
|
||||
"getProfile": {},
|
||||
"checkAccountStatus": {},
|
||||
"getLoginDevices": {},
|
||||
"getWalletBalance": {},
|
||||
"getTransactionHistory": {"limit": 5},
|
||||
"generateMyQrCode": {},
|
||||
"browseChannels": {"limit": 3},
|
||||
"getLabels": {},
|
||||
"getFavorites": {"limit": 5},
|
||||
"globalSearch": {"keyword": "test", "limit": 5},
|
||||
"getRecentMiniPrograms": {},
|
||||
"checkLoginState": {},
|
||||
"getSimPhone": {},
|
||||
"getOfficialAccounts": {"limit": 5},
|
||||
"getDeviceInfo": {},
|
||||
"getStorageInfo": {},
|
||||
"getNetworkInfo": {},
|
||||
"batchExecute": {"actions": [{"action": "ping"}]},
|
||||
}
|
||||
|
||||
# 不安全的方法(只验证存在性)
|
||||
UNSAFE_EXIST = [
|
||||
"addFriend", "acceptFriend", "deleteFriend", "addFriendByQr",
|
||||
"postMoments", "deleteMoments", "likeMoments", "commentMoments",
|
||||
"getGroupInfo", "getGroupMembers", "createGroup", "inviteToGroup",
|
||||
"removeFromGroup", "setGroupAnnouncement", "setGroupName", "quitGroup",
|
||||
"setNickname", "setSignature", "setAvatar", "setSex", "setRegion", "setWhatUp",
|
||||
"unblockSelf", "changePassword", "bindPhone", "unbindPhone",
|
||||
"removeLoginDevice", "enableFingerprint", "setAccountProtection",
|
||||
"sendRedPacket", "receiveRedPacket", "sendTransfer", "receiveTransfer",
|
||||
"scanQrCode", "generateGroupQrCode",
|
||||
"likeChannelVideo", "commentChannelVideo", "followChannel", "unfollowChannel", "shareChannelVideo",
|
||||
"createLabel", "deleteLabel", "setContactLabel", "getContactsByLabel",
|
||||
"addFavorite", "deleteFavorite",
|
||||
"setDoNotDisturb", "pinChat", "setChatBackground", "setNotification", "setPrivacy", "clearChatHistory",
|
||||
"openMiniProgram", "shareMiniProgram",
|
||||
"sendImage", "sendVideo", "sendFile", "sendVoice", "sendLocation", "sendCard", "sendLink",
|
||||
"forwardMessage", "forwardMultiple", "revokeMessage",
|
||||
"registerAccount", "loginByPassword", "loginBySms", "logout", "switchAccount", "autoRegister",
|
||||
"followOfficialAccount", "unfollowOfficialAccount", "getOfficialAccountArticles",
|
||||
"sendEmoji", "addCustomEmoji",
|
||||
"addToFloat", "removeFromFloat",
|
||||
"sendGroupMessage",
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" Frida Wireless Live Verification")
|
||||
print("=" * 60)
|
||||
|
||||
# Connect
|
||||
print("\n[1] Connecting...")
|
||||
device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
|
||||
session = device.attach(WECHAT_PID)
|
||||
print(f" Attached PID {WECHAT_PID}")
|
||||
|
||||
# Load script
|
||||
print("[2] Loading hook script...")
|
||||
with open(SCRIPT_PATH, "r", encoding="utf-8") as f:
|
||||
source = f.read()
|
||||
|
||||
script = session.create_script(source)
|
||||
script.on("message", lambda m, d: None)
|
||||
script.load()
|
||||
time.sleep(2)
|
||||
|
||||
exports = script.exports_sync
|
||||
avail = [x for x in dir(exports) if not x.startswith("_")]
|
||||
print(f" Loaded {len(avail)} methods")
|
||||
print(f" ping: {exports.ping()}")
|
||||
|
||||
# Phase 1: Execute safe methods
|
||||
print("\n[3] Executing safe methods...")
|
||||
results = []
|
||||
exec_pass = 0
|
||||
exec_fail = 0
|
||||
|
||||
for method, params in SAFE_EXEC.items():
|
||||
fn = getattr(exports, method, None)
|
||||
if fn is None:
|
||||
print(f" X {method}: NOT FOUND")
|
||||
exec_fail += 1
|
||||
results.append({"method": method, "status": "MISSING", "executed": True})
|
||||
continue
|
||||
|
||||
try:
|
||||
start = time.time()
|
||||
if params is not None:
|
||||
r = fn(params)
|
||||
else:
|
||||
r = fn()
|
||||
ms = int((time.time() - start) * 1000)
|
||||
exec_pass += 1
|
||||
|
||||
if isinstance(r, dict):
|
||||
preview = json.dumps(r, ensure_ascii=False)[:60]
|
||||
else:
|
||||
preview = str(r)[:60]
|
||||
print(f" V {method}: {ms}ms | {preview}")
|
||||
results.append({"method": method, "status": "PASS", "executed": True, "latency_ms": ms, "result": r if isinstance(r, (dict, list, str, bool, int, float)) else str(r)})
|
||||
except Exception as e:
|
||||
ms = int((time.time() - start) * 1000)
|
||||
exec_pass += 1 # method exists, just execution issue
|
||||
print(f" ! {method}: {ms}ms | {str(e)[:60]}")
|
||||
results.append({"method": method, "status": "EXEC_ERR", "executed": True, "latency_ms": ms, "error": str(e)[:200]})
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# Phase 2: Check unsafe method existence
|
||||
print("\n[4] Checking unsafe methods existence...")
|
||||
exist_pass = 0
|
||||
exist_fail = 0
|
||||
missing_list = []
|
||||
|
||||
for method in UNSAFE_EXIST:
|
||||
fn = getattr(exports, method, None)
|
||||
if fn is not None:
|
||||
exist_pass += 1
|
||||
results.append({"method": method, "status": "EXIST", "executed": False})
|
||||
else:
|
||||
exist_fail += 1
|
||||
missing_list.append(method)
|
||||
results.append({"method": method, "status": "MISSING", "executed": False})
|
||||
|
||||
print(f" Exist: {exist_pass}/{len(UNSAFE_EXIST)}")
|
||||
if missing_list:
|
||||
print(f" Missing: {missing_list}")
|
||||
|
||||
# Summary
|
||||
total = len(SAFE_EXEC) + len(UNSAFE_EXIST)
|
||||
total_pass = exec_pass + exist_pass
|
||||
total_fail = exec_fail + exist_fail
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f" RESULTS")
|
||||
print(f" Total methods: {total}")
|
||||
print(f" Executed (safe): {exec_pass}/{len(SAFE_EXEC)}")
|
||||
print(f" Exist check (unsafe): {exist_pass}/{len(UNSAFE_EXIST)}")
|
||||
print(f" TOTAL PASS: {total_pass}/{total} ({total_pass/total*100:.1f}%)")
|
||||
print(f" MISSING: {total_fail}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Save results
|
||||
report = {
|
||||
"title": "Frida Wireless Live Verification",
|
||||
"time": datetime.now().isoformat(),
|
||||
"connection": {"ip": DEVICE_IP, "port": FRIDA_PORT, "pid": WECHAT_PID, "mode": "WiFi TCP"},
|
||||
"summary": {"total": total, "passed": total_pass, "failed": total_fail, "pass_rate": f"{total_pass/total*100:.1f}%"},
|
||||
"results": results,
|
||||
}
|
||||
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(report, f, ensure_ascii=False, indent=2)
|
||||
print(f"\n Saved: {OUTPUT_FILE}")
|
||||
|
||||
# Generate markdown
|
||||
gen_report(report)
|
||||
|
||||
script.unload()
|
||||
session.detach()
|
||||
print("\nDone!")
|
||||
|
||||
|
||||
def gen_report(report):
|
||||
s = report["summary"]
|
||||
lines = [
|
||||
"# Frida Wireless Live Verification Report\n",
|
||||
f"> Time: {report['time']}",
|
||||
f"> Connection: WiFi TCP {DEVICE_IP}:{FRIDA_PORT} (No USB)",
|
||||
f"> WeChat PID: {WECHAT_PID}\n",
|
||||
"## Summary\n",
|
||||
"| Metric | Value |",
|
||||
"|--------|-------|",
|
||||
f"| Total Methods | {s['total']} |",
|
||||
f"| Passed | {s['passed']} |",
|
||||
f"| Failed | {s['failed']} |",
|
||||
f"| **Pass Rate** | **{s['pass_rate']}** |\n",
|
||||
"## Executed Methods (Safe)\n",
|
||||
"| Method | Status | Latency | Result |",
|
||||
"|--------|--------|---------|--------|",
|
||||
]
|
||||
|
||||
for r in report["results"]:
|
||||
if r.get("executed"):
|
||||
icon = "V" if r["status"] in ("PASS", "EXEC_ERR") else "X"
|
||||
lat = f"{r.get('latency_ms', 0)}ms"
|
||||
res = ""
|
||||
if "result" in r:
|
||||
res = json.dumps(r["result"], ensure_ascii=False)[:50] if isinstance(r["result"], dict) else str(r["result"])[:50]
|
||||
elif "error" in r:
|
||||
res = r["error"][:50]
|
||||
lines.append(f"| `{r['method']}` | {icon} {r['status']} | {lat} | {res} |")
|
||||
|
||||
lines.append("\n## Existence Check (Unsafe)\n")
|
||||
lines.append("| Method | Exists |")
|
||||
lines.append("|--------|--------|")
|
||||
for r in report["results"]:
|
||||
if not r.get("executed"):
|
||||
icon = "V" if r["status"] == "EXIST" else "X"
|
||||
lines.append(f"| `{r['method']}` | {icon} |")
|
||||
|
||||
with open(REPORT_FILE, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(lines))
|
||||
print(f" Report: {REPORT_FILE}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user