对于大多数想要设置 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 信息,内容如下:
