内核驱动获取所有进程的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