比如有个未知进程A,运行起来被驱动拦截到,收集进程信息,把数据放到OprList里,同时也放到WaitList里,这两个表一一对应,
OperList节点会发到应用层供其弹窗,驱动层等待用户层返回到数据,WaitList存放应用层操作的结果阻塞在。
应用层会开启一个弹窗线程异步读OperList中的数据,然后DeviceIoControl通知驱动,然后驱动在WaitList找到节点进行应用层的操作。
应用层读请求可能OperList是空的,这时Irp会挂起,放在PendingIrpList。一旦驱动发现有新进程,就会先给PendingIrpList。
因为有可能多次攻击,所以给每次进程事件分配一个ID。
整体思路
1.驱动将捕获的操作放入一个等待链表中(包含一个标识ID,一个event,一个等待结果的域),同时将操作内容放入一个内容链表中,并完成来自应用层的读Pendingirp列表
2.等待事件。
应用层开启一个独立线程读出(overLapped)驱动链表中的内容。如果没有内容,则挂起到pending irp中
驱动提供自己控制设备的读请求,将链表中的内容提供给应用层读操作
应用层拿到操作内容,弹窗
应用层通过IOCTL下发用户指定结果。
在驱动IOCTL分发函数中,将用户结果放入链表中同ID的结构中
驱动IOCTL分发函数中设置EVENT(事件多个没名字,因为多个攻击并且都是在内核层操作)
驱动拿到用户操作结果
摘掉链表中的操作,获取操作结果
驱动根据操作结果判断允许或者阻止
下发结果
多个弹窗防止内核层超时,可以开多线程,或者可以获取攻击数据函数处加锁。这样多弹窗就是串行了。
内核层可以调用ExRaiseHardError()弹窗
代码片段
前面蓝屏,BAD_POOL_HEADER (19),分析dump文件发现
Unable to get MmSystemRangeStart
Unable to get NonPagedPoolStart
Unable to get PagedPoolStart
后发现ExZreoMemory传错长度导致
//处理应用层的read()函数,就是引用层读取攻击进程数据
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;
if (lpIrpStack->Parameters.Read.Length < sizeof(RING3_OP_INFO))
{
ntStatus = STATUS_INVALID_PARAMETER;
ulLength = 0;
goto Completed;
}
LockWrite(&g_OperListLock);
if (IsListEmpty(&g_OperList) == TRUE)
{
UnLockWrite(&g_OperListLock);
LockWrite(&g_PendingIrpListLock);
PendingIrpToList(lpIrp, &g_PendingIrpList, CommonIrpCancel);//设置取消例程的原因是应用层最后可能要退出,pendingirp要取消掉
UnLockWrite(&g_PendingIrpListLock);
goto Pended;
}
lpOpInfoList = g_OperList.Flink;
lpOpInfoEntry = CONTAINING_RECORD(lpOpInfoList, OP_INFO, m_List);
RemoveEntryList(lpOpInfoList);
UnLockWrite(&g_OperListLock);
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_RESULT __stdcall GetResultFromUser()//负责把攻击数据放到operList里
{
R3_RESULT NotifyResult = R3Result_Pass;
BOOLEAN bSuccess = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
LARGE_INTEGER WaitTimeOut = {0};
OP_INFO *lpNewOpInfo = NULL;
WAIT_LIST_ENTRY *lpNewWaitEntry = NULL;
ULONG_PTR ulPtr = 0;
UNICODE_STRING uFullPath = { 0 };
uFullPath.MaximumLength = MAX_PATH* sizeof(WCHAR);
uFullPath.Buffer=ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), 'ELIF');
RtlZeroMemory(uFullPath.Buffer, MAX_PATH * sizeof(WCHAR));
lpNewOpInfo = (OP_INFO*)ExAllocatePool(PagedPool, sizeof(OP_INFO));
RtlZeroMemory(lpNewOpInfo, sizeof(OP_INFO));
if (lpNewOpInfo == NULL)
{
return NotifyResult;
}
//设置事件相关的数据,发送给R3,比如进程ID,名字,路径,以及具体操作(创建,修改,删除)等等
//当然,这里,我们只是简单的捕捉了进程的ID或者名字等
ulPtr = (ULONG_PTR)PsGetCurrentProcessId();//获得攻击者进程PID
//拿全路径
GetProcessFullNameByPid((HANDLE)ulPtr, &uFullPath);
DbgPrint("uFullPath is %wZ\n", uFullPath.Buffer);
RtlCopyMemory(lpNewOpInfo->m_ProcessName, uFullPath.Buffer, uFullPath.Length);
ExFreePool(uFullPath.Buffer);
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秒,环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;
}
End:
if (lpNewWaitEntry != NULL)
{
ExFreePool(lpNewWaitEntry);
}
if (lpNewOpInfo != NULL)
{
ExFreePool(lpNewOpInfo);
}
return NotifyResult;
}
//通过DeviceIoControl(),R3向内核传递弹窗结果,将对应的WaitID事件设置用户选择结果以及//攻击拦截模仿
NTSTATUS DispatchControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION lpIrpStack = NULL;
PVOID inputBuffer = NULL;
PVOID outputBuffer = NULL;
ULONG inputBufferLength = 0;
ULONG outputBufferLength = 0;
ULONG ioControlCode = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
//获取当前IRP堆栈位置
lpIrpStack = IoGetCurrentIrpStackLocation (Irp);
//获得输入缓冲和长度
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = lpIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//获得输出缓冲和长度
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = lpIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
//获取控制码
ioControlCode = lpIrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode )
{
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;
case IOCTL_XXX_ATTACK://攻击拦截模仿
{
R3_RESULT notifyResult = R3Result_DefaultNon;
notifyResult = GetResultFromUser();//从R3获得弹框结果,是阻止还是放过
if (notifyResult == R3Result_Block)
{
DbgPrint("阻止\n");
*(ULONG *)outputBuffer = 0;
ntStatus = STATUS_SUCCESS;
}
else if (notifyResult == R3Result_Pass)
{
DbgPrint("允许\n");
*(ULONG *)outputBuffer = 1;
ntStatus = STATUS_SUCCESS;
}
else
{
DbgPrint("超时允许\n");
*(ULONG *)outputBuffer = 1;
ntStatus = STATUS_SUCCESS;
}
}
Irp->IoStatus.Information = sizeof(ULONG);
Irp->IoStatus.Status = ntStatus;
break;
default:
break;
}
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return ntStatus;
}
应用层就是开线程弹窗,然后提供选项拦截或放行。