内核驱动获取所有进程的cmdline

本文介绍了如何在用户态和内核态下获取Linux进程的cmdline。在用户态,可以通过读取/proc/pid/cmdline文件来获取;而在内核态,通过for_each_process遍历进程,并使用get_cmdline函数复制cmdline到缓冲区。文章还展示了如何在驱动中实现这一功能,并讨论了如何在内核未导出函数中寻找函数地址的问题。
摘要由CSDN通过智能技术生成

内核驱动获取所有进程的cmdline

一、用户态获取进程的cmdline

# 查看/proc/pid/cmdline文件即可
$cat /proc/pid/cmdline

二、内核态获取进程cmdline

for_each_process

用于遍历Linux所有进程

#define for_each_process(p) \
        for (p = &init_task ; (p = next_task(p)) != &init_task ; )
get_cmdline
root@curtis-Aspire-E5-471G:/home/curtis/# cat /proc/kallsyms | grep get_cmdline
ffffffffb5c7c1e0 T get_cmdline
ffffffffb65a669c t get_cmdline.cold
函数原型
/**
 * get_cmdline() - copy the cmdline value to a buffer.
 * @task:     the task whose cmdline value to copy.
 * @buffer:   the buffer to copy to.
 * @buflen:   the length of the buffer. Larger cmdline values are truncated
 *            to this length.
 *
 * Return: the size of the cmdline field copied. Note that the copy does
 * not guarantee an ending NULL byte.
 */
int get_cmdline(struct task_struct *task, char *buffer, int buflen)
{
        int res = 0;
        unsigned int len;
        struct mm_struct *mm = get_task_mm(task);
        unsigned long arg_start, arg_end, env_start, env_end;
        if (!mm)
                goto out;
        if (!mm->arg_end)
                goto out_mm;    /* Shh! No looking before we're done */

        spin_lock(&mm->arg_lock);
        arg_start = mm->arg_start;
        arg_end = mm->arg_end;
        env_start = mm->env_start;
        env_end = mm->env_end;
        spin_unlock(&mm->arg_lock);

        len = arg_end - arg_start;

        if (len > buflen)
                len = buflen;

        res = access_process_vm(task, arg_start, buffer, len, FOLL_FORCE);

        /*
         * If the nul at the end of args has been overwritten, then
         * assume application is using setproctitle(3).
         */
        if (res > 0 && buffer[res-1] != '\0' && len < buflen) {
                len = strnlen(buffer, res);
                if (len < res) {
                        res = len;
                } else {
                        len = env_end - env_start;
                        if (len > buflen - res)
                                len = buflen - res;
                        res += access_process_vm(task, env_start,
                                                 buffer+res, len,
                                                 FOLL_FORCE);
                        res = strnlen(buffer, res);
                }
        }
out_mm:
        mmput(mm);
out:
        return res;
}

这个函数虽然不是内核导出函数,可以参考文章:Linux内核未导出符号使用方法_Configure-Handle的博客-CSDN博客

三、驱动实现源码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/kallsyms.h>

#define PARM_LENTH 256

static int (*get_cmdline_fn)(struct task_struct *task, char *buffer, int buflen);
//static unsigned long (*kallsyms_lookup_name_fn)(const char *name);

/* 将获取的buffer中的 ‘\0’替换成空格 */
static void deal_raw_cmdline(char *buffer, unsigned int length)
{
        int i = 0;
        for (i = 0; i < length; i ++) {
                if (buffer[i] == '\0') {
                        buffer[i] = ' ';
                }
        }
}

static int __init query_process_param_init(void)
{
        int ret = 0;
        struct task_struct *tsk = NULL;
        char buffer[PARM_LENTH] = {0};

#if 0
        /* 使用命令cat /proc/kallsyms | grep kallsyms_lookup_name 获取内核函数地址 */
        kallsyms_lookup_name_fn = (unsigned long (*)(const char *))0xffffffffab563dd0;

        /* 这里无法用kallsyms_lookup_name获取函数get_cmdline的地址 */
        get_cmdline_fn = (int (*)(struct task_struct *, char *, int))
                        kallsyms_lookup_name_fn("get_cmdline");
        if (get_cmdline_fn == NULL) {
                printk("Get func get_cmdline address failed\n");
        }
#endif
        /* 采用直接赋值的方法获取函数地址 */
        get_cmdline_fn = (int (*)(struct task_struct *, char *, int))0xffffffffab67c230;

        rcu_read_lock();
        for_each_process(tsk) {
                printk("pid -> %d comm -> %s\n", tsk->pid, tsk->comm);
                if (tsk->mm == NULL) {
                        continue;
                }

                memset(buffer, 0, sizeof(buffer));
                ret = get_cmdline_fn(tsk, buffer, sizeof(buffer));
                if (ret < 0) {
                        continue;
                }
                deal_raw_cmdline(buffer, sizeof(buffer));

                printk("param : %s\n", buffer);
        }
        rcu_read_unlock();

        return 0;
}

static void __exit query_process_param_exit(void)
{
        printk("Query process param exit!\n");
}

module_init(query_process_param_init);
module_exit(query_process_param_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("curtis");

为什么使用kallsyms_lookup_name()找到函数get_cmdline()地址?

[ 1415.524181] pid -> 1344 comm -> ibus-extension-
[ 1415.524186] /usr/libexec/ibus-extension-gtk3                                                                                                                       
[ 1415.524189] pid -> 1346 comm -> ibus-x11
[ 1415.524195] /usr/libexec/ibus-x11 --kill-daemon                                                                                                                    
[ 1415.524198] pid -> 1350 comm -> ibus-portal
[ 1415.524203] /usr/libexec/ibus-portal                                                                                                                               
[ 1415.524206] pid -> 1361 comm -> at-spi2-registr
[ 1415.524212] /usr/libexec/at-spi2-registryd --use-gnome-session 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值