此时,用户空间的寄存器会以pt_regs结构体的形式,存储在当前内核栈空间的最高地址处。...) - sizeof(struct pt_regs)); } 或者下面的方法经验证也是可以的: #include #if LINUX_VERSION_CODE>=KERNEL_VERSION(4,11,0...) #include #endif struct pt_regs *GetUserRegisters(struct task_struct *task) { return (struct pt_regs...*fregs) { struct pt_regs *kernel_regs = ftrace_get_regs(fregs); struct pt_regs *user_regs =...*fregs) { struct pt_regs *kernel_regs = ftrace_get_regs(fregs); struct pt_regs *user_regs =
<KERNEL_VERSION(5,11,0) #define FTRACE_OPS_FL_RECURSION 0 #define ftrace_regs pt_regs static __always_inline...struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs) { return fregs; } #endif //关于系统调用函数签名的版本差异处理...*const KernelRegisters; struct pt_regs *const UserRegisters; size_t *const SysCallNR; const...*GetUserRegisters(struct task_struct *task) { //用户线程原本的寄存器保存在内核栈地址最高处,为pt_regs结构体 //实际上,我们只是想要...*fregs) { struct pt_regs *kernel_regs = ftrace_get_regs(fregs); struct pt_regs *user_regs =
(%esp), %edx /* pt_regs->ip */ movl PT_OLDESP(%esp), %ecx /* pt_regs->sp */ 1: mov PT_FS(%...skip pt_regs->cx and pt_regs->dx */ popl %esi /* pt_regs->si */ popl %edi.../* pt_regs->di */ popl %ebp /* pt_regs->bp */ popl %eax /* pt_regs.../* pt_regs->cs */ pushq %rcx /* pt_regs->ip */ pushq %rax /...* pt_regs->cx */ pushq $-ENOSYS /* pt_regs->ax */ pushq %r8 /* pt_regs
/ptrace.h> int change_msg(struct pt_regs *ctx) { if (!...(&buf, sizeof(buf), (void *)PT_REGS_PARM1(ctx)); bpf_trace_printk("msg:%s,%d\\n", buf, res);...(ctx) 通过ctx获取被跟踪函数的第一个变量,这里对应 print_str 函数的 char *s,如果有其他变量 则对应的改成 PT_REGS_PARM2,PT_REGS_PARM3 u64 res...= bpf_probe_read_user(&buf, sizeof(buf), (void *)PT_REGS_PARM1(ctx)); 通过bpf_probe_read_user函数从用户空间读取数据到变量...bpf_trace_printk: 类似printf的功能,编写ebpf程序在debug时比较有用 bpf_probe_write_user((void *)PT_REGS_PARM1(ctx), foo2
#include linux/kernel.h>#include linux/module.h>#include linux/kprobes.h> #define MAX_SYMBOL_LEN...p, struct pt_regs *regs, unsigned long flags){ // ...} // 钩子执行出错或者单条执行执行出错时被执行函数static...int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr){ // ...} static int __init...static bool do_int3(struct pt_regs *regs){ kprobe_int3_handler(regs);}int kprobe_int3_handler(struct...pt_regs *regs){ kprobe_opcode_t *addr; struct kprobe *p; struct kprobe_ctlblk *kcb;
▸ ▸ /* pt_regs->flags */ pushq▸ $__USER_CS▸ ▸ ▸ ▸ /* pt_regs...,这个地址被保存到了当前的内核栈上,即pt_regs->ip。...signal.png void handle_signal(struct ksignal *ksig, struct pt_regs *regs) { ......我们先看一下如果建新的用户空间栈: void get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,...此时系统调用返回用户空间需要返回到signal设置的handler里,因此需要重置pt_regs结构,这段代码在__setup_rt_frame中: regs->sp = (unsigned long)
进程描述符 Linux进程使用 struct task_struct 来描述(include/linux/sched.h), 如下: struct task_struct { /* *...sys_fork()函数,sys_fork()函数会调用do_fork()函数,代码如下(arch/i386/kernel/process.c): asmlinkage int sys_fork(struct pt_regs...代码如下: int do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs,...unsigned long clone_flags, unsigned long esp, unsigned long unused, struct task_struct * p, struct pt_regs...* regs) { struct pt_regs * childregs; // 指向栈顶(见图2) childregs = ((struct pt_regs *)
代码分析: 代码路径:https://github.com/pacepi/whotouchmyfile #include linux/kernel.h> #include linux/module.h...> #include linux/kprobes.h> #include linux/fs.h> #include linux/slab.h> #include ...proc_handler = proc_dostring, }, { } }; static int handler_pre(struct kprobe *p, struct pt_regs...if (buf) kfree(buf); return 0; } static void handler_post(struct kprobe *p, struct pt_regs...0x%lx\n", p->addr, regs->flags); } static int handler_fault(struct kprobe *p, struct pt_regs *regs,
asm_do_IRQ() 1.1其中asm_do_IRQ函数原型如下所示: asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs...*regs); //irq:中断号 *regs:发生中断前的各个寄存器基地址(=sp基地址) 1.2其中pt_regs结构体成员如下图所示...因为此时的PC是返回地址,而PC-4才是CPU运行的地址) 3.首先来找到系统时钟的中断号irq 输入#cat /proc/interrupt,如下图所示: 其中中断号来自 linux-2.6.22.6...#endif) asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct...pt_regs *old_regs = set_irq_regs(regs); struct irq_desc *desc = irq_desc + irq; #ifdef
int sys_execve(struct pt_regs regs) { int error; char * filename; filename = getname((char __user...*/ int do_execve(char * filename, char __user *__user *argv, char __user *__user *envp, struct pt_regs...*bprm,struct pt_regs *regs) { int try,retval; struct linux_binfmt *fmt; retval = security_bprm_check...*, struct pt_regs *) = fmt->load_binary; if (!...*bprm, struct pt_regs *regs) { struct file *interpreter = NULL; /* to shut gcc up */ unsigned long
called just before the probed instruction is executed */ static int handler_pre(struct kprobe *p, struct pt_regs...called after the probed instruction is executed */ static void handler_post(struct kprobe *p, struct pt_regs...Kprobes * single-steps the probed instruction. */ static int handler_fault(struct kprobe *p, struct pt_regs...我在pre中注入一段访问0地址的逻辑: static void * g_addr=0; static int handler_pre(struct kprobe *p, struct pt_regs *...regs) __attribute__((optimize("O0"))); static int handler_pre(struct kprobe *p, struct pt_regs *regs)
这个结构体的声明位于include/linux/sched.h中。...这个结构体的声明位于include/linux/sched.h中。...例如sys_fork()声明如下(arch/x86/kernel/process.c): int sys_fork(struct pt_regs *regs) { return do_fork...(SIGCHLD, regs->sp, regs, 0, NULL, NULL); } int sys_vfork(struct pt_regs *regs) { return do_fork...clone_flags, unsigned long newsp, void __user *parent_tid, void __user *child_tid, struct pt_regs
movl TSS_entry2task_stack(%esp), %esp .Lsysenter_past_esp: pushl $__USER_DS /* pt_regs...->ss */ pushl %ebp /* pt_regs->sp (stashed in bp) */ pushfl /* pt_regs...>flags (except IF = 0) */ orl $X86_EFLAGS_IF, (%esp) /* Fix IF */ pushl $__USER_CS /* pt_regs...->cs */ pushl $0 /* pt_regs->ip = 0 (placeholder) */ pushl %eax /* pt_regs...->orig_ax */ SAVE_ALL pt_regs_ax=$-ENOSYS /* save rest, stack already switched */ testl $X86
sys_clone通过do_fork来调用copy_process完成进程的复制,它调用特定的copy_thread和copy_thread把相应的系统调用参数从pt_regs寄存器列表中提取出来,但是会导致意外的情况...老版本的do_fork只有在如下情况才会定义 只有当系统不支持通过TLS参数通过参数传递而是使用pt_regs寄存器列表传递时 未定义CONFIG_HAVE_COPY_THREAD_TLS宏 参数 描述...(指向pt_regs结构体的指针。.../process.c, line 710 x86_64 arch/x86_64/kernel/process.c, line 706 asmlinkage long sys_fork(struct pt_regs...process.c, line 737 x86_64 arch/x86_64/kernel/process.c, line 728 asmlinkage long sys_vfork(struct pt_regs
do_fork和_do_fork The commit 3033f14ab78c32687 (“clone: support passing tls argument via C rather than pt_regs...sys_clone通过do_fork来调用copy_process完成进程的复制,它调用特定的copy_thread和copy_thread把相应的系统调用参数从pt_regs寄存器列表中提取出来,但是会导致意外的情况...老版本的do_fork只有在如下情况才会定义 只有当系统不支持通过TLS参数通过参数传递而是使用pt_regs寄存器列表传递时 未定义CONFIG_HAVE_COPY_THREAD_TLS宏 参数 描述...(指向pt_regs结构体的指针。...*childregs = task_pt_regs(p); struct task_struct *tsk; int err; /* 获取寄存器的信息 */ p->
这张图画了挺久的,主要是想让大家可以从全局角度,看下linux内核中系统调用的实现。...先别急,先来看下struct pt_regs的定义: 你有没有发现,这里面的字段名都是寄存器的名字。...这里需要注意的是100行到121行这段逻辑,它将各寄存器的值压入到栈中,以此来构建struct pt_regs对象。 这就能构建出一个struct pt_regs对象了? 是的。...我们再想下,当我们要构建一个struct pt_regs对象时,我们要为其在内存中分配一块空间,然后用一个地址来指向这段空间,这个地址就是该struct pt_regs对象的指针,这里需要注意的是,这个指针里存放的地址...在构建完struct pt_regs对象后,123行将rax中存放的系统调用编号赋值到了rdx里,124行将rsp里存放的struct pt_regs对象的地址,即该对象的指针,赋值到了rsi中,接着后面执行了
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs) { // 加载程序 for...(fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(struct linux_binprm *, struct pt_regs *)...linux_binprm *, struct pt_regs * regs); int (*load_shlib)(int fd); int (*core_dump)(long signr..., struct pt_regs * regs);}; static struct linux_binfmt *formats = &aout_format;int register_binfmt(struct...linux_binfmt * fmt){ struct linux_binfmt ** tmp = &formats; if (!
目录 ptrace实现原理 本文使用的 Linux 2.4.16 版本的内核 看懂本文需要的基础:进程调度,内存管理和信号处理相关知识。...Linux实现系统调用的基本过程是: 应用程序准备参数,发出调用请求; C库封装函数引导。该函数在Linux提供的标准C库,即 glibc 中。..., regs.rsi, regs.rdx,regs.rax, regs.orig_rax); // 打印寄存器的值 ptrace(PTRACE_CONT, child, NULL, NULL...* bprm, struct pt_regs * regs) { ......信号是通过 do_signal() 函数进行处理的,而对 SIGTRAP 信号的处理逻辑如下: int do_signal(struct pt_regs *regs, sigset_t *oldset)
entry_INT80_32系统调用对应的中断处理程序 ENTRY(entry_INT80_32) ASM_CLAC pushl %eax /* pt_regs->orig_ax...*/ SAVE_ALL pt_regs_ax=$-ENOSYS switch_stacks=1 /* save rest */ TRACE_IRQS_OFF movl %...ENDPROC(entry_INT80_32) 我们略去了中间的一些细节部分,可以看到首先将中断向量号压栈,再保存所有当前的寄存器值到pt_regs, 保存当前栈指针到%eax寄存器,最后再调用 do_int80...syscall_32调用 do_syscall_32_irqs_on,我们看一下其实现: static __always_inline void do_syscall_32_irqs_on(struct pt_regs...*regs) { struct pt_regs *old_regs = set_irq_regs(regs); struct irq_desc * desc; /* high