程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi研究之驅動開發篇(四)--使用系統內存堆

Delphi研究之驅動開發篇(四)--使用系統內存堆

編輯:Delphi

作 者: mickeylan
時 間: 2008-01-19,20:54
鏈 接: http://bbs.pediy.com/showthread.php?t=58608

通過對前面幾篇教程的學習,相信大家已經掌握了一些用Delphi開發Windows驅動程序的基礎知識,從現在開始我們來了解一些必要的底層技術,首先我們要了解的就是內存管理方面的知識。
     內存管理器給用戶進程提供了大量的API。這些API可以分為三類:虛擬內存函數、內存映射文件函數和堆函數。內核的成員(包括驅動程序)有很多高級的工具。例如:驅動程序能夠在物理地址空間裡分配一個連續的內存。這類函數呢,前綴是"Mm"。另外呢,還有一種以"Ex"為前綴的函數,用於從系統內存池裡(分頁和不分頁的)分配和釋放內存,還可以操作後備列表(lookaside lists)。
     後備列表是啥東東?我們後面再講,它可以提供更快的內存分配,但要使用預定義的固定的塊大小。
     系統內存堆可跟用戶堆不一樣啊,它表現為系統地址空間的兩個所謂的內存池。
     ◎ 不分頁池--不分頁池不會分頁到交換文件(swap file),自然也不需要分頁回來。它們總是老老實實在物理內存裡活動,在你想訪問它們的時候總能找到它們(任何IRQL等級),並且不會出現分頁錯誤。這也正是它的優點,任何訪問都不會出現頁面錯誤!頁面錯誤往往會導致系統癱(當IRQL >= DISPATCH_LEVEL)!
     ◎ 分頁池--顧名思義,就是可以分頁(分入和分出)的了。你只能使用(IRQL < DISPATCH_LEVEL)的內存。
     以上兩種池都位於系統地址空間,在進程上下文中可以使用它們。有一個函數集合叫ExAllocatePoolXXX,用於從系統內存池分配內存。函數ExFreePool用來釋放。
在我們開始使用它們的時候,來看看基本要點:
前面提到,如果你訪問已經被交換出去的內存時IRQL >= DISPATCH_LEVEL會導致系統癱瘓!但是事情並不絕對,也許它當時不死機,過一會才死呢!啥時候死?就是當你的系統將內存交換了出去,並且你訪問它的時候!
     千萬不要太鐘愛不分頁內存,太浪費資源了!它總要占用物理內存!分配的堆使用完後記得一定要釋放,你在系統池裡分配了內存,無論你的驅動程序發生了什麼事情,這些內存不會被回收,除非ExFreePool 被調用。如果你不用ExFreePool顯式地釋放內存,即使你的驅動程序卸載了,這些內存還駐留在那裡。所以呢,你就乖乖地顯式地釋放內存吧!
     系統內存池分配的內存不會被自動清零,最後的使用者可能會留下垃圾。所以呢,使用之前,最好統統置零。
     你可以很容易地定義你需要的內存類型了,就兩種:分頁,不分頁。如果你的代碼要訪問IRQL >= DISPATCH_LEVEL,不用說你也知道,你必須使用不分頁類型。代碼本身,和涉及的數據都要在不分頁內存裡。在缺省情況下,驅動程序以不分頁內存加載,除非是INIT節區或者名稱以"PAGE"開始的節區。假如你不做任何動作去改變驅動程序的內存屬性(例如:別去調用MmPageEntireDriver使驅動程序的映像分頁),你就不用關心驅動程序了,它總是在內存裡。
先前的文章中我們討論了常用的驅動函數(DriverEntry, DriverUnload, DispatchXxx)被調用時所處的IRQL等級。
     DDK給了我們關於每一個函數被調用時的IRQL等級的相關信息。例如:在後面的文章中我們會使用IoInitializeTimer函數,該函數的描述這樣說的:該函數執行時,時鐘事件發生時的等級IRQL = DISPATCH_LEVEL 。這就意味著:這個函數訪問的所有內存都必須是不分頁的。
如果你不能確定到底是哪個IRQL,你寫程序時候可以這樣調用KeGetCurrentIrql:

代碼:If KeGetCurrentIrql < DISPATCH_LEVEL then
begin
    {使用任意內存}
End else
begin
    {只能使用不分頁內存}
End;
     下面讓我們來看一個簡單驅動程序例子SystemModules,該例子的主要動作集中在DriverEntry函數裡。我們會分配分頁內存(你應該記得DriverEntry運行在IRQL =PASSIVE_LEVEL等級,所以使用分頁內存自然是沒問題了),然後寫進一些信息,再釋放,並讓系統卸載驅動程序。

代碼:unit SystemModules;

interface

uses
    nt_status, ntoskrnl, native;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;

implementation

function _DriverEntry(pDriverObject:PDRIVER_OBJECT; pusRegistryPath:PUNICODE_STRING): NTSTATUS;
var
    cb:DWORD;
    p, pTemp:PVOID;
    dwNumModules:DWORD;
    pMessage, pModuleName: PCHAR;
    buffer: array [0..295] of char;
    szModuleName: array[0..100] of char;
    iCnt, iPos: integer;
begin
    DbgPrint(SystemModules: Entering DriverEntry);
    cb := 0;
    ZwQuerySystemInformation(SystemModuleInformation, @p, 0, cb);
    if cb <> 0 then
    begin
      p := ExAllocatePool(PagedPool, cb);
      if p <> nil then
      begin 
        DbgPrint(SystemModules: %u bytes of paged memory allocted at address %08X, cb, p);
        if ZwQuerySystemInformation(SystemModuleInformation,
          p, cb, cb) = STATUS_SUCCESS then
        begin 
          pTemp := p;
          dwNumModules := DWORD(p^);
          cb := (sizeof(SYSTEM_MODULE_INFORMATION) + 100) * 2;
          pMessage := ExAllocatePool(PagedPool, cb);
          if pMessage <> nil then
          begin
            DbgPrint(SystemModules: %u bytes of paged memory allocted at address %08X, cb, pMessage);
            memset(pMessage, 0, cb);
            inc(PCHAR(pTemp), sizeof(DWORD));
            for iCnt := 1 to dwNumModules do
            begin
              iPos := (PSYSTEM_MODULE_INFORMATION(pTemp))^.ModuleNameOffset;
              pModuleName := @((PSYSTEM_MODULE_INFORMATION(pTemp))^.ImageName[iPos]);
              if (_strnicmp(pModuleName, ntoskrnl.exe, length(ntoskrnl.exe)) = 0) or
                 (_strnicmp(pModuleName, ntice.sys, length(ntice.sys)) = 0) then
              begin
                memset(@szModuleName, 0, sizeof(szModuleName));
&nb

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved