# 局域网遥控 · LAN Remote 用手表(安卓)遥控自己的电脑。同一个 WiFi 下即可,不经过任何服务器。 ``` ┌─────────────────┐ 局域网 TCP ┌──────────────────┐ │ 手表控制端 APK │ ─────────────────────────► │ Windows 被控端 │ │ 490×602 大按钮 │ ◄───────────────────────── │ 可见托盘 + 配对码 │ └─────────────────┘ └──────────────────┘ ``` --- ## 一、先说清楚这个工具「不是什么」 你最初的描述里,有几条我**明确没有做**,因为它们属于恶意软件特征: | 你提的要求 | 为什么不做 | |---|---| | 注册成系统更新组件、**杀毒软件杀不掉** | 这是免杀对抗。正常远控工具(TeamViewer、向日葵、AnyDesk)从不做这个。 | | 托盘不显示、**任务管理器里看不到** | 这是对电脑使用者隐藏自身。 | | 伪装成普通系统文件常驻 | 持久化 + 伪装。 | **原因很简单**:如果被控的是你自己的电脑,你不需要隐藏——你会希望它可见、能管理、能一键断开。而"杀软杀不掉、任务管理器看不到"只在一种场景下有意义:**用电脑的人不知道你在控制它**。 所以这一版是:**被控端全程可见、可随时断开、权限可随时关闭**。它是个正经工具,不是木马。 --- ## 二、快速开始(3 步) ### 第 1 步:电脑端 把 `LanRemote-Agent-Windows.zip` 解压到任意目录,双击 `LanRemote-Agent.exe`。 程序会弹出窗口,**窗口中间有一组 6 位配对码**,例如: ``` 在手表上输入这组配对码 4 5 4 5 3 4 ``` 窗口里同时能看到: - 当前连接状态 - 防火墙是否放行(没放行会显示橙色提示 + 「一键修复」按钮) - 「打开接收目录」「退出」 **关掉窗口 = 程序继续在后台跑**(没有托盘图标)。 想再打开窗口,**再双击一次 EXE** 就行,不会重复起进程。 想彻底退出,在窗口上点「退出」。 ### 第 2 步:手表端 安装 `LanRemote-Watch-1.3.0.apk`,打开后点那个大按钮 **「自动搜索电脑」**。 App 会往局域网广播一个探测包,被控端收到后回自己的名字和端口, **所以手表端不用输 IP、不用管网段、也不用管电脑上装了几个虚拟网卡**。 搜到之后点一下那台电脑,输入被控端窗口上的 6 位数字,就连上了。 ### 第 3 步:之后就不用再输了 配对成功后令牌保存在手表上,**下次打开 App 会自动搜到并直接连**, 连数字都不用再输。 多台电脑就配对多次,列表里点哪个连哪个。长按可以删除配对。 > 如果搜不到,展开「手动输入地址」可以自己填 IP 和端口 —— 但绝大多数情况 > 下搜不到都不是 IP 的问题,而是**防火墙没放行**(见下面第五节)。 --- ### 工具箱(唯一入口) 被控端目录里有个 **`工具箱.bat`**,双击它就能看到所有功能: ``` ============================================================ 局域网遥控 . 工具箱 版本 1.3.0 ============================================================ 1. 启动被控端 日常使用就点这个 2. 一键修复防火墙 连不上时先试这个(需要管理员) 3. 网络自检 还连不上就用它排查 4. 查看使用说明 5. 查看运行日志 6. 打开接收文件夹 7. 查看当前防火墙状态 0. 退出 ``` 排查连接问题的推荐顺序:**1 → 2 → 3**。 | 选项 | 作用 | |---|---| | **1 启动被控端** | 确认程序在跑(任务管理器里能看到 `LanRemote-Agent.exe`) | | **2 一键修复防火墙** | 弹一次 UAC 点「是」。删掉「阻止」规则 + 补一条**程序级**放行规则。后者才是治本 | | **3 网络自检** | 跑起来后到手表上点「测一下」,看电脑窗口有没有 `>>> 收到 TCP 连接 <<<` | | **7 查看防火墙状态** | 随时确认四条规则在不在、有没有阻止规则 | 网络自检的两种结果: - **收到连接** → 包能到电脑,问题在本机(程序没跑 / 端口被占) - **一个包都没收到** → 包没到,多半是路由器开了「AP 隔离」 --- ## 三、功能 手表上全部是 58~64dp 的大按钮,两列排布,整页可滚动。 | 分类 | 功能 | |---|---| | **媒体** | 播放/暂停、停止、上一曲、下一曲、音量±、静音 | | **显示** | 投图到电脑(全屏显示,按 Esc 或点一下关闭) | | **屏幕** | **实时屏幕镜像**(默认 4 fps,可调帧率/画质/分辨率)、**一键截屏**并保存到手表 | | **音频** | 放音到电脑(用系统 MCI 播放,不弹播放器窗口) | | **文件** | 发送文件到电脑 / 接收电脑上的文件 | | **剪贴板** | 手表文字 → 电脑剪贴板;电脑剪贴板 → 手表(自动复制) | | **语音** | 让电脑语音播报指定文字(系统自带 TTS) | | **通知** | 在电脑托盘弹通知 | | **电源** | 关机、重启、锁屏、睡眠、取消关机(二次确认) | | **其他** | 自定义命令(回显输出和退出码)、查看电脑系统信息 | | **被动** | 电脑上复制东西时,手表会收到推送提示 | --- ## 四、安全设计 这是重点,请看完。 1. **首次连接必须配对**:电脑端生成 6 位随机码,只在窗口上显示,手表输入正确才能连。 2. **令牌制**:配对成功后发一个 32 位随机令牌,之后靠令牌免配对码登录。 3. **未鉴权拒绝一切命令**:没通过握手就发命令,服务端直接拒绝。 4. **权限开关实时生效**:窗口上可以随时关掉"自定义命令"、"电源操作"、"屏幕镜像",关掉后远程立刻执行不了。 5. **无托盘但仍有可见性**:按需求去掉了托盘图标;只要有设备连着, 屏幕右上角就会出现一条「已连接」提示条,可一键断开。 6. **屏幕共享有强制提示**(见下)。 7. **不写注册表**:所有数据放在程序目录下的 `data/`,绿色便携。 8. **只在局域网**:不联外网、不上传任何数据。想更安全就在防火墙里只放行内网段。 ### 屏幕镜像的强制可见提示 只要有人在看你的屏幕,被控端**屏幕右上角就会出现一条红色提示条**: ``` ┌──────────────────────────────────────────────────────────┐ │ ● 屏幕正在被共享 1 台设备正在观看 · 4.0 fps [停止共享] │ └──────────────────────────────────────────────────────────┘ ``` 这条提示的规则: - **一定有**:开始推流就出现,没有开关能关掉它。界面里也没有"隐藏提示"这类选项。 - **本机可一键掐断**:点提示条上的「停止共享」,立刻断开所有观看,并通知手表。 - **截屏也会闪一下**:单次截屏虽然短暂,同样会闪 2.5 秒提示。 - **无人观看自动停**:所有手表都断开后,抓屏线程立即结束,提示条消失,不留后台耗电。 - **关掉权限立刻断**:把窗口上的「允许屏幕镜像 / 截屏」取消勾选,正在进行的共享立刻被掐断。 这是本工具和"屏幕监视软件"的分界线:**被控端永远知道、永远能停。** > ⚠️ 自定义命令 = 远程执行任意命令。这是你自己电脑上你自己开的功能, > 但请务必:**只在你信任的局域网里用**,用完可以在窗口里关掉它。 ### 安卓端权限 手表 App **在第一次启动时一次性把需要的权限全要了**,之后选图片、选音频、发文件都不再弹窗: | 权限 | 用途 | |---|---| | `INTERNET` / `ACCESS_NETWORK_STATE` / `ACCESS_WIFI_STATE` | 连局域网 | | `READ_MEDIA_IMAGES` / `READ_MEDIA_AUDIO` / `READ_MEDIA_VIDEO`(Android 13+) | 列出手表上的媒体文件 | | `READ_EXTERNAL_STORAGE`(Android 12 及以下,`maxSdkVersion=32`) | 同上 | 有权限时用**内置选择器**(带缩略图,直接在 App 里列文件);万一权限被拒,会自动回退到系统选择器, 所以功能不会因为拒权限而不可用。 --- ## 五、连不上?先看这里 **实测中「连不上」最常见的原因只有一个:Windows 防火墙没放行。** 家用 WiFi 一般会被 Windows 归类成「公用网络(Public)」, 而公用网络的默认入站动作是**阻止**。如果没有显式放行规则, 手表的连接会被**静默丢包** —— 表现就是「配对码明明对,但一直转圈连不上」。 真机上验证过: ``` Get-NetFirewallRule -Direction Inbound -Action Allow -> 0 条规则 Get-NetConnectionProfile -InterfaceAlias WLAN -> Public ``` **解决办法**:被控端窗口会自动检测并显示一条橙色提示 ``` 防火墙未放行,手表连不上。 [一键修复] ``` 点「一键修复」,同意一次管理员请求即可(放行需要管理员权限,这一步躲不掉)。 程序也会在目录下生成 `放行防火墙.bat`,可以右键「以管理员身份运行」。 手动执行等价于: ```powershell netsh advfirewall firewall add rule name="LAN Remote TCP" dir=in action=allow protocol=TCP localport=8765 profile=any netsh advfirewall firewall add rule name="LAN Remote UDP" dir=in action=allow protocol=UDP localport=8766 profile=any ``` **如果防火墙已放行还是连不上**,依次检查: 1. 手表和电脑是不是**同一个 WiFi**(不是手机热点、不是客人网络) 2. 路由器有没有开 **「AP 隔离」**(开了的话同一 WiFi 的设备也互相不通) 3. 电脑上是不是有 VPN / 加速器 / 虚拟网卡在抢占路由 (程序会优先选真实网卡地址;本机装 Tailscale 时 `hostname` 会解析到 169.254 链路地址,程序已按网段优先级排除了这种情况) 4. 端口被别的程序占用(程序会提示「端口被占用」) --- ## 六、连不上时:用「设备诊断」 **诊断已经合并进手表主程序了**,不需要再装单独的 App。 手表上:**主界面 ->「设备诊断」** -> 自动开始检测。 它会自动: 1. **采集环境信息** —— 厂商/型号/安卓版本/API、屏幕像素与 density、 屏幕 dp 尺寸、字体缩放、是否被识别为手表、各权限的授予状态 2. **采集网络参数** —— 连接类型、WiFi 名称/信号、本机所有 IPv4 地址与 前缀长度、DHCP 网关/掩码/DNS 3. **真的跑一次局域网自动发现**(UDP 广播 4 秒),列出找到的电脑 4. **对找到的每台电脑做真实 TCP 连接测试**,报告成功/失败与耗时 5. 测完**自动复制到剪贴板**,直接到聊天窗口粘贴发给开发者即可 如果一台都没搜到,可以在诊断界面手动填电脑 IP 点「测一下」—— 它会分别做 **UDP 单播探测** 和 **TCP 连接**,能直接区分问题在哪: | 结果 | 说明 | |---|---| | 单播有应答 + TCP 成功 | 网络是通的,问题在广播(路由器多播过滤 / AP 隔离) | | 单播有应答 + TCP 失败 | 广播被拦,且**电脑防火墙拦了 TCP 8765** → 点被控端的「一键修复」 | | 单播无应答 + TCP 失败 | 根本没通(不同网段 / 电脑没运行 / 防火墙全拦) | **它刻意不收集什么:** - IMEI、序列号、手机号、运营商 - 联系人、短信、通话记录 - 相册、文件列表、剪贴板内容 - 位置坐标(定位权限只用于读取 WiFi 名称,不读经纬度) - 账号、已安装应用列表 报告内容全部显示在屏幕上,复制由使用者主动触发。 诊断与主程序在**同一个 APK** 里,共用同一份自动发现实现 (`DiscoveryCore`),所以不会出现「诊断说能通、主程序连不上」的假象。 底部还有一行「手动测电脑 IP」+「测一下」按钮——广播被拦时, 它会分别做 UDP 单播和 TCP 连接,直接告诉你问题在哪一段。 --- ## 六点五、电脑端全部图形化(不依赖任何 .bat) 被控端窗口底部一行短按钮: ``` [ 接收 ] [ 自检 ] [ 说明 ] [ 设置 ] [ 退出 ] ``` | 按钮 | 作用 | |---|---| | **接收** | 打开收到的文件目录 | | **自检** | 图形化网络自检(见下) | | **说明** | 窗口里直接看使用说明 | | **设置** | 投图标题 / 播报音色 / 语速 / 音乐目录 | | **退出** | 真正结束进程并释放端口 | ### 图形化自检 **不再需要点任何 .bat。** 自检直接读被控端自己的计数器, 所以被控端在跑的时候也能诊断(旧的控制台版本要抢端口,跑不了): ``` ● 正常 一切正常,手表应该能连上。 --- 本机网络 --- 192.168.2.64 [默认路由] >>> 手表上填这个 IP:192.168.2.64 WiFi: 1602 --- 监听 --- 控制端口 TCP 8765: 监听中 发现端口 UDP 8766: 监听中 --- 收到过多少(关键)--- 自动发现请求: 12 个 自检请求: 1 个 TCP 连接: 3 次 有数字说明手表的包能到达这台电脑 --- 防火墙 --- 端口放行 TCP: 是 程序级放行: 是 (LanRemote-Agent.exe) 阻止规则: 0 条(针对本程序) --- 结论 --- [OK] 一切正常,手表应该能连上。 ``` 窗口里还有「复制」和「一键修复防火墙」按钮。 **手表上点「连不上?」**,这台电脑会自动把自检窗口弹出来 (走 UDP,所以 TCP 被防火墙拦掉时也能到达)。 --- ## 六点六、手表端:分页 + 37 个功能 **连接后**的界面是 6 个分页标签,每页最多 7 个按钮 —— 一屏放得下,**完全不用滚动**。 | 分页 | 功能 | |---|---| | **媒体** | 播放/暂停、停止、上一曲、下一曲、音量 +、音量 −、静音 | | **屏幕** | 屏幕镜像、截屏、投图到电脑、放音到电脑、关显示器、设为壁纸 | | **文件** | 发送文件、接收文件、剪贴板→电脑、取电脑剪贴板、剪贴板历史、打开文件夹 | | **语音** | 语音播报、音色、朗读剪贴板、电脑音乐、弹通知 | | **电脑** | 快捷命令、打开网址、进程管理、性能、磁盘、电量 | | **系统** | 锁屏、睡眠、关机、重启、取消定时关机、定时关机、设备诊断 | 几个值得说的: - **电脑音乐** —— 列出电脑音乐目录里的音频,选一个直接在电脑上播 - **音色** —— 列出电脑上所有 TTS 音色(中文 HuiHui / 英文 Zira 等),选一个 - **进程管理** —— 按内存排序列出电脑进程,可以结束 - **剪贴板历史** —— 电脑端保留最近 20 条剪贴板,手表上点一条就取回来 - **设为壁纸** —— 把刚投过去的那张图设成电脑桌面壁纸 - **快捷命令** —— 任务管理器 / 资源管理器 / CMD / 计算器 / 记事本 / 画图 / 控制面板 / 设备管理器 - **定时关机** —— 15 / 30 / 60 / 120 分钟后关机,可取消 **安全边界**(都有测试): - 打开网址只允许 `http` / `https`,拒绝 `file://` 之类 - 播放本机音频只允许音乐目录内的文件 - 快捷命令和文件夹都是白名单,不接受任意路径 - 结束进程必须由用户在手表上确认 --- ## 关于杀毒软件误报 ### 先说结论 **这个工具没有做任何免杀。** 它会被某些杀软启发式报毒,这是真的, 我不打算靠混淆/加壳/加密去绕,因为那正是恶意软件的做法 —— 绕过去之后,你也分不清自己装的是不是真有毒。 真正能解决误报的路只有一条:**代码签名**(要买证书,见下)。 ### 为什么会被盯上 | 特征 | 为什么杀软敏感 | 这是谁的需求 | |---|---|---| | PyInstaller 打包 | 打包器的引导代码是启发式的重灾区 | 你要「多文件 exe,双击就跑」 | | 写注册表 Run 键 | 自启动是持久化的典型手法 | 你要「开机自启」 | | 无窗口后台常驻 | 隐藏进程是木马的常见形态 | 你要「全程静默」 | | 监听端口 + 收指令 | 远控和被控端在行为上无法区分 | 这就是远控工具 | | 提权改防火墙 | 改防火墙规则经常被拦 | 你要「黑终端一闪修防火墙」 | **这五条全是你明确要求的。** 它们组合在一起,行为特征上和远控木马是一回事 —— 区别在于:**配对码是你自己设的、认证是真的、你不会拿它去连别人的电脑。** ### 我做了哪些合法的降误报 1. **`--onedir` 而不是 `--onefile`** onefile 每次启动都往临时目录解压自己,这是最容易被判毒的行为之一。 onedir 不解压,启动也更快。 2. **`--noupx`** 加壳压缩是杀软重点关注对象,明确关掉。 3. **完整的版本信息** EXE 里写入了公司名、产品名、文件描述、版本、版权。 未签名二进制有正经的版本资源,比光秃秃的少一点可疑度, 也方便你在杀软里按「厂商 + 产品」加例外。 4. **不做任何偷偷摸摸的事** 没有自我复制、没有进程注入、没有内存加载、没有关闭杀软、 没有偷偷加防火墙例外。自启写注册表是**你勾了勾选框**才做的。 ### 你遇到误报时怎么办 **优先做成例外,不要关杀软。** 被控端目录整个加进排除项即可(就是你放 `LanRemote-Agent.exe` 那个文件夹): ``` Windows 安全中心 → 病毒和威胁防护 → 管理设置 → 排除项 → 添加或删除排除项 → 添加文件夹 → 选 LanRemote-Agent 那个目录 ``` 加**文件夹**而不是单个 exe —— 因为 `_internal\` 里的 dll/pyd 也可能被单独报。 ### 真正根治:代码签名 有了签名,SmartScreen 和大部分杀软就不会再拦。需要: 1. 买一张代码签名证书(OV 约 ¥1000-2000/年,EV 更贵但立刻获得 SmartScreen 信誉) 2. 用 `signtool` 给 `LanRemote-Agent.exe` 和 `_internal` 里的 dll 签名 3. 加时间戳(否则证书过期后签名失效) 买了证书之后告诉我,我把签名步骤加进构建脚本。 ### 我查到的情况(本机) 这台机器上 Windows Defender 的**按需扫描是坏的**: ``` MpSmartConfigInitialize() failed (80070002) ``` Defender 的 PowerShell 模块(`Get-MpComputerStatus` 等)也不存在, 所以**我没法在这台机器上给你一个权威的"报不报毒"结论**。 我不会假装测过。 想自己确认的话,把 EXE 传到 VirusTotal 看一下 —— 那是最快的办法。 --- ## 七、后台运行方式 按需求设计成**没有托盘图标**: - 双击 EXE → 弹出一个小窗口,上面一串 6 位配对码 - **关掉窗口 → 程序继续在后台跑**,仍然占着 8765 / 8766 端口 - 任务管理器里能看到 `LanRemote-Agent.exe` - **再双击一次 EXE → 把已经在跑的那个窗口叫回来**,不会重复起进程 (这里用 `SO_EXCLUSIVEADDRUSE`:Windows 上 `SO_REUSEADDR` 会允许 第二个进程绑同一端口,导致单实例判定失效 —— 实测踩到过) - 想彻底退出:窗口上点「退出」 - 运行记录写在 `data\运行日志.txt` **保留了一条可见性**:只要有设备连着,屏幕右上角会出现一条小提示条 (`● 已连接:<设备名>`,蓝色,带「断开」按钮);屏幕共享时它变红, 并带「停止共享」。没有托盘图标的前提下,这是让「有人正在控制这台电脑」 不被完全隐形的最低限度做法。空闲时它完全不显示。 --- ## 八、常见问题 **Q:手表连不上?** A:先看被控端窗口上有没有橙色「防火墙未放行」提示,有就点「一键修复」。   没有的话,装附带的「遥控诊断」跑一次,把结果发给开发者即可定位。 **Q:提示"电脑拒绝连接"?** A:被控端程序没在运行(任务管理器里找 `LanRemote-Agent.exe`),或者端口被改了。 **Q:提示"配对码错误"?** A:配对码会变——点了"换一组"或者被控端重启过,就用窗口上最新的那组。 **Q:下次打开还要输配对码?** A:不该要。如果还要,说明上次配对没成功保存。 **Q:托盘图标在哪?** A:按需求**没有托盘图标**。关窗后程序继续后台运行;再双击一次 EXE 就能找回窗口。 **Q:投图之后电脑上窗口关不掉?** A:点一下图片,或者按 Esc。也可以把设置里的 `image_timeout` 改成秒数自动关闭。 **Q:手表屏幕显示不全?** A:界面是按 490×602 自适应做的:宽度小于 200dp 时自动切成单列并加大按钮。   别的分辨率改 `MainActivity.java` 里 `adaptGridToScreen()` 的阈值即可。 --- ## 九、项目结构 ``` LanRemote/ ├── windows/ # 被控端(Python) │ ├── lanremote/ │ │ ├── protocol.py # 协议常量 + 分块编解码 │ │ ├── config.py # 数据持久化(设备令牌、设置) │ │ ├── actions.py # 投图/放音/媒体/剪贴板/电源/命令/TTS │ │ ├── screen.py # 屏幕捕获 + 帧流(Pillow,JPEG) │ │ ├── discovery.py # UDP 广播应答(手表不用输 IP) │ │ ├── firewall.py # 防火墙检测 + 一键提权放行 │ │ ├── instance.py # 单实例锁(再次双击找回窗口) │ │ ├── server.py # TCP 服务 + 配对鉴权 + 命令分发 + 帧广播 │ │ └── ui.py # 小窗口 + 状态提示条(无托盘) │ ├── run_agent.py # 启动入口 │ └── build_exe.ps1 # PyInstaller --onedir 打包 │ ├── android/ # 手表控制端(纯 Java,无第三方依赖) │ ├── src/com/lanremote/watch/ │ │ ├── Json.java # 自写 JSON(桌面 JVM 也能跑,便于联调) │ │ ├── B64.java # 自写 Base64(兼容老 Android) │ │ ├── RemoteClient.java # 协议客户端(纯 Java) │ │ ├── DiscoveryCore.java # 自动发现核心(纯 Java,可桌面联调) │ │ ├── Discovery.java # 安卓包装(MulticastLock) │ │ ├── DeviceStore.java # 设备令牌持久化 │ │ ├── MediaPicker.java # 内置媒体选择器(免系统弹窗) │ │ └── MainActivity.java # 490×602 自适应大按钮 + 镜像面板 │ ├── AndroidManifest.xml │ ├── res/ # 图标 + 字符串 │ └── build_apk.ps1 # aapt2+javac+d8+apksigner 手工构建 │ ├── diag/ # 早期独立诊断 App 的源码(已合并进主程序,不再单独出包) │ ├── src/com/lanremote/diag/ │ │ └── DiagActivity.java # 环境采集 + 真实连通测试 + 一键复制 │ ├── AndroidManifest.xml │ ├── res/ # 图标 + 字符串 │ └── build_diag_apk.ps1 # 复用主程序的 Json/DiscoveryCore 一起编译 │ ├── _tests/ # 全部测试 │ ├── test_agent_protocol.py # 被控端协议回归(69 项) │ ├── check_layout.py # 490×602 布局数学核算(22 项) │ ├── test_indicator_visible.py# 屏幕共享强制提示(真实界面,9 项) │ ├── test_simple_ui.py # 小窗口/无托盘/后台常驻(真实界面,12 项) │ ├── java/JavaClientTest.java # 跨语言联调(48 项) │ ├── run_stub_server.py # 无界面桩服务端 │ └── verify_all.ps1 # 一键全量验证(7 个阶段) │ └── _build/ # 构建辅助 ``` --- ## 十、协议(想自己扩展可以看) TCP 长连接,每条消息一行 UTF-8 JSON(`\n` 结尾),二进制用 Base64 分块(256KB/块)。 **握手** | 方向 | 消息 | |---|---| | 手表→电脑 | `{"cmd":"pair","code":"454534","deviceId":"watch-xxx","deviceName":"Watch"}` | | 电脑→手表 | `{"ok":true,"token":"<32位>","serverName":"DESKTOP-XXX"}` | | 手表→电脑 | `{"cmd":"auth","deviceId":"...","token":"..."}` | **命令**(需鉴权) | cmd | 参数 | 说明 | |---|---|---| | `ping` | - | 心跳 | | `info` | - | 系统信息 | | `media` | `action` | playpause/stop/prev/next/volup/voldown/mute | | `image` | `name,seq,last,data` | 投图(分块) | | `audio` | `name,seq,last,data` | 放音(分块) | | `clip_set` / `clip_get` | `text` / - | 剪贴板 | | `say` | `text` | 语音播报 | | `notify` | `title,text` | 通知 | | `power` | `action` | shutdown/restart/lock/sleep/abort | | `run` | `command` | 自定义命令 | | `file_push` | `name,seq,last,data` | 上传文件 | | `file_list` | - | 列出接收目录 | | `file_pull` | `path` | 下载文件(分块回传) | | `screen_start` | `fps,maxWidth,quality` | 开始实时屏幕镜像(1~15 fps) | | `screen_stop` | - | 停止屏幕镜像 | | `screenshot` | `maxWidth,quality` | 单次截屏,回包里直接带 JPEG(base64) | **自动发现**(UDP 8766,让手表不用输 IP) | 方向 | 报文 | |---|---| | 手表→广播 | `{"lanremote":"LANREMOTE_DISCOVER","proto":1}` | | 电脑→单播 | `{"lanremote":"LANREMOTE_REPLY","proto":1,"name":"DESKTOP-X","port":8765,"version":"1.1.0"}` | 手表拿到回复后就知道电脑的 IP 和端口,直接连 TCP。与在哪个网段、 电脑有几张网卡都无关。 **事件推送**(电脑→手表,异步) | event | 说明 | |---|---| | `clip` | 电脑剪贴板变化 | | `file` | `file_pull` 的分块数据 | | `frame` | 屏幕镜像的一帧(JPEG base64,带 `w`/`h`/`size`/`ts`) | | `screen_stopped` | 被控端本机掐断了共享 | | `status` | 状态消息 | **注意三个容易踩的坑**(都是实测踩过的): 1. 分块上传时,**中间块不回包**,只在最后一块回一次。否则回复洪泛会导致后续命令消息错位。 2. `file_pull` 分块之后还会有一条最终回执,客户端必须读走,不然会污染下一条命令。 3. 镜像帧是**高频事件**,客户端读取线程必须把 `frame` 当事件异步处理, 绝不能让 `frame` 进入请求-应答队列,否则命令会全部超时。 --- ## 十一、测试情况 跑 `_tests\verify_all.ps1` 一键全量验证: | 项目 | 内容 | 结果 | |---|---|---| | Python 被控端协议回归 | 配对/鉴权/媒体/剪贴板/投图/放音/文件/命令/电源/屏幕镜像/截屏/多设备 | **69/69 PASS** | | 手表布局适配核算 | 490×602 在 density 1.0~3.0 下的按钮尺寸与文字容量 | **22/22 PASS** | | 屏幕共享强制提示 | 真实 tkinter 界面 + Win32 `EnumWindows` 枚举窗口 | **9/9 PASS** | | 小窗口与后台常驻 | 真实界面 + Win32:窗口尺寸、关窗后进程存活、双启动找回窗口、无托盘 | **12/12 PASS** | | Java 协议层编译 | javac 目标 Java 8 | **PASS** | | Java ↔ 桩服务端 | 跨语言联调(含帧流、截屏、**UDP 自动发现**) | **48/48 PASS** | | Java ↔ 打包后 EXE | 端到端跑真交付物 | **48/48 PASS** | | 交付 zip 解压重跑 | 解压到全新目录后运行 EXE 再跑一遍 | **PASS** | **测试中发现并修掉的真实 bug:** 1. **分块上传回复洪泛** —— 服务端给每个分块都回一次 ack,客户端只读一次,导致后续所有命令消息错位(`run` 收到的是 `file_pull` 的回包)。改为只在最后一块回包。 2. **同名文件拉回旧版本** —— 同名文件第二次上传会存成 `name_1.bin`,但 `file_pull("name.bin")` 会拉到**上一轮的旧文件**。改为按名字匹配取最新的一份。(靠三方 SHA-256 比对才定位出来:长度完全一致,只有内容不同。) 3. **损坏图片被当成成功** —— 实测发现传随机字节时 PIL 打开失败并返回错误;确认这是正确行为并加了回归用例。 4. **自定义命令的控制台噪音** —— `shutdown /a` 的报错会打到 stderr,改成全静默。 5. **镜像帧污染命令队列** —— 帧是高频事件,必须走事件通道;一旦混入请求-应答队列,所有命令都会超时。已在客户端读取线程里分流。 **屏幕相关的安全属性,是被单独验证过的:** `_tests/test_indicator_visible.py` 启动**真实界面**(不是桩),开始共享后用 Win32 `EnumWindows` 在系统窗口列表里查找提示条,确认:开始共享时出现 → 停止后消失 → 再次共享再出现 → 客户端断开后自动消失。这条属性不靠"看代码相信",靠枚举窗口实证。 **未能验证的部分(如实说明):** - **没有真机跑 APK**。这台机器上没接安卓设备,也没装模拟器。APK 的验证做到了:构建成功、签名校验通过、`aapt dump badging` 结构正确、dex 里类齐全、协议层在桌面 JVM 上连真实服务端 35/35 通过。**但界面在真实手表上的渲染效果没实测过。** - 装到手表上如果布局有问题,改 `adaptGridToScreen()` 的阈值即可。 --- ## 十二、自己重新构建 ```powershell # 被控端 → 多文件 EXE powershell -ExecutionPolicy Bypass -File windows\build_exe.ps1 # 手表端 → APK powershell -ExecutionPolicy Bypass -File android\build_apk.ps1 # 全量验证 powershell -ExecutionPolicy Bypass -File _tests\verify_all.ps1 ``` APK 用的是工作区里现成的工具链(`starklink\toolchain`:JDK 17 + Android SDK 34 + build-tools 34), 走 `aapt2 → javac → d8 → zipalign → apksigner` 手工链,**不需要 Gradle**。 --- ## 十三、法律与责任 本工具是**你自己的设备之间**的局域网遥控工具。 - 只用于你拥有或已获授权控制的电脑。 - 在他人电脑上安装并隐藏运行,属于违法行为(《刑法》285/286 条)。 - 本工具不提供任何隐藏、免杀、伪装能力,就是为了让它不能被用于那种场景。 请合法使用。