从核晶入手浅谈一下syscall这块的攻防对抗

admin 2025年4月25日00:33:00评论0 views字数 4837阅读16分7秒阅读模式

前言

在hypervisor中,特别是安全软件的hv,接管syscall无非三种办法,MSR HOOK,EPT/NPT HOOK,EFER HOOK。本文来炒旧饭,对这些方法进行总结以及来说一下怎么检测这些方法。

注意,这个是科普文.相关技术其实已经快十几年了.不喜勿喷

系统syscall过程

大部分人对这块不是很理解(真不是syscall免杀),所以我们需要先复习一下syscall的过程.介绍一下系统是怎么从r3切换到r0的:
在经过一系列复杂的包装后,到ntdll.dll的调用会发出syscall指令,我们以ntvirtualalloc为视角,我们简单看一下这个指令在CPU里面会干什么:
首先在ntdll进行syscall后:
从核晶入手浅谈一下syscall这块的攻防对抗

会进入CPU的微码执行过程(amd手册的,intel手册的懒得找了):
从核晶入手浅谈一下syscall这块的攻防对抗

让我们说一下, 对于初学者来说,我们只需要知道

  1. rcx被设置为了rip
RCX := RIP; (* Will contain address of next instruction *)
  1. 并且 rip 已经等于一个msr寄存器的值,这个是 msr_lstar(非常重要)
RIP := IA32_LSTAR;

剩下的就交给系统了。你可能会看到,学免杀时候,syscall的是需要一个index的, 那么这里面syscall的sysnumber呢?ida里面截图的是mov eax, 74h去哪里了。这是因为,syscall是CPU指令,哪些字段用什么,是系统 规定的。如windows用eax, linux用rax

这也是我们接下来说的:
windows在初始化的时候会给msr_lstar寄存器设置一个地址,用于接受syscall的ip:
从核晶入手浅谈一下syscall这块的攻防对抗
这样r3发syscall指令其实就走到了系统的 KiSystemCall64这个函数里面
从核晶入手浅谈一下syscall这块的攻防对抗
对于初学者来说,只需要知道KiSystemCall64在干了参数数量识别(通过用eax的值 比对ssdt/sssdt表)
从核晶入手浅谈一下syscall这块的攻防对抗
大于4个参数用栈复制
从核晶入手浅谈一下syscall这块的攻防对抗
外就不需要其他的了

msr hoook

这个是非常经典的经典款方法,起因是在win7 x64后的PG的出现, 再也不能和x32一样hook KiFastCallEntry了, 因为PageGuard的出现, 会定时检查msr_lstar,读一次,看看是不是被换了.
但是由于有hypervisor了,hv能接管寄存器访问.所以我们可以:

接管msr_lstar寄存器的访问,如AMD是通过设置msr_bitmap字段进行接管

从核晶入手浅谈一下syscall这块的攻防对抗

设置amd的 msr权限映射表(permissions map) 大小是2个PAGE_SIZE
在amd手册里面MSR Intercepts有提到,如果要拦截中断
关系如下:
000h–7FFh 0000_0000h–0000_1FFFh
800h–FFFh C000_0000h–C000_1FFFh
1000h–17FFh C001_0000h–C001_1FFFh
1800h–1FFFh Reserved
比如IA32_MSR_EFER的msr值是C0000080h
那么就在
800h - FFFh 之间
要控制IA32_MSR_EFER 计算出在bitmap里面的偏移,然后置位为1
(IA32_MSR_EFER - C0000000h) 2(控制位) + 800h 8(CHAR_BIT)

在msr_lstar访问的时候给假的地址(原来的地址)

void vt_shadow_msr(ULARGE_INTEGER* fake_msr_value, bool is_read, uintptr_t origin_msr, _guest_status* guest_context) {
    if (is_read) {
        if (fake_msr_value->QuadPart == NULL) {
            fake_msr_value->QuadPart = origin_msr;
        }
        guest_context->guest_context->Rax = fake_msr_value->LowPart;
        guest_context->guest_context->Rdx = fake_msr_value->HighPart;
    }
    else {
        fake_msr_value->LowPart = guest_context->guest_context->Rax & MAXUINT32;
        fake_msr_value->HighPart = guest_context->guest_context->Rdx & MAXUINT32;
    }
}
....
msr exit handler:
if (msr_id == ia32_lstar) {
        vt_shadow_msr(&guest_status->fake_lstar_msr_value, access_type, g_orig_system_call, guest_status);
    }

这样,所有的call都会进我们的fake_kisystemcall了,剩下的就是手写模拟一下kisystemcall64:
从核晶入手浅谈一下syscall这块的攻防对抗
最后把系统的SSDT表换成自己的就行:
从核晶入手浅谈一下syscall这块的攻防对抗
这样就能挂钩任意API并且监控任意API了

微软如何检测的

在微软新版本系统中
翻开微软的内核,有如下函数
KiErrata361Present
KiErrataSkx55Present
KiErrata704Present

这些函数有如下特点:

名字莫名其妙
调用者也莫名其妙,比如KiErrata361Present在一个IDA分析都要半个小时的函数里面调用.
大部分都是微软检测虚拟机的函数

KiErrata704Present:
他长这样:

asm_pg_KiErrata704Present proc
    mov     ecx, 0C0000084h
    rdmsr
    push    rdx
    push    rax
    and     eax, 0FFFFFEFFh
    wrmsr
    pushfq
    or      qword ptr[rsp], 100h
    popfq
    syscall                 ; Low latency system call
    mov     r10, rcx
    mov     ecx, 0C0000084h
    pop     rax
    pop     rdx
    wrmsr
    mov     rax, r10
    ret
asm_pg_KiErrata704Present endp

让我们简单的说一下,这个函数首先保存FMASK的值,然后设置MSR值,以便SYSCALL操作不会修改TF.然后修改TF位.让下一步指令单步执行(#DB异常),DB异常后果就是如果你hook了lstar,断点就打在了你的kisystemcall64上了.你的位置会被直接暴露.然后就是蓝屏(蓝屏代码在系统的DB异常处理函数里)

KiErrata361Present:

asm_pg_KiErrata361Present proc
    mov ax,ss
    pushfq
    or qword ptr[rsp],100h
    popfq
    mov ss,ax
    db 0f1h ;icebp
    pushfq
    and qword ptr[rsp],0FFFFFEFFh
    popfq
    ret
asm_pg_KiErrata361Present endp

这个方法借鉴了一个drew的方法
https://howtohypervise.blogspot.com/2019/01/a-common-missight-in-most-hypervisors.html
这个原理的核心是”mov ss,XXX”导致的异常会推迟.按道理来说如果没有vmexit发生则会在vmexit指令那边执行延迟异常.但是如果有vmexit而且你的垃圾玩具机没有处理的话则会在vmexit指令的下面一个指令执行延迟异常.导致看起来有一个指令被跳过一样

从核晶入手浅谈一下syscall这块的攻防对抗

关于这个异常简单说明一下,icebp这个指令是intel的一个奇葩,他在intel是会走DB异常的,而且会要求CPU优先处理他的异常(因为他的type是ia32_prisw_exception,比普通的DB异常高一级).但是如果他跟mov ss结合在一起呢?
看我的CVE-2018-8897学习笔记:
https://key08.com/index.php/2021/03/13/968.html
mov ss会产生一个异常,但是这个异常会在下一个指令执行的时候再抛出.因此 他首先mov ss了,这样下一个指令才会执行异常, 到icebp的时候, mov ss造成的异常就应该被抛出了。但是同时icebp也会造成一个异常,而且异常优先级哎比mov ss的高,你说巧不巧.这样CPU就会处理icebp的异常而不处理mov ss的异常,但是mov ss的异常是必须要求dr6的single_instruction bit位(也就叫做DR6.BS位)为1的,要不然就炸。很遗憾的是icebp的异常他不会设置这个bs位,导致直接炸虚拟机.很多时候虚拟机在2004跑起来了结果一阵子就虚拟机guest机异常了就是这个毛病

缺点

在启动kvashadow后,win10 1809之前也不是没办法,可以用MMCreateShadowMapping映射自己的kisystemcall64到shadowmapping里面,但是之后,没了!也就是说,在之后这个方法会跟KVA-Shadow不兼容,所以要么关掉它-这意味着你的用户会再次受到熔断漏洞的问题
从核晶入手浅谈一下syscall这块的攻防对抗

扩展: EFER HOOK

让我们加点料继续说一下一些有趣的事情.再次回顾上面的syscall的微码,我们会看到什么?

IF (CS.L ≠ 1 ) or (IA32_EFER.LMA ≠ 1) or (IA32_EFER.SCE ≠ 1)
(* Not in 64-Bit Mode or SYSCALL/SYSRET not enabled in IA32_EFER *)
    THEN #UD;

是的,当CPU的efer.sec被清空的时候,就会抛#UD异常
而HV是可以接管UD的,所以就能利用efer让syscall抛异常,再在异常里面接管它.

EFER是MSR中的一员,它的地址是0xC0000080,它是x64提体系的基石。当EFER.LME=1时,表示要开起IA-32e模式。可是,此时并不代表已经进入了IA-32e模式,因为开启IA-32e模式后必须要开启分页内存管理机制,也就是说,当EFER.LME=1且CR0.PG=1时,处理器会将EFER.LMA置为1,当其成功置为1以后,才表示IA-32e模式处于激活状态。
处理器在上电开始运行时或复位后是处于实地址模式,CR0控制寄存器的PE标志用来控制处理器处于实地址模式还是保护模式。标志寄存器(EFLAGS)的VM标志用来控制处理器是在虚拟8086模式还是普通保护模式下,EFER寄存器的LME用来启用IA-32e模式

所以,我们可以先初始化的时候接管异常:

vcpu->stack->guest_vmcb.control.InterceptException |= (1UL <<
       amd64_invalid_opcode);

然后修改guest的efer,去掉sec

vcpu->stack->guest_vmcb.state_save.Efer = function::_huoji_readmsr(amd64_efer) & ~amd64_efer_sec;

这样在异常里面模拟一次syscall就行了,不过这部分代码丢了,就截图一下网上的吧:
从核晶入手浅谈一下syscall这块的攻防对抗
这块最开始的来源是:;
https://revers.engineering/syscall-hooking-via-extended-feature-enable-register-efer/

问题

实际用性能堪忧.因为系统每时每刻都在进行syscall,每次syscall CPU都要抛异常,速度已经满到离谱的了

原文始发于微信公众号(冲鸭安全):从核晶入手浅谈一下syscall这块的攻防对抗

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月25日00:33:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从核晶入手浅谈一下syscall这块的攻防对抗https://cn-sec.com/archives/3958559.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息