剖析Windows NT/2000核心對象組織(http://webcrazy.yeah.net)

來源:互聯網
上載者:User
剖析Windows NT/2000核心對象組織  
                  WebCrazy(http://webcrazy.yeah.net/)

    對象管理器在Windows NT/2000核心中佔了極其重要的位置,其一個最主要職能是組織管理系統核心對象。在Windows NT/2000中,核心對象管理器大量引入了C++物件導向的思想,即所有核心對象都封裝在對象管理器內部,除對象管理器自己以外,對其他所有想引用核心對象結構成員的子系統都是不透明的,也即都需通過對象管理器訪問這些結構。Microsoft極力推薦核心驅動代碼遵循這一原則(使用者態代碼根本不能直接存取這些資料),她提供了一系列以Ob開頭的常式供我們使用。但是也不是說只有對象管理器即這些函數才能訪問這些資料。我下面提供的代碼則直接存取這些結構,這種情況下,我們只能祈禱Microsoft永遠保持這些對象頭,對象體結構的不變,這一般不大可能,所以我提供的代碼只供學習Windows NT/2000核心之用,無其它實際應用意義。

    先談談核心已命名物件吧,命名物件存於系統全域命名核心區,與傳統的DOS目錄與檔案組織方式相似,對象管理器也採用樹狀結構管理這些對象,這樣即可快速檢索核心對象。這個優點很容易體現,對象檢索速度快,這樣會大大提高系統效能,舉個例子吧:譬如進程A某個時刻已經使用某個檔案,即A在核心中擁有了指向這個檔案的一個核心對象(FILE_OBJECT),而如果這時另一進程B也試圖訪問這個檔案,則B通過CreateFile API等開啟這個檔案時,系統指引對象管理器尋找Object Storage Service空間,如已存在此對象,則查看對象頭,是否已獨佔訪問,假如是的話,則會出現失敗,否則的話,增加此對象的引用計數或控制代碼計數。系統中這種搜尋對象的情況無處不在,因為Windows NT/2000中將所有的需SECURITY_DESCRIPTOR等 結構進行保護的可操作的資料結構都當作對象處理,如常見的進程對象(EPROCESS/KPEB)、線程對象(ETHREAD/KTEB)、驅動程式對象(DRIVER_OBJECT)等等。當然這種樹狀結構組織核心已命名物件,還有另一個優點,就是使所有已命名物件組織的十分有條理,如裝置對象處於/Device下,而物件類型名稱處於/ObjectTypes下等等。再者這樣也能達到使用者態進程僅能訪問/??與/BaseNamedObjects下的對象,而核心態代碼則沒有任何限制的目的。至於系統內部如何組織管理這些已命名物件,其實Windows NT/2000內部由核心變數ObpRootDirectoryObject指向的Directory對象指向根目錄,使用雜湊表(HashTable)來組織管理這些命名核心對象。先看看I386KD的分析,再讓我們來看看我寫的代碼吧。

    kd> !object /
    Object: 8148e210  Type: (814c5820) Directory
        ObjectHeader: 8148e1f8
        HandleCount: 0  PointerCount: 39
        Directory Object: 00000000  Name: /
        99 symbolic links snapped through this directory
        HashBucket[ 00 ]: 8148a350 Directory 'ArcName'
                          814a8f10 Device 'Ntfs'
        HashBucket[ 01 ]: e2390040 Port 'SeLsaCommandPort'
        HashBucket[ 03 ]: e1012030 Key '/REGISTRY'
        HashBucket[ 06 ]: e1394560 Port 'XactSrvLpcPort'
        HashBucket[ 07 ]: e13682e0 Port 'DbgUiApiPort'
        HashBucket[ 09 ]: 84305760 Directory 'NLS'
                        .
                        .
                        .

    kd> !object 814a8f10
    Object: 814a8f10  Type: (814b5ac0) Device
        ObjectHeader: 814a8ef8
        HandleCount: 0  PointerCount: 2
        Directory Object: 8148e210  Name: Ntfs

    實現代碼如下:

    //-----------------------------------------------
    //
    // Dump Windows 2000 Kernel Object
    // Only test on Windows 2000 Server Chinese Edition 
    // Build 2195(Free)!Programmed By WebCrazy
    // (tsu00@263.net ) on 11-04-2000!
    // Welcome to http://webcrazy.yeah.net !
    //
    //-----------------------------------------------

    ULONG ObpRootDirectoryObject=0x8148e210;  //fetch from symbol file

    void DumpDirectoryObject(PVOID DirectoryObject)
    {
       ULONG HashBucket;
       ULONG *Hash;

       for(HashBucket=0;HashBucket<=0x24;HashBucket++)
       {
          Hash=(ULONG *)((ULONG)DirectoryObject+HashBucket*4);
          if(*Hash==0) continue;

          DbgPrint("/n    HashBucket[%02X]/n",HashBucket);
          do
          {
              PUNICODE_STRING ObName,ObTypeName;
              PVOID Object,ObPreHeader,ObStandardHeader;

              Hash=(ULONG *)(*Hash);
              Object=(PVOID)(*(ULONG *)((ULONG)Hash+4));
              ObPreHeader=(PVOID)((ULONG)Object-0x28);
              ObStandardHeader=(PVOID)((ULONG)Object-0x18);

              ObName=(PUNICODE_STRING)((ULONG)ObPreHeader+4);
              DbgPrint("/tDump Object:%08X/n",(ULONG)Object);
              DbgPrint("/t  Name:%S",ObName->Buffer);

              ObTypeName=(PUNICODE_STRING)((ULONG)(*(ULONG *)((ULONG)ObStandardHeader+8))+0x40);
              DbgPrint("/t  Type:%S(%08X)/n",ObTypeName->Buffer,
                            *(ULONG *)((ULONG)ObStandardHeader+8));

              DbgPrint("/t  PointerCount:%d  HandleCount:%d/n",*(ULONG *)ObStandardHeader,
                            *(ULONG *)((ULONG)ObStandardHeader+4));

          }while(*Hash!=0);
       }
    }

    void DumpObject(PVOID Object)
    {

       PUNICODE_STRING ObName,ObTypeName;
       UNICODE_STRING temp;
       PVOID ObPreHeader=(PVOID)((ULONG)Object-0x28),
            ObStandardHeader=(PVOID)((ULONG)Object-0x18);

       if(((USHORT)NtBuildNumber)!=2195){
           DbgPrint("Only test on Windows 2000 Server Build 2195!/n");
           return;
       }

       ObName=(PUNICODE_STRING)((ULONG)ObPreHeader+4);
       DbgPrint("  Dump Object:%08X/n    Name:%S",(ULONG)Object,ObName->Buffer);

       ObTypeName=(PUNICODE_STRING)((ULONG)(*(ULONG *)((ULONG)ObStandardHeader+8))+0x40);
       DbgPrint("    Type:%S(%08X)/n",ObTypeName->Buffer,*(ULONG *)((ULONG)ObStandardHeader+8));

       DbgPrint("    PointerCount:%d  HandleCount:%d/n",*(ULONG *)ObStandardHeader,
                     *(ULONG *)((ULONG)ObStandardHeader+4));

       RtlInitUnicodeString(&temp,L"Directory");
       if(!RtlCompareUnicodeString(&temp,ObTypeName,FALSE))
          DumpDirectoryObject(Object);
    }

    void DumpRootDirectoryObject()
    {
       DumpObject((PVOID)ObpRootDirectoryObject);
    }

    看過上面Windbg的輸出或是用過Softice的objdir命令的人,應該都知道代碼的用處,所以我就不加以介紹。這段代碼直接讀取系統全域命名物件區,沒有考慮同步等,而且在引用對象成員時也沒有對PointerCount進行加一,另外目前也沒有考慮OBJECT_TYPE中定義的CallBack函數,這樣在使用者驅動代碼或作業系統掛接一些額外的應用時,可能會導致錯誤,不過因為代碼中我對對象區的資料結構有了較為清楚的定義,對理解Windows NT/2000的核心對象組織較有協助,所以我就將它列出(這段代碼我理解了很久後才寫的)。代碼中我使用PVOID指標,且將指標轉化為ULONG類型,直接將結構的位移地址進行相加。不使用如(ULONG *)Ob++這種文法,這樣您可以直接從代碼中提取一些結構重要成員的位移,但這也造成代碼不夠簡潔。關於ObpRootDirectoryObject這個最重要的Windows NT/2000核心變數,我直接從Symbols檔案中提取,您必鬚根據實際情況予以調整。或者使用ObReferenceObjectByName獲得。

    其實現在已經有很多應用程式可以實現這種列舉對象的功能,除了上面的windbg/i386kd的!object、SoftICE的objdir命令,另外還有DDK中的objdir.exe、Mark Russinovich的winobj.exe等等。如果你搜尋命名物件儲存地區,就可以實現系統裝置(Device)對象與驅動程式(Driver)對象的枚舉,即可實現SoftICE中device與driver命令的功能。必須指出的是,不是所有的device對象都位於/Device下,也不是所有的driver對象都位於/Driver下,所以必須通過遞迴調用DumpDirectoryObject尋找整個空間,我不知道為什麼Microsoft不限制device與driver對象的存放位置。

    以上談及的命名核心對象可在進程間共用,這也是Windows NT/2000中LPC(Local Procedure Call)的最基本原理,LPC使用到核心中稱為Port的核心對象。為什麼命名物件能在進程間共用,而以下要談及的未命名物件卻不能在進程間共用呢?通過我的理解,我覺得既然兩者都位於系統核心4G記憶體的高端,理論上應該均可以在進程間共用。只是因為Microsoft提供諸如ObReferenceObjectByName直接根據對象名尋找對象,而她提供的ObReferenceObjectByHandle卻沒有指定進程的參數,所以未命名物件在這種程度上是不能共用的。另外,Microsoft不提供這種參數的另一個最主要的原因我想應該是為了系統更健壯、更穩定。下面我就來說說“控制代碼”這種管理未命名核心對象的機制吧。

    為什麼要引入“控制代碼”的概念呢?為防止使用者代碼對核心對象造成破壞,Windows NT/2000中通過“控制代碼”這一機制避免將核心對象指標直接返回給使用者進程,而“控制代碼”除了此功能外,它還管理著進程特定擁有的所有核心對象,當然也包含未命名物件。SoftICE中的proc命令加-o選項可以得到特定進程擁有的所有控制代碼。但在檢索某一指定控制代碼時,SoftICE在使用過程中也感到不便,我曾試著使用Numega提供的KD2SYS.EXE將Windbg Extension DLL轉化成SoftICE使用的.SYS檔案,但在使用過程中也經常出現問題。後來,在分析了ObReferenceObjectByHandle核心常式後,我在SoftICE中定義了如下的宏(關於ObReferenceObjectByHandle分析過程與代碼我不列於此,您可以仔細認真跟蹤跟蹤):

    // 定義宏handle
    // handle <kpeb> <handle index>
    // 參數kpeb指定特定進程,handle index是控制代碼索引

    :macro handle="addr %1;what (@(@(@(@(@(%1+128)+8)))+(%2>>2)*8))|80000000;."
    Macro: 'handle' defined

    // 顯示System進程中handle index為4的控制代碼對象指標與物件類型名

    :handle dword(@PsInitialSystemProcess) 4
    The value 8148EBC8 is (a) Kernel Process object (handle=0004) for System(08)
              --------        ---------------------         ----   -------------    
                 |_控制代碼頭地址         |_控制代碼對應對象的類型    |      |_指明此控制代碼隸屬System進程
                                                              |_handle index

    這個宏我僅在Microsoft Windows 2000 Server Build 2195 Chinese Edition上測試通過。如上顯示輸出結果,其只顯示對象指標與物件類型名。要查看對象更詳細的資料您只能深入核心對象頭與對象體結構或是使用Windbg的!handle命令。這個宏已經將進程的控制代碼表的結構說明的很清楚了,而且對象的很多結構在我上面提供的代碼中也有應用到,我就不將具體實現代碼列出了,建議看看Mark Russinovich的HandleEx。

    關於標題所提到的Windows NT/2000核心對象組織還有非常多的內容,如核心對象的安全性設定等等,限於水平與精力,我也不可能在此都寫出。另外應該提出的是本文未涉及使用者模式的GDI對象等,其在組織上也有另一套的方式。在對Windows NT/2000的分析過程中,我初步感覺到Microsoft設計Windows NT/2000的許多先進思想,也越來越體會到分析的難度。很希望您在有所心得後,也能與大家交流交流,或是聯絡我(tsu00@263.net)!

參考資料:
      1.David Solomom《Inside Windows NT,2nd Edition》
      2.Mark Russinovich相關文檔

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.