首页
社区
课程
招聘
[原创]拦截,弹窗
2023-2-7 08:23 6203

[原创]拦截,弹窗

2023-2-7 08:23
6203

本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如有错漏,欢迎留言交流指正

拦截,弹窗

  • 应用场景:假设系统中了勒索病毒,开始加密整个磁盘,这时候内核监控到并拦截(Hook,驱动过滤,回调)了这些操作,进行数据收集弹窗给用户等待处理。

    弹窗

    整体思路

  1. 驱动截获攻击操作,进行数据收集
  • 驱动为了拿到应用处理完弹窗的返回的结果,建立一个WaitList(等待链表,结点包括一个标识ID(用来区分攻击事件),一个event,一个等待结果的域),通过WaitList上等待应用层处理弹窗的结果。
  • 驱动为了把收集数据传给应用层,建立一个OperList(操作链表,进程Pid,进程全路径,目标对象等), 建立一个PendingIrpList来应对OperList为空情况。把收集数据存为一个结构体放在OperList,传给应用。或者优先满足PendingIrpList的Irp。
    • 如果此时内容可读(链表不为空),则该独立线程向驱动发出一个读Irp来读取OperList一个结点的数据,弹出一个弹窗提示用户(比如某某进程正在往system32释放DLL文件)并倒计时等待用户操作,对弹窗处理的结果通过DeviceIoControl返回给驱动,把结果存放在WaitList里面,结点上的事件就会通知驱动来拿数据,驱动从WaitList拿到结果做出相应操作(阻止/执行某个进程的操作)
    • 如果此时没有内容可读(链表为空,系统比较健康,攻击事件比较少),则该独立线程向驱动发出的读Irp会被挂起,放在PendingIrpList中,当下次驱动截获到攻击操作的时候,不会把收集数据存为一个结构体放在OperList,而是会直接把收集的数据直接给PendingIrpList的Irp(此时PendingIrpList不为空,优先满足挂起的Irp请求),弹出一个弹窗提示用户(比如某某进程正在往system32释放DLL文件)并倒计时等待用户操作,对弹窗处理的结果通过DeviceIoControl返回给驱动,把结果存放在WaitList里面,结点上的事件就会通知驱动来拿数据,驱动从WaitList拿到结果做出相应操作(阻止/运行某个进程的操作)
  • 驱动拿到用户操作结果做出相应的动作。
  1. 应用层开一个独立线程去异步读出(overlapped)驱动中OperList中的内容

    实战

    驱动

  • 流程:
    • 1.驱动截获到攻击(在驱动IOCTL分发函数中(IOCTL_XXX_ATTACK)),生成一个WaitList结点插入等待队列,等待R3下发结果,收集信息放到一个结构体中,查看是否有未完成的pendingIRP,如果有,直接将该OperInfo传给R3;否则插入OperList,等待R3来读取
    • 2.应用层通过DipatchRead()读取R0的数据,如果为空,就把当前读Irp放到PendingIrpList中去,并注册一个CommonIrpCancel函数;不为空,直接读取数据
    • 3.在驱动IOCTL分发函数中(IOCTL_SEND_RESULT_TO_R0),将用户结果放入链表WaitList中同WaitID的结构体中,设置EVENT事件,唤醒内核层GetResultFromUser()里的等待事件
    • 4.驱动拿到用户操作结果,摘掉链表中的结点,并获取操作中的结果
    • 5.驱动根据操作结果判断是否允许或者阻止,下发结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
#include <ntddk.h>
#include "Ioctlcmd.h"
#include "main.h"
 
#define        DEVICE_NAME        L"\\device\\PopupDrv"
#define        LINK_NAME        L"\\dosDevices\\PopupDrv"
 
LIST_ENTRY g_OperList; ///< 操作链表
ERESOURCE  g_OperListLock; ///< 保护全局资源的读写锁共享锁,允许多人读,一个人写
 
LIST_ENTRY g_WaitList; ///< 等待链表
ERESOURCE  g_WaitListLock; ///< 保护全局资源的读写锁共享锁,允许多人读,一个人写
 
LIST_ENTRY g_PendingIrpList; ///< Irp挂起链表
ERESOURCE  g_PendingIrpListLock; ///< 保护全局资源的读写锁共享锁,允许多人读,一个人写
 
ERESOURCE  g_GetResultFromUserLock; ///< 保护全局资源的读写锁共享锁,允许多人读,一个人写
 
ULONG g_ulCurrentWaitID = 0; ///< Student 当前攻击事件的ID
 
/// __stdcall表示 1.参数从右向左压入堆栈 2.函数被调用者负责栈平衡
/// 拿写锁
VOID __stdcall LockWrite(ERESOURCE* lpLock)
{
    KeEnterCriticalRegion();
    ExAcquireResourceExclusiveLite(lpLock, TRUE);
}
 
/// 释放写锁
VOID __stdcall UnLockWrite(ERESOURCE* lpLock)
{
    ExReleaseResourceLite(lpLock);
    KeLeaveCriticalRegion();
}
 
/// 拿读锁
VOID __stdcall LockRead(ERESOURCE* lpLock)
{
    KeEnterCriticalRegion();
    ExAcquireResourceSharedLite(lpLock, TRUE);
}
 
/// 以让等待读优先的方式去拿写锁,StarveWriter,使写进程饥饿
VOID __stdcall LockReadStarveWriter(ERESOURCE* lpLock)
{
    KeEnterCriticalRegion();
    ExAcquireSharedStarveExclusive(lpLock, TRUE);
}
 
/// 释放读锁
VOID __stdcall UnLockRead(ERESOURCE* lpLock)
{
    ExReleaseResourceLite(lpLock);
    KeLeaveCriticalRegion();
}
 
/// 初始化锁
VOID __stdcall InitLock(ERESOURCE* lpLock)
{
    ExInitializeResourceLite(lpLock);
}
 
/// 删除锁
VOID __stdcall DeleteLock(ERESOURCE* lpLock)
{
    ExDeleteResourceLite(lpLock);
}
 
/// 初始化链表
VOID __stdcall InitList(LIST_ENTRY* list)
{
    InitializeListHead(list);
}
 
/// 用来取消Irp的例程
VOID CommonIrpCancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp
)
{
    KIRQL CancelOldIrql = Irp->CancelIrql;
 
    IoReleaseCancelSpinLock(DISPATCH_LEVEL);
    KeLowerIrql(CancelOldIrql);
 
    LockWrite(&g_PendingIrpListLock);
    RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
    UnLockWrite(&g_PendingIrpListLock);
 
    Irp->IoStatus.Status = STATUS_CANCELLED; //把Irp的执行状态设置成STATUS_CANCELLED
    Irp->IoStatus.Information = 0;
 
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
 
/// 将Irp挂起,并为Irp注册一个CommonIrpCancel
VOID PendingIrpToList(PIRP pIrp, PLIST_ENTRY pIrpList, PDRIVER_CANCEL pfnCancelRoutine)
{
    InsertTailList(pIrpList, &pIrp->Tail.Overlay.ListEntry); //把Irp插入到PendingIrpToList
    IoMarkIrpPending(pIrp); //把Irp标记成pending状态
    /// //为Irp设置一个CancelRoutine回调函数,R3调用CancelIo(handle)会触发CancelRoutine例程(假设Irp一直没有得到满足,直到客户端退出或者系统关机了还没有拿到数据,这时候只能把Irp取消掉,不然系统可能会蓝屏)
    //CancelIo from r3  or IoCancelIrp to call
    IoSetCancelRoutine(pIrp, pfnCancelRoutine);
}
 
//处理应用层的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);
    /// 如果为空,就把当前读Irp放到PendingIrpList中去,并注册一个CommonIrpCancel函数,
    if (IsListEmpty(&g_OperList) == TRUE)
    {
        UnLockWrite(&g_OperListLock);
 
        LockWrite(&g_PendingIrpListLock);
        PendingIrpToList(lpIrp, &g_PendingIrpList, CommonIrpCancel); //将Irp挂起,并为Irp注册一个CommonIrpCancel
        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;
}
 
WAIT_LIST_ENTRY*
FindWaitEntryByID(PLIST_ENTRY pListHead, ULONG ulWaitID)
{
    PLIST_ENTRY    pList = NULL;
    WAIT_LIST_ENTRY* pEntry = NULL;
 
    for (pList = pListHead->Flink; pList != pListHead; pList = pList->Flink)
    {
        pEntry = CONTAINING_RECORD(pList, WAIT_LIST_ENTRY, m_List);
        if (pEntry->m_ulWaitID == ulWaitID)
        {
            return pEntry;
        }
    }
    return NULL;
}
 
/// @brief       MakeWaitID 用来创建一个WaitID,用来区分攻击事件;
/// @return      ULONG 返回WaitID,4个字节
ULONG MakeWaitID()
{
    InterlockedIncrement(&g_ulCurrentWaitID); //原子操作,对全局变量+1
    return g_ulCurrentWaitID;
}
 
BOOLEAN
CompletePendingIrp(LIST_ENTRY* pIrpListHead, OP_INFO* pOpInfo)
{
    LIST_ENTRY* lpIrpList = NULL;
    PIRP                lpIrp = NULL;
    BOOLEAN                bFound = FALSE;
    BOOLEAN                bReturn = FALSE;
 
    /// 如果PendingIrpList为空,即当前没有读Irp在等待
    if (IsListEmpty(pIrpListHead) == TRUE)
    {
        return bReturn;
    }
    /// 找到Irp
    for (lpIrpList = pIrpListHead->Flink; lpIrpList != pIrpListHead; lpIrpList = lpIrpList->Flink)
    {
        lpIrp = CONTAINING_RECORD(lpIrpList, IRP, Tail.Overlay.ListEntry); //拿到一个结点
        if (IoSetCancelRoutine(lpIrp, NULL)) //把当前Irp设置成NULL并且returns the previous value of Irp->CancelRoutine(在Irp被取消的时候被调用(程序退出,系统重启等),如果注册了CancelRoutine则是之前被挂起Irp). no Cancel routine, or cancellation in progress, returns NULL.
        {
            RemoveEntryList(lpIrpList);
            bFound = TRUE;
            break;
        }
    }
 
    if (bFound == FALSE)
    {
        return bReturn;
    }
 
    /// 把OperList结点的数据copy到pIrp->AssociatedIrp.SystemBuffer,进程就可以拿到数据了
    RtlCopyMemory(lpIrp->AssociatedIrp.SystemBuffer, pOpInfo, sizeof(RING3_OP_INFO)); //只拷贝进程全路径,进程PID,攻击事件ID
 
    lpIrp->IoStatus.Information = sizeof(RING3_OP_INFO);
    lpIrp->IoStatus.Status = STATUS_SUCCESS;
 
    IoCompleteRequest(lpIrp, IO_NO_INCREMENT);
    bReturn = TRUE;
 
    return bReturn;
}
 
R3_RESULT __stdcall GetResultFromUser()
{
    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;
    LockWrite(&g_GetResultFromUserLock);
    /// 生成一个结点,是个OP_INFO结构体,存放进程的名字,进程的PID,攻击事件的ID,前面三个数据放在LIST_ENTRY之前是为了方便拷贝数据
    lpNewOpInfo = (OP_INFO*)ExAllocatePool(PagedPool, sizeof(OP_INFO));
    /// memset(lpNewOpInfo, 0, sizeof(OP_INFO));
    if (lpNewOpInfo == NULL)
    {
        UnLockWrite(&g_GetResultFromUserLock);
        return NotifyResult;
    }
 
    /// 设置事件相关的数据,发送给R3,比如进程ID,名字,路径,以及具体操作(创建,修改,删除)等等
    /// 这里只是简单的捕捉了进程的ID或者名字
    ulPtr = (ULONG_PTR)PsGetCurrentProcessId();
    lpNewOpInfo->m_ulProcessID = (ULONG_PTR)ulPtr;
 
    /// @todo 通过进程pid拿到进程路径,需要完善
 
    lpNewOpInfo->m_ulWaitID = MakeWaitID(); //生成WatitID,用来区别不同事件的ID
    /// 生成Wait结点,WaitID,WaitEvent(用来同步,当应用层把弹窗结果放在Blocked之后,设置成有信号状态,驱动就通过这个信号,知道应用层已经发结果下来了,就去WaitList上拿结果)
    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失败(当前没有读Irp在等待),将lpNewOpInfo插入Operlist
    {
        LockWrite(&g_OperListLock);
        InsertTailList(&g_OperList, &lpNewOpInfo->m_List); //插入OperList,等待R3来读取
        UnLockWrite(&g_OperListLock);
 
        lpNewOpInfo = NULL;
    }
 
    // 40秒,环330秒超时
    WaitTimeOut.QuadPart = -40 * 10000000;
    Status = KeWaitForSingleObject(&lpNewWaitEntry->m_ulWaitEvent,
        Executive, KernelMode, FALSE, &WaitTimeOut);//等待R3下发允许或阻止操作
 
    LockWrite(&g_WaitListLock);
    RemoveEntryList(&lpNewWaitEntry->m_List); //把当前WaitList结点摘除
    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);
    }
    UnLockWrite(&g_GetResultFromUserLock);
    return NotifyResult;
}
 
//处理应用层的DeviceIoControl()
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;
        }
        /// RING3_REPLY结构体包含WaitID和Blocked这两个成员
        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; //将对应的WaitList结点设置成用户选择结果
            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;
 
        //LockWrite(&g_GetResultFromUserLock); //最好是在函数内部。除非你在任何调用这个函数的地方都加锁
        //KeEnterCriticalRegion(); //改成串行
        notifyResult = GetResultFromUser();//这里最长会等待40s,收集数据封装成一个结构体结点,放在OperList或者满足PendingIrpList中等待的Irp,从R3获得弹框结果,是阻止还是放过
        //KeLeaveCriticalRegion();
        //UnLockWrite(&g_GetResultFromUserLock);
 
        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;
}
 
//驱动Unload()函数
VOID DriverUnload(
    IN PDRIVER_OBJECT    pDriverObject)
{
    UNICODE_STRING         deviceLink = { 0 };
 
    RtlInitUnicodeString(&deviceLink, LINK_NAME);
    IoDeleteSymbolicLink(&deviceLink);
    IoDeleteDevice(pDriverObject->DeviceObject);
    DeleteLock(&g_GetResultFromUserLock);
    DeleteLock(&g_OperListLock);
    DeleteLock(&g_WaitListLock);
    DeleteLock(&g_PendingIrpListLock);
    return;
}
 
//处理应用层的create()函数
NTSTATUS DispatchCreate(
    IN PDEVICE_OBJECT    pDevObj,
    IN PIRP    pIrp)
{
    //设置IO状态信息
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    //完成IRP操作,不向下层驱动发送
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}
 
//处理应用层的close()函数
NTSTATUS DispatchClose(
    IN PDEVICE_OBJECT    pDevObj,
    IN PIRP    pIrp)
{
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}
 
//驱动程序入口,完成各种初始化工作,创建设备对象
NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath)
{
    NTSTATUS         status = STATUS_SUCCESS;
    PDEVICE_OBJECT     pDevObj = NULL;
    UNICODE_STRING     uDevName = { 0 };
    UNICODE_STRING     uLinkName = { 0 };
    DbgPrint("Driver Load begin!\n");
 
    /// 初始化锁
    InitLock(&g_OperListLock);
    InitLock(&g_WaitListLock);
    InitLock(&g_PendingIrpListLock);
    InitLock(&g_GetResultFromUserLock);
 
    /// 初始化链表
    InitList(&g_OperList);
    InitList(&g_WaitList);
    InitList(&g_PendingIrpList);
 
    //初始化各个例程
 
    pDriverObject->MajorFunction[IRP_MJ_CREATE] =
        DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
        DispatchClose;
    pDriverObject->MajorFunction[IRP_MJ_READ] =
        DispatchRead;//read operlist data
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
        DispatchControl;//get r3 result&attack
    pDriverObject->DriverUnload =
        DriverUnload;
 
    RtlInitUnicodeString(&uDevName, DEVICE_NAME);
    //创建驱动设备
    status = IoCreateDevice(pDriverObject,
        0,//sizeof(DEVICE_EXTENSION)
        &uDevName,
        FILE_DEVICE_UNKNOWN,
        0, FALSE,
        &pDevObj);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("IoCreateDevice Failed:%x\n", status);
        return status;
    }
 
    pDevObj->Flags |= DO_BUFFERED_IO;
    RtlInitUnicodeString(&uLinkName, LINK_NAME);
    //创建符号链接
    status = IoCreateSymbolicLink(&uLinkName, &uDevName);
    if (!NT_SUCCESS(status))
    {
        //STATUS_INSUFFICIENT_RESOURCES     资源不足
        //STATUS_OBJECT_NAME_EXISTS         指定对象名存在
        //STATUS_OBJECT_NAME_COLLISION         对象名有冲突
        DbgPrint("IoCreateSymbolicLink Failed:%x\n", status);
        IoDeleteDevice(pDevObj);
        return status;
    }
    DbgPrint("Driver Load success!\n");
    return status;
}

应用

  • 流程
    • 1.加载驱动
    • 2.打开设备
    • 3.新建一个读线程,如果有数据读出来弹窗,如果没有数据可读则等待
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
// PopupClientDlg.cpp : implementation file
//
 
#include "stdafx.h"
#include "PopupClient.h"
#include "PopupClientDlg.h"
 
#include "ioctlcmd.h"
#include <shlwapi.h>
#include "PopupDlg.h"
 
#pragma comment(lib, "shlwapi.lib")
 
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
 
#define DRIVER_NAME _T("PopupDrv")
#define DRIVER_PATH _T(".\\PopupDrv.sys")
 
HANDLE gh_Device = INVALID_HANDLE_VALUE;
 
CWinThread *g_hReadThread = NULL;
BOOL g_bToExitThread = FALSE;
HANDLE g_hOverlappedEvent = NULL;
 
BOOL LoadDriver(TCHAR *lpszDriverName, TCHAR *lpszDriverPath)
{
    TCHAR szDriverImagePath[256] = {0};
    //得到完整的驱动路径
    GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);
 
    BOOL bRet = FALSE;
 
    SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
    SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
 
    //打开服务控制管理器
    hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 
    if (hServiceMgr == NULL)
    {
        // OpenSCManager失败
        // printf( "OpenSCManager() Failed %d ! \n", GetLastError() );
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        ////OpenSCManager成功
        printf("OpenSCManager() ok ! \n");
    }
 
    //创建驱动所对应的服务
    hServiceDDK = CreateService(hServiceMgr,
                                lpszDriverName,           //驱动程序的在注册表中的名字
                                lpszDriverName,           // 注册表驱动程序的 DisplayName 值
                                SERVICE_ALL_ACCESS,       // 加载驱动程序的访问权限
                                SERVICE_KERNEL_DRIVER, // 表示加载的服务是驱动程序
                                SERVICE_DEMAND_START,  // 注册表驱动程序的 Start 值
                                SERVICE_ERROR_IGNORE,  // 注册表驱动程序的 ErrorControl 值
                                szDriverImagePath,       // 注册表驱动程序的 ImagePath 值
                                NULL,                   // GroupOrder HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList
                                NULL,
                                NULL,
                                NULL,
                                NULL);
 
    DWORD dwRtn;
    //判断服务是否失败
    if (hServiceDDK == NULL)
    {
        dwRtn = GetLastError();
        if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
        {
            //由于其他原因创建服务失败
            // printf( "CrateService() Failed %d ! \n", dwRtn );
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            //服务创建失败,是由于服务已经创立过
            printf("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n");
        }
 
        // 驱动程序已经加载,只需要打开
        hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
        if (hServiceDDK == NULL)
        {
            //如果打开服务也失败,则意味错误
            dwRtn = GetLastError();
            // printf( "OpenService() Failed %d ! \n", dwRtn );
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            // printf( "OpenService() ok ! \n" );
        }
    }
    else
    {
        // printf( "CrateService() ok ! \n" );
    }
 
    //开启此项服务
    bRet = StartService(hServiceDDK, NULL, NULL);
    if (!bRet)
    {
        DWORD dwRtn = GetLastError();
        if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
        {
            // printf( "StartService() Failed %d ! \n", dwRtn );
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            if (dwRtn == ERROR_IO_PENDING)
            {
                //设备被挂住
                // printf( "StartService() Failed ERROR_IO_PENDING ! \n");
                bRet = FALSE;
                goto BeforeLeave;
            }
            else
            {
                //服务已经开启
                // printf( "StartService() Failed ERROR_SERVICE_ALREADY_RUNNING ! \n");
                bRet = TRUE;
                goto BeforeLeave;
            }
        }
    }
    bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
    if (hServiceDDK)
    {
        CloseServiceHandle(hServiceDDK);
    }
    if (hServiceMgr)
    {
        CloseServiceHandle(hServiceMgr);
    }
    return bRet;
}
 
//卸载驱动程序
BOOL UnloadDriver(TCHAR *szSvrName)
{
    BOOL bRet = FALSE;
    SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
    SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
    SERVICE_STATUS SvrSta;
    //打开SCM管理器
    hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hServiceMgr == NULL)
    {
        //带开SCM管理器失败
        printf("OpenSCManager() Failed %d ! \n", GetLastError());
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        //带开SCM管理器失败成功
        printf("OpenSCManager() ok ! \n");
    }
    //打开驱动所对应的服务
    hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS);
 
    if (hServiceDDK == NULL)
    {
        //打开驱动所对应的服务失败
        printf("OpenService() Failed %d ! \n", GetLastError());
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        printf("OpenService() ok ! \n");
    }
    //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载.
    if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta))
    {
        printf("ControlService() Failed %d !\n", GetLastError());
    }
    else
    {
        //打开驱动所对应的失败
        printf("ControlService() ok !\n");
    }
    //动态卸载驱动程序.
    if (!DeleteService(hServiceDDK))
    {
        //卸载失败
        printf("DeleteSrevice() Failed %d !\n", GetLastError());
    }
    else
    {
        //卸载成功
        printf("DelServer:eleteSrevice() ok !\n");
    }
    bRet = TRUE;
BeforeLeave:
    //离开前关闭打开的句柄
    if (hServiceDDK)
    {
        CloseServiceHandle(hServiceDDK);
    }
    if (hServiceMgr)
    {
        CloseServiceHandle(hServiceMgr);
    }
    return bRet;
}
 
HANDLE OpenDevice()
{
    //测试驱动程序
    HANDLE hDevice = CreateFile(_T("\\\\.\\PopupDrv"),
                                GENERIC_WRITE | GENERIC_READ,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                0, //这里要传入FILE_FLAG_OVERAPPED才是真正异步读
                                NULL);
    if (hDevice != INVALID_HANDLE_VALUE)
    {
        printf("Create Device ok ! \n");
    }
    else
    {
        printf("Create Device faild %d ! \n", GetLastError());
        return NULL;
    }
 
    return hDevice;
}
 
typedef struct _R3_REPLY
{
    ULONG m_ulWaitID;
    ULONG m_ulBlocked;
} R3_REPLY;
 
typedef struct _OP_INFO
{
    WCHAR m_ProcessName[MAX_PATH];
    DWORD m_ulProcessID;
    ULONG m_ulWaitID;
 
} OP_INFO, *POP_INFO;
 
VOID SendResultToR0(ULONG ulWaitID, BOOL bBlocked)
{
    if (gh_Device == INVALID_HANDLE_VALUE)
    {
        return;
    }
 
    R3_REPLY R3Reply;
    R3Reply.m_ulWaitID = ulWaitID;
    R3Reply.m_ulBlocked = bBlocked;
 
    ULONG ulRet = 0;
    ::DeviceIoControl(gh_Device, IOCTL_SEND_RESULT_TO_R0, &R3Reply, sizeof(R3_REPLY), NULL, 0, &ulRet, NULL); //把R3弹窗处理的结果返回给R0
 
    return;
}
BOOL HandleData(OP_INFO *pOpInfoData)
{
 
    CPopupDlg dlg;
 
    dlg.SetProcess(pOpInfoData->m_ProcessName); //进程名字
    dlg.SetDetail(_T("有进程正在非法攻击"));    //弹窗
 
    dlg.DoModal(); //倒计时30s
 
    if (dlg.m_bAllow == 0) //允许还是阻止
    {
        return FALSE;
    }
 
    return TRUE;
}
 
void PopupInfoToUser(OP_INFO *pOpInfo, int Num)
{
    OP_INFO *currData = pOpInfo;
    CString szNum;
 
    for (int i = 0; i < Num; i++)
    {
        BOOL bResult = HandleData(currData); // 此处可以弹框获得用户的结果
        if (bResult)
        {
            SendResultToR0(pOpInfo->m_ulWaitID, TRUE); //把弹窗结果返回给Ro0        }
            else
            {
                SendResultToR0(pOpInfo->m_ulWaitID, FALSE);
            }
            currData++;
        }
    }
 
    UINT ReadThreadProc(LPVOID lpContext)
    {
        OVERLAPPED Overlapped;
 
        g_hOverlappedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
        if (g_hOverlappedEvent == NULL || gh_Device == INVALID_HANDLE_VALUE)
        {
            return -1;
        }
 
        memset(&Overlapped, 0, sizeof(OVERLAPPED));
 
        ULONG ulReturn = 0;
        ULONG ulBytesReturn = 0;
 
        OP_INFO OpInfo;
        Overlapped.hEvent = g_hOverlappedEvent;
 
        ::SleepEx(1, TRUE);
 
        while (TRUE)
        {
            ulReturn = ReadFile(gh_Device, &OpInfo, sizeof(OP_INFO), &ulBytesReturn, &Overlapped); //异步读.该函数会立即返回
 
            if (g_bToExitThread == TRUE)
            {
                break;
            }
 
            if (ulReturn == 0) //没有读到数据
            {
                if (GetLastError() == ERROR_IO_PENDING)
                {
                    ULONG ulApiReturn = WaitForSingleObject(Overlapped.hEvent, INFINITE); //在这个事件上进行等待
 
                    if (ulApiReturn == WAIT_FAILED)
                    {
                        break;
                    }
                    if (g_bToExitThread == TRUE)
                    {
                        break;
                    }
                }
                else
                {
                    continue;
                }
            }
            if (ulBytesReturn == sizeof(OP_INFO))
            {
                PopupInfoToUser(&OpInfo, 1); //弹窗
            }
        }
 
        return 0;
    }
 
    /////////////////////////////////////////////////////////////////////////////
    // CAboutDlg dialog used for App About
 
    class CAboutDlg : public CDialog
    {
    public:
        CAboutDlg();
 
        // Dialog Data
        //{{AFX_DATA(CAboutDlg)
        enum
        {
            IDD = IDD_ABOUTBOX
        };
        //}}AFX_DATA
 
        // ClassWizard generated virtual function overrides
        //{{AFX_VIRTUAL(CAboutDlg)
    protected:
        virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
                                                         //}}AFX_VIRTUAL
 
        // Implementation
    protected:
        //{{AFX_MSG(CAboutDlg)
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()
    };
 
    CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
    {
        //{{AFX_DATA_INIT(CAboutDlg)
        //}}AFX_DATA_INIT
    }
 
    void CAboutDlg::DoDataExchange(CDataExchange * pDX)
    {
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CAboutDlg)
        //}}AFX_DATA_MAP
    }
 
    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    //{{AFX_MSG_MAP(CAboutDlg)
    // No message handlers
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
 
    /////////////////////////////////////////////////////////////////////////////
    // CPopupClientDlg dialog
 
    CPopupClientDlg::CPopupClientDlg(CWnd * pParent /*=NULL*/)
        : CDialog(CPopupClientDlg::IDD, pParent)
    {
        //{{AFX_DATA_INIT(CPopupClientDlg)
        // NOTE: the ClassWizard will add member initialization here
        //}}AFX_DATA_INIT
        // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
 
    void CPopupClientDlg::DoDataExchange(CDataExchange * pDX)
    {
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CPopupClientDlg)
        // NOTE: the ClassWizard will add DDX and DDV calls here
        //}}AFX_DATA_MAP
    }
 
    BEGIN_MESSAGE_MAP(CPopupClientDlg, CDialog)
    //{{AFX_MSG_MAP(CPopupClientDlg)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
    ON_WM_CLOSE()
    ON_COMMAND(ID_MENU_EXIT, OnMenuExit)
    ON_COMMAND(ID_MENU_RESTORE, OnMenuRestore)
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_ICON_NOTIFY, OnTrayNotification)
    ON_BN_CLICKED(IDOK, &CPopupClientDlg::OnBnClickedOk)
    END_MESSAGE_MAP()
 
    /////////////////////////////////////////////////////////////////////////////
    // CPopupClientDlg message handlers
 
    BOOL CPopupClientDlg::OnInitDialog()
    {
        CDialog::OnInitDialog();
 
        ::BringWindowToTop(m_hWnd);
        ::SetWindowPos(
            m_hWnd,
            HWND_TOPMOST,
            0, 0, 0, 0,
            SWP_NOMOVE | SWP_NOSIZE);
 
        // Add "About..." menu item to system menu.
 
        // IDM_ABOUTBOX must be in the system command range.
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);
 
        CMenu *pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != NULL)
        {
            CString strAboutMenu;
            strAboutMenu.LoadString(IDS_ABOUTBOX);
            if (!strAboutMenu.IsEmpty())
            {
                pSysMenu->AppendMenu(MF_SEPARATOR);
                pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
            }
        }
 
        // Set the icon for this dialog.  The framework does this automatically
        //  when the application's main window is not a dialog
        SetIcon(m_hIcon, TRUE);     // Set big icon
        SetIcon(m_hIcon, FALSE); // Set small icon
 
        // TODO: Add extra initialization here
 
        NOTIFYICONDATA m_tnid;
 
        m_tnid.cbSize = sizeof(NOTIFYICONDATA);              //设置结构大小//
        m_tnid.hWnd = this->m_hWnd;                          //设置图标对应的窗口
        m_tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; //图标属性
        m_tnid.uCallbackMessage = WM_ICON_NOTIFY;          //应用程序定义的回调消息ID
 
        CString szToolTip;
        szToolTip = _T("HIPS -- 客户端程序");
        _tcscpy(m_tnid.szTip, szToolTip); //帮助信息
        m_tnid.uID = IDR_MAINFRAME;          //应用程序图标
        m_tnid.hIcon = m_hIcon;              //图标句柄
        PNOTIFYICONDATA m_ptnid = &m_tnid;
        ::Shell_NotifyIcon(NIM_ADD, m_ptnid); //增加图标到系统盘
 
        GetDlgItem(IDOK)->EnableWindow(TRUE);
        GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
 
        return TRUE; // return TRUE  unless you set the focus to a control
    }
 
    void CPopupClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
    {
        if ((nID & 0xFFF0) == IDM_ABOUTBOX)
        {
            CAboutDlg dlgAbout;
            dlgAbout.DoModal();
        }
        else
        {
            CDialog::OnSysCommand(nID, lParam);
        }
 
        if (nID == SC_MINIMIZE)
        {
            ShowWindow(FALSE); //隐藏窗口
        }
    }
 
    // If you add a minimize button to your dialog, you will need the code below
    //  to draw the icon.  For MFC applications using the document/view model,
    //  this is automatically done for you by the framework.
 
    void CPopupClientDlg::OnPaint()
    {
        if (IsIconic())
        {
            CPaintDC dc(this); // device context for painting
 
            SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
 
            // Center icon in client rectangle
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;
 
            // Draw the icon
            dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
            CDialog::OnPaint();
        }
    }
 
    // The system calls this to obtain the cursor to display while the user drags
    //  the minimized window.
    HCURSOR CPopupClientDlg::OnQueryDragIcon()
    {
        return (HCURSOR)m_hIcon;
    }
 
    void CPopupClientDlg::OnButtonStop()
    {
        // TODO: Add your control notification handler code here
        g_bToExitThread = TRUE;
        if (g_hOverlappedEvent != NULL)
        {
            ResetEvent(g_hOverlappedEvent);
 
            if (g_hReadThread != NULL)
            {
                if (WaitForSingleObject(g_hReadThread->m_hThread, 3000) == WAIT_TIMEOUT)
                {
                    TerminateThread(g_hReadThread->m_hThread, 0);
                }
                delete g_hReadThread;
                g_hReadThread = NULL;
            }
 
            CloseHandle(g_hOverlappedEvent);
            g_hOverlappedEvent = NULL;
        }
        if (gh_Device != INVALID_HANDLE_VALUE)
        {
            CloseHandle(gh_Device);
            gh_Device = INVALID_HANDLE_VALUE;
        }
        // UnloadDriver(DRIVER_NAME);
 
        GetDlgItem(IDOK)->EnableWindow(TRUE);
        GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
    }
 
    void CPopupClientDlg::OnOK()
    {
        // TODO: Add extra validation here
 
        DWORD dwThreadID = 0;
        g_bToExitThread = FALSE;
        //加载驱动
        BOOL bRet = LoadDriver(DRIVER_NAME, DRIVER_PATH);
        if (!bRet)
        {
            MessageBox(_T("加载驱动失败"), _T("Error"), MB_OK);
            return;
        }
 
        gh_Device = OpenDevice();
        if (gh_Device == NULL)
        {
            MessageBox(_T("打开设备失败"), _T("Error"), MB_OK);
            return;
        }
 
        g_hReadThread = AfxBeginThread(ReadThreadProc, this); //创建一个读线程
 
        g_hReadThread->SuspendThread();
        g_hReadThread->m_bAutoDelete = FALSE;
        g_hReadThread->ResumeThread();
 
        if (g_hReadThread == NULL)
        {
            CloseHandle(gh_Device);
            gh_Device = INVALID_HANDLE_VALUE;
            UnloadDriver(DRIVER_NAME);
            return;
        }
        GetDlgItem(IDOK)->EnableWindow(FALSE);
        GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);
    }
 
    LRESULT CPopupClientDlg::OnTrayNotification(WPARAM wParam, LPARAM lParam)
    {
        switch (lParam)
        {
        case WM_LBUTTONDOWN:
        {
            AfxGetApp()->m_pMainWnd->ShowWindow(SW_SHOWNORMAL);
            SetForegroundWindow();
            break;
        }
        case WM_RBUTTONUP:
        {
            POINT point;
            HMENU hMenu, hSubMenu;
            GetCursorPos(&point); //鼠标位置
            hMenu = LoadMenu(NULL,
                             MAKEINTRESOURCE(IDR_MENU_TRAY)); // 加载菜单
            hSubMenu = GetSubMenu(hMenu, 0);                  //得到子菜单(因为弹出式菜单是子菜单)
            SetMenuDefaultItem(hSubMenu, -1, FALSE);          //设置缺省菜单项,-1为无缺省项
            SetForegroundWindow();                              // 激活窗口并置前
 
            TrackPopupMenu(hSubMenu, 0,
                           point.x, point.y, 0, m_hWnd, NULL);
        }
        }
        return 1;
    }
 
    void CPopupClientDlg::OnClose()
    {
 
        NOTIFYICONDATA nd = {0};
 
        nd.cbSize = sizeof(NOTIFYICONDATA);
        nd.hWnd = m_hWnd;
        nd.uID = IDR_MAINFRAME;
        nd.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
        nd.uCallbackMessage = WM_ICON_NOTIFY;
        nd.hIcon = m_hIcon;
 
        Shell_NotifyIcon(NIM_DELETE, &nd);
 
        CDialog::OnCancel();
    }
 
    void CPopupClientDlg::OnMenuExit()
    {
        OnClose();
    }
 
    void CPopupClientDlg::OnMenuRestore()
    {
        ShowWindow(SW_SHOWNORMAL);
        SetForegroundWindow();
    }
 
    void CPopupClientDlg::OnDestroy()
    {
        CDialog::OnDestroy();
    }
 
    void CPopupClientDlg::OnBnClickedOk()
    {
        // TODO: Add your control notification handler code here
        OnOK();
    }

模拟攻击者

  • 向驱动发送一个IOCTL_XXX_ATTACK控制码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// attacker.cpp : Defines the entry point for the console application.
//
#include "StdAfx.h"
#include <windows.h>
#include <winsvc.h>
#include <conio.h>
#include <stdio.h>
#include <winioctl.h>
 
#define DRIVER_NAME "PopupDrv"
#define DRIVER_PATH ".\\PopupDrv.sys"
 
#define IOCTL_BASE 0x800
#define MY_CTL_CODE(i) \
    CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTL_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
#define IOCTL_XXX_ATTACK MY_CTL_CODE(2)
 
//装载NT驱动程序
BOOL LoadDriver(char *lpszDriverName, char *lpszDriverPath)
{
    char szDriverImagePath[256] = {0};
    //得到完整的驱动路径
    GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);
 
    BOOL bRet = FALSE;
 
    SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
    SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
 
    //打开服务控制管理器
    hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 
    if (hServiceMgr == NULL)
    {
        // OpenSCManager失败
        printf("OpenSCManager() Failed %d ! \n", GetLastError());
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        ////OpenSCManager成功
        printf("OpenSCManager() ok ! \n");
    }
 
    //创建驱动所对应的服务
    hServiceDDK = CreateService(hServiceMgr,
                                lpszDriverName,           //驱动程序的在注册表中的名字
                                lpszDriverName,           // 注册表驱动程序的 DisplayName 值
                                SERVICE_ALL_ACCESS,       // 加载驱动程序的访问权限
                                SERVICE_KERNEL_DRIVER, // 表示加载的服务是驱动程序
                                SERVICE_DEMAND_START,  // 注册表驱动程序的 Start 值
                                SERVICE_ERROR_IGNORE,  // 注册表驱动程序的 ErrorControl 值
                                szDriverImagePath,       // 注册表驱动程序的 ImagePath 值
                                NULL,                   // GroupOrder HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList
                                NULL,
                                NULL,
                                NULL,
                                NULL);
 
    DWORD dwRtn;
    //判断服务是否失败
    if (hServiceDDK == NULL)
    {
        dwRtn = GetLastError();
        if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
        {
            //由于其他原因创建服务失败
            printf("CrateService() Failed %d ! \n", dwRtn);
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            //服务创建失败,是由于服务已经创立过
            printf("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n");
        }
 
        // 驱动程序已经加载,只需要打开
        hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
        if (hServiceDDK == NULL)
        {
            //如果打开服务也失败,则意味错误
            dwRtn = GetLastError();
            printf("OpenService() Failed %d ! \n", dwRtn);
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            printf("OpenService() ok ! \n");
        }
    }
    else
    {
        printf("CrateService() ok ! \n");
    }
 
    //开启此项服务
    bRet = StartService(hServiceDDK, NULL, NULL);
    if (!bRet)
    {
        DWORD dwRtn = GetLastError();
        if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
        {
            printf("StartService() Failed %d ! \n", dwRtn);
            bRet = FALSE;
            goto BeforeLeave;
        }
        else
        {
            if (dwRtn == ERROR_IO_PENDING)
            {
                //设备被挂住
                printf("StartService() Failed ERROR_IO_PENDING ! \n");
                bRet = FALSE;
                goto BeforeLeave;
            }
            else
            {
                //服务已经开启
                printf("StartService() Failed ERROR_SERVICE_ALREADY_RUNNING ! \n");
                bRet = TRUE;
                goto BeforeLeave;
            }
        }
    }
    bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
    if (hServiceDDK)
    {
        CloseServiceHandle(hServiceDDK);
    }
    if (hServiceMgr)
    {
        CloseServiceHandle(hServiceMgr);
    }
    return bRet;
}
 
//卸载驱动程序
BOOL UnloadDriver(char *szSvrName)
{
    BOOL bRet = FALSE;
    SC_HANDLE hServiceMgr = NULL; // SCM管理器的句柄
    SC_HANDLE hServiceDDK = NULL; // NT驱动程序的服务句柄
    SERVICE_STATUS SvrSta;
    //打开SCM管理器
    hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hServiceMgr == NULL)
    {
        //带开SCM管理器失败
        printf("OpenSCManager() Failed %d ! \n", GetLastError());
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        //带开SCM管理器失败成功
        printf("OpenSCManager() ok ! \n");
    }
    //打开驱动所对应的服务
    hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS);
 
    if (hServiceDDK == NULL)
    {
        //打开驱动所对应的服务失败
        printf("OpenService() Failed %d ! \n", GetLastError());
        bRet = FALSE;
        goto BeforeLeave;
    }
    else
    {
        printf("OpenService() ok ! \n");
    }
    //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载.
    if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta))
    {
        printf("ControlService() Failed %d !\n", GetLastError());
    }
    else
    {
        //打开驱动所对应的失败
        printf("ControlService() ok !\n");
    }
    //动态卸载驱动程序.
    if (!DeleteService(hServiceDDK))
    {
        //卸载失败
        printf("DeleteSrevice() Failed %d !\n", GetLastError());
    }
    else
    {
        //卸载成功
        printf("DelServer:DeleteSrevice() ok !\n");
    }
    bRet = TRUE;
BeforeLeave:
    //离开前关闭打开的句柄
    if (hServiceDDK)
    {
        CloseServiceHandle(hServiceDDK);
    }
    if (hServiceMgr)
    {
        CloseServiceHandle(hServiceMgr);
    }
    return bRet;
}
 
void TestDriver()
{
    //测试驱动程序
    HANDLE hDevice = CreateFile("\\\\.\\PopupDrv",
                                GENERIC_WRITE | GENERIC_READ,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                0,
                                NULL);
    if (hDevice != INVALID_HANDLE_VALUE)
    {
        printf("Create Device ok ! \n");
    }
    else
    {
        printf("Create Device failed %d ! \n", GetLastError());
        return;
    }
 
    CHAR bufInput[1024] = "Hello, world";
    DWORD dwAttackRes = 0;
    DWORD dwRet = 0;
    printf("Begin to attack\n");
    /// 得到的设备对象,通过DeviceIoControl发送一个控制码,模拟攻击
    DeviceIoControl(hDevice, IOCTL_XXX_ATTACK, bufInput, sizeof(bufInput), &dwAttackRes, sizeof(dwAttackRes), &dwRet, NULL);
    if (dwAttackRes == 0)
    {
        printf("Attack denied\n");
    }
    else
    {
        printf("Attack OK\n");
    }
 
    CloseHandle(hDevice);
}
 
int main(int argc, char *argv[])
{
    //加载驱动
    BOOL bRet = LoadDriver(DRIVER_NAME, DRIVER_PATH);
    if (!bRet)
    {
        printf("LoadNTDriver error\n");
        return 0;
    }
    //加载成功
 
    // printf( "press any to create device!\n" );
    // getch();
 
    TestDriver();
 
    //这时候你可以通过注册表,或其他查看符号连接的软件验证.
    // printf( "press any to unload the driver!\n" );
    // getch();
 
    //卸载驱动
    //     bRet = UnloadDriver(DRIVER_NAME);
    //     if (!bRet)
    //     {
    //         printf("UnloadNTDriver error\n");
    //         return 0;
    //     }
 
    return 0;
}

弹窗程序待完善的问题

1.超时问题

  • 同时模拟两个攻击(几乎同时运行两个attcker.exe),对GetResultFromUserLock()加了锁后,驱动中调用GetResultFromUser()应该是串行的,第一个弹窗倒计时30s后,自动关闭,attcker.exe退出,第二弹窗开始倒计时也需要倒计时满30s后,atccker.exe才退出
    原因
  • 内核层程序是支持多线程并发的(可以同时(并发不是并行)拦截多个攻击事件)
  • 应用层程序只有一个线程处理
  • 假设内核层同时监测到3个攻击,发给应用层,每个攻击同时等待40s,应用层只有一个线程,只能依次把攻击事件弹窗,倒计时30s。这时候用户可能在第一个30s内只处理完第一弹窗的话,30s过后第2、3个攻击事件可能来不及弹窗,因为超时都消失了,用户来不及处理。
    解决方法
  • 方法1:应用层改为多线程,当内核层在很短时间间隔内截获较多攻击事件,应用层为每个攻击事件单独开一个线程来弹窗处理,但这样会导致短时间内较多弹窗,用户体验不佳
  • 方法2:将内核层发送攻击事件改为串行的
    • 方式1:把攻击事件加到队列
    • 方式2:把GetResultFromUser()设为临界区,这样一次只能有一个线程调用GetResultFromUser()
1
2
3
4
5
6
/// 加放锁放入函数内部
        //LockWrite(&g_GetResultFromUserLock); //最好是在函数内部。除非你在任何调用这个函数的地方都加锁
        //KeEnterCriticalRegion(); //改成串行
        notifyResult = GetResultFromUser();//这里最长会等待40s,收集数据封装成一个结构体结点,放在OperList或者满足PendingIrpList中等待的Irp,从R3获得弹框结果,是阻止还是放过
        //KeLeaveCriticalRegion();
        //UnLockWrite(&g_GetResultFromUserLock);

@todo 共享资源锁并没有生效,还需要用windbg R3-R0联调一下

2.不是真正异步读

原因
  • CreateFile()倒数第二个参数是0

  • 异步编程:线程在调用函数的时候,不管这个函数有没有拿到数据,会立即返回,后面通过回调函数或者是事件机制来拿到函数执行的结果。异步比同步效率要高

  • 同步编程:线程在调用函数的时候,只要这个函数没有拿到数据,就一直在哪里等待,不会立即返回,直到拿到数据才会返回。
  • eg:A在微信上向女盆友B发消息之后,A去做其他事情了,B给A回消息,会有消息声音提醒A,A打开微信拿到B回复的消息
  • eg1:完成端口模型
  • 同步阻塞编程(Block)
    • 基于锁的(lock-based)
    • 阻塞的意思是,会睡眠让出CPU,有结果的时候就会被唤醒,一般用于读之前不确定有没有结果数据,使用阻塞比较好。
    • eg:A在微信上向女盆友B发消息之后,A抱着手机睡着了,直到B给A回消息,会有消息声音吵醒A,A醒来打开微信拿到B回复的消息
  • 同步非阻塞编程(Non-blocking Synchronization) :根据粒度不同分为以下几类
    • 非阻塞的意思是,不会睡眠,一直在等,直到拿到结果,一般用于读之前明确知道的是有结果数据的,而且很快能拿到结果。
      • eg:女盆友B在微信上向A发消息之前确定A是在线的,B发完消息后,A秒回B,B直接看到到A回复的消息
        • eg1:网络中的select模型、epoll模型,多路复用,多个IO复用同一个Event,只有Event会阻塞,读写不会阻塞
          解决方法
  • CreateFile()倒数第二个参数设置成FILE_FLAG_OVERAPPED
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
HANDLE OpenDevice()
{
    //测试驱动程序
    HANDLE hDevice = CreateFile(_T("\\\\.\\PopupDrv"),
                                GENERIC_WRITE | GENERIC_READ,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                0, //这里要传入FILE_FLAG_OVERAPPED才是真正异步读
                                NULL);
    if (hDevice != INVALID_HANDLE_VALUE)
    {
        printf("Create Device ok ! \n");
    }
    else
    {
        printf("Create Device faild %d ! \n", GetLastError());
        return NULL;
    }
 
    return hDevice;
}

3.获取攻击进程的全路径还没完成

根据PID获得进程全路径思路
  • 根据PID获得进程全路径
    • PID→exprocess→KeStackAttachProcess→ZwQuerylnformationProcess->ProcesslmageFileName→ZwCreateFile
  • 设备对象名转换成符号链接名
    • ->ObReferenceObjectByHandle→RtIVolumeDeviceToDosName->ZwQuerylnformationFile
      实战
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/**
*  @brief       GetProcessFullNameByPid 获取进程的全路径;
*  @param[in]   nPid 进程pid;
*  @param[out]  FullPath 进程的全路径;
*  @return      ntStatus 成功返回0,否则为非0,如果例程成功,则它必须返回 STATUS_SUCCESS。
*                        否则,它必须返回在 ntstatus中定义的错误状态值之一;
*/
NTSTATUS GetProcessFullNameByPid(HANDLE nPid, PUNICODE_STRING FullPath)
{
 
    HANDLE hFile = NULL;
    ULONG nNeedSize = 0;
    NTSTATUS nStatus = STATUS_SUCCESS;
    NTSTATUS nDeviceStatus = STATUS_DEVICE_DOES_NOT_EXIST;
    PEPROCESS Process = NULL;
    KAPC_STATE ApcState = { 0 };
    PVOID lpBuffer = NULL;
    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
    IO_STATUS_BLOCK IoStatus = { 0 };
    PFILE_OBJECT FileObject = NULL;
    PFILE_NAME_INFORMATION FileName = NULL;
    WCHAR FileBuffer[MAX_PATH] = { 0 };
    DECLARE_UNICODE_STRING_SIZE(ProcessPath, MAX_PATH);
    DECLARE_UNICODE_STRING_SIZE(DosDeviceName, MAX_PATH);
 
    PAGED_CODE();
    /// 通过pid拿到进程的eprocess结构
    nStatus = PsLookupProcessByProcessId(nPid, &Process);
    if (NT_ERROR(nStatus))
    {
        KdPrint(("%s error PsLookupProcessByProcessId.\n", __FUNCTION__));
        return nStatus;
    }
 
    __try
    {
        /// 切换到当前进程的上下文
        KeStackAttachProcess(Process, &ApcState);
 
        nStatus = ZwQueryInformationProcess(
            NtCurrentProcess(),
            ProcessImageFileName,
            NULL,
            NULL, //第一次传NULL是为了获取进程路径的大小
            &nNeedSize);
 
        if (STATUS_INFO_LENGTH_MISMATCH != nStatus)
        {
            KdPrint(("%s NtQueryInformationProcess error.\n", __FUNCTION__));
            nStatus = STATUS_MEMORY_NOT_ALLOCATED;
            __leave;
        }
        /// 分配一个内存来存放进程的全路径
        lpBuffer = ExAllocatePoolWithTag(NonPagedPool, nNeedSize, 'GetP');
        if (lpBuffer == NULL)
        {
            KdPrint(("%s ExAllocatePoolWithTag error.\n", __FUNCTION__));
            nStatus = STATUS_MEMORY_NOT_ALLOCATED;
            __leave;
        }
 
        nStatus = ZwQueryInformationProcess(
            NtCurrentProcess(),
            ProcessImageFileName,
            lpBuffer, //在查询一遍就获得进程的全路径了
            nNeedSize,
            &nNeedSize);
 
        if (NT_ERROR(nStatus))
        {
            KdPrint(("%s NtQueryInformationProcess error2.\n", __FUNCTION__));
            __leave;
        }
        //获得的路径是\device\harddiskvolume3\program files\qq.exe这种格式,需要转化成用户熟悉的盘符
        //\??\c:\program files\qq.exe
        RtlCopyUnicodeString(&ProcessPath, (PUNICODE_STRING)lpBuffer);
        InitializeObjectAttributes(
            &ObjectAttributes,
            &ProcessPath,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL);
        /// 打开路径得到handle
        nStatus = ZwCreateFile(
            &hFile,
            FILE_READ_ATTRIBUTES,
            &ObjectAttributes,
            &IoStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            0,
            FILE_OPEN,
            FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
            NULL,
            0);
 
        if (NT_ERROR(nStatus))
        {
            hFile = NULL;
            __leave;
        }
 
        nStatus = ObReferenceObjectByHandle(
            hFile,
            NULL,
            *IoFileObjectType,
            KernelMode,
            (PVOID*)&FileObject, //得到handle对应的FileObject
            NULL);
 
        if (NT_ERROR(nStatus))
        {
            FileObject = NULL;
            __leave;
        }
 
        FileName = (PFILE_NAME_INFORMATION)FileBuffer;
 
        nStatus = ZwQueryInformationFile(
            hFile,
            &IoStatus,
            FileName,
            sizeof(WCHAR) * MAX_PATH,
            FileNameInformation);
 
        if (NT_ERROR(nStatus))
        {
            __leave;
        }
 
        if (FileObject->DeviceObject == NULL) //通过FileObject得到DeviceObject
        {
            nDeviceStatus = STATUS_DEVICE_DOES_NOT_EXIST;
            __leave;
        }
        /// 求出进程文件所在盘符的符号链接
        nDeviceStatus = RtlVolumeDeviceToDosName(FileObject->DeviceObject, &DosDeviceName);
    }
    __finally
    {
        if (NULL != FileObject)
        {
            ObDereferenceObject(FileObject);
        }
 
        if (NULL != hFile)
        {
            ZwClose(hFile);
        }
 
        if (NULL != lpBuffer)
        {
            ExFreePool(lpBuffer);
        }
 
        KeUnstackDetachProcess(&ApcState);
    }
 
    if (NT_SUCCESS(nStatus))
    {
        RtlInitUnicodeString(&ProcessPath, FileName->FileName);
 
        if (NT_SUCCESS(nDeviceStatus))
        {
            /// 拼接DosDeviceName + ProcessPath
            RtlCopyUnicodeString(FullPath, &DosDeviceName);
            RtlUnicodeStringCat(FullPath, &ProcessPath);
        }
        else
        {
            RtlCopyUnicodeString(FullPath, &ProcessPath);
        }
    }
 
    return nStatus;
}

4.也可以在内核层弹窗

不仅可以应用层弹窗。内核层也调用ExRaiseHardError()来弹窗(但可定制性不高,没有计时功能,比如在反截屏中,可以弹窗提示用户)

主防的几个经典问题

弹窗弹的是什么?

A把B怎么了?

  • A:R3某进程。
    • 忽略内核操作,互不干涉,内核和内核对战是没必要的,主要是阻止进程进入内核。比如五大常任理事国都有核武器,五大常任理事国一旦打架,地球重启。共同阻止其他非常任理事国突破核武器。朝鲜。
  • B:文件/注册表/驱动/进程
  • 怎么了:创建/删除/修改/重命名/启动

    允许还是阻止

    弹框的不同颜色警示危险等级

  • 做得太弱智,老弹窗,用户会厌烦,用户也可能误判
  • 拦截到攻击自己判断处理最好,也防止用户误判,但太智能也不行,一次都没有弹窗,用户会怀疑你行不行,所以还是偶尔弹一下绿框告诉用户,拦截到某个攻击事件已经自动处理,一闪而过刷一下存在感

    模式匹配

    正则匹配

  • 为了性能和安全的平衡,不可能监控所有文件,监控重要的文件比较合理,比如sytem32文件夹等。这时候就需要使用模式匹配了
  • *表示零次或者任意次字符
  • 表示零次或下一次字符
  • eg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOLEAN IsPatternMatch(PUNICODE STRING Expression,
    PUNICODE_STRING Name, BOOLEAN IgnoreCase)
{
    /// 如果匹配成功return True,但也有局限性,只支持文件路径的正则匹配,比如注册表就不支持了
    return FsRtllsNamelnExpression(
        Expression, //正则式
        Name, //具体某个文件的路径,
        IgnoreCase, //如果这里设置为TRUE,那么Expression必须是大写的
        NULL
    );
}
 
/// 比如现在有两条规则:假设忽略大小写
L'C:\\WINDOWS\\SYSTEM32\\*.SYS"
L"C:\\WINDOWS\\SYSTEM32\\*\\*.SYS"
/// 有路径:
L"C:\\Window\\system32\\122222\\2.sys"
/// 就会匹配第一、第二条规则
/// 如果区分分文件夹,只想命中第二条规则,可以把第一条规则改成
L'C:\\WINDOWS\\SYSTEM32/*.SYS"
实战:支持任何字符串的正则匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <Ntifs.h>
#include <ntddk.h>
#include <windef.h>
 
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    DbgPrint("Goodbye!\n");
}
 
BOOLEAN IsPatternMatch(PUNICODE_STRING Expression, PUNICODE_STRING Name, BOOLEAN IgnoreCase)
{
    return FsRtlIsNameInExpression(
                Expression,
                Name,
                IgnoreCase,//如果这里设置为TRUE,那么Expression必须是大写的
                NULL
                );
 
}
 
BOOL PatternMatch(WCHAR * pat, WCHAR * str)
{
   register WCHAR * s;
   register WCHAR * p;
   BOOL star = FALSE;
 
loopStart:
   for (s = str, p = pat; *s; ++s, ++p) {
      switch (*p) {
         case L'?':
            if (*s == L'.') goto starCheck;
            break;
         case L'*':
            star = TRUE;
            str = s, pat = p;
            if (!*++pat) return TRUE;
            goto loopStart;
         default:
            //if (Lower(*s) != Lower(*p))
               goto starCheck;
            break;
      }
   }
   if (*p == L'*') ++p;
   return (!*p);
 
starCheck:
   if (!star) return FALSE;
   str++;
   goto loopStart;
}
 
BOOL PatternNMatch(WCHAR * pat, WCHAR * str, DWORD count)
{
   register WCHAR * s;
   register WCHAR * p;
   BOOL star = FALSE;
   DWORD dwCount = count;
 
loopStart:
   for (s = str, p = pat; dwCount>0; --dwCount, ++s, ++p) {
      switch (*p) {
         case L'?':
            if (*s == L'.') goto starCheck;
            break;
         case L'*':
            star = TRUE;
            str = s, pat = p;
            if (!*++pat) return TRUE;
            goto loopStart;
         default:
            //if (Lower(*s) != Lower(*p))
               goto starCheck;
            break;
      }
   }
   if (*p == L'*') ++p;
   return (!*p);
 
starCheck:
   if (!star) return FALSE;
   str++;
   dwCount--;
   goto loopStart;
}
 
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
 
    UNICODE_STRING uExpression = {0};
    UNICODE_STRING uName       = {0};
 
    RtlInitUnicodeString(&uExpression, L"C:\\WINDOWS\\SYSTEM32\\*.SYS");
    RtlInitUnicodeString(&uName, L"c:\\Windows\\system32\\122222\\2.sys");
 
    if (IsPatternMatch(&uExpression, &uName, TRUE))
    {
        DbgPrint("Matched\n");
    }
    else
    {
        DbgPrint("Not Matched\n");
    }
    pDriverObject->DriverUnload = DriverUnload;
 
    return STATUS_SUCCESS;
 
}

格式转换

获得目标对象的全路径
  • 内核中有多种对象,对象不同获取全路径的方式不同
  • Handle
  • ObReferenceObjectByHandle→ loQueryFileDosDeviceName
  • ObReferenceObjectBylHandle→ ObQueryNameString(强删文件例子里)
  • IRP查询(FILEMON4.34中FilemonQueryFile函数)
  • FltGetName(minifilter)
    获得文件短名的长名
  • windows用dir /x 查看短名
    • dos环境下的83格式(文件名总数<=8,扩展名<=3,太长的文件名中间部分会被~替换)
  • 统一使用长名来做规则匹配
    • 所以需要短名转化成长名
  • 思路:
    • 把文件名按~来分割,从左往右依次调用ZwQueryDirectoryFile()获得长名,直至所有短名都转换成功拼接在一起就得到长名
      实战
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#include <ntifs.h>
#include <ntstrsafe.h>
#include <ntddk.h>
#include <windef.h>
 
BOOL IsRootDirecotry(WCHAR * wszDir)
{
    SIZE_T length = wcslen(wszDir);
 
    // c:
    if((length == 2) && (wszDir[1] == L':'))
        return TRUE;
    //\\??\\c:
    if((length == 6) &&
        (_wcsnicmp(wszDir, L"\\??\\", 4) == 0) &&
        (wszDir[5] == L':'))
        return TRUE;
    //\\DosDevices\\c:
    if((length == 14) &&
        (_wcsnicmp(wszDir, L"\\DosDevices\\", 12) == 0) &&
        (wszDir[13] == L':'))
        return TRUE;
    //\\Device\\HarddiskVolume1
    if((length == 23) &&
        (_wcsnicmp(wszDir, L"\\Device\\HarddiskVolume", 22) == 0))
        return TRUE;
 
 
    return FALSE;
}
 
 
BOOL IsDirectorySep(WCHAR ch)
{
    return (ch == L'\\' || ch == L'/');
}
 
//C:\\Program\\123456~1
//wszRootdir为:c:\\Program
//wszShortName为:123456~1
 
BOOL QueryDirectoryForLongName(
                      WCHAR * wszRootDir,
                      WCHAR * wszShortName,
                      WCHAR *wszLongName,
                      ULONG ulSize)
{
    UNICODE_STRING                ustrRootDir        = {0};
    UNICODE_STRING                ustrShortName    = {0};
    UNICODE_STRING                ustrLongName    = {0};
    OBJECT_ATTRIBUTES            oa                = {0};
    IO_STATUS_BLOCK                Iosb            = {0};
    NTSTATUS                    ntStatus        = 0;
    HANDLE                        hDirHandle        = 0;
    BYTE                        *Buffer            = NULL;
    WCHAR                        *wszRoot        = NULL;
    PFILE_BOTH_DIR_INFORMATION    pInfo            = NULL;
 
    RtlZeroMemory(&Iosb, sizeof(IO_STATUS_BLOCK));
    Iosb.Status = STATUS_NO_SUCH_FILE;
 
    wszRoot = ExAllocatePoolWithTag(PagedPool,
                                  MAX_PATH * sizeof(WCHAR),
                                  'L2S');
    if(wszRoot == NULL)
    {
        return FALSE;
    }
 
    RtlZeroMemory(wszRoot, MAX_PATH * sizeof(WCHAR));
 
    wcsncpy(wszRoot, wszRootDir, MAX_PATH);
 
    RtlInitUnicodeString(&ustrRootDir, wszRoot);
    RtlInitUnicodeString(&ustrShortName, wszShortName);
 
    if(IsRootDirecotry(wszRoot))
        RtlAppendUnicodeToString(&ustrRootDir, L"\\");
 
    InitializeObjectAttributes(&oa,
                               &ustrRootDir,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               0,
                               0); 
 
    ntStatus = ZwCreateFile(&hDirHandle,
                            GENERIC_READ | SYNCHRONIZE,
                            &oa,
                            &Iosb,
                            0,
                            FILE_ATTRIBUTE_DIRECTORY,
                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                            FILE_OPEN,
                            FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ,
                            0,
                            0);
 
    if (!NT_SUCCESS(ntStatus))
    {
        ExFreePool(wszRoot);
        return FALSE;
    }
 
    ExFreePool(wszRoot);
 
    Buffer = ExAllocatePoolWithTag(PagedPool,
                          1024,
                          'L2S');
    if(Buffer == NULL)
    {
        ZwClose(hDirHandle);
        return FALSE;
    }
 
    RtlZeroMemory(Buffer, 1024);
 
    ntStatus = ZwQueryDirectoryFile(hDirHandle,
                                NULL,
                                0,
                                0,
                                &Iosb,
                                Buffer,
                                1024,
                                FileBothDirectoryInformation,
                                TRUE,
                                &ustrShortName, //传回与 ustrShortName Match的项
                                TRUE);
 
    if (!NT_SUCCESS(ntStatus))
    {
        ExFreePool(Buffer);
        ZwClose(hDirHandle);
        return FALSE;
    }
 
    ZwClose(hDirHandle);
 
    pInfo = (PFILE_BOTH_DIR_INFORMATION) Buffer;
 
    if(pInfo->FileNameLength == 0)
    {
        ExFreePool(Buffer);
        return FALSE;
    }
 
    ustrShortName.Length  = (USHORT)pInfo->FileNameLength;
    ustrShortName.MaximumLength = (USHORT)pInfo->FileNameLength;
    ustrShortName.Buffer = pInfo->FileName;    //长名
 
    if(ulSize < ustrShortName.Length)
    {   
        ExFreePool(Buffer);
        return FALSE;
    }
 
    ustrLongName.Length = 0;
    ustrLongName.MaximumLength = (USHORT)ulSize;
    ustrLongName.Buffer = wszLongName;
 
    RtlCopyUnicodeString(&ustrLongName, &ustrShortName);
    ExFreePool(Buffer);
    return TRUE;
}
 
BOOL QueryLongName(WCHAR * wszFullPath, WCHAR * wszLongName, ULONG size)
{
    BOOL        rtn                = FALSE;
    WCHAR *        pchStart        = wszFullPath;
    WCHAR *        pchEnd            = NULL;
    WCHAR *        wszShortName    = NULL;
 
    //c:\\Program\\Files1~1-->获得Files1~1的长名
    while(*pchStart)
    {
        if(IsDirectorySep(*pchStart))
            pchEnd = pchStart;
 
        pchStart++;
    }
    //wszFullPath=c:\\Program
    //pchEnd = Files~1
 
    if(pchEnd)
    {
        *pchEnd++ = L'\0';
        //c:\\Program\\Files1~1
        //wszFullPath:c:\\Program
        //pchEnd:Files1~1
        wszShortName = pchEnd;
        rtn = QueryDirectoryForLongName(wszFullPath, wszShortName, wszLongName, size);
        *(--pchEnd) = L'\\';
        //wszFullPath=c:\\Program\\Files1~1
    }
    return rtn;
}
 
//先把根目录拷贝到目标目录中,剩下的找到下一级目录是否含有~,如果有,则开始转化.
//如:c:\\Progam\\a~1\\b~1\hi~1.txt
//pchStart指向目录中前一个\\,pchEnd扫描并指向目录的下一个\\,其中如果发现了~,则是短名,需要转换.
//传c:\\Program\\a~1-->c:\\Progam\\ax
//传c:\\Program\\ax\\b~1-->c:\\Program\\ax\\by
//传c:\\Program\\ax\by\\hi~1.txt-->c:\\Program\\ax\by\\hiz.txt
BOOL ConverShortToLongName(WCHAR *wszLongName, WCHAR *wszShortName, ULONG size)
{
    WCHAR            *szResult        = NULL;
    WCHAR            *pchResult        = NULL;
    WCHAR            *pchStart        = wszShortName;
    INT                Offset            = 0;
 
    szResult = ExAllocatePoolWithTag(PagedPool,
                          sizeof(WCHAR) * (MAX_PATH * 2 + 1),
                          'L2S');
 
    if(szResult == NULL)
    {
        return FALSE;
    }
 
    RtlZeroMemory(szResult, sizeof(WCHAR) * (MAX_PATH * 2 + 1));
    pchResult = szResult;
 
 
    //C:\\x\\-->\\??\\c:
    if (pchStart[0] && pchStart[1] == L':')
    {
        *pchResult++ = L'\\';
        *pchResult++ = L'?';
        *pchResult++ = L'?';
        *pchResult++ = L'\\';
        *pchResult++ = *pchStart++;
        *pchResult++ = *pchStart++;
        Offset = 4;
    }
    //\\DosDevices\\c:\\xx-->\\??\\c:
    else if (_wcsnicmp(pchStart, L"\\DosDevices\\", 12) == 0)
    {
        RtlStringCbCopyW(pchResult, sizeof(WCHAR) * (MAX_PATH * 2 + 1), L"\\??\\");
        pchResult += 4;
        pchStart += 12;
        while (*pchStart && !IsDirectorySep(*pchStart))
            *pchResult++ = *pchStart++;
        Offset = 4;
    }
    //\\Device\\HarddiskVolume1\\xx-->\\Device\\HarddiskVolume1
    else if (_wcsnicmp(pchStart, L"\\Device\\HardDiskVolume", 22) == 0)
    {
        RtlStringCbCopyW(pchResult, sizeof(WCHAR) * (MAX_PATH * 2 + 1),L"\\Device\\HardDiskVolume");
        pchResult += 22;
        pchStart += 22;
        while (*pchStart && !IsDirectorySep(*pchStart))
            *pchResult++ = *pchStart++;
    }
    //\\??\\c:\\xx-->\\??\\c:
    else if (_wcsnicmp(pchStart, L"\\??\\", 4) == 0)
    {
        RtlStringCbCopyW(pchResult, sizeof(WCHAR) * (MAX_PATH * 2 + 1), L"\\??\\");
        pchResult += 4;
        pchStart += 4;
 
        while (*pchStart && !IsDirectorySep(*pchStart))
            *pchResult++ = *pchStart++;
    }
    else
    {
        ExFreePool(szResult);
        return FALSE;
    }
 
    while (IsDirectorySep(*pchStart))
    {
        BOOL            bShortName            = FALSE;
        WCHAR            *pchEnd                = NULL;
        WCHAR            *pchReplacePos        = NULL;
 
        *pchResult++ = *pchStart++;
 
        pchEnd = pchStart;
        pchReplacePos = pchResult;
 
        while (*pchEnd && !IsDirectorySep(*pchEnd))
        {
            if(*pchEnd == L'~')
            {
                bShortName = TRUE;
            }
 
            *pchResult++ = *pchEnd++;
        }
 
        *pchResult = L'\0';
 
        if(bShortName)
        {
            WCHAR  * szLong = NULL;
 
            szLong = ExAllocatePoolWithTag(PagedPool,
                          sizeof(WCHAR) * MAX_PATH,
                          'L2S');
            if(szLong)
            {
                RtlZeroMemory(szLong,  sizeof(WCHAR) * MAX_PATH);
 
                if(QueryLongName(szResult, szLong, sizeof(WCHAR) * MAX_PATH))
                {
                    RtlStringCbCopyW(pchReplacePos, sizeof(WCHAR) * (MAX_PATH * 2 + 1), szLong);
                    pchResult = pchReplacePos + wcslen(pchReplacePos);
                }
 
                ExFreePool(szLong);
            }
        }
 
        pchStart = pchEnd;
    }
 
    wcsncpy(wszLongName, szResult + Offset, size/sizeof(WCHAR));
    ExFreePool(szResult);
    return TRUE;
}
 
BOOL IsShortNamePath(WCHAR * wszFileName)
{
    WCHAR *p = wszFileName;
 
    while(*p != L'\0')
    {
        if(*p == L'~')
        {
            return TRUE;
        }
        p++;
    }
 
    return FALSE;
}
 
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    DbgPrint("Goodbye!\n");
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
    //dir /x看短名
    WCHAR    wszShortName[MAX_PATH] =L"\\??\\C:\\PROGRA~1\\COMMON~1\\MICROS~1\\VC\\1.txt";
    WCHAR    wszLongName[MAX_PATH] = {0};
 
    if(ConverShortToLongName(wszLongName, wszShortName, sizeof(wszLongName)))
    {
        DbgPrint("%ws\n", wszLongName);
    }
 
    pDriverObject->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}
设备名与符号链接名的转化
  • 设备名-> 符号链接名
    • 有指定的API可以调用
    • ZwOpenSymbolicLinkObject()得到符号链接名
    • ZwQuerySymbolicLinkObject()得到设备名
  • 设备名<-符号链接名
    • 没有指定的API可以调用
    • 但盘符是有限的,只有26个,A-Z
    • 可以通过枚举A-Z盘符对应的设备名是否与目标一致来找到符号链接名
      实战
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <ntddk.h>
#include <windef.h>
#include <ntstrsafe.h>
 
//输入\\??\\c:-->\\device\\\harddiskvolume1
//LinkTarget.Buffer注意要释放
 
NTSTATUS QuerySymbolicLink(
            IN PUNICODE_STRING SymbolicLinkName,
            OUT PUNICODE_STRING LinkTarget
            )                                 
{
    OBJECT_ATTRIBUTES    oa        = {0};
    NTSTATUS            status    = 0;
    HANDLE                handle    = NULL;
 
    InitializeObjectAttributes(
                            &oa,
                            SymbolicLinkName,
                            OBJ_CASE_INSENSITIVE,
                            0,
                            0);
 
    status = ZwOpenSymbolicLinkObject(&handle, GENERIC_READ, &oa);
    if (!NT_SUCCESS(status))
    {
        return status;
    }
 
    LinkTarget->MaximumLength = MAX_PATH*sizeof(WCHAR);
    LinkTarget->Length = 0;
    LinkTarget->Buffer = ExAllocatePoolWithTag(PagedPool, LinkTarget->MaximumLength,'SOD');
    if (!LinkTarget->Buffer)
    {
        ZwClose(handle);
        return STATUS_INSUFFICIENT_RESOURCES;
    }
 
    RtlZeroMemory(LinkTarget->Buffer, LinkTarget->MaximumLength);
 
    status = ZwQuerySymbolicLinkObject(handle, LinkTarget, NULL);
    ZwClose(handle);
 
    if (!NT_SUCCESS(status))
    {
        ExFreePool(LinkTarget->Buffer);
    }
 
    return status;
}
 
//输入\\Device\\harddiskvolume1
//输出C:
//DosName.Buffer的内存记得释放
 
NTSTATUS
MyRtlVolumeDeviceToDosName(
                        IN PUNICODE_STRING DeviceName,
                        OUT PUNICODE_STRING DosName
                        )
 
/*++
 
Routine Description:
 
This routine returns a valid DOS path for the given device object.
This caller of this routine must call ExFreePool on DosName->Buffer
when it is no longer needed.
 
Arguments:
 
VolumeDeviceObject - Supplies the volume device object.
DosName - Returns the DOS name for the volume
Return Value:
 
NTSTATUS
 
--*/
 
{
    NTSTATUS                status                    = 0;
    UNICODE_STRING            driveLetterName            = {0};
    WCHAR                    driveLetterNameBuf[128] = {0};
    WCHAR                    c                        = L'\0';
    WCHAR                    DriLetter[3]            = {0};
    UNICODE_STRING            linkTarget                = {0};
 
    for (c = L'A'; c <= L'Z'; c++)
    {
        RtlInitEmptyUnicodeString(&driveLetterName,driveLetterNameBuf,sizeof(driveLetterNameBuf));
        RtlAppendUnicodeToString(&driveLetterName, L"\\??\\");
        DriLetter[0] = c;
        DriLetter[1] = L':';
        DriLetter[2] = 0;
        RtlAppendUnicodeToString(&driveLetterName,DriLetter);
 
        status = QuerySymbolicLink(&driveLetterName, &linkTarget);
        if (!NT_SUCCESS(status))
        {
            continue;
        }
 
        if (RtlEqualUnicodeString(&linkTarget, DeviceName, TRUE))
        {
            ExFreePool(linkTarget.Buffer);
            break;
        }
 
        ExFreePool(linkTarget.Buffer);
    }
 
    if (c <= L'Z')
    {
        DosName->Buffer = ExAllocatePoolWithTag(PagedPool, 3*sizeof(WCHAR), 'SOD');
        if (!DosName->Buffer)
        {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
 
        DosName->MaximumLength = 6;
        DosName->Length   = 4;
        *DosName->Buffer  = c;
        *(DosName->Buffer+ 1) = ':';
        *(DosName->Buffer+ 2) = 0;
 
        return STATUS_SUCCESS;
    }
 
    return status;
}
 
//c:\\windows\\hi.txt<--\\device\\harddiskvolume1\\windows\\hi.txt
BOOL NTAPI GetNTLinkName(IN WCHAR * wszNTName, OUT WCHAR * wszFileName)
{
    UNICODE_STRING        ustrFileName = {0};
    UNICODE_STRING        ustrDosName = {0};
    UNICODE_STRING        ustrDeviceName = {0};
 
    WCHAR                *pPath = NULL;
    ULONG                i = 0;
    ULONG                ulSepNum = 0;
 
 
    if (wszFileName == NULL ||
        wszNTName == NULL ||
        _wcsnicmp(wszNTName, L"\\device\\harddiskvolume", wcslen(L"\\device\\harddiskvolume"))!=0)
    {
        return FALSE;
    }
 
    ustrFileName.Buffer = wszFileName;
    ustrFileName.Length = 0;
    ustrFileName.MaximumLength = sizeof(WCHAR)*MAX_PATH;
 
    while(wszNTName[i]!=L'\0')
    {
 
        if (wszNTName[i] == L'\0')
        {
            break;
        }
        if (wszNTName[i] == L'\\')
        {
            ulSepNum++;
        }
        if (ulSepNum == 3)
        {
            wszNTName[i] = UNICODE_NULL;
            pPath = &wszNTName[i+1];
            break;
        }
        i++;
    }
 
    if (pPath == NULL)
    {
        return FALSE;
    }
 
    RtlInitUnicodeString(&ustrDeviceName, wszNTName);
 
    if (!NT_SUCCESS(MyRtlVolumeDeviceToDosName(&ustrDeviceName, &ustrDosName)))
    {
        return FALSE;
    }
 
    RtlCopyUnicodeString(&ustrFileName, &ustrDosName);
    RtlAppendUnicodeToString(&ustrFileName, L"\\");
    RtlAppendUnicodeToString(&ustrFileName, pPath);
 
    ExFreePool(ustrDosName.Buffer);
 
    return TRUE;
}
 
BOOL QueryVolumeName(WCHAR ch, WCHAR * name, USHORT size)
{
    WCHAR szVolume[7] = L"\\??\\C:";
    UNICODE_STRING LinkName;
    UNICODE_STRING VolName;
    UNICODE_STRING ustrTarget;
    NTSTATUS ntStatus = 0;
 
    RtlInitUnicodeString(&LinkName, szVolume);
 
    szVolume[4] = ch;
 
    ustrTarget.Buffer = name;
    ustrTarget.Length = 0;
    ustrTarget.MaximumLength = size;
 
    ntStatus = QuerySymbolicLink(&LinkName, &VolName);
    if (NT_SUCCESS(ntStatus))
    {
        RtlCopyUnicodeString(&ustrTarget, &VolName);
        ExFreePool(VolName.Buffer);
    }
    return NT_SUCCESS(ntStatus);
 
}
 
//\\??\\c:\\windows\\hi.txt-->\\device\\harddiskvolume1\\windows\\hi.txt
 
BOOL NTAPI GetNtDeviceName(IN WCHAR * filename, OUT WCHAR * ntname)
{
    UNICODE_STRING uVolName = {0,0,0};
    WCHAR volName[MAX_PATH] = L"";
    WCHAR tmpName[MAX_PATH] = L"";
    WCHAR chVol = L'\0';
    WCHAR * pPath = NULL;
    int i = 0;
 
 
    RtlStringCbCopyW(tmpName, MAX_PATH * sizeof(WCHAR), filename);
 
    for(i = 1; i < MAX_PATH - 1; i++)
    {
        if(tmpName[i] == L':')
        {
            pPath = &tmpName[(i + 1) % MAX_PATH];
            chVol = tmpName[i - 1];
            break;
        }
    }
 
    if(pPath == NULL)
    {
        return FALSE;
    }
 
    if(chVol == L'?')
    {
        uVolName.Length = 0;
        uVolName.MaximumLength = MAX_PATH * sizeof(WCHAR);
        uVolName.Buffer = ntname;
        RtlAppendUnicodeToString(&uVolName, L"\\Device\\HarddiskVolume?");
        RtlAppendUnicodeToString(&uVolName, pPath);
        return TRUE;
    }
    else if(QueryVolumeName(chVol, volName, MAX_PATH * sizeof(WCHAR)))
    {
        uVolName.Length = 0;
        uVolName.MaximumLength = MAX_PATH * sizeof(WCHAR);
        uVolName.Buffer = ntname;
        RtlAppendUnicodeToString(&uVolName, volName);
        RtlAppendUnicodeToString(&uVolName, pPath);
        return TRUE;
    }
 
    return FALSE;
}
 
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    DbgPrint("Goodbye!\n");
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
    UNICODE_STRING        ustrDeviceName            = {0};
    UNICODE_STRING        ustrLinkName            = {0};
    WCHAR                *wszDeviceName            = L"\\Device\\harddiskvolume1";
    NTSTATUS            ntStatus                = 0;
    WCHAR            DeviceName[MAX_PATH]        = L"\\Device\\harddiskvolume1\\windows\\hi.txt";
    WCHAR            FileName[MAX_PATH]        = {0};
    WCHAR            szDeviceName[MAX_PATH]  = {0};
 
    RtlInitUnicodeString(&ustrDeviceName, wszDeviceName);
 
    ntStatus = MyRtlVolumeDeviceToDosName(&ustrDeviceName, &ustrLinkName);
    if (NT_SUCCESS(ntStatus))
    {
 
        DbgPrint("linkname:%wZ\n", &ustrLinkName);
        if (ustrLinkName.Buffer)
        {
            ExFreePool(ustrLinkName.Buffer);
        }
    }
    if (GetNTLinkName(DeviceName, FileName))
    {
        DbgPrint("FileName:%ws\n", FileName);
        GetNtDeviceName(FileName, szDeviceName);
        DbgPrint("szDeviceName:%ws", szDeviceName);
    }
 
    pDriverObject->DriverUnload = DriverUnload;
 
    return STATUS_SUCCESS;
 
}

规则的下发方式

  • 用dat文件存放规则,dat文件做一些简单加密(防止木马和病毒利用,绕过监测)
  • 客户端把dat文件的规则读出,通过DeviceIoControl()发到内核,保存在内核中的链表或者树或者hash表里面,内核在拦截到攻击事件的时候,就会去遍历这个存放规则数据结构,匹配上就弹窗或者自行处理。

    放行问题

  • 大数据机器学习,智能放行
  • 中国杀毒软件层参加国际比赛到很高分,是针对病毒库定制的版本(查杀率很高),和用户的版本不一样,取消比赛成绩

    放行的条件

  • 内核态的操作不拦截
    • ExGetPreviousMode() == KernnelMode来判断是不是内核态
    • 或者用Irp->RequestorMode == kernelMode来判断是不是内核态
  • 自己进程的操作不拦截
    1
    - 在DeviceIoctrlFilter中通过PsGetCurrentProcessId()拿到进程的`PID`
  • 系统进程的操作不拦截
    • DriverEntry里处于系统上下文,再通过PsGetCurrentProcessId()拿到的就是系统进程,以后凡是这些系统进程的操作都放行
    • 应用层的Irql一般不会超过APC_LEVEL,即KeGetCurrentIrql()>APC_LEVEL放行,只拦截应用层
  • 白名单中的进程(交了钱的)
    • 但有可能会被白利用,让白名单的程序把我做一些攻击操作,借刀杀人
    • 防范白利用,用调用链,不仅要看当前进程是好的还是坏的,还要看调用这个进程的父进程是好的还是坏的,只要有一个是坏的,就是不放行。

      如何放行

  • 如果通过HOOK进行监控,放行就是直接调用原来被HOOK的API
  • IRP(如果是在过滤驱动进行监控的)
1
2
3
/// 把Irp往下发
IoSkipCurrentIrpStackLocation(IpIrp);
IoCallDriver();

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回