Jprobe没有';t监控所有的'do_execve'呼叫
我知道过去有一个问题,但我没有找到解决办法 我正在编写下一个内核模块来跟踪Jprobe没有';t监控所有的'do_execve'呼叫,c,linux-kernel,jprobe,C,Linux Kernel,Jprobe,我知道过去有一个问题,但我没有找到解决办法 我正在编写下一个内核模块来跟踪do\u exec调用。AFAIK每个新的进程映像(不是创建)都应该这样加载,所以我想我可以用这个jprobe跟踪所有的执行情况 不幸的是,此jprobe的唯一输出是: execve通过kworker/u8:3调用/usr/lib/systemd/systemd cgroups代理 我的模块代码如下: #include <linux/kernel.h> #include <linux/module.h&g
do\u exec
调用。AFAIK每个新的进程映像(不是创建)都应该这样加载,所以我想我可以用这个jprobe
跟踪所有的执行情况
不幸的是,此jprobe的唯一输出是:
execve通过kworker/u8:3调用/usr/lib/systemd/systemd cgroups代理
我的模块代码如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/fs.h>
static long jdo_execve(struct filename *filename,
const char __user *const __user __argv,
const char __user *const __user __envp)
{
const char *name = filename->name;
printk("execve called %s by %s\n", name, current->comm);
jprobe_return();
return 0;
}
static struct jprobe execve_jprobe = {
.entry = jdo_execve,
.kp = {
.symbol_name = "do_execve",
},
};
static int __init jprobe_init(void)
{
int ret;
ret = register_jprobe(&execve_jprobe);
if (ret < 0) {
printk(KERN_INFO "register_jprobe failed, returned %d\n", ret);
return -1;
}
return 0;
}
static void __exit jprobe_exit(void)
{
unregister_jprobe(&execve_jprobe);
printk(KERN_INFO "jprobe at %p unregistered\n", write_jprobe.kp.addr);
}
module_init(jprobe_init)
module_exit(jprobe_exit)
MODULE_LICENSE("GPL");
#包括
#包括
#包括
#包括
静态长jdo_execve(结构文件名*文件名,
常量字符*常量用户*常量用户,
常量字符用户*常量用户环境)
{
常量字符*名称=文件名->名称;
printk(“execve由%s调用%s\n”,名称,当前->通信);
jprobe_return();
返回0;
}
静态结构jprobe execve\u jprobe={
.entry=jdo_execve,
.kp={
.symbol\u name=“do\u execve”,
},
};
静态整数初始化jprobe初始化(无效)
{
int ret;
ret=寄存器(register_jprobe)(&execve_jprobe);
如果(ret<0){
printk(KERN_INFO“register_jprobe失败,返回%d\n”,ret);
返回-1;
}
返回0;
}
静态void\uuu退出jprobe\u退出(void)
{
取消注册\u jprobe(&execve\u jprobe);
printk(KERN_INFO“jprobe at%p unregistered\n”,write_jprobe.kp.addr);
}
模块初始化(jprobe初始化)
模块出口(jprobe出口)
模块许可证(“GPL”);
我使用的是CentOS 7,内核版本3.10.0-514.el7.x86_64
感谢您的帮助 手边的代码是:
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
return do_execve(getname(filename), argv, envp);
}
以及:
考虑到函数有多短,第一个明显的怀疑是它内联到了execve入口点。他证实了这一怀疑:
0xffffffff811e6e20 <sys_execve>: nopl 0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffff811e6e25 <sys_execve+0x5>: push %rbp
0xffffffff811e6e26 <sys_execve+0x6>: mov %rsp,%rbp
0xffffffff811e6e29 <sys_execve+0x9>: push %r12
0xffffffff811e6e2b <sys_execve+0xb>: mov %rdx,%r12
0xffffffff811e6e2e <sys_execve+0xe>: push %rbx
0xffffffff811e6e2f <sys_execve+0xf>: mov %rsi,%rbx
0xffffffff811e6e32 <sys_execve+0x12>: callq 0xffffffff811ef380 <getname>
0xffffffff811e6e37 <sys_execve+0x17>: mov %r12,%r8
0xffffffff811e6e3a <sys_execve+0x1a>: mov %rbx,%rdx
0xffffffff811e6e3d <sys_execve+0x1d>: xor %ecx,%ecx
0xffffffff811e6e3f <sys_execve+0x1f>: xor %esi,%esi
0xffffffff811e6e41 <sys_execve+0x21>: mov %rax,%rdi
0xffffffff811e6e44 <sys_execve+0x24>: callq 0xffffffff811e6520 <do_execve_common>
0xffffffff811e6e49 <sys_execve+0x29>: pop %rbx
0xffffffff811e6e4a <sys_execve+0x2a>: pop %r12
0xffffffff811e6e4c <sys_execve+0x2c>: cltq
0xffffffff811e6e4e <sys_execve+0x2e>: pop %rbp
0xffffffff811e6e4f <sys_execve+0x2f>: retq
0xFFFFFF811E6E20:nopl 0x0(%rax,%rax,1)[FTRACE NOP]
0xFF811E6E25:推送%rbp
0xFFFF811E6E26:mov%rsp,%rbp
0xFF811E6E29:推送%r12
0xFFFF811E6E2B:mov%rdx,%r12
0xFFFF811E6E2E:推送%rbx
0xFFFF811E6E2F:mov%rsi,%rbx
0xFFFFFF811E6E32:调用0xffffffff811ef380
0xFFFF811E6E37:mov%r12,%r8
0xFFFF811E6E3A:mov%rbx,%rdx
0xFFFF811E6E3D:xor%ecx,%ecx
0xFFFF811E6E3F:xor%esi,%esi
0xFFFF811E6E41:mov%rax,%rdi
0xFFFFFF811E6E44:调用0xffffffff811e6520
0xFFFF811E6E49:弹出%rbx
0xFFFF811E6E4A:弹出%r12
0xFFFF811E6E4C:cltq
0xFFFF811E6E4E:弹出%rbp
0xFFFF811E6E4F:retq
因此,您看不到大多数要执行的“调用”,因为在您感兴趣的代码路径中没有任何调用
另一种调试方法是尝试在堆栈中更深或更高的位置放置探测器
我不得不问你为什么要玩jprobes或者内核。到目前为止,您似乎是一个初学者程序员,现在还不应该玩它。特别是,您不太可能编写遵循所有规则的内核代码(包括jprobes),并且能够解释为什么会这样做。创建代码太容易了,因为在测试不足的情况下,代码似乎可以工作,但却被破坏了。AFAIK每个新的进程映像(不是创建)都应该这样加载
-例如,从32位环境执行的进程不使用此函数,它们使用compat\u do\u execve
。尝试钩住do\u execve\u common
,这样你也会截获32位进程创建。当然,忘了提到我正在运行一个64位内核,这个内核中没有compat\u do\u execve
符号(请参阅文章中的确切版本)符号compat\u execve
(以及其他“compat”符号)当系统调用由32位应用程序发出时,是专门针对64位内核的(是的,我在您的帖子中注意到了内核版本)。换句话说,您有64位内核,64位操作系统,但它可以运行32位应用程序,它将使用“compat”系统调用[root@localhost3.10.0-514.el7.x86_64]#grep-R do_execve*include/linux/sched.h:extern int do_execve(结构文件名*,System.map:ffffffffff81205ac0 t do_execve\u common.isra.25 System.map:ffffffffff81206150 t do_execve[root@localhost3.10.0-514.el7.x86_64]#grep-R compat_do_execve*[root@localhost3.10.0-514.el7.x86_64]35;
0xffffffff811e6e20 <sys_execve>: nopl 0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffff811e6e25 <sys_execve+0x5>: push %rbp
0xffffffff811e6e26 <sys_execve+0x6>: mov %rsp,%rbp
0xffffffff811e6e29 <sys_execve+0x9>: push %r12
0xffffffff811e6e2b <sys_execve+0xb>: mov %rdx,%r12
0xffffffff811e6e2e <sys_execve+0xe>: push %rbx
0xffffffff811e6e2f <sys_execve+0xf>: mov %rsi,%rbx
0xffffffff811e6e32 <sys_execve+0x12>: callq 0xffffffff811ef380 <getname>
0xffffffff811e6e37 <sys_execve+0x17>: mov %r12,%r8
0xffffffff811e6e3a <sys_execve+0x1a>: mov %rbx,%rdx
0xffffffff811e6e3d <sys_execve+0x1d>: xor %ecx,%ecx
0xffffffff811e6e3f <sys_execve+0x1f>: xor %esi,%esi
0xffffffff811e6e41 <sys_execve+0x21>: mov %rax,%rdi
0xffffffff811e6e44 <sys_execve+0x24>: callq 0xffffffff811e6520 <do_execve_common>
0xffffffff811e6e49 <sys_execve+0x29>: pop %rbx
0xffffffff811e6e4a <sys_execve+0x2a>: pop %r12
0xffffffff811e6e4c <sys_execve+0x2c>: cltq
0xffffffff811e6e4e <sys_execve+0x2e>: pop %rbp
0xffffffff811e6e4f <sys_execve+0x2f>: retq