創(chuàng)建時間:2004-08-05 文章屬性:原創(chuàng) 文章提交:tombkeeper (t0mbkeeper_at_hotmail.com) 用Bochs調(diào)試NTLDR 作 者:于旸 郵 件:tombkeeper[0x40]nsfocus[0x2e]com tombkeeper[0x40]xfocus[0x2e]org 完成于:2004.7.9 關(guān)鍵字:bochs, bochsdbg, ntldr, 調(diào)試 對一臺安裝了Windows NT 系列操作系統(tǒng)的PC來說,按下電源開關(guān)之后,CPU中首 先開始運行的是Bios,然后是MBR,接著是引導(dǎo)扇,然后就是NTLDR。ntoskrnl.exe和 hal.dll 都是由NTLDR來加載的。也就是說,運行NTLDR的時候,系統(tǒng)中還沒有任何應(yīng) 用程序或者驅(qū)動,當然也就沒有任何基于軟件的調(diào)試器可用。當然,無所不能的硬件 調(diào)試器肯定是可以的,可惜我們沒有硬件調(diào)試器。 幸好有了Bochs。Bochs是一個基于LGPL的開源x86 虛擬機軟件。Bochs的CPU指令 是完全自己模擬出來的,這種方式的缺點是速度比較慢;優(yōu)點是具有無以倫比的可移 植性:有Gcc的地方就可以有Bochs。甚至已經(jīng)有了跑在PocketPC上的Bochs。 現(xiàn)在的Bochs 已經(jīng)實現(xiàn)了一定程度的調(diào)試功能,雖然在易用性和功能上還無法和 WinDbg、SoftICE相比,但優(yōu)勢也是很明顯的:對跑在Bochs里面的代碼來說,這就是 “硬件調(diào)試器”。 對Windows 版本的Bochs來說,安裝目錄下的bochsdbg.exe就是Bochs的調(diào)試版本。 用它來運行Bochs虛擬機就可以進行“硬件調(diào)試”。 Bochs的調(diào)試命令風(fēng)格是按照GDB習(xí)慣來設(shè)計的,這對于用慣了WinDbg的人來說無 疑是痛苦的,好在這是個開源軟件,看著不順眼可以考慮自己改改。 目前版本的Bochs(Version 2.1.1)支持的調(diào)試命令如下: [注意] 1、Bochs的文檔和幫助信息中的使用說明與真實情況之間存在很大的差錯和缺失, 下面的命令說明根據(jù)源碼作了很多補充和修正。 2、其中涉及到的seg(段)、off(偏移)、addr(地址)、val(值)等數(shù)字, 可以使用十六進制、十進制或者八進制,但必須按照如下形式書寫: 十六進制 0xCDEF0123 八進制 01234567 十進制 123456789 尤其要注意,Bochs不能自動識別16進制的數(shù)字,也不接受12345678h這種寫法。 [執(zhí)行控制] c|cont 向下執(zhí)行,相當于WinDBG的“g”。 s|step|stepi [count] 單步執(zhí)行,相當于WinDBG的“t”,count 默認為 1。 p|n|next 單步執(zhí)行,類似于WinDBG的“p”。 q|quit|exit 退出調(diào)試,同時關(guān)閉虛擬機。 Ctrl-C 結(jié)束執(zhí)行狀態(tài),返回調(diào)試器提示符。 Ctrl-D if at empty line on command line, exit (至少在Windows版本中我沒有發(fā)現(xiàn)Ctrl-D有什么功能) [執(zhí)行斷點] vb|vbreak [seg:off] 在虛擬地址上下斷點。 lb|lbreak [addr] 在線性地址上下斷點,相當于WinDBG的“bp”。 pb|pbreak|b|break [addr] 在物理地址上下斷點。(為了兼容GDB的語法,地址前 可以加上一個“*”)。 blist 顯示斷點狀態(tài),相當于WinDBG的“bl”。 bpd|bpe [num] 禁用/啟用斷點,WinDBG的“be”和“bd”。num是斷 點號,可以用blist命令查詢。 d|del|delete [num] 刪除斷點,相當于WinDBG的“bc”。mum是斷點號,可 以用blist命令查詢。 [讀寫斷點] watch read [addr] 設(shè)置讀斷點。 watch write [addr] 設(shè)置寫斷點。 unwatch read [addr] 清除讀斷點。 unwatch write [addr] 清除寫斷點。 watch 顯示當前所有讀寫斷點。 unwatch 清除當前所有讀寫斷點。 watch stop|continue 開關(guān)選項,設(shè)置遇到讀寫斷點時中斷下來還是顯示出來但 是繼續(xù)運行。 [內(nèi)存操作] x /nuf [addr] 顯示線性地址的內(nèi)容 xp /nuf [addr] 顯示物理地址的內(nèi)容 n 顯示的單元數(shù) u 每個顯示單元的大小,u可以是下列之一: b BYTE h WORD w DWORD g DWORD64 注意: 這種命名法是按照GDB習(xí)慣的,而并不是按照inter的規(guī)范。 f 顯示格式,f可以是下列之一: x 按照十六進制顯示 d 十進制顯示 u 按照無符號十進制顯示 o 按照八進制顯示 t 按照二進制顯示 c 按照字符顯示 n、f、u是可選參數(shù),如果不指定,則u默認是w,f默認是x。如果前面使用過x或 者xp命令,會按照上一次的x或者xp命令所使用的值。n默認為1。addr 也是一個 可選參數(shù),如果不指定,addr是0,如過前面使用過x或者xp命令,指定了n=i, 則再次執(zhí)行時n默認為i+1。 setpmem [addr] [size] [val] 設(shè)置物理內(nèi)存某地址的內(nèi)容。 需要注意的是,每次最多只能設(shè)置一個DWORD: 這樣是可以的: <bochs:1> setpmem 0x00000000 0x4 0x11223344 <bochs:2> x /4 0x00000000 [bochs]: 0x00000000 <bogus+ 0>: 0x11223344 0x00000000 0x00000000 0x00000000 這樣也可以: <bochs:1> setpmem 0x00000000 0x2 0x11223344 <bochs:2> x /4 0x00000000 [bochs]: 0x00000000 <bogus+ 0>: 0x00003344 0x00000000 0x00000000 0x00000000 或者: <bochs:1> setpmem 0x00000000 0x1 0x20 <bochs:2> x /4 0x00000000 [bochs]: 0x00000000 <bogus+ 0>: 0x00000020 0x00000000 0x00000000 0x00000000 下面的做法都會導(dǎo)致出錯: <bochs:1> setpmem 0x00000000 0x3 0x112233 Error: setpmem: bad length value = 3 <bochs:2> setpmem 0x00000000 0x8 0x11223344 Error: setpmem: bad length value = 8 crc [start] [end] 顯示物理地址start到end之間數(shù)據(jù)的CRC。 [寄存器操作] set $reg = val 設(shè)置寄存器的值?,F(xiàn)在版本可以設(shè)置的寄存器包括: eax ecx edx ebx esp ebp esi edi 暫時不能設(shè)置: eflags cs ss ds es fs gs r|reg|registers reg = val 同上。 dump_cpu 顯示完整的CPU信息。 set_cpu 設(shè)置CPU狀態(tài),這里可以設(shè)置dump_cpu所能顯示出來的 所有CPU狀態(tài)。 [反匯編命令] u|disas|disassemble [/num] [start] [end] 反匯編物理地址start到end 之間的代碼,如 果不指定參數(shù)則反匯編當前EIP指向的代碼。 num是可選參數(shù),指定處理的代碼量。 set $disassemble_size = 0|16|32 $disassemble_size變量指定反匯編使用的段 大小。 set $auto_disassemble = 0|1 $auto_disassemble決定每次執(zhí)行中斷下來的 時候(例如遇到斷點、Ctrl-C等)是否反匯 編當前指令。 [其他命令] trace-on|trace-off Tracing開關(guān)打開后,每執(zhí)行一條指令都會將反匯編的結(jié)果 顯示出來。 ptime 顯示Bochs自本次運行以來執(zhí)行的指令條數(shù)。 sb [val] 再執(zhí)行val條指令就中斷。val是64-bit整數(shù),以L結(jié)尾,形 如“1000L” sba [val] 執(zhí)行到Bochs自本次運行以來的第val條指令就中斷。val是 64-bit整數(shù),以L結(jié)尾,形如“1000L” modebp 設(shè)置切換到v86模式時中斷。 record ["filename"] 將輸入的調(diào)試指令記錄到文件中。文件名必須包含引號。 playback ["filename"] 回放record的記錄文件。文件名必須包含引號。 print-stack [num] 顯示堆棧,num默認為16,表示打印的條數(shù)。 |calc 和WinDBG的“?”命令類似,計算表達式的值。 load-symbols [global] filename [offset] 載入符號文件。如果設(shè)定了“global”關(guān)鍵字,則符號針 對所有上下文都有效。offset會默認加到所有的symbol地 址上。symbol文件的格式為:"%x %s"。 [info命令] info program 顯示程序執(zhí)行的情況。 info registers|reg|r 顯示寄存器的信息。 info pb|pbreak|b|break 相當于blist info dirty 顯示臟頁的頁地址。 info cpu 顯示所有CPU寄存器的值。 info fpu 顯示所有FPU寄存器的值。 info idt 顯示IDT。 info gdt [num] 顯示GDT。 info ldt 顯示LDT。 info tss 顯示TSS。 info pic 顯示PIC。 info ivt [num] [num] 顯示IVT。 info flags 顯示狀態(tài)寄存器。 info cr 顯示CR系列寄存器。 info symbols 顯示symbol信息。 info ne2k|ne2000 顯示虛擬的ne2k網(wǎng)卡信息。 弄明白了調(diào)試命令,接下來就可以著手進行NTLDR的調(diào)試工作了。下面所進行的工 作都是在Windows版Bochs 2.1.1上實現(xiàn)的。我們假設(shè)讀者了解Bochs的基本使用方法和 術(shù)語。 首先要安裝一個Windows NT 4的Bochs虛擬機。 1、創(chuàng)建虛擬硬盤。 運行bximage.exe,創(chuàng)建一個500M、flat模式的虛擬硬盤文件“C.img”。 2、創(chuàng)建一個Windows NT安裝光盤的ISO文件“nt.iso” 如果你打算直接用光盤安裝,也可以省去這一步。 3、創(chuàng)建bochsrc.txt 內(nèi)容可參考下面: ############################################################### megs: 32 romimage: file=$BXSHARE\BIOS-bochs-latest, address=0xf0000 vgaromimage: $BXSHARE\VGABIOS-lgpl-latest ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0-master: type=disk, path="C.img", mode=flat, cylinders=1015, heads=16, spt=63 ata0-slave: type=cdrom, path="nt.iso", status=inserted newharddrivesupport: enabled=1 boot: cdrom log: nul mouse: enabled=1 clock: sync=realtime, time0=local ############################################################### 4、創(chuàng)建start.bat 內(nèi)容如下: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::假設(shè)你的Bochs安裝在D:\Program\Bochs set BXSHARE=D:\Program\Bochs %BXSHARE%\bochs.exe -q -f bochsrc.txt ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 把C.img、nt.iso、bochsrc.txt、start.bat放到同一個目錄下,運行start.bat, 進行Windows NT的安裝。 事實上,如果只是為了調(diào)試MBR、引導(dǎo)扇和NTLDR 的話,并沒有必要安裝完整的操 作系統(tǒng),只要根目錄下有ntldr等那幾個文件就可以了。這里之所以安裝Windows NT而 不是Windows 2000或者更高版本,一方面是考慮速度問題,另一方面,Windows NT 是 可以在Bochs上確保順利完成安裝的。如果要調(diào)試Windows 2000/XP/2003 的NTLDR,只 需用這些操作系統(tǒng)的ntldr文件替換Windows NT的即可。 安裝完Windows NT之后,可以進行NTLDR的調(diào)試了。把start.bat中的“bochs.exe” 換成“bochsdbg.exe”。然后運行start.bat。 下面是操作的屏幕拷貝: ======================================================================== Bochs x86 Emulator 2.1.1 February 08, 2004 ======================================================================== 00000000000i[ ] reading configuration from bochsrc.txt 00000000000i[ ] installing win32 module as the Bochs GUI 00000000000i[ ] Warning: no rc file specified. 00000000000i[ ] using log file nul Next at t=0 //啟動bochsdbg.exe,會自動停在Bios的第一條指令上。 (0) context not implemented because BX_HAVE_HASH_MAP=0 [0x000ffff0] f000:fff0 (unk. ctxt): jmp f000:e05b ; ea5be000f0 <bochs:1> b 0x00007c00 //MBR和引導(dǎo)扇都會加載在0000:7c00。 <bochs:2> c (0) Breakpoint 1, 0x7c00 in ?? () //第一次會在MBR上中斷下來。 Next at t=772567 (0) [0x00007c00] 0000:7c00 (unk. ctxt): cli ; fa <bochs:3> c (0) Breakpoint 1, 0x7c00 in ?? () //第二次會在引導(dǎo)扇上中斷。 Next at t=773872 (0) [0x00007c00] 0000:7c00 (unk. ctxt): jmp 0x7c5d ; eb5b <bochs:4>b 0x00020000 //ntldr會加載在2000:0000,事實上無論是CDFS、NTFS還是FAT, //Windows加載啟動文件都是這個地址。 <bochs:5> c (0) Breakpoint 2, 0x20000 in ?? () //在NTLDR的第一條指令上斷下來了,可以開始進行調(diào)試。 Next at t=861712 (0) [0x00020000] 2000:0000 (unk. ctxt): jmp 0x1f6 ; e9f301 現(xiàn)在,我們可以像上帝俯看蕓蕓眾生一樣,看著操作系統(tǒng)一步一步啟動起來,一 切盡在眼底,甚至可以看到系統(tǒng)啟動過程中實模式切換到保護模式的情景: (0).[28734582] [0x00020247] 2000:0247 (unk. ctxt): opsize or eax, 0x1 ; 6683c801 (0).[28734583] [0x0002024b] 2000:024b (unk. ctxt): mov cr0, eax ; 0f22c0 (0).[28734584] [0x0002024e] 2000:0000024e (unk. ctxt): xchg bx, bx ; 87db (0).[28734585] [0x00020250] 2000:00000250 (unk. ctxt): jmp 0x253 ; eb01 (0).[28734586] [0x00020253] 2000:00000253 (unk. ctxt): push 0x58 ; 6a58 (0).[28734587] [0x00020255] 2000:00000255 (unk. ctxt): push 0x259 ; 685902 (0).[28734588] [0x00020258] 2000:00000258 (unk. ctxt): retf ; cb 參考文獻: Bochs的文檔和源碼。 |