浅谈DPCHookSSDT和RemoveDPC

  最近学了DPC这一对,把Win7 32位和64位都做了,查阅了大量的资料,并且进行了大量调试,理一下思路,为了后面更好的学习。

   转载请注明出处:http://www.cnblogs.com/littlepear/p/6733145.html

 

 1. 当我们需要定时的完成某项任务时,就需要注册一个DPC定时器了,在Ring3中, CTimer类中有一个SetTimer()函数,对应的驱动中的函数就是KeSetTimer()了。Timer和要执行回调函数结构的DPC都必须初始化, DPC需提供一个回调函数。然后会把KTimer插入到一个timer table的链表中,具体什么样的链表,我们移除的时候再看。

  

typedef struct MY_TIMER_
{
    KDPC        Dpc;
    KTIMER        Timer;
    PKDEFERRED_ROUTINE    func;
    PVOID                private_context;
}MY_TIMER, *PMY_TIMER;


VOID    MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func)
{
    KeInitializeTimer(&(Timer->Timer));        //定时器的Timer和要执行的回调函数结构Dpc都必须先初始化
    KeInitializeDpc(&(Timer->Dpc), Func, Timer);//第三个参数把Timer作为 DeferredContext传给回调函数,更好的封装
    Timer->func = Func;
}

BOOLEAN        MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context)
{
    LARGE_INTEGER        Interval;
    Interval = RtlConvertLongToLargeInteger(-10000 * Ms);    //100ns每unit    1s 负数代表相对时间
    Timer->private_context = context;                    //传一块上下背景文,便于利用
    return KeSetTimer(&(Timer->Timer), Interval, &(Timer->Dpc));
    //            定时器   延后执行的时间     要执行的回调函数结构
    //return KeSetTimerEx(&(Timer->Timer), Interval,1000, &(Timer->Dpc));
}

为了更好的理解KeSetTimer函数,我查了下wrk,下面是wrk的源码。

BOOLEAN
KeSetTimer (
    __inout PKTIMER Timer,
    __in LARGE_INTEGER DueTime,
    __in_opt PKDPC Dpc
    )

/*++

Routine Description:

    This function sets a timer to expire at a specified time. If the timer is
    already set, then it is implicitly canceled before it is set to expire at
    the specified time. Setting a timer causes its due time to be computed,
    its state to be set to Not-Signaled, and the timer object itself to be
    inserted in the timer list.

Arguments:

    Timer - Supplies a pointer to a dispatcher object of type timer.

    DueTime - Supplies an absolute or relative time at which the timer
        is to expire.

    Dpc - Supplies an optional pointer to a control object of type DPC.

Return Value:

    A boolean value of TRUE is returned if the the specified timer was
    currently set. Else a value of FALSE is returned.

--*/

{

    //
    // Set the timer with a period of zero.
    //

    return KeSetTimerEx(Timer, DueTime, 0, Dpc);
}


BOOLEAN
KeSetTimerEx (
    __inout PKTIMER Timer,
    __in LARGE_INTEGER DueTime,
    __in LONG Period,
    __in_opt PKDPC Dpc
    )

/*++

Routine Description:

    This function sets a timer to expire at a specified time. If the timer is
    already set, then it is implicitly canceled before it is set to expire at
    the specified time. Setting a timer causes its due time to be computed,
    its state to be set to Not-Signaled, and the timer object itself to be
    inserted in the timer list.

Arguments:

    Timer - Supplies a pointer to a dispatcher object of type timer.

    DueTime - Supplies an absolute or relative time at which the timer
        is to expire.

    Period - Supplies an optional period for the timer in milliseconds.

    Dpc - Supplies an optional pointer to a control object of type DPC.

Return Value:

    A boolean value of TRUE is returned if the the specified timer was
    currently set. Otherwise, a value of FALSE is returned.

--*/

{

    ULONG Hand;
    BOOLEAN Inserted;
    KIRQL OldIrql;
    BOOLEAN RequestInterrupt;

    ASSERT_TIMER(Timer);

    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

    //
    // Raise IRQL to dispatcher level and lock dispatcher database.
    //
    // Capture the timer inserted status and if the timer is currently
    // set, then remove it from the timer list.
    //

    KiLockDispatcherDatabase(&OldIrql);
    Inserted = Timer->Header.Inserted;
    if (Inserted != FALSE) {
        KiRemoveTreeTimer(Timer);
    }

    //
    // Set the DPC address, set the period, and compute the timer due time.
    // If the timer has already expired, then signal the timer. Otherwise,
    // set the signal state to false and attempt to insert the timer in the
    // timer table.
    //
    // N.B. The signal state must be cleared before it is inserted in the
    //      timer table in case the period is not zero.
    //

    Timer->Dpc = Dpc;
    Timer->Period = Period;
    if (KiComputeDueTime(Timer, DueTime, &Hand) == FALSE) {
        RequestInterrupt = KiSignalTimer(Timer);
        KiUnlockDispatcherDatabaseFromSynchLevel();
        if (RequestInterrupt == TRUE) {
            KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
        }

    } else {
        Timer->Header.SignalState = FALSE;
        KiInsertOrSignalTimer(Timer, Hand);
    }

    KiExitDispatcher(OldIrql);

    //
    // Return boolean value that signifies whether the timer was set or
    // not.
    //

    return Inserted;
}
KeSetTimer

下面是我完整的DPCHookSSDT代码

 1 #include <ntifs.h>
 2 
 3 #define SEC_IMAGE  0x001000000
 4 
 5 
 6 extern
 7 PIMAGE_NT_HEADERS
 8 NTAPI
 9 RtlImageNtHeader(PVOID BaseAddress);
10 
11 extern
12 char* PsGetProcessImageFileName(PEPROCESS EProcess);
13 
14 typedef
15 NTSTATUS(*pfnNtOpenProcess)(
16     _Out_    PHANDLE            ProcessHandle,
17     _In_     ACCESS_MASK        DesiredAccess,
18     _In_     POBJECT_ATTRIBUTES ObjectAttributes,
19     _In_opt_ PCLIENT_ID         ClientId
20     );
21 
22 typedef struct MY_TIMER_
23 {
24     KDPC        Dpc;
25     KTIMER        Timer;
26     PKDEFERRED_ROUTINE    func;
27     PVOID                private_context;
28 }MY_TIMER, *PMY_TIMER;
29 
30 typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE_
31 {
32     PVOID    ServiceTableBase;                    //SSDT(System Service Dispatch Table)服务表的基地址
33     PVOID    ServiceCounterTableBase;            //用于checked builds,包含SSDT中每个服务被调用的次数
34     PVOID    NumberOfServices;                    //服务函数的个数,NumberOfService * 4就是整个地址表的大小    64位 0x191, 32位 0x11c 
35     PVOID    ParamTableBase;                        //SSPT(System Service Parameter Table)的基地址
36 }SYSTEM_SERVICE_DESCRIPTOR_TABLE, *PSYSTEM_SERVICE_DESCRIPTOR_TABLE;
37 
38 BOOLEAN    GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress);
39 BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress);
40 VOID DriverUnload(PDRIVER_OBJECT DriverObject);
41 BOOLEAN    GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex);
42 PVOID
43 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName);
44 BOOLEAN     Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize);
45 ULONG32    CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount);
46 VOID    HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress,
47     PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
48 VOID    HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress);
49 VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
50 NTSTATUS MyOpenProcess(
51     _Out_    PHANDLE            ProcessHandle,
52     _In_     ACCESS_MASK        DesiredAccess,
53     _In_     POBJECT_ATTRIBUTES ObjectAttributes,
54     _In_opt_ PCLIENT_ID         ClientId
55     );
56 VOID WPOFF();
57 VOID WPON();
58 
59 VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress,
60     UCHAR* OldFunctionCode, ULONG32 PatchCodeLength);
61 VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress);
62 VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength);
63 
64 VOID    MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func);
65 
66 BOOLEAN        MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context);
67 
68 VOID CustomDpc(
69     _In_     struct _KDPC *Dpc,
70     _In_opt_ PVOID        DeferredContext,
71     _In_opt_ PVOID        SystemArgument1,
72     _In_opt_ PVOID        SystemArgument2
73     );
DPCHookSSDT.h
  1 #include "NewDPCHookSSDT.h"
  2 #include <ntimage.h>
  3 #include <Ntstrsafe.h>
  4 
  5 //bp NewDPCHookSSDT!DriverEntry
  6 ULONG32   __NtOpenProcessIndex = 0;
  7 PVOID      __ServiceTableBase = NULL;
  8 ULONG32      __OldNtOpenProcessOffset = 0;
  9 pfnNtOpenProcess      __OldNtOpenProcessAddress = NULL;  //这里无所谓,PVOID也可以
 10 BOOLEAN      __IsHook = FALSE;
 11 UCHAR      __OldCode[15] = { 0 };
 12 
 13 MY_TIMER    MyTimer;
 14 
 15 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
 16 {
 17     NTSTATUS    Status = STATUS_UNSUCCESSFUL;
 18     char        szFunctionName[] = "ZwOpenProcess";
 19     ULONG_PTR    SSDTAddress = 0;    //保存整个SSDT基地址            KeServiceDescriptorTable
 20     ULONG32        TempOffset = 0;
 21     DriverObject->DriverUnload = DriverUnload;
 22     
 23 #ifdef _WIN64
 24     if (GetSSDTAddressInWin7_x64(&SSDTAddress) == FALSE)
 25     {
 26         return Status;
 27     }
 28 #else
 29     if (GetSSDTAddressInWin7_x86(&SSDTAddress) == FALSE)
 30     {
 31         return Status;
 32     }
 33 #endif
 34 
 35     DbgPrint("SSDT:%p\r\n", SSDTAddress);                                    //0x23h
 36     if (GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(szFunctionName, &__NtOpenProcessIndex) == FALSE)
 37     {
 38         return Status;
 39     }
 40      
 41     __ServiceTableBase = ((PSYSTEM_SERVICE_DESCRIPTOR_TABLE)SSDTAddress)->ServiceTableBase;
 42 #ifdef _WIN64
 43     __OldNtOpenProcessOffset = ((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]; 
 44     TempOffset = __OldNtOpenProcessOffset >> 4;    //抹掉最右边的一位
 45     __OldNtOpenProcessAddress = (PVOID)((ULONG64)__ServiceTableBase + TempOffset);
 46     HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, 5, __OldCode, 15);
 47 #else
 48     __OldNtOpenProcessAddress = (pfnNtOpenProcess)(((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]);
 49     /*kd > u 0x84016ba1
 50         nt!NtOpenProcess:
 51     84016ba1 8bff            mov     edi, edi
 52         84016ba3 55              push    ebp
 53         84016ba4 8bec            mov     ebp, esp
 54         84016ba6 51              push    ecx
 55         84016ba7 51              push    ecx
 56         84016ba8 64a124010000    mov     eax, dword ptr fs : [00000124h]
 57         84016bae 8a803a010000    mov     al, byte ptr[eax + 13Ah]
 58         84016bb4 8b4d14          mov     ecx, dword ptr[ebp + 14h]*/
 59 
 60     //0x00145dc8
 61 
 62     HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪
 63 #endif
 64     MyTimerInit(&MyTimer, (PKDEFERRED_ROUTINE)CustomDpc);
 65     MyTimerSet(&MyTimer, 1000, NULL);
 66     return STATUS_SUCCESS;
 67 }
 68 
 69 VOID    MyTimerInit(PMY_TIMER Timer, PKDEFERRED_ROUTINE Func)
 70 {
 71     KeInitializeTimer(&(Timer->Timer));        //定时器的Timer和要执行的回调函数结构Dpc都必须先初始化
 72     KeInitializeDpc(&(Timer->Dpc), Func, Timer);//第三个参数把Timer作为 DeferredContext传给回调函数,更好的封装
 73     Timer->func = Func;
 74 }
 75 
 76 BOOLEAN        MyTimerSet(PMY_TIMER Timer, LONG Ms, PVOID context)
 77 {
 78     LARGE_INTEGER        Interval;
 79     Interval = RtlConvertLongToLargeInteger(-10000 * Ms);    //100ns每unit    1s 负数代表相对时间
 80     Timer->private_context = context;                    //传一块上下背景文,便于利用
 81     return KeSetTimer(&(Timer->Timer), Interval, &(Timer->Dpc));
 82     //            定时器   延后执行的时间     要执行的回调函数结构
 83     //return KeSetTimerEx(&(Timer->Timer), Interval,1000, &(Timer->Dpc));
 84 }
 85 
 86 VOID CustomDpc(
 87     _In_     struct _KDPC *Dpc,
 88     _In_opt_ PVOID        DeferredContext,
 89     _In_opt_ PVOID        SystemArgument1,
 90     _In_opt_ PVOID        SystemArgument2
 91     )
 92 {
 93     PMY_TIMER  Timer = (PMY_TIMER)DeferredContext;
 94     PVOID        MyContext = Timer->private_context;
 95     DbgPrint("CustomDpc()\r\n");
 96 #ifdef _WIN64
 97     HookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, MyOpenProcess, KeBugCheckEx, 5, __OldCode, 15);
 98 #else
 99     HookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, (ULONG32)MyOpenProcess);//强制转换后,MyOpenProcess才可以传,真奇怪
100 #endif // _WIN64
101     MyTimerSet(Timer, 1000, MyContext);
102     //如果前面是KeSetTimerEx函数,这里就不用写MyTimerSet啦
103 }
104 
105 VOID DriverUnload(PDRIVER_OBJECT DriverObject)
106 {
107     DbgPrint("ByeBye,Driver!\r\n");
108 
109     KeCancelTimer(&(MyTimer.Timer));
110 
111     if (__IsHook)
112     {
113 #ifdef _WIN64
114 
115         UnHookSSDTInWin7_x64(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessOffset, KeBugCheckEx,
116             __OldCode, 15);
117 #else
118         UnHookSSDTInWin7_x86(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessAddress);
119 #endif
120         __IsHook = FALSE;
121     }
122 }
123 
124 BOOLEAN    GetSSDTAddressInWin7_x64(ULONG64* SSDTAddress)
125 {
126     //rdmsr c0000082
127     CHAR*    StartSearchAddress = (char*)__readmsr(0xc0000082);            
128     CHAR*    EndSearchAddress = StartSearchAddress + PAGE_SIZE; //4KB
129     CHAR*    i = NULL;
130     UCHAR    v1 = 0, v2 = 0, v3 = 0;
131     //必须用UCHAR类型,因为Char的范围是 -128~127   0x80-0x7F
132     INT64    Offset = 0;
133     *SSDTAddress = NULL;
134     for (i = StartSearchAddress; i < EndSearchAddress; i++)
135     {
136         if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
137         {
138             v1 = *i;
139             v2 = *(i + 1);
140             v3 = *(i + 2);
141             if (v1 == 0x4c && v2 == 0x8d && v3 == 0x15)
142             {
143                 // 4c8d15238f4700
144                 memcpy(&Offset, i+3, 4);
145                 *SSDTAddress = Offset + (ULONG64)i+7;
146                 break;
147             }
148         }
149     }
150     if (*SSDTAddress == NULL)
151     {
152         return FALSE;
153     }
154     return TRUE;
155 }
156 
157 BOOLEAN GetSSDTAddressInWin7_x86(ULONG32* SSDTAddress)
158 {
159     //32位下Ntoskrnl.exe有导出 KeServiceDescriptorTable,直接查找即可
160     *SSDTAddress = NULL;
161     *SSDTAddress = (ULONG32)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeServiceDescriptorTable");
162     if (*SSDTAddress != NULL)
163     {
164         return TRUE;
165     }
166     return FALSE;
167 }
168 PVOID
169 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName)
170 {
171     //通过导出变量名字从NtosKrnl中获得导出变量地址
172     UNICODE_STRING    uniVariableName;
173     PVOID            VariableAddress = NULL;
174 
175     if (wzVariableName&&wcslen(wzVariableName) > 0)
176     {
177         RtlUnicodeStringInit(&uniVariableName, wzVariableName);
178         //从Ntos模块的导出表中获得导出变量的地址
179         VariableAddress = MmGetSystemRoutineAddress(&uniVariableName);
180     }
181     return VariableAddress;
182 }
183 //从内存中读出
184 BOOLEAN    GetSSDTFunctionIndexFromExportTableOfNtdllByFunctionName(CHAR* szFuntionName, ULONG32* SSDTFunctionIndex)
185 {
186     WCHAR            szFileFullPath[] = L"\\SystemRoot\\System32\\ntdll.dll";    //C:\Windows\ 
187     ULONG_PTR        MappingViewSize = 0;
188     PVOID            MappingBaseAddress = 0;
189     BOOLEAN            IsOk = FALSE;
190     PIMAGE_NT_HEADERS    Image_Nt_Headers = NULL;
191     PIMAGE_EXPORT_DIRECTORY        ImageExportDirectory = NULL;    //导出表
192     //因为不识别DWORD,所以用UINT32
193     UINT32*            AddressOfFunctions = NULL;
194     UINT32*            AddressOfNames = NULL;
195     UINT16*            AddressOfNameOrdinals = NULL;
196     int                i = 0;
197     char*            v1 = NULL;
198     ULONG32            OrdinalOfFunction = 0;
199     PVOID            AddressOfFunction = 0;
200 #ifdef _WIN64
201     /*
202     0:004> u zwopenprocess
203     ntdll!NtOpenProcess:
204     00000000`774ddc10 4c8bd1          mov     r10,rcx
205     00000000`774ddc13 b823000000      mov     eax,23h
206     00000000`774ddc18 0f05            syscall
207     00000000`774ddc1a c3              ret
208     00000000`774ddc1b 0f1f440000      nop     dword ptr [rax+rax]
209     */
210     ULONG32            Offset_SSDTFunctionIndex = 4;
211 #else
212     /*
213     0:004> u zwopenprocess
214     ntdll!NtOpenProcess:
215     00000000`774ddc10 4c8bd1          mov     r10,rcx
216     00000000`774ddc13 b823000000      mov     eax,23h
217     00000000`774ddc18 0f05            syscall
218     00000000`774ddc1a c3              ret
219     00000000`774ddc1b 0f1f440000      nop     dword ptr [rax+rax]
220     */
221     ULONG32            Offset_SSDTFunctionIndex = 1;
222 #endif
223     
224 
225     *SSDTFunctionIndex = -1;
226     IsOk = Ring0MappingPEFile(szFileFullPath, &MappingBaseAddress, &MappingViewSize);
227     if (IsOk == FALSE)
228     {
229         return FALSE;
230     }
231     else
232     {
233         __try {
234             Image_Nt_Headers = RtlImageNtHeader(MappingBaseAddress);    //extern进来
235             if (Image_Nt_Headers&&Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
236             {
237                 ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((char*)MappingBaseAddress
238                     + Image_Nt_Headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
239 
240                 AddressOfFunctions = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfFunctions);
241                 //函数的地址(RVA)
242                 AddressOfNames = (UINT32*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNames);;
243                 //函数名字的地址(RVA)
244                 AddressOfNameOrdinals = (UINT16*)((char*)MappingBaseAddress + ImageExportDirectory->AddressOfNameOrdinals);
245                 //有名字的序号的首地址(RVA)
246                 for (i = 0; i < ImageExportDirectory->NumberOfNames; i++)
247                 {
248                     v1 = (char*)((char*)MappingBaseAddress + AddressOfNames[i]); //可以这样写 ImageExportDirectory->AddressOfNames + i * 4
249                     if (_stricmp(szFuntionName, v1) == 0)
250                     {
251                         OrdinalOfFunction = AddressOfNameOrdinals[i];
252                         AddressOfFunction = (PVOID)((char*)MappingBaseAddress + AddressOfFunctions[OrdinalOfFunction]);//AddressOfFunctions[AddressOfNameOrdinals[i]]
253                         *SSDTFunctionIndex = *(ULONG32*)((char*)AddressOfFunction + Offset_SSDTFunctionIndex);
254                         break;
255                     }
256                 }
257             }
258         }
259         __except(EXCEPTION_EXECUTE_HANDLER)
260         {}
261 
262     }
263     ZwUnmapViewOfSection(NtCurrentProcess(), MappingBaseAddress);
264     if (*SSDTFunctionIndex == -1)
265     {
266         return FALSE;
267     }
268     return TRUE;
269 }
270                             //函数需要,所以传双字            二维指针
271 BOOLEAN     Ring0MappingPEFile(WCHAR* szFileFullPath, PVOID* MappingBaseAddress, ULONG_PTR* MappingViewSize)
272 {
273     NTSTATUS            Status;
274     UNICODE_STRING        Temp;
275     OBJECT_ATTRIBUTES    oa;
276     HANDLE                hFile = NULL;
277     HANDLE                hSection = NULL;
278     IO_STATUS_BLOCK        IoStatusBlock;
279 
280     if (!szFileFullPath&&MmIsAddressValid(szFileFullPath))
281     {
282         return FALSE;
283     }
284     if (!MappingBaseAddress&&MmIsAddressValid(MappingBaseAddress))
285     {
286         return FALSE;
287     }
288     RtlUnicodeStringInit(&Temp, szFileFullPath);
289     InitializeObjectAttributes(&oa,    //out
290         &Temp,                    //in ObjectName
291         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,//被系统在和ObjectName比较匹配时不区分大小写||此句柄只能用于内核模式下
292         NULL,                    //与ObjectName参数匹配的根目录对象,如果ObjectName是对象的全路径则设置此参数为NULL
293         NULL                    //驱动程序可以使用NULL用于创建一个具有默认安全描述符的对象
294         );
295     //打开文件,获得文件句柄
296     Status = IoCreateFile(&hFile,
297         GENERIC_READ | SYNCHRONIZE, //可读|同步
298         &oa,
299         &IoStatusBlock,            //返回I/O请求的完成情况
300         NULL,
301         FILE_ATTRIBUTE_NORMAL,
302         FILE_SHARE_READ,
303         FILE_OPEN,
304         FILE_SYNCHRONOUS_IO_NONALERT,
305         NULL,
306         0,
307         CreateFileTypeNone,
308         NULL,
309         IO_NO_PARAMETER_CHECKING
310         );
311     if (!NT_SUCCESS(Status))
312     {
313         return    FALSE;
314     }
315     oa.ObjectName = NULL;
316     Status = ZwCreateSection(&hSection,
317         SECTION_QUERY | SECTION_MAP_READ,
318         &oa,
319         NULL,
320         PAGE_WRITECOPY,
321         SEC_IMAGE,//内存按1M对齐 0x1000 000
322         hFile
323         );
324     ZwClose(hFile);
325     if (!NT_SUCCESS(Status))
326     {
327         return FALSE;
328     }
329     Status = ZwMapViewOfSection(hSection,
330         NtCurrentProcess(),
331         MappingBaseAddress,
332         0,
333         0,
334         0,
335         MappingViewSize,
336         ViewUnmap,            //不能被映射到子进程中
337         0,
338         PAGE_WRITECOPY
339         );
340     ZwClose(hSection);
341     if (!NT_SUCCESS(Status))
342     {
343         return FALSE;
344     }
345     return TRUE;
346 }
347 
348 ULONG32    CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParamterCount)
349 {
350     //完全没必要像下面这样折腾,就是一个做个SSDT表中类似的地址,有时间自己写一个
351     ULONG32  Offset = 0;
352     CHAR     Temp = 0;
353     CHAR     Bits[4] = { 0 };
354     int      i = 0;
355     Offset = (ULONG32)((ULONG64)FunctionAddress - (ULONG64)ServiceTableBase);
356     Offset = Offset << 4;
357     if (ParamterCount > 4)
358     {
359         ParamterCount = ParamterCount-4;     //NtReadFile  9个参数  和参数压栈有关
360     }
361     else
362     {
363         ParamterCount = 0;
364     }                                    
365     memcpy(&Temp, &Offset, 1);        // 1010 0010    <<4-----> 0010 0000 后面处理参数
366 #define SETBIT(x,y) x|=(1<<y)         //将X的第Y位置1
367 #define CLRBIT(x,y) x&=~(1<<y)        //将X的第Y位清0
368 #define GETBIT(x,y) (x & (1 << y))    //取X的第Y位,返回0或非0
369 
370     for (i = 0; i < 4; i++)
371     {
372         Bits[i] = GETBIT(ParamterCount, i);
373         if (Bits[i])
374         {
375             SETBIT(Temp, i);
376         }
377         else
378         {
379             CLRBIT(Temp, i);
380         }
381     }
382     memcpy(&Offset, &Temp, 1);
383     return Offset;
384 }
385 
386 //write protect 第17位
387 VOID WPOFF()
388 {
389     _disable();
390     __writecr0(__readcr0() & (~(0x10000)));
391 
392 }
393 
394 VOID WPON()
395 {
396     __writecr0(__readcr0() ^ 0x10000);
397     _enable();
398 }
399 
400 VOID    HookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID MyFunctionAddress,
401     PVOID OldFuntionAddress, ULONG32 OldFunctionParamterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
402 {
403     ULONG32        MyOffset = 0;
404     WPOFF();
405     InlineHook(OldFuntionAddress, MyFunctionAddress, OldFunctionCode, PatchCodeLength);
406     WPON();
407 
408     MyOffset = CalcFunctionOffsetInSSDT(ServiceTableBase, OldFuntionAddress, OldFunctionParamterCount);
409     WPOFF();
410     ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = MyOffset;
411     WPON();
412 
413     __IsHook = TRUE;
414 }
415 
416 VOID    HookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 MyFunctionAddress)
417 {
418     WPOFF();
419     ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)MyFunctionAddress;
420     WPON();
421 
422     __IsHook = TRUE;
423 }
424 
425 VOID InlineHook(ULONG64 OldFuntionAddress, ULONG64 MyFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
426 {
427     CHAR PatchCode[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";  //神坑啊,脑残啊,写成PatchCode 到memcpy才崩溃
428     ULONG64 Temp = MyFunctionAddress;
429     memcpy(OldFunctionCode, (PVOID)OldFuntionAddress, PatchCodeLength); //保存原来的函数指令
430     memcpy(PatchCode + 6, &Temp, 8);
431     memset((PVOID)OldFuntionAddress, 0x90, PatchCodeLength);  //先全部打上NOP(计组)
432     memcpy((PVOID)OldFuntionAddress, PatchCode, 14);
433 }
434 
435 
436 //SSDT HOOK的替换函数如何对访问进行过滤”比“如何实现SSDT HOOK”要复杂多了,详见ediary   HookNtOpenProcess后的事情
437 NTSTATUS MyOpenProcess(
438     _Out_    PHANDLE            ProcessHandle,
439     _In_     ACCESS_MASK        DesiredAccess,
440     _In_     POBJECT_ATTRIBUTES ObjectAttributes,
441     _In_opt_ PCLIENT_ID         ClientId
442     )
443 {
444     //EnumProcessByForce.exe
445     //调用OpenProcess---->去NtDll导出表中寻找ntdll!NtOpenProcess或者ntdll!ZwOpenProcess 导出表有名称的索引---->切入内核-->跳板nt!ZwOpenProcess  Ntoskernl.exe(NtOpenProcess mov eax,23h SSDT[23h])
446     // ---->KeCheckBugEx ---->Jmp MyOpenProcess
447     PEPROCESS    EProcess = PsGetCurrentProcess(); //EnumProcessByForce.exe
448     if (EProcess != NULL && MmIsAddressValid(EProcess))
449     {
450         char *szProcessImageFileName = PsGetProcessImageFileName(EProcess);
451         if (strstr(szProcessImageFileName, "EnumProcess") != 0)
452         {
453             return STATUS_ACCESS_DENIED;    //黑名单中返回
454         }
455     }
456     __OldNtOpenProcessAddress(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); //白名单
457                                                     //OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, i)
458     //return STATUS_SUCCESS;      死循环
459     //return STATUS_UNSUCCESSFUL;  阻塞      很有趣
460 }
461 
462 VOID UnHookSSDTInWin7_x64(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress,
463     UCHAR* OldFunctionCode, ULONG32 PatchCodeLength)
464 {
465     WPOFF();
466     UnlineHook(OldFunctionAddress, OldFunctionCode, PatchCodeLength);
467     WPON();
468 
469     WPOFF();
470     ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionOffset;
471     WPON();
472 }
473 
474 VOID UnlineHook(PVOID OldFunctionAddress, CHAR* OldFunctionCode, ULONG32 PatchCodeLength)
475 {
476     memcpy((PVOID)OldFunctionAddress, OldFunctionCode, PatchCodeLength);
477 }
478 
479 VOID UnHookSSDTInWin7_x86(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionAddress)
480 {
481     WPOFF();
482     ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionAddress;
483     WPON();
484 }
DPCHookSSDT.c

 

2. 接下来就是RemoveDPC了,首先我们要找到DPC,关于Win7以前的版本可以参考这份博客,讲解的很详细,http://bbs.pediy.com/thread-148135.htm

win7下定时器链表被放在KPRCB结构中,64位下,在KPRCB+0x2200的位置:

相关结构:

所以大体流程就是:取KPRCB地址,+0x2200偏移得到_KTIMER_TABLE,+0x200偏移得到_KTIMER_TABLE.TimerEntries
最后遍历_KTIMER_TABLE.TimerEntries.Entry 。32位下类似,只是偏移不同。

那么现在关键是获得KPRCB地址,那怎么获得呢?

每一个CPU中都有一个KPRCB结构地址,它保存在KiProcessorBlock全局变量中。

 而KiProcessorBlock在KdDebuggerData64这个结构中,KdDebuggerData64可以从KdVersionBlock中得到。方法如下:

KdVersionBlock在KPCR中位置如图所示,这里需要注意,对于多处理器的环境下,如果想正确获取KdVersionBlock的值,必须保证当前的线程运行在1号处理器上。

具体原因可查看这份博客:http://www.youranshare.com/push/code/win-c-cpp/439.html

然后查看KdVersionBlock结构,最下面的便是KdDebuggerData64结构。

从wrk上查看KdDebuggerData64结构体的定义,发现KiProcessorBlock在0x218h的偏移处。

  1 DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!!
  2 //
  3 // If you remove a field, replace it with an "unused" placeholder.
  4 // Do not reuse fields until there has been enough time for old debuggers
  5 // and extensions to age out.
  6 //
  7 typedef struct _KDDEBUGGER_DATA64 {
  8 
  9     DBGKD_DEBUG_DATA_HEADER64 Header;
 10 
 11     //
 12     // Base address of kernel image
 13     //
 14 
 15     ULONG64   KernBase;
 16 
 17     //
 18     // DbgBreakPointWithStatus is a function which takes an argument
 19     // and hits a breakpoint.  This field contains the address of the
 20     // breakpoint instruction.  When the debugger sees a breakpoint
 21     // at this address, it may retrieve the argument from the first
 22     // argument register, or on x86 the eax register.
 23     //
 24 
 25     ULONG64   BreakpointWithStatus;       // address of breakpoint
 26 
 27     //
 28     // Address of the saved context record during a bugcheck
 29     //
 30     // N.B. This is an automatic in KeBugcheckEx's frame, and
 31     // is only valid after a bugcheck.
 32     //
 33 
 34     ULONG64   SavedContext;
 35 
 36     //
 37     // help for walking stacks with user callbacks:
 38     //
 39 
 40     //
 41     // The address of the thread structure is provided in the
 42     // WAIT_STATE_CHANGE packet.  This is the offset from the base of
 43     // the thread structure to the pointer to the kernel stack frame
 44     // for the currently active usermode callback.
 45     //
 46 
 47     USHORT  ThCallbackStack;            // offset in thread data
 48 
 49     //
 50     // these values are offsets into that frame:
 51     //
 52 
 53     USHORT  NextCallback;               // saved pointer to next callback frame
 54     USHORT  FramePointer;               // saved frame pointer
 55 
 56     //
 57     // pad to a quad boundary
 58     //
 59     USHORT  PaeEnabled:1;
 60 
 61     //
 62     // Address of the kernel callout routine.
 63     //
 64 
 65     ULONG64   KiCallUserMode;             // kernel routine
 66 
 67     //
 68     // Address of the usermode entry point for callbacks.
 69     //
 70 
 71     ULONG64   KeUserCallbackDispatcher;   // address in ntdll
 72 
 73 
 74     //
 75     // Addresses of various kernel data structures and lists
 76     // that are of interest to the kernel debugger.
 77     //
 78 
 79     ULONG64   PsLoadedModuleList;
 80     ULONG64   PsActiveProcessHead;
 81     ULONG64   PspCidTable;
 82 
 83     ULONG64   ExpSystemResourcesList;
 84     ULONG64   ExpPagedPoolDescriptor;
 85     ULONG64   ExpNumberOfPagedPools;
 86 
 87     ULONG64   KeTimeIncrement;
 88     ULONG64   KeBugCheckCallbackListHead;
 89     ULONG64   KiBugcheckData;
 90 
 91     ULONG64   IopErrorLogListHead;
 92 
 93     ULONG64   ObpRootDirectoryObject;
 94     ULONG64   ObpTypeObjectType;
 95 
 96     ULONG64   MmSystemCacheStart;
 97     ULONG64   MmSystemCacheEnd;
 98     ULONG64   MmSystemCacheWs;
 99 
100     ULONG64   MmPfnDatabase;
101     ULONG64   MmSystemPtesStart;
102     ULONG64   MmSystemPtesEnd;
103     ULONG64   MmSubsectionBase;
104     ULONG64   MmNumberOfPagingFiles;
105 
106     ULONG64   MmLowestPhysicalPage;
107     ULONG64   MmHighestPhysicalPage;
108     ULONG64   MmNumberOfPhysicalPages;
109 
110     ULONG64   MmMaximumNonPagedPoolInBytes;
111     ULONG64   MmNonPagedSystemStart;
112     ULONG64   MmNonPagedPoolStart;
113     ULONG64   MmNonPagedPoolEnd;
114 
115     ULONG64   MmPagedPoolStart;
116     ULONG64   MmPagedPoolEnd;
117     ULONG64   MmPagedPoolInformation;
118     ULONG64   MmPageSize;
119 
120     ULONG64   MmSizeOfPagedPoolInBytes;
121 
122     ULONG64   MmTotalCommitLimit;
123     ULONG64   MmTotalCommittedPages;
124     ULONG64   MmSharedCommit;
125     ULONG64   MmDriverCommit;
126     ULONG64   MmProcessCommit;
127     ULONG64   MmPagedPoolCommit;
128     ULONG64   MmExtendedCommit;
129 
130     ULONG64   MmZeroedPageListHead;
131     ULONG64   MmFreePageListHead;
132     ULONG64   MmStandbyPageListHead;
133     ULONG64   MmModifiedPageListHead;
134     ULONG64   MmModifiedNoWritePageListHead;
135     ULONG64   MmAvailablePages;
136     ULONG64   MmResidentAvailablePages;
137 
138     ULONG64   PoolTrackTable;
139     ULONG64   NonPagedPoolDescriptor;
140 
141     ULONG64   MmHighestUserAddress;
142     ULONG64   MmSystemRangeStart;
143     ULONG64   MmUserProbeAddress;
144 
145     ULONG64   KdPrintCircularBuffer;
146     ULONG64   KdPrintCircularBufferEnd;
147     ULONG64   KdPrintWritePointer;
148     ULONG64   KdPrintRolloverCount;
149 
150     ULONG64   MmLoadedUserImageList;
151 
152     // NT 5.1 Addition
153 
154     ULONG64   NtBuildLab;
155     ULONG64   KiNormalSystemCall;
156 
157     // NT 5.0 QFE addition
158 
159     ULONG64   KiProcessorBlock;
160     ULONG64   MmUnloadedDrivers;
161     ULONG64   MmLastUnloadedDriver;
162     ULONG64   MmTriageActionTaken;
163     ULONG64   MmSpecialPoolTag;
164     ULONG64   KernelVerifier;
165     ULONG64   MmVerifierData;
166     ULONG64   MmAllocatedNonPagedPool;
167     ULONG64   MmPeakCommitment;
168     ULONG64   MmTotalCommitLimitMaximum;
169     ULONG64   CmNtCSDVersion;
170 
171     // NT 5.1 Addition
172 
173     ULONG64   MmPhysicalMemoryBlock;
174     ULONG64   MmSessionBase;
175     ULONG64   MmSessionSize;
176     ULONG64   MmSystemParentTablePage;
177 
178     // Server 2003 addition
179 
180     ULONG64   MmVirtualTranslationBase;
181 
182     USHORT    OffsetKThreadNextProcessor;
183     USHORT    OffsetKThreadTeb;
184     USHORT    OffsetKThreadKernelStack;
185     USHORT    OffsetKThreadInitialStack;
186 
187     USHORT    OffsetKThreadApcProcess;
188     USHORT    OffsetKThreadState;
189     USHORT    OffsetKThreadBStore;
190     USHORT    OffsetKThreadBStoreLimit;
191 
192     USHORT    SizeEProcess;
193     USHORT    OffsetEprocessPeb;
194     USHORT    OffsetEprocessParentCID;
195     USHORT    OffsetEprocessDirectoryTableBase;
196 
197     USHORT    SizePrcb;
198     USHORT    OffsetPrcbDpcRoutine;
199     USHORT    OffsetPrcbCurrentThread;
200     USHORT    OffsetPrcbMhz;
201 
202     USHORT    OffsetPrcbCpuType;
203     USHORT    OffsetPrcbVendorString;
204     USHORT    OffsetPrcbProcStateContext;
205     USHORT    OffsetPrcbNumber;
206 
207     USHORT    SizeEThread;
208 
209     ULONG64   KdPrintCircularBufferPtr;
210     ULONG64   KdPrintBufferSize;
211 
212     ULONG64   KeLoaderBlock;
213 
214     USHORT    SizePcr;
215     USHORT    OffsetPcrSelfPcr;
216     USHORT    OffsetPcrCurrentPrcb;
217     USHORT    OffsetPcrContainedPrcb;
218 
219     USHORT    OffsetPcrInitialBStore;
220     USHORT    OffsetPcrBStoreLimit;
221     USHORT    OffsetPcrInitialStack;
222     USHORT    OffsetPcrStackLimit;
223 
224     USHORT    OffsetPrcbPcrPage;
225     USHORT    OffsetPrcbProcStateSpecialReg;
226     USHORT    GdtR0Code;
227     USHORT    GdtR0Data;
228 
229     USHORT    GdtR0Pcr;
230     USHORT    GdtR3Code;
231     USHORT    GdtR3Data;
232     USHORT    GdtR3Teb;
233 
234     USHORT    GdtLdt;
235     USHORT    GdtTss;
236     USHORT    Gdt64R3CmCode;
237     USHORT    Gdt64R3CmTeb;
238 
239     ULONG64   IopNumTriageDumpDataBlocks;
240     ULONG64   IopTriageDumpDataBlocks;
241 
242 } KDDEBUGGER_DATA64, *PKDDEBUGGER_DATA64;
KdDebuggerData64

相应的代码:(通常在内核模式下段寄存器FS指向的是KPCR结构,用户模式下指向的是TEB结构)

而我调试64位的时候发现,KdVersionBlock都为null,那我们就不能用上面的方法去得KPRCB了,

不过我们却发现,在msr寄存器的0xC0000101处,存的是KPCR,我们直接_KPCR+偏移0x20就得到KPRCB了。

结构体中的Self是自己的地址,CurrentPrcb是KPRCB的地址。

最后回到正题,我们通过遍历KTIMER_TABLE.TimerEntries.Entry链表,利用CONTAINING_RECORD得到KTIMER结构,判断它的地址是否在我们sys模块地址范围内即可。最后KeCancelTimer掉它。

 

(第一个地址是KTIMER,第二个地址是sys的基地址,判断是否落入范围)

 

代码如下:

 1 #include <ntifs.h>
 2 
 3 typedef struct _KTIMER_TABLE_ENTRY
 4 {
 5     ULONG_PTR            Lock;
 6     LIST_ENTRY        Entry;
 7     ULARGE_INTEGER    Time;
 8 } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;
 9 
10 typedef struct _LDR_DATA_TABLE_ENTRY64
11 {
12     LIST_ENTRY64    InLoadOrderLinks;
13     LIST_ENTRY64    InMemoryOrderLinks;
14     LIST_ENTRY64    InInitializationOrderLinks;
15     PVOID            DllBase;
16     PVOID            EntryPoint;
17     ULONG            SizeOfImage;
18     UNICODE_STRING    FullDllName;
19     UNICODE_STRING     BaseDllName;
20     ULONG            Flags;
21     USHORT            LoadCount;
22     USHORT            TlsIndex;
23     PVOID            SectionPointer;
24     ULONG            CheckSum;
25     PVOID            LoadedImports;
26     PVOID            EntryPointActivationContext;
27     PVOID            PatchInformation;
28     LIST_ENTRY64    ForwarderLinks;
29     LIST_ENTRY64    ServiceTagLinks;
30     LIST_ENTRY64    StaticLinks;
31     PVOID            ContextInformation;
32     ULONG64            OriginalBase;
33     LARGE_INTEGER    LoadTime;
34 } LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
35 
36 
37 
38 typedef struct _LDR_DATA_TABLE_ENTRY32
39 {
40     LIST_ENTRY32 InLoadOrderLinks;
41     LIST_ENTRY32 InMemoryOrderLinks;
42     LIST_ENTRY32 InInitializationOrderLinks;
43     ULONG DllBase;
44     ULONG EntryPoint;
45     ULONG SizeOfImage;
46     UNICODE_STRING32 FullDllName;
47     UNICODE_STRING32 BaseDllName;
48     ULONG Flags;
49     USHORT LoadCount;
50     USHORT TlsIndex;
51     union {
52         LIST_ENTRY32 HashLinks;
53         struct {
54             ULONG SectionPointer;
55             ULONG  CheckSum;
56         };
57     };
58     union {
59         struct {
60             ULONG  TimeDateStamp;
61         };
62         struct {
63             ULONG LoadedImports;
64         };
65     };
66 } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
67 
68 
69 #ifdef _WIN64
70 #define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY64
71 #define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY64
72 #else
73 #define LDR_DATA_TABLE_ENTRY LDR_DATA_TABLE_ENTRY32
74 #define PLDR_DATA_TABLE_ENTRY PLDR_DATA_TABLE_ENTRY32
75 #endif
76 
77 VOID DriverUnload(PDRIVER_OBJECT DriverObject);
78 
79 NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject, WCHAR* wzKernelModuleName, PVOID* KernelModuleBase, ULONG_PTR* KernelModuleSize);
80 
81 PVOID
82 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName);
83 
84 BOOLEAN    GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways);
85 
86 KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways);
87 
88 PULONG GetKiProcessorBlock();
89 
90 BOOLEAN    GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize, ULONG_PTR* Timer);
91 BOOLEAN    RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize);
RemoveDPC.h
  1 #include "RemoveDPC.h"
  2 #include <ntstrsafe.h>
  3 //bp RemoveDPC!DriverEntry
  4 
  5 PVOID    __KernelModuleBase = NULL;
  6 ULONG_PTR    __KernelModuleSize = 0;
  7 
  8 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
  9 {
 10     NTSTATUS Status = STATUS_SUCCESS;
 11     PDEVICE_OBJECT  DeviceObject = NULL;
 12     DbgPrint("DriverSuccuss\r\n");
 13     DriverObject->DriverUnload = DriverUnload;
 14 
 15     WCHAR        wzKernelModuleName[] = L"NewDPCHookSSDT.sys";
 16     if (GetKernelModuleInformationByKernelModuleName(DriverObject, wzKernelModuleName, &__KernelModuleBase, &__KernelModuleSize) == STATUS_UNSUCCESSFUL)
 17     {
 18         return STATUS_UNSUCCESSFUL;
 19     }
 20     RemoveDPCFromNtos(__KernelModuleBase, __KernelModuleSize);
 21     return Status;
 22 }
 23 
 24 
 25 NTSTATUS GetKernelModuleInformationByKernelModuleName(PDRIVER_OBJECT DriverObject,WCHAR* wzKernelModuleName,PVOID* KernelModuleBase,ULONG_PTR* KernelModuleSize)
 26 {
 27     PLDR_DATA_TABLE_ENTRY    CurrentEntry = NULL;
 28     PLDR_DATA_TABLE_ENTRY    NextEntry = NULL;
 29     if (DriverObject&&MmIsAddressValid(DriverObject))
 30     {
 31         CurrentEntry = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
 32         NextEntry = (PLDR_DATA_TABLE_ENTRY)CurrentEntry->InLoadOrderLinks.Flink;
 33         while (NextEntry != CurrentEntry)
 34         {
 35             if (NextEntry->BaseDllName.Length >= wcslen(wzKernelModuleName))
 36             {
 37                 if (NextEntry->BaseDllName.Buffer
 38                     &&MmIsAddressValid((PVOID)NextEntry->BaseDllName.Buffer)
 39                     && !_wcsnicmp(wzKernelModuleName, (WCHAR*)NextEntry->BaseDllName.Buffer, wcslen(wzKernelModuleName)))  //这里掉坑了,wcsnicmp是比较函数,相等时返回0,
 40                                                                                                                          //再次掉坑,第三个参数表示比较的的字母的个数,如果是0,肯定相等
 41                 {
 42                     DbgPrint("%S\r\n", wzKernelModuleName);
 43                     DbgPrint("%S\r\n", (WCHAR*)NextEntry->BaseDllName.Buffer);
 44                     *KernelModuleBase = NextEntry->DllBase;
 45                     *KernelModuleSize = NextEntry->SizeOfImage;
 46                     return STATUS_SUCCESS;
 47                 }
 48             }
 49             NextEntry = (PLDR_DATA_TABLE_ENTRY)NextEntry->InLoadOrderLinks.Flink;
 50         }
 51     }
 52     return STATUS_UNSUCCESSFUL;
 53 }
 54 
 55 BOOLEAN    RemoveDPCFromNtos(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize)
 56 {
 57     ULONG_PTR  Timer = NULL;
 58     if (GetDPCTimerInfoByModuleInfo(KernelModuleBase, KernelModuleSize, &Timer) == FALSE)
 59     {
 60         return FALSE;
 61     }
 62     if (Timer && MmIsAddressValid(Timer))
 63     {
 64         if (KeCancelTimer(Timer))
 65         {
 66             return TRUE;
 67         }
 68     }
 69     return FALSE;
 70 }
 71 
 72                          
 73 PULONG GetKiProcessorBlock()
 74 {
 75     ULONG* KiProcessorBlock = 0;
 76 
 77     KeSetSystemAffinityThread(1); //使当前线程运行在第一个处理器上  
 78 
 79     _asm
 80     {
 81         push eax
 82         mov  eax, FS:[0x34]; 得到KdVersionBlock的地址
 83             add  eax, 20h; 得到指向DebuggerDataList的地址
 84             mov  eax, [eax]; 得到DebuggerDataList的地址
 85             mov  eax, [eax]; 取出里面的内容,即KdDebuggerData64结构
 86             mov  eax, [eax + 218h]; 取出KiProcessBlock的地址
 87             mov  KiProcessorBlock, eax;放到变量里
 88             pop  eax
 89     }
 90 
 91     KeRevertToUserAffinityThread();
 92 
 93     return KiProcessorBlock;
 94 
 95 }
 96 
 97 BOOLEAN    GetDPCTimerInfoByModuleInfo(PVOID KernelModuleBase, ULONG_PTR KernelModuleSize,ULONG_PTR* Timer)
 98 {
 99     ULONG32 NumberOfProcessors = KeNumberProcessors;        //一般运行在第一个CPU上
100     ULONG_PTR    KPRCB = 0;
101     PUCHAR        TimerEntries = NULL;
102     PULONG_PTR    KiWaitNever = NULL;
103     PULONG_PTR    KiWaitAlways = NULL;
104     PLIST_ENTRY    CurrentEntry = NULL;
105     PLIST_ENTRY    NextEntry = NULL;
106     PKTIMER        MyTimer = NULL;
107     int i = 0;
108     KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
109 #ifdef _WIN64
110 
111     KeSetSystemAffinityThread(1);        //使当前线程运行在第一个CPU上
112     KPRCB = (ULONG64)__readmsr(0xC0000101) + 0x20;    //msr(0xC0000101)处位置存的是KPCR
113     KeRevertToUserAffinityThread();
114     TimerEntries = (PUCHAR)(*(ULONG64*)KPRCB + 0x2200 + 0x200);
115     if (GetKiWaitVariableAddress(&KiWaitNever, &KiWaitAlways) == FALSE)
116     {
117         return FALSE;
118     }
119     for (i = 0; i < 0x100; i++)
120     {
121         CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + 8);
122         NextEntry = CurrentEntry->Blink;
123         if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry))
124         {
125             while (NextEntry != CurrentEntry)
126             {
127                 PKDPC    RealDPC;
128                 MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
129 
130                 RealDPC = TransTimerDPCEx(MyTimer, *KiWaitNever, *KiWaitAlways);
131                 if (MmIsAddressValid(MyTimer) && MmIsAddressValid(RealDPC) && MmIsAddressValid(RealDPC->DeferredRoutine))
132                 {
133                     if ((ULONG64)MyTimer >= (ULONG64)KernelModuleBase && (ULONG64)MyTimer <= (ULONG64)KernelModuleBase + KernelModuleSize)
134                     {
135                         *Timer = (ULONG64)MyTimer;
136                         KeLowerIrql(OldIrql);
137                         return TRUE;
138                     }
139                 }
140                 NextEntry = NextEntry->Blink;
141             }
142         }
143     }
144 #else
145     PULONG                      KiProcessorBlock = NULL;
146     KiProcessorBlock = (PULONG)GetKiProcessorBlock();
147     TimerEntries = (PUCHAR)(*KiProcessorBlock + 0x1960 + 0x40);
148     for (i = 0; i < 0x100; i++)
149     {
150         CurrentEntry = (PLIST_ENTRY)(TimerEntries + sizeof(KTIMER_TABLE_ENTRY)*i + 4);
151         NextEntry = CurrentEntry->Blink;
152         if (MmIsAddressValid(CurrentEntry) && MmIsAddressValid(NextEntry))
153         {
154             while (NextEntry != CurrentEntry)
155             {
156                 MyTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
157 
158                 if (MmIsAddressValid(MyTimer) && MmIsAddressValid(MyTimer->Dpc) && MmIsAddressValid(MyTimer->Dpc->DeferredRoutine))
159                 {
160                     if ((ULONG32)MyTimer >= (ULONG32)KernelModuleBase && (ULONG32)MyTimer <= (ULONG32)KernelModuleBase + KernelModuleSize)
161                     {
162                         *Timer = (ULONG32)MyTimer;
163                         KeLowerIrql(OldIrql);
164                         return TRUE;
165                     }
166                 }
167                 NextEntry = NextEntry->Blink;
168             }
169         }
170     }
171 
172 #endif
173     
174     
175     KeLowerIrql(OldIrql);
176     return FALSE;
177 }
178 
179 
180 
181 KDPC* TransTimerDPCEx(PKTIMER Timer, ULONG64 KiWaitNever, ULONG64 KiWaitAlways)
182 {
183     ULONG64            DPC = (ULONG64)Timer->Dpc;     //Time 
184     DPC ^= KiWaitNever;
185     DPC = _rotl64(DPC, (UCHAR)(KiWaitNever & 0xFF));
186     DPC ^= (ULONG64)Timer;
187     DPC = _byteswap_uint64(DPC);
188     DPC ^= KiWaitAlways;
189     return (KDPC*)DPC;
190 }
191 
192 BOOLEAN    GetKiWaitVariableAddress(PULONG64* KiWaitNever, PULONG64* KiWaitAlways)
193 {
194     /*
195     kd> u kesettimer l 50
196     nt!KeSetTimer:
197     fffff800`03ef10a8 4883ec38        sub     rsp,38h
198     fffff800`03ef10ac 4c89442420      mov     qword ptr [rsp+20h],r8
199     fffff800`03ef10b1 4533c9          xor     r9d,r9d
200     fffff800`03ef10b4 4533c0          xor     r8d,r8d
201     fffff800`03ef10b7 e814000000      call    nt!KiSetTimerEx (fffff800`03ef10d0)
202     fffff800`03ef10bc 4883c438        add     rsp,38h
203     fffff800`03ef10c0 c3              ret
204     fffff800`03ef10c1 90              nop
205     fffff800`03ef10c2 90              nop
206     fffff800`03ef10c3 90              nop
207     fffff800`03ef10c4 90              nop
208     fffff800`03ef10c5 90              nop
209     fffff800`03ef10c6 90              nop
210     fffff800`03ef10c7 90              nop
211     nt!KxWaitForLockChainValid:
212     fffff800`03ef10c8 90              nop
213     fffff800`03ef10c9 90              nop
214     fffff800`03ef10ca 90              nop
215     fffff800`03ef10cb 90              nop
216     fffff800`03ef10cc 90              nop
217     fffff800`03ef10cd 90              nop
218     fffff800`03ef10ce 90              nop
219     fffff800`03ef10cf 90              nop
220     nt!KiSetTimerEx:
221     fffff800`03ef10d0 48895c2408      mov     qword ptr [rsp+8],rbx
222     fffff800`03ef10d5 4889542410      mov     qword ptr [rsp+10h],rdx
223     fffff800`03ef10da 55              push    rbp
224     fffff800`03ef10db 56              push    rsi
225     fffff800`03ef10dc 57              push    rdi
226     fffff800`03ef10dd 4154            push    r12
227     fffff800`03ef10df 4155            push    r13
228     fffff800`03ef10e1 4156            push    r14
229     fffff800`03ef10e3 4157            push    r15
230     fffff800`03ef10e5 4883ec50        sub     rsp,50h
231     fffff800`03ef10e9 488b0518502200  mov     rax,qword ptr [nt!KiWaitNever (fffff800`04116108)]
232     fffff800`03ef10f0 488b1de9502200  mov     rbx,qword ptr [nt!KiWaitAlways (fffff800`041161e0)]
233     */
234     ULONG64        KeSetTimer = 0;
235     PUCHAR        StartSearchAddress = 0;
236     PUCHAR        EndSearchAddress = 0;
237     ULONG64        iOffset = 0;
238     PUCHAR        i = NULL;
239     KeSetTimer = (ULONG64)GetExportVariableAddressFromNtosKrnlExportTableByVariableName(L"KeSetTimer");
240     
241     StartSearchAddress = (PUCHAR)KeSetTimer;
242     EndSearchAddress = StartSearchAddress + 0x500;
243 
244     for (i = StartSearchAddress; i < EndSearchAddress; i++)
245     {
246         if (*i == 0x48 && *(i + 1) == 0x8B && *(i + 2) == 0x05)
247         {
248             memcpy(&iOffset, i + 3, 4);
249             *KiWaitNever = (PULONG64)(iOffset + (ULONG64)i + 7);
250             i = i + 7;
251             memcpy(&iOffset, i + 3, 4);
252             *KiWaitAlways = (PULONG64)(iOffset + (ULONG64)i + 7);
253             return TRUE;
254         }
255     }
256 
257     return FALSE;
258 }
259 
260 PVOID
261 GetExportVariableAddressFromNtosKrnlExportTableByVariableName(WCHAR* wzVariableName)
262 {
263     //通过导出变量名字从NtosKrnl中获得导出变量地址
264     UNICODE_STRING    uniVariableName;
265     PVOID            VariableAddress = NULL;
266 
267     if (wzVariableName&&wcslen(wzVariableName) > 0)
268     {
269         RtlUnicodeStringInit(&uniVariableName, wzVariableName);
270         //从Ntos模块的导出表中获得导出变量的地址
271         VariableAddress = MmGetSystemRoutineAddress(&uniVariableName);
272     }
273     return VariableAddress;
274 }
275 
276 VOID DriverUnload(PDRIVER_OBJECT DriverObject)
277 {
278     DbgPrint("Unload Success\r\n");
279 }
RemoveDPC.c

That's all.

 

posted @ 2017-04-19 22:06  卷珠帘  阅读(684)  评论(0编辑  收藏  举报