## 需求
从 GL iNet 那边淘来了不错的玩具:GL-USB150。其固件是基于 OpenWRT 进行二次开发,并且没有对原始的 LuCI 界面进行二次编译。内置固件里集成了 a65535 设计的 Shadowsocks 和 ChinaDNS 配置界面。此次修改目的是给 Shadowsocks 界面添加全局忽略代理的路由表,以及启动 ChinaDNS 时自动作为 Dnsmasq 的上游两个功能。
## 功能集成方案
得益于 `squashfs`,`jffs2` 和 `mini_fo` 的有机结合,OpenWRT 的文件系统是**完全**可读可写,因此直接修改文件系统内对应的脚本即可完成上述功能的补充。但是,一次的恢复出厂操作会让所有修改丢失。因此,修改测试妥当之后,通过修改固件的方式“固化”到固件并刷入,不失为比较好的方案。
## OpenWRT 固件组成(AR7xxx/AR9xxx)
目前已知的是,此类 CPU 平台的 OpenWRT 的固件由三部分组成:Linux 内核,squashfs 镜像和约 64KB 的 jffs2 填充部分。分割需要借助 16 进制的文本编辑器确认分割位置。分割依据如下:
* 从头部开始,到出现 `0x68 0x73 0x71 0x73` (即 ASCII 字符“hsqs”,不含这 4 个字符)为止,为 Linux 内核部分。
* 从 `0x68 0x73 0x71 0x73` (即 ASCII 字符“hsqs”,包含这 4 个字符)开始,到出现大规模连续的 `0xFF` 之前,为 squashfs 镜像部分。
* 剩余大约 64KB 的 `0xFF` 及其末尾少量非 `0xFF` 的部分,为 jffs2 的填充部分(可能还包含部分 ART 数据)
## 作业准备
* Ubuntu/Debian 作业系统,并已安装 `squashfs-tools` 软件包
* CPU 平台对应的 OpenWRT SDK
* 待修改固件,以及需要集成的脚本
## 作业过程
1.使用 16 进制编辑器(例如 WinHex)打开要修改的目标固件。
2.利用字符串搜索快速定位 ASCII 字符“hsqs”,并记录其位置。此处录得位置为 `0x150000`

3.同样利用字符串搜索,或者直观观察,找出大规模 `0xFF` 区域的起始位置。此处录得起始位置为 `0x721BDA`

4.利用 Windows 自带的计算器,快速换算 `0x150000` 和 `0x721BDA` 对应的 10 进制为 `1376256` 和 `7478234`,这两个数值代表了固件切割的位置点(单位:Byte)
5.进行计算:第一部分,即内核部分大小为 `1376256 Byte`;第二部分 squashfs 大小为 `7478234 - 1376256 = 6101978`;第三部分由于直接截取到尾部,无需计算。
6.使用 Linux 的 `dd` 命令进行切割并得到三个部分的 bin。
```bash
# dd if=origin.bin of=vmlinux.bin bs=1 ibs=1 count=1376256
# dd if=origin.bin of=squashfs.bin bs=1 ibs=1 count=6101978 skip=1376256
# dd if=origin.bin of=jffs2.bin bs=1 ibs=1 skip=7478234
```
7.使用 Linux 自带的 `unsquashfs` 命令解压 `squashfs.bin` 到 `squashfs-root` 目录
```bash
# unsquashfs squashfs.bin ./squashfs-root
```
8.`squashfs-root` 即等同于 Openwrt 系统的 /rom 目录。 将需要改写的,新增的脚本放置在相应的目录。
9.使用 Openwrt SDK 里面的 `mksquashfs4` 进行打包,压缩为新的 squashfs.bin。([参数释义](#SDK-中的参数释义))
```bash
# <SDK Path>/staging_dir/host/bin/mksquashfs4 ./squashfs-root ./new-squashfs.bin \
> -nopad \
> -noappend \
> -root-owned \
> -comp xz \
> -Xpreset 9 \
> -Xe \
> -Xlc 0 \
> -Xlp 2 \
> -Xpb 2 \
> -b 256k \
> -processors 1
```
10. 连接 `vmlinux.bin`,`new-squashfs.bin` 和 `jffs2.bin` 重新生成新的固件。并刷入即可
```bash
# cat vmlinux.bin new-squashfs.bin jffs2.bin > new-firmware.bin
```
## 其他
#### SDK 中的参数释义
* -nopad: 不要将固件大小填充至 4K 的倍数
* -noappend: 不附加到现有的文件系统(即完全覆写模式)
* -root-owned: `-all-root` 参数的别称,将所有文件的 **所有者** 置为 `root`
* -comp xz: 使用 `xz` 模式压缩
* -Xpreset 9: 压缩等级 `9`
* -Xe:尝试占用更大的 CPU 来提高压缩率
* -Xlc: 原文是 `Number of literal context bits (0-4, default 3)`,暂未知作用
* -Xlp: 原文是 `Number of literal position bits (0-4, default 0)`,意义不明 +1
* -Xpb: 原文是 `Number of position bits (0-4, default 2)`,意义不明+2
* -b: 将数据块设置为指定大小,默认 131072 字节。此处设置为 256KB
* -processors:使用指定颗数的处理器去压缩固件。默认是使用所有可用的处理器。
OpenWRT固件修改记