HIPS实现

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.下发结果


R0将检测到的攻击行为放到一个链表中(如果没有可用IRP) 应用层发送读请求就到这个链表中取
如果有可用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;
    }




在回调中判断 就可以了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值