原文連結:http://advdbg.org/blogs/advdbg_system/articles/784.aspx
在Vista之前,NTLDR是Windows作業系統的載入程式,它負責將CPU從實模式切換為保護模式,載入核心檔案和啟動類型的驅動程式,然後把執行權交給核心檔案的入口函數,即KiSystemStartup。
從要完成的任務角度來看,NTLDR內部又分為兩個部分,一部分負責接受執行權,做模式切換,硬體檢查,即啟動的準備工作,這部分通常稱為boot;另一部分負責載入核心檔案,並為核心的運行做必要的準備,通常稱為OsLoader。
Vista將以上兩個部分分成兩個獨立的程式檔案,即BootMgr和WinLoad.exe。描述了BootMgr和WinLoad的相互關係和它們在啟動過程中的作用。
與調試NTLDR需要替換check版本的NTLDR不同,在BootMgr和WinLoad內部都已經內建了偵錯引擎,這與核心的做法是一致的。因此,如果要調試BootMgr和WinLoad只要啟動它們內部的偵錯引擎,不需要替換這兩個程式檔案。
啟用BOOTMGR調試的方法是在具有管理員權限的控制台視窗中執行如下命令:
bcdedit /set {bootmgr} bootdebug on
bcdedit /set {bootmgr} debugtype serial
bcdedit /set {bootmgr} debugport 1
bcdedit /set {bootmgr} baudrate 115200
其中後三行是設定調試器與偵錯引擎的串連方式,如果使用1394,那麼應該設定為:
bcdedit /set {bootmgr} debugtype 1394
bcdedit /set {bootmgr} channel 22
做以上設定後,重新啟動系統,BIOS階段過後,螢幕會保持黑色,呈現無法繼續啟動的樣子,事實上,這是BootMgr在等待與調試器建立串連。串連好電纜後,啟動WinDBG調試器,二者就會建立串連,顯示類似下面這樣的資訊:
Microsoft (R) Windows Debugger Version 6.9.0003.113 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Opened \\.\com1
Waiting to reconnect...
BD: Boot Debugger Initialized
Connected to Windows Boot Debugger 6000 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*d:\symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows Boot Debugger Kernel Version 6000 UP Free x86 compatible
Product: unknown <ffffffff>, suite: SmallBusiness Enterprise BackOffice CommunicationServer TerminalServer SmallBusinessRestricted EmbeddedNT DataCenter SingleUserTS Personal Blade EmbeddedRestricted SecurityAppliance StorageServer ComputeServer <8000>
Primary image base = 0x00400000 Loaded module list = 0x004ffff8
System Uptime: not available
其中0x00400000是BootMgr模組的基地址。BootMgr的主要代碼都是32位的,只有一小部分16位代碼,負責從韌體那裡接過控制權。另外,值得說明的是,BootMgr是工作在保護模式下的,但它沒有啟用分頁機制,只是使用分頁機制。觀察CR0和CR3寄存器的取值,可以證明這一點:
kd> r cr0
cr0=00000011
kd> r cr3
cr3=00000000
建立串連後,WinDBG會讓BootMgr繼續運行,BootMgr通常會顯示啟動菜單。此時按下Ctrl+Break鍵,那麼BootMgr就會中斷到調試器中,此時可以執行各種核心調試命令,比如:
kd> lm
start end module name
00400000 00514000 bootmgr (pdb symbols) d:\symbols\bootmgr.pdb\819F5A93195D47E3857ED729B0D341191\bootmgr.pdb
微軟的符號伺服器提供了BootMgr的公開符號檔案,因此只要設定好符號伺服器的路徑,那麼WinDBG就可以自動下載合適的符號檔案。
kd> x bootmgr!*
0042b3fd bootmgr!TpmApiAuthSessCreateOSAP = <no type information>
0044c4da bootmgr!CmpGetIndexElementSize = <no type information>
00452d54 bootmgr!RcConsoleBaudRates = <no type information>
0042f56f bootmgr!OsxmlBrowser::~OsxmlBrowser = <no type information>
00431b10 bootmgr!XslStylesheet::getContents = <no type information>
.......
當然也可以使用棧回溯命令來觀察函數調用情況:
kd> kv
ChildEBP RetAddr Args to Child
00061e34 00430c7e 001b6380 00000001 00061e90 bootmgr!DbgBreakPoint
00061e44 0043024f 00061e8c 001b63b8 00061e88 bootmgr!BlXmlConsole::getInput+0xe
00061e90 004024c1 00000002 004021f3 00000000 bootmgr!OsxmlBrowser::browse+0xe0
00061e98 004021f3 00000000 00000000 00061f8f bootmgr!BmDisplayGetBootMenuStatus+0x13 (FPO: [0,0,1])
00061f10 004017a4 00104f9c 001f4280 00000002 bootmgr!BmDisplayBootMenu+0x174
00061f6c 00401261 00104f9c 00061f9c 00000002 bootmgr!BmpGetSelectedBootEntry+0xf8
00061ff0 000209ea 000249a8 7d8b697c 74ff8514 bootmgr!BmMain+0x261
WARNING: Frame IP not in any known module. Following frames may be wrong.
00000000 f000ff53 f000e2c3 f000ff53 f000ff53 0x209ea
00000000 00000000 f000e2c3 f000ff53 f000ff53 0xf000ff53
通過以上棧回溯可以看到,BmMain是BootMgr的32位代碼入口函數;BmDisplayBootMenu是負責顯示和管理啟動菜單的主要函數。BmMain下方的幾個棧幀應該是16位代碼執行時留下的痕迹。
對BmpGetSelectedBootEntry函數的返回地址00401261設定個斷點後,按F5讓目標系統繼續執行,此時目標系統的菜單可以活動了,選擇引導Vista的一項後,剛才設定的斷點立刻命中,單步跟蹤可以看到BootMgr載入WinLoad的過程,其中的主要函數有:
bootmgr!BmpLaunchBootEntry
bootmgr!ImgArchPcatStartBootApplication
在BootMgr將執行權交給WinLoad之前,它會調用BlBdStop與調試器中斷連線。
如果想繼續調試WinLoad,那麼應該先啟用WinLoad中的偵錯引擎,其命令是:
bcdedit /set {GUID} bootdebug on
WinLoad啟動後,如果它的偵錯引擎已經啟用,那麼偵錯引擎也會與調試器建立串連,並顯示與前面類似的建立連結資訊。以下是WinLoad中的幾個重要函數:
winload!MmArchInitialize
winload!ArchBuildKernelGdt
winload!OslArchpKernelSetupPhase0
winload!OslArchTransferToKernel
其中最後一個負責將執行權轉交給核心。移交執行權之前,WinLoad也會調用BlBdStop函數來切斷偵錯工作階段。如果要繼續調試核心,那麼應該先啟用核心調試,然後在核心階段再與其建立串連。
如果系統是從休眠中恢複,那麼BootMgr會載入WinResume,WinResume中也整合了一個偵錯引擎,因此也可以對其進行跟蹤和調試,例如,下面的棧回溯顯示了WinResume程式初始化的過程:
kd> kPL
ChildEBP RetAddr
00120ad8 0059da7f winresume!RtlpBreakWithStatusInstruction
00120cf0 0059d97d winresume!vDbgPrintExWithPrefixInternal+0x100
00120d00 0059641a winresume!DbgPrint+0x11
00120d28 00587550 winresume!BlStatusPrint+0x60
00120d6c 00585524 winresume!ConsoleFirmwareOpen+0x64
00120d88 0058373d winresume!ConsoleOpen+0x58
00120da8 00583242 winresume!BlpDeviceOpen+0x13c
00120dc4 0058bd5b winresume!BlDeviceOpen+0x16
00120ed8 0058bcc7 winresume!DsppRegisterConsole+0x56
00120f6c 0058b999 winresume!DsppInitialize+0xfd
00120f78 005950a2 winresume!BlpDisplayInitialize+0x26
00120f94 0057c0c6 winresume!InitializeLibrary+0x176
00120fe8 004444fa winresume!HbMain+0xc6
WARNING: Frame IP not in any known module. Following frames may be wrong.
00000000 f000eef3 0x4444fa
00000000 00000000 0xf000eef3
概而言之,在一個安裝好的Vista系統中,已經內建了四個偵錯引擎,分別位於BootMgr、WinLoad、WinResume和NTOSKRNL.exe中,可以將這幾個偵錯引擎同時啟用,也可以根據需要啟用其中的某一個。前三個偵錯引擎是根據位於核心中的核心偵錯引擎(KD)複製出來的,它們使用與KD相容的協議,對調試器(WinDBG)來說,它並不區分對方是真正的KD還是KD的複製。因此在BootMgr中斷偵錯工作階段時,WinDBG提示的資訊和核心退出時的資訊一樣的。