一种基于虚拟化技术的无代理客户机进程防护方法与流程

文档序号:12733224阅读:156来源:国知局
本发明涉及客户机进程防护领域,具体涉及一种基于虚拟化技术的无代理客户机进程防护方法。
背景技术
::近年来,云计算技术发展迅速。一方面,利用云服务,客户可以节省大笔的硬件成本以及维护开销;另一方面,作为云厂商,在把服务提供给用户的同时,可以更专注于云服务本身的研究,推动云计算技术的发展。而客户为了降低成本,越来越多的服务被迁移到云平台的虚拟机中。但是美国国家标准机构NIST发布的《完全虚拟化安全指南》指出,在传统主机中面临的安全威胁,在虚拟机中同样存在。基于操作系统的特点,进程作为动作发起的实体,独立运行于系统中,因此进程成为恶意攻击的首要目标。基于此,研究虚拟机进程防护技术具有极大现实意义。SSDT:SSDT(SystemServicesDescriptorTable)是系统调用表,用户程序的行为要具体落实,最终都要通过某种途径调用到具体的系统调用,由对应的系统调用去执行,SSDT的重要性就不言而喻了。也正因如此,SSDT一直以来都是攻击和防护的要点。恶意攻击者常通过挂钩特定的系统调用,修改进程的执行流,进而指导进程的行为。而防护者同样也挂钩关键的系统调用,从而保护自身服务不被攻击。而用户程序调用系统的方式并不是各行其路,是有一个唯一的入口,通过这个入口,CPU陷入到内核态,然后由系统调用分发函数进行具体调用的分发。由此可见,这个入口就至关重要。在windowsXP之前是采用软中断的方式,即int2e的方式陷入到内核,在后来windowsXP以及后来的版本都采用了快速系统调用指令sysenter/syscall的方式陷入到内核,X86架构使用sysenter指令。IA32_SYSENTER_EIP寄存器保存系统调用内核空间的入口函数地址,x86架构下就是KiFastCallEntry函数的地址。在执行sysenter指令时,IA32_SYSENTER_EIP寄存器的值被加载到当时的EIP寄存器中,从而KiFastCallEntry函数得到执行。因此,通过设置IA32_SYSENTER_EIP寄存器的值,可以手动设置系统调用入口。非换页内存:非换页内存是位于系统地址空间,在系统运行过程中不会被换出到外存,且非换页内存的页表是在系统初始化时期就建立完成,因此系统运行期间,非换页区的地址不会发生pagefault异常,因此要构建新的SSDT必须要使用非换页区的内存。句柄:windows的句柄类似于Linux下的文件描述符。当打开一个对象就返回一个关于此对象的句柄,有了该句柄就可以访问该对象。当然,在打开对象的时候会写入权限位,即句柄实际上是有权限划分的。句柄在windows中其实本质就是索引,系统为每个进程维护了一个局部的句柄表,同时系统还有一个全局的句柄表。前者用于进程自己打开的各种对象的句柄,比如文件、进程、设备等,方便文件访问某个对象。后者用于为每个进程、线程申请ID,即PID本质上也是句柄表中的索引。FS寄存器:x86架构下的windows系统在内核模式下,FS寄存器指向当前处理器的结构KPCR,通过KPCR可以获取到当前线程、进程以及其他的一些核心信息。现有的方法为了防止指定进程被恶意进程攻击,都是通过拦截指定系统调用,过滤进程的行为。而具体区别体现在拦截系统调用的方式上,目前主要有无代理和有代理两种。1)无代理情况下直接把IA32_SYSENTER_EIP寄存器的值设置为非法值如0xFFFFFFFF,每次进行调用时,非法值被加载到EIP寄存器中,由于非法值不存在对应的页表项,因此会发生pagefault异常。intel为各种异常设置了陷入开关,通过设置pagefault异常陷入,可以在客户机发生指定pagefault的时候陷入到VMM中进行处理,目前的无代理方案主要利用这种方式在底层对进行的调用行为做控制,无代理方案工作架构如图1所示。2)相对于无代理,更多的还是有代理的解决方案。通过在内核中加载代理驱动,在VM(VirtualMachine,虚拟机)内部拦截系统调用,如替换SSDT中的函数地址为自定义的函数,处理完毕再返回到系统服务函数,相当于绕了一下。还有的方案没有替换SSDT,而是把函数中的几个字节替换成jmp指令,从而执行自己的代码,执行完毕再返回到原函数相应位置,这种方式更为隐蔽,但是操作难度较大。通过这种方式还有个关键点就是需要保护代理的安全性,有代理工作架构如图2所示。不难看出,在无代理的情况下,由于设置了IA32_SYSENTER_EIP为非法值,VM每次发生系统调用都会陷入,由于系统把这些系统服务提供给用户,被调用的频率相当频繁,这就导致VM频繁的发生VM-Exit和Vm-Entry,每次切换都要保存客户机的状态,然后恢复Host的状态,因此会造成比较大的性能开销,虽然拦截比较隐蔽,但是性能方面确实开销较大。有代理情况下,虽然避免了大规模的模式切换,但是其隐蔽性不够强。不管是直接修改SSDT还是利用inLineHOOK,修改函数本身,客户机的程序很容易检测到被修改的部分,且很容易予以恢复。再者,代理本身的安全性也很难得到保证,代理一旦被卸载,所要实施的安全措施也就不复存在。技术实现要素:本发明所要解决的技术问题是提供一种基于虚拟化技术的无代理客户机进程防护方法,该方法采用无代理的方式,透明拦截虚拟机中涉及进程操作的关键系统调用,在VMM(VirtualMachineMonitor,虚拟机监视器)中通过过滤源头进程保护虚拟机进程的安全。由于不需要在虚拟机内部安装任何代理以辅助功能的实现,虚拟机就不会发现SSDT被拦截,同时也正是只有关键调用被设置陷入,大部分无关的系统调用是走正常的处理流程,因此本方法更加高效。为解决上述技术问题,本发明采用的技术方案是:一种基于虚拟化技术的无代理客户机进程防护方法,包括以下步骤:步骤1:在KVM(Kernel-basedVirtualMachine)中透明获取windows非换页内存页,记录内存页的起始地址到虚拟机对应的KVM结构体中;步骤2:注入虚拟机信息,包括SSDT控制结构KeServiceDescriptorTable和SSDT内容,以及改变客户机系统调用执行流的代码的硬编码;步骤3:设置IA32-SYSENTER_EIP寄存器为新注入的KiFastCallEntry函数地址,同时对KiSystemService函数执行挂钩;步骤4:执行流修改完成后,对原有的SSDT内容执行清除;步骤5:设置VMCS结构相关字段,设置IA32-SYSENTER_EIP寄存器的读写陷入和取指令型的pagefault异常陷入;步骤6:虚拟机监视器拦截客户机的取指令pagefault异常,对当前操作进行分析,返回结果到客户机,完成一次访问的处理。根据上述方案,所述步骤1具体为:1.1)在IA32-SYSENTER_EIP寄存器首次被填充时,获取当前处理器的KNODE节点;1.2)从KNODE节点中获取非换页内存链表;1.3)从链表中摘除首个非换页内存页,记录地址到KVM结构体中。根据上述方案,所述步骤2具体为:2.1)从当前处理器结构中获取当前线程结构Kthread,从Kthread中读取当前SSDT控制结构的地址;2.2)根据2.1)中地址获取原始SSDT,读取内容并注入到申请的非换页内存中,记录地址到KVM结构体;2.3)根据SSDT信息注入的实际地址,构建跳转代码的硬编码,注入到客户机中。根据上述方案,还包括步骤7,用户利用用户层工具,向虚拟机监视器下发受保护进程信息,虚拟机监视器维护受保护进程链表。根据上述方案,所述步骤2中,跳转模块利用一块跳板内存,将用于跳转的代码的硬编码注入客户机。根据上述方案,所述步骤2中,SSDT中的进程相关系统调用的地址被设置成非法地址0xFFFFFFFF。与现有技术相比,本发明的有益效果是:首先,本发明在VMM中通过操作VM内存组织结构,透明地在客户机内部分配非换页内存,构造新的SSDT,并从虚拟机监视器向客户机注入跳转函数,并未修改客户机内部的系统调用入口函数,同时,由于屏蔽了IA32_SYSENTER_EIP寄存器的读写,VM本身不会知道设置的函数入口。因此VM不会发现修改了调用执行流,所以,本发明的隐蔽性可以得到较大保证。其次,修改的是新的SSDT中的关键表项而并非对对所有表项设置陷入,只有在关键系统调用被执行的时候才会发生陷入,这样就可以避免大规模的VM-Exit和VM-Entry,拥有较高的性能优势。最后,由于不需要安装客户机代理,一方面代理的安全性不需要考虑,无疑增大了本发明的隐蔽性,另外,安装客户机代理本身肯定会耗费一定的内存资源和CPU资源,从这一点看,本发明同样为VM节省了资源。附图说明图1是传统无代理方法示意图。图2是传统有代理方法示意图。图3是防护各个模块架构关系图。图4是防护系统部署后的防护执行流示意图。图5是KIFastCallEntry跳转流程示意图。图6是KiSystemService跳转流程示意图。图7是windows句柄表结构示意图。图8是句柄解析架构示意图。图9是VM陷入后的处理流程示意图。具体实施方式下面结合附图和具体实施方式对本发明作进一步详细的说明。本发明根据VM系统的特性,基于intelVT技术,构造无代理客户机进程运行时防护系统,用户只需要在客户机内部使用一个用户层工具告诉VMM哪些进程需要保护即可,在功能实现上不需要安装任何代理驱动。系统功能的实现主要分为三个部分,windows内存获取模块、虚拟机信息注入模块、客户机pagefault异常拦截及处理模块,整个防护执行流如图4所示。系统具体实施步骤如下:1、分配windows非换页内存该方法采取无代理最重要的原因就是代理的安全不便防护,如果安装的代理被恶意卸载,安全防护的功能就烟消云散。为了弥补这一缺陷,从虚拟机监视器中透明地获取windows客户机的内存,这样这块内存就必须满足以下几点:1)分配的内存必须是未使用的,否则会影响客户机系统运行。2)内存被分配之后,客户机不能在继续分配这片区域。3)这块内存不能被换页交换出去。4)这块内存的页表必须存在。因为不能被交换出去且页表存在,所以分配内存的区域必须在非换页内存区(非换页内存区的页表在系统初始化期间被建立好且不会被换出到外存)。因为原因1)和原因2),分配必须走正常windows内存分配流程,这并不是说要执行windows内部分配函数,这不可能也不现实。要实现的是把分配到的页在windows中标记为已分配即可。windows的非换页内存的管理依赖于几条链表,且windows用一个数组保存所有的链表头即MmNonPagedPoolFreeListHead,其中保存了四个链表头,第一个元素保存单个页面,第二个元素保存两个页面,第三个元素保存三个页面,最后一个元素保存大于等于4个页面。这里链表本身不浪费空间,系统充分利用这些空闲页,在每个空闲页页首是一个_SLIST_HEADER结构,在分配页之后自然不需要此结构,所以该页恰好可以正常使用。在win7及以后,微软为了发挥多处理器更大的性能,加入了对NUMA系统的支持。NUMA(非一致性内存),即把内存区域分片管理,每个区域有一个KNODE节点负责,一般一个KNODE会绑定至少一个CPU,当运行在当前CPU的线程要分配内存时,系统首先从当前CPU绑定的KNODE节点分配内存,这些叫做localmemory,访问起来速度较快。本方法采用在MSR_SYSENTER_EIP寄存器首次被填充的时候,同时,这也是系统部署的时机。这个时候系统核心部分初始化刚刚完毕,驱动和应用程序还未加载,可以保证在系统调用机制被使用之前完成替换。通过当前处理器结构KPRCB获取当前处理器的KNODE节点,从而找到KNODE节点关联的非换页内存页链表表头NonPagedPoolSListHead,这是一个三个_SLIST_HEADER结构的数组,每个结构位于一个非换页内存页的起始位置,通过实时debug也会发现每个结构都是页对齐的,故获取_SLIST_HEADER结构的地址就得到了页面的地址。本方法使用单页面即可,所以直接操作第0个表头,摘除之后移动第二个节点到第一个位置,保证链表的可使用性。摘除页面后,移动表头指针指向第二个页面,同时保存页面地址到虚拟机监视器的虚拟机监视器结构体中。2、虚拟机信息注入在客户机内存获取模块获取了非换页内存后,就需要在虚拟机监视器中向客户机注入信息,主要包含两部分:SSDT内容以及其控制结构KeServiceDescriptorTable和重定向函数的注入。前者是构建全新的SSDT以及其控制结构KeServiceDescriptorTable,后者是修改客户机系统调用执行流,从而让客户机使用全新的SSDT。2.1SSDT内容的获取及写入由于在函数中直接使用的是全局结构,要使用新的SSDT必须构造一个全新的结构,之所以不直接修改原来的结构指向,是因为一旦那样,系统便可以正常获取新的SSDT地址,这样构造这个SSDT的意义就不复存在。在当前线程结构Kthread结构中,保存有SSDT的指针Servicetable,指向全局的KeServiceDescriptorTable结构,可以通过此结构获取原始SSDT。KeServiceDescriptorTable结构占用16字节,新申请的页面中,最前面16个字节就存放该结构称之为new_ServiceTable,需要注意为了尽可能减少对系统的影响,这里new_ServiceTable仅仅第一个字段ServiceTableBase和原结构不同,其余字段完全相同。在new_ServiceTable之后空闲4个字节做隔离,在页面偏移0x14处存放新的SSDT,因为已经获取了系统本身导出的结构,所以在虚拟机监视器层利用VMI技术可以直接读取SSDT内容,然后写入到新的空间,得到新的SSDT。2.2重定向函数的注入该部分包括KiFastCallEntry相关代码的注入和KiSystemService函数相关代码的注入。2.2.1KiFastCallEntry相关代码的注入本方法采取一种拿空间换时间的方法,即自己重新构造KiFastCallEntry函数,由于该函数比较庞大,所以取函数前159字节(获取SSDT结构之后即可),注入到前面申请的内存中,同时把新的函数的地址放入到IA32_SYSENTER_EIP中。这样在用户发起系统调用时,新的函数得到执行,在把新SSDT结构放入指定位置后,就返回到原函数继续执行,由于未修改原函数,同时对RDMSR/WRMSR设置陷入,客户机本身并不会知道修改了执行流。但windows系统有KeServiceDescriptorTable和KeServiceDescriptorTableShadow两种SSDT,前者被导出,可被用户程序请求;后者用于windows子系统,未被导出。而两者被调用的途径都是由KiFastCallEntry分发的,所以还不能生硬地直接修改函数中的基地址。基于此,本方法采用了二级跳转机制,跳转流程如图5所示。如图5所示,需要三个代码模块:1)代码段0:KiFastCallEntry前159个字节。2)代码段1:条件判断模块。3)代码段2:SSDT替换模块。在sysenter指令执行以后,就执行代码段0的newKiFastCallEntry函数,在该函数的偏移0x99处,替换成JMP指令,JMP到代码段1进行判断目标SSDT是哪个,如果是影子SSDT,就返回到原函数的位置继续执行,否则就Jmp到代码段2,把新SSDT放入EDI寄存器中,接着也返回到原函数中。这样,由于屏蔽了MSR寄存器的读写,客户机本身并不会感知到新的函数流程,恶意程序也没有办法更改的执行流。2.2.2KiSystemService相关代码的注入在从内核发起系统调用时,会用到KISystemService函数,该函数执行过程中会跳转到KiFastCallEntry函数,在清除旧的SSDT之前,需要设置KiSystemService函数jmp到新的KiFasrCallEntry对应的偏移处,由于的内存位于非换页内存区,近跳转已经无法满足需求,而远跳转的字节数超过了5个字节。基于此,本方法采取了和前面函数注入类似的方式,利用pushret指令实现,利用跳板内存就不受字节数的限制。为了对跳板内存本身做到足够隐蔽,这里在原始函数执行JMP前的4个字节就替换成跳转指令,这样根据原始函数的结构,依然无法找到跳板内存,继而无法定位新的KIFastCallEntry函数。具体的跳转结构如图6所示。3、设置客户机系统调用入口为新的KiFastCallEntry的地址IA32_SYSENTER_EIP寄存器保存系统调用入口函数KiFastCallEntry函数的地址,通过设置该寄存器为新的KiFastCallEntry函数地址,可以在客户机发起系统调用时,让其转入新的调用执行流,进而使用全新的SSDT。4、原始SSDT的清除为了不让客户机中恶意程序找到系统调用函数的地址,从而进行inlineHOOK,需要对原来的SSDT进行清除,即对所有表项设置成0x0,这样恶意程序就很难获取具体的系统调用函数的地址,从而无法进行函数内的挂钩。5、IA32_SYSENTER_EIP寄存器的读写屏蔽为了向客户机隐藏实际的系统调用入口,本发明采用对该寄存器设置读写陷入来向客户机隐藏其具体内容。每个MSR寄存器对应一个MMIO的地址,IA32_SYSENTER_EIP寄存器对应的地址为176h,具体设置其读写陷入的步骤为:1)设置CPU_BASED_VM_EXEC_CONTROL中的CPU_BASED_USE_MSR_BITMAPS位为1;2)设置Readbitmap和Writebitmap中176h对应的位为1;这样在发生对该寄存器的读写时,就会陷入到虚拟机监视器中,在虚拟机监视器中有对应的处理函数,具体处理方案为:1)在发生读取该寄存器的内容时,向客户机注入原始的系统调用入口,隐藏实际的入口。2)在对该寄存器发生写入操作时,在虚拟机监视器中不做写入,但是正常返回,不让客户机有所察觉。6、pagefault异常拦截及处理该模块是本发明的最终目的,即安全透明的拦截系统调用,但是本模块却不是最难的,因为前面的工作已经把调用表设置好,如果要拦截系统调用,只需要把想要拦截的项在调用表中设置成0xFFFFFFFF即可。对于拦截哪些系统调用,本发明不做限制。由于0xFFFFFFFF是一个非法地址,页表项没有建立,在客户机从该地址取指令的时候就会发生pagefault,拦截这种pagefault,然后在虚拟机监视器中的异常处理函数handle_exception中进行过滤,如果发现当前EIP的值是0xFFFFFFFF,就说明是因为访问要拦截的系统调用而发生的陷入,从而进入自己的处理流程。6.1pagefault的陷入设置pagefault陷入是通过设置VMCS中的相关字段,这里说到在发生pagefault的时候,处理器会根据两个公式判断:PFEC&PFEC_MASK==PFEC_MATCHExceptionBitmap>>14==1如果上述公式为真,则发生陷入,否则不陷入。正是根据这一点,本发明方法采取只对取指令类型的pagefault设置陷入。具体为1)设置ExceptionBitmap的第14位为1。2)PFEC_MASK和PFEC_MATCH为0x8。3)设置进程相关调用在新的系统调用表中的地址为0xFFFFFFFF。这样既可以保证在取指令类型的pagefault发生时陷入又可以避免其他无谓的陷入。目前定义了NTTerminateProcess,NTDebugActiveProcess,NtReadVirtualMemory,NtWriteVirtualMemory四个系统调用,通过对着四个系统调用的透明拦截处理,保障进程运行时安全。NTTerminateProcess:进程终止的必要函数。如果一个进程要终止另一个进程,首先必须要先OpenProcess获取指定进程的句柄,并获取对应的权限。然后调用NTDLL中的存根函数,最终要走到NTTerminatePRocess函数这里,刚才被打开的句柄被作为参数传递进来。通过拦截这个函数,并根据参数中的句柄解析当前进程的句柄表,如果发现目标进程是受保护的进程,且源进程是第三方进程,那么就拒绝本次操作。NTDebugActiveProcess:进程附加的必备函数。由于考虑的是进程运行时安全防护,一般调试有两种方式,一种是通过调试器启动进程,另一种直接附加到已经运行的进程。考虑到的运行场景,只考虑第二种情况。通过拦截进程附加的函数,让受保护的进程无法被附加,进而调试器和受保护进程之间无法建立连接,就自然无法调试了。NtReadVirtualMemory,NtWriteVirtualMemory:windows内核中操作虚拟内存的函数。通过操作此函数,可以向指定的进程读取、写入数据,作为受保护进程,自然不愿意自己的数据被窃取,同时也不愿被注入什么恶意代码,使自己作为恶意代码的巢穴。所以通过拦截这两个函数,可以有效地禁止跨进程读写的行为,让受保护进程真正享受独立的地址空间。通过设置这些函数地址为非法地址,在这些函数被调用的时候,就会发生VM-exit,进而在VMM中处理异常函数中进行过滤、拦截处理,针对进程的系统调用始终会把进程句柄作为首个参数,为了在发生VM-exit后解析出具体的目标进程,需要根据进程句柄解析当前进程的句柄表,从而获取具体的进程对象信息。6.2pagefault陷入处理经过上节的设置,在执行进程相关调用的时候就会发生陷入,进程相关调用的参数均包含进程句柄,根据此参数,通过解析相应进程的句柄表,可以获取进程对象Eprocess,进而获取进程的详细信息。6.2.1句柄参数的获取进程相关的系统调用中,句柄往往是作为第一个参数被传递进来,当具体的系统调用被调用时,这些参数被从用户空间的栈中复制到内核空间的栈中,且ESP寄存器指向内核栈顶。根据这一特点,在发生具体的陷入后,在虚拟机监视器中通过ESP寄存器获取具体的句柄值。6.2.2windows句柄表的解析windows句柄表是分级的句柄表,级数由TableCode的最低两位决定。末级句柄表的表项是一个8字节的_HANDLE_TABLE_ENTRY结构,末级句柄表首个表项不做分配,即单张末级句柄表最多容纳511个句柄。句柄表的结构如图7所示。在一个进程被创建时,首先为进程分配一个单层句柄表,随着进程中句柄数量的增加,单层句柄表会根据情况扩展成二层句柄表、三层句柄表。要获取windows的句柄表,在进程结构Eprocess中保存有当前进程句柄表的地址。根据上面句柄表的结构,句柄表的解析就不是难题了。具体而言,本方法仍然基于FS寄存器,获得当前线程结构Kthread,通过该结构获得当前进程结构EProcess,进而从当前进程结构获得句柄表的地址。整个解析过程虽然原理已经明确,但是由于句柄值以4递增而并非连续,要准确解析一个句柄需要注意限制,不能超出范围。整个解析架构可参考图8。6.2.3核心处理逻辑在根据进程句柄解析出进程对象之后,就可以根受保护进程链表中的进程信息做对比,判断当前目标进程是否是受保护进程,然后根据KPCR结构获取当前运行进程信息,若二者不一致,就说明有第三方进程操作受保护进程,进而判断该进程是否是系统进程,如果不是,就说明是第三方恶意进程,那么就略过当前指令的执行,实际上就是call指令,向eax写入错误码,直接返回客户机。否则,向EIP写入正确的函数地址,返回客户机表示对本次访问放行。整个异常处理流程如图9所示。1)正常的取指令引起的pagefault,即不是设置的系统调用引起的pagefault对于此类陷入,不必做其他操作,仅仅向客户机注入pagefault异常,让客户机处理即可。2)是设置的系统调用引起的陷入,但目标进程不是受保护的进程这种情况注入系统调用的正确地址,返回客户机即可。3)是设置的系统调用引起的陷入,且目标进程是受保护的进程这种情况就要对此操作溯源,查看发起此操作的对象是否是合理的对象,比如是否是进程自身发起的还是第三方发起的,如果操作对象合法,就注入正确的调用地址,返回客户机,如果非法就向eax注入error返回值表示执行错误,然后略过call指令的执行,直接返回。当前第1页1 2 3 当前第1页1 2 3 
当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1