windows 核心驅動的檔案操作(轉載二)

來源:互聯網
上載者:User

在核心驅動中建立檔案,並進行讀寫。用於儲存驅動運行中的資訊。

方式:
可以用兩種方式實現該操作。
1、 在應用程式中建立檔案,驅動運用IOCTL將資訊傳給應用程式層,應用程式對檔案進行讀寫。Tdi_fw就是利用這種方式來記錄驅動過濾資訊,Tdi_fw的應用程式建立一個線程,迴圈利用IOCTL來讀取驅動資訊,並儲存到檔案。
2、 在驅動中完成所有的這些操作。在這裡我們主要討論這種方式。

實現:
利用ZwCreateFile建立磁碟檔案,利用ZwReadFile、ZwWriteFile讀寫檔案。
建立檔案代碼如下:
RtlInitUnicodeString (&usname,L"//SystemRoot//System32//LogFiles//passthru.log");
InitializeObjectAttributes(&oa, &usname, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus, NULL,
   FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

寫檔案的代碼如下:
ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, PrintContent, count, NULL, NULL);

這些代碼倒是不難,網上找一些例子就搞定了。
注意兩點:
1、 記得要在結束時,ZwClose(hFile)。
2、 系統剛啟動時,最好將檔案建立於C:,或者系統檔案下。否則會建立檔案失敗。
真正的問題在下面,這些Zw函數都只能運行在PASSIVE_LEVEL上。在DISPATCH_LEVEL上調用這幾個函數會出現BSOD。這時問題就來了,我們在PtReceive函數裡面也需要儲存一些啟動並執行資訊,而PtReceive處於DISPATCH_LEVEL的層級。這個問題需要解決。

這裡有兩種方式:
1.運用工作隊列WorkItem,WorkItem能排隊註冊的回呼函數。當常式處於DISPATCH_LEVEL層級時將回呼函數塞入隊列,當進程降低到PASSIVE_LEVEL時,這些隊列中的回呼函數將會被系統調用。
2.運用PsCreateSystemThread方式註冊一個線程,在註冊一個事件,申請一段記憶體。
在我們有資訊寫入檔案的時候,先將資訊寫入記憶體,然後Set這個事件。在這個線程中迴圈KeWaitForSingleObject這個Event。然後在調用ZwWriteFile將資訊寫入檔案。這裡要注意一點的是檔案讀寫的同步問題。我實現的就是這種方案。

1、可以用用RtlStringCbPrintfA代替sprintf函數,對記憶體進行字串初始化。例子如下:

#include "ntstrsafe.h"

CHAR pszDest[30];
ULONG cbDest = 30;
LPCSTR pszFormat = "%s %d + %d = %d.";
CHAR* pszTxt = "The answer is";

RtlStringCbPrintfA(pszDest, cbDest, pszFormat, pszTxt, 1, 2, 3);
KdPrint(("%s",pszDest));
需要注意的是這個函數還有另一個版本RtlStringCbPrintfW,專門用來對UNICODE進行超作。其他可能對我們比較有用的函數是RtlStringCbCopyA和RtlStringCbCatA用於拷貝和串連。操作都很簡單,它們本身就是驅動下的sprintf的替代版本。

2、發現一個問題,就是安裝完驅動重啟後,在DriverEntry調用那個ZwCreateFile函數會返回失敗。而在系統啟動以後安裝Passthru,就不會出現這個問題。原因是在作業系統啟動時,系統初始化Passthru,調用我們的DriverEntry函數。這時候磁碟還沒完全初始化好無法訪問磁碟。
這個問題其實對我們來說沒有什麼影響,我們需要記錄資訊的時候,一定是在應用程式用戶端已經開啟後的事情了。這時候磁碟早已啟動好了,我們不用擔心。
這裡的解決方案是,在應用程式一啟動,開啟驅動裝置的時候,才ZwCreateFile。這樣一定是可以的。

3、 運用WorkItem進行系統回呼函數排隊的方法也不難。在NDIS下可以使用NdisInitializeWorkItem和NdisScheduleWorkItem函數來排隊我們在高irql下的不能執行的動作。等到進程下降到passive_level時,這些已經排隊了的回呼函數就會Dequeue。
這個方法我今天試過了,可以使用。但是從效率來講,並沒有什麼突出的地方。而且根據CSDN的介紹,工作隊列有兩個需要注意的地方。一是不能執行太長的操作,會死結。2是在排隊工作項目之前,最好釋放所有的mutex、lock、semaphore,否則很容易死結。反正這兩種操作都不錯(另一種線程的方法參見前面文檔)。隨便選一個用用就行了。
Regmon(2)

if(NT_SUCCESS(ntStatus))
{
建立符號串連,以便GUI能夠指名,訪問這個驅動/裝置
建立每個需要處理的方法的發佈點
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] =
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RegmonDispatch;
#if DBG
DriverObject->DriverUnload = RegmonUnload;
#end if
}

if(!NT_SUCCESS(ntStatus))
{
DbgPrint((“Regmon: Failed   to create our device!/n”));
發生錯誤,清理環境(資源等)
if(GUIDevice) IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return ntStatus;
}

初始化互斥量
MUTEX_INIT(StoreMutex);
MUTEX_INIT(HashMutex);
MUTEX_INIT(FilterMutex);

初始化根鍵長度
for(I = 0; I < NUMROOTKEYS; I ++)
{
RootKey[I].RootNameLen = strlen(RootKey[I].RootName;
}
for(I = 0; I < 2; I ++)
{
CurrentUser[I].RootNameLen = strlen(CurrentUser[I].RootName;
}

系統資料表資料結構指標,一個NTOSKRNL匯出
ServiceTable = KeServiceDescriptorTable; 使用者自訂的結構
DbgPrint((“HookRegistry:Servicetable:%x/n”, ServiceTable));
擷取進程名稱的位移
ProcessNameOffset = GetProcessNameOffset(); 這個函數是牛人自己寫的。

分配初始化的輸出緩衝區
Store = ExAllocatePool(PagePool, sizeof(*Store));
if(!Store)
{
IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return STATUS_INSUFFICENT_RESOURCES;
}
Store->Len = 0;
Store->Next = NULL;
NumStore = 1;

如果我們是個啟動裝置則開始記日誌
if(startType != SERVICE_DEMAND_START)
{
初始化日誌
BoolLogging = TRUE;

KeInitializeEvent(&LoggingEvent, SynchronizationEvent, FALSE);
GUIActive = TRUE;
Fileltet = BootFilter;
RegmonUpdateFilters();
HookRegisters();

告訴使用者,記錄模式已經開啟
RtlInitUnicodeString(&bootMessageUnicodeString, bootMessage);
ZwDisplayString(&bootMessageUnicodeString);

註冊關閉通知
IoRegisterShutdonwNotification註冊一個驅動提供的系統關閉通知函數,在系統被完全關閉之前調用。
IoRegisterShutdown Notification(GUIDevice);

}
return STATUS_SUCCESS;
}

 

 

 

RegmonDispatch
這個函數處理裝置的請求。需要顯示處理的請求來自GUI。當然,當GUI建立,關閉合驅動的通訊時,也需要處理Create,Close。
NTSTAUTS RegmonDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack; 當前堆棧
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
PSTORE_BUF old;
WORK_QUEUE_ITEM workItem;

設定處理成功
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

擷取Irp中當前位置的指標,這就是代碼和參數定位的地方
irpStack = IoGetCurrentIrpStackLocation(Irp);

inputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = iprStack->Parameters.DeviceIoControl.InputBufferLength;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

switch(irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
       DbgPrint((“Regmon: IRP_MJ_CREATE/n”));
       關閉開機記錄
       if(BootLogging)
       {
         BootLogging = FALSE;
         IoUnregisterShutdownNotification(DeviceObject);
         MUTEX_WAIT(StoreMutex);
         ExInitializeWorkItem(&workItem, RegmonCloseBootLog, 0);
這個函數已經到期,應該使用IoAllocateWorkItem。返回一個指向私人結構IO_WORKITEM的指標。驅動不應該以任何形式訪問這個結構!
         ExQueueWorkItem(&workItem, CriticalWorkQueue);
         這個函數也到期了,應該使用IoQueueWorkItem。插入一個指定的工作項目到隊列中,這個隊列由系統背景工作執行緒訪問,系統背景工作執行緒從隊列中移出一個工作項目,並調用其中的回呼函數。
         KeWaitForSingleObject(&LoggingEvent, Executive, KernelMode, FALSE, NULL);
         MUTEX_RELEASE(StoreMutex);
       }
       Sequence = 0;
       GUIActive = TRUE;
       DbgPrint((“GUI Active: %d/n”, GUIActive));
       break;
}
}

還能繼續小
Project->Settings開啟Link屬性頁面,將Object/library modules:下面編輯框中的各種lib全部刪除,然後打上msvcrt.lib kernel32.lib user32.lib
Project->Settings的Link屬性頁面裡,在Project Options下面的編輯框裡加上一句:/ALIGN:4096

這都是些簡潔的方法

摘自: 

http://blog.csdn.net/floweronwarmbed/archive/2009/06/01/4233452.aspx

分享到:

在核心驅動中建立檔案,並進行讀寫。用於儲存驅動運行中的資訊。

方式:
可以用兩種方式實現該操作。
1、 在應用程式中建立檔案,驅動運用IOCTL將資訊傳給應用程式層,應用程式對檔案進行讀寫。Tdi_fw就是利用這種方式來記錄驅動過濾資訊,Tdi_fw的應用程式建立一個線程,迴圈利用IOCTL來讀取驅動資訊,並儲存到檔案。
2、 在驅動中完成所有的這些操作。在這裡我們主要討論這種方式。

實現:
利用ZwCreateFile建立磁碟檔案,利用ZwReadFile、ZwWriteFile讀寫檔案。
建立檔案代碼如下:
RtlInitUnicodeString (&usname,L"//SystemRoot//System32//LogFiles//passthru.log");
InitializeObjectAttributes(&oa, &usname, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus, NULL,
   FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

寫檔案的代碼如下:
ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, PrintContent, count, NULL, NULL);

這些代碼倒是不難,網上找一些例子就搞定了。
注意兩點:
1、 記得要在結束時,ZwClose(hFile)。
2、 系統剛啟動時,最好將檔案建立於C:,或者系統檔案下。否則會建立檔案失敗。
真正的問題在下面,這些Zw函數都只能運行在PASSIVE_LEVEL上。在DISPATCH_LEVEL上調用這幾個函數會出現BSOD。這時問題就來了,我們在PtReceive函數裡面也需要儲存一些啟動並執行資訊,而PtReceive處於DISPATCH_LEVEL的層級。這個問題需要解決。

這裡有兩種方式:
1.運用工作隊列WorkItem,WorkItem能排隊註冊的回呼函數。當常式處於DISPATCH_LEVEL層級時將回呼函數塞入隊列,當進程降低到PASSIVE_LEVEL時,這些隊列中的回呼函數將會被系統調用。
2.運用PsCreateSystemThread方式註冊一個線程,在註冊一個事件,申請一段記憶體。
在我們有資訊寫入檔案的時候,先將資訊寫入記憶體,然後Set這個事件。在這個線程中迴圈KeWaitForSingleObject這個Event。然後在調用ZwWriteFile將資訊寫入檔案。這裡要注意一點的是檔案讀寫的同步問題。我實現的就是這種方案。

1、可以用用RtlStringCbPrintfA代替sprintf函數,對記憶體進行字串初始化。例子如下:

#include "ntstrsafe.h"

CHAR pszDest[30];
ULONG cbDest = 30;
LPCSTR pszFormat = "%s %d + %d = %d.";
CHAR* pszTxt = "The answer is";

RtlStringCbPrintfA(pszDest, cbDest, pszFormat, pszTxt, 1, 2, 3);
KdPrint(("%s",pszDest));
需要注意的是這個函數還有另一個版本RtlStringCbPrintfW,專門用來對UNICODE進行超作。其他可能對我們比較有用的函數是RtlStringCbCopyA和RtlStringCbCatA用於拷貝和串連。操作都很簡單,它們本身就是驅動下的sprintf的替代版本。

2、發現一個問題,就是安裝完驅動重啟後,在DriverEntry調用那個ZwCreateFile函數會返回失敗。而在系統啟動以後安裝Passthru,就不會出現這個問題。原因是在作業系統啟動時,系統初始化Passthru,調用我們的DriverEntry函數。這時候磁碟還沒完全初始化好無法訪問磁碟。
這個問題其實對我們來說沒有什麼影響,我們需要記錄資訊的時候,一定是在應用程式用戶端已經開啟後的事情了。這時候磁碟早已啟動好了,我們不用擔心。
這裡的解決方案是,在應用程式一啟動,開啟驅動裝置的時候,才ZwCreateFile。這樣一定是可以的。

3、 運用WorkItem進行系統回呼函數排隊的方法也不難。在NDIS下可以使用NdisInitializeWorkItem和NdisScheduleWorkItem函數來排隊我們在高irql下的不能執行的動作。等到進程下降到passive_level時,這些已經排隊了的回呼函數就會Dequeue。
這個方法我今天試過了,可以使用。但是從效率來講,並沒有什麼突出的地方。而且根據CSDN的介紹,工作隊列有兩個需要注意的地方。一是不能執行太長的操作,會死結。2是在排隊工作項目之前,最好釋放所有的mutex、lock、semaphore,否則很容易死結。反正這兩種操作都不錯(另一種線程的方法參見前面文檔)。隨便選一個用用就行了。
Regmon(2)

if(NT_SUCCESS(ntStatus))
{
建立符號串連,以便GUI能夠指名,訪問這個驅動/裝置
建立每個需要處理的方法的發佈點
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] =
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RegmonDispatch;
#if DBG
DriverObject->DriverUnload = RegmonUnload;
#end if
}

if(!NT_SUCCESS(ntStatus))
{
DbgPrint((“Regmon: Failed   to create our device!/n”));
發生錯誤,清理環境(資源等)
if(GUIDevice) IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return ntStatus;
}

初始化互斥量
MUTEX_INIT(StoreMutex);
MUTEX_INIT(HashMutex);
MUTEX_INIT(FilterMutex);

初始化根鍵長度
for(I = 0; I < NUMROOTKEYS; I ++)
{
RootKey[I].RootNameLen = strlen(RootKey[I].RootName;
}
for(I = 0; I < 2; I ++)
{
CurrentUser[I].RootNameLen = strlen(CurrentUser[I].RootName;
}

系統資料表資料結構指標,一個NTOSKRNL匯出
ServiceTable = KeServiceDescriptorTable; 使用者自訂的結構
DbgPrint((“HookRegistry:Servicetable:%x/n”, ServiceTable));
擷取進程名稱的位移
ProcessNameOffset = GetProcessNameOffset(); 這個函數是牛人自己寫的。

分配初始化的輸出緩衝區
Store = ExAllocatePool(PagePool, sizeof(*Store));
if(!Store)
{
IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return STATUS_INSUFFICENT_RESOURCES;
}
Store->Len = 0;
Store->Next = NULL;
NumStore = 1;

如果我們是個啟動裝置則開始記日誌
if(startType != SERVICE_DEMAND_START)
{
初始化日誌
BoolLogging = TRUE;

KeInitializeEvent(&LoggingEvent, SynchronizationEvent, FALSE);
GUIActive = TRUE;
Fileltet = BootFilter;
RegmonUpdateFilters();
HookRegisters();

告訴使用者,記錄模式已經開啟
RtlInitUnicodeString(&bootMessageUnicodeString, bootMessage);
ZwDisplayString(&bootMessageUnicodeString);

註冊關閉通知
IoRegisterShutdonwNotification註冊一個驅動提供的系統關閉通知函數,在系統被完全關閉之前調用。
IoRegisterShutdown Notification(GUIDevice);

}
return STATUS_SUCCESS;
}

 

 

 

RegmonDispatch
這個函數處理裝置的請求。需要顯示處理的請求來自GUI。當然,當GUI建立,關閉合驅動的通訊時,也需要處理Create,Close。
NTSTAUTS RegmonDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack; 當前堆棧
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
PSTORE_BUF old;
WORK_QUEUE_ITEM workItem;

設定處理成功
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

擷取Irp中當前位置的指標,這就是代碼和參數定位的地方
irpStack = IoGetCurrentIrpStackLocation(Irp);

inputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = iprStack->Parameters.DeviceIoControl.InputBufferLength;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

switch(irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
       DbgPrint((“Regmon: IRP_MJ_CREATE/n”));
       關閉開機記錄
       if(BootLogging)
       {
         BootLogging = FALSE;
         IoUnregisterShutdownNotification(DeviceObject);
         MUTEX_WAIT(StoreMutex);
         ExInitializeWorkItem(&workItem, RegmonCloseBootLog, 0);
這個函數已經到期,應該使用IoAllocateWorkItem。返回一個指向私人結構IO_WORKITEM的指標。驅動不應該以任何形式訪問這個結構!
         ExQueueWorkItem(&workItem, CriticalWorkQueue);
         這個函數也到期了,應該使用IoQueueWorkItem。插入一個指定的工作項目到隊列中,這個隊列由系統背景工作執行緒訪問,系統背景工作執行緒從隊列中移出一個工作項目,並調用其中的回呼函數。
         KeWaitForSingleObject(&LoggingEvent, Executive, KernelMode, FALSE, NULL);
         MUTEX_RELEASE(StoreMutex);
       }
       Sequence = 0;
       GUIActive = TRUE;
       DbgPrint((“GUI Active: %d/n”, GUIActive));
       break;
}
}

還能繼續小
Project->Settings開啟Link屬性頁面,將Object/library modules:下面編輯框中的各種lib全部刪除,然後打上msvcrt.lib kernel32.lib user32.lib
Project->Settings的Link屬性頁面裡,在Project Options下面的編輯框裡加上一句:/ALIGN:4096

這都是些簡潔的方法

摘自: 

http://blog.csdn.net/floweronwarmbed/archive/2009/06/01/4233452.aspx

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.