如何强制使用int $0x80而不是sysenter来检测系统调用

3

我正在尝试完成一个项目,将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
1个回答

0

我不打算解释这段代码(需要更多内容,但我不能因为 git 存储库还没有可用)

但是,检测 vmx 非根中的系统调用依赖于一些技巧,您可以在此 文章 的第 6 和第 7 页找到。基本上,要检测

  • syscall/sysret:您必须禁用 EFER 中的 CSE 位,以使任何尝试使用 syscall/sysret 都会导致无效的操作码异常,您将拦截它
  • sysenter/sysexit:您必须将 SYSENTER_CS_MSR 保存在 hypervisor 中的某个位置,然后将其加载为 null 值,以使任何使用 sysenter/sysexit 都会导致通用保护异常,您将捕获它

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接