feat(hook): 扩展 ACTION_TO_RPC 映射与 wechat_hook_v2 RPC 实现

同步 agent/app 双端 hook_executor 动作表与 wechat_hook_v2.js exports;
补充 hook 子脚本与 _hook_smoke 冒烟入口,更新 device_modules 探测结果。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Manus AI
2026-05-24 10:02:00 +08:00
parent 355ed279be
commit 68b4d8f7aa
12 changed files with 1824 additions and 276 deletions

View File

@@ -177,8 +177,118 @@ ACTION_TO_RPC: Dict[str, str] = {
"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",
}
# 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",
"set_remark": "set_friend_remark",
"add_to_favorites": "add_favorite",
"clear_history": "clear_chat_history",
"like_video": "like_channel_video",
"send_voice_message": "send_voice",
"set_group_notice": "set_group_announcement",
"transfer": "send_transfer",
"receive_payment": "receive_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 == "create_label" and p.get("tag_name") and not p.get("name"):
p["name"] = p["tag_name"]
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": "朋友圈发布",
@@ -206,17 +316,20 @@ class HookExecutor:
if not self.available:
return {"success": False, "error": "Hook 通道不可用Frida 未连接)", "channel": "hook"}
rpc_method = ACTION_TO_RPC.get(action)
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} → rpc.{rpc_method}")
result = self.frida.call_rpc(rpc_method, params or {})
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 action in ACTION_TO_RPC
return resolve_action(action) in ACTION_TO_RPC
def get_supported_actions(self) -> list:
return sorted(ACTION_TO_RPC.keys())