DPC Timer

来源:互联网 发布:办公用品公司软件 编辑:程序博客网 时间:2024/05/12 12:14
呼 回家第三天~睡到下午三点,太爽了。起来无聊看看timer

不知道是我构造关键词太烂还是怎么,好像网上说这个的挺少,internals也没提这个。所以一下内容大多是自己鼓捣的,不一定对。

一。使用

定时器的使用是比较简单的...随便抛点代码上来,自己看WDK就能搞定

1.初始化

  1 //测试代码 就图方便用全局变量了,实际使用时可以放到设备扩展里   2 //KTIMER要自己分配内存 常驻非分页内存   3     g_pkt = (PKTIMER)ExAllocatePoolWithTag(NonPagedPool,sizeof(KTIMER),'RM');   4     g_pdpc = (PKDPC)ExAllocatePoolWithTag(NonPagedPool,sizeof(KDPC),'RM');   5   6     KeInitializeDpc(g_pdpc,(PKDEFERRED_ROUTINE )DpcForTimer,NULL);   7   8     KeInitializeTimerEx(g_pkt,NotificationTimer);   9  10     LARGE_INTEGER li={0};  11     li.QuadPart =5000000*-10;  12      KeSetTimer(g_pkt,li,g_pdpc);  132.DPC routine  1 VOID   2 DpcForTimer(   3         IN struct _KDPC  *Dpc,   4         IN PVOID  DeferredContext,   5         IN PVOID  SystemArgument1,   6         IN PVOID  SystemArgument2   7         )   8 {   9     DbgPrint("got it\n");  10     LARGE_INTEGER li={0};  11     li.QuadPart =5000000*-10;  12  13     KeSetTimer(g_pkt,li,g_pdpc);  14  15 }      


每次 settimer 都可以使 5秒后 触发dpc

PS: timer 有分发头 可等待

3.取消定时器

KeCancelTimer(g_pkt);
ExFreePoolWithTag(g_pkt,'RM');
ExFreePoolWithTag(g_pdpc,'RM'); 

二、枚你个举....

dpc定时器内部实现 好像每一版系统都改动不少,网络上已有人做过一些简单的逆向分析...以下讨论以xp为主,最后会说一点vista。

列一下定时器成员 
typedef struct _KTIMER {DISPATCHER_HEADER Header;ULARGE_INTEGER DueTime;LIST_ENTRY TimerListEntry;struct _KDPC *Dpc;LONG Period;} KTIMER, *PKTIMER, *PRKTIMER;



xp下 ,KeSetTimer 大体工作是把KTIMER 挂入全局数组 KiTimerTableListHead。KiTimerTableListHead是LIST_ENTRY的数组,xp下这个数组有0x100个....插入的机制有点类似于HashTable的方式,以ktimer 的成员 DueTime作为key(wrk是这样的,vista也是,xp下好像还有一个参数,没搞懂是什么) 调用KiComputeTimerTableIndex计算出要把定时器对象挂入的index值,然后把KTIMER挂入这个链表,并且把 KITIMER.Header.Inserted 设置为TRUE。

百度到0GiNr里有一同学纠结于此问题,不过他那个有错误哎,而且方法也不好

他的方法大概是:自己先申请一个定时器,然后在KeSetTimer之后, 判断Inserted(他判断的是SignalState,不过这个跟是否受信没关系啊~)为TRUE时 遍历自己KTIMER 找到链表头 就是 KiTimerTableListHead 的某一项,然后向下搜索0x100次 向上搜索0x100次(他搜索的512次,估计是WRK里TIMER_TABLE_SIZE 是512吧 但xp下似乎是0x100 少了一半)。

这样的方法问题就来了:肯定会搜索到非KiTimerTableListHead的地方,在拿那个地方当做是链表头来遍历,很容易造成死循环

一开始我想自己通过DueTime计算出Index 这样就能定位 KiTimerTableListHead 的地址,后来发现xp的计算方法并不像WRK里面的那么和谐,而是要用到2个未导出变量参与计算,自己实现就比较烦琐了

还是自己太弱智,虽然从 KeSetTimer里面定位 KiTimerTableListHead 不太容易 但是从KeUpdateSystemTime定位就很容易

  1 ULONG GetKiTimerList(ULONG addr)   2 {   3 /*   4     KeUpdateSystemTime()+95   8D 0C C5 80 57 48 00                 lea     ecx, _KiTimerTableListHead.anonymous_0[eax*8] ; Load Effective Address   5         KeUpdateSystemTime()+9C   000                 mov     edx, [ecx]   6     KeUpdateSystemTime()+9E   000                 cmp     ecx, edx        ; Compare Two Operands   7         KeUpdateSystemTime()+A0   000                 jz      short loc_46E062 ; Jump if Zero (ZF=1)   8         KeUpdateSystemTime()+A2   000                 cmp     esi, [edx-4]    ; Compare Two Operands   9         KeUpdateSystemTime()+A5   000                 jb      short loc_46E062 ; Jump if Below (CF=1)  10         KeUpdateSystemTime()+A7   000                 ja      short loc_46E082 ; Jump if Above (CF=0 & ZF=0)  11         KeUpdateSystemTime()+A9   000                 cmp     edi, [edx-8]    ; Compare Two Operands  12         KeUpdateSystemTime()+AC   000                 jnb     short loc_46E082 ; Jump if Not Below (CF=0)*/  13  14  15     PBYTE opcode =     (PBYTE)addr;  16     for (int i=0;i<0x150;i++)  17     {  18         if (opcode[i] == 0x8d  19         && opcode[i+1] == 0xc  20         && opcode[i+2]== 0xc5)  21         {  22             return *(PULONG)&opcode[i+3];  23         }  24     }  25     return 0;  26  27 }  28 VOID SearchForDPCTimer(ULONG addr)  29 {  30     PLIST_ENTRY pnode,pnext;  31     ULONG count = 0;  32     KIRQL irql;  33     ULONG routine;  34       35     PMODULES pmodule = GetModuleList();  36       37     irql = KeRaiseIrqlToDpcLevel();  38  39         for (int i=0;i<0x100;i++)  40         {  41             pnode = (PLIST_ENTRY)(addr+i*8);  42             pnext = pnode->Blink;  43             while(pnext!=pnode)  44             {  45                 PKTIMER ptimer = CONTAINING_RECORD(pnext,KTIMER,TimerListEntry);  46                 if (MmIsAddressValid(ptimer)  47                     && MmIsAddressValid(ptimer->Dpc)  48                     && MmIsAddressValid(ptimer->Dpc->DeferredRoutine))  49                 {  50                     routine = (ULONG)ptimer->Dpc->DeferredRoutine;  51                     DbgPrint("kitimer:0x%x addr:0x%x  ",(ULONG)ptimer,routine);  52                     for (int j=0;j<pmodule->dwNumberOfModules;j++)  53                     {  54                         if (routine>(ULONG)pmodule->smi[j].Base  55                         && routine < (ULONG)pmodule->smi[j].Base + pmodule->smi[j].Size)  56                         {  57                             DbgPrint("path %s\n",pmodule->smi[j].ImageName);  58                         }  59                     }  60                       61                     count ++;  62                 }  63                     pnext = pnext->Blink;  64             }  65         }  66  67     KeLowerIrql(irql);  68  69     FreeModList(pmodule);  70     DbgPrint("count:%d\n",count);  71  72 }  73  74 //入口函数  75 extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)  76 {  77     NTSTATUS status;  78  79     //注册驱动调用函数入口  80     pDriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnload;  81     pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]= DriverIOCtrl    ;  82     pDriverObject->MajorFunction[IRP_MJ_CLOSE]= DriverDispatch    ;  83     pDriverObject->MajorFunction[IRP_MJ_CREATE]= DriverDispatch    ;  84     pDriverObject->MajorFunction[IRP_MJ_READ]= DriverDispatch    ;  85     pDriverObject->MajorFunction[IRP_MJ_WRITE]= DriverDispatch    ;  86     //创建设备  87     status = CreateDevice(pDriverObject);  88  89     __asm int 3  90  91     UNICODE_STRING ustrFuncName = RTL_CONSTANT_STRING(L"KeUpdateSystemTime");  92     ULONG addr = (ULONG)MmGetSystemRoutineAddress(&ustrFuncName);  93     addr = GetKiTimerList(addr);  94     SearchForDPCTimer(addr);  95  96     return status;  97 }


效果挺和谐....跟xuetr一样

三、Vista下的情况

具体的机理还没看,貌似变化挺大,有涉及到n个函数。

不过KiTimerTableListHead倒是好找了,KeSetTimerEx 开头就有

KiTimerTableListHead变成了KTIMER_TABLE_ENTRY

0: kd> dt _ktimer_table_entry
nt!_KTIMER_TABLE_ENTRY
+0x000 Entry : _LIST_ENTRY
+0x008 Time : _ULARGE_INTEGER

大小也变成了0x200

IDA F5 KiInitSystem( ):

v3 = &KiTimerTableSize; //vista多了一个这个,值似乎一直是0x200 ida里只有这一处引用 搞不懂为什么
v0 = &KiTimerTableListHead;
v1 = 0x200u; //这个数组大小是 0x200 得到验证 下面操作是初始化链表头 和 Time
do
{
*(v0 + 1) = (int)v0;
*v0 = (int)v0;
*(v0 + 3) = -1;
*(v0 + 2) = 0;
v0 += 4;
--v1;
}
while ( v1 );

因此vista下枚举方法跟xp一样了

知道内核把定时器对象放哪,也就可以检测DPC定时器了

参考资料:
1.高手进阶windows内核定时器之二
2.A Performance Issue in Windows Timer Management?
3.WRK/ReactOS 0.3.10
原创粉丝点击