特邀体验者
- 帖子:2994
- 注册:
2007-06-02
- 来自:
|
发表于:
2009-06-11 15:25
|
显示全部
短消息
资料
回复: 反病毒引擎设计(作者不详)Part4
3.设备控制例程(IRP_MJ_DEVICE_CONTROL):它会从入口IRP当前堆栈单元中取得用户程序利用DeviceIoControl传送进来的IO控制代码(IoControlCode),以此判断用户程序的意图。和Hooksys.sys协同工作的ring3级客户程序guidll.dll会依次向Hooksys.sys发送IO控制请求来完成一系列工作,具体次序和代码含义如下: 83003C2F:将guidll取得的驱动器类型值传给驱动(保存在DriverType变量中),根据此变量值的不同,设置不同的等待(KeWaitForSingleObject)超时值,因为非固定驱动器的读写时间会稍长些。 83003C0F:保存guidll传送的用户指定的拦截文件的类型,其实这个类型过滤器在查毒模块中已存在,这里再设置显然是为了提高处理效率:它确保不会将非指定类型文件送到ring3级查毒模块,节省了通信的开销。经过解析的各文件类型过滤块指针将保存在_gaFileNameFilterArra数组中,同时更新过滤项个数_gNumOfFilters变量的值。 83003C13:修改文件系统驱动程序对象调度例程入口,启动拦截文件操作的钩子函数的工作。 83003C17:恢复文件系统驱动程序原调度例程入口,停止拦截文件操作的钩子函数工作。 以上列出的IO控制代码的发出是固定,而当钩子函数启动后,还会发出一些随机的控制代码: 83003C07:驱动将打开文件链表的头元素即最先的请求打开的文件删除并插入到等待链表尾部,同时将元素的用户空间地址传送至ring3级等待查杀打开文件的线程中处理。 83003C0B:驱动将关闭文件链表的头元素即最先的请求关闭的文件删除并插入到备用链表尾部,同时将元素中的文件名串传送至ring3级等待查杀关闭文件的线程中处理 83003C1F:当查得关闭文件是病毒时,更新历史记录链表。 下面介绍钩子函数_HookCreateDispatch和guidll中等待查杀打开文件的线程协同工作流程,而关闭,清除,设置文件信息,和写入操作的处理与此大同小异: 当文件请求进入钩子函数_HookCreateDispatch后,它首先从入口IRP中定位当前的堆栈单元并从中取得代表此次请求的文件对象。然后判断当前进程是否为我们自己,若是则必须放过去,因为查毒模块中要频繁的进行文件操作,所以拦截来自ravmon的文件请求将导致严重的系统死锁。接下来利用堆栈单元中的文件对象取得完整的文件路径名并确保文件不是:\PIPE\,\IPC。之后查找历史记录链表以确定该文件是否最近曾被检查并记录过,若在历史记录链表中找到关于该文件的记录并且记录未失效即其时间戳和当前系统时间之差不得大于1F4h,则可直接从记录中读取查毒结果。如历史链表中没有该文件的记录则利用保存的文件类型过滤阵列检查文件是否在被拦截的文件类型之列。至此才进入真正的检查打开文件函数_RAVCheckOpenFile,此函数入口处先从备用,等待或关闭链表头部摘得一空闲元素(_GetFreeEntry)并填充之,如文件路径名域等。接着将空闲元素加入打开文件链表尾部并释放Hookopen信号量唤醒ring3下等待检查打开文件的线程。然后调用KeWaitForSingleObject在空闲元素中保存的一个事件对象上等待ring3查毒的完成。当钩子函数挂起后,ring3查毒线程得到执行:它会向驱动发出一IO控制码为83003C07的请求以取得打开文件链表头元素即保存最先提交而未决的文件请求,驱动会将元素映射到用户空间中的偏移地址直接传给它。接着它调用RsEngine.dll中的fnScanOneFile函数进行查毒并在元素中设置查毒结果位,完毕后再对元素中保存的事件对象调用SetEvent唤醒在此事件上等待的钩子函数。被唤醒的钩子函数检查被ring3查毒代码设置的结果位以此决定该文件请求是被采纳即调用保存的原调度例程还是被取消即调用IofCompleteRequest直接返回,同时增加历史记录。 以上只是钩子函数与ring3线程流程的一个简单介绍,其中省略了诸如判断固定驱动器,超时等内容,具体细节请参看guidll.dll和hooksys.sys的反汇编代码注释。 4.关闭例程(IRP_MJ_CLOSE):停止钩子函数工作,恢复文件系统驱动程序原调度入口(_StopFilter)。解除到用户空间的内存映射。 5.卸载例程(DriverUnload):停止钩子函数工作,恢复文件系统驱动程序原调度入口。删除设备和符号连接。删除初始化时创建的一组命名事件对象Hookxxxx,包括解除指针引用,关闭打开的句柄。释放为MDL(_pMdl),备用链表(_SysBufAddr),历史记录链表(_HistoryBuf)和过滤器分配的内存空间。删除为文件名过滤数组访问同步设置的资源变量(_FilterResource)。解除对系统全局命名内核区中Hookopen和Hookclose两个命名信号量的指针引用。 3.4.3HOOKSYS.SYS逆向工程代码剖析 3.4.3.1取得当前进程名称代码 初始化例程中取得进程名在Eprocess中偏移 00011889 call ds:__imp__IoGetCurrentProcess@0 ;得到当前进程System的Eprocess指针 0001188F mov edi, eax ;Eprocess基地址 00011891 xor esi, esi ;初始化偏移为0 00011893 lea eax, [esi+edi] ;扫描指针 00011896 push 6 ;进程名长度 00011898 push eax ;扫描指针 00011899 push offset $SG8452 ; "System" ;进程名串 0001189E call ds:__imp__strncmp ;比较扫描指针处是否为进程名 000118A4 add esp, 0Ch ;恢复堆栈 000118A7 test eax, eax ;测试比较结果 000118A9 jz short loc_118B9 ;找到则跳出循环 000118AB inc esi ;增加偏移量 000118AC cmp esi, 3000h ;在12K范围中扫描 000118B2 jb short loc_11893 ;在范围之内则继续比较 钩子函数开始处取得当前进程名 00010D1E call ds:__imp__IoGetCurrentProcess@0 ;得到当前进程System的Eprocess指针 00010D24 mov ecx, _ProcessNameOffset ;取得保存的进程名偏移量 00010D2A add eax, ecx ;得到指向进程名的指针 3.4.3.2启动钩子函数工作代码 000114F4 push 4 ;预先将文件系统驱动对象个数压栈 000114F6 mov esi, offset FsDriverObjectPtrList ;取得文件系统驱动对象指针列表偏移地址 000114FB pop edi ;用EDI做记数器,初始值为4 000114FC mov eax, [esi] ;取得第一个驱动对象的指针 000114FE test eax, eax ;测试是否合法 00011500 jz short loc_11548 ;不合法则继续下一个修改驱动对象 00011502 mov edx, offset _HookCreateDispatch@8 ;取得自己的钩子函数的偏移地址 00011507 lea ecx, [eax+38h] ;取得对象中打开调度例程(IRP_MJ_CREATE)偏移 0001150A call @InterlockedExchange@8 ;原子操作,替换驱动对象中打开调度例程的入口为钩子函数的偏移地址 0001150F mov [esi-10h], eax ;保存原打开调度例程的入口 3.4.3.3映射系统内存至用户空间代码 0001068E push esi ;系统内存大小 0001068F push _SysBufAddr ;系统内存基地址 00010695 call ds:__imp__MmSizeOfMdl@8 ;计算描述系统内存所需内存描述符表(MDL)大小 0001069B push 206B6444h ;调试用标签 000106A0 push eax ;MDL大小 000106A1 push 0 ;在系统非分页内存池中分配 000106A3 call ds:__imp__ExAllocatePoolWithTag@12 ;为MDL分配内存 000106A9 push esi ;系统内存大小 000106AA mov _pMdl, eax ;保存MDL指针 000106AF push _SysBufAddr ;系统内存基地址 000106B5 push eax ;MDL指针 000106B6 call ds:__imp__MmCreateMdl@12 ;初始化MDL 000106BC push eax ;MDL指针 000106BD mov _pMdl, eax ;保存MDL指针 000106C2 call ds:__imp__MmBuildMdlForNonPagedPool@4 ;填写MDL后物理页面数组 000106C8 push 1 ;访问模式 000106CA push _pMdl ;MDL指针 000106D0 call ds:__imp__MmMapLockedPages@8 ;映射MDL描述的物理内存页面 ...... 000106DB mov _UserBufAddr, eax ;保存映射后的用户空间地址 _UserBufAddr 和_SysBufAddr映射到相同的物理地址。 结 论 至此本论文已告撰写完毕。本论文在介绍了诸多目前较为流行的病毒技术后着重讨论了当今两大反病毒技术:虚拟机和实时监控。 我参与开发的w32encode是一个功能完备且结构复杂的商用虚拟机,它属于32位自含指令式虚拟机,与其它搜索清除模块合并在一起组成了一个功能强大的反病毒引擎。虽然目前它还不能支持所有的386+指令集,但从其查杀毒的运行效果来看结果还是非常令人满意的:普通的加密变形病毒可以在虚拟机默认的处理常式中查杀;特殊的,如hps,marburg等复杂加密变形病毒则可通过向虚拟机中添加少量的病毒特定处理代码来完成查杀。由于反虚拟执行技术的出现,所以今后对此虚拟机源代码的更新--向其中添加更多的对操作系统机制的支持--或者重写--成为真正的虚拟机器而非虚拟CPU--将是不可避免的。 同时,我通过逆向工程某反病毒软件的实时监控程序,在系统原理和驱动编程上又有了新的认识,并且它大大增强了我的反汇编功力。今后我会将注释的反汇编代码编写成C语言版源代码,并把病毒扫描模块移到系统核心态下工作,从而使整个工程变为“主动的与内核无缝连接”式监控。 总之当今反病毒技术的主流发展方向是屏弃传统的特征码扫描,创建智能的监控与行为分析引擎,这就必然要求更加先进的虚拟机和实时监控技术。
|