编写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}'

如此会得到一个列表,比如:

0x1234
0x6678
0x8641
0x4570
...

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

crash module.name $addr_above

然而,我必须一个一个输入,或者使用文件redirect:

crash list nf_hook_ops.list -s nf_hook_ops.owner -H $var |awk -F '=' '/owner/{print $2}' >./aa
crash module.name <./aa 

这太麻烦了。

我想知道能不能在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 {}

从而一次性获得所有结果,免除手工输入的麻烦。

我试了,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

将生成的so放在执行crash命令的目录下,在crash命令行加载之:

crash> extend nfhooks.so
./nfhooks.so: shared object loaded

随后就可以用了:

# 首先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.csdn.net/dog250/article/details/107997905

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    编写Linux crash工具插件扩展crash命令

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/405855

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午9:28
下一篇 2023年4月26日 上午9:28

相关推荐