我正在尝试完成一个项目,将ELF二进制文件转换为虚拟机,以提供进程自己的执行环境,类似于http://dune.scs.stanford.edu/。许多论文都说“我们检测系统调用……”,但没有提供有关检测的详细信息,除了这篇论文,其代码在vmx非根ring 0中检测syscall(因为进程将在此环境下运行)。但是,由于缺乏文档和提示,我无法理解他的方法。
相关的代码如下:
/*
* macro to switch to G0 fs.base
*
* NOTE: clobbers %rax, %rdx, and %rcx
*/
.macro SET_G0_FS_BASE
movq $0, %gs:IN_USERMODE
movq %gs:KFS_BASE, %rax
movq %gs:UFS_BASE, %rdx
cmp %rax, %rdx
je 1f
#if USE_RDWRGSFS
wrfsbase %rax
#else
movq %rax, %rdx
shrq $32, %rdx
movl $MSR_FS_BASE, %ecx
wrmsr
#endif /* USE_RDWRGSFS */
1:
.endm
__dune_syscall:
/* handle system calls from G0 */
testq $1, %gs:IN_USERMODE
jnz 1f
pushq %r11
popfq
vmcall
jmp *%rcx
1:
/* first switch to the kernel stack */
movq %rsp, %gs:TMP
movq %gs:TRAP_STACK, %rsp
/* now push the trap frame onto the stack */
subq $TF_END, %rsp
movq %rcx, RIP(%rsp)
movq %r11, RFLAGS(%rsp)
movq %r10, RCX(%rsp) /* fixup to standard 64-bit calling ABI */
SAVE_REGS 0, 1
movq %gs:TMP, %rax
movq %rax, RSP(%rsp)
/* then restore the CPL0 FS base address */
SET_G0_FS_BASE
/* then finally re-enable interrupts and jump to the handler */
sti
movq %rsp, %rdi /* argument 0 */
lea dune_syscall_handler, %rax
call *%rax
/* next restore the CPL3 FS base address */
SET_G3_FS_BASE
/* then pop the trap frame off the stack */
RESTORE_REGS 0, 1
movq RCX(%rsp), %r10
movq RFLAGS(%rsp), %r11
movq RIP(%rsp), %rcx
/* switch to the user stack and return to ring 3 */
movq RSP(%rsp), %rsp
sysretq
.globl __dune_syscall_end
__dune_syscall_end:
nop
以下是我的问题:
1) 以上代码如何工作?任何解释、指针或参考都将有所帮助。
2) 在这种情况下,是否可能使用其他方法来检测系统调用?我认为调整 vdso
将是另一种解决方案。我考虑的是在 vmx 非根中强制系统调用使用 int 80h
并挂钩此中断。请分享此黑客的任何建议。
vdso
中构建内核以导出一个int 0x80
存根而不是sysenter
存根对于glibc来说应该是完全透明的。你有研究过strace(1)
用于跟踪系统调用的机制吗?我不确定它在底层是如何工作的,但我知道它基于ptrace(2)
系统调用,这与gdb
控制另一个进程进行单步调试的方式相同。还有ltrace(1)
,它使用共享库黑客技巧来跟踪所有进入共享库的函数调用。 - Peter Cordes