编写Linux crash工具插件扩展crash命令(转载)

当前时间,周五晚10点45分左右。

我的需求是用crash工具dump出Netfilter的某个hook点所有hook所属模块的名字。

我的方法如下,首先找到模块地址:

crash px nf_hooks[2][0] =>var
crash list nf_hook_ops.list -s nf_hook_ops.owner -H $var |awk -F ‘=’ ‘/owner/{print $2}’
1
2
如此会得到一个列表,比如:

0x1234
0x6678
0x8641
0x4570

显然,这些地址都是module结构体,我需要执行下面的命令得到其name字段:

crash module.name $addr_above
1
然而,我必须一个一个输入,或者使用文件redirect:

crash list nf_hook_ops.list -s nf_hook_ops.owner -H $var |awk -F ‘=’ ‘/owner/{print $2}’ >./aa
crash module.name <./aa
1
2
这太麻烦了。

我想知道能不能在crash命令行编写脚本或者使用类似xargs,awk BEGIN,END之类的,比如使用下面这种:

crash list nf_hook_ops.list -s nf_hook_ops.owner -H $var |awk -F ‘=’ ‘/owner/{print $2}’ | xargs -i module.name {}
1
从而一次性获得所有结果,免除手工输入的麻烦。

我试了,crash命令没有办法被管道化。

经过询问,了解到crash有一个extend命令可以加载extension扩展插件,研究了一下,比较简单且有意义。

说白了就是基于crash的API写一个共享库,在crash命令行输入help extend将会显示这样的共享库如何来编码。我基于上述需求简单写了一个,可以用:

// nfhooks.c
#include <crash/defs.h>

static int get_field(unsigned long addr, char *name, char field, void buf)
{
unsigned long off = MEMBER_OFFSET(name, field);

if (!readmem(addr + off, KVADDR, buf, MEMBER_SIZE(name, field), name, FAULT_ON_ERROR))
	return 0;
return 1;

}

void do_cmd(void)
{
unsigned long ops_addr, owner_addr;
unsigned long list_addr, base, next;
char name[64];

optind++;
base = next = list_addr = htol(args[optind], FAULT_ON_ERROR, NULL);

do {
	// 由于list就是nf_hook_ops的第一个字段,因此就不炫技了,强转即可。
	//get_field(list_addr - MEMBER_OFFSET("nf_hook_ops", "list"), "list_head", "next", &ops_addr);
	ops_addr = list_addr = next;
	get_field(ops_addr, "nf_hook_ops", "owner", &owner_addr);
	get_field(owner_addr, "module", "name", &name[0]);
	if (list_addr != base)
		fprintf(fp, "--%s\n", name);
	} while(get_field(list_addr, "list_head", "next", &next) && next != base);

}

static struct command_table_entry command_table[] = {
{ “nfhooks”, do_cmd, NULL, 0},
{ NULL },
};

void attribute((constructor)) nfhooks_init(void)
{
register_extension(command_table);
}

void attribute((destructor)) nfhooks_fini(void) { }

编译命令如下:

gcc -fPIC -shared nfhooks.c -o nfhooks.so
1
将生成的so放在执行crash命令的目录下,在crash命令行加载之:

crash> extend nfhooks.so
./nfhooks.so: shared object loaded
1
2
随后就可以用了:

首先dump出INET IPv4的PREROUTING点的list地址

crash> px &nf_hooks[2][0]
$1 = (struct list_head *) 0xffffffff81a71000 <nf_hooks+256>
crash> nfhooks 0xffffffff81a71000
–bridge
–nf_defrag_ipv4
–iptable_raw
–nf_conntrack_ipv4
–iptable_mangle
–iptable_nat
crash>

是不是很有意思呢。

有了这个机制,就可以非常方便地使用readmem来进行Linux内核任意地址任意结构体字段的解析了,你就再也不用抱怨crash命令不够用了,不够用就自己写一个,而自己的写一个的成本非常低,无非就是readmem不断解析结构体,这一切的背后,只需要你对内核足够熟悉即可。

我之前写过一个手工解析/dev/mem或者vmcore的程序,但和crash插件扩展相比,太low太麻烦了,有了这个机制,以后再也不用干手工活儿了。

将上面的代码稍微扩展一下,就可以dump出所有协议族,所有hook点的所有模块名字了:

#include <crash/defs.h>

static int get_field(unsigned long addr, char *name, char field, void buf)
{
unsigned long off = MEMBER_OFFSET(name, field);

if (!readmem(addr + off, KVADDR, buf, MEMBER_SIZE(name, field), name, FAULT_ON_ERROR))
	return 0;
return 1;

}

#define FAMILY 12
#define TYPE 8

struct dummy_list {
struct dummy_list *next, *prev;
};

struct dummy_list *iter;
void do_cmd(void)
{
unsigned long ops_addr, owner_addr;
unsigned long list_addr, base, next;
char name[64];
int i, j;

optind++;
iter = (struct dummy_list *)htol(args[optind], FAULT_ON_ERROR, NULL);

for (i = 0; i < FAMILY; i++) {
	for (j = 0; j < TYPE; j++) {
		fprintf(fp, "at PF: %d  hooknum: %d \n", i, j);
		base = next = list_addr = (unsigned long)&iter[i*TYPE + j];
		do {
			ops_addr = list_addr = next;
			get_field(ops_addr, "nf_hook_ops", "owner", &owner_addr);
			get_field(owner_addr, "module", "name", &name[0]);
			if (list_addr != base)
				fprintf(fp, "  ----%s\n", name);
			} while(get_field(list_addr, "list_head", "next", &next) && next != base);
	}
}

}

static struct command_table_entry command_table[] = {
{ “allnfhooks”, do_cmd, NULL, 0},
{ NULL },
};

void attribute((constructor)) nfhooks_init(void)
{
register_extension(command_table);
}

void attribute((destructor)) nfhooks_fini(void) { }

是不是很简单呢?我从来不记录复杂的东西,简单的才是真美。

当前时间,周五晚10点45分左右。已经不在996的管辖范围,请继续举报!

浙江温州皮鞋湿,下雨进水不会胖。

原文链接:https://blog.youkuaiyun.com/dog250/article/details/107997905?utm_medium=distribute.pc_feed.none-task-blog-personrec_tag-10.nonecase&depth_1-utm_source=distribute.pc_feed.none-task-blog-personrec_tag-10.nonecase&request_id=5f43950d0388ae0b5643c567

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值