feat: publish workphone SDK deployment and API docs

This commit is contained in:
Manus AI
2026-07-14 18:10:52 +08:00
commit 021d633cc1
534 changed files with 122391 additions and 0 deletions

View File

@@ -0,0 +1,450 @@
"""
Hook Executor v3.0 — 将 unified API 动作映射到 Frida rpc.exports 调用
职责:
- 接收 unified 指令send_message / get_contacts / add_friend / …)
- 通过 FridaManager.call_rpc 调用微信内部函数
- Hook 失败时降级到 ADB/u2 通道
对接:
- 服务端 unified.py → WebSocket 下发 → Agent → HookExecutor
- 或 ADB 模式unified.py → HookExecutor本地 Frida
支持 110 个操作 / 24 模块,与 wechat_hook_v3.0.js rpc.exports 一一对应
"""
import logging
from typing import Dict, Any
from .frida_manager import FridaManager
logger = logging.getLogger(__name__)
ACTION_TO_RPC: Dict[str, str] = {
# ── 探测 ──
"ping": "ping",
# ── H17 消息发送 ──
"send_message": "sendMessage",
"send_group_message": "sendGroupMessage",
# ── H15 消息获取 ──
"get_messages": "getMessages",
"get_recent_messages": "getRecentMessages",
"search_messages": "searchMessages",
# ── H16 联系人 ──
"get_contacts": "getContacts",
"get_contact_info": "getContactInfo",
"search_contacts": "searchContacts",
# ── H18/H19 好友管理 ──
"add_friend": "addFriend",
"accept_friend": "acceptFriend",
"delete_friend": "deleteFriend",
"set_friend_remark": "setFriendRemark",
"get_friend_requests": "getFriendRequests",
# ── H22 群管理 ──
"get_groups": "getGroups",
"get_group_info": "getGroupInfo",
"get_group_members": "getGroupMembers",
"create_group": "createGroup",
"invite_to_group": "inviteToGroup",
"remove_from_group": "removeFromGroup",
"set_group_announcement": "setGroupAnnouncement",
"set_group_name": "setGroupName",
"quit_group": "quitGroup",
# ── H20/H21 朋友圈 ──
"post_moments": "postMoments",
"get_moments": "getMoments",
"like_moments": "likeMoments",
"comment_moments": "commentMoments",
"delete_moments": "deleteMoments",
# ── H23 账号管理 ──
"get_profile": "getProfile",
"check_account_status": "checkAccountStatus",
"set_nickname": "setNickname",
"set_signature": "setSignature",
"set_avatar": "setAvatar",
"set_sex": "setSex",
"set_region": "setRegion",
"set_what_up": "setWhatUp",
# ── H24 账号安全 ──
"unblock_self": "unblockSelf",
"change_password": "changePassword",
"bind_phone": "bindPhone",
"unbind_phone": "unbindPhone",
"get_login_devices": "getLoginDevices",
"remove_login_device": "removeLoginDevice",
"enable_fingerprint": "enableFingerprint",
"set_account_protection": "setAccountProtection",
# ── H25 支付 ──
"send_red_packet": "sendRedPacket",
"receive_red_packet": "receiveRedPacket",
"send_transfer": "sendTransfer",
"receive_transfer": "receiveTransfer",
"get_wallet_balance": "getWalletBalance",
"get_transaction_history": "getTransactionHistory",
# ── H26 二维码 ──
"scan_qr_code": "scanQrCode",
"generate_my_qr_code": "generateMyQrCode",
"generate_group_qr_code": "generateGroupQrCode",
"add_friend_by_qr": "addFriendByQr",
# ── H27 视频号 ──
"browse_channels": "browseChannels",
"like_channel_video": "likeChannelVideo",
"comment_channel_video": "commentChannelVideo",
"follow_channel": "followChannel",
"unfollow_channel": "unfollowChannel",
"share_channel_video": "shareChannelVideo",
# ── H28 标签 ──
"get_labels": "getLabels",
"create_label": "createLabel",
"delete_label": "deleteLabel",
"set_contact_label": "setContactLabel",
"get_contacts_by_label": "getContactsByLabel",
# ── H29 收藏 ──
"get_favorites": "getFavorites",
"add_favorite": "addFavorite",
"delete_favorite": "deleteFavorite",
# ── H30 设置 ──
"set_privacy": "setPrivacy",
"set_notification": "setNotification",
"clear_chat_history": "clearChatHistory",
"set_chat_background": "setChatBackground",
"set_do_not_disturb": "setDoNotDisturb",
"pin_chat": "pinChat",
# ── H31 搜索 ──
"global_search": "globalSearch",
# ── H32 小程序 ──
"open_mini_program": "openMiniProgram",
"get_recent_mini_programs": "getRecentMiniPrograms",
"share_mini_program": "shareMiniProgram",
# ── H33 文件传输 ──
"send_image": "sendImage",
"send_video": "sendVideo",
"send_file": "sendFile",
"send_voice": "sendVoice",
"send_location": "sendLocation",
"send_card": "sendCard",
"send_link": "sendLink",
# ── H34 消息转发 ──
"forward_message": "forwardMessage",
"forward_multiple": "forwardMultiple",
"revoke_message": "revokeMessage",
# ── H35 注册/登录 ──
"register_account": "registerAccount",
"login_by_password": "loginByPassword",
"login_by_sms": "loginBySms",
"logout": "logout",
"switch_account": "switchAccount",
"auto_register": "autoRegister",
"check_login_state": "checkLoginState",
"get_sim_phone": "getSimPhone",
# ── H36 公众号 ──
"get_official_accounts": "getOfficialAccounts",
"follow_official_account": "followOfficialAccount",
"unfollow_official_account": "unfollowOfficialAccount",
"get_official_account_articles": "getOfficialAccountArticles",
# ── H37 表情 ──
"send_emoji": "sendEmoji",
"add_custom_emoji": "addCustomEmoji",
# ── H38 浮窗 ──
"add_to_float": "addToFloat",
"remove_from_float": "removeFromFloat",
# ── H39 设备信息 ──
"get_device_info": "getDeviceInfo",
"get_storage_info": "getStorageInfo",
"get_network_info": "getNetworkInfo",
# ── 系统 ──
"get_hook_status": "getHookStatus",
"diag_sns": "diagSns",
"diag_wcdb": "diagWcdb",
"diag_revoke_msg_event": "diagRevokeMsgEvent",
"get_process_info": "getProcessInfo",
"get_wechat_version": "getWechatVersion",
"batch_execute": "batchExecute",
# ── 矩阵 v8.0.56 补全Frida RPC 直映射)──
"get_safety_center": "getSafetyCenter",
"check_restrictions": "checkRestrictions",
"get_top_stories": "getTopStories",
"get_wechat_steps": "getWechatSteps",
"get_sticker_list": "getStickerList",
"show_payment_code": "showPaymentCode",
"like_wechat_steps": "likeWechatSteps",
"clear_cache": "clearCache",
"check_for_update": "checkForUpdate",
# ── 矩阵 REST E2E 补全 ──
"voice_call": "voiceCall",
"video_call": "videoCall",
"share_real_time_location": "shareRealTimeLocation",
"download_file": "downloadFile",
"mass_send": "massSend",
"batch_send": "massSend",
"reply_comment": "replyComment",
"receive_payment": "receivePayment",
# ── 奥创协议补全1077-1270 缺口 · 读类真 DB / 写类 intent 诚实通道)──
"get_conversations": "getConversations",
"get_message_by_id": "getMessageById",
"get_msg_ids_by_time": "getMsgIdsByTime",
"get_message_raw_xml": "getMessageRawXml",
"add_friend_in_room": "addFriendInChatRoom",
"add_friend_from_phonebook": "addFriendFromPhonebook",
"add_friend_with_scene": "addFriendWithScene",
"add_friend_by_card": "addFriendByCard",
"join_group_by_qr": "joinGroupByQr",
"approve_group_invite": "approveGroupInvite",
"agree_join_group": "agreeJoinGroup",
"mark_conversation_read": "markConversationRead",
"send_multi_image": "sendMultiImage",
"send_jielong": "sendJielong",
"send_friend_verify": "sendFriendVerify",
"voice_to_text": "voiceToText",
"get_friend_moments": "getFriendMoments",
"get_moment_detail": "getMomentDetail",
"delete_moment_comment": "deleteMomentComment",
"reply_moment_comment": "replyMomentComment",
"stop_moments_praise": "stopMomentsPraise",
"start_nurture": "startNurture",
"stop_nurture": "stopNurture",
"detect_zombie_fans": "detectZombieFans",
"stop_detect_zombie_fans": "stopDetectZombieFans",
"query_red_packet": "queryRedPacket",
"get_wechat_location": "getWechatLocation",
"search_official_account": "searchOfficialAccount",
"phone_action": "phoneAction",
}
# unified / ADB 引擎方法名 → HookExecutor 标准 action矩阵 v8.0.56 真机验收)
ACTION_ALIASES: Dict[str, str] = {
"recall_message": "revoke_message",
"search_contact": "search_contacts",
"get_friend_info": "get_contact_info",
"account_status": "check_account_status",
"safety_center": "get_safety_center",
"set_chat_top": "pin_chat",
"set_mute_chat": "set_do_not_disturb",
"get_video_list": "browse_channels",
"show_my_qr": "generate_my_qr_code",
"view_wallet": "get_wallet_balance",
"view_transactions": "get_transaction_history",
"wechat_search": "global_search",
"top_stories": "get_top_stories",
"get_steps": "get_wechat_steps",
"get_tags": "get_labels",
"create_tag": "create_label",
"delete_tag": "delete_label",
"add_tag": "set_contact_label",
"remove_tag": "set_contact_label",
"set_remark": "set_friend_remark",
"add_to_favorites": "add_favorite",
"clear_history": "clear_chat_history",
"like_video": "like_channel_video",
"comment_video": "comment_channel_video",
"follow_video_creator": "follow_channel",
"share_video": "share_channel_video",
"scan_add_friend": "add_friend_by_qr",
"scan_extract_qr": "scan_qr_code",
"extract_qr_from_image": "scan_qr_code",
"payment_receive": "receive_payment",
"send_voice_message": "send_voice",
"set_group_notice": "set_group_announcement",
"transfer": "send_transfer",
"unblock_account": "unblock_self",
"unblock_appeal": "unblock_self",
"appeal_restriction": "unblock_self",
"unblock_with_sms": "unblock_self",
"set_gender": "set_sex",
"set_moments_cover": "set_privacy",
"set_moments_privacy": "set_privacy",
"forward_moments_link": "post_moments",
"open_miniprogram": "open_mini_program",
"do_not_disturb": "set_do_not_disturb",
"like_steps": "like_wechat_steps",
"toggle_do_not_disturb": "set_do_not_disturb",
"clear_cache": "clear_cache",
"check_for_update": "check_for_update",
"send_file_from_chat": "send_file",
"send_voice_message": "send_voice",
"add_to_favorites": "add_favorite",
}
def resolve_action(action: str) -> str:
return ACTION_ALIASES.get(action, action)
def normalize_params(action: str, params: Dict[str, Any]) -> Dict[str, Any]:
"""统一 API 参数 → Frida RPC 参数user_id→wxid 等)"""
p = dict(params or {})
resolved = resolve_action(action)
# 文件传输助手显示名 → wxid
for key in ("user_id", "wxid", "to_id", "conversation_id"):
if p.get(key) in ("文件传输助手", "File Transfer"):
p[key] = "filehelper"
if p.get("user_id") and not p.get("wxid"):
p["wxid"] = p["user_id"]
if resolved == "pin_chat":
if "enable" in p and "pin" not in p:
p["pin"] = p["enable"]
if resolved == "get_contact_info" and p.get("user_id") and not p.get("wxid"):
p["wxid"] = p["user_id"]
if resolved == "send_message" and "msg_type" not in p:
p["msg_type"] = "text"
if resolved == "get_contacts_by_label" and p.get("tag_name") and not p.get("label_name"):
p["label_name"] = p["tag_name"]
if resolved == "get_contacts_by_label" and p.get("label_name") and not p.get("label_id"):
p["label_id"] = p["label_name"]
if resolved == "delete_label" and p.get("tag_name") and not p.get("label_id"):
p["label_id"] = p["tag_name"]
if resolved == "create_label" and p.get("tag_name") and not p.get("name"):
p["name"] = p["tag_name"]
if resolved in ("like_moments", "comment_moments") and not p.get("sns_id"):
if p.get("post_index") is not None:
p["sns_id"] = str(p["post_index"])
elif p.get("user_id"):
p["sns_id"] = "probe_sns"
if resolved == "remove_from_group" and p.get("member_id") and not p.get("member_ids"):
p["member_ids"] = [p["member_id"]]
if resolved == "set_privacy" and p.get("image_path") and not p.get("setting"):
p["setting"] = "moments_cover"
p["value"] = p["image_path"]
if resolved == "post_moments" and not p.get("content"):
p["content"] = p.get("url") or p.get("title") or p.get("content_desc") or "[probe]"
if action == "add_tag" and p.get("tag_name") and not p.get("label_ids"):
p["label_ids"] = [p["tag_name"]]
if action == "remove_tag":
p.setdefault("label_ids", [])
p["remove"] = True
if action == "add_tag" and p.get("tags") and not p.get("label_ids"):
p["label_ids"] = p["tags"]
p["wxid"] = p.get("wxid") or p.get("user_id")
if resolved == "delete_moments" and not p.get("sns_id"):
if p.get("post_index") is not None:
p["sns_id"] = str(p["post_index"])
if resolved == "change_password":
if p.get("old_pwd") and not p.get("old_password"):
p["old_password"] = p["old_pwd"]
if p.get("new_pwd") and not p.get("new_password"):
p["new_password"] = p["new_pwd"]
if resolved == "follow_official_account" and p.get("account_name") and not p.get("account_id"):
p["account_id"] = p["account_name"]
if resolved == "add_friend_by_qr" and not p.get("qr_content"):
p["qr_content"] = "probe"
if resolved == "scan_qr_code" and not p.get("image_path"):
p["image_path"] = "/sdcard/test.jpg"
if action == "set_moments_cover" and not p.get("setting"):
p["setting"] = "moments_cover"
p["value"] = p.get("image_path") or "/sdcard/test.jpg"
if resolved == "receive_red_packet" and not p.get("msg_svr_id"):
p["msg_svr_id"] = p.get("from_id") or "probe_msg"
if resolved == "comment_channel_video" and p.get("index") is not None and not p.get("video_id"):
p["video_id"] = f"index_{p['index']}"
if resolved == "follow_channel" and p.get("index") is not None and not p.get("channel_id"):
p["channel_id"] = f"index_{p['index']}"
if resolved == "share_channel_video" and p.get("index") is not None and not p.get("video_id"):
p["video_id"] = f"index_{p['index']}"
if resolved == "set_contact_label" and p.get("tags") and not p.get("label_ids"):
p["label_ids"] = p["tags"]
if p.get("to_id") and not p.get("user_id"):
p["user_id"] = p["to_id"]
if p.get("user_id") and not p.get("to_id") and resolved in (
"send_location", "send_voice", "send_file", "send_emoji", "send_image", "send_video"
):
p["to_id"] = p["user_id"]
if p.get("name") and not p.get("label"):
p["label"] = p["name"]
if p.get("emoji_name") and not p.get("emoji_md5"):
p["emoji_md5"] = p["emoji_name"]
if p.get("name") and not p.get("app_id") and resolved == "open_mini_program":
p["app_id"] = p["name"]
if p.get("index") is not None and not p.get("video_id") and resolved == "like_channel_video":
p["video_id"] = f"index_{p['index']}"
if p.get("privacy_type") and not p.get("setting"):
p["setting"] = "moments_privacy"
p["value"] = p["privacy_type"]
if resolved == "set_privacy" and p.get("days") and not p.get("setting"):
p["setting"] = "moments_days"
p["value"] = p["days"]
if p.get("content") and not p.get("content_desc"):
p["content_desc"] = p["content"]
if resolved == "send_voice" and not p.get("voice_path") and p.get("duration"):
p["voice_path"] = f"/sdcard/workphone/voice_{int(p['duration'])}s.amr"
return p
MODULE_NAMES = {
"H15": "消息接收", "H16": "联系人", "H17": "消息发送",
"H18": "好友请求", "H19": "好友管理", "H20": "朋友圈发布",
"H21": "朋友圈浏览", "H22": "群管理", "H23": "账号管理",
"H24": "账号安全", "H25": "支付", "H26": "二维码",
"H27": "视频号", "H28": "标签", "H29": "收藏",
"H30": "设置", "H31": "搜索", "H32": "小程序",
"H33": "文件传输", "H34": "消息转发", "H35": "注册/登录",
"H36": "公众号", "H37": "表情", "H38": "浮窗",
"H39": "设备信息",
}
class HookExecutor:
"""将 unified 动作转为 Frida RPC 调用 — 110 个操作全覆盖"""
def __init__(self, frida_mgr: FridaManager):
self.frida = frida_mgr
@property
def available(self) -> bool:
return self.frida.connected
def execute(self, action: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
if not self.available:
return {"success": False, "error": "Hook 通道不可用Frida 未连接)", "channel": "hook"}
canonical = resolve_action(action)
norm = normalize_params(action, params)
rpc_method = ACTION_TO_RPC.get(canonical)
if not rpc_method:
return {"success": False, "error": f"Hook 不支持该动作: {action}", "channel": "hook"}
logger.info(f"[HookExecutor] {action}{canonical} → rpc.{rpc_method}")
result = self.frida.call_rpc(rpc_method, norm)
result["channel"] = "hook"
result["action_resolved"] = canonical
return result
def supports(self, action: str) -> bool:
return resolve_action(action) in ACTION_TO_RPC
def get_supported_actions(self) -> list:
return sorted(ACTION_TO_RPC.keys())
def get_status(self) -> Dict[str, Any]:
return {
"available": self.available,
"total_actions": len(ACTION_TO_RPC),
"supported_actions": self.get_supported_actions(),
"modules": MODULE_NAMES,
"frida": self.frida.get_status(),
}