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

301
sdk/agent/wireless_agent.py Normal file
View File

@@ -0,0 +1,301 @@
"""
无线 Agent 增强模块 — 为 WorkPhoneAgent 添加 WiFi Frida 支持
=============================================================
功能:
- 自动检测 Root 状态,选择最佳 Frida 连接模式
- 通过 WiFi TCP 连接 Frida无需 USB
- 集成 WirelessBridge 处理服务端指令
- 向服务端注册 Frida 连接信息
- 支持 frida-serverRoot和 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 ""