97 lines
3.5 KiB
Python
97 lines
3.5 KiB
Python
"""宝塔侧 Frida RPC 桥。
|
||
|
||
手机通过认证的反向隧道暴露 frida-server,SDK 容器在本模块中持有真正的
|
||
Frida Session 和 JS RPC。Android 系统控制仍走手机 WebSocket,两条通道互不抢占。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import logging
|
||
import threading
|
||
from typing import Any, Dict
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class ServerFridaBridge:
|
||
def __init__(self) -> None:
|
||
self._manager = None
|
||
self._executor = None
|
||
self._lock = threading.RLock()
|
||
|
||
@staticmethod
|
||
def _settings():
|
||
from config import settings
|
||
return settings
|
||
|
||
def enabled_for(self, device_id: str) -> bool:
|
||
settings = self._settings()
|
||
if not bool(getattr(settings, "SERVER_FRIDA_ENABLED", False)):
|
||
return False
|
||
expected = str(getattr(settings, "SERVER_FRIDA_DEVICE_ID", "") or "").strip()
|
||
return not expected or expected == device_id
|
||
|
||
def _connect(self) -> bool:
|
||
with self._lock:
|
||
if self._manager and getattr(self._manager, "connected", False):
|
||
return True
|
||
|
||
from hook.frida_manager import FridaManager
|
||
from hook.hook_executor import HookExecutor
|
||
|
||
settings = self._settings()
|
||
manager = FridaManager(
|
||
mode="remote",
|
||
gadget_host=str(getattr(settings, "SERVER_FRIDA_HOST", "172.18.0.1")),
|
||
gadget_port=int(getattr(settings, "SERVER_FRIDA_PORT", 14538)),
|
||
use_adb_forward=False,
|
||
auto_reconnect=True,
|
||
)
|
||
if not manager.start():
|
||
logger.warning("宝塔 Frida 桥连接失败")
|
||
return False
|
||
self._manager = manager
|
||
self._executor = HookExecutor(manager)
|
||
logger.info("宝塔 Frida 桥已附着微信并加载 RPC")
|
||
return True
|
||
|
||
def execute_sync(self, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
try:
|
||
if not self._connect():
|
||
return {
|
||
"success": False,
|
||
"code": 503,
|
||
"error": "宝塔 Frida 桥不可用,请检查手机反向隧道和微信进程",
|
||
"_channel_used": "server/frida/offline",
|
||
}
|
||
result = self._executor.execute(action, params)
|
||
result.setdefault("code", 200 if result.get("success") else 500)
|
||
result["_channel_used"] = "server/frida"
|
||
result["transport"] = "frida_reverse_tunnel"
|
||
return result
|
||
except Exception as exc:
|
||
logger.exception("宝塔 Frida RPC 执行异常")
|
||
return {
|
||
"success": False,
|
||
"code": 500,
|
||
"error": str(exc),
|
||
"_channel_used": "server/frida/error",
|
||
}
|
||
|
||
async def execute(self, device_id: str, action: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
if not self.enabled_for(device_id):
|
||
return {"success": False, "code": 503, "error": "宝塔 Frida 桥未启用"}
|
||
return await asyncio.to_thread(self.execute_sync, action, params)
|
||
|
||
def status(self) -> Dict[str, Any]:
|
||
connected = bool(self._manager and getattr(self._manager, "connected", False))
|
||
return {
|
||
"enabled": bool(getattr(self._settings(), "SERVER_FRIDA_ENABLED", False)),
|
||
"connected": connected,
|
||
"manager": self._manager.get_status() if connected else None,
|
||
}
|
||
|
||
|
||
server_frida_bridge = ServerFridaBridge()
|