2011年8月1日星期一

ZeroAccess:內核模式下的一個高級rootkit 分析

文/Marco Giuliani 

      ZeroAccess rootkit 的安裝是由dropper病毒感染而成,dropper病毒通常是用戶訪問到crack 或warez 網站時被惡意下載至客戶端或者是黑客使用漏洞利用工具包exploit packs 惡意加載dropper 到客戶端生成的。 Dropper 運用了大量反調試技術以降低代碼被分析的可能。在首次解壓縮後,代碼會嘗試獲取如下特權:SeDebugPrivilege (允許用戶綁定一個調試器到任何進程.可訪問敏感和關鍵的操作系統組件),SeTakeOwnershipPrivilege(取得文件或其他對象的所有權), SeRestorePrivilege(恢復數據權限), SeSystemtimePrivilege(更改系統時間權限), SeSecurityPrivilege(管理審核和安全日誌). 然後才會感染有效載荷。

      在詳細分析感染過程之前,有必要簡要分析下ZeroAccess 是如何感染系統的。Dropper病毒會在驅動目錄下如“C:\WINDOWS\system32\drivers”下隨機選擇一個驅動,並覆寫此驅動源代碼(覆寫之前備份源碼),編譯並加載此惡意驅動後,它會創建一個新的磁盤設備對象,用來作為rootkit 自身創建的捲隱藏驅動入口點,並保存它的文件和數據。

      儘管很類似TDL3 rootkit 的感染方式,但它很有效。 ZeroAccess 與TDL3 的主要區別:

   其一,ZeroAccess在文件系統內創建一個隱藏的加密新卷,而TDL3 則是利用硬件驅動的末尾幾個扇區創建一個嶄新的加密文件系統;其二,兩者的rootkit 都是感染一個隨機驅動,但ZeroAccess 是通過完整覆寫指定驅動的源代碼,而TDL3 則是劫持驅動的入口點,覆寫不超過1KB字節大小的驅動資源段;其三,磁盤I/O 驅動過濾引擎完全不一樣,TDL3 在性能上略勝一籌。

      下面我們將深入地分析ZeroAccess rootkit 中的驅動感染函數如何工作,以及rootkit如何選擇合適驅動進行感染:
    (1) rootkit 計算一個用來校驗驅動鏡像文件大小的特殊值,分析實例的值為0x7410(29712字節大小),即rootkit 內核驅動大小。

    (2) rootkit 調用SystemModuleInformation類中的成員函數ZwQuerySystemInformation 枚舉系統內所有驅動。

    (3) 目標驅動必須在​​定位在classpnp.sys驅動和win32k.sys 驅動之間。

    (4) 在classmp.sys和win32k.sys 之間且鏡像文件小於0x7410 的驅動將被放棄。

    (5) 大於0x7410值的所有驅動需校驗以下值:驅動文件名必須以“.sys”擴展名結尾;驅動寄存器鍵碼的起始值必須大於0 (驅動不應該在系統啟動時被啟動起來);驅動的PE 導出表必須為0 (驅動不應導出任何內容)。

    (6) 若第5步列出的要求都符合,則此驅動可標識為“良好的潛在目標”,即將SYSTEM_MODULE->ld 結構體設置為1.

    (7)循環遍歷所要滿足大於0x7410值的驅動,直到驅動全部分析完畢。

      第一輪循環遍歷完成後,找出所有具備條件的目標驅動,然後再進入第二輪的循環搜索,直至找到一個最適合感染的驅動,具體策略:其一,rootkit 調用GetTickCount( )和

   RtlRandom() 方法計算出一個隨機值;其二,使用RandomValue%NumberOfProtentialTargetsFount 等式初始化輪詢的計數器counter;其三rootkit 循環遍歷所有系統驅動,每找到一個具備條件的驅動(設置SYSTEM_MODULE->ld =1),counter 自減。

      當counter == 0 時,rootkit 確定出一個可感染的目標驅動,並創建一個新的section段,命名為\.<待感染的驅動名>,如“\.NdProxy”),以暫存一個未感染的(clean driver) 驅動源碼。隨後,Rootkit會在“HKLM\SYSTEM\CurrentControlSet\Services”下創建一個新服務的註冊表鍵值,鍵碼的ImagePath值設置成“\*.”,這是避免文件加載時被安全軟件攔截的一個巧點子。通過設置值“\*.”,安全軟件將會被愚弄過去,因為它並沒有指向任何一個真實文件,但實際上,rootkit的dropper已經通過調用API 函數ZwCreateSysbolicLinkObject 創建了一個新符號鏈接使其“\ *”指向一個真實文件。

      Dropper 通過整體覆寫內核模式下的驅動源碼方式感染目標驅動,並調用ZwLoadDriver函數加載驅動。在覆蓋驅動實體前,dropper 會首先暫停所有與sfc_os.dll 模塊相關的線程,最後確定係統文件校驗SFC線程是否暫停運行。在整個感染完成後,重新啟動已暫停線程。在執行受感染的有效載荷之前,dropper會檢驗自身是否運行在WoW64 的模擬環境下。

   若是,進程立刻終止。 Rootkit 當前並不會去感染X64 位的Windows 系統,而且dropper會校驗感染函數是否調用ZwOpenFile 函數嘗試打開了rootkit 設備,並運行在系統內部。若係統已經感染,rootkit 設備會返回一個NTSTATUS 錯誤即STATUS_VALIDATE_CONTINUE.
      Rootkit 驅動加載後,用戶模式下可以訪問到rootkit設備“\\?\ACPI#PNP0303#2&da1a3ff&0”,同時dropper也能使用NTFS文件系統格式化一個新卷,為了實現這些動作,它會加載fmifs .dll 模塊——針對可安裝的文件系統模塊格式化管理器,並導入API函數FormatEx( ).

     Format_Virtual_Drivproc near ; CODE XREF :Infection_Payload + 390
            pushesi
            push offset LibFileName ; "fmifs"
            callds:LoadLibraryW
            movesi, eax
            testesi, esi
            jz short loc_402032
            pushoffset_ProcName ; "FormatEx"
            pushesi ; hModule
            callds:GetProcAddress
            testeax, eax
            jz short loc_40202B
            push offset sub_401FEB
            push 0
            push 1
            push offset unk_40A3A0
            push offset aNtfs; "NTFS"
            push 0Bh

            push offset a?AcpiPnp03032D
            calleax
     loc_40202B: ;CODE XREF : Format_Virtual_Drive+20

     pushesi ; hLibModule
     call ds: FreeLibrary


      新的隱藏卷現在可以用來存放被覆蓋之前的驅動源碼實現。 Dropper 並不會使用真實的文件名,它會隨機產生一個文件名,具體操作如下所示:

    (1)rootkit 調用ZwQueryKey 函數,帶上參數KeyBasicInformation,查詢註冊表鍵值“HKLM\SYSTEM\CurrentControlSet\Control\agp ” ; 之後rootkit 會詢問參數值“_KEY_BASIC_INFORMATION->LastWriteTime”,並產生兩個特殊種子值,第一個值即通過LastWriteTime 參數的LowPart 和HighPart 做異或操作得到;第二個種子的值即在前一個種子值的基礎上再加上“LowPart+1”。完成步驟1後,rootkit 會使用一個啟動字符串變量用來產生一個隨機的字符串組成新的文件名。字符數組值為“eaoimnqazwsxedcrfvtgbyhnujmikolp”。

    (2)文件名由8 個字符構成,故函數按照以下方式循環獲取單個字符,種子Seed 和0x1F做與操作(0x1F 即字符數組長度),得到的值作為啟動數組StartingString 的索引,即新文件名的某個字符值;同時每次循環時,使用_allshr()函數使Seed 值右移5位。 (在內核模式的驅動程序中針對一個64 位的LARGE_INTEGER 使用移位操作符<<和>>,編譯器和鏈接器會自動導入ntoskrnl.exe 的_allshr() 和_allshl()函數)。

    (3 ) 直到完整構造出一個8 字節的字符串, 並將文件存儲在“\??\ACPI#PNP0303#2&da1a3ff&0\L\Snifer67”,其中Snifer67被替換成新產生的文件名,文件名生成函數如下:

    char *StartString = "";
    charFileName[9];
    DWORD index=7;
    RegOpenKeyA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\agp",
    &regKey);
    NtQueryKey(regKey, KeyBasicInformation, &KeyInfo,
    sizeof(KEY_BASIC_INFORMATION),&result);

    seed2 = (KeyInfo.LastWriteTime.HighPart ^ KeyInfo.LastWriteTime.LowPart);
    seed = (Seed2 + KeyInfo.LastWriteTime.LowPart + 1);
    while(index >= 0){
        FileName[index] = StartString[(seed & 0x1F)];
        _allshr(&seed, &seed2, 5);
         if(index == 0) break;
           index--;
    }

      文件名產生後,rootkit 設備內創建此文件,同時clean驅動的整體拷貝存放在此處。
                                  

                            內核模式Rootkit 感染

                      
      下面我們將更深入地分析ZeroAccess rootkit 感染後的內核驅動是如何工作的。由上小節可知,rootkit創建了一個命名為“ACPI#PNP0303#2&da1a3ff&0”的新設備對象,用作訪問rootkit 隱藏設備的入口Gate. 然後,它通過劫持與底層設備端口通信的disk.sys攔截Windows 的磁盤I/O. 此時,若用戶嘗試讀寫被攔截的驅動時,rootkit 會顯示原有驅動清潔拷貝來偽造文件內容。

       Rootkit 啟動後,它會校驗註冊表啟動項鍵值是否已執行來判斷其是否首次運行在系統上。如它來自.<drivername> (eg. .NdProxy)服務的註冊表鍵值,則斷定其是首次運行,此時rootkit 會刪除此鍵值。隨後,rootkit 讀取受感染的驅動路徑,並調用RtlHashUnicodeString 函數計算出驅動路徑和文件名的hash 值。 Rootkit 利用計算出的hash 來校驗是否有人嘗試訪問過受感染驅動。感染後的驅動備份拷貝存儲在內存中,由特殊的MDL 指向。

      此時,Rootkit 具備創建屬於自己的代碼的機會,調用IoCreateDriver ()函數,並設置自己的驅動對象,將對象隱藏在DriverSection 中,同時將它所有的派遣函數地址指向一個特殊的rootkit 派遣函數。為了隱藏新產生的驅動對象,rootkit 竊取原磁盤驅動“\driver\disk”的對象,並一個一字段的拷貝clean驅動disk.sys 的驅動對象成員到一個偽造的驅動對像上,兩者之間的每個成員地址如下:

   偽造的磁盤驅動結構體:
lkd>dt _DRIVER_OBJECT 0x8201f590
     nt!_DRIVER_OBJECT
        +0x000 Type : 4
        +0x002 Size : 168
        +0x004 DeviceObject : 0x81fd5040 _DEVICE_OBJECT
        +0x008 Flags: 0x12
        +0x00c DriverStart: 0xf86cb000
        +0x010 DriverSize : 0x8e00
        +0x014 DriverSection : 0x821edbc0
        +0x018 DriverExtension : 0x8201f638 _DRIVER_EXTENSION
        +0x01c DriverName : _UNICODE_STRING "\Driver\Disk"
        +0x024 HardwareDatabase : 0x8066e9d8 _UNICODE_STRING
     "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
        +0x028 FastIoDispatch : (null)
        +0x02c DriverInit : 0xf86d28ab long +fffffffff86d28ab
        +0x030 DriverStartIo : (null)
        +0x034 DriverUnload : (null)
        +0x038 MajorFunction : [28] 0xf4b79134 long +ffffffffff4b79134

   原磁盤驅動對象結構體:

     lkd>dt _DRIVER_OBJECT 821eb320
     nt!_DRIVER_OBJECT
        +0x000 Type : 4
        +0x002 Size : 168
        +0x004 DeviceObject : 0x821a89f0 _DEVICE_OBJECT
        +0x008 Flags : 0x12
        +0x00c DriverStart : 0xf86cb000
        +0x010 DriverSize : 0x8e00
 
        +0x014 DriverSection : 0x821edbc0
        +0x018 DriverExtension : 0x821eb3c8 _DRIVER_EXTENSION
        +0x01c DriverName : _UNICODE_STRING "\Driver\Disk"
        +0x024 HardwareDatabase : 0x8066e9d8 _UNICODE_STRING

     "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
        +0x028 FastIoDispatch : (null)
        +0x02c DriverInit : 0xf86d28ab long +fffffffff86d28ab
        +0x030 DriverStartIo : (null)

        +0x034 DriverUnload : 0xf86e253a
        +0x038 MajorFunction : [28] 0xf86e1c30 long +ffffffffff4b79134

       從上面的各結構體成員來看,除派遣函數和驅動對象(偽造的驅動對象指針指向自己構造的對象)外,其它成員值完全等同。 Rootkit 的驅動對象創建了兩個不同的設備對象,一個用來攔截發送給磁盤disk.sys 的I/O請求,另一個則指向隱藏設備對象,即訪問隱藏設備對象的入口地址。為了攔截磁盤驅動disk.sys 的I/O請求,rootkit 通過交換它的設備擴展對象結構體,實現設備對象“\driver\disk\DR0 ”設備對象的攔截。此時DR0_Device_Object->DevExtension->LowerDeviceObject 指針值被修改為rootkit 設備對象指針。此時rootkit 可以設置回調函數指針,待disk.sys 完成上層的I/O請求返回IRP時,IRP會轉到rootkit 的設備對象層,此時它可以分析IRP 內容並有效過濾部分內容,然後再將IRP 向上傳遞到端口設備驅動如atapi.sys等。

        Rootkit 分析IRP是否發送到偽造的設備對像上,若是,則它會調用自己的派遣函數處理IRP請求,在偽造的隱藏卷內,它可以處理所有IOCTL請求,如IOCTL_DISK_CHECK_VERIFY、IOCTL_DISK_IS_WRITEABLE等。隱藏卷是加密處理過的,rootkit 的讀寫請求能夠對磁盤上的數據進行加解碼操作。偽造的捲存儲在一個文件內,它位於“系統目錄\system32\config\<random file name>”,隨機名與dropper產生的文件名一致,並用來存儲感染驅動的拷貝。這個文件在磁盤上總是加密的,rootkit 使用的加密算法為RC4,密鑰長度為128 位,RC4 加解密操作是以扇區為單位進行的,加密密鑰內容為:
   “0xFF,0x7C,0xF1,0x64,0x12,0xE2,0x2D,​​0x4D,0xB1,0xCF,0x0F,0x5D,0x6F,0xE5,0xA0,0x49”,rootkit 的加解密處理如下:

      相反,若IRP沒有轉向rootkit 設備上來,派遣函數會分析請求包,在磁盤上的已感染驅動文件內搜索I/O 請求。 Rootkit 過濾IPR_MJ_INTERNAL_DEVICE_CONTROL 函數,並蒐索SCSI 請求塊結構。若SRB->Function == SRB_FUNCTION_EXECUTE_SCSI,過濾函數會繼續搜索。 Rootkit 檢驗文件對象結構體是否為未決IRP 請求填充的,若是,則調用RtlHashUnicodeString 函數計算文件路徑的hash值,同時計算rootkit 驅動啟動時受感染驅動路徑的hash 值,將兩者進行比較,若相等,則此IRP請求系rootkit 偽造。

      若SCSI_REQUEST_BLOCK 請求包操作是SCSIOP_READ,讀請求會傳遞給底層端口設備,返回的結果被rootkit 的CompletionRoutine 函數虛構;若操作為“SCSIOP_WRITE”,則底層返回的buffer 會被受感染的驅動rootkit 覆寫,ZeroAccess感染後的驅動代碼執行流如下圖所示:


       Rootkit 工作在TDI 網絡層,可繞過那些未在網絡層級監控網絡連接的防火牆及各類安全軟件,rootkit 發送一個加密請求給列表內的所有server 時,請求包會發送到遠端的TCP端口13620上。 Rootkit 通過下載並存儲一些相關文件到rootkit 的隱藏卷中,允許攻擊者放棄系統內的深層次感染,從而實現對安全軟件的透明。這些dropped文件以內核模式驅動的形式存在,因為rootkit 主驅動能夠在內核中通過調用IoCreateDriver()函數定位到它們。

                                  小結

       ZeroAccess 絕對是當前最先進的內核模式rootkit 之一,儘管它沒有TDL rootkit 家族系列那麼強大,但它具備很多獨有的特性,使其具備很大的威脅。 ZeroAccess 提供的一些強大的功能如修改並感染系統核心驅動如disk.sys,atapi.sys,並竊取PIC 驅動對象抵禦文件系統取證技術;用戶模式下的進程創建攔截與DLL 劫持;、DLL 隱藏與反病毒軟件繞過技術等。

没有评论:

发表评论