[原創]W2k Driving 學習筆記(一)內核線程及同步
[大體流程]
Win32與Kernel交互,從User層向內核發送2個請求:
0 IOCTL_Start_Thread : 從內核新建一個線程,最多建立MAX_THREAD_NUM個線程;
1 IOCTL_Stop_Thread : 關閉剛才建立的所有線程;
每個線程做同樣的事,每次將變量Count增加1,使用一個FAST_MUTEX來同步加1操作。
線程過程如下,沒什麼特別說明的,其中為了照顧偶的眼球眨動頻率,特地做了1s的延時。
DDKAPI VOID CountThreadMain(IN PVOID pContext) { PDEVICE_EX pDevEx = (PDEVICE_EX)pContext; LARGE_INTEGER dt = RtlConvertLongToLargeInteger(/ -10 * 1000 * 1000); ULONG tid = (ULONG)PsGetCurrentThreadId(); PRINT("[%s]entry new thread[id:%d]",/ __func__,tid); while(!pDevEx->bEXIT_THREADS) { ExAcquireFastMutex(&pDevEx->FastMtx); ++pDevEx->Count; ExReleaseFastMutex(&pDevEx->FastMtx); PRINT("[%s][tid:%d]Now Count is %d/n",/ __func__,tid,pDevEx->Count); if(!NT_SUCCESS(KeDelayExecutionThread(KernelMode,/ false,&dt))) { PRINT("[%s]err : Delay Failed!/n",__func__); break; } } PRINT("[%s]exit thread[id:%d]/n",/ __func__,tid); (VOID)PsTerminateSystemThread(STATUS_SUCCESS); }
在退時要注意,在未退出所有線程之前,絕不能Unload Driver,否則線程所在的內存
區將會被Unmap,係統將會發生缺頁0x0E異常。我做了一個等待處理:
DDKAPI VOID WaitKillThreads(PDEVICE_OBJECT pDeviceObject) { PDEVICE_EX pDevEx = (PDEVICE_EX)pDeviceObject->DeviceExtension; PRINT("[%s]sizeof WAIT_BLOCK is %d/n",__func__,sizeof(KWAIT_BLOCK)); if(pDevEx->CURRENT_THREAD_NUM == 0) return; pDevEx->bEXIT_THREADS = TRUE; PKWAIT_BLOCK pKB = (PKWAIT_BLOCK)ExAllocatePool(/ NonPagedPool,/ sizeof(KWAIT_BLOCK)*pDevEx->CURRENT_THREAD_NUM); if(pKB == NULL) { PRINT("[%s]Alloc Mem Failed!/n",__func__); return; } if(!NT_SUCCESS(KeWaitForMultipleObjects(/ pDevEx->CURRENT_THREAD_NUM,/ pDevEx->pThreadObjs,WaitAll,/ Executive,KernelMode,TRUE,/ NULL,pKB))) { PRINT("[%s]KeWaitForMultipleObjects Failed!/n",/ __func__); } ExFreePool((PVOID)pKB); }
原先的WaitKillThreads老是導致係統藍屏,調試發現隻有當建立的線程數超過3個時
才會藍屏,進一步分析,藍屏的原因是WaitKillThreads將DrvDispatchControl的
第2個參數pIrp寫成了垃圾數據。為什麼隻有當Thread數大於3時才這樣呢?觀察
WaitKillThreads中可能會擦寫內核棧的隻有KeWaitForMultipleObjects的回寫操作
(pKB),查閱DDK Helps 之後發現,如果等待的對象> THREAD_WAIT_OBJECTS則,
必須分配 sizeof(KWAIT_BLOCK) * Count 字節的非分頁內存讓其寫入。一查
THREAD_WAIT_OBJECTS大小正好等於3,答案揭曉:
WaitKillThreads在最高優化中是內聯編入派遣過程,這意味著它們共用一個函數棧楨,
KWAIT_BLOCK大小為24,則如果線程數大於3則正好溢出覆蓋掉pIrp內容,My God!
另外,在編譯代碼是發現找不到WaitAll或WaitAny的聲明,我用的是MinGW(gcc)5.1.4
版本,在winddk.h中發現其將WAIT_TYPE定義成ULONG,將其改為:
typedef enum _WAIT_TYPE { WaitAll, WaitAny }WAIT_TYPE;
後建立成功。
最後更新:2017-04-02 00:06:41