""" Frida 无线桥接器 — 连接 FridaManager 与 WebSocket 服务端 ======================================================= 核心职责: - 通过 WiFi TCP 连接远程 Frida(替代 USB) - 将服务端 WebSocket 指令转换为 Frida RPC 调用 - 将 Frida Hook 事件通过 WebSocket 上报服务端 - 管理 Frida 会话生命周期(连接/断开/重连) 数据流: 服务端 API → WebSocket → Agent → WirelessBridge → FridaManager → Frida RPC → 微信 微信 → Frida Hook Event → FridaManager → WirelessBridge → WebSocket → 服务端 技术栈:Frida 16.5.6 + asyncio + WebSocket """ import os import json import time import logging import asyncio import threading from typing import Optional, Dict, Any, Callable, List from datetime import datetime logger = logging.getLogger(__name__) # 导入同级模块 from .frida_manager import FridaManager, ConnectionMode from .hook_executor import HookExecutor, ACTION_TO_RPC, MODULE_NAMES from .event_reporter import EventReporter class WirelessBridge: """ 无线桥接器 — 将 WebSocket 指令转为 Frida RPC 调用 这是 Agent 端的核心组件,负责: 1. 初始化 Frida 无线连接(remote 模式,通过 WiFi TCP) 2. 接收 WebSocket 指令并执行对应的 Frida RPC 3. 将 Hook 事件回传给 WebSocket 4. 维护连接健康状态 """ def __init__( self, device_ip: str = "127.0.0.1", frida_port: int = 27042, mode: ConnectionMode = "remote", script_path: Optional[str] = None, ws_send_fn: Optional[Callable] = None, ): self.device_ip = device_ip self.frida_port = frida_port self.mode = mode # 默认脚本路径 if script_path is None: script_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "wechat_hook_v2.js" ) # 事件上报器 self.event_reporter = EventReporter() if ws_send_fn: self.event_reporter.set_send_fn(ws_send_fn) # Frida 管理器(使用 remote 模式通过 WiFi 连接) self.frida_mgr = FridaManager( mode=mode, gadget_host=device_ip, gadget_port=frida_port, script_path=script_path, on_event=self.event_reporter.on_hook_event, on_detach=self._on_frida_detach, auto_reconnect=True, reconnect_interval=5.0, ) # Hook 执行器 self.hook_executor = HookExecutor(self.frida_mgr) # 状态 self._connected = False self._stats = { "commands_received": 0, "commands_success": 0, "commands_failed": 0, "events_sent": 0, "connect_time": None, "last_command_time": None, } # ---- 连接管理 ---- def connect(self) -> Dict[str, Any]: """ 建立 Frida 无线连接 流程: 1. 通过 WiFi TCP 连接远程 frida-server 2. attach 到微信进程 3. 加载 Hook 脚本 4. 验证 RPC 可用性 """ logger.info(f"正在连接 Frida: {self.device_ip}:{self.frida_port} (模式={self.mode})") try: success = self.frida_mgr.start() if not success: return { "success": False, "error": "Frida 连接失败", "device_ip": self.device_ip, "frida_port": self.frida_port, } self._connected = True self._stats["connect_time"] = datetime.now().isoformat() # 验证 RPC ping_result = self.frida_mgr.call_rpc("ping") rpc_ok = ping_result.get("success", False) logger.info(f"✅ Frida 无线连接成功 | RPC: {'OK' if rpc_ok else 'FAIL'}") return { "success": True, "device_ip": self.device_ip, "frida_port": self.frida_port, "mode": self.mode, "rpc_available": rpc_ok, "ping": ping_result.get("data", ""), "supported_actions": len(ACTION_TO_RPC), "modules": list(MODULE_NAMES.values()), } except Exception as e: self._connected = False logger.error(f"Frida 连接异常: {e}") return {"success": False, "error": str(e)} def disconnect(self): """断开 Frida 连接""" self.frida_mgr.stop() self._connected = False logger.info("Frida 无线连接已断开") @property def connected(self) -> bool: return self._connected and self.frida_mgr.connected # ---- 指令执行 ---- def execute_command(self, command: Dict[str, Any]) -> Dict[str, Any]: """ 执行 WebSocket 指令 指令格式: { "type": "command", "action": "send_message", "params": {"to_id": "xxx", "content": "hello"}, "command_id": "cmd_123" } 返回: { "success": true/false, "command_id": "cmd_123", "channel": "hook", "data": {...} } """ self._stats["commands_received"] += 1 self._stats["last_command_time"] = datetime.now().isoformat() action = command.get("action", "") params = command.get("params", {}) command_id = command.get("command_id", "") if not action: self._stats["commands_failed"] += 1 return { "success": False, "error": "缺少 action", "command_id": command_id, } if not self.connected: self._stats["commands_failed"] += 1 return { "success": False, "error": "Frida 未连接", "command_id": command_id, } # 通过 HookExecutor 执行 result = self.hook_executor.execute(action, params) result["command_id"] = command_id if result.get("success"): self._stats["commands_success"] += 1 else: self._stats["commands_failed"] += 1 return result def execute_batch(self, commands: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """批量执行指令""" results = [] for cmd in commands: result = self.execute_command(cmd) results.append(result) return results # ---- WebSocket 消息处理 ---- def handle_ws_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]: """ 处理来自 WebSocket 的消息 消息类型: - command: 执行 Frida RPC 指令 - query: 查询状态 - ping: 心跳 - batch: 批量执行 """ msg_type = message.get("type", "") if msg_type == "command": return self.execute_command(message) elif msg_type == "batch": commands = message.get("commands", []) return { "success": True, "type": "batch_result", "results": self.execute_batch(commands), "total": len(commands), } elif msg_type == "query": query_type = message.get("query", "status") if query_type == "status": return self.get_status() elif query_type == "actions": return { "success": True, "actions": self.hook_executor.get_supported_actions(), "total": len(ACTION_TO_RPC), } elif query_type == "modules": return { "success": True, "modules": MODULE_NAMES, } else: return {"success": False, "error": f"未知查询类型: {query_type}"} elif msg_type == "ping": return { "type": "pong", "connected": self.connected, "timestamp": datetime.now().isoformat(), } else: return {"success": False, "error": f"未知消息类型: {msg_type}"} # ---- 事件回调 ---- def _on_frida_detach(self, reason: str): """Frida 会话断开回调""" self._connected = False logger.warning(f"Frida 会话断开: {reason}") # 通知服务端 if self.event_reporter._send_fn: try: self.event_reporter._send_fn({ "type": "frida_disconnected", "reason": reason, "timestamp": datetime.now().isoformat(), }) except Exception: pass def set_ws_send_fn(self, fn: Callable): """设置 WebSocket 发送函数""" self.event_reporter.set_send_fn(fn) # ---- 状态查询 ---- def get_status(self) -> Dict[str, Any]: """获取桥接器完整状态""" return { "success": True, "bridge": { "connected": self.connected, "device_ip": self.device_ip, "frida_port": self.frida_port, "mode": self.mode, "stats": self._stats, }, "frida": self.frida_mgr.get_status(), "hook": self.hook_executor.get_status() if self.connected else { "available": False, "total_actions": len(ACTION_TO_RPC), }, "events": self.event_reporter.get_stats(), } def get_supported_actions(self) -> Dict[str, Any]: """获取支持的所有操作""" return { "total": len(ACTION_TO_RPC), "modules": MODULE_NAMES, "actions": { module: [ action for action, rpc in ACTION_TO_RPC.items() # 简单按模块前缀分组 ] for module in MODULE_NAMES.keys() }, "action_list": sorted(ACTION_TO_RPC.keys()), } # ============================================================ # § 4 异步包装器(用于 FastAPI / asyncio 环境) # ============================================================ class AsyncWirelessBridge: """ 异步包装器 — 将同步的 WirelessBridge 包装为 async 接口 用于 FastAPI WebSocket 端点 """ def __init__(self, bridge: WirelessBridge): self._bridge = bridge self._loop = None async def connect(self) -> Dict[str, Any]: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, self._bridge.connect) async def disconnect(self): loop = asyncio.get_event_loop() return await loop.run_in_executor(None, self._bridge.disconnect) async def execute_command(self, command: Dict[str, Any]) -> Dict[str, Any]: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, self._bridge.execute_command, command) async def handle_ws_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, self._bridge.handle_ws_message, message) @property def connected(self) -> bool: return self._bridge.connected def get_status(self) -> Dict[str, Any]: return self._bridge.get_status()