低于 4.6 的内核使用程序集存根来强化关键系统调用(如 fork、clone、exec 等)的挂钩。特别是对于 execve,以下代码片段来自内核4.5显示 execve 的条目存根:
ENTRY(stub_execve)
call sys_execve
return_from_execve:
...
END(stub_execve)
系统调用表包含此存根的地址,并且此存根进一步调用原始 execve。因此,要在这个环境中挂钩 execve,我们需要使用call sys_execve
挂钩例程修补存根,并在完成所需的操作后调用原始的 execve。这一切都可以在行动中看到执行程序,Linux 的进程执行监控实用程序。我测试了 execmon 在内核 4.4 的 Ubuntu 16.04 中成功运行。
从内核4.6开始,关键调用保护的上层方案已经改变。现在存根看起来像:
ENTRY(ptregs_\func)
leaq \func(%rip), %rax
jmp stub_ptregs_64
END(ptregs_\func)
where\func
将扩展到sys_execve
execve 调用。再说系统调用表包含此存根,并且此存根调用原始 execve,但现在以更安全的方式而不是仅仅执行call sys_execve
.这个较新的存根将被调用函数的地址存储在RAX
寄存器中,并跳转到另一个存根,如下所示(注释已删除):
ENTRY(stub_ptregs_64)
cmpq $.Lentry_SYSCALL_64_after_fastpath_call, (%rsp)
jne 1f
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
popq %rax
jmp entry_SYSCALL64_slow_path
1:
jmp *%rax /* called from C */
END(stub_ptregs_64)
请看一下这查看此存根中的评论和其他引用的标签。
我努力想出一些逻辑来克服这种保护,并用挂钩函数修补原始调用,但还没有成功。有人愿意加入我并帮助我摆脱困境吗?