在内核模块中解析内核符号
2026-04-11: 相关代码已开源: KernelSU/lkmloader.ko
问题
本文中,「内核模块」、「可加载(内核)模块」、「LKM」、「ko」是同义词。
DDK 让适用于 Android GKI 的内核模块开发更加便捷了,我们很容易就能编译出 ABI 正确的内核模块。然而想要在内核玩魔法,解决访问各种内核符号的问题是少不了的一关。
Linux 内核只给可加载模块导出了很少一部分函数,而大量有用的符号都隐藏在 kallsyms 中。一个符号只有通过 EXPORT_SYMBOL[_GPL] 标记的符号可以被导出。导出符号被记录到名为 ksymtab[_gpl] 的表中,内核加载模块只会从这个地方来查找未定义符号。因此直接如果 ko 的 undef 符号包含非导出符号,那么内核加载的时候在解析符号阶段就会出现错误,导致模块无法加载。
内核提供了 kallsyms_lookup_name 这个函数来查找内核符号,因此内核自身可以访问全部的 kallsyms ,然而不幸的是, kallsyms_lookup_name 也不在导出符号之中,因此可加载内核模块无法访问 ...
Linux kernel Kallsyms 构建与解析
Kallsyms 是 Linux 内核镜像自带的符号表,包含了大多数内核函数、变量、标签的地址,因此是分析 Linux 内核镜像的重要工具。本文将首先介绍 Kallsyms 的结构在内核构建过程中的生成过程,然后以 vmlinux-to-elf 为例,讨论如何解析 vmlinux 以找到 kallsyms 结构的位置。
构建生成 kallsyms 在内核中的布局,是靠 scripts/kallsyms.c 这个程序,它会生成一个汇编源文件(out/.tmp_vmlinux.kallsyms2.S),其中包含了 kallsyms 的必要结构。
本文参考 android common kernel android14-6.1 的 scripts/kallsyms.c
由于内核包含数量巨大的符号(约 10w+ 符号,500w+ 字符),直接明文存储太浪费内存,因此需要进行压缩操作。
kallsyms.c 需要建立一个符号表,长度为 256 ,因为 0-255 都编码为组成符号的某个子串。
为了最大程度节省空间,这些子串必须是常见的;同时为了能编码所有的符号,这 256 ...
ELF 动态链接中的符号查找
概述在程序进行动态链接的时候,需要通过符号名字进行符号的查找。ELF 动态链接库中,动态链接所需的符号(动态符号)及字符串位于 .dynsym 节(section)和 .dynstr 节中,可以通过动态节(Dynamic Section)获取地址。由于这些节都位于需要被加载到内存的段(segment)中,因此 ELF 被映射到内存后即可直接从内存中访问动态符号的信息。
在加载 ELF 后,ELF 基址指向的就是 ELF Header (这也是 ELF 文件头部),其中包含程序头表(Program Header Table) 地址,而动态节的地址偏移可以从程序头表中获得(类型为 PT_DYNAMIC)。其中动态符号表(.dynsym)偏移的类型为 DT_SYMTAB ,字符串表(.dynstr)偏移类型为 DT_STRTAB 。
查找符号即通过符号名字,找到其在动态符号表的位置的过程。这个位置可能是 0 ,表示没有找到符号。动态符号表的第 0 个符号一般不表示任何实际的符号(类型是 NOTYPE ,value 是 0),可以认为是逻辑上的 NULL 。
由于 ELF 中可能包含大量的动态 ...
记 LSPosed 的一个随机崩溃的调查过程
记 LSPosed 的一个随机崩溃的调查过程LSPosed 已经停更数个月,由于缺少维护,问题不断显现。从上个月开始,就看到有不少用户汇报 LSPosed 存在导致系统概率性 crash 或 bootloop 的问题,然而原因却让人摸不着头脑。在最近,经过几天的与问题用户的跟踪调查,总算有了眉目。
初见收到的 crash dump 往往具有下面的特征:
1234567891011121314151617181920212223Process uptime: 13sZygotePid: 8731Cmdline: system_serverpid: 8968, tid: 9054, name: PackageManager >>> system_server <<<uid: 1000tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x000000000001001f x0 ...
Android arm64 在 ptrace 中使用硬件断点
Android arm64 在 ptrace 中使用硬件断点ARM64内核研究(五) | Ylarod’s Blog
gdb 系列(1) (hwbreakpoint\watchpoint)
正文为了跟踪相应地址发生的事件(读、写、执行)是哪一条指令造成的,我们可以使用硬件断点来达成这一目标。
准备条件需要内核启用 CONFIG_HAVE_HW_BREAKPOINT 和 CONFIG_HAVE_ARCH_TRACEHOOK
gki 上应该是默认打开的
https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/arch/arm64/Kconfig;l=217;drc=661dc19066ef0fdcb2db3e2542c45744a4067e87
注册断点在 arm64 上,使用 PTRACE_GET/SETREGSET 调用来设置断点。内核的处理逻辑如下:
common/arch/arm64/kernel/ptrace.c
12345678 ...
在 AVD 上使用 KernelSU - 第二回 -
编译兼容内核模块的版本前篇 已经成功编译出了适合 avd 使用的内核,然而同时需要编译兼容的内核模块,过于复杂。经过一番探索,发现只要找到正确的源码,还是能编译出与发布的 avd 镜像兼容的内核。
认识版本号还是回到 /proc/version 上,内核的版本号提供了很多信息,比如 -g 后跟着的是一个 12 位 16 进制数,表示编译的内核的源码树的提交 hash ,此外还有编译器版本。
1Linux version 6.1.23-android14-4-00257-g7e35917775b8-ab9964412 (build-user@build-host) (Android (9796371, based on r487747) clang version 17.0.0 (https://android.googlesource.com/toolchain/llvm-project d9f89f4d16663d5012e5c09495f3b30ece3d2362), LLD 17.0.0) #1 SMP PREEMPT Mon Apr 17 20:50:58 ...
在 AVD 上使用 KernelSU
在 AVD 上使用 KernelSU曾经尝试过给 AVD 构建带有 KernelSU 的内核,方便开发测试,但是发现内核模块不兼容,现在的方法是同时构建内核模块,略为复杂。
构建内核https://source.android.com/docs/setup/build/building-kernels?hl=zh-cn
Android 14 的 AVD 内核版本:
1Linux version 6.1.23-android14-4-00257-g7e35917775b8-ab9964412 (build-user@build-host) (Android (9796371, based on r487747) clang version 17.0.0 (https://android.googlesource.com/toolchain/llvm-project d9f89f4d16663d5012e5c09495f3b30ece3d2362), LLD 17.0.0) #1 SMP PREEMPT Mon Apr 17 20:50:58 UTC 2023
根据 KernelSU C ...
记一次 LSPosed Native Hook 偶现崩溃
记一次 LSPosed Native Hook 偶现崩溃发现故障昨天(12-05)调试 HMA 的时候重启手机,发现某些加载了 LSPosed 模块的 app 总是无法启动,除非禁用作用于它的所有模块,它们的 tombstone 全指向了同一处地址的 SIGSEGV 。
重启后恢复正常,于是我赶紧去翻查 tombstone ,发现 backtrace 来自某个 zygisk 模块,并且堆栈回溯到这里断掉了。
12345signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7196fa5224backtrace: #00 pc 00000000000859b0 /apex/com.android.runtime/lib64/bionic/libc.so (__memcpy+96) (BuildId: a790cdbd8e44ea8a90802da343cb82ce) #01 pc 000000000001c57c /memfd:jit-cache (deleted)
既然是 LSPosed 模块导致了 ...
Android 安装系统证书
Android 安装系统证书抓包的时候通常需要安装特定的 CA 证书以便中间人解密,本文以安装 mitmproxy 证书为例介绍如何准备证书文件和临时安装(挂载)证书到特定的 app 。
准备证书这里可以直接参考 mitmproxy 给出的流程
System CA on Android Emulator
mitmproxy CA 证书位于 ~/.mitmproxy/mitmproxy-ca-cert.cer (Windows: %UserProfile%\.mitmproxy\mitmproxy-ca-cert.cer) ,Android 接受这种格式的证书,不过需要重命名为特定的名字。
命令 openssl x509 -inform PEM -subject_hash_old -in mitmproxy-ca-cert.cer 第一行输出证书的 hash ,本例中为 c8750f0d ,因此复制一份 mitmproxy-ca-cert.cer ,将其重命名为 c8750f0d.0 ,即可得到 Android 上可用的证书。
挂载到系统目录对于 Android 13 和之前的版本, ...
My new blog ...
My new blog …终于还是用 hexo + gh pages 搭了个博客。
原本希望用 my-notes 作博客,用 github 自带的 actions 生成,然而效果不佳,最大的问题是没有首页,自己又懒于研究,于是就一直挂在那了,想必没人会看吧(笑)。
开这个新的博客,也是希望另起炉灶,因为原先的 my-notes 堆积了大量的口水话,我还是希望新的 blog 内容更有质量一些,少一些记录性的内容。
现在我已经从 my-notes 搬运了一些我认为相对来说好一些的内容到这里。也许将来 my-notes 的作用就是草稿箱,如果有成品就会放到这里来。