为什么要自编译

尽管 OpenWrt 官方有编译好的版本,但是出于以下需求,有时候可能会需要自行编译一份:

  • 某些插件需要随同固件一起编译,而不是单独使用 SDK 编译(这应该是最常见的需求);
  • 深度自定义固件中的预装插件。

UA2F 的作用

声明

以下措施仅供网络技术的交流与测试,严禁用于违规违法用途。请您遵守您所在组织的网络使用限制及安全管制。

部分院校会有同一网口限制上网设备数量的规定。比较简单的做法是,通过检查从校园网内部发起的 HTTP 请求的 user-agent 字段,即可识别不同的上网设备。使用下级路由器(即插网线的电脑开热点)的方法不能规避这一措施,因为显然正常来说家庭路由器不会修改 HTTP 请求的 user-agent 字段。

备注

User-Agent 字段是HTTP请求头中的一部分,用于标识发起HTTP请求的用户代理(通常是浏览器、应用程序或机器)。它包含了有关用户代理的信息,以便服务器能够识别和适应请求的源头。服务器可以针对不同的设备提供不同的服务(如手机、电脑端两套渲染)、跟踪与分析不同浏览器和设备的使用情况。

那么,应对方法自然就是统一所有设备的 ua 字段,统统变成同一个 ua,就能欺骗这套逻辑。在每一台接入网络的设备上统一 ua 字段是不现实的,只有在网关(也就是路由器)处对出口流量处理才具备可行性。

需要注意的是,处理 UA 字段仅限于 HTTP 请求。HTTPS 会加密通信链路——自然包括请求头,因此 HTTPS 请求的 UA 字段无法在网关处修改,也无法被网络安全服务器窥探。同样,VPN 也会加密通信链路。

除了检测 UA 这种简单手段外,还有其他更先进的检测方案。本文不涉及这些方案。有需求者请自行参阅:https://blog.sunbk201.site/posts/crack-campus-network

方法一:使用自己的电脑编译

使用自己的电脑需要全程科学上网,否则很多软件包没法下载。下面以虚拟机中的 debian 11 做示范(安装可见:从零开始配置 debian 虚拟机 保姆级教程)。

本文中 OpenClash 部分参照: 编译带 OpenClash 的 OpenWrt 固件 | 心底的河流

配置:

  • 实体机:锐龙 3700X 8 核 16 线程 CPU
  • 虚拟机:1 CPU 16 核心,16GB 内存,40GB 磁盘空间
  • 虚拟机网卡为桥接模式,能访问到实体机

注意

不要在 root 用户下编译。

设置代理

首先回到家目录:

cd $HOME

在宿主机上开启小猫,允许 LAN 连接(网上很多教程)。然后在 debian 中设置代理:

export http_proxy=http://192.168.1.101:7890
export https_proxy=http://192.168.1.101:7890

其中,192.168.1.101 是宿主机的局域网 IP。

下载源代码

# 这里我下载的是 immortalwrt 的源码,保存到openwrt文件夹
# 有需要可以使用原版 openwrt 或者 LEDE 等
git clone https://github.com/immortalwrt/immortalwrt openwrt

# 下载 OpenClash,没有需求的可以不要
wget https://github.com/vernesong/OpenClash/archive/master.zip
unzip master.zip
cp -r OpenClash-master/luci-app-openclash openwrt/package

cd openwrt

# 下载自己所需的一些包
# 比如我下载了 UA 修改
git clone https://github.com/CHN-beta/rkp-ipid package/rkp-ipid
git clone https://github.com/Zxilly/UA2F package/UA2F

# 切换到 immortalwrt v21.02.7 版本
git checkout v21.02.7

./scripts/feeds update -a
./scripts/feeds install -a

配置要被编译进固件的软件包

make menuconfig

会弹出一个图形界面。按上下方向键选择,回车进入,按两次 esc 返回到上一级,y 选中,m 恢复默认,n 排除。

## 选择系统(以 x86_64 为例)
Target System -> x86
Subtarget -> x86_64


## 修改软件包可用空间,默认安装会占用 160M 左右,扩更大可以以后安装更多软件
## 我修改为了 300 MB
## 虽然其实自编译以后也不会安装太多软件
Target Images -> Root filesystem partition size

## 添加web界面
LuCI > Collections -> Luci

## 添加兼容性依赖
LuCI > Modules -> luci-compat

## 添加中文
LuCI > Modules -> Translations -> Chinese Simplified

## 添加openclash,还有其他两个小工具(不需要可以不加)
LuCI > Applications -> luci-app-openclash
LuCI > Applications -> luci-app-adblock
LuCI > Applications -> luci-app-ddns-go  

## 添加主题
LuCI -> Themes -> luci-theme-argon

## 添加wget
Network -> File Transfer -> wget-ssl

## 添加ua2f
Network -> Routing and Redirection -> ua2f
Network -> Routing and Redirection -> ua2f -> Enable custom User-Agent

## 添加kmod-tun,openclash TUN模式必须
Kernel modules -> Network Support -> kmod-tun

## 排除dnsmasq,由于默认会安装dnsmasq-full,这里需要排除dnsmasq,否则会冲突报错。
Base system -> dnsmasq(取消勾选) 

然后按方向键,保存配置到 .config ,如下图。

使用键盘方向键移动。鼠标仅用于指示位置,实际上不能点击

最后一直双击 esc 直到退出,回到 ssh。

然后 vim .config,在开头添加一行:(UA2F 插件需要)

CONFIG_NETFILTER_NETLINK_GLUE_CT=y

(可选)可以在宿主机的 cmd 中使用 scp 命令传输到宿主机中:

# kaede: 虚拟机用户名
# 192.168.1.109: 虚拟机 IP
scp kaede@192.168.1.109:$HOME/openwrt/.config config.txt

编译

# 下面的核心数根据自己虚拟机的核心数修改
# -j8: 使用8个内核,V=s: 为故障排除目的提供更详细的信息
make -j8 download V=s

# 开始编译,16是16核心 
make -j16 V=s

下载耗时根据网络状况而不同,我耗时约 10 分钟;编译半个小时左右。

成功后,使用下面命令打包:

zip -r bin.zip bin/

然后在宿主机的 cmd 中使用 scp 命令传输到宿主机中:

# kaede: 虚拟机用户名
# 192.168.1.109: 虚拟机 IP
scp kaede@192.168.1.109:$HOME/bin.zip openclash-bin.zip

方法二:使用 Github Actions 编译(推荐)

使用 Github Actions 的 CI/CD 流程简单方便,而且下载软件包时没有科学上网的问题,非常适合新手、自己电脑性能不强的人使用。

首先 fork 我的仓库:https://github.com/Eterance/Actions-OpenWrt-x86-64-UA2F

有需求可以选择修改 /.github/workflows/Main-immortalwrt.yml(或者其他文件)的 TAG_PREFIX,修改为所需要的描述。

然后修改 .config。最好是先用上面的本地 make menuconfig 图形界面里修改,然后再把内容复制到仓库中。

最后,在 Action 运行工作流。如果没有其他插件,大约需要编译一个半小时;插件越多编译时间越长。

编译好之后,点进去 workflow 可以直接在下面下载固件合集:

或者回到仓库主页,在 release 处下载:

安装固件

安装编译好的固件。参见:

个人数字基建(1):在 VMware 安装 OpenWrt 主路由

个人数字基建(2):在 VMware 安装 OpenWrt 旁路由

修改内核指纹

解决方法来源: 好像无法使用部分官方源安装,会报Kernel版本不对 · Issue #8 · iyuangang/openwrt · GitHub

如果使用了自编译的固件,在后台安装一些依赖于内核的软件时,如下图:虽然内核版本一致(5.4.225-1),但是指纹(后面的字符串)不一致,导致无法安装。

首先,右键检查元素,在控制台中复制当前的指纹字符串和需要的指纹字符串。(下面动图是 edge 浏览器)

然后 ssh 连接 openwrt,输入命令:

# 备份一下,出错可以恢复
cp /usr/lib/opkg/status /usr/lib/opkg/status.bak
# 使用 vim 打开
vim /usr/lib/opkg/status

先输入英文冒号 :,然后输入并回车:

%s/<当前自编译的指纹>/<官方指纹>/g

最后输入 :wq 保存并退出,输入 reboot 重启 openwrt。

配置 UA2F

参考资料:

Immortalwrt/openwrt 编译UA2F

路由固件存档 - 伴无言的小窝

GitHub - Zxilly/UA2F

WebUI

首先进入网页后台,关闭所有加速:

然后在启动项添加一行,保存:

ipset create nohttp hash:ip,port hashsize 65535 timeout 300

命令行

接着 ssh 连接 openwrt,输入以下命令:

# 启用 UA2F
uci set ua2f.enabled.enabled=1

# 可选的防火墙配置选项
# 是否自动添加防火墙规则
uci set ua2f.firewall.handle_fw=1

# 是否尝试处理 443 端口的流量, 通常来说,流经 443 端口的流量是加密的,因此无需处理
uci set ua2f.firewall.handle_tls=1

# 是否处理微信的流量,微信的流量通常是加密的,因此无需处理。这一规则在启用 nftables 时无效
uci set ua2f.firewall.handle_mmtls=1

# 是否处理内网流量,如果你的路由器是在内网中,且你想要处理内网中的流量,那么请启用这一选项
uci set ua2f.firewall.handle_intranet=1

# 应用配置
uci commit ua2f

# 开机自启
service ua2f enable

# 启动 UA2F
service ua2f start

# 重启路由器
reboot

测试

电脑 edge 浏览器按下 F12 打开开发者工具,并且模拟为安卓设备:(chrome 可能有所不同)

打开网址:http://ua.233996.xyz/。如果显示的两个 UA 不同,且点击 TLS 的两个 UA 相同,说明成功:

备注

如果没有成功,请检查是否开启了代理。如果有则需要关闭。