pwndbg kfile 命令完全指南:在内核调试中查看任务文件描述符表
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
导读
kfile是 pwndbg 提供的内核调试命令之一,用于在 Linux 内核调试会话中展示内核任务(kernel task)可访问的文件描述符(fd)信息,包括每个 fd 对应的struct file对象地址、文件操作函数表f_op、打开模式标志、private_data私有数据以及解析后的完整文件路径。阅读完本文,你将掌握kfile的全部参数用法、输出字段含义、底层实现原理,以及如何配合ktask/kcurrent在 QEMU 内核调试环境中快速定位目标任务的打开文件。
命令概览与使用前提
kfile命令的完整用法如下:
usage: kfile [-h] [--fd [FD]] [pid]其官方帮助描述为:
Displays information about fds accessible by a kernel task.
即:显示内核任务可访问的 fd 信息。该命令属于 pwndbg 的KERNEL命令类别(见 kcurrent.py 中的CommandCategory.KERNEL),与kstack、knamespace、ksighand、kseccomp、kcurrent等命令同属内核任务分析工具族。
运行前置条件
与大多数 pwndbg 内核命令一样,kfile依赖以下四个装饰器限定的调试环境(见 kcurrent.py):
| 装饰器 | 含义 |
|---|---|
@OnlyWhenQemuKernel | 仅在通过 QEMU 调试 Linux 内核时可用(即当前调试会话是内核而非普通用户态进程) |
@OnlyWhenPagingEnabled | 要求内核分页(paging)机制已启用,命令才能正确进行虚拟地址解析 |
@OnlyWithKernelSymbols | 要求加载了内核符号信息,以便定位task_struct等结构 |
@WarnOnKernelConfigRandstruct | 若内核启用了CONFIG_RANDSTRUCT随机化结构布局,会给出警告(此时字段偏移可能不可靠) |
因此,使用kfile的典型场景是:通过 QEMU 启动一个带调试符号的 Linux 内核,例如gdb vmlinux后连接qemu-system-*,并保证分页开启。
参数详解
kfile接受两个参数:一个可选的位置参数pid和一个可选的--fd选项。
| 参数 | 类型 | 默认行为 | 说明 |
|---|---|---|---|
pid | int(可选) | 使用当前内核任务 | 指定要查询文件描述符表的内核任务的 PID;省略时使用kcurrent设置的任务或调试器当前所处任务 |
--fd [FD] | int(可选) | 显示全部 fd | 只显示指定文件描述符编号的信息,用于精确过滤输出 |
参数解析逻辑位于 kcurrent.py:
parser = argparse.ArgumentParser( description="Displays information about fds accessible by a kernel task." ) parser.add_argument("pid", nargs="?", type=int, help="") parser.add_argument("--fd", nargs="?", type=int, help="")注意nargs="?"表示两个参数都可省略;若省略pid,命令会自动回退到「当前任务」。
pid 的解析规则
kfile内部通过select_kthread_from_pid()选定目标线程(kcurrent.py),其优先级为:
- pid 为空:优先使用
kcurrent --set <pid>设置的当前内核任务(见下文「与 kcurrent 的协同」);若未设置,则取调试器当前所处任务(pwndbg.aglib.kernel.current_task()),并要求该地址属于内核地址空间(is_kernel),否则提示current task not found; - pid 不为空:遍历
ktask枚举到的所有任务及其线程,按 PID 精确匹配;若找不到,输出警告ktask with pid {pid} not found并返回。
这一解析机制与kstack、knamespace、ksighand、kseccomp完全共享,属于内核任务族命令的统一入口。
输出字段解读
kfile输出采用缩进分层的格式(使用IndentContextManager实现),首先打印任务摘要,随后逐行列出每个 fd。以测试代码中的断言[fileno 001]为特征格式,典型输出如下:
task @ 0xffff88800a1a0000: [pid 1234] [user task] [bash] - [fileno 000] file @ 0xffff88800a3b0000: ops @ 0xffffffff8205a3e0 private: 0x0, flags: R, path: /dev/pts/0 - [fileno 001] file @ 0xffff88800a3b0120: ops @ 0xffffffff8205a3e0 private: 0x0, flags: RW, path: /dev/pts/0各字段含义如下(对应 kcurrent.py 的实现):
| 输出片段 | 含义 | 源码来源 |
|---|---|---|
task @ <地址> | 任务摘要,由Kthread对象打印,包含任务地址、PID、是否用户任务及进程名 | ktask.Kthread |
[fileno NNN] | 文件描述符编号,三位补零显示 | f"{i:03}" |
file @ <地址> | struct file对象在内核中的地址 | int(file) |
ops @ <地址> | f_op文件操作函数表(struct file_operations *)的地址 | int(file["f_op"]) |
private: <地址> | struct file的private_data字段值,常为驱动私有数据或空指针 | int(file["private_data"]) |
flags: R/W/X | 由f_mode位标志解出的访问模式 | fmode_flags(见下文) |
path: <路径> | 解析出的文件完整路径,可能为伪文件/匿名对象 | get_filepath() |
f_mode 标志位的解析
f_mode通过位标志类BitFlags解码(kcurrent.py):
fmode_flags = BitFlags([("R", 0), ("W", 1), ("X", 5)])即:第 0 位为只读(R)、第 1 位为可写(W)、第 5 位为可执行(X)。输出时通过ctx_color.format_flags按位着色显示,例如只读文件显示R,读写显示RW。这对应 Linux 内核FMODE_READ、FMODE_WRITE、FMODE_EXEC的概念(注意其具体位值来自内核定义,此处为 pwndbg 为展示需要建立的映射)。
底层实现原理:文件描述符表如何被枚举
kfile的数据来源是内核task_struct的files字段(struct files_struct *)。核心枚举逻辑位于 ktask.py 的Kthread.files()方法:
@pwndbg.lib.cache.cache_until("stop") def files(self) -> tuple[tuple[int, pwndbg.dbg_mod.Value], ...]: fdt = self.thread["files"]["fdt"] fds = fdt["fd"] files = [] for i in range(int(fdt["max_fds"])): file = fds[i] addr = int(file) if addr == 0: continue files.append((i, file)) return tuple(files)实现要点:
- 从
thread["files"]取得struct files_struct,再取其fdt(文件描述符表); - 遍历
0 .. max_fds范围内的fd数组,跳过空槽位(地址为 0 表示该 fd 未打开); - 返回值是
(fd 编号, struct file 指针)的元组,并通过cache_until("stop")缓存,直到调试器停止事件(如断点命中、单步)才失效,避免重复读内存; kfile在遍历时若传入了--fd,则只保留i == fd的条目(kcurrent.py)。
路径解析:get_filepath 如何还原完整文件路径
kfile输出的path字段由 ktask.py 的get_filepath()生成,这是整个命令最核心的解析逻辑,其工作流程如下:
- 将
struct file转为类型化指针,空指针返回空字符串; - 取
file["f_path"]["dentry"](目录项)与file["f_path"]["mnt"](挂载点),从挂载点偏移得到struct mount; - 自底向上回溯:沿
dentry["d_parent"]逐级向上收集d_name(目录项名),直到抵达挂载根mnt_root或自身(父目录等于自身); - 跨挂载点:若到达当前挂载根且不是最顶层挂载,则跳到
mount["mnt_parent"]的mnt_mountpoint,实现跨文件系统边界的路径拼接; - 将收集到的路径段逆序组合(
os.path.join(*path[::-1])),并附上f_inode的i_ino信息; - 特殊文件类型归类(依据路径名):UNIX/NETLINK/TCP/TCPv6/UDP/UDPv6/PACKET 显示为
[<类型>] socket:[<inode>];非/开头的匿名路径显示为anon:[<路径>];空路径显示为pipe:[<inode>]。
因此kfile不仅能显示普通文件的绝对路径,还能区分 socket、匿名 inode 与管道等特殊打开对象,这在分析内核 exploit 时尤为关键——例如攻击者通过memfd_create创建的匿名文件会显示为anon:[memfd:xxx],伪造的 socket 也会被明确标注。
与 kcurrent、ktask 的协同使用
kfile是内核任务族命令的一员,实际调试中通常与以下命令配合:
ktask:枚举当前内核中的所有任务及其线程,从中挑选感兴趣的 PID,再交给kfile <pid>查询其打开的文件;kcurrent [--set <pid>]:设置或查看 pwndbg 当前关注的内核任务。--set选项的帮助信息明确列出其影响范围(kcurrent.py):sets the kernel task used for supported pwndbg commands (kfile, kstack, knamespace, ksighand, kseccomp, pagewalk, vmmap)
即设置后,不带 pid 直接执行
kfile就会以该任务为目标;该设置只影响这些命令的解析行为,不改变调试器内部内存状态。
典型工作流:
(gdb) ktask # 枚举所有内核任务,记下目标用户进程的 PID (gdb) kcurrent --set 1234 # 将 1234 设为当前任务 (gdb) kfile # 直接查看该任务的全部打开文件 (gdb) kfile --fd 3 # 只看 fd 3(如 socket 或目标文件)测试验证
仓库的内核命令测试 test_commands_kernel.py 中,test_command_ktask对kfile的验证路径如下:
res = gdb.execute("ktask", to_string=True) # 枚举任务,解析出某个 user task 的 PID res = gdb.execute("kcurrent", to_string=True) # 查看当前任务 res = gdb.execute(f"kcurrent --set {userpid}", to_string=True) if "not found" not in res and "user task" in res: res = gdb.execute("kfile", to_string=True) assert "[fileno 001]" in res # 断言输出包含 fd 编号行该测试证实:
kfile需要在kcurrent --set <pid>指向用户任务后才能稳定输出;- 输出格式中必然包含
[fileno NNN]样式的 fd 编号行; - 测试运行于 QEMU 系统模式(
tests/library/qemu_system),与命令的OnlyWhenQemuKernel前置条件一致。
这意味着你在真实调试中若遇到kfile无输出,可优先检查:是否处于 QEMU 内核调试会话、是否用kcurrent --set指定了用户任务、以及该任务的 fd 表是否为空。
总结
kfile是 pwndbg 内核调试工具链中查看任务文件描述符的专用命令,具备以下能力:
- 无参数列出当前任务的全部打开文件,或通过
--fd精确过滤单个描述符; - 通过
pid参数查询任意枚举到的内核任务; - 输出每个 fd 的
struct file地址、f_op操作表、f_mode访问模式、private_data与解析后的路径; - 底层基于
files_struct->fdt遍历与 dentry 回溯路径解析,能识别 socket、匿名文件与管道等特殊对象。
配合ktask枚举任务与kcurrent --set切换目标任务,你可以在内核漏洞利用与逆向分析中快速还原一个进程的文件访问视图——这对于判断目标是否打开特定文件、定位匿名映射对象或识别网络连接的底层 socket 都非常实用。源码参考:命令实现、fd 枚举、路径解析、集成测试。
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考