pahole与内核模块在netlink_sock偏移量上存在差异(offsetof)
在研究不同的内核漏洞时,我需要获取结构体中某个值的偏移量,例如:portid偏移量指向netlink_sock。
我发现了一个叫pahole的工具,正好可以完成这项工作。
pahole -E netlink_sock vmlinux | grep portid
但它显示该portid值的偏移量为:
/* typedef u32 -> __u32 */ unsigned int portid; /* 792 4 */
这意味着偏移量 = 792,大小为4 字节,也就是说如果我把偏移量处的数据改成AAAA,那么就不会覆盖那些数据,
但我用C 写了一个内核模块来打印portid的偏移量。它打印出的偏移量是688,而且是正确的。
我从我的内核3.16实验室虚拟机中获取了vmlinux,并在我的宿主机(内核更新的机器)上运行那条pahole命令,使用不同的内核来运行pahole会不会造成混淆??
另外根据pahole,结构体成员的顺序也不同。
对于官方源代码,3.16的 netlink_sock结构如下
struct netlink_sock {
/* struct sock has to be the first member of netlink_sock */
struct sock sk;
u32 portid;
u32 dst_portid;
u32 dst_group;
u32 flags;
...
};
但在pahole的输出中
flags成员出现在portid值之上。
在对一个 3.16.0-4-amd64 的实验室VM进行内核漏洞研究时,我试图确定 portid 在 struct netlink_sock 中的确切偏移量。我遇到了静态分析与运行时结果之间的显著差异。
1.静态分析,使用 pahole**:** 我在我的宿主机上对目标VM提取的 vmlinux 运行了 pahole:pahole -C netlink_sock vmlinux
输出表明 portid 的偏移量在792,并且显示出不同的成员顺序(例如 flags 出现在 portid 之前):
C
/* pahole output excerpt */
unsigned int flags; /* 784 4 */
unsigned int portid; /* 792 4 */
2.使用内核模块进行运行时分析:然而,当我使用 offsetof(struct netlink_sock, portid) 编写一个简单的内核模块并在VM内部运行时,它输出的偏移量为688。688的偏移量似乎是我的利用中的“正确”偏移量。
3.官方源参考(3.16):3.16的上游源代码将该结构定义为:
C
struct netlink_sock {
struct sock sk; // struct sock has to be the first member
u32 portid;
u32 dst_portid;
...
};
为什么在源代码清晰地定义 portid 紧接在 struct sock sk 之后时,pahole 会把 flags 显示在 portid 的上方?
在更新的宿主内核上运行 pahole,会影响它解析一个3.16 vmlinux 文件的方式吗,还是这纯粹是 vmlinux 构建配置的问题(例如 CONFIG_NETLINK_MMAP)?
如何确保 pahole 反映出我正在运行的内核的实际内存布局?
解决方案
很可能你在比较两种不同的内核构建,而不是同一结构的两种不同视图。
有几点要注意:
pahole不会解析C 源代码。它读取你提供的ELF映像中嵌入的DWARF/BTF类型信息。offsetof(struct netlink_sock, portid)是由编译器根据你模块正在编译所用的内核构建头文件计算的。- 只要
pahole能从目标vmlinux读取有效的调试信息,宿主内核版本不会影响pahole给出的偏移量。
最关键的线索是你同时看到:
- 不同的偏移量(
792vs688)。 - 不同的成员排序(
flags出现在portid之前)。
填充/对齐会改变偏移量,但它 不能重新排序成员。如果成员顺序不同,你几乎可以肯定不是在查看同一个类型定义。
有几件事我会核实:
uname -r
在VM内,确保传给 pahole 的 vmlinux 来自那一个确切的内核构建。
也要检查你的 vmlinux 是否实际包含DWARF信息:
readelf -S vmlinux | grep debug
或
file vmlinux
如果你是从 /boot/vmlinuz-* 提取它,那么很可能你在使用一个剥离(stripped)的镜像,而不是匹配的调试构建。在这种情况下,请安装/下载相应的调试符号包,并对未剥离的 vmlinux 运行 pahole。
对于漏洞利用研究,我会信任来自运行时的结果:
offsetof(struct netlink_sock, portid)
胜过一个不匹配的 pahole 转储。如果该模块是针对与正在运行的3.16内核相同的头文件/配置构建的,那么 688 就是该内核构建的正确偏移量。
792 值以及重新排序的成员强烈表明,pahole 正在检查的类型信息来自于一个与模块编译时所用的内核版本或配置不同的内核修订版本。