对于大多数想要设置 Nethunter 设备的人来说,最大的障碍是内核要求。除非你的设备已经有预构建(并持续维护)的内核,否则你会被告知需要自己构建一个。关于这一过程已经有大量文档,但根据我的经验,很难知道哪些内容适用于你的情况,哪些不适用。
这个问题因 混蛋 那些撰写标题党指南(如“为任何 Android 设备构建 Nethunter 内核!”)或作出虚假承诺(如“十分钟学会编译 Nethunter 内核!”)的人而更加严重。另一个 2025 年互联网的不幸现实是,许多 白痴 人认为 ChatGPT 或其他大型语言模型对他们的疑问拥有权威答案,而这些模型真正拥有的,只不过是从前述混蛋那里抓取的信息。
我正在尽可能彻底和诚实地撰写这份指南。然而,它并非意在成为你完成这一过程所需的唯一文档——你肯定会有这份文档无法解答的问题。我鼓励你在相关软件文档中寻找这些问题的答案,而不是在 YouTube 或 ChatGPT 上寻找。
在开始本流程的任何步骤之前,你应该已经完成以下操作:
apt update && apt upgrade -y (可选:已添加元包)要完成本流程,你将需要:
cd, rm, ls, mkdir, find, diff, grep, 等)vim 适合酷小孩,否则用 nano)的基本知识build-devel,Debian 系列为 build-essential)——我还建议你安装 Perl 和 Python3,因为一些内核 Makefile 需要它们这对我的设备可行吗?
Linux 内核采用 GNU 通用公共许可证 2.0 授权,这是一份 copyleft 许可证。这意味着 Linux 内核的源代码可以自由分发,你也可以随意修改源代码(正如每家 Android 制造商所做的那样),然而,你修改后的源代码也必须自由分发。
不出任何人所料,许多使用 GPL 授权代码的巨型公司都在藐视该许可证的条款。他们要么拒绝分发源代码,要么只发布其中一部分(通常是残缺的、过时的,并且包含内联二进制 blob)。如果你的设备没有可用的内核源代码,那么就没有办法为其创建 Nethunter 内核。
你解锁引导加载程序和安装自定义 ROM 的能力也在很大程度上取决于设备制造商的意愿。即使他们允许解锁引导加载程序,并且你已能够通过 Magisk root 手机,但如果他们不发布设备树,那么很可能你的设备就没有自定义 ROM。在这种情况下,他们发布的(如果有的话)内核源代码自发布以来是否得到过任何维护,甚至是否能在不做任何修改的情况下构建成功,都非常值得怀疑。如果你没有看到任何其他人在为你的设备维护任何东西(内核、ROM、恢复模式),那么你将很难(如果不是不可能)把该设备变成一台完整的 Nethunter。
关于内核版本的说明
假设你的设备有定期更新和维护的自定义 ROM/内核/恢复模式,但就是没有 Nethunter 内核。在这种情况下,你应该能够完成这一过程,但该过程会因你的设备使用的 Linux 内核版本不同而有所差异。
本指南中的信息基于我修改 4.14.x 内核的经验。有一些更古老的指南讲述了使用 GCC 工具链(而不是我们将使用的 LLVM/clang),这些适用于 3.x 内核。如果你的设备使用 5.x/6.x 版本的 Linux 内核,那么本指南中的信息将不足以完成此过程。 Android 的构建方式一直在变化,你应该寻找涵盖 GKI 内核以及 Bazel/Kleaf 使用方法的指南。
找到包含设备内核源代码的仓库的一个好方法是使用它的代号。每台 Android 设备都有一个代号,尽管一些制造商(例如一加)在这一点上比其他制造商(例如三星)更有趣一点。我的小米 Poco X3 NFC 的代号是“surya”,而我的三星 A51 的代号是“SM-A515F”。通常,内核源代码仓库遵循命名约定
android_kernel_MANUFACTURER_CODENAME
其中 manufacturer 是母公司,例如 android_kernel_xiaomi_surya,而不是 android_kernel_poco_surya。
然而,也可能存在围绕你设备的 SoC(片上系统)的仓库,有时“统一”包含相同 SoC 的其他设备的源文件。我提到的三星就是这种情况,它的源代码仓库名为 android_kernel_samsung_exynos9611。去 GitHub/GitLab 上找找看,看看你能发现什么。
为了本指南的目的,我将使用 surya 上最新的 LineageOS 22.2(构建于 2025-08-04),因此,我想从 LineageOS 仓库克隆内核源代码。但在此之前,我想把两个文件从我的设备复制到我将用来构建内核的电脑上:/proc/config.gz 和 /proc/version。
我可以把第一个文件 /proc/config.gz 复制到 /sdcard,通过 ADB 从设备上拉取它,gunzip 它,然后重命名。但这似乎步骤太多,所以我做的是这样的:

然后,在设备本身上,我(以 root 身份)运行了

这里发生了什么?
在我的笔记本电脑上:nc(netcat)-l(监听传入连接)-p(端口)4545(实际上这可以是 1024-65535 之间的任何数字,但我通常使用 4545)>(写入文件)surya-defconfig-lineageos22.2-20250804(描述性文件名)<(从文件输入)/dev/null(空设备——这确保如果我碰到键盘,它不会把击键发送到我正在接收的文件中,从而可能弄乱它)。
在手机上:zcat(类似于用于拼接的 cat 命令,但用于 gzip 压缩的文件)/proc/config.gz(当前运行内核构建时使用的内核配置)|(将该进程的输出发送给下一个进程作为输入)nc(又是 netcat)192.168.1.42(我笔记本电脑的本地 IP 地址)4545(我之前设置的端口)
虽然这应该能给你用于构建当前运行内核的 .config 文件,但情况并不总是如此。一些较新的设备/更新的 ROM 构建,如果 /proc/config.gz 中保存的配置与用于构建出厂内核的 .config 不匹配,将拒绝启动。幸运的是,这是唯一会进行的检查(而不是像整个内核的 SHA256 校验和那样更难绕过的东西),所以一些聪明的人已经想出了欺骗 /proc/config.gz 中的文件以匹配出厂内核的方法,尽管他们做了修改。如果你不确定设备中的文件是真的还是被篡改的,你可以执行 zcat /proc/config.gz | head,然后执行 uname -r。如果发布号不匹配,那么 /proc 中的文件就是被篡改的。在这种情况下,虽然你仍然可以继续阅读本指南,但你或许应该运行 diff,将提取出的配置与你在内核源代码的 arch/arm64/configs 目录中找到的一些文件进行比较。
至于 /proc/version,也许没有必要发送该文件,因为我们真正需要的只是关于用于编译它的 clang/ld.lld 版本的信息。所以我们可以直接运行 cat /proc/version,然后看看:

哎呀!看来构建这个内核的人有点“失误”,他们的 Makefile 记录了用 clang 运行的所有 -CFLAGS,但没有记录 Clang 的版本。不过,我们可以看到他们使用了预构建的 Android 工具链,并且使用了 ld.lld 19.0.1 版本。
这里有一张更有帮助一点的图片。第一个输出来自在三星 A51 上修改、构建和安装新内核之前的 /proc/version。第二个输出来自当前 /proc/version。

注意到什么了吗?(不,不是我那奇怪的用户名和主机名。)
选择工具链的最佳技巧,是使用编译当前运行内核的完全相同的工具链。
在这种情况下,那就是 Neutron clang 18.0.0git,它相当容易找到:

你瞧,md5sum 也匹配,等等一切正常。
为了好玩,我们快速看一下一台相当老的设备——三星 J7(2016),代号 j7xelte 的 /proc/version 字符串:

这台设备已经足够老旧,仍然使用 GCC 工具链来编译内核。
但当我搜索“gcc version 4.9.x 20150123 prerelease”时,我找到了两个仓库:

那么我应该下载哪一个?
答案是:两个都要。GCC 与 Clang 的不同之处在于,在为交叉编译构建编译器/binutils/链接器等时,它需要为每个“target triple”分别构建一个工具链。Target triple(据说)遵循以下格式
machine-vendor-operating_system
但正如我们将看到的,这条“规则”有无数例外。不过让我们从“machine”开始。
在本文档的“你将需要”部分,我说过你需要一台使用 Intel 或 AMD 处理器、运行 GNU/Linux 的 64 位计算机。那是因为我们将要使用的工具链是为在 x86_64-linux-gnu 上执行而编译的。
然而,这些工具链是交叉编译器,意味着它们从 C 源码文件生成的机器码不会在那台计算机上运行,而是在另一台具有自己的 target triple 的计算机上运行。
Aarch64,也称为 arm64,是几乎每台 Android 设备都使用的架构。不带“64”的 Arm,指的是 ARM 芯片的 32 位实现。大多数 Android 内核使用 aarch64 和 arm 两种 triple 编译,以提供对 32 位软件的向后兼容性。
继续看下一部分:“Linux”。不言自明。它是 Linux 内核。
但是这些“triple”的最后一部分值得解释一下,至少简要解释一下,因为它实在是一团乱麻,而且每个使用“triple”的编译器(GCC、Rust、Go、LLVM)的做法都略有不同。第一个例子“Android”说得通,但“androideabi”是什么?EABI 代表“embedded application binary interface”(嵌入式应用程序二进制接口),但你真的不必太担心这一点——我们就把 EABI 当作“基础”的第三个值吧。你还会看到“gnueabi”,现在它更有意义一些了——EABI 的 GNU 实现,对吧?(这实际上指的是 GNU 的 C 库 glibc,这就是为什么你在针对像 musl 这样的替代 C 库构建时,还会看到像 arm-linux-musleabi 这样的 triple)。你可能还会看到“gnueabihf”。HF 代表“hard float”(硬浮点),如果你想知道 ARM 是如何实现浮点整数运算的片上解决方案的,去读一篇 Wikipedia 文章吧。
以下是我们需要知道的:
如果使用 LLVM 工具链,我们将通过在命令行中设置 CROSS_COMPILE=aarch64-linux-gnu- CROSS_COMPILE_32=arm-linux-gnueabi- 来声明交叉编译的意图(就是这样,末尾带有尾部的 -)。
如果为非常老的设备编译,因此使用 GCC 工具链,我们将需要两套文件,这些文件以不同的 triple 作为前缀。这些 triple 可能与 LLVM 示例中的值相同,也可能不同,就像 j7xelte 的工具链那样。
但为什么有这么多方式来表达同一个东西?i386、i686、386、i32、i64、x86、x64、x86_64、amd64——这样的变化会有什么可能的原因呢?!
来自 xkcd 的相关漫画:

回到我今天要编译的示例(surya),我碰巧保存了来自另一个内核的 /proc/version 信息,内容如下:

这差不多就是它应有的样子,包含链接、校验和、编译器/链接器的版本等等。既然这里的 clang 和 LLD 版本都是 17.0.3,而我们当前 LineageOS 中被篡改过的 /proc/version 显示 LLD 19.0.3,我认为我们有理由寻找一个同时具有 19.0.3 两个版本的工具链。
大约搜索了三分钟后,我找到了一个仓库,我们可以将其克隆为子模块(以便更容易实现可重现构建):https://gitlab.com/kei-space/clang/r536225/ 那个工具链后来被证明有严重问题,所以我改用了 Neutron Clang 19。(我们稍后还会回到这一点。)
因此,我们的源代码和工具链已经准备好克隆,这让我们进入:
我决定为本教程创建一个新用户,主要是为了可以使用 gh auth login 并运行提交,而不会意外提交到我主要的 GitHub 账户。但要做到这一点,我必须(在浏览器中)创建一个账户,设置一个新用户,为他们生成 SSH 密钥……

将 .ssh/id_ed25519.pub 复制到 GitHub,然后在终端中再次运行 gh

然后我就准备好了

……差不多。我还必须在浏览器中 fork 该仓库,

然后在终端中 init/clone 它。

所以一旦一切就绪(源代码已更新,origin 已设置,等等),我们就可以开始克隆子模块了。

语法是 git submodule add URL directory/

记住,为了获得非常干净的提交历史,我们希望在每次更改后运行 git add . 和 git commit -a。

我现在还不打算运行 git push,但当我最终运行它时,它会更新所有提交。
到这里,你可能预期指南会讨论修改内核源代码以添加 Nethunter 支持。那很快就会到来。然而,首先,让我们看看未经修改的源代码在我們的工具链和配置下如何编译。

当前运行内核中的那个配置需要被复制到内核源代码目录中,但由于我们将使用 out/ 存放编译出的二进制文件,我也需要把它复制到那里。我还需要用工具链的 bin/ 目录来追加我的 PATH 变量。我可以写 export PATH=/home/build_user/android_kernel_xiaomi_surya/toolchain/bin:$PATH,但更简单的方法是直接 cd 到该目录,然后运行 export PATH=$(pwd):$PATH。$() 会展开为其所包含命令的结果,而 pwd 会列出当前工作目录的完整路径。
我还列出了工具链 bin/ 目录的内容,这样你可以看到 LLVM binutils 如何有自己的前缀。理想情况下,Makefile 中包含一条指令:如果我声明 LLVM=1,它会自动设置 AR=llvm-ar、AS=llvm-as 等。那么让我们看看 Makefile 里有什么。

太棒了!我可以直接声明 LLVM=1,而无需单独声明其余部分……大多数情况下。我还需要使用 LLVM 汇编器,但它有自己的声明,LLVM_IAS:

所以现在我需要声明的只有 ARCH=arm64 LLVM=1 LLVM_IAS=1 AS=llvm-as O=out CROSS_COMPILE=aarch64-linux-gnu- CROSS_COMPILE_32=arm-linux-gnueabi-。这看起来可能很多,但远比逐个声明每个 binutil 要少。
那么让我们运行命令来打开配置……
等等。看来这个工具链有些问题。所以我决定取消初始化子模块,改而从这里获取 Neutron Clang 19.0.0,我将用 wget 下载它。

然后解压到一个新创建的 toolchain/ 目录中(一开始忘记了如何解压 .tar.zst 文件的语法)

然后我将这个目录添加到 .gitignore 文件中。
现在当我运行这个时:

它成功了,打开了 nconfig 菜单:

许多教程会告诉你使用 menuconfig,但我更喜欢 nconfig,因为 1) 它看起来更好看 2) 它不像 menuconfig 那样倾向于因为找不到你已安装的 ncurses 库而无法加载。无论如何,这只是一个测试内核,所以我们可以加载 .config 并保存它(再次作为 .config),目前就完成了。
现在我们尝试构建 Image.gz,然后……

我们的第一个错误!万岁!
根据这个,这是因为 Arch 的一次更新破坏了某些东西。
虽然 Arch 会拿走,但 Arch 也会给予,这次是以 AUR 包 libxml-legacy 的形式。所以我安装了它,再次运行 make,这次:

给 LineageOS 维护者点赞,因为整个内核构建只出现了一个警告:

所以现在在 out/arch/arm64/boot/ 中,我们可以找到 Image.gz。但我们该如何将其刷入设备呢?
我们需要制作一个 AnyKernel3 zip。
有两种方法可以做到这一点:
Image;可能就是我们做好的 Image.gz,仅此而已;可能是 Image.gz-dtb(一种设备树/内核组合镜像——这类镜像现在不那么常见了,但如果你的设备需要这种格式,你就得在内核配置里启用 “Build a concatenated Image.gz-dtb”);也可能是 Image.gz、dtbo.img 和/或 dtb.img。
为了写这份指南,我下载了 Telegram 上众多常见的、带个蠢萌动漫名字却没有任何源码链接的内核之一。永远不要安装这类内核。
但我们不会这么做,别担心,我们只是借用一下它们的 AnyKernel zip 配置。所以我重命名并解压了这个文件

我还编辑了 anykernel.sh,把内核名字符串也改掉了。现在,我们用新的 Image.gz 覆盖那个 zip 里的旧文件

使用 zip -f(即 “freshen”)只会把 zip 内的文件替换成新版本。0 表示零压缩。
来试试看能不能刷入这个内核并让它开机,不过在此之前:

在文件名里带上时间和日期,会更容易追踪各个内核 zip。

不过,为了做到面面俱到(也为了尽可能地不依赖 Telegram 上的那些内核),我会告诉你怎么自己制作 dtb.img 和/或 dtbo.img 文件,因为相关文档实在少得可怜。
AOSP 里有两个用于此目的的程序。其中一个 mkdtboimg 是用 Python 写的,所以直接下载并运行 mkdtboimg.py(来自 AOSP 官方网站)即可。但你可能还需要另一个 mkdtimg,它是用 C 写的,而且没有附带 Makefile。根据文档,使用其中的 Android.bp 文件就够了——但你得先检出整个 AOSP 源码,才能运行文档里列出的命令。我觉得这简直离谱,所以我就拿别人很久以前做好的 Makefile 来单独编译这个工具,把源文件更新到最新版本,然后用 Musl 静态 PIE 编译出二进制。你可以从这里下载这个二进制文件(它能在任何 x86_64 Linux 电脑上运行,比如你现在用来编译内核的那台),也可以从源码自己构建。
接下来是麻烦的地方:内核构建过程中产生的 .dtbo 文件,若想转换成 dtb.img/dtbo.img,还得额外花点功夫。Android 需要调用 dtc 时加上 -a 64 参数。既然我们已经在用 DTC_EXT=/usr/bin/dtc 来强制 Makefile 使用软件源里那个更新更好的 DTC(而不是许多内核自带的那个有问题的版本),那么把这条命令放进单引号里并加上参数其实也不难。你只需传入 DTC_EXT='/usr/bin/dtc -a 64'。不过,就我而言,我喜欢为我维护的所有内核编写(并测试)build.sh 脚本,这样即使没读过这份指南,任何人——无论多菜——都能从源码自己构建。正当我在无数层单引号与双引号的 bash 逻辑里挣扎时,我想到了一个简单(尽管不太优雅)的解决办法,能确保 DTC 带上正确的参数被调用:一个包装脚本。
我只需在内核顶层目录创建一个名为 dtc 的可执行文件,内容如下:
#!/bin/sh
exec /usr/bin/dtc -a 64 $@
然后把 DTC_EXT= 指向这个文件,问题就解决了。
你可能会问:“什么 .dtbo 文件?我编译内核时从来没生成过这些!”如果是这样,请检查这个内核符号是否已启用:

那么,这些工具该怎么用呢?我做的第一件事,就是分析其他内核 zip 里找到的 dtb.img 和 dtbo.img,看看它们是什么类型的文件。(我知道,我知道,我们不想依赖那些古怪的自定义 ROM 圈子里的内核,但这一检查确实值得做)。执行 file dtb.img 得到:
dtb.img: Device Tree Blob version 17, size=345849, boot CPU=0, string block size=31481, DT structure block size=314312
这(几乎)和我运行 file ../arch/arm64/boot/dts/qcom/sdmmagpie.dtb 得到的结果完全一致:
../arch/arm64/boot/dts/qcom/sdmmagpie.dtb: Device Tree Blob version 17, size=345856, boot CPU=0, string block size=31481, DT structure block size=314312
这种细微的差异曾让我担心,直到我运行 diff <(strings dtb.img) <(strings ../arch/arm64/boot/dts/qcom/sdmmagpie.dtb),发现它们包含的内容完全一样。所以现在我完全可以认为,创建自己的 dtb.img 只需复制 sdmmagpie.dtb 并改名即可。
至于 dtbo.img,file 帮不上什么忙:
dtbo.img: data
但这时我们就能用 mkdtimg 或 mkdtboimg.py 来分析它了。当我运行 mkdtimg dump dtbo.img 时,得到:

所以我知道,在创建自己的文件时,应该传入
mkdtimg create dtbo.img --page_size=4096 ../arch/arm64/boot/dts/qcom/sdmmagpie-idp-overlay.dtbo
或
mkdtboimg create --page_size=4096 dtbo.img ../arch/arm64/boot/dts/qcom/sdmmagpie-idp-overlay.dtbo
两条命令生成的文件完全一致。之后你还可以运行 mkdtimg dump dtbo.img,与另一个文件对比其中的数值。如果你需要在镜像里包含多个 .dtbo 文件,只需在任一命令末尾追加其路径即可。
现在我们回到刚做好的测试内核。好消息是它刷入时毫无问题;坏消息是我得去修那个生成如此丑陋 /proc/version 的 Makefile。
不过这可以等。现在,我们终于来到了:
是时候加一些子模块了。首先是这个仓库,它附带了非常容易照做的说明。(编辑:它现在也包含了 CAN 子系统内核符号,因为我提交的 PR 已被合并,不过你在下面的截图里是看不到这一点的)。

现在我再运行一次 make nconfig(命令和之前一样)

我们看到了这个可爱的新选项

它会启用所有这些功能。选中它,另存为 .config——但要小心,它会保存到 out/.config,而我们下次构建前会删除整个目录。所以退出 nconfig 后,要把这个文件复制到内核源码目录。
现在该修补一些源文件了,我们先把 nethunter 构建脚本仓库添加为子模块:

cd 到新目录并运行 ./build.sh,然后选择选项 4: Apply Nethunter kernel patches。

如果有任何提示说 “the test run was completed with errors”,就不要应用该补丁。否则,放心打。就我而言,我应用了补丁 4、5 和 8。
既然我们做了改动,就回到内核源码顶层目录并提交它们。

此时,你完全可以重新构建内核,然后大功告成。
但我偏要折腾,我还想加入一大堆额外支持,即:

我们需要修改 drivers/net/wireless/realtek 下的 Kconfig 和 Makefile,以便在配置中拾取这些新加入的驱动,但同时还要:

确保把 rtl8812au/Makefile 里的平台切换到 ANDROID_ARM64(rtl8188eus 也一样)。
同时,最好再检查一下新加驱动的 Kconfig,看配置选项叫什么:

现在我知道,在上一级的 Makefile 里要把它写成 88XXAU。

幸运的是,Kconfig 是这样被 source 进来的:

在加入 rtl88x2bu 和 rtl8188fu 的 ARM 分支之后,我仍需要再编辑这些文件。(事实证明,rtl88x2bu 的配置选项叫 RTL8822BU,所以检查一下总是值得的)。
接下来是 MediaTek 反向移植驱动。参考这个提交,我已成功将这批驱动移植到两个使用 4.14.x 内核的设备上。(现在明白提交历史的重要性了吧?)但首先,我们需要包含全部文件的 mt76 目录。
与其去 git clone 某个 4.19 内核、把文件拷出来,再对这些新拷贝里的文件打必需的补丁,不如直接把 https://github.com/akabul0us/mt76.git 添加为子模块。然后编辑 drivers/net/wireless/mediatek 目录下的 Makefile 和 Kconfig:

通过添加这个仓库——其中已经应用了前面提到那个提交里的补丁——我们可以直接跳到需要修改的其他内核文件。第一个是:

但结果我们的内核源码里已经有这个结构体了:

所以我们接着看下一个要改的文件,include/linux/overflow.h。这个文件确实有些改动,但它们在原始提交里读起来非常清楚,我就不放更多截图了。include/linux/skbuff.h 和 include/linux/scatterplot.h 也有改动,但 include/net/cfg80211.h 都不需要改。include/net/mac80211.h 需要的改动也很小——只需在 enum mac80211_rx_encoding { } 中最后插入一行 RX_ENC_HE,并在 struct ieee80211_rx_status { } 中加入 u8 vht_flag;。net/wireless/of.c 这个文件已经存在,而且和我们 cherry-pick 的那个提交里一模一样,所以这个也搞定了。(呼!)
至于 Docker 支持,感谢(再次)cyberknight777 的这个仓库,我们只需复制粘贴

现在我们又可以运行 nconfig 了!……差不多吧。首先,提交并推送改动,然后 rm -rf out/(确保你的 .config 已保存在别处),mkdir -p out,然后才能重新进入配置。

关于我们加入内核的这些树外 Realtek USB 驱动,有一点要说明:它们实在不怎么样。如果你尝试把其中多个以内联方式构建,链接时就会报错。一个由来已久的解决办法是:只把其中 1 个做成内联,其余的都编成模块。不过 Mediatek 驱动可以放心地以内联方式构建。
我还启用了一大堆别的东西,如果你想看完整 diff,我放在这里。总而言之,开编吧!

开局不错——能看到刚加入的新目标文件在无报错、无警告地编译,总是件赏心悦目的事。

这个?就没那么赏心悦目了,但修起来容易。我们只需从含有该警告标志的 Makefile 里移除 -Wno-stringop-overread 即可。顺便说一下,-Werror 是不是有点太硬核了?(这个标志的意思是‘把所有警告都当作错误’,遇到任何小问题——包括一个未知的 -W 标志——都会停止编译)。

至少这个容易修。

在那行前面加个 # 号,就可以再次运行 make 了——而这次,它从头到尾都成功了!
不过,因为我配置了一些驱动为模块,所以还没完。我们需要运行 make modules,更确切地说是 make -j $(nproc --all) ARCH=arm64 O=out CROSS_COMPILE=aarch64-linux-gnu- CROSS_COMPILE_32=arm-linux-gnueabi- LLVM=1 LLVM_IAS=1 AS=llvm-as modules。哦天哪,rtl8188fu 的 Realtek 驱动代码可真是一团糟:

我能手动修好引发这些警告的每一行吗?当然能。我会去修吗?不会。这就是我为什么把这些驱动编成模块而不是内联,并积极劝大家避开 Realtek 网卡的原因。不过:

至少它们编译成功了。这些待会再说。首先,让我们更新内核 zip:

(差点忘了——这不再是个“测试”内核了,而是一个正式的 Nethunter 内核)。
至于模块,.tar 文件才是正路,因为它们能完整保留文件的所有属性——不过,它们也会保留路径。除非我们做点什么,否则解压模块 tarball 时会产生一堆目录,比如 drivers/net/wireless/realtek/rtl8188fu/rtl8188fu.ko,所以让我们创建一个目录(并把它加进 .gitignore),把 .ko 复制进去,然后再打包。

这些能像内核一样直接刷入吗?

它们需要被解压到设备上的某个地方。之所以说“某个地方”,是因为放哪其实无所谓,只要你运行 insmod /whatever/path/to/whatever.ko 时模块能加载就行。你可以在 /sdcard 里解压,没问题,但别忘了,我很折腾,所以我会把它们解压到这个设备的“正确”位置,也就是 /vendor/lib/modules。当然,/vendor 是以只读方式挂载的,所以我得先开一个 root shell(不要在 Nethunter 终端里开——那是个 chroot——不过你可以用 Nethunter 终端应用,选择 “New Session → Root Shell”),然后运行 mount -o rw,remount /vendor,之后才能对这个分区做任何操作。接着 cd /vendor/lib/modules; tar xzvf /sdcard/or/wherever/you/saved/the/tarball.tgz; mount -o ro,remount /vendor,模块部分就搞定了。
如果该分区提示空间不足,你也可以改用符号链接——例如把文件放在 /sdcard/modules,再用绝对路径创建链接。所以如果用 88x2bu.ko 的话,就运行 ln -s /storage/emulated/0/modules/88x2bu.ko /vendor/lib/modules/88x2bu.ko。

最后再推一次仓库,如果你愿意,也可以把你使用的 .config 保存为新的 nethunter defconfig。(这对于从源码构建的人来说真是帮了大忙)。
它能用吗?刷入倒是没问题,但它真的能用吗?
嗯,现在天色已晚,我不会立刻去测试每一个功能,但让我们先测试最关键的:我加入内核的那些驱动的监控模式/数据包注入。先从 rtl88x2bu 开始:

对于 Realtek 驱动来说,还算不错。
接下来是重头戏:mt76x2u:

这才像话嘛,妥妥的渗透测试利器!
最后一步,是把你的 .zip 和 .tar.gz 作为 release 上传到 GitHub。然后去 xdaforums、Telegram、Twitter 之类的地方发帖,尽可能让同设备/同 ROM 的其他用户看到并受益。
当某个屁都不懂还嘴臭的家伙在你的帖子底下回复“现在你给 BUTTPHONE 34AC PRO EDITION 做内核了?!!”,就把这份指南的链接甩给他,让他 git gud。
各位,晚安。
——Akabul0us