crash是一个用于分析Linux内核转储文件(vmcore)的工具。正在运行的内核安装上debuginfo包之后,直接运行crash也可以直接分析运行中的内核,这对于分析一些内核问题极为有用。
在我们的某个场景中,需要分析NFQUEUE队列中的数据包内容。队列在内核中的表示是结构体nfqnl_instance,数据包sk_buff通过nf_queue_entry结构链在队列nfqnl_instance中。
要找到对应的nfqnl_instance,需要执行多条crash命令:
加载内核模块
nfnetlink_queue:1
mod -s nfnetlink_queue
查看索引数字:
1
p nfnl_queue_net_id
获取
net.gen地址:1
net.gen init_net
通过上述步骤获取的地址进一步获取
net_generic.ptr:
1 | struct net_generic.ptr 0xffff9312763e5680 |
从上述步骤获取地址读取若干地址:
1
rd -x 0xffffa08279cda518 12
根据第二步中获取的索引数字找到
nfnl_queue_net的地址,并进一步查看其结构:1
struct nfnl_queue_net ffffa0852b249900
从中找到
nfqnl_instance结构地址并查看:1
struct nfqnl_instance 0xffff931109f736c0
从中通过链表找到所有的
nf_queue_entry进行查看:1
struct nf_queue_entry 0xffff931529496d80
整个过程是有顺序依赖的,后序步骤的命令需要前边步骤所返回的地址。每次分析都需要从头手动执行一遍效率太低,因而期望可以有批量执行命令脚本的能力。
crash支持通过-i参数或者<符号读入批量命令进行执行,但这种方式需要把所有的命令提前写入文件。对于我们这种后序命令依赖前序命令的结果,这种方式就不奏效了。
经过查阅,发现crash工具支持so扩展,可以通过C语言开发crash扩展,可以参考:
我们可以在自定义扩展中依次调用crash命令,并解析命令输出,进而拼接出后序命令。当然,用C语言解析命令字符串输出的这种逻辑开发效率较低,我们可以将执行命令的能力导出到Lua语言中,由Lua脚本来解析命令输出,从而给crash添加上执行脚本的能力。
上述查找nfqnl_instance的逻辑可以通过Lua脚本来完成:
1 | require "crash" |
扩展的实现位于:
参考: