A盾的原理是在驱动加载时重载os内核,获取原始ssdt表的地址。
应用层点击查询的代码在文件A-ProtectView.cpp中,每种点击操作调用相应的
query查询函数,在query函数里 ReadFile。读操作的Handle是A盾自定义的操作码,
类似DeviceIoControl的控制码,比如handle 为LIST_SSDT,LIST_INLINEHOOK等。
驱动加载时hook 了NtReadFile,在hook函数里做应用层要求的操作,返回对应的结构buffer
给应用层。
下面分两部分做分析。驱动加载的操作和加载和应用层交互完成具体功能部分。
驱动加载的DriverEntry在SafeSystem.c中,驱动加载时主要完成 1.判断是否是系统刚刚启动。通过当前进程数大于三。2.创建注册表标识驱动运行起来,应用层启动成功删除,主要是判断是否启动成功。3.
判断是否是hook 了 KiFastCallEntry。主要是读取msr寄存器指向该函数的指针,inline 前五个字节到新内核。3.重载内核。重载内核的目的是获取原始ssdt表的地址,方便查询ssdt hook 和 自己调用函数时不经过其他软件的过滤。4.对服务做深度扫描。主要是在驱动里面枚举Services的注册表键值。
加载驱动的核心操作在第3步重载内核ReLoadNtos函数中。函数中操作主要是1.获取已经加载的内核的信息,2.调用InitSafeOperationModule来peload一个ntos避免其它软件hook的过滤,peload的操作需要先获取内核文件大小,通过IRP_MJ_QUERY_INFORMATION自己构造IRP来获取。然后自己通过IRP_MJ_READ自己构造IRP把文件全部读入内存。然后读取dos,nt头和节表,获取模块基地址。然后修复导入表,重定位表。这么麻烦的操作主要是内存中的pe文件和硬盘中的文件会有字节对齐的差别,导致计算ssdt偏移时出错。重载os后,在导出表中搜寻KeServiceDescriptorTable,然后计算它和加载基址的偏移差。通过旧的ssdt偏移,新的os内核基址,旧的os内核基址,_KiServiceTable,计算出重载后的os的ssdt表的基址。得到基址后,修复对ssdt表的地址重定位。将重定位后的ssdt表保存到自己的全局变量中了。3.将导出表里面的函数地址信息保存到自己的结构中。
4.调用ReLoadNtosCALL通过刚才保存的结构从新内核中获取PsGetCurrentProcess,MmIsAddressValid的原始地址,方便后面调用。5.hook KiFastCallEntry开头5个字节跳转到新内核的KiFastCallEntry,再 hook dwReloadKiFastCallEntry,目的不是很清楚。6.初始化和应用层的通信,hook ZwReadFile ,通过handle和应用层通信。hook ZwTerminateProcess保护自己不被结束进程。hook ZwSetValueKey监控start,ImagePath,ServiceDll启动服务,加载驱动。hook ZwCreateSection监控进程启动,object hook,子进程创建,dll加载,dll劫持。7. hook PsCreateSystemThread监控系统线程的创建。8.hook NtReadFile获取csrss.exe的
EPROCESS. Reload win32k根据对话框,判断是否是A盾发送的命令。
和应用层交互完成具体功能部分。按菜单提供的功能从上到下分析。
查询SSDT的hook,分为两种ssdt hook 和inline hook。通过reload os的操作,对比下系统的ssdt和reload os的ssdt就可以出来了。对于inline hook,依次遍历ssdt表的函数,在导出表中找到对应的函数名,然后二分法查找旧的函数和reload 的函数汇编代码,如果不同就是inline hook 了,然后根据call 指令还是jmp指令判断跳转类型。还有简单处理一下多级跳。
查询Shadow SDT的hook,先附加CSRSS进程,查找方法同SSDT hook。
查询内核钩子,对于win32k和ntos处理一样。先检查模块的eat导出表hook,对于导出的函数依次调用反汇编引擎,二分法查找每个函数内是否有hook 指令。它的反汇编引擎是libdasm 开源库。如果是扫描指定模块,方法同上面。
查询Object Hook,对于FileObject,DeviceObject,DriverObject,CmpKeyObject分别进行检测。对于FileObject,检测FileCloseProcedure,IopProcedure,IopQueryName是否被inline hook,这个是文件对象的关闭函数,打开函数,查询函数。对于DeviceObject,检测IopDeleteDevice,IopParseDevice设备对象的删除,打开函数。对于DriverObject,检测IopDeleteDriver函数。对于CmpKeyObject,检测CmpCloseKeyObject,CmpDeleteKeyObject,CmpParseKey,CmpKeyObjectType函数。注册表的是openkey注册表,然后ObReferenceObjectByHandle,ObGetObjectType。
检测驱动模块,检测分为链表检测和A盾运行后的驱动监控,暴力枚举驱动对象。链表检测主要是遍历DriverObject->DriverSection->InLoadOrderLinks。暴力枚举参考了sysnap的代码貌似,搜索MmNonPagedPoolStart开始的内存区,搜索0xf000000个字节,通过判断MajorFunction[28]来确定是不是驱动。
检测内核中线程的创建,主要是hook PsCreateSystemThread监视线程的创建。
检测内核中系统线程或者进程的线程,主要是检测硬编码偏移,eprocess的ThreadListHead 线程链表和ethread的ThreadListEntry。
检测DPC定时器,系统有DPC和IO两种定时器。获取每个cpu的kpcr+0x34->KdVersionBlock->KiProcessorBlock结构,每个cpu有个KPRCB控制结构,里面有KTIMER_TABLE_ENTRY,即DPC定时器的地址。
检测IO定时器,在IoInitializeTimer定时器初始化函数中,搜寻到nt!IopTimerQueueHead定时器链表
指针的偏移指令,然后枚举这个链表。
检测系统回调,分别检测文件系统,创建进程,创建线程,loadImage,BugCheck,关机回调,LeaveSession注销回调。对于文件系统回调链表,在IoRegisterFsRegistrationChange函数搜索IopFsNotifyChangeQueueHead回调链表的地址。对于进程,是在PsSetCreateProcessNotifyRoutine搜索回调数组的指令偏移。对于线程,是在PsSetCreateThreadNotifyRoutine搜索回调数组的指令固定偏移。对于LoadImage是在PsSetLoadImageNotifyRoutine中搜索回调数组指令偏移。对于BugCheck,是搜索KeRegisterBugCheckCallback函数中的回调例程指令地址。关机回调是搜索IoRegisterShutdownNotification的固定偏移。对于注销回调,是搜索SeRegisterLogonSessionTerminatedRoutine中的固定偏移。
检测工作队列线程。硬编码WorkSuspendThread检查。可以枚举ExWorkerQueue。
检测过滤驱动,分别枚举\FileSystem,\Driver目录下的驱动。依次调用ZwOpenDirectoryObject,
ZwQueryDirectoryObject打开目录对象,查询目录对象,然后遍历设备栈看AttachedDevice附加设备。
检测Ntfs驱动例程,Peload 驱动文件ntfs.sys,找到驱动入口点DriverEntry后,硬编码找到派遣例程,
重定位ntfs,然后找到各种irp_mj_xx的派遣例程地址。
检测TcpIp驱动例程,Nsiproxy网络netBios驱动例程,KbdClass,Mouclass,Atapi。即网络,键盘,鼠标的驱动派遣例程。方式同ntfs。Atapi是Windows IDE/ATAPI 硬盘的小端口驱动程序。Nsiproxy是以前的
netio.sys驱动。
检测系统进程,主要是遍历Eprocess中的ActiveProcessLinks进程链表。
检测系统服务,分为普通检测和深度检测。普通检测就是驱动中枚举注册表CurrentControlSetServices。深度检测就是在系统启动时在注册表刚初始化好时枚举依次,然后做一次对比。
检测网络连接,主要是显示网络连接状态。先reload tcpip.sys和nsiproxy.sys,然后自己构建一个
IRP向Tcpip驱动发送IOCTL_TCP_QUERY_INFORMATION_EX查询,然后打印出来。
检测被动防御,主要是向驱动查询日志信息,驱动启动后会记录日志信息,比如进程,线程创建等。
本机所有数据的监控。这个是自己注册了一个Ndis协议驱动,监控所有发送出去的包。当开启监控时,
加载A-ProtectTcpSniffer.sys协议驱动,向该驱动发送控制码IOCTL_NDIS5PKT_BIND_ADAPTER绑定网卡,
发送IOCTL_NDIS5PKT_SET_OID_VALUE控制码设置网卡信息,然后创建A-Protect-TcpSniffer.txt记录文件
最后开启一个线程注册事件死循环等待ndis激活事件,事件激活后输出到记录文件中。向协议驱动发送读请求,然后驱动中把请求队列包中的一个包复制到输出缓冲区。驱动在ReceiveHandler中把包保存到接收队列里。
检测启动项,主要是在驱动中读Winlogon,CurrentVersion,Installed Components,PrintMonitors,PrintProviders注册表项传给应用层。
一键体检。主要是在应用层调用前面的检测方式输出到文件中。
下面是手动配置中的功能,在文件ProtectSetting中。
强制删除文件。就是把文件对象FileObject的SECTION_OBJECT_POINTERS结构的DataSectionObject和ImageSectionObject两个域清空,常规代码。
禁止加载驱动。就是hook SeSinglePrivilegeCheck,在权限检查的时候都返回失败。
强制环保重启就是KeBugCheck(POWER_FAILURE_SIMULATE),其实是HalReturnToFirmware。
一键卸载360主要是PspTerminateThread结束360进程里面的所有线程。然后删除目录和注册表。
禁止创建进程,禁止创建文件,进程服务回写,禁止内核线程,禁止全局钩子,关闭dll劫持防护在开源代码中目前还没有实现。