# 适用于 Linux 的 Radmin VPN
通过 Wine 在 Linux 上运行 [Radmin VPN](https://www.radmin-vpn.com/)。加入 VPN 网络、查看对等节点、畅玩游戏——无需 Windows 虚拟机。
> 我构建这个项目并不是因为它比虚拟机更简单。我构建它是因为我认为它比虚拟机更简单。
**AI 辅助代码。** 由人类与 Claude(Anthropic)协作构建。驱动程序、钩子和桥接层是在 Ghidra 的帮助下,对 Radmin VPN 未文档化的驱动程序协议进行了大量 AI 辅助逆向工程后编写的。**该项目可用,但不提供任何保证。** 与 Famatech 无关联。Radmin VPN 是专有软件——请自行从 [radmin-vpn.com](https://www.radmin-vpn.com/) 下载。使用风险自负。
## 工作原理
Radmin VPN 的 Windows 服务通过 NDIS 微型端口驱动程序与其虚拟网络适配器通信。Wine 不支持 NDIS,因此我们用自研实现替换该驱动程序,将其桥接到 Linux TAP 设备。一个钩子 DLL 处理 Wine 兼容性问题(适配器命名、注册表权限)。最终成果是一个在 Wine 下原生运行的、功能完整的 Radmin VPN 客户端。
```
Linux 应用 ← TAP (radminvpn0) ← tap_bridge ← FIFO ← rvpnnetmp.sys (Wine 驱动) ← RvControlSvc.exe
```
## 快速开始(AppImage,推荐)
从[最新版本](https://github.com/baptisterajaut/radmin-vpn-linux/releases/latest)获取 `RadminVPN-Linux-x86_64.AppImage`。无需安装任何东西——Wine 已内置。
> **候选发布版**(例如 `v1.0.0-rc1`)以 GitHub *预发布* 形式发布。上面的 `latest` 链接始终指向最新的 *稳定版* 构建,因此预发布版本不会出现在那里——请从[完整发布列表](https://github.com/baptisterajaut/radmin-vpn-linux/releases)获取这些版本。
```bash
chmod +x RadminVPN-Linux-x86_64.AppImage
./RadminVPN-Linux-x86_64.AppImage
```
首次启动时,它会下载经过验证的 Radmin VPN 安装程序(或使用 `--installer` 传入你自己的安装程序)。终端会显示进度,并且需要一次 sudo 密码提示来完成 TAP 设置。
持久化状态(wineprefix、MAC、日志)存储在 `~/.local/share/radmin-vpn-linux/` 中。
> **Radmin 自行关闭?** 它自带的更新程序正在运行中的 prefix 内安装更新版本。
> 请以受控方式执行升级——这样可以保留你的 prefix 和 Radmin ID:
> ```bash
> ./RadminVPN-Linux-x86_64.AppImage --update
> ```
> 然后在 Radmin 设置中关闭“自动更新”,使其不再尝试。详情请参阅
> [Radmin 自带的自动更新程序](#radmin-自带的自动更新程序)。
## 前置条件(源码构建 / 非 AppImage)
- **Wine** >= 11.0(已在 Arch Linux 的 Wine 11.5 和 Ubuntu 24.04 的 Wine 11.6 上测试)
- **mingw-w64** 交叉编译器(`i686-w64-mingw32-gcc`、`x86_64-w64-mingw32-gcc`)——用于从源码构建
- **iconv**(glibc)——用于服务日志解析
- **sudo** 权限——用于 TAP 设备创建和路由配置
- **TUN/TAP 内核支持**——通常已内置,使用 `modprobe tun` 检查
- **Radmin VPN 安装程序**——如果缺失则自动下载。本项目验证过的版本在 `lib.sh` 中的 `RADMIN_VERSION` 一处固定(当前为 **2.1.4951.1**;2.0.4899.9 也可用)。Radmin VPN **1.4 不受支持**——它能够注册并打开适配器,但在 Wine 垫片下永远无法完成连接,导致 GUI 卡在“正在连接...”。
### Arch Linux
```bash
sudo pacman -S wine mingw-w64-gcc
```
### Ubuntu/Debian
```bash
sudo apt install wine64 wine32 gcc-mingw-w64
```
## 源码快速开始
```bash
git clone https://github.com/baptisterajaut/radmin-vpn-linux.git
cd radmin-vpn-linux
# 选项 A:从 GitHub Releases 下载预构建二进制文件
mkdir -p build
TAG=$(curl -sI https://github.com/baptisterajaut/radmin-vpn-linux/releases/latest | grep -i location | grep -oP 'v[\d.]+')
curl -sL "https://github.com/baptisterajaut/radmin-vpn-linux/releases/download/${TAG}/radmin-vpn-linux-${TAG}.tar.gz" \
| tar xz -C build/
# 选项 B:从源码构建
make
# 从 https://www.radmin-vpn.com/ 下载 Radmin VPN 安装程序
./run.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
后续运行时,只需:
```bash
./run.sh
```
### 命令行参数
| 参数 | 描述 |
|------|-------------|
| `--installer <path>` | `Radmin_VPN_*.exe` 安装程序的路径(仅首次运行)。 |
| `--no-ui` | 运行服务但不启动 Radmin GUI。 |
| `--update` | 就地升级 Radmin 到固定版本。保留 prefix,因此向 Famatech 注册的 RID 得以保留。 |
| `--no-broadcast-routes` | 不添加广播/组播 → TAP 路由。 |
| `--filter-ui` | 启动可选的 GTK4 数据包过滤 UI(默认关闭)。 |
| `--fix-chat` | 修补 Qt 的 `qwindows.dll` 以修复 Wine 下的聊天窗口崩溃问题(默认关闭)。 |
### Radmin 自带的自动更新程序
Radmin 的 GUI 可以下载更新版本并在*运行中的 prefix 内*运行其安装程序,
这会杀死 GUI 并使运行中的服务出错(GUI 日志随后会显示 `AppData\\Local\\Temp` 下的安装程序)。两道防线:启动器附带当前已验证的构建版本,因此更新程序无内容可推送;并且 GUI 崩溃不再拖垮隧道——它会被重启一次,然后 VPN 继续以无头模式运行。要主动升级,请使用 `./run.sh --update`,它会先停止所有内容。在 GUI 设置中关闭“自动更新”可以完全避免竞争条件。
`--filter-ui` 和 `--fix-chat` 均为可选启用。过滤 UI 需要 `rvpn_filter_ui` 二进制文件(由 `make` 构建);聊天修复需要 `patch_qwindows_font.py` 和 Python 3 解释器。
## 无头 / 服务器模式
两个额外的启动器可在没有本地桌面的情况下运行 Radmin VPN——适用于 VPS 或数据中心主机。两者的安装和配置方式与 `run.sh` 完全相同(相同的 wineprefix、相同的 `--installer` 首次运行流程、相同的 TAP 桥接);它们仅在 GUI 的访问方式上有所不同。
### `run_datacenter.sh` — 通过浏览器访问 GUI
在虚拟显示器(Xvfb)上运行真正的 Radmin GUI,并通过 VNC + noVNC 提供服务,因此你可以从浏览器配置网络,然后让它持续运行。
```bash
sudo apt install -y xvfb x11vnc websockify novnc # 或:make install-datacenter-deps
./run_datacenter.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
| 参数 | 默认值 | 描述 |
|------|---------|-------------|
| `--vnc-port <n>` | `5900` | x11vnc 服务器的端口。 |
| `--web-port <n>` | `6080` | noVNC Web 端点的端口。 |
| `--vnc-password <pw>` | *(无)* | VNC / Web 会话的密码。 |
| `--web-bind <addr>` | `127.0.0.1` | noVNC 监听的地址。 |
默认情况下 noVNC 绑定到 `127.0.0.1`,因此只能通过 SSH 隧道访问:
```bash
ssh -L 6080:localhost:6080 your-vps # 然后打开 http://localhost:6080/vnc.html
```
要公开暴露,请传入 `--web-bind 0.0.0.0` **并同时**使用 `--vnc-password`——否则任何能访问 Web 端口的人都会进入一个未认证、具有 root 权限的桌面。如果你在未设置密码的情况下公开绑定,脚本会打印一条醒目的警告。
### `run_vps.sh` — 仅服务,固定 GUID
以无头模式启动服务,使用硬编码的 TAP GUID(无需 Wine WMI),且完全不启动 GUI。如果上一级目录中存在自定义网络枚举器(`rv_net_enum.exe`,本仓库不附带),则会启动它;否则服务将一直运行直到你按 Ctrl+C。仅接受 `--installer`。
```bash
./run_vps.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
## 从源码构建
需要 `mingw-w64` 交叉编译器。如果你不想安装 mingw,可以从 [Releases](https://github.com/baptisterajaut/radmin-vpn-linux/releases) 获取预构建二进制文件(由 CI 在每个标记版本上构建)。
```bash
make # 构建所有内容到 build/
make clean # 移除构建产物
```
生成产物:
- `build/rvpnnetmp.sys` — Wine 内核驱动程序(64 位 PE)
- `build/adapter_hook.dll` — 钩子 DLL(32 位 PE)
- `build/rvpn_launcher.exe` — DLL 注入器(32 位 PE)
- `build/netsh.exe` — netsh 替代品(32 位 PE,安装到 SysWOW64)
- `build/netsh64.exe` — netsh 替代品(64 位 PE,安装到 System32)
- `build/drvinst.exe` — 无操作存根,替换 Radmin 真正的 NDIS 驱动程序安装程序(issue #12)
- `build/tap_bridge` — 原生 Linux TAP 桥接器
- `build/rvpn_dnsfix.so` — 原生 LD_PRELOAD 垫片,仅预加载到服务中
- `build/rvpn_reuseport.so` — 原生 LD_PRELOAD 垫片,预加载到 `wineserver` 中
- `build/rvpn_filter_ui` — 可选的 GTK4 数据包过滤 UI(`--filter-ui`)
### 构建 AppImage
```bash
make
./packaging/build-appimage.sh # → packaging/dist/RadminVPN-Linux-x86_64.AppImage
```
首次运行时下载 Kron4ek Wine-Staging `amd64-wow64` 构建(约 100 MB)和 `appimagetool`,两者都缓存在 `packaging/dist/` 中。需要 `curl` 和 ImageMagick(`convert`)。
## `run.sh` 的功能
1. **首次运行**:通过 Wine 安装 Radmin VPN(`/VERYSILENT`),移除真正的 NDIS 驱动程序(与 Wine 不兼容),注册我们的自定义驱动程序
2. **每次运行**:创建 TAP 设备,启动 TAP 到 FIFO 的桥接器,配置 Wine 注册表(适配器 GUID、驱动程序服务),启动 Radmin VPN 服务和 GUI
3. **退出时**(Ctrl+C 或关闭 GUI):终止 Wine,移除 TAP 设备,清理
wineprefix 存储在 `./wineprefix/`(源码运行)或 `~/.local/share/radmin-vpn-linux/wineprefix/`(AppImage)中。首次运行时会生成一个持久的 MAC 地址并保存在 wineprefix 中。
## 架构
| 组件 | 描述 |
|---|---|
| `rvpnnetmp.sys` | Wine 内核驱动程序。模拟 Radmin NDIS 微型端口。处理 IOCTL(VERSION、STATUS、SETUP、PEERMAC)、TLV 帧编解码、用于重叠 I/O 的 IRP 队列、基于 MAC 的帧路由以支持多对等节点。 |
| `adapter_hook.dll` | 与 RvControlSvc.exe 一起加载的伴随 DLL。IAT 钩子:将 TAP 适配器重命名为 Radmin 期望的名称,将 `RegSetKeySecurity` 变为无操作以绕过 Wine SCM 的一个 bug(服务缺少 SYSTEM SID),并应答 Wine 的 `advapi32` 未导出的四个 `Perf{Increment,Decrement}ULong{,Long}CounterValue` 查找。最后一项并非装饰性:Radmin 2.1 在一个全有或全无的级联中解析七个 perflib 入口点,然后将生成的每对等节点计数器对象作为对等节点握手的负载传递——因此四个缺失的遥测导出会导致整个数据路径瘫痪(issue #24,`docs/wine-perflib.md`)。 |
| `tap_bridge` | 原生 Linux 二进制文件。在 TAP 设备与 Wine 驱动程序读写的命名管道(FIFO)之间中继以太网帧。 |
| `netsh.exe` / `netsh64.exe` | 替换 Wine 的 netsh 存根(32 位在 SysWOW64,64 位在 System32)。通过基于文件的继电器将 Windows `netsh interface ip` 命令转换为 Linux `ip addr`/`ip link` 命令,在地址到达 root 继电器之前进行验证。 |
| `rvpn_launcher.exe` | 通过 `CreateRemoteThread` + `LoadLibrary` 将 `adapter_hook.dll` 注入 Radmin 服务进程。 |
| `drvinst.exe` | 无操作存根,替换 Radmin 真正的 NDIS 驱动程序安装程序。Radmin 在运行时运行它以加载 `NetMP60_1_1_64.sys`,该文件通过 `NdisInitializeReadWriteLock` 使 Wine 11.x 中止(issue #12);由于我们的驱动程序已经替换了该适配器,真正的驱动程序绝不能加载。 |
| `rvpn_dnsfix.so` | 原生 `LD_PRELOAD` 垫片,仅注入到服务启动中。在 glibc 层短路私有 IPv4/IPv6 的反向 DNS(`getnameinfo`/`gethostbyaddr`)。Radmin 会对其收集的每个本地候选地址进行 PTR 解析;在解析器黑洞 RFC1918 PTR 查询的主机上,这会停滞数分钟,服务注册了但永远无法就绪(issue #16)。该查找由 Wine 的 Unix 侧发出,`adapter_hook.dll` 内的任何钩子都无法触及。 |
| `rvpn_reuseport.so` | 原生 `LD_PRELOAD` 垫片,预加载到 `wineserver` 中(而非服务中——Unix 套接字属于 wineserver)。在创建时为每个 TCP 套接字设置 `SO_REUSEPORT`。Radmin 2.1 执行 TCP 端口复用 NAT 穿越:它将监听器绑定到 `uplink:PORT`,通告该映射,然后将其出站对等套接字以 `SO_REUSEADDR` 绑定到同一本地端口,Windows 允许这样做。Wine 将该请求转换为无 Unix 级 TCP 选项(`server/sock.c`,`SO_REUSEPORT` 仅编译为 `__APPLE__`),因此内核拒绝第二次绑定,客户端在每个对等连接上看到 `WSAEACCES`(issue #24)。Wineserver 自身的冲突簿记仍然运行,因此 Windows 会拒绝的绑定仍然会被拒绝。 |
| `rvpn_filter_ui` | 可选的 GTK4 UI,用于检查和过滤穿过桥接器的数据包。默认关闭;使用 `--filter-ui` 启动。 |
## 故障排除
**GUI 卡在“正在等待适配器”**:驱动程序未加载。检查 `/tmp/radmin_driver.log` 是否存在且有内容。如果为空,则驱动程序服务注册可能缺失——删除 wineprefix 并重新运行。
**已注册但从未“就绪”**:服务达到 `Registered as #…` 后停止。有两个已知原因,超时时打印的诊断块都会报告。(a)透明代理或具有自身路由策略的隧道(sing-box、clash/mihomo、v2ray 等)拦截出站连接——此类协议栈在本地应答 TCP 握手,因此套接字读取到 ESTABLISHED 但没有任何内容被中继。从其路由中排除 `26.0.0.0/8`,如果可以直接访问 `*.radminte.com` 也一并排除。(b)解析器黑洞了私有地址的反向查找(`172.17.0.1` 处的 `docker0` 是常见触发因素)。`rvpn_dnsfix.so` 处理此问题,但它是 64 位垫片:构建为 *old-wow64* 的系统 Wine 会忽略它。此处的解决方法——在 `/etc/hosts` 中为该地址添加一行,或使用 `contrib/run-in-netns.sh`(它能让服务达到“就绪”状态,但将 VPN 保留在命名空间内:主机应用程序无法访问 `26.0.0.0/8`,请阅读其头部的警告)。
**运行 Radmin 后你的*其他* Wine 应用损坏(`unimplemented function ndis.sys.NdisInitializeReadWriteLock`)**:1.0.0 之前的版本允许 Wine 的 `winemenubuilder.exe` 运行,它会重写主机的 Windows 文件关联(`.exe`、`.msi`、`.lnk`、`.reg`、`.chm` 等)以在*我们的* prefix 中打开。其他 Windows 程序随后会在 Radmin prefix 内启动,如果 Radmin 真正的 NDIS 微型端口(`RvNetMP60`)仍在那里注册,它们每个都会在该 ndis 存根上中止。1.0.1 在所有位置禁用 `winemenubuilder`,清除它为 Radmin prefix 写入的条目,并离线从 `system.reg` 中移除 `RvNetMP60`——只需运行一次 1.0.1,两者都会被修复。注意,在这种情况下 `echo $WINEPREFIX` 不显示任何内容:劫持位于 `~/.local/share/applications/*.desktop` 中,而非你的环境中。如果你愿意,可以手动清理:
```bash
grep -rl 'WINEPREFIX=[^"]*radmin' ~/.local/share/applications --include='*.desktop' | xargs -r rm
rm -rf ~/.local/share/applications/wine/Programs/"Radmin VPN"
update-desktop-database ~/.local/share/applications
```
**在线且在你的网络中,但没有对等节点能连接(`error: 0x700000000`)**:这是 1.1.0 之前构建上的 Radmin 2.1。服务日志在对等连接之前看起来正常,然后以该代码循环失败,最终以 `node offline` 放弃。两个独立的 Wine 缺口,均在 1.1.0 中修复,且都在上面的 `rvpn_reuseport.so` 和 `adapter_hook.dll` 行中解释过:Wine 不为 TCP `SO_REUSEADDR` 设置 Unix 级选项,因此 2.1 的端口复用 NAT 穿越在每个对等连接上得到 `WSAEACCES`;且 Wine 的 `advapi32` 缺少四个 perflib 导出,没有它们 2.1 会发送空负载作为其对等握手。注意该代码本身不携带任何信息——它是错误级联末尾的通用放弃信号,所以不要试图从中解读出子系统。修复方法是将此项目更新到 1.1.0;Radmin 侧无需任何更改。
**服务立即死亡**:检查 `/tmp/radmin_service.log` 中的 Wine 错误。常见原因:来自不同 Wine 版本的旧 wineprefix。删除 `./wineprefix/` 并重新运行。
**单个对等节点 0% 丢包,多个对等节点高丢包**:这是最初的 bug——已通过驱动程序中的基于 MAC 的帧路由修复。确保你使用的是最新构建。
**首次 ping 较慢(约 1 秒)**:最新版本会为驱动程序看到的每个 IP 数据包缓存 MAC 地址。如果目标 IP 在驱动程序运行期间至少发送过一个数据包,MAC 已被缓存,零 ARP 延迟。只有完全静默的对等节点(从未发送过数据包)才需要初始 ARP 解析,这增加约 1 秒。
**局域网游戏看不到其他对等节点 / “自动发现”失效**:大多数局域网游戏通过广播探测(UDP 到 `255.255.255.255`)或组播(`224.0.0.0/4`)发现彼此。在 Windows 上,Radmin TAP 驱动程序将自身通告为这些流量的首选接口;在 Linux 上你必须显式告知内核。`run.sh` 现在在 VPN 启动时安装两条额外路由:
```
ip route append 255.255.255.255/32 dev radminvpn0 metric 0
ip route append 224.0.0.0/4 dev radminvpn0 metric 0
```
副作用:物理局域网上的 mDNS / Bonjour / SSDP(Chromecast、AirPrint、Sonos、智能电视等)在 VPN 运行期间会走 VPN。如果你需要同时使用本地局域网发现和 Radmin,请使用 `--no-broadcast-routes` 运行:
```bash
./run.sh --no-broadcast-routes
```
这些路由限定在 TAP 设备范围内,因此当 `run.sh` 退出时拆除设备时会自动移除。
## 已知限制
- 同一时间只能运行一个实例(`/tmp/` 中的共享 FIFO)
- `26.0.0.0/8` 的 on-link 路由在运行期间影响整个系统(退出时清理)
- 默认广播(`255.255.255.255/32`)和组播(`224.0.0.0/4`)路由被导向 VPN——如果需要并行使用本地局域网 mDNS / SSDP,请使用 `--no-broadcast-routes` 禁用
- 较旧的 Wine 版本(< 11.0)可能具有不同的重叠 I/O 行为,导致驱动程序故障
## 备注
**Wine bug 变通方案。** `RegSetKeySecurity` 钩子绕过了[已知的 Wine 限制](https://forum.winehq.org/viewtopic.php?t=37183),即服务无法获得 SYSTEM SID(S-1-5-18)。这可能在未来的 Wine 版本中在上游修复。
## 致谢
逆向工程和 Wine 垫片:[ @baptisterajaut](https://github.com/baptisterajaut)。
- [@ayozetr](https://github.com/ayozetr) — 将“已注册但从未就绪”的挂起问题追踪到主机解析器黑洞的私有地址反向 DNS 查找,在四个错误理论(包括我的)被排除后,用裸 libc 复现证明了这一点,并编写了 `src/rvpn_dnsfix.c` 和 `contrib/run-in-netns.sh` 的原始版本。
- [@gringoestrangeiro](https://github.com/gringoestrangeiro) — Linux 稳定性工作:数据包过滤器、ARP 缓存、崩溃恢复、无头和数据中心模式。
- [@yuxiaole-bili](https://github.com/yuxiaole-bili) — 发现 Wine 的 SCM 会自动启动第二个未挂钩的服务实例,与注入的实例争夺适配器。
- [@D1spell-tech](https://github.com/D1spell-tech)、[@Milkiway13](https://github.com/Milkiway13) 以及所有粘贴完整诊断块的人——两个独立的主机才能让 bug 可被发现。
## 许可证
GPL-3.0。请参阅 [LICENSE](https://github.com/baptisterajaut/radmin-vpn-linux/blob/main/LICENSE)。
从精神上讲,此代码属于公共领域——你可以随意使用。GPL 在此作为法律安全网:它明确保护为互操作性而进行的逆向工程,这正是本项目所做的。双保险。
Radmin VPN 是 Famatech Corp. 的专有软件。本项目仅提供互操作性工具——不包含也不分发任何 Famatech 代码。