HIPS思路:
1.驱动将捕获的操作放入一个等待链表中(包括一个标识ID,一个event,一个等待结果的域);同时将操作内容放入一个内容链表中,并完成来自应用层的读pendingirp列表
2.wait for event
1.应用层开一个独立线程负责读出(overlapped)驱动中链表中的内容.如果此时没有内容可读,则pending入pending irp列表中
2.驱动提供自己控制设备的读请求,将链表中的内容提供给应用层的读操作
3.应用层拿到操作内容,弹窗
4.应用层通过IOCTL下发用户指定的结果
1.在驱动IOCTL分发函数中,将用户结果放入链表中同ID的结构中
2.驱动IOCTL分发函数中设置EVENT
3.驱动拿到用户操作结果
4.摘掉链表中的操作,并获取操作中的结果
5.驱动根据操作结果判断是否允许或者阻止
6.下发结果
如果有可用IRP就直接将这次行为给这个IRP(这时R3一定已经在等待了 看后面就会懂)
代码:
//设置事件相关的数据,发送给R3,比如进程ID,名字,路径,以及具体操作(创建,修改,删除)等等
//当然,这里,我们只是简单的捕捉了进程的ID或者名字等
ulPtr = (ULONG_PTR)PsGetCurrentProcessId();
lpNewOpInfo->m_ulProcessID = (ULONG_PTR)ulPtr;
lpNewOpInfo->m_ulWaitID = MakeWaitID();//区别不同事件的ID
lpNewWaitEntry = (WAIT_LIST_ENTRY*)ExAllocatePool(NonPagedPool, sizeof(WAIT_LIST_ENTRY));
if (lpNewWaitEntry == NULL)
{
goto End;
}
lpNewWaitEntry->m_ulWaitID = lpNewOpInfo->m_ulWaitID;
KeInitializeEvent(&lpNewWaitEntry->m_ulWaitEvent, SynchronizationEvent, FALSE);
// 插入等待队列,等待R3下发结果
LockWrite(&g_WaitListLock);
InsertTailList(&g_WaitList, &lpNewWaitEntry->m_List);
UnLockWrite(&g_WaitListLock);
LockWrite(&g_PendingIrpListLock);
bSuccess = CompletePendingIrp(&g_PendingIrpList, lpNewOpInfo);//查看是否有未完成的pendingIRP,直接将该OperInfo传给R3
UnLockWrite(&g_PendingIrpListLock);
if (bSuccess == FALSE) //完成pending irp失败,将lpNewOpInfo插入operlist
{
LockWrite(&g_OperListLock);
InsertTailList(&g_OperList, &lpNewOpInfo->m_List); //插入OperList,等待R3来读取
UnLockWrite(&g_OperListLock);
lpNewOpInfo = NULL;
}
注意下:这是如果是看有没有未完成的IRP 有就直接给它了 没有就将检测到的攻击行为插入记录事件链表
CompletePendingIrp:
BOOLEAN
CompletePendingIrp(LIST_ENTRY* pIrpListHead, OP_INFO* pOpInfo)
{
LIST_ENTRY *lpIrpList = NULL;
PIRP lpIrp = NULL;
BOOLEAN bFound = FALSE;
BOOLEAN bReturn = FALSE;
if (IsListEmpty(pIrpListHead) == TRUE)
{
return bReturn;
}
for (lpIrpList = pIrpListHead->Flink; lpIrpList != pIrpListHead; lpIrpList = lpIrpList->Flink)
{
lpIrp = CONTAINING_RECORD(lpIrpList, IRP, Tail.Overlay.ListEntry);
if (IoSetCancelRoutine(lpIrp, NULL))
{
RemoveEntryList(lpIrpList);
bFound = TRUE;
break;
}
}
if (bFound == FALSE)
{
return bReturn;
}
RtlCopyMemory(lpIrp->AssociatedIrp.SystemBuffer, pOpInfo, sizeof(RING3_OP_INFO));
lpIrp->IoStatus.Information = sizeof(RING3_OP_INFO);
lpIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(lpIrp, IO_NO_INCREMENT);
bReturn = TRUE;
return bReturn;
}
读分发函数:
NTSTATUS DispatchRead (
IN PDEVICE_OBJECT pDevObj,
IN PIRP lpIrp)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
ULONG ulLength = 0;
PIO_STACK_LOCATION lpIrpStack = IoGetCurrentIrpStackLocation(lpIrp);
OP_INFO *lpOpInfoEntry = NULL;
LIST_ENTRY *lpOpInfoList = NULL;
//如果读的len小于这个结构 就返回无效的
if (lpIrpStack->Parameters.Read.Length < sizeof(RING3_OP_INFO))
{
ntStatus = STATUS_INVALID_PARAMETER;
ulLength = 0;
goto Completed;
}
//先锁定Write
LockWrite(&g_OperListLock);
//如果是空的链表 将这次的IRP放入Pending链表 就是如果没有消息 就将这次的请求保存起来 等有了在给它
//当这个IRP被处理时调用CommonIrpCancel这个函数 通知R3这个IRP被处理了
if (IsListEmpty(&g_OperList) == TRUE)
{
UnLockWrite(&g_OperListLock);
LockWrite(&g_PendingIrpListLock);
PendingIrpToList(lpIrp, &g_PendingIrpList, CommonIrpCancel);
UnLockWrite(&g_PendingIrpListLock);
goto Pended;
}
//处理第一个节
lpOpInfoList = g_OperList.Flink;
//获得Entry头
lpOpInfoEntry = CONTAINING_RECORD(lpOpInfoList, OP_INFO, m_List);
//移除这个节点
RemoveEntryList(lpOpInfoList);
UnLockWrite(&g_OperListLock);
//将链表的信息窗给R3
RtlCopyMemory(lpIrp->AssociatedIrp.SystemBuffer, lpOpInfoEntry, sizeof(RING3_OP_INFO));
ntStatus = STATUS_SUCCESS;
ulLength = sizeof(RING3_OP_INFO);
ExFreePool(lpOpInfoEntry);
Completed:
lpIrp->IoStatus.Status = ntStatus;
lpIrp->IoStatus.Information = ulLength;
IoCompleteRequest(lpIrp, IO_NO_INCREMENT);
return ntStatus;
Pended:
return STATUS_PENDING;
}
有两种情况 一种是有攻击行为 这个链表有值 这时就很好办 下面是第一种的步骤
应用层使用异步读请求--如果驱动层这时有攻击事件(记录攻击事件的链表不为空)就直接获得信息见上面的读分发函数
并将这个事件插入等待队列 等待R3的结果
返回给R3的信息包括一个进程ID 进程名称 一个标记(用于区别不同事件)
结构如下:
typedef struct _OP_INFO
{
WCHAR m_ProcessName[MAX_PATH];
DWORD m_ulProcessID;
ULONG m_ulWaitID;
LIST_ENTRY m_List;
} OP_INFO, *POP_INFO;
但给R3的只给前三个数据 不需要拷贝 直接对上面的数据大小赋值sizeof(_OP_INFO_R3)
typedef struct _OP_INFO_R3
{
WCHAR m_ProcessName[MAX_PATH];
DWORD m_ulProcessID;
ULONG m_ulWaitID;
}
驱动里面将这个等待事件插入等待队列 等待队列链表中结构如下
typedef struct _WAIT_LIST_ENTRY
{
LIST_ENTRY m_List;
ULONG m_ulWaitID;
KEVENT m_ulWaitEvent;
ULONG m_bBlocked;
}WAIT_LIST_ENTRY;
注意:在插入之前要为其中的的事件和等待ID(就是上面的标记)赋值 m_bBlocked留空 等R3处理后再来赋值
代码:
lpNewOpInfo = (OP_INFO*)ExAllocatePool(PagedPool, sizeof(OP_INFO));
if (lpNewOpInfo == NULL)
{
return NotifyResult;
}
//设置事件相关的数据,发送给R3,比如进程ID,名字,路径,以及具体操作(创建,修改,删除)等等
//当然,这里,我们只是简单的捕捉了进程的ID或者名字等
ulPtr = (ULONG_PTR)PsGetCurrentProcessId();
lpNewOpInfo->m_ulProcessID = (ULONG_PTR)ulPtr;
lpNewOpInfo->m_ulWaitID = MakeWaitID();//区别不同事件的ID
lpNewWaitEntry = (WAIT_LIST_ENTRY*)ExAllocatePool(NonPagedPool, sizeof(WAIT_LIST_ENTRY));
if (lpNewWaitEntry == NULL)
{
goto End;
}
lpNewWaitEntry->m_ulWaitID = lpNewOpInfo->m_ulWaitID;
KeInitializeEvent(&lpNewWaitEntry->m_ulWaitEvent, SynchronizationEvent, FALSE);
// 插入等待队列,等待R3下发结果
LockWrite(&g_WaitListLock);
InsertTailList(&g_WaitList, &lpNewWaitEntry->m_List);
UnLockWrite(&g_WaitListLock);
LockWrite(&g_PendingIrpListLock);
bSuccess = CompletePendingIrp(&g_PendingIrpList, lpNewOpInfo);//查看是否有未完成的pendingIRP,直接将该OperInfo传给R3
UnLockWrite(&g_PendingIrpListLock);
if (bSuccess == FALSE) //完成pending irp失败,将lpNewOpInfo插入operlist
{
LockWrite(&g_OperListLock);
InsertTailList(&g_OperList, &lpNewOpInfo->m_List); //插入OperList,等待R3来读取
UnLockWrite(&g_OperListLock);
lpNewOpInfo = NULL;
}
//并在这里等待40秒(R3等待30秒)
这40秒钟R3必定为发送一次读下来 读到信息后决定是否放行
R3返回结构 结构如下:
typedef struct _R3_REPLY
{
ULONG m_ulWaitID;
ULONG m_ulBlocked;
}R3_REPLY;
m_ulWaitID 为读到的ID m_ulBlocked为是否放行
填好后使用DeviceIoControl将数据发送下去
驱动收到后 根据ID遍历等待链表并返回链表头 然后设置链表中的放行状态
case IOCTL_SEND_RESULT_TO_R0://R3向内核传递弹窗结果,将对应的WaitID事件设置用户选择结果
{
RING3_REPLY *lpReply = NULL;
WAIT_LIST_ENTRY *lpWaitEntry = NULL;
if (lpIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(RING3_REPLY))
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
lpReply = (RING3_REPLY*)Irp->AssociatedIrp.SystemBuffer;
LockWrite(&g_WaitListLock);
lpWaitEntry = FindWaitEntryByID(&g_WaitList, lpReply->m_ulWaitID);//根据WaitID,找到对应的拦截事件
if (lpWaitEntry != NULL)
{
lpWaitEntry->m_bBlocked = lpReply->m_ulBlocked;
KeSetEvent(&lpWaitEntry->m_ulWaitEvent, 0, FALSE);//设置EVENT事件,唤醒GetResultFromUser()里的等待事件
}
UnLockWrite(&g_WaitListLock);
Irp->IoStatus.Information = 0;
ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS;
}
break;
设置EVENT事件后,唤醒前面等待的线程
线程被激活后 首先将这个事件移除链表 然后对数据监测 进行放行/阻止工作 并要释放先前为链表申请的内存
// 等40秒,环3是30秒超时
WaitTimeOut.QuadPart = -40 * 10000000;
Status = KeWaitForSingleObject(&lpNewWaitEntry->m_ulWaitEvent,
Executive, KernelMode, FALSE, &WaitTimeOut);//等待R3下发允许或阻止操作
LockWrite(&g_WaitListLock);
RemoveEntryList(&lpNewWaitEntry->m_List);
UnLockWrite(&g_WaitListLock);
if (Status != STATUS_TIMEOUT)
{
if (lpNewWaitEntry->m_bBlocked == TRUE)
{
NotifyResult = R3Result_Block;
}
else
{
NotifyResult = R3Result_Pass;
}
}
else
{
NotifyResult = R3Result_DefaultNon;
}
在回调中判断 就可以了