首页
社区
课程
招聘
[原创]强删,强杀,穿越
发表于: 2023-2-7 08:19 8677

[原创]强删,强杀,穿越

2023-2-7 08:19
8677

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

《Windows NT File System Iternals》 文件加解密必备

《Windows驱动开发技术详解》张帆 电子工业出版社 很基础,仅仅适合入门
《Windows 2000设备驱动程序设计智能》 Art Baker等主 机械工业出版社
《寒江独钓:Windows内核安全编程》 邵坚磊等著 电子工业出版社
《Windows内核原理与实现》潘爱民

强删,强杀,穿越

强制删除文件

  • 安全软件用强删清除病毒木马
  • 病毒木马也可以用强删来删除系统保护文件或是受杀软保护的文件。

    内核层实现强删文件

    强删文件思路

  1. 打开文件
  • 删除文件首先要打开文件,得到文件的句柄,打开文件一般是调用ZwCreateFile
  • 通过在windbg看到应用层到内核层的调用栈,可以发现ZwCreateFile->NtCreateFile函数底层调用的就是IoCreateFile
  • NTCreteFile很容易就被HOOK(SSDT HOOK),而IoCreateFile更底层,对它的HOOK难度更大一些,更可靠一些,所以强删文件这里使用IoCreateFile来打开文件。
  1. 解锁

a. 只读

  • 文件或文件夹包含三种属性:
    • 1.只读:只读属性是不允许修改的文件。若将文件或文件夹设置为只读属性,则该文件或文件夹不允许更改。在应用层是可以删除的。,在内核层则无法删除
    • 2.隐藏:隐藏的文件是看不见的。若将文件或文件夹设置为隐藏属性,则该文件或文件夹在常规显示中将不被看到;
    • 3.存档:一般的文件的属性是存档的。若将文件或文件夹设置为存档属性,则表示该文件或文件夹已存档,有些程序用此选项来确定哪些文件需做备份
  • 解决方法:
    • 抹除只读权限后再删除
    • @todo 失败

b.被其它程序独占

  • 无法删除的原因:
    • 正常情况下,一个文件被别的进程独占打开时,我们是无法打开该文件的,从而得不到该文件的句柄,自然就删除不了该文件。
  • 解决方法:
    • 调用ZwQuerySystemInfomation 查询全局句柄表,找到要删除的文件句柄和打开该文件的进程
      • 调用ZwDuplicateObject(导出但未文档化的函数,现在可以调用它,但未来可能会失效)把句柄从独占它的进程空间中拷贝过来,同时把这个句柄从目标进程中关闭掉(函数传参传入这个参数DUPLICATE_CLOSE_SOURCE)。从而独占失效,就可以拿到该文件的句柄,将其删除。
      • 这是市面上大多数解锁程序用的技术。但也有局限性,对硬链接没有效果。
    • 硬链接:mklink /h link.txt gb.txt link.txt是对gb.txt的一个alias,链接计数(删除减一)。
    • 当gb.txt被独占了,打不开,link.txt也被独占了,也打不开,此时如果用关句柄的方法去强删link.txt,无法删除,因为硬链接link.txt在句柄表中根本不存在。
    • 硬连接是不能跨卷的(即在C盘中的文件不能在D盘中创建一个硬链接),只有在同一文件系统中的文件之间才能创建链接。
    • 软链接(也叫符号链接)与硬链接不同,文件中存放的内容是另—文件的路径名的指向
    • 软链接就是普通文件,只是数据块内容有点特殊。删除软链接并不影响被指向的文件,若被指向的原文件件被删除,则相关软连接就变成了死链接

c.正在运行.exe

  • 无法删除的原因:(反汇编逆向分析ZwDeleteFile或者ZwSetinformationFile,或者直接看之前泄漏的winxp微软源代码)
    • 上层调用ZwDeleteFile或者ZwSetinformationFile删除文件的时候最终会执行这个函数NtfsSetDispositionlnfo最终会调用MmFlushImageSection
    • 然后在MmFlushImageSection函数内,操作系统会检查要删除文件对象SectionObjectPointer中的成员ImageSectionObjectDataSectionObject是否为0,如果不为0,则认为该文件是正在运行的.exe,拒绝删除
1
2
3
pSectionObjectPointer = fileObject->SectionObjectPointer;
psectionObjectPointer->ImageSectionObject = 0;
pSectionObjectPointer->DataSectionObject = 0;
  • 解决方式
    • 可以提前把 ImageSectionObjectDataSectionObject设为0,欺骗操作系统。
  1. 构建IRP删除文件
    • 一般情况下,在删除文件的时候是调用ZwDeleteFile或者ZwSetinformationFile来删除文件
    • 但有时候基于这些函数对文件的操作已经不可靠了(调用的这些函数已经被病毒或者木马篡改了,当函数被Hook之后,删除病毒和木马,病毒和密木马自我保护,会把删除请求拒绝)。
    • 这时候需要我们自己构建一个专门用来删除文件的Irp来删除文件。
      实战
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
#include <ntddk.h>
#include <ntimage.h>
#include <ntdef.h>
#include "DelFile.h"
 
 
PDEVICE_OBJECT    g_HookDevice;
 
NTSTATUS dfQuerySymbolicLink(
    IN PUNICODE_STRING SymbolicLinkName,
    OUT PUNICODE_STRING LinkTarget
    )                                 
{
    OBJECT_ATTRIBUTES oa;
    NTSTATUS status;
    HANDLE handle;
 
    InitializeObjectAttributes(
        &oa,
        SymbolicLinkName,
        OBJ_CASE_INSENSITIVE,
        0,
        0);
 
    status = ZwOpenSymbolicLinkObject(&handle, GENERIC_READ, &oa);
    if (!NT_SUCCESS(status))
    {
        return status;
    }
 
    LinkTarget->MaximumLength = 1024*sizeof(WCHAR);
    LinkTarget->Length = 0;
    LinkTarget->Buffer = ExAllocatePoolWithTag(PagedPool, LinkTarget->MaximumLength, 'A0');
    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;
}
 
BOOLEAN dfCloseFileHandle(WCHAR *name)
{
 
    NTSTATUS                     status;
    PVOID                         buf   = NULL;
    PSYSTEM_HANDLE_INFORMATION      pSysHandleInfo;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO handleTEI;
 
    ULONG                        size  = 1;
    ULONG                        NumOfHandle = 0;
    ULONG                        i;
    CLIENT_ID                     cid;
    HANDLE                        hHandle;
    HANDLE                        hProcess;
    HANDLE                         hDupObj;
    HANDLE                        hFile;
    HANDLE                        link_handle;
    OBJECT_ATTRIBUTES             oa;
    ULONG                        FileType;
    ULONG                        processID;
    UNICODE_STRING                 uLinkName;
    UNICODE_STRING                uLink;
    OBJECT_ATTRIBUTES             objectAttributes;
    IO_STATUS_BLOCK              IoStatus;
    ULONG                         ulRet;
    PVOID                         fileObject;
    POBJECT_NAME_INFORMATION     pObjName = {0};
    UNICODE_STRING                delFileName = {0};
    int                            length;
    WCHAR                        wVolumeLetter[3];
    WCHAR                        *pFilePath;
    UNICODE_STRING                uVolume;
    UNICODE_STRING                uFilePath;
    UNICODE_STRING                 NullString = RTL_CONSTANT_STRING(L"");
    BOOLEAN                    bRet = FALSE;
 
 
    for ( size = 1; ; size *= 2 ) //句柄表是动态变化的,所以不知道多大的buffer存放合适,从1B开始 指数增长
    {
        if ( NULL == ( buf = ExAllocatePoolWithTag(NonPagedPool,size, 'FILE') ) )
        {
            DbgPrint(("alloc mem failed\n"));
            goto Exit;
        }
        RtlZeroMemory( buf ,size );
        status = ZwQuerySystemInformation( SystemHandleInformation, buf, size, NULL ); //不像ZwEnumerateValueKey函数把参数传NULL就返回buffer的实际大小,所以就需要调整buffer的大小
        if ( !NT_SUCCESS( status ) )
        {
            if ( STATUS_INFO_LENGTH_MISMATCH == status ) //这样写是为了在编译阶段发现status = STATUS_INFO_LENGTH_MISMATCH的错误
            {
                ExFreePool( buf );
                buf = NULL;
            }
            else
            {
                DbgPrint(( "ZwQuerySystemInformation() failed"));
                goto Exit;
            }
        }
        else
        {
            break; //直到buffer能存放全局句柄表退出循环
        }
    }
 
    pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)buf;
    NumOfHandle = pSysHandleInfo->NumberOfHandles; //句柄个数
 
 
    //句柄表只有handle和打开这个句柄的进程的PID,所以需要把句柄转换成具体的文件名,
    //而且转换过来的这个文件名是\device\harddiskvolume3\haha.doc这种格式的,但删除文件的文件名硬编码成这种格式"\??\c:\haha.doc",所以还需要转换一下格式
    /* Get the volume character like C: */
    //\??\c:\haha.doc-->\device\harddiskvolume3\haha.doc
 
    wVolumeLetter[0] = name[4];
    wVolumeLetter[1] = name[5];
    wVolumeLetter[2] = 0;
    uLinkName.Buffer = ExAllocatePoolWithTag(NonPagedPool, 256 + sizeof(ULONG), 'A1');
    uLinkName.MaximumLength = 256;
    RtlInitUnicodeString(&uVolume, wVolumeLetter);
    RtlInitUnicodeString( &uLink, L"\\DosDevices\\");
    RtlCopyUnicodeString(&uLinkName, &uLink);
 
    status = RtlAppendUnicodeStringToString(&uLinkName, &uVolume);
    if (!NT_SUCCESS(status))
    {
        KdPrint(("RtlAppendUnicodeStringToString() failed"));
        return FALSE;
    }
 
    dfQuerySymbolicLink(&uLinkName, &delFileName);
    RtlFreeUnicodeString(&uLinkName);
    KdPrint(("delFileName:%wZ", &delFileName));
 
    pFilePath = (WCHAR *) &name[6];
    RtlInitUnicodeString( &uFilePath, pFilePath);
 
    RtlAppendUnicodeStringToString(&delFileName, &uFilePath);
    if (!NT_SUCCESS(status))
    {
        KdPrint(("RtlAppendUnicodeStringToString() failed"));
        return FALSE;
    }
 
    KdPrint(("delFile:%wZ", &delFileName));
 
    for(i = 0; i < NumOfHandle ;i++) //遍历全局句柄表
    {
        handleTEI = pSysHandleInfo->Handles[i];
        if (handleTEI.ObjectTypeIndex != 25 && handleTEI.ObjectTypeIndex != 28)//28文件,25设备对象
            continue;
        processID = (ULONG) handleTEI.UniqueProcessId; //打开该句柄的进程PID
        cid.UniqueProcess = (HANDLE)processID;
        cid.UniqueThread = (HANDLE)0;
        hHandle = (HANDLE)handleTEI.HandleValue;
        InitializeObjectAttributes( &oa ,NULL ,0 ,NULL ,NULL );
        status = ZwOpenProcess( &hProcess ,PROCESS_DUP_HANDLE ,&oa ,&cid ); //打开打开该句柄的进程,目的是把目标进程的句柄拷贝到当前进程中来,因为句柄不跨进程只在同一个进程有效,如果把句柄传给另一个进程,它是无效的,只能通过目标进程来访问该句柄
        if ( !NT_SUCCESS( status ) )
        {
            KdPrint(( "ZwOpenProcess:%d Fail ", processID));
            continue;
        }
 
        status = ZwDuplicateObject( hProcess ,hHandle ,NtCurrentProcess() ,&hDupObj ,\
         PROCESS_ALL_ACCESS ,0 ,DUPLICATE_SAME_ACCESS ); //第一次拷贝,把句柄从`独占`它的进程空间中`拷贝`过来
        if ( !NT_SUCCESS( status ) )
        {
            DbgPrint(( "ZwDuplicateObject1 : Fail " ));
            continue;
        }
        status = ObReferenceObjectByHandle( //根据句柄得到文件的内核对象
              hDupObj,
              FILE_ANY_ACCESS,
              0,
              KernelMode,
              &fileObject,
              NULL);
 
        if (!NT_SUCCESS(status))
        {
            DbgPrint(( "ObReferenceObjectByHandle : Fail " ));
            continue;
        
 
        pObjName = (POBJECT_NAME_INFORMATION) ExAllocatePoolWithTag(NonPagedPool, \
            sizeof (OBJECT_NAME_INFORMATION) + 1024 * sizeof (WCHAR), 'A1');
 
        if (STATUS_SUCCESS != (status = ObQueryNameString(fileObject, pObjName, \
            sizeof (OBJECT_NAME_INFORMATION) + 1024 * sizeof (WCHAR), &ulRet))) //查询文件内核对象对应的文件名
        {
           ObDereferenceObject(fileObject);
           continue;
        }
        if (RtlCompareUnicodeString(&pObjName->Name, &delFileName, TRUE) == 0) //相等,则是要找被独占的强删的文件
        {
 
            ObDereferenceObject(fileObject);
            ZwClose(hDupObj);
 
            status = ZwDuplicateObject( hProcess ,hHandle ,NtCurrentProcess() ,&hDupObj ,\
             PROCESS_ALL_ACCESS ,0 ,DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE ); //第二次拷贝,把句柄从`独占`它的进程空间中`拷贝`过来,同时把这个句柄从目标进程中`关闭掉`(函数传参传入这个参数`DUPLICATE_CLOSE_SOURCE`)
            if ( !NT_SUCCESS( status ) )
            {
                DbgPrint(( "ZwDuplicateObject2 : Fail " ));
                //return FALSE;
            }
            else
            {
                ZwClose(hDupObj);
                bRet = TRUE;
                //return TRUE;
            }
            break;
 
        }
 
        ExFreePool(pObjName);
        pObjName = NULL;
 
        ObDereferenceObject(fileObject);
        ZwClose( hDupObj );
        ZwClose( hProcess );
 
    }
 
Exit:
    if (pObjName != NULL)
    {
        ExFreePool(pObjName);
        pObjName = NULL;
    }
    if (delFileName.Buffer != NULL)
    {
        ExFreePool(delFileName.Buffer);   
    }
    if ( buf != NULL )
    {
        ExFreePool( buf );
        buf = NULL;
    }
    return(bRet);
 
}
 
NTSTATUS
dfOpenFile(WCHAR* name,PHANDLE phFileHandle, ACCESS_MASK access,ULONG share)
{
 
   IO_STATUS_BLOCK iosb;
   NTSTATUS stat;
   OBJECT_ATTRIBUTES oba;
   UNICODE_STRING nameus;
 
   if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;}
   RtlInitUnicodeString(&nameus,name);
   InitializeObjectAttributes(
        &oba,
        &nameus,
        OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,
        0,
        0);
   stat=IoCreateFile(
        phFileHandle,
        access,
        &oba,
        &iosb,
        0,
        FILE_ATTRIBUTE_NORMAL,
        share,
        FILE_OPEN,
        0,
        NULL,
        0,
        0,
        NULL,
        IO_NO_PARAMETER_CHECKING); //不要进行参数校验,IoCreateFile才会调用成功
 
    return stat;
}
 
NTSTATUS
dfSkillSetFileCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
{
    Irp->UserIosb->Status = Irp->IoStatus.Status;
    Irp->UserIosb->Information = Irp->IoStatus.Information;
 
    KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
 
    IoFreeIrp(Irp);
 
    return STATUS_MORE_PROCESSING_REQUIRED;
}
BOOLEAN dfDelFile(WCHAR* name)
{
    NTSTATUS        ntStatus = STATUS_SUCCESS;
    PFILE_OBJECT    fileObject;
    PDEVICE_OBJECT  DeviceObject;
    PIRP            Irp;
    KEVENT          event;
    FILE_DISPOSITION_INFORMATION  FileInformation;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION irpSp;
    PSECTION_OBJECT_POINTERS pSectionObjectPointer;
    HANDLE handle;
    OBJECT_ATTRIBUTES                    objAttributes = { 0 };
    InitializeObjectAttributes(&objAttributes,
        &name,
        OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
        NULL,
        NULL);
    /// 1.打开文件
    ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE);
    DbgPrint("dfOpenFile(%ws) failed(%x)\n", &name, ntStatus);
    if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND || //传的名字错了或者路径错了
        ntStatus == STATUS_OBJECT_PATH_NOT_FOUND )
    {
        DbgPrint("No such file",ntStatus);
        return FALSE;
    }
    else if (!NT_SUCCESS(ntStatus))
    {
        /// 2.a抹除文件的只读属性
        //ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE);
        ntStatus = ZwCreateFile(
            &handle,
            SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
            &objAttributes,
            &ioStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            FILE_OPEN,
            FILE_SYNCHRONOUS_IO_NONALERT,
            NULL,
            0);
        if (NT_SUCCESS(ntStatus))
        {
            FILE_BASIC_INFORMATION        basicInfo = { 0 };
 
            ntStatus = ZwQueryInformationFile(handle, &ioStatus,
                &basicInfo, sizeof(basicInfo), FileBasicInformation); //把文件的基本属性读出来
            if (!NT_SUCCESS(ntStatus))
            {
                DbgPrint("ZwQueryInformationFile(%ws) failed(%x)\n", &name, ntStatus);
            }
 
            basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; //改成normal
            ntStatus = ZwSetInformationFile(handle, &ioStatus,
                &basicInfo, sizeof(basicInfo), FileBasicInformation); //把修改后的属性在写回去
            if (!NT_SUCCESS(ntStatus))
            {
                DbgPrint("ZwSetInformationFile(%ws) failed(%x)\n", &name, ntStatus);
            }
        }
        /// 2.b 遍历全局句柄表,关闭独占打开的句柄
        if (dfCloseFileHandle(name))
        {
            ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE); //再次打开文件,就会得到文件的句柄
            if (!NT_SUCCESS(ntStatus))
                return FALSE;
        }
        else
        {
            return FALSE;
        }
    }
 
    ntStatus = ObReferenceObjectByHandle(handle, //根据句柄拿到内核对象,因为句柄不跨进程只在同一个进程有效,如果把句柄传给另一个进程,它是无效的。所以一般是拿到handle之后直接得到它的`fileobject`
        DELETE,
        *IoFileObjectType, //如果没有指定一个句柄的类型,攻击者可以传入非文件类型的句柄从而造成系统漏洞,得到其他类型的内核对象,对应的结构体的定义里很可能没有一些成员,就会行为未定义或者无效内存,下面如果访问这些缺少的成员,系统会崩溃,造成蓝屏。
        KernelMode,
        &fileObject,
        NULL);
 
    if (!NT_SUCCESS(ntStatus))
    {
        DbgPrint("ObReferenceObjectByHandle()");
        ZwClose(handle);
        return FALSE;
    
 
    DeviceObject = IoGetRelatedDeviceObject(fileObject); //通过fileObject拿到文件所在的设备对象,用来接受Irp
    /// 3. 构建IRP删除文件
    Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
 
    if (Irp == NULL)
    {
        ObDereferenceObject(fileObject);
        ZwClose(handle);
        return FALSE;
    }
 
    KeInitializeEvent(&event, SynchronizationEvent, FALSE); //初始化事件,synchronization事件:自动恢复,比如声控灯。FALSE表示最初为无信号状态
 
    FileInformation.DeleteFile = TRUE;
 
    Irp->AssociatedIrp.SystemBuffer = &FileInformation;
    Irp->UserEvent = &event;
    Irp->UserIosb = &ioStatus;
    Irp->Tail.Overlay.OriginalFileObject = fileObject;
    Irp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread();
    Irp->RequestorMode = KernelMode;
 
    irpSp = IoGetNextIrpStackLocation(Irp);
    irpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
    irpSp->DeviceObject = DeviceObject;
    irpSp->FileObject = fileObject;
    irpSp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION);
    irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation;
    irpSp->Parameters.SetFile.FileObject = fileObject;
 
    /// 为当前IRP设置一个完成例程,相当于回调函数,当下层驱动将Irp结束之后就会调用这个完成例程
    IoSetCompletionRoutine(
            Irp,
            dfSkillSetFileCompletion, //完成例程,在完成例程里面设置事件,通过这个事件通知上层驱动,上层驱动才知道Irp完成了,并且知道Irp的完成状态
            &event,
            TRUE,
            TRUE,
            TRUE);
 
    /// 2.b 删除正在运行中的exe所做的处理,欺骗操作系统
    pSectionObjectPointer = fileObject->SectionObjectPointer;
    if(pSectionObjectPointer)
    {
        pSectionObjectPointer->ImageSectionObject = 0;
        pSectionObjectPointer->DataSectionObject = 0;
    }
    /// 3. 将Irp往下发
    ntStatus = IoCallDriver(DeviceObject, Irp);
    if (!NT_SUCCESS(ntStatus))
    {
         ObDereferenceObject(fileObject);
         ZwClose(handle);
         return FALSE;
    
    /// 等待完成例程中事件发生,NULL表示无限等待
    KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, NULL);
    //IoFreeIrp(Irp);
    ObDereferenceObject(fileObject);
    ZwClose(handle);
    return TRUE;
 
}
 
 
NTSTATUS OnUnload(IN PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING          deviceLinkUnicodeString;
    PDEVICE_OBJECT       p_NextObj;
 
 
    DbgPrint("OnUnload called\n");
 
    p_NextObj = DriverObject->DeviceObject;
 
    if (p_NextObj != NULL)
    {
 
        RtlInitUnicodeString( &deviceLinkUnicodeString, deviceLinkBuffer );
        IoDeleteSymbolicLink( &deviceLinkUnicodeString );
 
        IoDeleteDevice( DriverObject->DeviceObject );
    }
    return STATUS_SUCCESS;
}
 
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS                ntStatus;
    UNICODE_STRING          deviceNameUnicodeString;
    UNICODE_STRING          deviceLinkUnicodeString;
 
    RtlInitUnicodeString (&deviceNameUnicodeString,deviceNameBuffer );
    RtlInitUnicodeString (&deviceLinkUnicodeString,deviceLinkBuffer );
 
    ntStatus = IoCreateDevice ( DriverObject,
        0,
        &deviceNameUnicodeString,
        FILE_DEVICE_SWAP,
        0,
        TRUE,
        &g_HookDevice );
 
    if(! NT_SUCCESS(ntStatus))
    {
          DbgPrint(("Failed to create device!\n"));
          return ntStatus;
     }
 
    /* We test the DelFile() function here */   
    if (dfDelFile(L"\\??\\c:\\haha.doc")) //硬编码了两个文件的路径
    {
        KdPrint(("Deleted"));
    }
    else
    {
        KdPrint(("Failed"));
    }
    if (dfDelFile(L"\\??\\c:\\filedelet.exe"))
    {
        KdPrint(("Deleted"));
    }
    else
    {
        KdPrint(("Failed"));
    }
 
    ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
        &deviceNameUnicodeString );
    if(! NT_SUCCESS(ntStatus))
    {
         IoDeleteDevice(DriverObject->DeviceObject);
            DbgPrint("Failed to create symbolic link!\n");
            return ntStatus;
     }
 
    DriverObject->DriverUnload  = OnUnload;
    return STATUS_SUCCESS;
}

应用层实现强删文件

  • 原理与内核层类似
  • 优点:应用层比内核层稳定
  • 存在问题
    • NtQueryObject会导致某些句柄hang住(死锁),但某些句柄并不是文件句柄
    • 如果句柄传给NtQueryObject会hang住,传给GetFileType(hFile)也会超时
    • 所以只需要把这些句柄过滤掉就可以了
  • 解决办法
    • 新建一个线程,调用GetFileType(hFile),设置一个超时,如果超时了,就把该句柄排除掉。
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
#include "avCloseAllFileHandles.h"
 
ZWQUERYSYSTEMINFORMATION        NtQuerySystemInformation = NULL;
ZWQUERYOBJECT                    NtQueryObject = NULL;
HMODULE                            g_hNtDLL = NULL;
 
BOOL InitNTDLL()
{
    g_hNtDLL = LoadLibrary( "ntdll.dll" );
    if ( !g_hNtDLL )
    {
        return FALSE;
    }
 
    NtQuerySystemInformation =
        (ZWQUERYSYSTEMINFORMATION)GetProcAddress( g_hNtDLL, "NtQuerySystemInformation");
 
    NtQueryObject =
        (ZWQUERYOBJECT)GetProcAddress( g_hNtDLL, "NtQueryObject");
 
    if (NtQuerySystemInformation == NULL ||
        NtQueryObject == NULL)
    {
        return FALSE;
    }
 
    return TRUE;
}
 
VOID CloseNTDLL()
{
    if(g_hNtDLL != NULL)
    {
        FreeLibrary(g_hNtDLL);
    }
}
 
DWORD WINAPI IsHandleSafe(LPVOID lpParam)
{
    HANDLE hFile = (HANDLE)lpParam;
    GetFileType(hFile);
    return 0;
}
 
BOOL avCloseHandle(HANDLE Process, HANDLE Handle)
{
    BOOL rtn = FALSE;
    HANDLE h = 0;
 
    rtn = DuplicateHandle(Process,
        Handle,
        GetCurrentProcess( ),
        &h,
        0,
        FALSE,
        DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
 
    if(rtn)
        CloseHandle(h);
    return rtn;
}
 
NTSTATUS avNtQueryObject(
    IN HANDLE ObjectHandle,
    IN ULONG ObjectInformationClass,
    OUT PVOID ObjectInformation,
    IN ULONG ObjectInformationLength,
    OUT PULONG ReturnLength OPTIONAL,
    IN    ULONG waitTime
    )
{
    DWORD             dwTid=0;
    HANDLE             hThread;
    DWORD             dwEax;
    NTSTATUS        st;
    //创建一个线程判断handle是否安全
    hThread = CreateThread(NULL,0,IsHandleSafe,ObjectHandle,0,&dwTid);
    dwEax = WaitForSingleObject(hThread,waitTime);
 
    if(dwEax == STATUS_TIMEOUT) //如果超时就不会传给NtQueryObject避免死锁
    {
        DWORD dwTimeOut = 0;
 
        GetExitCodeThread(hThread, &dwTimeOut);
        TerminateThread(hThread, dwTimeOut);
 
        CloseHandle(hThread);
        return STATUS_UNSUCCESSFUL;
    }
    CloseHandle(hThread);
    st = NtQueryObject(ObjectHandle, ObjectInformationClass, ObjectInformation,
        ObjectInformationLength, ReturnLength);
    return st;
 
}
//比较文件名
BOOL avMatchRemoteFileByHandle(wchar_t *filename, HANDLE Process, HANDLE Handle)
{
    HANDLE            h = NULL;
    ULONG            ret = 0;
    char            *namebuf = NULL;
    BOOLEAN            bMatched = FALSE;
    NTSTATUS        st;
    wchar_t         *outstr = NULL;
 
    if(DuplicateHandle(Process,
        Handle,
        GetCurrentProcess( ),
        &h,
        0,
        FALSE,
        DUPLICATE_SAME_ACCESS))
    {
 
        avNtQueryObject(h, FileNameInformation, NULL, 0, &ret, 100);
        if (ret == 0)
        {
            ret = MAX_PATH;
        }
        namebuf = new char[ret];
        if (namebuf == NULL)
        {
            DebugPrint("No memory available\n");
            CloseHandle(h);
            return FALSE;
        }
        st = avNtQueryObject(h, FileNameInformation, namebuf, ret, NULL, 100); //查询句柄所对应的文件名
        POBJECT_NAME_INFORMATION name = (POBJECT_NAME_INFORMATION)namebuf;
        if (st >= 0)
        {   
            outstr = new wchar_t[MAX_PATH];
            if (outstr == NULL)
            {
                DebugPrint("No memory available");
                if (namebuf)
                {
                    delete []namebuf;
                }
                CloseHandle(h);
                return FALSE;
            }
            memset(outstr, 0, MAX_PATH);
            outstr[0] = L'A';
            outstr[1] = L':';
            if (name->Name.Length > 23 && \
                memicmp(name->Name.Buffer, L"\\Device\\HardDiskVolume", 44) == 0)
            {
                outstr[0] = name->Name.Buffer[22] - L'1' + L'C';
                memcpy(&outstr[2], &name->Name.Buffer[23], name->Name.Length-23*2);
                outstr[name->Name.Length/2-21] = 0;
            }
            /*
            if (name->Name.Length > 23 && \
                _memicmp(name->Name.Buffer, L"\\Device\\HardDiskVolume", 44) == 0)
            {
                //查询,非硬编码
                WCHAR szDiskSymbol[] = L"A:";
                for (WCHAR ch = L'C'; ch <= L'Z'; ch++)
                {
                    szDiskSymbol[0] = ch;
                    WCHAR szBuf[MAX_PATH] = { 0 };
                    QueryDosDeviceW(szDiskSymbol, szBuf, MAX_PATH);
                    if (szBuf[22] == name->Name.Buffer[22])
                    {
                        break;
                    }
                }
 
                outstr[0] = szDiskSymbol[0];
                memcpy(&outstr[2], &name->Name.Buffer[23], name->Name.Length - 23 * 2);
                outstr[name->Name.Length / 2 - 21] = 0;
            }
            */
 
            if (wcsncmp(outstr, filename, wcslen(filename)) == 0)
            {
                DebugPrint("Found:%ws\n",filename);
                bMatched = TRUE;
            }
            delete []outstr;
            outstr = NULL;
        }
        if (namebuf)
        {
            delete []namebuf;
        }
        CloseHandle(h);
        return bMatched;
    }
 
    return FALSE;
}
//获得句柄表
PULONG avGetHandleList()
{
   ULONG             cbBuffer = 0x1000;
   PULONG             pBuffer = new ULONG[cbBuffer];
   NTSTATUS         Status;
   DWORD             dwNumBytesRet = 0x10;
   do
   {
       Status = NtQuerySystemInformation(
           SystemHandleInformation,
           pBuffer,
           cbBuffer * sizeof * pBuffer,
           &dwNumBytesRet);
 
       if (Status == STATUS_INFO_LENGTH_MISMATCH)
       {
           delete [] pBuffer;
           pBuffer = new ULONG[cbBuffer *= 2];
       }
       else if (!NT_SUCCESS(Status))
       {
           delete [] pBuffer;
           return NULL;
       }
   } while (Status == STATUS_INFO_LENGTH_MISMATCH);
 
   return pBuffer;
}
 
BOOL avCloseAllHandlesForFile(wchar_t *filename)
{
    ULONG                            dSize = 0;
    ULONG                            dData = 0;
    ULONG                            NumOfHandle = 0;
    BOOL                            rtn = TRUE;
    ULONG                            i;
    PSYSTEM_HANDLE_INFORMATION         pSysHandleInfo;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO    handleTEI;
    char                            *namebuf = NULL;
    char                            namenull[1000];
    HANDLE                            hTmp;
    UCHAR                            TypeNum;
    HANDLE                            hProcess;
    BOOLEAN                            bClosed = FALSE;
 
 
    GetModuleFileName(NULL,namenull,MAX_PATH);
    hTmp = CreateFile(namenull,
        GENERIC_READ,
        FILE_SHARE_READ,
        0,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);
    if (hTmp == 0)
    {
        return FALSE;
    }
 
    PULONG buf = avGetHandleList();
    if (buf == NULL)
    {
        CloseHandle(hTmp);
        return FALSE;
    }
 
    pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)buf;
    NumOfHandle = pSysHandleInfo->NumberOfHandles;
 
    /* We get file object header type  dynamically */
    for (i = 0; i < NumOfHandle ;i++)
    {
        handleTEI = pSysHandleInfo->Handles[i];
        if (GetCurrentProcessId() == handleTEI.UniqueProcessId &&
            handleTEI.HandleValue == (USHORT)hTmp)
                TypeNum = handleTEI.ObjectTypeIndex;
    }
    CloseHandle(hTmp);
 
    for(i = 0; i < NumOfHandle ;i++)
    {
        handleTEI = pSysHandleInfo->Handles[i];
        if (handleTEI.ObjectTypeIndex != TypeNum)
            continue;
 
        hProcess = OpenProcess(PROCESS_ALL_ACCESS,
            FALSE,
            handleTEI.UniqueProcessId);
        if(hProcess)
        {
            if(avMatchRemoteFileByHandle(filename, hProcess, (HANDLE)handleTEI.HandleValue)) //比较文件名
            {
                if (avCloseHandle(hProcess, (HANDLE)handleTEI.HandleValue))
                {
                    DebugPrint("file:%ws handle closed\n", filename);
                    bClosed = TRUE;
                }
            }
            CloseHandle(hProcess);
        }
    }
    if (buf)
    {
        delete [] buf;
    }
    return bClosed;
}
 
//We test the function avCloseAllHandlesForFile() here
 
int main(int argc, char *argv[])
{
 
    if (argc < 2)
    {
        DebugPrint("Please run it as: closehandle filename\n");
        exit(1);
    }
 
    wchar_t filename[MAX_PATH + 1];
    ZeroMemory(filename, MAX_PATH + 1);
 
    int num = MultiByteToWideChar(CP_OEMCP,MB_PRECOMPOSED,argv[1],
        strlen(argv[1]),filename,MAX_PATH + 1);
 
    filename[strlen(argv[1])] = L'\0';
 
    if (!InitNTDLL())
    {
        DebugPrint("%s\n","InitNTDLL() failed");
        exit(1);
    }
 
    if (avCloseAllHandlesForFile(filename))
    {
        DebugPrint("%s\n", "file handle closed");
    }
    else
    {
        DebugPrint("%s\n", "no file handle closed");
    }
 
    CloseNTDLL();
    return 0;
}

强杀进程

内核层强杀进程

  • 思路1: 创建一个线程,通过特征码(硬编码,不同系统的补丁不同,特征码不同)暴力搜索未导出比较底层函数来杀进程
  • 过程:
    • NtTerminateProcess->PspTerminateProcess(系统未导出的函数)→PspTerminateThreadByPointer ->PspExitThread(杀进程最终变成杀线程)
  • 特征值获取方法:
    • windbg链接上目标系统,加载上系统符号
    • 然后使用:dd函数名L4就可以获得前16个字节的特征值
  • 进入内核地址空间:
    • NtQueryXXX(win8之后版本好像不支持了)或者AuxKlibQueryModulelnformation查询到内核的起始地址大小从而确定ntosknlEndAddrntosknlBase
  • 暴力搜索特征值
1
2
3
4
5
6
/// 在内核空间中暴力搜索,找到16个字节的特征码
/// xp上的特征码
    0x8B55ff8B
    0xA16456EC
    0x00000124
    0x3B08758B
  • 思路2:往进程里插入APC(异步过程调用(APC :Asynchronous Procedure Call)
    @todo
  • 思路3:内存空间填0
    @todo

    内核层实现

1
2
3
4
5
6
7
8
/// ioctlcmd.h
#define FILE_DEVICE_SWAP     0x0000800a
 
//IOCTL_CODE
 
#define IOCTL_PROC_KILL   (ULONG) CTL_CODE(FILE_DEVICE_SWAP, 0x8009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
 
#define IOCTL_TRANSFER_TYPE( _iocontrol)   (_iocontrol & 0x3)
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
/// 驱动代码
#include <ntddk.h>
#include <ntimage.h>
#include <ntdef.h>
#include "Ioctlcmd.h"
 
const WCHAR deviceLinkBuffer[]  = L"\\DosDevices\\KillProc";
const WCHAR deviceNameBuffer[]  = L"\\Device\\KillProc";
 
typedef NTSTATUS (*NTQUERYSYSTEMINFORMATION)(
 
        IN ULONG                        SystemInformationClass,
        OUT PVOID                        SystemInformation,
        IN ULONG                        SystemInformationLength,
        OUT PULONG                        ReturnLength OPTIONAL  );
typedef unsigned long DWORD;   
NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;
#define    SystemModuleInformation    11   
typedef struct _SYSTEM_MODULE_INFORMATION
{
        ULONG  Reserved[2];
        PVOID  Base;
        ULONG  Size;
        ULONG  Flags;
        USHORT Index;
        USHORT Unknown;
        USHORT LoadCount;
        USHORT ModuleNameOffset;
        CHAR   ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
 
PDEVICE_OBJECT g_HookDevice;
NTSTATUS  PsLookupProcessByProcessId(ULONG ProcessId,PEPROCESS *Process);
 
typedef  NTSTATUS  (*PSPTERPROC) ( PEPROCESS Process, NTSTATUS ExitStatus );
PSPTERPROC MyPspTerminateProcess = NULL ;
 
 
NTSTATUS OnUnload(IN PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING          deviceLinkUnicodeString;
    PDEVICE_OBJECT       p_NextObj;
 
 
    DbgPrint("OnUnload called\n");
 
    p_NextObj = DriverObject->DeviceObject;
 
    if (p_NextObj != NULL)
    {
 
        RtlInitUnicodeString( &deviceLinkUnicodeString, deviceLinkBuffer );
        IoDeleteSymbolicLink( &deviceLinkUnicodeString );
 
        IoDeleteDevice( DriverObject->DeviceObject );
    }
    return STATUS_SUCCESS;
}
 
NTSTATUS
DispatchControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp)
{
    PIO_STACK_LOCATION      irpStack;
    PVOID                   inputBuffer;
    PVOID                   outputBuffer;
    PVOID                 userBuffer;
    ULONG                   inputBufferLength;
    ULONG                   outputBufferLength;
    ULONG                   ioControlCode;
    NTSTATUS             ntstatus;
 
    unsigned int i;
 
    unsigned total = 0;
    ULONG count = 0;
 
    HANDLE handle;
 
 
    ULONG cnt;
 
    PEPROCESS Eprocess = NULL;
    DWORD pid;
 
 
    ntstatus = Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
 
    irpStack = IoGetCurrentIrpStackLocation (Irp);
 
    inputBuffer             = Irp->AssociatedIrp.SystemBuffer;
    inputBufferLength       = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    outputBuffer            = Irp->AssociatedIrp.SystemBuffer;
    outputBufferLength      = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    ioControlCode           = irpStack->Parameters.DeviceIoControl.IoControlCode;
 
 
 
    switch (irpStack->MajorFunction)
   {
    case IRP_MJ_CREATE:
        break;
 
    case IRP_MJ_SHUTDOWN:
        break;
 
    case IRP_MJ_CLOSE:
        break;
 
    case IRP_MJ_DEVICE_CONTROL:
 
        if(IOCTL_TRANSFER_TYPE(ioControlCode) == METHOD_NEITHER)
    {
            outputBuffer = Irp->UserBuffer;
        }
 
 
    switch (ioControlCode )
    {
 
    case IOCTL_PROC_KILL:
                if(MyPspTerminateProcess==NULL) //没有找到函数,直接返回
                {
                    *(DWORD*)outputBuffer = -1;
                    Irp->IoStatus.Information = sizeof(DWORD);
                }
                else
                {
                    pid = *(DWORD*)inputBuffer;
                    {
 
                        ntstatus = PsLookupProcessByProcessId(pid , &Eprocess);//对Eprocess的引用计数进行了加1,所以后面需要ObDereference对应用计数进行减1
                        if(!NT_SUCCESS(ntstatus))
                        {
                            DbgPrint("Failed to lookup process 0x%x, status %8.8x\n", pid , ntstatus);
                            *(DWORD*)outputBuffer = 1;
                            Irp->IoStatus.Information = sizeof(DWORD);
                            break;
                        }
                        DbgPrint("Lookup of process 0x%x, PEPROCESS at %8.8x\n", pid, Eprocess);
                        ntstatus = MyPspTerminateProcess(Eprocess, 0); //0退出码
                        if(!NT_SUCCESS(ntstatus))
                        {
                            DbgPrint("Failed to terminate process 0x%x, status %8.8x\n", pid, ntstatus);
                            *(DWORD*)outputBuffer = 2;
                            Irp->IoStatus.Information = sizeof(DWORD);
                            break;
                        }
                        *(DWORD*)outputBuffer = 0;
                        Irp->IoStatus.Information = sizeof(DWORD);
                        DbgPrint("Process 0x%x terminated\n", pid);
                    }
                }
                break;
 
 
    default:
            break;
        }
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
 
    }
    return ntstatus; 
}
 
NTSTATUS DispatchCreate (
        IN PDEVICE_OBJECT    pDevObj,
        IN PIRP        pIrp)
{
 
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT );
    return STATUS_SUCCESS;
}
 
ULONG GetFunctionAddr( IN PCWSTR FunctionName)
    {
        UNICODE_STRING UniCodeFunctionName;
 
        RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
        return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName );   //动态获取函数的地址,在某些系统中不一定有要找的函数
 
    }
 
VOID DoFind(IN PVOID pContext)
    {
        NTSTATUS ret;
        PSYSTEM_MODULE_INFORMATION  module = NULL;
        ULONG n=0;
        void  *buf    = NULL;
        ULONG ntosknlBase;
        ULONG ntosknlEndAddr;
        ULONG curAddr;
        ULONG code1_sp3=0x8b55ff8b,code2_sp3=0xA16456EC,code3_sp3=0x00000124,code4_sp3=0x3B08758B; //特征码
        ULONG i;
 
        NtQuerySystemInformation=(NTQUERYSYSTEMINFORMATION)GetFunctionAddr(L"NtQuerySystemInformation");
        if (!NtQuerySystemInformation)
        {
            DbgPrint("Find NtQuerySystemInformation faild!");
            goto Ret;
        }
        ret=NtQuerySystemInformation(SystemModuleInformation,&n,0,&n); //第一次查询模块的大小
        if (NULL==( buf=ExAllocatePoolWithTag(NonPagedPool, n, 'DFSP')))
        {
            DbgPrint("ExAllocatePool() failed\n" );
            goto Ret;
        }
        ret=NtQuerySystemInformation(SystemModuleInformation,buf,n,NULL); //第二次查询,把查询的结果放在buf
        if (!NT_SUCCESS(ret))    {
            DbgPrint("NtQuerySystemInformation faild!");
            goto Ret;
        }
        //+1 reason: The data returned to the SystemInformation buffer is a ULONG count of the number of
        //modules followed immediately by an array of SYSTEM_MODULE_INFORMATION
        module=(PSYSTEM_MODULE_INFORMATION)((PULONG)buf+1); //buf中的前面4Byte表示buf存放模块的个数,内核模块是buf中的第一个模块
        ntosknlEndAddr=(ULONG)module->Base+(ULONG)module->Size;
        ntosknlBase=(ULONG)module->Base;
        curAddr=ntosknlBase;
        ExFreePool(buf);
        //MmIsAddressValid(i) //i可能是无效的,校验一下
        for (i=curAddr;i<=ntosknlEndAddr;i++) //暴力搜索
        {
                if (*((ULONG *)i)==code1_sp3)
                {
                    if (*((ULONG *)(i+4))==code2_sp3)
                    {
                        if (*((ULONG *)(i+8))==code3_sp3)
                        {
                            if (*((ULONG *)(i+12))==code4_sp3)
                            {
                                MyPspTerminateProcess=(PSPTERPROC)i; //地址转换成对应的函数指针
                                break;
                            }
                        }
                    }
                }
        }
Ret:
    PsTerminateSystemThread(STATUS_SUCCESS);
    }
 
VOID GetPspAddr()
{
        HANDLE hThread;
        PVOID objtowait=0;
        NTSTATUS dwStatus =
            PsCreateSystemThread(
            &hThread,
                  0,
               NULL,
            (HANDLE)0,
                  NULL,
               DoFind, //在线程DoFind里面找函数地址
            NULL
            );
        NTSTATUS st;
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
            st=KfRaiseIrql(PASSIVE_LEVEL);//KeLowerIrql()?
 
        }
        if ((KeGetCurrentIrql())!=PASSIVE_LEVEL)
        {
 
            return;
        }
 
        ObReferenceObjectByHandle(
            hThread,
            THREAD_ALL_ACCESS,
            NULL,
            KernelMode,
            &objtowait,
            NULL
            );
 
        st=KeWaitForSingleObject(objtowait,Executive,KernelMode,FALSE,NULL); //NULL表示无限期等待.
        return;
 
 
}
 
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS rc;
 
    RTL_OSVERSIONINFOW osvi;
    NTSTATUS                ntStatus;
    UNICODE_STRING          deviceNameUnicodeString;
    UNICODE_STRING          deviceLinkUnicodeString;  
 
    RtlInitUnicodeString (&deviceNameUnicodeString,
        deviceNameBuffer );
    RtlInitUnicodeString (&deviceLinkUnicodeString,
        deviceLinkBuffer );
 
    ntStatus = IoCreateDevice ( DriverObject,
        0,
        &deviceNameUnicodeString,
        FILE_DEVICE_SWAP,
        0,
        TRUE,
        &g_HookDevice );
 
    if(! NT_SUCCESS(ntStatus))
    {
          DbgPrint(("Failed to create device!\n"));
          return ntStatus;
     }       
    ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
        &deviceNameUnicodeString );
    if(! NT_SUCCESS(ntStatus))
    {
         IoDeleteDevice(DriverObject->DeviceObject);
            DbgPrint("Failed to create symbolic link!\n");
            return ntStatus;
     }
        DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]        =
        DriverObject->MajorFunction[IRP_MJ_CREATE]          =   DispatchCreate;
        DriverObject->MajorFunction[IRP_MJ_CLOSE]           =   
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = DispatchControl;
 
    DriverObject->DriverUnload  = OnUnload;
 
    GetPspAddr(); //驱动启动时找到函数地址
    if(MyPspTerminateProcess == NULL)
    {
        DbgPrint("PspFunc Not Find!\n");
    }
    return STATUS_SUCCESS;
}

应用层实现

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
void CLstProcDlg::OnForceKillproc()
{
    DWORD ret = 0;
    DWORD read;
    if(m_dwPID==-1)
    {
        MessageBox("请选择进程");
        return;
    }
    if(MessageBox(_T("强杀进程可能会造成系统不稳定,确认要进行吗?"),_T("强杀进程"),MB_YESNO)==IDYES)
    {
        InitDriver();
        Initialized = FALSE;
        DeviceIoControl(gh_Device,
                        IOCTL_PROC_KILL,
                        &m_dwPID,
                        sizeof(m_dwPID),
                        &ret,
                        sizeof(ret),
                        &read,
                        NULL);
        CloseHandle(gh_Device);
        UnloadDeviceDriver(ac_driverName);
        if(ret==0)
            MessageBox(_T("成功执行"));
        else
            MessageBox(_T("执行失败"));
        CloseHandle(gh_Device);
        ShellExecute(NULL, "open", "sc","stop LstProc", NULL, SW_HIDE);
 
 
    }   
}

文件注册表穿越

文件穿越思路

@todo

  • 文件穿越目的:不调用系统提供的函数对文件进行操作,是为绕过这些被木马和病毒hook的函数
  • 思路1:打开文件用IoCreateFile,其他比较好发Irp的(比如删除操作)走FSD irp(文件系统),摘掉过滤设备(Irp下发还会被文件过滤驱动拦截)
  • 思路2:自己实现了所有Nt系列操作文件的功能
  • 思路3:重载一个内核
  • 思路4MJ XCB大法(XCB是FCB、VCB、CCB、SCB 、LCB总称)
    • 当文件从磁盘打开,加载到内存中,会构造一个FCB(文件控制块),

      https://bbs.pediy.com/thread-87741.htm
      https://blog.csdn.net/Shevacoming/article/details/7559078
      《Windows NT File System Iternals》 文件加解密必备

    • 特权方式无法打开(带delete标志),带FlLE_ATTRIBUTE_NORMAL方式打开文件,获得FileObject:
      pSCB=FileObject->FsContext
      pCCB=FileObject->FsContext2
    • 伪造句柄完全被关闭的情形来欺骗系统。CleanUpCount针对一个文件被打开的句柄数,,我们打开文件以后,这个值就是2。伪造为1:
      scb->CleanUpCount=1
      fcb->CleanUpCount=1
      ccb->CleanUpCount=1
    • 修改过CleanUpCount之后,调用NtClose关闭句柄。这样在NtClose下层将调用NtfsRemoveLink()移除硬链接;然后NfsDecrementCleanupCounts()将CleanupCounte减1,这样文件对象的引用计数会降为0,并释放所有关联的SCB。然后调用loRemoveShareAccess()将ShareAccess.Openeount会清零,解锁,独占就被解除了。
  • 思路5:直接磁盘填0(不走文件系统,直接以磁盘的方式打开,直接进行扇区操作
    • MFT(Master File Table)表,定位到文件在磁盘的扇区
    • hDrive =CreateFile("\\\\.\\PHYSICALDRIVE0",GENERIC_READ,FlLE_SHARE_READ|FILE_SHAREWRITE,0,OPEN_EXISTING,0.0);
    • TCHAR_devicename[] = \_T("\\\\.\\C")
    • writefile/readfile
    • CNtsFileSys::ReadSector(ULONGLONG sector, ULONG count, PVOID buffer)
    • WIN/7权限问题:DeviceloControl向逻辑分区发一个FSCTL_LOCK_VOLUME指令,把它锁住,然后就可以WriteFile写扇区了
1
2
3
4
5
6
/// (不太靠谱,只要这个扇区中是个系统盘或者包含page file就会失败)
systerm volume or contains a page file, fails .
/// 在这个卷上有任何一个打开的文件,也会失败。
any open files on the volume,fail.no open files.
/// 所以这种方法只能用在实模式下,做磁盘工具或者备份程序。在保护模式下权限不够
useful for disk utilily  and backup programs.

注册表穿越思路

@todo

  • 思路1:将ntoskrnl.exe (内核)reload到内存中,自己调用无SSDT/Inline hook的注册表操作。CmpCallback(一组用于注册表监控的回调函数)清0,这样调用的操作就不会被监控和被拦截了。
  • 思路2:自已实现ssdt注册表函数;
    • CmpCallback清零
    • 找到Cm***Key函数:通过KCBroutine hook(hook _CM_KEY_CONTROL_BLOCK->KeyHive->GetCellRoutine)栈回溯得到以下函数的地址:
1
2
3
4
5
6
7
b23d0e40 8063578a nt!CmDeleteKey
b23d0e48 8063234a nt!CmEnumerateKey
b23d0e58 80632724 nt!CmQueryValuekey
b23d0e5c 80632170 nt!CmDeletevalueKey
b23d0e60 80633cd2 nt!CmSetvalueKey
b23d0e68 8063578a nt!CmDeleteKey
b23d0e7c 80632422 nt!CmEnumerateValueKey
  • 内存爆搜得到CmpParseKey:
    _CmpCloseKeyObject
    _CmpParseKey
    _CmpQueryKeyName
  • 自己实现ssdt部分直接调用Cm***Key可参考WRK
  • 思路3.reghive注册表解析:

    https://www.52pojie.cn/thread-41492-1-1.html sudami


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//