参考资料:
3、为什么win32k.sys在System进程空间无法访问
4、明明白白自旋锁
6、用DeviceIoControl实现应用程序与驱动程序通信
推荐使用的环境 | 备注 | |
操作系统 | Windows 7 SP3 | 简体中文版 |
虚拟机 | VM | 版本号:15 |
编译器 | VS2013 + WDK8.1 | |
调试 | Windbg | sxe ld xxxx.sys |
目录:
1、流程图文介绍
2、驱动入口点DriverEntry
3、驱动高低版本区别
4、驱动接口使用
5、HookPort_InitSDT
1、流程图文介绍
1、Hook前后的对比图
2、HookPort的工作流程
HookPort负责构造一份空白的Hook模板(不负责编写对应的Fake函数,导出给SalfProtectionX用)
可以理解为老板(HookPort)小弟(SelfProtectionX)
理论上我们是可以有无数个SelfProtectionX,但是大数字最大限制16个
Hook模板结构如下(单向链表结构):
举个例子:
假设我们一共有SelfProtection1、SelfProtection2两个驱动设置了对应的Fake_CraeteProcess函数
原始CreateProcess->KiFastCallEntry->Filter_CreateProcess代理函数->HookPort_DoFilter
循环将链表中所有Fake函数取出来并执行,直到链表下一个为零终止
必须全部所有Fake函数合法返回才算正确,其中一个返回错误都算错误
2、驱动入口点DriverEntry
2、1 如何调试
首先输入:sxe ld xxxxx.sys 中断
然后输入:lmvm xxxxx 获取基地址,后面基地址+偏移
2、2 代码逻辑流程
1、获取系统版本信息,假设是win10将Global_Version_Win10_Flag变量置1
2、安全模式下禁止启动
3、创建\\Device\\***HookPort设备和\\DosDevices\\***HookPort符号链接
4、设备DeviceExtension驱动接口,为3600SelfProtection服务
5、注册IRP_MJ_CREATE、IRP_MJ_CLOSE、IRP_MJ_DEVICE_CONTROL
6、执行HookPort_InitSDT函数该函数实现功能如下:
6、1 设置内核API过滤函数
6、2 挂钩KiFastCallEntry
6、3 创建线程、进程、模块回调
6、4 IAT方式挂钩KeUserModeCallback,可以拦截DLL注入、键盘劫持等等。
7、初始化驱动导出接口
8、执行HookPort_19230函数(不知取什么名字好)
8、1 假设是Win2K(Int 2E)就挂钩KiSystemService
8、2 实现LoadImageNotifyRoutine对应的Fake函数
8、3 LoadImageNotifyRoutine的Fake函数是根据你打开个某个进程设置ZwDisplayString对应的Fake函数为空函数
2、3 代码实现:
//************************************ // 函数名称: DriverEntry // 函数说明:驱动程序入口 // 作 者:Mr.M // 参考网址: // 作成日期:2019/11/29 // 返 回 值: NTSTATUS // 参 数: IN PDRIVER_OBJECT DriverObj // 参 数: IN PUNICODE_STRING RegPath //************************************ NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, //代表本驱动的驱动对象 IN PUNICODE_STRING RegPath //驱动的路径,在注册表中 ) { NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; UNICODE_STRING SymbolicLinkName; UNICODE_STRING DestinationString; PDEVICE_OBJECT DeviceObject = NULL; Global_DriverObject = (ULONG)DriverObject; //1、获取版本信息 Status = HookPort_PsGetVersion(); if (!NT_SUCCESS(Status)) { return Status; } //2、安全模式下不启动 if (*(ULONG*)InitSafeBootMode) { if (*(ULONG*)InitSafeBootMode == 1) { Status = RtlCheckRegistryKey(RTL_REGISTRY_CONTROL, HookPort_Minimal); } else { if (*(ULONG*)InitSafeBootMode <= 1u || *(ULONG*)InitSafeBootMode > 3u) return STATUS_NOT_SAFE_MODE_DRIVER; Status = RtlCheckRegistryKey(RTL_REGISTRY_CONTROL, HookPort_Network); } if (Status < 0) return STATUS_NOT_SAFE_MODE_DRIVER; } //2、创建设备 RtlInitUnicodeString(&DestinationString, HookPort_DeviceName); RtlInitUnicodeString(&SymbolicLinkName, HookPort_LinkName); Status = IoCreateDevice( DriverObject, sizeof(HOOKPORT_EXTENSION), //扩展18u &DestinationString, FILE_DEVICE_UNKNOWN, //#define FILE_DEVICE_UNKNOWN 0x00000022 FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics ,#define FILE_DEVICE_SECURE_OPEN 0x00000100 FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { KdPrint(("HookPort: DriverEntry IoCreateDevice failed,err=%08x\n", Status)); return Status; } //3、给设备创建一个符号链接 Status = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString); if (!NT_SUCCESS(Status)){ KdPrint(("HookPort: DriverEntry IoCreateSymbolicLink failed,err=%08x\n", Status)); IoDeleteDevice(DeviceObject); return Status; } //4、DeviceControl都是些开启调试信息相关的直接无视 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)HookPort_Create; DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)HookPort_Close; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)HookPort_DeviceControl; //5、初始化部分各种hook、创建进程、线程回调等等 if (!NT_SUCCESS(HookPort_InitSDT())) { IoDeleteSymbolicLink(&SymbolicLinkName); IoDeleteDevice(DeviceObject); return STATUS_UNSUCCESSFUL; } //6、初始化导出接口函数 HookPort_InitDeviceExtInterface(DeviceObject); //7、 //1、根据条件判断是否启用FakeKiSystemService的hook //2、初始化扩展结构,导出给另外一个sys使用 HookPort_19230(); KdPrint(("360HookPort驱动加载成功\t\n")); DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } |
3、驱动高低版本区别
1、高低版本HookPort代码区别
底:layerfsd作者发布的Hookport为蓝本作为低版本(2010年)
高:笔者逆向的Hookport高版本(2019年)
2、Hook代理函数优化
低版本
1、我们发现一个问题,就是有多少个fake函数定义多少个相同的代理函数
2、我们发现代理函数逻辑基本相同,除了参数个数不一致
高版本
1、除了个别感兴趣的其他都通用函数模板处理
2、通用函数模板
3、修复通用函数分为5个部分:
3、Hook方式(加密解密4已经有图文介绍了13章)
数字hook点:
// 保存特征指令之后的那个地址,即钩子处理之后的返回地址
//840541a4 2be1 sub esp, ecx 此时address = 840541a4
//840541a6 c1e902 shr ecx, 2 此时g_KiFastCallEntry_360HookPoint = 840541a6
//840541a9 8bfc mov edi, esp 此时g_KiFastCallEntry_Fake_rtn_address = 840541a9
hook方式有两种:
IdtHook4号中断
InlineHook
4、驱动接口使用
注意HookPort只是初始化接口,是导出给N个类似于SelfProtection的驱动使用
导出接口结构体定义如下:
/*
// sizeof(HOOKPORT_EXTENSION) = 0x18
设备扩展包含了添加规则的接口
1、其他驱动需要增加规则时只需要获取Hookport的驱动扩展访问里面的HookPort_FilterRule_Init初始化一条规则
2、HookPort_SetFilterSwitchFunction 设置规则过滤函数
3、HookPort_SetFilterRuleFlag 设置开关表示启动 or 关闭
State 启动标识
HookPort_FilterRule_Init 初始化规则,新建规则会加到规则链中
HookPort_SetFilterSwitchFunction 设置规则过滤函数
HookPort_SetFilterRuleFlag 设置规则开关
HookPort_SetFilterRuleName 设置规则名字
Value3F1 该驱动版本
*/
typedef struct _HOOKPORT_EXTENSION
{
_DWORD State;
_DWORD HookPort_FilterRule_Init;
_DWORD HookPort_SetFilterSwitchFunction;
_DWORD HookPort_SetFilterRule;
_DWORD HookPort_SetFilterRuleName;
_DWORD Value3F1;
}HOOKPORT_EXTENSION, *PHOOKPORT_EXTENSION;
然后进行初始化操作
//初始化导出接口
ULONG NTAPI HookPort_InitDeviceExtInterface(IN PDEVICE_OBJECT DeviceObject)
{
PHOOKPORT_EXTENSION pHookPortExt;
pHookPortExt = DeviceObject->DeviceExtension;
pHookPortExt->State = (PULONG)3;
pHookPortExt->HookPort_FilterRule_Init = HookPort_AllocFilterRuleTable; //初始化规则
pHookPortExt->HookPort_SetFilterSwitchFunction = HookPort_SetFilterSwitchFunction; //设置规则过滤函数
pHookPortExt->HookPort_SetFilterRule = HookPort_SetFilterRule; //设置规则开关
pHookPortExt->HookPort_SetFilterRuleName = HookPort_SetFilterRuleName; //设置规则名字
pHookPortExt->Value3F1 = 0x3F1; //版本
return pHookPortExt;
}
使用:
FakeServiceRoutine[X] | Fake_xxxxx | 设置单独的Fake函数 |
FakeServiceRoutine[X] | 1 | 设置单独的Fake函数开关 |
IsFilterFunFilledReady | 1 | 设置总开关 |
关闭:
FakeServiceRoutine[X] | 0 | 设置单独的Fake函数,随你清不清 |
FakeServiceRoutine[X] | 0 | 设置单独的Fake函数开关,随你清不清 直接拉总闸省事 |
IsFilterFunFilledReady | 0 | 设置总开关,嫌一个个关闭麻烦直接关这个(拉总闸直接GG) |
5、HookPort_InitSDT
1、获取SSDT和ShadowSSDT地址
SSDT表获取方法:
直接NT内核里面找KeServiceDescriptorTable,KeServiceDescriptorTable是导出的遍历下导出表
ShadowSSDT表获取方法:
首先获取win32k基地址,然后再通过特征码查找代码如下
2、获取函数索引
1、通过导出表找到zwXXX的地址,然后再取对应的索引号B8+1就是索引号
2、取B8后面4个字节就是索引号
3、SSDT和ShadowSSDT区别
ShadowSSDT的索引号要-0x1000才是真正的索引
例如4419-4096=323
3、填写函数过滤数组
该数组一共有0x9E个
4、申请一块缓冲区专门存放过滤函数开关、代理函数等地址,大小是0x7D10
1、接下来执行HookPort_InitProxyAddress函数填充该结构,感兴趣的自己单独写一个代理函数,不感兴趣的直接通用模板处理即可。
2、注意我们要过滤掉以下几个函数,因为他们由HookPort实现Fake函数
5、KiFastCallEntry处理
1、通过修改SSDT表的ZwSetEvent来触发安装钩子
设置一个虚假的ZwSetEvent句柄来触发,注意保存原始函数地址后面要恢复
5、1 Fake_ZwSetEvent处理部分
1、判断Hook方式,默认Global_IdtHook_Or_InlineHook置1,并且恢复SSDT钩子(ZwSetEvent)
修改:Jmpxxx Global_IdtHook_Or_InlineHook == 0
修改:Int 4 Global_IdtHook_Or_InlineHook == 1
2、sub_1567A实在没看懂,有明白的老哥告诉下
3、栈回溯获取返回地址[EBP+4](这里指的是正常调用时返回到KiFastCallEntry中的地址),找到hook点
4、多核Hook方法
假设是单核直接替换即可
假设是多核就采用DPC方式处理即可
暂停N一段时间处理hook
6、创建进程、线程、模块回调和IAT方式挂钩KeUserModeCallback
ClientLoadLibrary加载模块相关
ClientImmLoadLayout加载模块相关
fnHkOPTINLPEVENTMSG未知
fnHkINLPKBDLLHOOKSTRUCT拦截键盘消息