首页
社区
课程
招聘
[原创]遍历内核中的DpcTimer
发表于: 2012-3-20 11:17 30079

[原创]遍历内核中的DpcTimer

2012-3-20 11:17
30079


其实本篇帖子是对于之前一篇帖子的补完 http://bbs.pediy.com/showthread.php?t=140344

遍历系统内核DpcTimer是一个比较烦人的事情,记得当初弄的时候蓝脸N次
而且作为系统底层内核中的底层,遍历它也不是那么安全的事情,
主要因为不知道有没有相关的系统锁能够让我们在遍历的时候加以利用,
来使我们的操作更加安全,希望有相关信息的朋友能指点我一下,呵呵

当初在遍历DpcTimer的时候网上都没搜到相关信息,
后来对某工具进行了一下逆向,才找到的线索
写这篇帖子之前又搜了一下,已经有零星的介绍,大家可以去搜索一下

废话不多说了,DpcTimer这条链在xp 2k3 win7下的遍历方法都有些许的不同
首先看 win xp

我们还是从已知入手:
我们的驱动可以注册Dpc定时器的,系统接受我们的注册之后就应当会把它放入一个链表中.
所以先看一下注册Dpc定时器的相关函数:
KeInitializeDpc                初始化一个DPC对象
KeSetTimerEx                设置定时器,其中可以加入DPC对象

KeSetTimerEx之后我们的DPC过程就能在指定的时间被调用了,说明已被加入了系统底层链表中
我们在IDA中打开 xp 的ntkrnlpa.exe, 定位到KeSetTimerEx的实现部分:

这里并没有发现有对于系统级全局变量的引用,
不过通过观察名称发现KiInsertTreeTimer应该与插入一个定时器到链表有点关系.看一下:

对于参数进行了一些处理之后调用了KiInsertTimerTable,继续追寻:

这里对全局变量KiInsertTimerTable进行了引用,看名称就是全局的定时器链表了

同时我们在这里也可以发现一些其他的线索,
在调用了KiComputeTimerTableIndex之后直接使用其返回值进行KiTimerTableListHead对应项的索引,
而步进是8,所以KiTimerTableListHead应该就是8字节为大小的结构数组的首地址,
而这个结构数组有多少成员呢?

看一下有那些地方对KiTimerTableListHead进行了引用,在IDA中的KiTimerTableListHead上按x可以看到:

其中有KiInitSystem对其的引用,应该就是对于结构数据的初始化地方了:


这里我们就得到了两个重要的信息:
1是这个结构数组项数为 0x100 = 256
2是这个结构数组每一项都是一个ListEntry结构,因为这里出现了明显的初始化片段

所以我们遍历这256个ListEntry就能得到所有的定时器了,
到现在唯一还需要得知的就是ListEntry上每一个结构的定义形式,
而这个结构在WDK中已经定义好了,就是KeSetTimerEx传入的KTIMER
typedef struct _KTIMER {
	DISPATCHER_HEADER	Header;
	ULARGE_INTEGER		DueTime;	//+0x10
	LIST_ENTRY			TimerListEntry;
	PKDPC				Dpc;		//+0x20
	LONG				Period;		//+0x24
} KTIMER, *PKTIMER, *PRKTIMER;

最后我们查找到KiTimerTableListHead然后遍历就行了,不过上面的查找路线过于曲折了,
再看一下其他地方对KiTimerTableListHead的引用:

KeUpdateSystemTime是内核导出的函数,在这里查找就方便多了:


可以通过搜索特征码来找到它,不过在这里我用了反汇编引擎,呵呵
通过观察,对KiTimerTableListHead的引用的lea指令是在KeUpdateSystemTime中出现的第一个lea指令,
因此利用反汇编引擎得到第一个lea指令地址就能定位到KiTimerTableListHead了

相关代码:
	// 取KeUpdateSystemTime地址
	RtlInitUnicodeString(&destString,(PWCHAR)L"KeUpdateSystemTime");
	Address = (ULONG)MmGetSystemRoutineAddress(&destString);
	if (Address == 0) return 0;

	// 反汇编找到C_LEA<就是 lea 指令>首次出现的地址
	Address = DisAsmFindFirstSpecialInstructionAddress(Address, C_LEA);
	if ( Address == 0 ) return 0;
	if ( *(PUSHORT)Address != 0x0C8D ) return 0;

	Address = *(PULONG)(Address + 3);
	if ( MmIsAddressValidEx((PVOID)Address) == VCS_INVALID ) return 0;

这里插个小广告,反汇编引擎我使用的是自己精简的OD的引擎,各位需要的话在这里可以找到:
http://bbs.pediy.com/showthread.php?t=140587

DisAsmFindFirstSpecialInstructionAddress的实现部分:
ULONG DisAsmFindFirstSpecialInstructionAddress(ULONG BeginAddress, ULONG CmdType)
{
	ULONG		DecodedLength = 0;
	ULONG		dw = 0;
	ULONG		Address = 0;
	Disasm		dis;

	while (TRUE)
	{
		if ( MmIsAddressValidEx((PVOID)(BeginAddress + DecodedLength)) == VCS_INVALID ) return 0;

		dw = DisasmCode((PUCHAR)(BeginAddress + DecodedLength),36,&dis);
		DecodedLength = DecodedLength + dw;
		if ( dis.cmdtype != CmdType ) continue;

		Address = BeginAddress + DecodedLength - dw;		//返回指令地址 此时指向指令
		break;
	}

	if ( MmIsAddressValidEx((PVOID)Address) == VCS_INVALID ) Address = 0;
	return Address;
}


找到KiTimerTableListHead之后遍历即可:

//自定义的回传DPC TIMER结构
typedef struct _MyDpcTimer{
	ULONG	TimerAddress;		//KTIMER结构地址
	ULONG	Period;				//循环间隔
	ULONG	DpcAddress;			//DPC结构地址
	ULONG	DpcRoutineAddress;	//例程地址
}MyDpcTimer,*PMyDpcTimer;

#define MAX_DPCTIMER_COUNT 250

#pragma PAGECODE
static ULONG GetDpcTimerInformation_XP(PVOID* pvBuf)
{
	ULONG	NumberOfTimerTable;
	ULONG	i;
	ULONG	ulCount = 0;
	PLIST_ENTRY	pList = NULL;
	PLIST_ENTRY pNextList = NULL;
	MyDpcTimer	MyDpc;
	PKDPCTIMER	pTimer = NULL;

	NumberOfTimerTable = 0x100;																	//_KTIMER_TABLE_ENTRY数量
	pList = (PLIST_ENTRY)GetDpcTimerListHeadForXp_2K3();										//取得链表头
	if (pList == NULL) return 0;

	*pvBuf = CallExAllocatePoolWithTag(PagedPool,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer),652);		//分配足够大的缓冲
	if (*pvBuf == NULL) return 0;
	RtlZeroMemory(*pvBuf,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer));

	for ( i = 0; i < NumberOfTimerTable; i++, pList++ )											//NumberOfTimerTable 个list
	{
		if ( MmIsAddressValidEx((PVOID)&pList) == VCS_INVALID ) goto __exit1;


		if ( MmIsAddressValidEx((PVOID)pList->Blink) == VCS_INVALID ) continue;					//如果listentry域地址无效,continue
		if ( MmIsAddressValidEx((PVOID)pList->Flink) == VCS_INVALID ) continue;


		for ( pNextList = pList->Blink; pNextList != pList; pNextList = pNextList->Blink )		//遍历blink链
		{
			pTimer = CONTAINING_RECORD(pNextList,KDPCTIMER,TimerListEntry);						//得到结构首
			
			if ( MmIsAddressValid((PVOID)pTimer) &&
				 MmIsAddressValid((PVOID)pTimer->Dpc) &&
				 MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine) &&
				 MmIsAddressValid((PVOID)&pTimer->Period) )										//过滤
			{
			
				RtlZeroMemory(&MyDpc,sizeof(MyDpcTimer));										//准备更新结构信息
				if ( !MmIsAddressValid((PVOID)pTimer) ) break;
				MyDpc.TimerAddress = (ULONG)pTimer;

				if ( !MmIsAddressValid((PVOID)pTimer->Dpc) ) break;
				MyDpc.DpcAddress = (ULONG)pTimer->Dpc;

				if ( !MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine) ) break;
				MyDpc.DpcRoutineAddress = (ULONG)pTimer->Dpc->DeferredRoutine;

				if ( !MmIsAddressValid((PVOID)&pTimer->Period) ) break;
				MyDpc.Period = pTimer->Period;

				RtlMoveMemory( (PVOID)((ULONG)*pvBuf+ulCount*sizeof(MyDpcTimer)),&MyDpc,sizeof(MyDpcTimer) );
				ulCount++;
				
				// 简单退出了,需要更改为重新分配更大的缓冲
				if (ulCount >= MAX_DPCTIMER_COUNT) goto __exit1;
			}

			if ( !MmIsAddressValid(pNextList->Blink) ) break;									//过滤

		}// end for
	}//end for

	return ulCount * sizeof(MyDpcTimer);

__exit1:
	if (*pvBuf != NULL) ExFreePool(*pvBuf);
	*pvBuf = NULL;
	return 0;
}

一直到如上进行了这么多的过滤,最后得到的结果才和xt的差不多了,大家可以根据需要自己修改



虽然 win 2k3 用的人不多,也简单说下,因为有wrk,我们直接在这里搜索KiTimerTableListHead查找线索
于是找到:
#define TIMER_TABLE_SIZE 512

typedef struct _KTIMER_TABLE_ENTRY {
    LIST_ENTRY Entry;
    ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;

extern DECLSPEC_CACHEALIGN KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];

结构数组项数被扩展成 512 个而且每个项添加了Time域,对应的KiInitSystem中的初始化片段:

可见Time.LowPart初始化成0,Time.HighPart初始化成0xFFFFFFFF (没有使用的Entry都会设置为0xFFFFFFFF,可以当作一个过滤条件)

在 2k3 中KiTimerTableListHead在KeSetTimerEx直接被引用,可以在这里查找:


我是使用反汇编引擎查找首个 add 指令来实现的
	RtlInitUnicodeString(&destString,(PWCHAR)L"KeSetTimerEx");
	Address = (ULONG)MmGetSystemRoutineAddress(&destString);
	if (Address == 0) return 0;

	// C_ADD 即表示 add 指令
	Address = DisAsmFindFirstSpecialInstructionAddress(Address, C_ADD);
	if ( Address == 0 ) return 0;

	Address = *(PULONG)(Address + 2);
	if (MmIsAddressValidEx((PVOID)Address) == VCS_INVALID) return 0;


遍历链表:
typedef struct _KTIMER_TABLE_ENTRY_2K3 {
	LIST_ENTRY Entry;
	ULARGE_INTEGER Time;
}KTIMER_TABLE_ENTRY_2K3, *PKTIMER_TABLE_ENTRY_2K3;

#pragma PAGECODE
static ULONG GetDpcTimerInformation_2K3(PVOID* pvBuf)
{
	ULONG						NumberOfTimerTable;
	ULONG						i;
	ULONG						ulCount = 0;
	MyDpcTimer					MyDpc;
	PKDPCTIMER					pTimer = NULL;
	PKTIMER_TABLE_ENTRY_2K3		pTimerTableEntry = NULL;
	PLIST_ENTRY					pNextList = NULL;

	NumberOfTimerTable = 0x200;																	//_KTIMER_TABLE_ENTRY数量
	pTimerTableEntry = (PKTIMER_TABLE_ENTRY_2K3)GetDpcTimerListHeadForXp_2K3();
	if (pTimerTableEntry == NULL) return 0;


	*pvBuf = CallExAllocatePoolWithTag(PagedPool,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer),64152);		//分配足够大的缓冲
	if (*pvBuf == NULL) return 0;
	RtlZeroMemory(*pvBuf,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer));



	for ( i = 0; i < NumberOfTimerTable; i++, pTimerTableEntry++ )								//保存在ntos中的一个结构数组
	{
		if (MmIsAddressValidEx((PVOID)pTimerTableEntry) == VCS_INVALID) goto __exit1;

		if (pTimerTableEntry->Time.HighPart == 0xFFFFFFFF) continue;							//为空的数组高位双字为FFFFFFFF


		if ( MmIsAddressValidEx((PVOID)pTimerTableEntry->Entry.Blink) == VCS_INVALID ) continue;	//如果listentry域地址无效,continue
		if ( MmIsAddressValidEx((PVOID)pTimerTableEntry->Entry.Flink) == VCS_INVALID ) continue;


		for ( pNextList	 = (PLIST_ENTRY)pTimerTableEntry->Entry.Blink;
			  pNextList	!= (PLIST_ENTRY)&pTimerTableEntry->Entry;
			  pNextList	 = pNextList->Blink)
		{
			//取得timer对象
			pTimer = CONTAINING_RECORD(pNextList,KDPCTIMER,TimerListEntry);

			if (!MmIsAddressValid((PVOID)pTimer) || 
				!MmIsAddressValid((PVOID)pTimer->Dpc) || 
				!MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine))
			{
				if ( !MmIsAddressValid(pNextList->Blink) ) break;
				continue;
			}

			while (TRUE)
			{
				RtlZeroMemory(&MyDpc,sizeof(MyDpcTimer));
				if ( !MmIsAddressValid((PVOID)pTimer) ) break;
				MyDpc.TimerAddress = (ULONG)pTimer;

				if ( !MmIsAddressValid((PVOID)pTimer->Dpc) ) break;
				MyDpc.DpcAddress = (ULONG)pTimer->Dpc;

				if ( !MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine) ) break;
				MyDpc.DpcRoutineAddress = (ULONG)pTimer->Dpc->DeferredRoutine;

				if ( !MmIsAddressValid((PVOID)&pTimer->Period) ) break;
				MyDpc.Period = pTimer->Period;

				RtlMoveMemory( (PVOID)((ULONG)*pvBuf+ulCount*sizeof(MyDpcTimer)),&MyDpc,sizeof(MyDpcTimer) );
				ulCount++;
				if (ulCount >= MAX_DPCTIMER_COUNT) break;	

				break;
			}

			if ( !MmIsAddressValid(pNextList->Blink) ) break;

		}//end while
	}// end for

	return ulCount * sizeof(MyDpcTimer);

__exit1:
	if (*pvBuf != NULL) ExFreePool(*pvBuf);
	*pvBuf = NULL;
	return 0;
}


最后说一下 win7 下面的遍历,在 win7 下KiTimerTableListHead被从符号表中抹去了,所以也就没办法通过搜索的方式寻找了
在 win7 下定时器链表被放到了KPRCB结构中,在 win7 7600 和 win7 7601 中KPRCB+0x1960的位置:


相关结构:


win7下_KTIMER_TABLE_ENTRY数量又被调整为256,找到_KTIMER_TABLE.TimerEntries后,
遍历TimerEntries.Entry就行了
所以大体流程就是:取KPRCB地址,+0x1960偏移得到_KTIMER_TABLE,+0x40偏移得到_KTIMER_TABLE.TimerEntries
最后遍历_KTIMER_TABLE.TimerEntries.Entry

需要注意的是KPRCB是每个处理器都拥有一个的,所以需要遍历每一个处理器
KiProcessorBlock是一个未导出的全局变量,指向一个数组,这个数组保存了每一个CPU的KPRCB结构地址
而KiProcessorBlock在KdDebuggerData64这个结构中出现:


KdDebuggerData64可以从KdVersionBlock后面得到,KdVersionBlock又是个啥啊?
http://hi.baidu.com/combojiang/blog/item/bfa7a6d9f1c913ee38012f28.html
combojiang大牛在这里有介绍,其中关于KdDebuggerData64结构的定义可以在wrk中找到 <搜索_KDDEBUGGER_DATA64>

遍历过程:
#define MAX_PROCESSOR_COUNT 32
#pragma PAGECODE
static ULONG GetDpcTimerInformation_WIN7(PVOID* pvBuf)
{
	ULONG						TimerTableOffsetInKprcb;
	ULONG						NumberOfTimerTable;
	ULONG						NumberOfProcessor;
	ULONG						i,j;
	ULONG						ulTemp;
	ULONG						ulCount = 0;
	PULONG						pKiProcessorBlock = NULL;
	PKTIMER_TABLE_ENTRY_WIN7	pTimerTableEntryWin7 = NULL;
	PLIST_ENTRY					pNextList = NULL;
	PKDPCTIMER					pTimer = NULL;
	MyDpcTimer					MyDpc;

	TimerTableOffsetInKprcb = 0x1960+0x40;														//首个_KTIMER_TABLE_ENTRY在PRCB中的偏移
	NumberOfTimerTable = 0x100;																	//_KTIMER_TABLE_ENTRY数量

	NumberOfProcessor = (ULONG)KeNumberProcessors;												//当前机器处理器数量
	if (NumberOfProcessor > MAX_PROCESSOR_COUNT) return 0;


	pKiProcessorBlock = (PULONG)GetKiProcessorBlock();											//取得KiProcessorBlock,包含了NumberOfProcessor个KPRCB
	if (pKiProcessorBlock == NULL) return 0;

	
	*pvBuf = CallExAllocatePoolWithTag(PagedPool,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer),652);		//分配足够大的缓冲
	if (*pvBuf == NULL) return 0;
	RtlZeroMemory(*pvBuf,MAX_DPCTIMER_COUNT*sizeof(MyDpcTimer));


	for ( i = 0; i < NumberOfProcessor; i++, pKiProcessorBlock++ )								//DPC timer在每个cpu中都有一个队列,所以枚举每一个KPRCB
	{
		if ( MmIsAddressValidEx((PVOID)pKiProcessorBlock) == VCS_INVALID ) goto __exit1;		//检测一下当前的KPRCB地址是否可访问


		ulTemp = *pKiProcessorBlock + TimerTableOffsetInKprcb;									//取得当前CPU的KPRCB中首个KTIMER_TABLE_ENTRY地址
		if ( MmIsAddressValidEx((PVOID)ulTemp) == VCS_INVALID )	goto __exit1;


		pTimerTableEntryWin7 = (PKTIMER_TABLE_ENTRY_WIN7)ulTemp;								//此时ulTemp是 timer table entry地址

		for ( j = 0; j < NumberOfTimerTable; j++, pTimerTableEntryWin7++ )						//准备遍历timer table表
		{
			if ( MmIsAddressValidEx((PVOID)pTimerTableEntryWin7) == VCS_INVALID ) goto __exit1;
			if ( pTimerTableEntryWin7->Time.HighPart == 0xFFFFFFFF ) continue;					//为空的数组高位双字为FFFFFFFF


			if ( MmIsAddressValidEx((PVOID)pTimerTableEntryWin7->Entry.Blink) == VCS_INVALID ) continue;
			if ( MmIsAddressValidEx((PVOID)pTimerTableEntryWin7->Entry.Flink) == VCS_INVALID ) continue;


			for ( pNextList	 = (PLIST_ENTRY)pTimerTableEntryWin7->Entry.Blink;
				  pNextList	!= (PLIST_ENTRY)&pTimerTableEntryWin7->Entry;
				  pNextList	 = pNextList->Blink)
			{

				pTimer = CONTAINING_RECORD(pNextList,KDPCTIMER,TimerListEntry);					//取得timer对象

				if (!MmIsAddressValid((PVOID)pTimer)||
					!MmIsAddressValid((PVOID)pTimer->Dpc)||
					!MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine))
				{
					if (!MmIsAddressValid((PVOID)pNextList->Blink)) break;
					continue;
				}


				while (TRUE)
				{
					RtlZeroMemory(&MyDpc,sizeof(MyDpcTimer));
					if ( !MmIsAddressValid((PVOID)pTimer) ) break;
					MyDpc.TimerAddress = (ULONG)pTimer;

					if ( !MmIsAddressValid((PVOID)pTimer->Dpc) ) break;
					MyDpc.DpcAddress = (ULONG)pTimer->Dpc;

					if ( !MmIsAddressValid((PVOID)pTimer->Dpc->DeferredRoutine) ) break;
					MyDpc.DpcRoutineAddress = (ULONG)pTimer->Dpc->DeferredRoutine;

					if ( !MmIsAddressValid((PVOID)&pTimer->Period) ) break;
					MyDpc.Period = pTimer->Period;

					RtlMoveMemory( (PVOID)((ULONG)*pvBuf+ulCount*sizeof(MyDpcTimer)),&MyDpc,sizeof(MyDpcTimer) );
					ulCount++;
					if (ulCount >= MAX_DPCTIMER_COUNT) break;	

					break;
				}

				if (!MmIsAddressValid((PVOID)pNextList->Blink)) break;

			}// end while
		}// end for
	}// end for

	return ulCount * sizeof(MyDpcTimer);

__exit1:
	if (*pvBuf != NULL) ExFreePool(*pvBuf);
	*pvBuf = NULL;
	return 0;
}


大体过程与 2k3是一样的,可以修改一下以兼容两个系统

最后看一下 win7 下的枚举效果:

xt:


能力有限不知道哪里会不会出错,希望各位不吝赐教


[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (29)
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
求附件 凑齐字数
2012-3-20 11:44
0
雪    币: 4580
活跃值: (992)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
不错不错,收藏之
2012-3-20 13:28
0
雪    币: 2506
活跃值: (2323)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
4
lz威武,学习
2012-3-20 13:30
0
雪    币: 2177
活跃值: (2045)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
5
挺详细的,很适合初学者。
2012-3-20 19:37
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习++
2012-3-20 20:44
0
雪    币: 169
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
亲 有附件没?
2012-3-20 21:04
0
雪    币: 1683
活跃值: (674)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
感谢分享~~~~
2012-3-20 21:15
0
雪    币: 2323
活跃值: (4113)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
9
学习,谢谢分享~~
2012-3-20 22:04
0
雪    币: 66
活跃值: (835)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
学习了。
2012-3-21 00:05
0
雪    币: 219
活跃值: (738)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
11
学习.........
2012-3-21 07:12
0
雪    币: 71
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
不错不错,收藏之
2012-3-21 12:18
0
雪    币: 585
活跃值: (568)
能力值: ( LV13,RANK:290 )
在线值:
发帖
回帖
粉丝
13
放出源码吧
哈哈,,
2012-3-21 12:43
0
雪    币: 91
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
学习学习,求源码
2012-3-22 13:49
0
雪    币: 278
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
放出附件吧。。大侠
2012-3-23 21:32
0
雪    币: 535
活跃值: (245)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
16
这些就是源码了啊
2012-3-23 22:55
0
雪    币: 949
活跃值: (18)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
17
遍历内核中的DpcTimer mark
2012-3-24 12:16
0
雪    币: 796
活跃值: (370)
能力值: ( LV9,RANK:380 )
在线值:
发帖
回帖
粉丝
18
求GetKiProcessorBlock
2012-4-2 15:19
0
雪    币: 238
活跃值: (55)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
19
jas。。。你这mark得也忒具体了吧。。。膜拜中
2012-4-2 15:37
0
雪    币: 242
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
很详细的,支持
2012-4-4 21:42
0
雪    币: 615
活跃值: (530)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
21
这种强文怎么能不顶呢,太适合我了
2012-4-18 10:23
0
雪    币: 60
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
Mark Dpc
2012-4-25 09:56
0
雪    币: 381
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
给力好文,支持一个
2012-10-31 17:25
0
雪    币: 778
活跃值: (208)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
24
谢谢分享,楼主太牛了
2012-11-2 22:21
0
雪    币: 43
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Edj
25
很详细,非常感谢.MARK
2013-1-30 15:33
0
游客
登录 | 注册 方可回帖
返回
//