""" 无线 Agent 增强模块 — 为 WorkPhoneAgent 添加 WiFi Frida 支持 ============================================================= 功能: - 自动检测 Root 状态,选择最佳 Frida 连接模式 - 通过 WiFi TCP 连接 Frida(无需 USB) - 集成 WirelessBridge 处理服务端指令 - 向服务端注册 Frida 连接信息 - 支持 frida-server(Root)和 frida-gadget(免Root) 使用方式: 在 agent.py 的 __init__ 中调用 init_wireless() 或在 Termux 中设置环境变量: WP_FRIDA_MODE=remote WP_FRIDA_HOST=127.0.0.1 WP_FRIDA_PORT=27042 @author 卡若 @version 1.0.0 """ import os import json import time import logging import subprocess import asyncio from typing import Optional, Dict, Any logger = logging.getLogger(__name__) class WirelessAgentMixin: """ 无线 Agent 混入类 将此类混入 WorkPhoneAgent,为其添加 WiFi Frida 支持。 新增属性: - wireless_bridge: WirelessBridge 实例 - frida_mode: 当前 Frida 连接模式 - frida_host: Frida 服务地址 - frida_port: Frida 服务端口 """ def init_wireless(self): """ 初始化无线 Frida 连接 优先级: 1. 环境变量 WP_FRIDA_MODE / WP_FRIDA_HOST / WP_FRIDA_PORT 2. 自动检测 Root → remote 模式 3. 非 Root → gadget 模式 """ self.frida_mode = os.environ.get("WP_FRIDA_MODE", "auto").strip().lower() self.frida_host = os.environ.get("WP_FRIDA_HOST", "127.0.0.1").strip() self.frida_port = int(os.environ.get("WP_FRIDA_PORT", "27042")) # 自动检测模式 if self.frida_mode == "auto": if self._check_root(): self.frida_mode = "remote" logger.info("✅ Root 检测通过 → 使用 remote 模式(frida-server)") else: self.frida_mode = "gadget" logger.info("⚠️ 无 Root → 使用 gadget 模式(frida-gadget)") # 确保 frida-server 正在运行(Root 模式) if self.frida_mode == "remote": self._ensure_frida_server_running() # 初始化 FridaManager(使用 WiFi 模式) self._init_frida_wireless() # 初始化 WirelessBridge self._init_wireless_bridge() logger.info(f"📡 无线 Frida 初始化完成: {self.frida_mode} ({self.frida_host}:{self.frida_port})") def _check_root(self) -> bool: """检查是否有 Root 权限""" try: result = subprocess.run( ["su", "-c", "id"], capture_output=True, text=True, timeout=5, ) return "uid=0" in result.stdout except Exception: return False def _ensure_frida_server_running(self): """确保 frida-server 正在运行""" try: # 检查端口是否已监听 result = subprocess.run( ["su", "-c", f"netstat -tlnp 2>/dev/null | grep :{self.frida_port}"], capture_output=True, text=True, timeout=5, shell=True, ) if str(self.frida_port) in result.stdout: logger.info(f"frida-server 已在运行 (端口 {self.frida_port})") return # 尝试启动 frida_bin = os.path.expanduser("~/.frida-server/frida-server") if not os.path.exists(frida_bin): # 查找其他可能的路径 for path in [ "/data/local/tmp/frida-server", "/data/local/tmp/app_process64", os.path.expanduser("~/.frida-server/app_process64"), ]: if os.path.exists(path): frida_bin = path break if os.path.exists(frida_bin): subprocess.Popen( ["su", "-c", f"nohup {frida_bin} -l 0.0.0.0:{self.frida_port} &"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) time.sleep(2) logger.info(f"frida-server 已启动 (端口 {self.frida_port})") else: logger.warning("frida-server 二进制文件不存在,请先执行部署脚本") except Exception as e: logger.warning(f"frida-server 启动检查失败: {e}") def _init_frida_wireless(self): """初始化 Frida Manager(无线模式)""" try: from hook.frida_manager import FridaManager # 如果已有 frida_mgr 且已连接,跳过 if hasattr(self, 'frida_mgr') and self.frida_mgr and self.frida_mgr.connected: logger.info("Frida 已连接,跳过重新初始化") return self.frida_mgr = FridaManager( mode=self.frida_mode, gadget_host=self.frida_host, gadget_port=self.frida_port, on_event=self._on_frida_event if hasattr(self, '_on_frida_event') else None, auto_reconnect=True, reconnect_interval=5.0, ) started = self.frida_mgr.start() if started: logger.info(f"🔗 Frida 无线连接成功 ({self.frida_mode})") else: logger.warning("⚠️ Frida 无线连接失败") self.frida_mgr = None except ImportError: logger.info("Frida 模块未安装") self.frida_mgr = None except Exception as e: logger.warning(f"Frida 无线初始化异常: {e}") self.frida_mgr = None def _init_wireless_bridge(self): """初始化 WirelessBridge""" try: from hook.wireless_bridge import WirelessBridge ws_send_fn = None if hasattr(self, 'ws') and self.ws: async def _ws_send(data): if self.ws and self.connected: await self.ws.send(json.dumps(data)) ws_send_fn = _ws_send self.wireless_bridge = WirelessBridge( device_ip=self.frida_host, frida_port=self.frida_port, mode=self.frida_mode, ws_send_fn=ws_send_fn, ) # 如果 frida_mgr 已连接,直接使用 if self.frida_mgr and self.frida_mgr.connected: self.wireless_bridge.frida_mgr = self.frida_mgr self.wireless_bridge._connected = True logger.info("WirelessBridge 已初始化") except ImportError: logger.info("WirelessBridge 模块未安装") self.wireless_bridge = None except Exception as e: logger.warning(f"WirelessBridge 初始化异常: {e}") self.wireless_bridge = None async def handle_wireless_command(self, data: dict) -> Optional[dict]: """ 处理无线 Frida 指令 在 _handle_message 中调用,处理 type=command 的消息。 """ if not hasattr(self, 'wireless_bridge') or not self.wireless_bridge: return None msg_type = data.get("type", "") if msg_type == "command": action = data.get("action", "") # 特殊指令:Frida 连接管理 if action == "frida_connect": params = data.get("params", {}) self.frida_host = params.get("ip", self.frida_host) self.frida_port = params.get("port", self.frida_port) self.frida_mode = params.get("mode", self.frida_mode) self._init_frida_wireless() self._init_wireless_bridge() return { "success": self.frida_mgr is not None and self.frida_mgr.connected, "command_id": data.get("command_id"), "wechat_attached": getattr(self.frida_mgr, 'connected', False), "supported_actions": 112, } elif action == "frida_disconnect": if self.frida_mgr: self.frida_mgr.stop() if self.wireless_bridge: self.wireless_bridge.disconnect() return { "success": True, "command_id": data.get("command_id"), } # 普通 Frida RPC 指令 return self.wireless_bridge.execute_command(data) elif msg_type in ("query", "ping", "batch"): return self.wireless_bridge.handle_ws_message(data) return None async def register_frida_to_server(self): """向服务器注册 Frida 连接信息""" if not hasattr(self, 'ws') or not self.ws or not self.connected: return try: device_ip = self._get_device_ip() register_data = { "type": "event", "event": "frida_registered", "data": { "device_id": self.device_id, "ip": device_ip, "frida_port": self.frida_port, "mode": self.frida_mode, "frida_connected": self.frida_mgr is not None and self.frida_mgr.connected, "android_version": self._get_prop("ro.build.version.release"), "device_model": self._get_prop("ro.product.model"), }, } await self.ws.send(json.dumps(register_data)) logger.info(f"已向服务器注册 Frida 信息: {device_ip}:{self.frida_port}") except Exception as e: logger.warning(f"Frida 注册失败: {e}") def _get_device_ip(self) -> str: """获取设备 WiFi IP""" try: result = subprocess.run( ["ip", "route", "get", "1"], capture_output=True, text=True, timeout=5, ) parts = result.stdout.strip().split() for i, p in enumerate(parts): if p == "src" and i + 1 < len(parts): return parts[i + 1] except Exception: pass try: import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except Exception: return "127.0.0.1" def _get_prop(self, prop: str) -> str: """获取 Android 系统属性""" try: result = subprocess.run( ["getprop", prop], capture_output=True, text=True, timeout=5, ) return result.stdout.strip() except Exception: return ""