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,366 @@
"""
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()