這裏會構建一個簡單可是完整的驅動程序和一個客戶端,部署內核執行一些平時user下沒法執行的操做。html
將經過如下內容進行講解:ios
1 介紹編程
2 驅動初始化數組
3 Create和Close操做調度實例數據結構
4 DeviceIoControl操做調度實例app
5 安裝和測試驅動程序函數
整個完整源代碼最後面測試
該驅動將解決Windows API設置線程優先級的不靈活性。spa
在User模式下,線程的優先級由其進程優先級類和基於每一個線程的偏移量組合來肯定,偏移量具備有限的級別數。更改進程的優先級類別能夠採用SetPriorityClass函數來實現。命令行
每一個優先級類對應着一個優先級,這個對應的優先級也是在進程中建立線程時默認的優先級。可使用SetThreadPriority函數來修改特定線程的優先級。
基於進程優先級類和線程的優先級偏移量的可用線程優先級圖 :
Priority Class | -Sat | -2 | -1 | 0(default) | +1 | +2 | +Sat | Comments |
---|---|---|---|---|---|---|---|---|
Idle(Low) | 1 | 2 | 3 | 4 | 5 | 6 | 15 | |
Below Normal | 1 | 4 | 5 | 6 | 7 | 8 | 15 | |
Normal | 1 | 6 | 7 | 8 | 9 | 10 | 15 | |
Above Normal | 1 | 8 | 9 | 10 | 11 | 12 | 15 | |
High | 1 | 11 | 12 | 13 | 14 | 15 | 15 | 只有6個級別能夠選,不是7個。 |
Real-time | 16 | 22 | 23 | 24 | 25 | 26 | 31 | 16-31全部級別均可以選 |
SetThreadPriority函數能夠接受指定偏移量的值,五個普通級別分別對應的偏移量是從-2到2:THREAD_PRIORITY_LOWEST (-2), THREAD_PRIORITY_BELOW_NORMAL (-1), THREAD_PRIORITY_NORMAL (0), THREAD_PRIORITY_ABOVE_NORMAL (+1), THREAD_PRIORITY_HIGHEST (+2)。另外兩個級別被稱爲飽和級別,將優先級設置爲支持的兩個極端:THREAD_PRIORITY_IDLE (-Sat) 和 THREAD_PRIORITY_TIME_CRITICAL (+Sat)。
//修改優先級的例子
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
//將進程優先級類修改成ABOVE NORAML
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
//將線程優先級修改成Above_normal
上面的基於進程優先級類和線程的優先級偏移量的可用線程優先級圖表示了咱們要解決的問題,這裏只有一小部分的線程優先級能夠設置,咱們此次準備寫的驅動就是爲了來繞過這些限制,容許將線程的優先級設置爲任意數字而且不用考慮進程優先級類。
小結:設計驅動的目的就是在User態下設計的線程優先級不太行,東西比較少,並且麻煩,採用內核來處理後直接將線程的優先級設置爲任何數字並且不用考慮進行的優先級類。
建立WDM項目,刪除inf文件,再建立C++源文件,而後添加WDK頭文件建立一個空的DriverEntry()函數。
#include<ntddk.h>
extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
return STATUS_SUCCESS;
}
大部分通常的驅動須要實現如下內容:
1 設置Unload卸載函數。
2 設置驅動程序支持的調度實例。
3 建立設備對象。
4 建立對設備對象的符號連接。
實現完以上的內容後,一個驅動程序就能夠進行交互了。
第一步:建立一個Unload實例函數,而且將DriverEntry中的驅動對象指針指向Unload:
#include<ntddk.h>
void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject);
extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = PriorityBoosterUnload;
return STATUS_SUCCESS;
}
對於Unload函數,咱們須要根據DriverEntry函數來具體實現裏面的邏輯結構,由於該函數的主要目的仍是釋放資源。
其實也能夠理解爲交互。其實全部的驅動都應該支持IRP_MJ_CREATE和IRP_MJ_CLOSE操做,否則是沒法打開和關閉驅動對象的。
在DriverEntry中添加如下代碼:
#include<ntddk.h>
void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject);
NTSTATUS PriorityBoosterCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);//新增
extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = PriorityBoosterUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = PriorityBoosterCreateClose;//新增
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PriorityBoosterCreateClose;//新增
return STATUS_SUCCESS;
}
這裏咱們注意到Create和CLOSE都指向的是同一個實例函數,這是由於它們兩個執行的代碼邏輯差很少,若是有比較複雜的狀況,能夠將其分開寫。其實全部的驅動對象的majorfunction函數指針數組都有一個相同的原型(由於他們都是函數指針數組的一部分),因此這裏的新增的函數申明就是major function對於的函數原型:
NTSTATUS PriorityBoosterCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);
//固然函數的名字是能夠改的。
該函數必須返回一個NTSTATUS類型變量,而後接受一個設備對象的指針,和一個指向I/O Request Packet(IRP)的指針,對於全部類型的請求,IRP是存儲請求信息的主要對象。
光有Create和Close確定不夠的,由於咱們的需求裏面咱們還須要告訴驅動給那一個線程設置成爲何優先級。用User Client的角度來看有三個基本API能夠用WriteFile,ReadFile,DeviceIoControl,Read是一個讀,不能寫數據進去,因此從驅動程序來看就能夠不用這個函數了,由於咱們要把信息傳給驅動。對於Write和DeviceIoControl的選擇這個就全看你們喜歡了。通常來講若是真的是一個寫的操做就用Write,可是對於其它任何東西DeviceIoControl確定是首選,由於它是將數據傳入和傳出驅動程序的通用機制。
更改線程的優先級並非純粹的Write,因此這裏咱們採用DeviceIoControl:
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_writes_bytes_to_opt_(nOutBufferSize,*lpBytesReturned) LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped);
對DeviceIoControl來講有三個東西很是重要:
1:可控制的代碼
2:一個輸入緩衝區
3:一個輸出緩衝區
DeviceIoControl:比較靈活,能夠支持多種控制代碼。
在驅動端,DeviceIoControl對應IRP_MJ_DEVICE_CONTROL的MajorFunction函數指針數組的內容。因此添加已下代碼到DriverEntry中:
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PriorityBoosterDeviceControl;
爲了讓Client和kernel能夠交換數據,咱們得實現剛剛申明的PriorityBoosterDeviceControl函數,咱們須要控制的代碼邏輯以及輸出輸入的緩衝區,緩衝區應該包含咱們須要的線程ID和要設置的優先級,這些信息由客戶端提供驅動對其採起行動。要兩個交互就意味着Client/kernel須要有一個單獨的文件來傳輸信息。
因此這裏咱們新建一個PriorityBoosterCommon.h 頭文件來做爲信息傳輸的介質,該文件也會被Client使用。
該文件咱們須要兩個數據一個是須要的結構體,另外一個是更改線程優先級的控制代碼。
先看看結構體:
struct ThreadData {
ULONG ThreadId;
int Priority;
};
須要線程的惟一ID和目標優先級,TID(Thread ID)是一個32位無符號整數,用ULONG不用DWORD是由於ntddk裏面沒有DWORK只有ULONG,而ULONG比較通用。
優先級應該是1-31之間的數字,因此採用一個簡單的int就行了。
接下來還須要一個控制代碼,該控制代碼必須採用CTL_CODE宏來定義,該宏接受構成最終控制代碼的四個參數,CTL_CODE宏的定義:
#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
對這段宏定義的講解:
參數 | 做用 |
---|---|
DeviceType | 標識一種設備,能夠是WDK中定義的FILE_DEVICE_XXX裏面常量之一,可是這個主要用於硬件,和咱們這個軟件的驅動來講這個值不重要,可是微軟指定第三方的驅動程序的這個值應該以0x8000開頭 |
Function | 一個升序數字用來表示特定操做,通常狀況下,這個數字在同一驅動的不一樣控制代碼下必須不一樣,一樣,任何數字均可以,可是官方文檔規定 第三方驅動程序該值應該以0x800開頭 |
Method | 最重要的部分,表示客戶端提供的輸入和輸出緩衝區如何傳遞給驅動程序,對於咱們的驅動程序這裏採用最簡單的值METHOD_NEITHER |
Access | 指示此操做是針對驅動程序 (FILE_WRITE_ACCESS)、來自驅動程序 (FILE_READ_ACCESS) 仍是雙向 (FILE_ANY_ACCESS)。 |
這裏咱們採用下面這種宏定義:
#define PRIORITY_BOOSTER_DEVICE 0x8000
#define IOCTL_PRIORITY_BOOSTER_SET_PRIORITY CTL_CODE(PRIORITY_BOOSTER_DEVICE, \
0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
在DriverEntry中還須要設備對象,以便咱們能夠打開句柄來到達驅動程序。
典型的軟件驅動程序只須要一個設備對象,並帶有指向它的符號連接(能夠理解爲文件的快捷方式)來方便User Client獲取它的句柄。
建立一個設備對象須要使用IoCreateDevice API:
NTSTATUS IoCreateDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG DeviceExtensionSize,
_In_opt_ PUNICODE_STRING DeviceName,
_In_ DEVICE_TYPE DeviceType,
_In_ ULONG DeviceCharacteristics,
_In_ BOOLEAN Exclusive,
_Outptr_ PDEVICE_OBJECT *DeviceObject);
IoCreateDevice API參數解析
參數 | 說明 |
---|---|
DriverObject | 設備對象所屬的驅動程序對象,通常只用來傳遞給DriverEntry函數的驅動程序對象 |
DeviceExtensionSize | 除了sizeof(DEVICE-OBJECT)還有額外字節,用於將某些數據結構與設備相關聯。 對於僅建立單個設備對象的軟件驅動程序而言,它不太有用,由於設備所需的狀態能夠簡單地由全局變量管理。 |
DeviceName | 內部設備名稱,一般在設備對象管理器目錄下建立 |
DeviceType | 與某種類型的基於硬件的驅動程序相關。對於軟件驅動採用FILE_DEVICE_UNKNOWN值 |
DeviceCharacteristics | 一組標誌和某些特定驅動程序相關(軟件驅動程序不多用它),若是軟件驅動程序支持真正的命名空間則該值指定0或者FILE_DEVICE_SECURE_OPEN |
Exclusive | 是否容許多個文件對象打開同一設備。FALSE贊成,TRUE不一樣意 |
DeviceObject | 返回的設備對象指針,若是成功函數會從Non paged Pool非分頁內存池分配結構並將結果指針存儲在引用參數中 |
在建立設備對象前,先要建立一個UNICODE_STRING字符串來保存該內部設備名字:
UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\PriorityBooster");
// 或者RtlInitUnicodeString(&devName, L"\\Device\\ThreadBoost");
設備對象的名字能夠是任意的,可是必須在Device目錄下。有兩種使用常量字符串來初始化UNICODE_STRING的辦法,第一種是使用RtlInitUnicodeString 這個很好用,可是RtlInitUnicodeString必須計算字符串中的字符數才能很好的初始化。
還有一種更快的辦法是採用RTL_CONSTANT_STRING宏,它在編譯時靜態計算字符串的長度,這意味着它只能與常量字符串一塊兒工做。
而後在DriverEntry中寫入咱們的代碼:
PDEVICE_OBJECT DeviceObject;
NTSTATUS status = IoCreateDevice(
DriverObject // our driver object,
0 // no need for extra bytes,
&devName // the device name,
FILE_DEVICE_UNKNOWN // device type,
0 // characteristics flags,
FALSE // not exclusive,
&DeviceObject // the resulting pointer
);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create device object (0x%08X)\n", status));
return status;
}
如今咱們有一個指向咱們的設備對象的指針,下一步須要提供符號連接來使User態的調用者能夠訪問該設備對象,如下幾行代碼建立一個符號連接並將其鏈接到咱們的設備對象:
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\PriorityBooster");
status = IoCreateSymbolicLink(&symLink, &devName);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create symbolic link (0x%08X)\n", status));
IoDeleteDevice(DeviceObject);
return status;
}
一樣名字隨你取,可是目錄必須是 \??目錄下
IoCreateSymbolicLink 經過接受符號連接和連接的目標來完成工做。可是務必注意,若是建立失敗,須要調用IoDeleteDevice來銷燬建立的內容。通常狀況下,若是DriverEntry返回的是失敗狀態,則不會調用Unload函數,若是咱們有不少初始化要作,那麼記得若是失敗記得銷燬掉。
一旦咱們前面的都成功了,那麼必定不要忘記在Unload函數裏面撤銷在DriverEntry中所作的任何事情。
咱們前面建立了設備對象,已經符號連接,是先有的設備對象後有的符號連接。因此咱們銷燬的時候得反着來,先銷燬符號連接,再銷燬設備對象。這裏有點像C++的析構函數了。
void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject) {
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\PriorityBooster");
// delete symbolic link
IoDeleteSymbolicLink(&symLink);
// delete device object
IoDeleteDevice(DriverObject->DeviceObject);
}
對於驅動通常有幾個板塊,1 開始 2 中間交互 3 結束
中間交互呢通常是User和Kernel經過I/O設備對象來進行交互,經常是採起開闢設備對象,而後User採用符號連接來使用該對象設備對象能夠理解爲一個用來交互的東西。而後結束須要刪除掉中間用了的東西。
再在該解決方案下添加一個空項目,而後新建一個.cpp文件來編寫客戶端代碼:
添加如下頭文件:
#include"../PriorityBooster/PriorityBoosterCommon.h"
#include<Windows.h>
#include<stdio.h>
#include<iostream>
//PriorityBoosterCommon.h是咱們用來給Client和Driver進行交互的文件。
修改main函數來接受命令行參數,咱們須要接受線程id,和優先級的value值。
int main(int argc, const char* argv[])
{
if (argc < 3)
{
std::cout << "Usage: Booster <threadid> <priority>" << endl;
return 0;
}
}
而後須要打開設備的句柄來獲取設備傳輸的數據,CreateFile 的第一個參數「filename」應該是前綴爲「\\.\」的符號連接:
HANDLE hDevice = CreateFile(L"\\\\.\\PriorityBooster", GENERIC_WRITE,
FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (hDevice == INVALID_HANDLE_VALUE)
return Error("Failed to open device");
//再定義了一個error函數來打印錯誤的文本。
int Error(const char* message) {
printf("%s (error=%d)\n", message, GetLastError());
return 1;
}
CreateFile會經過IRP_MJ_CREATE調度實例來寫入驅動,若是驅動沒有加載就會報錯說沒有符號連接。就會收到錯誤2(File not found)。
如今經過符號連接拿到了設備的句柄,如今開始能夠調用DeviceIoControl和設備對象進行交互了。在交互前先定義結構體和給給結構體賦值。
ThreadData TempData;
TempData.ThreadId = atoi(argv[1]);//命令行的第一個參數,atoi字符串int
TempData.Priority = atoi(argv[2]);//命令行的第一個參數
調用DeviceIoControl傳遞數據,而後關閉Device句柄:
DWORD returned;
BOOL success = DeviceIoControl(
hDevice,//設備句柄
IOCTL_PRIORITY_BOOSTER_SET_PRIORITY,//控制代碼
&TempData, sizeof(TempData),//輸入buffer和length
nullptr, 0, //輸出buffer和length
&returned, nullptr
);
CloseHandle(hDevice);
DeviceControl經過IRP_MJ_DEVICE_CONTROL majorfunction的實例函數來和driver交互。
這樣客戶端的代碼就搞定了。
如今咱們須要添加的就是驅動代碼裏面的調度函數,由於以前咱們只是申明瞭而已,並無實現這個函數。
Create和Close的調度函數是最好實現的,只須要返回成功就好。
_Use_decl_annotations_//函數的註釋和參數的註釋同樣,無關緊要
NTSTATUS PriorityBoosterCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
每一個調度實例函數都接受設備對象和一個I/O對象IRP,設備對象不用處理,由於咱們這裏只有一個設備對象只能是咱們在DriverEntry中建立的那個,IRP很是重要,在下下章講。
IRP是一個表示請求(交互)的半文檔結構,一般來自執行中的管理器:I/O管理器、即插即用管理器或電源管理器。對於一個簡單的軟件驅動程序極可能就是一個I/O管理器,IRP如何建立不用考慮,driver的目的都是用來處理IRP,請求(交互)的細節須要完成它才行。
驅動的各類請求都是包含在IRP中,經過查看IRP的成員能夠找到請求的類型和詳細數據。
須要注意的是IRP不會單獨運行,它伴隨着一個或多個IO_STACK_LOCATION類型的結構。咱們這個簡單驅動就只有一個IO_STACK_LOCATION。
簡單來講就是咱們須要的一些信息在基礎的IRP結構裏,還有一些在咱們設備堆棧的IO_STACK_LOCATION中。
在建立和關閉的狀況下,咱們不須要查看任何成員。 咱們只須要在其 IoStatus 成員(類型爲 IO_STATUS_BLOCK)中設置 IRP 的狀態,該成員有兩個成員:
Status | 代表此請求將完成的狀態 |
Information | 一個多態成員,在不一樣的請求中意味着不一樣的東西。 在建立和關閉的狀況下,零值就能夠了。 |
爲了真正完成IRP,還在最後調用了IoCompleteRequest函數,這個函數主要是將IRP傳播回給它的調用者通知客戶端操做已經完成。第二個參數是驅動程序能夠提供給其客戶端的臨時優先級提高至,大多數狀況下0值是比較好的IO_NO_INCREMENT被定義爲0,由於這樣請求就是同步的了,就你們優先級都同樣。該函數還要作的最後一個操做是返回與放入 IRP 的操做相同的狀態。
這是最重要的地方了。首先須要檢查的是控制代碼。 典型的驅動程序可能支持不少控制碼,因此若是控制碼不被識別,咱們當即返回請求失敗 :
_Use_decl_annotations_
NTSTATUS PriorityBoosterDeviceControl(PDEVICE_OBJECT, PIRP Irp) {
// 獲取IO_STACK_LOCATION
auto stack = IoGetCurrentIrpStackLocation(Irp); // IO_STACK_LOCATION*
auto status = STATUS_SUCCESS;
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
//獲取控制代碼
case IOCTL_PRIORITY_BOOSTER_SET_PRIORITY:
// do the work
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
任何獲取IRP信息的關鍵是查看和當前設備管理的IO_STACK_LOCATION中的內容,調用IoGetCurrentIrpStackLocation會返回一個指向正確IO_STACK_LOCATION的指針。
IO_STACK_LOCATION中的主要成分是一個名爲Parameters 的union成員,它包含一組結構體和每種IRP一一對應。
在IRP_MJ_DEVICE_CONTROL 狀況下,咱們要查看它的DeviceControl成員,在該結構體中咱們能夠找到傳遞給client的信息,如:控制代碼、緩衝區和緩衝區長度等等。
無論前面怎麼判斷,最後必須有一段代碼來肯定執行,來返回status:
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
這段代碼一般是放到最後,若是前面的都判斷正確時才真正實現DeviceControl。
開頭和結尾咱們寫好了,最後是最好玩和最重要的部分了,就是修改線程優先級。
首先咱們要檢測咱們收到的緩衝區是否足夠大能夠包含一個ThreadData,爲何要檢測?由於Kernel和User下的棧是不同的,不屬於一個東西,因此必須檢查,特別是對於kernel的東西檢查是很是重要的。
指向User提供的輸入緩衝區指針在Type3InputBuff中,輸入緩衝區長度在InputBufferLength中:
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
而後假設緩衝區夠大,那麼咱們就獲取獲得緩衝區指針:
auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
若是指針爲空須要終止:
if (data == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}
而後檢測線程的優先級number是否在1-31之間:
if (data->Priority < 1 || data->Priority > 31) {
status = STATUS_INVALID_PARAMETER;
break;
}
接下來調用設置線程優先級API :
KPRIORITY KeSetPriorityThread(
_Inout_ PKTHREAD Thread,
_In_ KPRIORITY Priority);
PKTHREAD是一個8位整數,線程自己是由一個指向KTHREAD對象的指針來標識的,KTHREAD是內核管理線程的方式之一,KTHREAD沒有文檔來記錄只能由API來經過線程ID獲取Kernel中指向真是線程對象的指針。該API叫作PsLookupThreadByThreadId,使用它須要添加頭文件<ntifs.h>。
如今能夠把線程ID變成一個指針了:
PETHREAD Thread;
status = PsLookupThreadByThreadId(ULongToHandle(data->ThreadId), &Thread);
if (!NT_SUCCESS(status))
break;
找到以後就能夠調用優先級函數來改動優先級了:
KeSetPriorityThread((PKTHREAD)Thread, data->Priority);
可是在使用完以後還須要釋放線程句柄防止資源濫用:
ObDereferenceObject(Thread);
最終函數代碼:
_Use_decl_annotations_
NTSTATUS PriorityBoosterDeviceControl(PDEVICE_OBJECT, PIRP Irp) {
// get our IO_STACK_LOCATION
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto status = STATUS_SUCCESS;
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_PRIORITY_BOOSTER_SET_PRIORITY:
{
// do the work
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
if (data == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}
if (data->Priority < 1 || data->Priority > 31) {
status = STATUS_INVALID_PARAMETER;
break;
}
PETHREAD Thread;
status = PsLookupThreadByThreadId(ULongToHandle(data->ThreadId), &Thread);
if (!NT_SUCCESS(status))
break;
KeSetPriorityThread((PKTHREAD)Thread, data->Priority);
ObDereferenceObject(Thread);
KdPrint(("Thread Priority change for %d to %d succeeded!\n",
data->ThreadId, data->Priority));
break;
}
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
最終的徹底代碼:
終於到了這裏了,千萬不要把東西直接安裝到本機上,說不定就藍屏了。最好是用虛擬機來操做。
在虛擬機中用sc.exe來加載,不清楚的能夠看一看前面的博客:
添加並加載該驅動,採用WinObj來查看加載的數據:
符號連接沒問題
而後使用一下:
這裏咱們經過process Explorer看到cmd進程由一個線程的級別是8,咱們給它改一下

booster 768 25
搞定