diff --git a/工作手机SDK补全包_微信Frida/README.md b/工作手机SDK补全包_微信Frida/README.md new file mode 100644 index 0000000000..be61b149ad --- /dev/null +++ b/工作手机SDK补全包_微信Frida/README.md @@ -0,0 +1,26 @@ +# 工作手机 SDK · 微信 Frida 无线控制补全包 + +本包用于承接上传 ZIP 中断的开发进度,补齐 **服务器 → WebSocket → 手机端 Agent → Frida → 微信 Hook** 的闭环。 + +## 快速启动 + +服务器端: + +```bash +pip install -r requirements.txt +uvicorn server.app:app --host 0.0.0.0 --port 8000 +``` + +手机 Termux 端: + +```bash +python mobile_agent/wireless_agent.py --server-ws ws://服务器IP:8000/ws/phone --hook hooks/wechat_hook_bridge.js +``` + +验证端: + +```bash +python scripts/verify_wechat_frida.py --base-url http://服务器IP:8000 --device-id phone-xxxx +``` + +如果已有完整 `wechat_hook_v3.js`,请替换 `hooks/wechat_hook_bridge.js`,SDK 会自动兼容 camelCase / snake_case / lowercase 方法名。 diff --git a/工作手机SDK补全包_微信Frida/generate_impl.py b/工作手机SDK补全包_微信Frida/generate_impl.py new file mode 100644 index 0000000000..63f3879cfb --- /dev/null +++ b/工作手机SDK补全包_微信Frida/generate_impl.py @@ -0,0 +1,578 @@ +from pathlib import Path + +ROOT = Path('/home/ubuntu/work_phone_sdk_completion') +files = {} + +files['sdk/frida/frida_manager.py'] = r'''""" +工作手机 SDK · FridaManager + +负责手机端或服务器端连接 Frida、attach 微信进程、加载 Hook 脚本,并提供兼容 RPC 调用。 +设计重点:历史归档显示 Frida Python 对 rpc.exports 方法名存在混淆,因此这里采用多候选名兼容策略。 +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from pathlib import Path +import re +import time +from typing import Any, Callable, Dict, Iterable, Optional + + +class FridaUnavailable(RuntimeError): + """当前环境未安装或无法连接 Frida。""" + + +class RpcMethodMissing(RuntimeError): + """Hook 中缺少指定 RPC 方法。""" + + +@dataclass +class FridaConfig: + device_host: str = "127.0.0.1" + device_port: int = 27042 + package_name: str = "com.tencent.mm" + process_name: str = "WeChat" + attach_timeout: float = 15.0 + prefer_usb: bool = False + hook_path: Optional[str] = None + + +@dataclass +class RpcResult: + ok: bool + action: str + method: str + data: Any = None + error: Optional[str] = None + elapsed_ms: int = 0 + + +class FridaManager: + def __init__(self, config: Optional[FridaConfig] = None): + self.config = config or FridaConfig() + self.frida = None + self.device = None + self.session = None + self.script = None + self.exports = None + self.loaded_hook_path: Optional[str] = None + + def _import_frida(self): + if self.frida is not None: + return self.frida + try: + import frida # type: ignore + except Exception as exc: + raise FridaUnavailable(f"未安装 frida Python 包或加载失败:{exc}") from exc + self.frida = frida + return frida + + def connect(self): + frida = self._import_frida() + if self.config.prefer_usb: + self.device = frida.get_usb_device(timeout=int(self.config.attach_timeout)) + else: + self.device = frida.get_device_manager().add_remote_device( + f"{self.config.device_host}:{self.config.device_port}" + ) + return self.device + + def attach(self, target: Optional[str] = None): + if self.device is None: + self.connect() + assert self.device is not None + target = target or self.config.package_name + last_error: Optional[Exception] = None + candidates: Iterable[Any] = [target, self.config.process_name] + for candidate in candidates: + try: + self.session = self.device.attach(candidate) + return self.session + except Exception as exc: # pragma: no cover - depends on phone + last_error = exc + try: + processes = self.device.enumerate_processes() + for proc in processes: + name = getattr(proc, 'name', '') or '' + pid = getattr(proc, 'pid', None) + if pid and (self.config.package_name in name or self.config.process_name.lower() in name.lower() or '微信' in name): + self.session = self.device.attach(pid) + return self.session + except Exception as exc: # pragma: no cover + last_error = exc + raise FridaUnavailable(f"无法 attach 微信进程:{last_error}") + + def load_script(self, hook_path: Optional[str] = None): + path = Path(hook_path or self.config.hook_path or '') + if not path.exists(): + raise FileNotFoundError(f"Hook 脚本不存在:{path}") + if self.session is None: + self.attach() + assert self.session is not None + source = path.read_text(encoding='utf-8') + self.script = self.session.create_script(source) + self.script.on('message', self._on_message) + self.script.load() + self.exports = getattr(self.script, 'exports_sync', None) or getattr(self.script, 'exports', None) + self.loaded_hook_path = str(path) + return self.script + + def _on_message(self, message, data): # pragma: no cover - runtime callback + # 生产环境可转发到 EventReporter;这里保持最小日志。 + print({'frida_message': message, 'data_len': len(data) if data else 0}) + + @staticmethod + def _snake(name: str) -> str: + return re.sub(r'(? RpcResult: + started = time.time() + if self.exports is None: + raise FridaUnavailable('Hook 脚本尚未加载,无法调用 RPC') + last_error = None + for candidate in self.method_candidates(method): + try: + fn = getattr(self.exports, candidate) + data = fn(*args, **kwargs) + return RpcResult(True, method, candidate, data=data, elapsed_ms=int((time.time() - started) * 1000)) + except AttributeError as exc: + last_error = exc + except Exception as exc: + return RpcResult(False, method, candidate, error=str(exc), elapsed_ms=int((time.time() - started) * 1000)) + # 兼容新版 frida 的 exports_sync.invoke 或 script.exports_sync.invoke + try: + invoke = getattr(self.exports, 'invoke') + data = invoke(method, list(args), kwargs or None) + return RpcResult(True, method, 'invoke', data=data, elapsed_ms=int((time.time() - started) * 1000)) + except Exception as exc: + last_error = exc + return RpcResult(False, method, '', error=f'RPC 方法不存在或不可调用:{method}; last={last_error}', elapsed_ms=int((time.time() - started) * 1000)) + + def cleanup(self): + for obj, method in [(self.script, 'unload'), (self.session, 'detach')]: + if obj is not None: + try: + getattr(obj, method)() + except Exception: + pass + self.script = None + self.session = None + self.exports = None +''' + +files['sdk/wechat/wechat_actions.py'] = r'''"""微信动作目录:服务器、手机端 Agent、验证脚本共同使用。""" +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Any, Dict, List + +@dataclass(frozen=True) +class WechatAction: + action: str + rpc: str + module: str + description: str + required: List[str] = field(default_factory=list) + safe: bool = True + verify: bool = True + +ACTIONS: List[WechatAction] = [ + WechatAction('ping', 'ping', 'system', 'Hook 连通性检测'), + WechatAction('get_connection_status', 'getConnectionStatus', 'system', '读取连接状态'), + WechatAction('screenshot', 'takeScreenshot', 'device', '手机当前屏幕截图'), + WechatAction('ui_dump', 'dumpUiTree', 'device', '导出当前 UI 树'), + WechatAction('go_home', 'goHome', 'navigation', '返回微信首页'), + WechatAction('open_contacts', 'openContacts', 'navigation', '打开通讯录'), + WechatAction('open_chats', 'openChats', 'navigation', '打开聊天列表'), + WechatAction('search_contact', 'searchContact', 'contact', '搜索联系人', ['keyword']), + WechatAction('get_contacts', 'getContacts', 'contact', '读取联系人列表'), + WechatAction('get_contact_profile', 'getContactProfile', 'contact', '读取联系人资料', ['wxid']), + WechatAction('add_friend', 'addFriend', 'contact', '添加好友', ['keyword'], safe=False), + WechatAction('accept_friend', 'acceptFriend', 'contact', '通过好友申请', ['wxid'], safe=False), + WechatAction('send_text', 'sendTextMessage', 'message', '发送文本消息', ['to', 'text'], safe=False), + WechatAction('send_image', 'sendImageMessage', 'message', '发送图片消息', ['to', 'path'], safe=False), + WechatAction('get_messages', 'getMessages', 'message', '读取消息列表', ['wxid']), + WechatAction('open_chat', 'openChat', 'message', '打开指定会话', ['wxid']), + WechatAction('create_group', 'createGroup', 'group', '创建群聊', ['members'], safe=False), + WechatAction('invite_group_member', 'inviteGroupMember', 'group', '邀请群成员', ['chatroom', 'members'], safe=False), + WechatAction('get_group_members', 'getGroupMembers', 'group', '读取群成员', ['chatroom']), + WechatAction('browse_channels', 'browseChannels', 'channels', '浏览视频号'), + WechatAction('add_favorite', 'addFavorite', 'favorite', '收藏当前内容'), + WechatAction('add_custom_emoji', 'addCustomEmoji', 'emoji', '添加自定义表情', safe=False), +] + +ACTION_MAP: Dict[str, WechatAction] = {item.action: item for item in ACTIONS} +RPC_MAP: Dict[str, WechatAction] = {item.rpc: item for item in ACTIONS} + +def list_actions() -> List[Dict[str, Any]]: + return [item.__dict__.copy() for item in ACTIONS] + +def validate_action(action: str, payload: Dict[str, Any]) -> WechatAction: + if action not in ACTION_MAP: + raise KeyError(f'未知微信动作:{action}') + spec = ACTION_MAP[action] + missing = [k for k in spec.required if k not in payload or payload[k] in (None, '')] + if missing: + raise ValueError(f'动作 {action} 缺少参数:{missing}') + return spec +''' + +files['sdk/frida/hook_executor.py'] = r'''"""将业务 action 映射到 Frida RPC。""" +from __future__ import annotations +from dataclasses import asdict +from typing import Any, Dict, Optional + +from sdk.frida.frida_manager import FridaManager, RpcResult +from sdk.wechat.wechat_actions import validate_action, list_actions + +class HookExecutor: + def __init__(self, manager: FridaManager): + self.manager = manager + + def execute(self, action: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + payload = payload or {} + try: + spec = validate_action(action, payload) + except Exception as exc: + return {'ok': False, 'action': action, 'error': str(exc), 'stage': 'validate'} + args = [payload[k] for k in spec.required] + # 允许额外参数透传给 Hook;Hook 若不支持会返回错误,便于报告定位。 + extra = {k: v for k, v in payload.items() if k not in spec.required} + result: RpcResult = self.manager.call(spec.rpc, *args, **extra) + return { + 'ok': result.ok, + 'action': action, + 'rpc': spec.rpc, + 'method_used': result.method, + 'module': spec.module, + 'description': spec.description, + 'safe': spec.safe, + 'data': result.data, + 'error': result.error, + 'elapsed_ms': result.elapsed_ms, + } + + def catalog(self): + return list_actions() +''' + +files['mobile_agent/wireless_agent.py'] = r'''"""手机端 Termux/Python Agent:通过 WebSocket 接收服务器动作,调用 Frida Hook。""" +from __future__ import annotations +import argparse +import asyncio +import json +import platform +import uuid +from pathlib import Path + +from sdk.frida.frida_manager import FridaConfig, FridaManager +from sdk.frida.hook_executor import HookExecutor + +try: + import websockets +except Exception: # pragma: no cover + websockets = None + +class WirelessAgent: + def __init__(self, server_ws: str, hook_path: str, device_id: str | None = None): + self.server_ws = server_ws + self.hook_path = hook_path + self.device_id = device_id or f"phone-{uuid.getnode():x}" + self.manager = FridaManager(FridaConfig(hook_path=hook_path)) + self.executor = HookExecutor(self.manager) + + async def boot(self): + self.manager.connect() + self.manager.attach() + self.manager.load_script(self.hook_path) + + def hello(self): + return { + 'type': 'hello', + 'device_id': self.device_id, + 'platform': platform.platform(), + 'actions': self.executor.catalog(), + } + + async def run(self): + if websockets is None: + raise RuntimeError('请先安装 websockets:pip install websockets') + await self.boot() + async with websockets.connect(self.server_ws, ping_interval=20, ping_timeout=20) as ws: + await ws.send(json.dumps(self.hello(), ensure_ascii=False)) + async for raw in ws: + try: + msg = json.loads(raw) + if msg.get('type') != 'command': + continue + result = self.executor.execute(msg['action'], msg.get('payload') or {}) + await ws.send(json.dumps({'type': 'result', 'request_id': msg.get('request_id'), 'device_id': self.device_id, 'result': result}, ensure_ascii=False)) + except Exception as exc: + await ws.send(json.dumps({'type': 'error', 'error': str(exc)}, ensure_ascii=False)) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--server-ws', required=True, help='例如 ws://192.168.1.10:8000/ws/phone') + parser.add_argument('--hook', required=True, help='wechat_hook_bridge.js 或真实 wechat_hook_v3.js 路径') + parser.add_argument('--device-id') + args = parser.parse_args() + asyncio.run(WirelessAgent(args.server_ws, args.hook, args.device_id).run()) + +if __name__ == '__main__': + main() +''' + +files['server/routes/wechat_frida.py'] = r'''"""FastAPI 微信 Frida 无线控制路由。""" +from __future__ import annotations +import asyncio +import json +import uuid +from dataclasses import dataclass, field +from typing import Any, Dict, Optional + +from fastapi import APIRouter, WebSocket, WebSocketDisconnect, HTTPException +from pydantic import BaseModel + +from sdk.wechat.wechat_actions import list_actions + +router = APIRouter(prefix='/api/v3/wechat-frida', tags=['wechat-frida']) + +@dataclass +class PhoneConn: + device_id: str + ws: WebSocket + meta: Dict[str, Any] = field(default_factory=dict) + pending: Dict[str, asyncio.Future] = field(default_factory=dict) + +phones: Dict[str, PhoneConn] = {} + +class ActionRequest(BaseModel): + device_id: str + action: str + payload: Dict[str, Any] = {} + timeout: float = 30.0 + +@router.get('/actions') +def actions(): + return {'ok': True, 'actions': list_actions()} + +@router.get('/devices') +def devices(): + return {'ok': True, 'devices': [{'device_id': k, 'meta': v.meta} for k, v in phones.items()]} + +@router.post('/execute') +async def execute(req: ActionRequest): + if req.device_id not in phones: + raise HTTPException(404, f'设备未连接:{req.device_id}') + conn = phones[req.device_id] + request_id = uuid.uuid4().hex + fut = asyncio.get_event_loop().create_future() + conn.pending[request_id] = fut + await conn.ws.send_text(json.dumps({'type': 'command', 'request_id': request_id, 'action': req.action, 'payload': req.payload}, ensure_ascii=False)) + try: + return await asyncio.wait_for(fut, timeout=req.timeout) + finally: + conn.pending.pop(request_id, None) + +async def phone_socket(websocket: WebSocket): + await websocket.accept() + device_id: Optional[str] = None + try: + async for raw in websocket.iter_text(): + msg = json.loads(raw) + if msg.get('type') == 'hello': + device_id = msg.get('device_id') or uuid.uuid4().hex + phones[device_id] = PhoneConn(device_id=device_id, ws=websocket, meta=msg) + await websocket.send_text(json.dumps({'type': 'hello_ack', 'device_id': device_id}, ensure_ascii=False)) + elif msg.get('type') == 'result': + rid = msg.get('request_id') + did = msg.get('device_id') or device_id + conn = phones.get(did or '') + if conn and rid in conn.pending and not conn.pending[rid].done(): + conn.pending[rid].set_result({'ok': True, 'device_id': did, 'result': msg.get('result')}) + elif msg.get('type') == 'error': + # 保留连接,错误由客户端下一次请求再显式返回。 + pass + except WebSocketDisconnect: + pass + finally: + if device_id and device_id in phones: + phones.pop(device_id, None) +''' + +files['server/app.py'] = r'''from fastapi import FastAPI, WebSocket +from server.routes.wechat_frida import router as wechat_frida_router, phone_socket + +app = FastAPI(title='工作手机 SDK · 微信 Frida 控制服务', version='0.3.0') +app.include_router(wechat_frida_router) + +@app.get('/health') +def health(): + return {'ok': True, 'service': 'work-phone-sdk'} + +@app.websocket('/ws/phone') +async def ws_phone(websocket: WebSocket): + await phone_socket(websocket) +''' + +files['hooks/wechat_hook_bridge.js'] = r'''// 工作手机 SDK · 微信 Hook 桥接模板 +// 说明:这是安全桥接版,优先提供连通性、截图、UI 导航等通用方法。 +// 如果已有完整 wechat_hook_v3.js,可直接替换本文件;Python SDK 会兼容 camelCase/snake_case 调用。 +'use strict'; + +function ok(data) { return { ok: true, data: data || null, ts: Date.now() }; } +function fail(message) { return { ok: false, error: String(message), ts: Date.now() }; } + +function runJava(fn) { + let result; + Java.perform(function () { + try { result = fn(); } catch (e) { result = fail(e.stack || e.message || e); } + }); + return result; +} + +rpc.exports = { + ping: function () { return 'pong from wechat_hook_bridge'; }, + getConnectionStatus: function () { + return ok({ java_available: Java.available, process: Process.id, arch: Process.arch, platform: Process.platform }); + }, + takeScreenshot: function () { + // 截图建议在手机端通过 uiautomator/screencap 实现;Hook 层返回占位,Agent 可扩展真实文件路径。 + return ok({ mode: 'placeholder', message: '请在 mobile_agent 扩展 screencap -p 后回传文件路径' }); + }, + dumpUiTree: function () { return ok({ mode: 'placeholder', message: '建议通过 uiautomator dump 获取 UI XML' }); }, + goHome: function () { return ok({ action: 'goHome', message: '桥接模板未执行 UI 点击;请替换完整 Hook 后验证' }); }, + openContacts: function () { return ok({ action: 'openContacts', message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + openChats: function () { return ok({ action: 'openChats', message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + searchContact: function (keyword) { return ok({ keyword: keyword, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + getContacts: function () { return ok({ contacts: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + getContactProfile: function (wxid) { return ok({ wxid: wxid, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + addFriend: function (keyword) { return fail('危险动作未在模板中实现:addFriend ' + keyword); }, + acceptFriend: function (wxid) { return fail('危险动作未在模板中实现:acceptFriend ' + wxid); }, + sendTextMessage: function (to, text) { return fail('危险动作未在模板中实现:sendTextMessage ' + to); }, + sendImageMessage: function (to, path) { return fail('危险动作未在模板中实现:sendImageMessage ' + to); }, + getMessages: function (wxid) { return ok({ wxid: wxid, messages: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + openChat: function (wxid) { return ok({ wxid: wxid, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + createGroup: function (members) { return fail('危险动作未在模板中实现:createGroup'); }, + inviteGroupMember: function (chatroom, members) { return fail('危险动作未在模板中实现:inviteGroupMember'); }, + getGroupMembers: function (chatroom) { return ok({ chatroom: chatroom, members: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + browseChannels: function () { return ok({ message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + addFavorite: function () { return fail('危险动作未在模板中实现:addFavorite'); }, + addCustomEmoji: function () { return fail('危险动作未在模板中实现:addCustomEmoji'); } +}; +''' + +files['scripts/verify_wechat_frida.py'] = r'''"""一键验证服务器控制手机微信功能,输出 JSON 与 Markdown 报告。""" +from __future__ import annotations +import argparse +import json +import time +from pathlib import Path +from typing import Any, Dict + +import requests + +from sdk.wechat.wechat_actions import ACTIONS + +DEFAULT_PAYLOADS: Dict[str, Dict[str, Any]] = { + 'search_contact': {'keyword': '文件传输助手'}, + 'get_contact_profile': {'wxid': 'filehelper'}, + 'add_friend': {'keyword': '__dry_run__'}, + 'accept_friend': {'wxid': '__dry_run__'}, + 'send_text': {'to': 'filehelper', 'text': '工作手机SDK自动验证'}, + 'send_image': {'to': 'filehelper', 'path': '/sdcard/Pictures/test.png'}, + 'get_messages': {'wxid': 'filehelper'}, + 'open_chat': {'wxid': 'filehelper'}, + 'create_group': {'members': ['filehelper']}, + 'invite_group_member': {'chatroom': '__dry_run__', 'members': ['filehelper']}, + 'get_group_members': {'chatroom': '__dry_run__'}, +} + +def call(base_url: str, device_id: str, action: str, payload: Dict[str, Any], timeout: float): + r = requests.post(f'{base_url}/api/v3/wechat-frida/execute', json={'device_id': device_id, 'action': action, 'payload': payload, 'timeout': timeout}, timeout=timeout + 5) + try: + return r.status_code, r.json() + except Exception: + return r.status_code, {'ok': False, 'text': r.text} + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--base-url', default='http://127.0.0.1:8000') + parser.add_argument('--device-id', required=True) + parser.add_argument('--out-dir', default='reports') + parser.add_argument('--include-dangerous', action='store_true', help='默认不执行加好友/发消息等危险动作,只记录 SKIPPED') + parser.add_argument('--timeout', type=float, default=30) + args = parser.parse_args() + + out_dir = Path(args.out_dir) + out_dir.mkdir(parents=True, exist_ok=True) + rows = [] + for spec in ACTIONS: + if not spec.safe and not args.include_dangerous: + rows.append({'action': spec.action, 'rpc': spec.rpc, 'ok': None, 'status': 'SKIPPED_DANGEROUS', 'description': spec.description}) + continue + status, data = call(args.base_url, args.device_id, spec.action, DEFAULT_PAYLOADS.get(spec.action, {}), args.timeout) + rows.append({'action': spec.action, 'rpc': spec.rpc, 'http_status': status, 'ok': bool(data.get('ok') and (data.get('result') or {}).get('ok', True)), 'response': data, 'description': spec.description}) + time.sleep(0.2) + + stamp = time.strftime('%Y%m%d_%H%M%S') + json_path = out_dir / f'wechat_frida_verify_{stamp}.json' + md_path = out_dir / f'wechat_frida_verify_{stamp}.md' + json_path.write_text(json.dumps(rows, ensure_ascii=False, indent=2), encoding='utf-8') + lines = ['# 微信 Frida 功能验证报告', '', f'- 时间:{time.strftime("%Y-%m-%d %H:%M:%S")}', f'- 设备:`{args.device_id}`', '', '| 动作 | RPC | 结果 | 说明 |', '|---|---|---|---|'] + for row in rows: + result = 'SKIP' if row.get('status') else ('PASS' if row.get('ok') else 'FAIL') + lines.append(f"| `{row['action']}` | `{row['rpc']}` | {result} | {row.get('description','')} |") + md_path.write_text('\n'.join(lines) + '\n', encoding='utf-8') + print(json_path) + print(md_path) + +if __name__ == '__main__': + main() +''' + +files['requirements.txt'] = 'fastapi\nuvicorn\nrequests\nwebsockets\nfrida\n\n' +files['README.md'] = r'''# 工作手机 SDK · 微信 Frida 无线控制补全包 + +本包用于承接上传 ZIP 中断的开发进度,补齐 **服务器 → WebSocket → 手机端 Agent → Frida → 微信 Hook** 的闭环。 + +## 快速启动 + +服务器端: + +```bash +pip install -r requirements.txt +uvicorn server.app:app --host 0.0.0.0 --port 8000 +``` + +手机 Termux 端: + +```bash +python mobile_agent/wireless_agent.py --server-ws ws://服务器IP:8000/ws/phone --hook hooks/wechat_hook_bridge.js +``` + +验证端: + +```bash +python scripts/verify_wechat_frida.py --base-url http://服务器IP:8000 --device-id phone-xxxx +``` + +如果已有完整 `wechat_hook_v3.js`,请替换 `hooks/wechat_hook_bridge.js`,SDK 会自动兼容 camelCase / snake_case / lowercase 方法名。 +''' + +for rel, content in files.items(): + path = ROOT / rel + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding='utf-8') +print(f'generated {len(files)} files under {ROOT}') diff --git a/工作手机SDK补全包_微信Frida/hooks/wechat_hook_bridge.js b/工作手机SDK补全包_微信Frida/hooks/wechat_hook_bridge.js new file mode 100644 index 0000000000..d69423fa67 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/hooks/wechat_hook_bridge.js @@ -0,0 +1,45 @@ +// 工作手机 SDK · 微信 Hook 桥接模板 +// 说明:这是安全桥接版,优先提供连通性、截图、UI 导航等通用方法。 +// 如果已有完整 wechat_hook_v3.js,可直接替换本文件;Python SDK 会兼容 camelCase/snake_case 调用。 +'use strict'; + +function ok(data) { return { ok: true, data: data || null, ts: Date.now() }; } +function fail(message) { return { ok: false, error: String(message), ts: Date.now() }; } + +function runJava(fn) { + let result; + Java.perform(function () { + try { result = fn(); } catch (e) { result = fail(e.stack || e.message || e); } + }); + return result; +} + +rpc.exports = { + ping: function () { return 'pong from wechat_hook_bridge'; }, + getConnectionStatus: function () { + return ok({ java_available: Java.available, process: Process.id, arch: Process.arch, platform: Process.platform }); + }, + takeScreenshot: function () { + // 截图建议在手机端通过 uiautomator/screencap 实现;Hook 层返回占位,Agent 可扩展真实文件路径。 + return ok({ mode: 'placeholder', message: '请在 mobile_agent 扩展 screencap -p 后回传文件路径' }); + }, + dumpUiTree: function () { return ok({ mode: 'placeholder', message: '建议通过 uiautomator dump 获取 UI XML' }); }, + goHome: function () { return ok({ action: 'goHome', message: '桥接模板未执行 UI 点击;请替换完整 Hook 后验证' }); }, + openContacts: function () { return ok({ action: 'openContacts', message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + openChats: function () { return ok({ action: 'openChats', message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + searchContact: function (keyword) { return ok({ keyword: keyword, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + getContacts: function () { return ok({ contacts: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + getContactProfile: function (wxid) { return ok({ wxid: wxid, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + addFriend: function (keyword) { return fail('危险动作未在模板中实现:addFriend ' + keyword); }, + acceptFriend: function (wxid) { return fail('危险动作未在模板中实现:acceptFriend ' + wxid); }, + sendTextMessage: function (to, text) { return fail('危险动作未在模板中实现:sendTextMessage ' + to); }, + sendImageMessage: function (to, path) { return fail('危险动作未在模板中实现:sendImageMessage ' + to); }, + getMessages: function (wxid) { return ok({ wxid: wxid, messages: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + openChat: function (wxid) { return ok({ wxid: wxid, message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + createGroup: function (members) { return fail('危险动作未在模板中实现:createGroup'); }, + inviteGroupMember: function (chatroom, members) { return fail('危险动作未在模板中实现:inviteGroupMember'); }, + getGroupMembers: function (chatroom) { return ok({ chatroom: chatroom, members: [], message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + browseChannels: function () { return ok({ message: 'NOT_IMPLEMENTED_IN_TEMPLATE' }); }, + addFavorite: function () { return fail('危险动作未在模板中实现:addFavorite'); }, + addCustomEmoji: function () { return fail('危险动作未在模板中实现:addCustomEmoji'); } +}; diff --git a/工作手机SDK补全包_微信Frida/mobile_agent/__init__.py b/工作手机SDK补全包_微信Frida/mobile_agent/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/mobile_agent/wireless_agent.py b/工作手机SDK补全包_微信Frida/mobile_agent/wireless_agent.py new file mode 100644 index 0000000000..594bd01f44 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/mobile_agent/wireless_agent.py @@ -0,0 +1,65 @@ +"""手机端 Termux/Python Agent:通过 WebSocket 接收服务器动作,调用 Frida Hook。""" +from __future__ import annotations +import argparse +import asyncio +import json +import platform +import uuid +from pathlib import Path + +from sdk.frida.frida_manager import FridaConfig, FridaManager +from sdk.frida.hook_executor import HookExecutor + +try: + import websockets +except Exception: # pragma: no cover + websockets = None + +class WirelessAgent: + def __init__(self, server_ws: str, hook_path: str, device_id: str | None = None): + self.server_ws = server_ws + self.hook_path = hook_path + self.device_id = device_id or f"phone-{uuid.getnode():x}" + self.manager = FridaManager(FridaConfig(hook_path=hook_path)) + self.executor = HookExecutor(self.manager) + + async def boot(self): + self.manager.connect() + self.manager.attach() + self.manager.load_script(self.hook_path) + + def hello(self): + return { + 'type': 'hello', + 'device_id': self.device_id, + 'platform': platform.platform(), + 'actions': self.executor.catalog(), + } + + async def run(self): + if websockets is None: + raise RuntimeError('请先安装 websockets:pip install websockets') + await self.boot() + async with websockets.connect(self.server_ws, ping_interval=20, ping_timeout=20) as ws: + await ws.send(json.dumps(self.hello(), ensure_ascii=False)) + async for raw in ws: + try: + msg = json.loads(raw) + if msg.get('type') != 'command': + continue + result = self.executor.execute(msg['action'], msg.get('payload') or {}) + await ws.send(json.dumps({'type': 'result', 'request_id': msg.get('request_id'), 'device_id': self.device_id, 'result': result}, ensure_ascii=False)) + except Exception as exc: + await ws.send(json.dumps({'type': 'error', 'error': str(exc)}, ensure_ascii=False)) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--server-ws', required=True, help='例如 ws://192.168.1.10:8000/ws/phone') + parser.add_argument('--hook', required=True, help='wechat_hook_bridge.js 或真实 wechat_hook_v3.js 路径') + parser.add_argument('--device-id') + args = parser.parse_args() + asyncio.run(WirelessAgent(args.server_ws, args.hook, args.device_id).run()) + +if __name__ == '__main__': + main() diff --git a/工作手机SDK补全包_微信Frida/requirements.txt b/工作手机SDK补全包_微信Frida/requirements.txt new file mode 100644 index 0000000000..b52d28ca18 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/requirements.txt @@ -0,0 +1,6 @@ +fastapi +uvicorn +requests +websockets +frida + diff --git a/工作手机SDK补全包_微信Frida/sdk/__init__.py b/工作手机SDK补全包_微信Frida/sdk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/sdk/frida/__init__.py b/工作手机SDK补全包_微信Frida/sdk/frida/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/sdk/frida/frida_manager.py b/工作手机SDK补全包_微信Frida/sdk/frida/frida_manager.py new file mode 100644 index 0000000000..9ec11f589a --- /dev/null +++ b/工作手机SDK补全包_微信Frida/sdk/frida/frida_manager.py @@ -0,0 +1,166 @@ +""" +工作手机 SDK · FridaManager + +负责手机端或服务器端连接 Frida、attach 微信进程、加载 Hook 脚本,并提供兼容 RPC 调用。 +设计重点:历史归档显示 Frida Python 对 rpc.exports 方法名存在混淆,因此这里采用多候选名兼容策略。 +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from pathlib import Path +import re +import time +from typing import Any, Callable, Dict, Iterable, Optional + + +class FridaUnavailable(RuntimeError): + """当前环境未安装或无法连接 Frida。""" + + +class RpcMethodMissing(RuntimeError): + """Hook 中缺少指定 RPC 方法。""" + + +@dataclass +class FridaConfig: + device_host: str = "127.0.0.1" + device_port: int = 27042 + package_name: str = "com.tencent.mm" + process_name: str = "WeChat" + attach_timeout: float = 15.0 + prefer_usb: bool = False + hook_path: Optional[str] = None + + +@dataclass +class RpcResult: + ok: bool + action: str + method: str + data: Any = None + error: Optional[str] = None + elapsed_ms: int = 0 + + +class FridaManager: + def __init__(self, config: Optional[FridaConfig] = None): + self.config = config or FridaConfig() + self.frida = None + self.device = None + self.session = None + self.script = None + self.exports = None + self.loaded_hook_path: Optional[str] = None + + def _import_frida(self): + if self.frida is not None: + return self.frida + try: + import frida # type: ignore + except Exception as exc: + raise FridaUnavailable(f"未安装 frida Python 包或加载失败:{exc}") from exc + self.frida = frida + return frida + + def connect(self): + frida = self._import_frida() + if self.config.prefer_usb: + self.device = frida.get_usb_device(timeout=int(self.config.attach_timeout)) + else: + self.device = frida.get_device_manager().add_remote_device( + f"{self.config.device_host}:{self.config.device_port}" + ) + return self.device + + def attach(self, target: Optional[str] = None): + if self.device is None: + self.connect() + assert self.device is not None + target = target or self.config.package_name + last_error: Optional[Exception] = None + candidates: Iterable[Any] = [target, self.config.process_name] + for candidate in candidates: + try: + self.session = self.device.attach(candidate) + return self.session + except Exception as exc: # pragma: no cover - depends on phone + last_error = exc + try: + processes = self.device.enumerate_processes() + for proc in processes: + name = getattr(proc, 'name', '') or '' + pid = getattr(proc, 'pid', None) + if pid and (self.config.package_name in name or self.config.process_name.lower() in name.lower() or '微信' in name): + self.session = self.device.attach(pid) + return self.session + except Exception as exc: # pragma: no cover + last_error = exc + raise FridaUnavailable(f"无法 attach 微信进程:{last_error}") + + def load_script(self, hook_path: Optional[str] = None): + path = Path(hook_path or self.config.hook_path or '') + if not path.exists(): + raise FileNotFoundError(f"Hook 脚本不存在:{path}") + if self.session is None: + self.attach() + assert self.session is not None + source = path.read_text(encoding='utf-8') + self.script = self.session.create_script(source) + self.script.on('message', self._on_message) + self.script.load() + self.exports = getattr(self.script, 'exports_sync', None) or getattr(self.script, 'exports', None) + self.loaded_hook_path = str(path) + return self.script + + def _on_message(self, message, data): # pragma: no cover - runtime callback + # 生产环境可转发到 EventReporter;这里保持最小日志。 + print({'frida_message': message, 'data_len': len(data) if data else 0}) + + @staticmethod + def _snake(name: str) -> str: + return re.sub(r'(? RpcResult: + started = time.time() + if self.exports is None: + raise FridaUnavailable('Hook 脚本尚未加载,无法调用 RPC') + last_error = None + for candidate in self.method_candidates(method): + try: + fn = getattr(self.exports, candidate) + data = fn(*args, **kwargs) + return RpcResult(True, method, candidate, data=data, elapsed_ms=int((time.time() - started) * 1000)) + except AttributeError as exc: + last_error = exc + except Exception as exc: + return RpcResult(False, method, candidate, error=str(exc), elapsed_ms=int((time.time() - started) * 1000)) + # 兼容新版 frida 的 exports_sync.invoke 或 script.exports_sync.invoke + try: + invoke = getattr(self.exports, 'invoke') + data = invoke(method, list(args), kwargs or None) + return RpcResult(True, method, 'invoke', data=data, elapsed_ms=int((time.time() - started) * 1000)) + except Exception as exc: + last_error = exc + return RpcResult(False, method, '', error=f'RPC 方法不存在或不可调用:{method}; last={last_error}', elapsed_ms=int((time.time() - started) * 1000)) + + def cleanup(self): + for obj, method in [(self.script, 'unload'), (self.session, 'detach')]: + if obj is not None: + try: + getattr(obj, method)() + except Exception: + pass + self.script = None + self.session = None + self.exports = None diff --git a/工作手机SDK补全包_微信Frida/sdk/frida/hook_executor.py b/工作手机SDK补全包_微信Frida/sdk/frida/hook_executor.py new file mode 100644 index 0000000000..da046d0d35 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/sdk/frida/hook_executor.py @@ -0,0 +1,37 @@ +"""将业务 action 映射到 Frida RPC。""" +from __future__ import annotations +from dataclasses import asdict +from typing import Any, Dict, Optional + +from sdk.frida.frida_manager import FridaManager, RpcResult +from sdk.wechat.wechat_actions import validate_action, list_actions + +class HookExecutor: + def __init__(self, manager: FridaManager): + self.manager = manager + + def execute(self, action: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + payload = payload or {} + try: + spec = validate_action(action, payload) + except Exception as exc: + return {'ok': False, 'action': action, 'error': str(exc), 'stage': 'validate'} + args = [payload[k] for k in spec.required] + # 允许额外参数透传给 Hook;Hook 若不支持会返回错误,便于报告定位。 + extra = {k: v for k, v in payload.items() if k not in spec.required} + result: RpcResult = self.manager.call(spec.rpc, *args, **extra) + return { + 'ok': result.ok, + 'action': action, + 'rpc': spec.rpc, + 'method_used': result.method, + 'module': spec.module, + 'description': spec.description, + 'safe': spec.safe, + 'data': result.data, + 'error': result.error, + 'elapsed_ms': result.elapsed_ms, + } + + def catalog(self): + return list_actions() diff --git a/工作手机SDK补全包_微信Frida/sdk/wechat/__init__.py b/工作手机SDK补全包_微信Frida/sdk/wechat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/sdk/wechat/wechat_actions.py b/工作手机SDK补全包_微信Frida/sdk/wechat/wechat_actions.py new file mode 100644 index 0000000000..c405fbb39a --- /dev/null +++ b/工作手机SDK补全包_微信Frida/sdk/wechat/wechat_actions.py @@ -0,0 +1,54 @@ +"""微信动作目录:服务器、手机端 Agent、验证脚本共同使用。""" +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Any, Dict, List + +@dataclass(frozen=True) +class WechatAction: + action: str + rpc: str + module: str + description: str + required: List[str] = field(default_factory=list) + safe: bool = True + verify: bool = True + +ACTIONS: List[WechatAction] = [ + WechatAction('ping', 'ping', 'system', 'Hook 连通性检测'), + WechatAction('get_connection_status', 'getConnectionStatus', 'system', '读取连接状态'), + WechatAction('screenshot', 'takeScreenshot', 'device', '手机当前屏幕截图'), + WechatAction('ui_dump', 'dumpUiTree', 'device', '导出当前 UI 树'), + WechatAction('go_home', 'goHome', 'navigation', '返回微信首页'), + WechatAction('open_contacts', 'openContacts', 'navigation', '打开通讯录'), + WechatAction('open_chats', 'openChats', 'navigation', '打开聊天列表'), + WechatAction('search_contact', 'searchContact', 'contact', '搜索联系人', ['keyword']), + WechatAction('get_contacts', 'getContacts', 'contact', '读取联系人列表'), + WechatAction('get_contact_profile', 'getContactProfile', 'contact', '读取联系人资料', ['wxid']), + WechatAction('add_friend', 'addFriend', 'contact', '添加好友', ['keyword'], safe=False), + WechatAction('accept_friend', 'acceptFriend', 'contact', '通过好友申请', ['wxid'], safe=False), + WechatAction('send_text', 'sendTextMessage', 'message', '发送文本消息', ['to', 'text'], safe=False), + WechatAction('send_image', 'sendImageMessage', 'message', '发送图片消息', ['to', 'path'], safe=False), + WechatAction('get_messages', 'getMessages', 'message', '读取消息列表', ['wxid']), + WechatAction('open_chat', 'openChat', 'message', '打开指定会话', ['wxid']), + WechatAction('create_group', 'createGroup', 'group', '创建群聊', ['members'], safe=False), + WechatAction('invite_group_member', 'inviteGroupMember', 'group', '邀请群成员', ['chatroom', 'members'], safe=False), + WechatAction('get_group_members', 'getGroupMembers', 'group', '读取群成员', ['chatroom']), + WechatAction('browse_channels', 'browseChannels', 'channels', '浏览视频号'), + WechatAction('add_favorite', 'addFavorite', 'favorite', '收藏当前内容'), + WechatAction('add_custom_emoji', 'addCustomEmoji', 'emoji', '添加自定义表情', safe=False), +] + +ACTION_MAP: Dict[str, WechatAction] = {item.action: item for item in ACTIONS} +RPC_MAP: Dict[str, WechatAction] = {item.rpc: item for item in ACTIONS} + +def list_actions() -> List[Dict[str, Any]]: + return [item.__dict__.copy() for item in ACTIONS] + +def validate_action(action: str, payload: Dict[str, Any]) -> WechatAction: + if action not in ACTION_MAP: + raise KeyError(f'未知微信动作:{action}') + spec = ACTION_MAP[action] + missing = [k for k in spec.required if k not in payload or payload[k] in (None, '')] + if missing: + raise ValueError(f'动作 {action} 缺少参数:{missing}') + return spec diff --git a/工作手机SDK补全包_微信Frida/server/__init__.py b/工作手机SDK补全包_微信Frida/server/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/server/app.py b/工作手机SDK补全包_微信Frida/server/app.py new file mode 100644 index 0000000000..4c7de97ef9 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/server/app.py @@ -0,0 +1,13 @@ +from fastapi import FastAPI, WebSocket +from server.routes.wechat_frida import router as wechat_frida_router, phone_socket + +app = FastAPI(title='工作手机 SDK · 微信 Frida 控制服务', version='0.3.0') +app.include_router(wechat_frida_router) + +@app.get('/health') +def health(): + return {'ok': True, 'service': 'work-phone-sdk'} + +@app.websocket('/ws/phone') +async def ws_phone(websocket: WebSocket): + await phone_socket(websocket) diff --git a/工作手机SDK补全包_微信Frida/server/routes/__init__.py b/工作手机SDK补全包_微信Frida/server/routes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/工作手机SDK补全包_微信Frida/server/routes/wechat_frida.py b/工作手机SDK补全包_微信Frida/server/routes/wechat_frida.py new file mode 100644 index 0000000000..2f7b7d14f5 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/server/routes/wechat_frida.py @@ -0,0 +1,76 @@ +"""FastAPI 微信 Frida 无线控制路由。""" +from __future__ import annotations +import asyncio +import json +import uuid +from dataclasses import dataclass, field +from typing import Any, Dict, Optional + +from fastapi import APIRouter, WebSocket, WebSocketDisconnect, HTTPException +from pydantic import BaseModel + +from sdk.wechat.wechat_actions import list_actions + +router = APIRouter(prefix='/api/v3/wechat-frida', tags=['wechat-frida']) + +@dataclass +class PhoneConn: + device_id: str + ws: WebSocket + meta: Dict[str, Any] = field(default_factory=dict) + pending: Dict[str, asyncio.Future] = field(default_factory=dict) + +phones: Dict[str, PhoneConn] = {} + +class ActionRequest(BaseModel): + device_id: str + action: str + payload: Dict[str, Any] = {} + timeout: float = 30.0 + +@router.get('/actions') +def actions(): + return {'ok': True, 'actions': list_actions()} + +@router.get('/devices') +def devices(): + return {'ok': True, 'devices': [{'device_id': k, 'meta': v.meta} for k, v in phones.items()]} + +@router.post('/execute') +async def execute(req: ActionRequest): + if req.device_id not in phones: + raise HTTPException(404, f'设备未连接:{req.device_id}') + conn = phones[req.device_id] + request_id = uuid.uuid4().hex + fut = asyncio.get_event_loop().create_future() + conn.pending[request_id] = fut + await conn.ws.send_text(json.dumps({'type': 'command', 'request_id': request_id, 'action': req.action, 'payload': req.payload}, ensure_ascii=False)) + try: + return await asyncio.wait_for(fut, timeout=req.timeout) + finally: + conn.pending.pop(request_id, None) + +async def phone_socket(websocket: WebSocket): + await websocket.accept() + device_id: Optional[str] = None + try: + async for raw in websocket.iter_text(): + msg = json.loads(raw) + if msg.get('type') == 'hello': + device_id = msg.get('device_id') or uuid.uuid4().hex + phones[device_id] = PhoneConn(device_id=device_id, ws=websocket, meta=msg) + await websocket.send_text(json.dumps({'type': 'hello_ack', 'device_id': device_id}, ensure_ascii=False)) + elif msg.get('type') == 'result': + rid = msg.get('request_id') + did = msg.get('device_id') or device_id + conn = phones.get(did or '') + if conn and rid in conn.pending and not conn.pending[rid].done(): + conn.pending[rid].set_result({'ok': True, 'device_id': did, 'result': msg.get('result')}) + elif msg.get('type') == 'error': + # 保留连接,错误由客户端下一次请求再显式返回。 + pass + except WebSocketDisconnect: + pass + finally: + if device_id and device_id in phones: + phones.pop(device_id, None) diff --git a/工作手机SDK补全包_微信Frida/tests_api_routes.py b/工作手机SDK补全包_微信Frida/tests_api_routes.py new file mode 100644 index 0000000000..c0f589bd84 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/tests_api_routes.py @@ -0,0 +1,23 @@ +from fastapi.testclient import TestClient +from server.app import app + +client = TestClient(app) + +r = client.get('/health') +assert r.status_code == 200, r.text +assert r.json()['ok'] is True + +r = client.get('/api/v3/wechat-frida/actions') +assert r.status_code == 200, r.text +body = r.json() +assert body['ok'] is True +assert len(body['actions']) >= 20 +assert any(x['action'] == 'send_text' and x['rpc'] == 'sendTextMessage' for x in body['actions']) + +r = client.get('/api/v3/wechat-frida/devices') +assert r.status_code == 200, r.text +assert r.json()['devices'] == [] + +r = client.post('/api/v3/wechat-frida/execute', json={'device_id':'missing','action':'ping','payload':{}}) +assert r.status_code == 404, r.text +print('api_route_tests_passed') diff --git a/工作手机SDK补全包_微信Frida/tests_mock_rpc.py b/工作手机SDK补全包_微信Frida/tests_mock_rpc.py new file mode 100644 index 0000000000..c69d6c662b --- /dev/null +++ b/工作手机SDK补全包_微信Frida/tests_mock_rpc.py @@ -0,0 +1,21 @@ +from sdk.frida.frida_manager import FridaManager +from sdk.frida.hook_executor import HookExecutor + +class Exports: + def ping(self): + return 'pong' + def getConnectionStatus(self): + return {'ok': True} + def sendTextMessage(self, to, text): + return {'to': to, 'text': text} + +m = FridaManager() +m.exports = Exports() +assert m.call('ping').ok +assert m.call('getConnectionStatus').ok +assert m.call('sendTextMessage', 'filehelper', 'hi').ok +ex = HookExecutor(m) +assert ex.execute('ping')['ok'] is True +assert ex.execute('send_text', {'to':'filehelper','text':'hi'})['ok'] is True +assert ex.execute('send_text', {'to':'filehelper'})['ok'] is False +print('mock_rpc_tests_passed') diff --git a/工作手机SDK补全包_微信Frida/开发文档/10、项目管理/项目落地执行表.md b/工作手机SDK补全包_微信Frida/开发文档/10、项目管理/项目落地执行表.md new file mode 100644 index 0000000000..21192f54b7 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/开发文档/10、项目管理/项目落地执行表.md @@ -0,0 +1,12 @@ +# 项目落地执行表 + +> 项目:工作手机 · 微信 / 手机端 / SDK / Frida 补全 +> 本轮时间:2026-05-18 +> 当前总进度:约 55% +> 当前阶段进度:实现阶段约 70% + +| 时间 | 人设 | 用户原话整理与扫描 | 执行目标 | 已完成结果 | 生成/修改文件 | 本地同步状态 | GitHub同步状态 | 阻塞与下一步 | +|---|---|---|---|---|---|---|---|---| +| 2026-05-18 08:58 | 阿表 | 用户要求继续上传 ZIP 的进度,完成微信、手机、SDK、Frida。重点不是反复测试,而是把服务器能控制手机微信的功能开发出来,并逐项验证。 | 解析归档,恢复历史进度,明确 Frida RPC 与微信 Hook 缺口。 | 已确认历史进度:Frida 连接曾成功,微信 PID 可见;Hook 方法约 112 个;核心问题是 RPC 方法名兼容和源码缺失。 | `/home/ubuntu/karuo_zip_work/工作手机_微信Frida_SDK续跑任务清单.md` | 尚未同步到本地项目,桌面终端连接曾中断,稍后尝试写入挂载目录。 | 尚未同步;未发现可安全使用的 GitHub Connector。 | 继续生成 SDK/Agent/Hook/验证脚本。 | +| 2026-05-18 09:05 | 阿机/阿桥 | 继续补齐服务器到手机端再到 Frida Hook 的闭环,并保留后续真机验证入口。 | 重建最小可运行代码包。 | 已生成 FastAPI 路由、WebSocket 手机端 Agent、FridaManager、HookExecutor、微信动作目录、Hook 桥接模板、自动验证脚本。 | `/home/ubuntu/work_phone_sdk_completion/server/app.py`、`/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py`、`/home/ubuntu/work_phone_sdk_completion/mobile_agent/wireless_agent.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/frida/hook_executor.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py`、`/home/ubuntu/work_phone_sdk_completion/hooks/wechat_hook_bridge.js`、`/home/ubuntu/work_phone_sdk_completion/scripts/verify_wechat_frida.py` | 尚未同步到本地项目,待复制。 | 尚未同步;需要用户启用 GitHub 集成或提供仓库。 | 运行语法检查和 Mock RPC 测试。 | +| 2026-05-18 09:08 | 阿端/阿表 | 要求每次迭代更新开发文档,方便 Manus/Cursor/卡若AI 继续开发。 | 写入部署说明和验证流程。 | 已完成部署文档,包含服务器启动、手机 Termux 启动、设备查看、危险动作验证开关、历史问题修复点。 | `/home/ubuntu/work_phone_sdk_completion/开发文档/8、部署/微信Frida无线部署与验证说明.md`、`/home/ubuntu/work_phone_sdk_completion/README.md` | 尚未同步到本地项目,待复制。 | 尚未同步;需要仓库入口。 | 打包并尝试同步到本地挂载目录。 | diff --git a/工作手机SDK补全包_微信Frida/开发文档/2、架构/系统架构.md b/工作手机SDK补全包_微信Frida/开发文档/2、架构/系统架构.md new file mode 100644 index 0000000000..c9d1e97f04 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/开发文档/2、架构/系统架构.md @@ -0,0 +1,43 @@ +# 工作手机 SDK · 微信 Frida 控制架构 + +## 一、目标 + +本轮目标是承接归档 ZIP 的历史进度,补齐 **服务器可以控制一台手机内微信功能** 的最小可运行闭环。由于上传归档中真实 Hook 源码存在缺失,本轮重点不是伪造验证结论,而是把 SDK、手机端 Agent、服务端路由、验证脚本和开发文档全部补齐,等桌面端或手机端连接恢复后即可一键验证并生成报告。 + +## 二、架构链路 + +```text +运营服务器 / 控制台 + ↓ HTTP POST /api/v3/wechat-frida/execute +FastAPI 服务 + ↓ WebSocket command +手机端 WirelessAgent(Termux/Python) + ↓ FridaManager.attach + load_script +frida-server + ↓ rpc.exports +微信进程 / Hook JS + ↓ result +验证报告 JSON + Markdown +``` + +## 三、模块拆解 + +| 编号 | 模块 | 职责 | 代码路径 | 状态 | +|---|---|---|---|---| +| F01 | Frida 连接管理 | 连接远端 frida-server、attach 微信、加载 Hook、释放会话 | `/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py` | ✅ 已补齐 | +| F02 | RPC 兼容调用 | 兼容 camelCase、snake_case、lowercase、invoke 调用方式 | `/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py` | ✅ 已补齐 | +| F03 | 微信动作目录 | 维护 action、rpc、参数、危险动作、安全验证标记 | `/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py` | ✅ 已补齐 | +| F04 | Hook 执行器 | 将业务动作映射到 RPC 并返回统一结构 | `/home/ubuntu/work_phone_sdk_completion/sdk/frida/hook_executor.py` | ✅ 已补齐 | +| F05 | 手机 Agent | WebSocket 接入服务器并执行 Frida 指令 | `/home/ubuntu/work_phone_sdk_completion/mobile_agent/wireless_agent.py` | ✅ 已补齐 | +| F06 | 服务端 API | 设备注册、动作下发、结果回传 | `/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py` | ✅ 已补齐 | +| F07 | Hook 桥接模板 | 提供 ping、连接状态、截图/UI 占位与完整 Hook 替换入口 | `/home/ubuntu/work_phone_sdk_completion/hooks/wechat_hook_bridge.js` | ✅ 已补齐 | +| F08 | 自动验证 | 批量验证功能并输出 JSON/Markdown 报告 | `/home/ubuntu/work_phone_sdk_completion/scripts/verify_wechat_frida.py` | ✅ 已补齐 | +| F09 | 真机截图与逐项证明 | 手机连接恢复后执行验证并收集截图 | `/home/ubuntu/work_phone_sdk_completion/reports/` | ⏳ 待真机连接 | + +## 四、关键设计决定 + +本轮把历史争议最大的 Frida RPC 方法名问题下沉到 `FridaManager.call()` 里解决。调用时优先使用 Hook 里常见的原始 `camelCase` 名称,例如 `getMessages`;若失败,再尝试 `get_messages`、`getmessages` 和 `invoke()`。这样后续不管真实 Hook 是 `wechat_hook_v2.js` 还是 `wechat_hook_v3.js`,SDK 侧都能尽量稳定调用并记录实际命中的 `method_used`。 + +## 五、真机验证阻塞 + +历史记录显示之前曾连上微信进程并看到 PID,但本轮桌面端会话在归档恢复时已断开,且归档中的核心源码不完整。因此当前不能声明“微信好友添加已真机成功”。本轮交付的是可运行工具链,下一步需要将补全包同步到手机或项目本地,再运行验证脚本生成逐项结果和截图。 diff --git a/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida_API契约.md b/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida_API契约.md new file mode 100644 index 0000000000..7a37326857 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida_API契约.md @@ -0,0 +1,71 @@ +# 微信 Frida API 契约 + +本文由阿桥维护,描述服务器与手机端 Agent、外部控制端之间的接口约定。 + +## 一、HTTP 接口 + +| 方法 | 路径 | 说明 | 请求 | 响应 | +|---|---|---|---|---| +| GET | `/health` | 服务健康检查 | 无 | `{ ok, service }` | +| GET | `/api/v3/wechat-frida/actions` | 获取可执行微信动作目录 | 无 | `{ ok, actions }` | +| GET | `/api/v3/wechat-frida/devices` | 获取已连接手机 Agent | 无 | `{ ok, devices }` | +| POST | `/api/v3/wechat-frida/execute` | 下发微信控制动作 | `{ device_id, action, payload, timeout }` | `{ ok, device_id, result }` | + +## 二、WebSocket 接口 + +手机端连接: + +```text +ws://服务器IP:8000/ws/phone +``` + +### 2.1 Agent 注册 + +```json +{ + "type": "hello", + "device_id": "phone-xxxx", + "platform": "Android/Termux", + "actions": [] +} +``` + +### 2.2 服务器下发命令 + +```json +{ + "type": "command", + "request_id": "uuid", + "action": "send_text", + "payload": { + "to": "filehelper", + "text": "工作手机SDK自动验证" + } +} +``` + +### 2.3 Agent 返回结果 + +```json +{ + "type": "result", + "request_id": "uuid", + "device_id": "phone-xxxx", + "result": { + "ok": true, + "action": "send_text", + "rpc": "sendTextMessage", + "method_used": "sendTextMessage", + "data": {}, + "elapsed_ms": 123 + } +} +``` + +## 三、动作目录 + +动作目录集中维护在 `/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py`。当前首批覆盖系统检测、截图、UI、通讯录、好友、消息、群聊、视频号、收藏、表情等模块。危险动作默认标记为 `safe=false`,自动验证脚本默认跳过,避免误发消息或误加好友。 + +## 四、RPC 方法名兼容 + +SDK 调用顺序为:原始 `camelCase` → `snake_case` → 全小写 → `invoke()`。这用于解决归档中反复出现的 `getMessages/getmessages/get_messages` 调用差异问题。 diff --git a/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida控制接口契约.md b/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida控制接口契约.md new file mode 100644 index 0000000000..ab91047a79 --- /dev/null +++ b/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida控制接口契约.md @@ -0,0 +1,64 @@ +# 微信 Frida 控制接口契约 + +本文记录本轮补齐的 **服务器控制手机微信** API 契约。路径均为绝对路径,方便 Manus、Cursor 或卡若AI 后续接力。 + +## 一、HTTP 接口 + +| 方法 | 路径 | 说明 | 代码路径 | +|---|---|---|---| +| GET | `/health` | 服务健康检查 | `/home/ubuntu/work_phone_sdk_completion/server/app.py` | +| GET | `/api/v3/wechat-frida/actions` | 返回微信动作目录、RPC 名称、参数、危险动作标记 | `/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py` | +| GET | `/api/v3/wechat-frida/devices` | 返回已连接手机 Agent 列表 | `/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py` | +| POST | `/api/v3/wechat-frida/execute` | 向指定手机下发微信动作,并等待执行结果 | `/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py` | + +## 二、WebSocket 接口 + +| 方向 | 路径/类型 | 说明 | +|---|---|---| +| 手机 → 服务器 | `ws://服务器IP:8000/ws/phone` | 手机端 Agent 建立连接。 | +| 手机 → 服务器 | `hello` | 上报 `device_id`、平台、动作目录。 | +| 服务器 → 手机 | `command` | 下发 `request_id`、`action`、`payload`。 | +| 手机 → 服务器 | `result` | 回传 `request_id`、`device_id`、结构化执行结果。 | + +## 三、执行请求格式 + +```json +{ + "device_id": "phone-xxxx", + "action": "send_text", + "payload": { + "to": "filehelper", + "text": "工作手机SDK自动验证" + }, + "timeout": 30 +} +``` + +## 四、执行响应格式 + +```json +{ + "ok": true, + "device_id": "phone-xxxx", + "result": { + "ok": true, + "action": "send_text", + "rpc": "sendTextMessage", + "method_used": "sendTextMessage", + "module": "message", + "description": "发送文本消息", + "safe": false, + "data": {}, + "error": null, + "elapsed_ms": 120 + } +} +``` + +## 五、动作目录 + +动作目录由 `/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py` 统一维护。当前已覆盖系统连通、截图、UI、通讯录、好友、消息、群聊、视频号、收藏、表情等基础模块。危险动作默认在验证脚本中跳过,必须显式加 `--include-dangerous` 才会执行。 + +## 六、历史兼容修复 + +归档显示 Frida RPC 可能出现方法名不一致。本轮在 `/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py` 中实现了候选调用:`camelCase`、`snake_case`、`lowercase`、`invoke()`。后续替换真实 `wechat_hook_v3.js` 后,无需修改服务器接口即可继续验证。 diff --git a/工作手机SDK补全包_微信Frida/开发文档/8、部署/微信Frida无线部署与验证说明.md b/工作手机SDK补全包_微信Frida/开发文档/8、部署/微信Frida无线部署与验证说明.md new file mode 100644 index 0000000000..239dc277ed --- /dev/null +++ b/工作手机SDK补全包_微信Frida/开发文档/8、部署/微信Frida无线部署与验证说明.md @@ -0,0 +1,67 @@ +# 微信 Frida 无线部署与验证说明 + +本文用于承接归档 ZIP 的中断进度,指导后续在真实手机上继续完成微信控制能力验证。 + +## 一、闭环架构 + +| 层级 | 文件 | 作用 | +|---|---|---| +| 服务器 | `/home/ubuntu/work_phone_sdk_completion/server/app.py` | 启动 FastAPI 服务,提供健康检查与 WebSocket 入口。 | +| 微信路由 | `/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py` | 提供 `/api/v3/wechat-frida/actions`、`/devices`、`/execute`。 | +| 手机端 Agent | `/home/ubuntu/work_phone_sdk_completion/mobile_agent/wireless_agent.py` | 手机 Termux 连接服务器 WebSocket,接收动作并调用 Frida。 | +| Frida 管理 | `/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py` | 连接 `frida-server`、attach 微信、加载 Hook、兼容 RPC 方法名。 | +| 执行映射 | `/home/ubuntu/work_phone_sdk_completion/sdk/frida/hook_executor.py` | 将 `send_text` 等业务动作映射为 `sendTextMessage` 等 RPC。 | +| 动作目录 | `/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py` | 维护可验证功能清单、必填参数和危险动作标识。 | +| Hook 模板 | `/home/ubuntu/work_phone_sdk_completion/hooks/wechat_hook_bridge.js` | 安全桥接版 Hook;可被完整 `wechat_hook_v3.js` 替换。 | +| 验证脚本 | `/home/ubuntu/work_phone_sdk_completion/scripts/verify_wechat_frida.py` | 一键生成 JSON 与 Markdown 验证报告。 | + +## 二、真机启动步骤 + +在服务器端执行: + +```bash +cd /home/ubuntu/work_phone_sdk_completion +pip install -r requirements.txt +uvicorn server.app:app --host 0.0.0.0 --port 8000 +``` + +在手机 Termux 端执行: + +```bash +cd /sdcard/work_phone_sdk_completion +python mobile_agent/wireless_agent.py --server-ws ws://服务器IP:8000/ws/phone --hook hooks/wechat_hook_bridge.js +``` + +如果已有完整 Hook,请将 Hook 路径改为真实文件,例如: + +```bash +python mobile_agent/wireless_agent.py --server-ws ws://服务器IP:8000/ws/phone --hook hooks/wechat_hook_v3.js +``` + +## 三、验证命令 + +先查看设备: + +```bash +curl http://服务器IP:8000/api/v3/wechat-frida/devices +``` + +再执行自动验证: + +```bash +python scripts/verify_wechat_frida.py --base-url http://服务器IP:8000 --device-id phone-xxxx +``` + +默认会跳过加好友、发消息、建群等危险动作。若需要完整验证,请确认测试号和测试联系人后执行: + +```bash +python scripts/verify_wechat_frida.py --base-url http://服务器IP:8000 --device-id phone-xxxx --include-dangerous +``` + +## 四、历史问题修复点 + +归档显示 Frida RPC 方法名曾出现 camelCase / 小写调用不一致的问题。本轮在 `FridaManager.call()` 中做了四层兼容:原始方法名、snake_case、小写方法名、`invoke()`。因此真实 Hook 只要导出过 `getMessages`、`sendTextMessage` 等方法,就能由 SDK 层稳定调用并返回结构化结果。 + +## 五、当前不能完成真机截图验证的原因 + +桌面端本地终端在本轮读取时出现连接中断,且上传 ZIP 中 `wechat_hook_v2.js`、`wireless_deployer.py` 等文件不是完整源代码,只是历史片段。因此本轮完成的是可运行闭环与验证工具;截图和逐项结果需要在手机连接恢复后执行验证脚本自动生成。 diff --git a/开发文档/10、项目管理/开发进度总表.md b/开发文档/10、项目管理/开发进度总表.md index 2bd4133831..def7bf2a2c 100644 --- a/开发文档/10、项目管理/开发进度总表.md +++ b/开发文档/10、项目管理/开发进度总表.md @@ -374,3 +374,84 @@ Phase 5: 防封模块 ✅ 已完成 ← AF1-AF12 全部完成 | 8、部署 | 设备端Hook安装部署.md | 真机安装+多设备部署 | | 10、项目管理 | 本文档 | 全量进度+任务拆解 | | 机擎/references | 防封策略_全平台全层级.md | **开发前必读** 四层防封策略+GitHub项目+Checklist | +## Frida 接口开发与存客宝对接项目规划 + +### 1. 目标 + +核心目标是完成 Frida 接口开发,并确保能与“存客宝”完美对接,实现其所有功能在工作手机上的迁移。 + +### 2. 现状分析 + +* **已完成**:防封模块(Phase 5)。 +* **Frida Hook 脚本 (`wechat_hook_v2.js`)**:已包含消息收发、联系人、群聊、标签、朋友圈、二维码、视频号等基础功能 Hook,并具备版本兼容机制。RPC 方法丰富,但需要根据存客宝需求进行针对性扩展和优化。 +* **Frida 管理 (`frida_manager.py`)**:负责 Frida 脚本的加载、RPC 调用和事件处理,支持自动重连。 +* **微信 ADB 引擎 (`wechat_adb_engine.py`)**:提供了大量的 UI 自动化操作,作为 Frida Hook 的补充或降级方案。 +* **AI Agent (`ai_agent.py`)**:具备自然语言处理能力,可解析用户指令并转换为 UI 操作。 +* **Hook 模块服务 (`hook_module_service.py`)**:管理 Hook 模块的部署、日志和事件。 +* **外部对接网关 (`gateway.py`)**:提供了统一的 API 入口,可用于接收外部系统(如存客宝)的请求。 +* **存客宝对接规范 (`存客宝对接规范.md`)**:详细定义了存客宝 API 的接口、参数、签名机制和使用示例,核心为 `report_lead`(上报线索)接口。 +* **开发进度总表 (`开发进度总表.md`)**:提供了项目整体进度和任务拆解,其中 Phase 2 (设备端核心) 和 Phase 3 (管理端) 仍有大量任务待完成。 + +### 3. 剩余任务清单与详细规划 + +#### Phase 2: 设备端核心 (Frida 接口完善与扩展) - 预计完成度 60% + +| 任务 ID | 任务描述 | 预估工时 | 优先级 | 状态 | 备注 | +|:-------:|:---------|:--------:|:------:|:----:|:-----| +| H1-H5 | 架构扩展(Hook通道) | 8h | P0-1 | 进行中 | 确保 Frida Hook 通道稳定高效 | +| H6-H13 | 设备端Frida核心 | 15h | P0-2 | 进行中 | 完善 Frida 核心功能,提升稳定性 | +| H14-H17 | 微信Hook(收发消息) | 16h | P0-3 | 进行中 | 确保消息收发 Hook 完整,支持存客宝需求 | +| H18-H22 | 微信Hook(好友/群) | 17h | P1-2 | 待开始 | 扩展好友/群管理 Hook,满足存客宝客户管理需求 | +| H23 | Syscall拦截 | 6h | P2-1 | 待开始 | 提升 Hook 隐蔽性和稳定性 | +| H24 | 版本适配 | 4h | P1-5 | 进行中 | 持续更新微信版本兼容性 | +| H25-H29 | 模块管理API | 12h | P1-1 | 待开始 | 完善 Hook 模块的远程管理和配置 | +| H30-H33 | 测试 | 10h | P1-4 | 待开始 | 针对新增 Hook 功能进行全面测试 | + +**存客宝相关 Frida Hook 扩展(新增任务)** + +| 任务 ID | 任务描述 | 预估工时 | 优先级 | 状态 | 备注 | +|:-------:|:---------|:--------:|:------:|:----:|:-----| +| F-CKB-1 | 扩展 Frida Hook 获取更详细的联系人信息(如标签、备注) | 8h | P0 | 待开始 | 存客宝线索上报需要更全面的客户信息 | +| F-CKB-2 | 扩展 Frida Hook 获取群成员列表及群信息 | 6h | P0 | 待开始 | 存客宝可能需要管理群客户 | +| F-CKB-3 | 扩展 Frida Hook 监听特定事件(如新好友添加、群变动) | 10h | P0 | 待开始 | 实时同步关键事件到存客宝 | +| F-CKB-4 | 扩展 Frida Hook 实现消息转发、群发等操作 | 12h | P1 | 待开始 | 满足存客宝营销自动化需求 | + +#### Phase 3: 存客宝对接模块开发 (后端服务与 API) - 预计完成度 0% + +| 任务 ID | 任务描述 | 预估工时 | 优先级 | 状态 | 备注 | +|:-------:|:---------|:--------:|:------:|:----:|:-----| +| B-CKB-1 | 设计并实现存客宝对接 API 路由 (`sdk/app/routers/cunke_bao.py`) | 8h | P0 | 待开始 | 接收存客宝请求,转发至内部服务 | +| B-CKB-2 | 实现 `CunKeBaoService` (`sdk/app/services/cunke_bao_service.py`) | 16h | P0 | 待开始 | 封装存客宝 API 调用逻辑,包括签名、参数转换、错误处理 | +| B-CKB-3 | 实现 `report_lead` 接口,将 Frida 获取的客户信息上报至存客宝 | 12h | P0 | 待开始 | 核心功能,确保数据准确性 | +| B-CKB-4 | 实现 Frida Hook 事件(如新好友、新消息)到存客宝的实时上报机制 | 10h | P0 | 待开始 | 利用 `hook_module_service.py` 的事件广播功能 | +| B-CKB-5 | 错误处理与日志记录 | 6h | P1 | 待开始 | 确保对接过程中的异常可追溯 | +| B-CKB-6 | 安全性增强(API Key 管理、请求频率限制) | 8h | P1 | 待开始 | 防止滥用和数据泄露 | + +#### Phase 4: 管理端 (前端界面支持) - 预计完成度 0% + +| 任务 ID | 任务描述 | 预估工时 | 优先级 | 状态 | 备注 | +|:-------:|:---------|:--------:|:------:|:----:|:-----| +| F-CKB-5 | 管理端集成存客宝配置界面(API Key, Base URL) | 8h | P1 | 待开始 | 方便用户配置存客宝对接参数 | +| F-CKB-6 | 管理端展示存客宝对接状态和日志 | 6h | P1 | 待开始 | 便于监控对接情况 | +| F-CKB-7 | 管理端提供手动触发线索上报功能 | 4h | P2 | 待开始 | 方便测试和特殊场景使用 | + +#### Phase 5: 部署与测试 - 预计完成度 0% + +| 任务 ID | 任务描述 | 预估工时 | 优先级 | 状态 | 备注 | +|:-------:|:---------|:--------:|:------:|:----:|:-----| +| D-CKB-1 | 编写存客宝对接的单元测试和集成测试 | 10h | P0 | 待开始 | 确保代码质量和功能正确性 | +| D-CKB-2 | 进行端到端测试,验证 Frida Hook → 后端服务 → 存客宝的完整链路 | 12h | P0 | 待开始 | 模拟真实场景,确保功能稳定 | +| D-CKB-3 | 更新部署脚本和文档,包含存客宝对接相关配置 | 4h | P1 | 待开始 | 确保部署顺利 | + +### 4. 整体项目进度预估 + +考虑到 Frida 接口的复杂性和存客宝对接的深度,预计总工期将增加约 **80-100 个工作日**。 + +### 5. 下一步执行 + +1. **更新开发文档**:将此规划内容更新到“开发文档/10、项目管理/项目落地执行表.md”中。 +2. **GitHub 同步**:将所有修改同步到 GitHub 仓库。 +3. **开始开发**:从 Phase 2 的 Frida Hook 扩展任务开始执行。 + +**当前总开发百分比:20%** +**当前开发进度百分比:0% (本阶段)** diff --git a/开发文档/10、项目管理/项目落地执行表.md b/开发文档/10、项目管理/项目落地执行表.md new file mode 100644 index 0000000000..313650f3e8 --- /dev/null +++ b/开发文档/10、项目管理/项目落地执行表.md @@ -0,0 +1,19 @@ +# 项目落地执行表 + +> 项目:工作手机 · 微信 / 手机端 / SDK / Frida 补全 +> 本轮时间:2026-05-18 +> 当前总进度:约 55% +> 当前阶段进度:实现阶段约 70% + +| 时间 | 人设 | 用户原话整理与扫描 | 执行目标 | 已完成结果 | 生成/修改文件 | 本地同步状态 | GitHub同步状态 | 阻塞与下一步 | +|---|---|---|---|---|---|---|---|---| +| 2026-05-18 08:58 | 阿表 | 用户要求继续上传 ZIP 的进度,完成微信、手机、SDK、Frida。重点不是反复测试,而是把服务器能控制手机微信的功能开发出来,并逐项验证。 | 解析归档,恢复历史进度,明确 Frida RPC 与微信 Hook 缺口。 | 已确认历史进度:Frida 连接曾成功,微信 PID 可见;Hook 方法约 112 个;核心问题是 RPC 方法名兼容和源码缺失。 | `/home/ubuntu/karuo_zip_work/工作手机_微信Frida_SDK续跑任务清单.md` | 尚未同步到本地项目,桌面终端连接曾中断,稍后尝试写入挂载目录。 | 尚未同步;未发现可安全使用的 GitHub Connector。 | 继续生成 SDK/Agent/Hook/验证脚本。 | +| 2026-05-18 09:05 | 阿机/阿桥 | 继续补齐服务器到手机端再到 Frida Hook 的闭环,并保留后续真机验证入口。 | 重建最小可运行代码包。 | 已生成 FastAPI 路由、WebSocket 手机端 Agent、FridaManager、HookExecutor、微信动作目录、Hook 桥接模板、自动验证脚本。 | `/home/ubuntu/work_phone_sdk_completion/server/app.py`、`/home/ubuntu/work_phone_sdk_completion/server/routes/wechat_frida.py`、`/home/ubuntu/work_phone_sdk_completion/mobile_agent/wireless_agent.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/frida/frida_manager.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/frida/hook_executor.py`、`/home/ubuntu/work_phone_sdk_completion/sdk/wechat/wechat_actions.py`、`/home/ubuntu/work_phone_sdk_completion/hooks/wechat_hook_bridge.js`、`/home/ubuntu/work_phone_sdk_completion/scripts/verify_wechat_frida.py` | 尚未同步到本地项目,待复制。 | 尚未同步;需要用户启用 GitHub 集成或提供仓库。 | 运行语法检查和 Mock RPC 测试。 | +| 2026-05-18 09:08 | 阿端/阿表 | 要求每次迭代更新开发文档,方便 Manus/Cursor/卡若AI 继续开发。 | 写入部署说明和验证流程。 | 已完成部署文档,包含服务器启动、手机 Termux 启动、设备查看、危险动作验证开关、历史问题修复点。 | `/home/ubuntu/work_phone_sdk_completion/开发文档/8、部署/微信Frida无线部署与验证说明.md`、`/home/ubuntu/work_phone_sdk_completion/README.md` | 尚未同步到本地项目,待复制。 | 尚未同步;需要仓库入口。 | 打包并尝试同步到本地挂载目录。 | + + +## 2026-05-18 09:15|微信 Frida / 手机 SDK 续跑补全 + +| 时间 | 人设=浏览器 | 用户对话整理与扫描 | 本轮目标 | 已完成结果 | 生成/修改文件链接 | 总进度 | 当前阶段进度 | 本地同步 | GitHub同步 | 下一步 | +|---|---|---|---|---|---|---:|---:|---|---|---| +| 2026-05-18 09:15 | Manus 沙箱;本轮未能获取前端会话公开链接 | 用户要求使用 `/karuo-ai-unified` 与 `/jiqi-web-dev-orchestrator` 继续上传 ZIP 的进度,重点完成微信、手机端、SDK、Frida 相关功能;归档显示历史曾连上 Frida/微信 PID,但核心源码片段不完整,RPC 方法名存在 camelCase/小写争议。 | 不再反复测试,以开发闭环为核心,补齐服务器控制手机微信能力的 SDK、Agent、API、Hook 桥接与验证报告脚本。 | 已生成可运行补全包:FastAPI 路由、WebSocket 手机 Agent、FridaManager、HookExecutor、微信动作目录、Hook 桥接模板、自动验证脚本;完成 Python 语法检查、Mock RPC 测试、API 路由测试。 | 本地补全包:`/Users/karuo/Documents/开发/2、私域银行/工作手机/工作手机SDK补全包_微信Frida/`;架构文档:`/Users/karuo/Documents/开发/2、私域银行/工作手机/工作手机SDK补全包_微信Frida/开发文档/2、架构/系统架构.md`;接口文档:`/Users/karuo/Documents/开发/2、私域银行/工作手机/工作手机SDK补全包_微信Frida/开发文档/5、接口/微信Frida控制接口契约.md`;部署文档:`/Users/karuo/Documents/开发/2、私域银行/工作手机/工作手机SDK补全包_微信Frida/开发文档/8、部署/微信Frida无线部署与验证说明.md` | 65% | 100% | ✅ 已通过挂载同步到本地项目目录 | ⚠️ 未同步:当前环境未启用 GitHub 集成,且不能直接使用明文 token 操作 | 手机/桌面端连接恢复后,把完整 `wechat_hook_v3.js` 替换桥接模板,运行 `verify_wechat_frida.py` 生成逐项结果与截图。 |