1.1 實(shí)驗(yàn)環(huán)境搭建
1.1.1 bochs
Bochs 是一個(gè)開(kāi)源的 x86 模擬器和調(diào)試器,允許您在虛擬環(huán)境中模擬 x86 架構(gòu)的計(jì)算機(jī)系統(tǒng)。Bochs 的主要用途之一是用于開(kāi)發(fā)和調(diào)試操作系統(tǒng)內(nèi)核、嵌入式系統(tǒng)以及其他與低級(jí)系統(tǒng)編程相關(guān)的任務(wù)。它提供了一種方式來(lái)模擬整個(gè)計(jì)算機(jī)系統(tǒng),包括處理器、內(nèi)存、設(shè)備和外部接口,使您可以在不需要物理硬件的情況下進(jìn)行系統(tǒng)級(jí)別的實(shí)驗(yàn)和調(diào)試。
windows11 + bochs虛擬機(jī)
bochs下載地址Bochs x86 PC emulator - Browse /bochs at SourceForge.nethttps://sourceforge.net/projects/bochs/files/bochs/
注:
1.安裝時(shí),可以勾選下圖所示的“DLX Linux Demo”,這樣安裝時(shí)會(huì)帶有一個(gè)Linux 1.x版本的模擬示例。?
2.最好是把?Bochs
?直接安裝到?C:\
?下,路徑名中間不能有空格
安裝好后bochs根目錄如下:
應(yīng)用程序:
-
bochs.exe直接運(yùn)行虛擬機(jī)
-
bochsdbg.exe可以從頭開(kāi)始調(diào)試
-
bximage.exe可以用于生成軟盤(pán)或磁盤(pán)鏡像
其他文件:主要用于配置虛擬機(jī)
-
BIOS系統(tǒng)鏡像,有BIOS-bochs-latest和BIOS-bochs-legacy
-
VGABIOS鏡像,提供屏幕接口
-
鍵盤(pán)映射,在keymaps文件中
1.1.2 Linux 0.00
從網(wǎng)站上下載源代碼
鏈接:https://pan.baidu.com/s/1t5yz5-XsqUmY-POA0O9pnQ? 提取碼:ixm3?
-
Linux000?code/
?目錄下是源碼,經(jīng)修改后可編譯成功。 -
windows
?下可直接運(yùn)行?Linux000.bat
-
linux000_gui.bxrc
?是?Bochs
?配置文件 -
Image
?是生成的?Linux?0.00
?的二進(jìn)制文件,被加載到軟驅(qū),由?Bochs
?從配置文件讀取并啟動(dòng)執(zhí)行。
1.1.3運(yùn)行Linux 0.00
-
打開(kāi) bochsdbg.execc?
- ?點(diǎn)擊load,選擇Linux000文件下的linux000_gui.bxrc文件,然后點(diǎn)擊start出現(xiàn)如下界面
即可開(kāi)始調(diào)試
1.2 實(shí)驗(yàn)內(nèi)容
1.2.1?掌握如何手寫(xiě)B(tài)ochs虛擬機(jī)的配置文件
1.簡(jiǎn)介?Bochs
?虛擬機(jī)的配置文件????????
????????Bochs的配置文件是一個(gè)文本文件,通常命名為 bochsrc.txt
,它包含了用于定義虛擬機(jī)的各種參數(shù)和選項(xiàng)的設(shè)置。這些參數(shù)和選項(xiàng)允許配置虛擬機(jī)的硬件、啟動(dòng)選項(xiàng)、內(nèi)存分配、設(shè)備模擬和其他相關(guān)設(shè)置。

bochsrc.txt
,它包含了Bochs虛擬機(jī)的各種設(shè)置和參數(shù)。這個(gè)配置文件對(duì)于定義虛擬機(jī)的硬件和軟件環(huán)境非常重要。
# Configuration file for Bochs
# Sample bochsrc.txt file
# 設(shè)置了虛擬機(jī)使用的BIOS鏡像文件,BIOS-bochs-latest 是BIOS鏡像文件的名稱
romimage: file=BIOS-bochs-latest
# 設(shè)置了虛擬機(jī)使用的VGA BIOS鏡像文件,VGABIOS-lgpl-latest 是VGA BIOS鏡像文件的名稱
vgaromimage: file=VGABIOS-lgpl-latest
# 內(nèi)存分配,在這里,虛擬機(jī)將被分配32兆字節(jié)(MB)的內(nèi)存
megs: 32
# 設(shè)置了虛擬CPU的性能參數(shù),ips代表每秒執(zhí)行的指令數(shù)
cpu: ips=1000000
# 設(shè)置設(shè)備從軟驅(qū)啟動(dòng)
boot: floppy
# 確保虛擬機(jī)配置文件中有正確配置的軟驅(qū)設(shè)備
floppya: 1_44=floppy.img, status=inserted
# 設(shè)置設(shè)備從硬盤(pán)啟動(dòng)
boot: disk
# 確保虛擬機(jī)配置文件中正確配置了硬盤(pán)設(shè)備
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14, type=disk, path=harddisk.img, cylinders=1024, heads=16, spt=63
# 設(shè)置涉及虛擬機(jī)中的網(wǎng)絡(luò)適配器
ne2k: ioaddr=0x300, irq=9
# 設(shè)置涉及虛擬機(jī)的日志和內(nèi)部調(diào)試功能
debug: action=ignore, guest_os=dos, parport1=none
log: bochsout.txt
debug: internal
2.如何設(shè)置從軟驅(qū)啟動(dòng)
編輯Bochs的配置文件,通常是 bochsrc.txt
在配置文件中,找到以下行并確保正確設(shè)置啟動(dòng)選項(xiàng)為 floppy,并
確保虛擬機(jī)配置文件中有正確配置的軟驅(qū)設(shè)備。
boot: floppy
floppya: 1_44=a.img, status=inserted
3.如何設(shè)置從硬盤(pán)啟動(dòng)
在配置文件中,找到以下行并確保正確設(shè)置啟動(dòng)選項(xiàng)為 disk,并
確保虛擬機(jī)配置文件中正確配置了硬盤(pán)設(shè)備。
boot: disk
ata0-master: type=disk, path="harddisk.img", mode=flat, cylinders=1024, heads=16, spt=63
4.如何設(shè)置調(diào)試選項(xiàng)
在配置文件中,找到調(diào)試選項(xiàng)的部分或在文件末尾添加以下內(nèi)容以配置調(diào)試選項(xiàng)
# 啟用調(diào)試輸出
debug: action="option_name", parameter="value"
# 例如,設(shè)置調(diào)試動(dòng)作為打印所有調(diào)試信息
debug: action="debug", option="all"
# 可以設(shè)置其他選項(xiàng),例如設(shè)置斷點(diǎn)
debug: action="bpoint", name="my_breakpoint", type=address, addr=0x1234
action
:指定要執(zhí)行的調(diào)試操作,例如 "debug"(打印調(diào)試信息)或 "bpoint"(設(shè)置斷點(diǎn))等。
option
:如果存在,可以設(shè)置特定的調(diào)試選項(xiàng),例如 "all"(打印所有調(diào)試信息)。
name
:為斷點(diǎn)指定一個(gè)名稱,用于標(biāo)識(shí)它。
type
:指定斷點(diǎn)的類型,可以是address
(地址斷點(diǎn))等。
addr
:如果設(shè)置了地址斷點(diǎn),指定要設(shè)置的地址。
1.2.2?掌握Bochs虛擬機(jī)的調(diào)試技巧
1.如何單步跟蹤
輸入s命令即可單步執(zhí)行,每次輸入 s
命令,虛擬機(jī)將執(zhí)行當(dāng)前指令并停在下一條指令之前。
s
也可以點(diǎn)擊上方的step[s]按鍵
2. 如何設(shè)置斷點(diǎn)進(jìn)行調(diào)試
使用 b
命令設(shè)置斷點(diǎn)。命令的基本語(yǔ)法如下
b <address>
# <address>:指定要設(shè)置斷點(diǎn)的內(nèi)存地址或符號(hào)
Bochs 將在執(zhí)行到指定地址時(shí)停止
使用 info break
命令可以列出當(dāng)前設(shè)置的斷點(diǎn),以查看斷點(diǎn)的狀態(tài)和地址
若要清除斷點(diǎn),使用 bc
命令,然后輸入要?jiǎng)h除的斷點(diǎn)號(hào)
3.?如何查看通用寄存器的值

使用 r
命令來(lái)查看通用寄存器的值。命令的基本語(yǔ)法如下:
r <register_name>
# <register_name>:指定要查看的寄存器名稱,如 eax、ebx、ecx、edx、esi、edi、esp 或 ebp 等。
還可以使用 info reg
命令來(lái)一次性查看所有通用寄存器的值
4. 如何查看系統(tǒng)寄存器的值

只關(guān)心特定系統(tǒng)寄存器的值,可以使用相應(yīng)的命令來(lái)查看
info reg cr0
# Bochs 將顯示 CR0 寄存器的當(dāng)前值
5. 如何查看內(nèi)存指定位置的值
使用 x
命令來(lái)查看內(nèi)存中特定位置的值。命令的基本語(yǔ)法如下:
x /<count><format> <address>
# <count>:指定要查看的數(shù)據(jù)項(xiàng)數(shù)量。
# <format>:指定數(shù)據(jù)的顯示格式,如 b(字節(jié))、w(字)、d(雙字)等。
# <address>:指定要查看的內(nèi)存地址。
# 例如,要查看內(nèi)存地址0x1234處的雙字(32位數(shù)據(jù))的內(nèi)容
x /1wd 0x1234
6. 如何查看各種表,如?gdt
?,idt
?,ldt
?等
查看 GDT:
使用 info gdt
命令來(lái)查看全局描述符表 (GDT) 的內(nèi)容
info gdt
查看 IDT:
使用 info idt
命令來(lái)查看中斷描述符表 (IDT) 的內(nèi)容。
info idt
查看 LDT:使用 info ldt
命令來(lái)查看局部描述符表 (LDT) 的內(nèi)容。
info ldt
7.?如何查看?TSS
使用 info tss
命令來(lái)查看任務(wù)狀態(tài)段(TSS)
info tss
8.?
如何查看棧中的內(nèi)容
使用 info stack
命令來(lái)查看棧中的內(nèi)容
info stack
若要查看特定內(nèi)存地址處的棧內(nèi)容,可以使用 x
命令,類似于查看內(nèi)存地址的方式
9. 如何在內(nèi)存指定地方進(jìn)行反匯編
使用 disasm
命令來(lái)進(jìn)行反匯編。命令的基本語(yǔ)法如下:
disasm <address> <count>
# <address>:指定要反匯編的內(nèi)存地址。
# <count>:指定要反匯編的指令數(shù)量。
# 例如,要反匯編從內(nèi)存地址0x1234開(kāi)始的10條指令
disasm 0x1234 10
?1.2.3?計(jì)算機(jī)引導(dǎo)程序
1.如何查看?0x7c00
?處被裝載了什么?
????????要查看在內(nèi)存地址0x7C00處加載了什么內(nèi)容,通常是查看引導(dǎo)扇區(qū)的內(nèi)容,因?yàn)樵趚86體系結(jié)構(gòu)中,計(jì)算機(jī)通常從0x7C00處開(kāi)始加載引導(dǎo)扇區(qū)
使用 x
命令來(lái)查看內(nèi)存地址0x7C00處的內(nèi)容。命令的基本語(yǔ)法如下:
x /<count><format> 0x7C00
# <count>:指定要查看的數(shù)據(jù)項(xiàng)數(shù)量。
# <format>:指定數(shù)據(jù)的顯示格式,如 b(字節(jié))、w(字)、d(雙字)等。
2.如何把真正的內(nèi)核程序從硬盤(pán)或軟驅(qū)裝載到自己想要放的地方;
????????將真正的內(nèi)核程序從硬盤(pán)或軟驅(qū)裝載到指定位置通常需要編寫(xiě)引導(dǎo)加載程序,這是一個(gè)低級(jí)程序,負(fù)責(zé)加載操作系統(tǒng)內(nèi)核。
????????利用引導(dǎo)啟動(dòng)程序boot.s , boot.s 主要功能就是將軟盤(pán)或映像文件中的 head 內(nèi)核diamagnetic加載到內(nèi)存中某個(gè)指定位置處,并在設(shè)置臨時(shí) GDT 表等信息后,把處 理器設(shè)置成運(yùn)行在保護(hù)模式下,然后跳轉(zhuǎn)到hed 代碼處去運(yùn)行代碼。????????實(shí)際上,boot.s 程序會(huì)首先利用 ROM BIOS 中斷 int 0x13 把軟盤(pán)中的 head 代碼讀入到內(nèi)存0x10000 位置開(kāi)始處,然后再把這段 head 代碼移動(dòng)到內(nèi)存 0 處,最后設(shè)置控制寄存器CR0 中的開(kāi)啟保護(hù)運(yùn)行模式,并跳轉(zhuǎn)到內(nèi)存 0 處開(kāi)始執(zhí)行 head 代碼。
3.如何查看實(shí)模式的中斷程序?
-
查找中斷向量表:在實(shí)模式下,中斷處理程序通常通過(guò)中斷向量表來(lái)查找。這個(gè)表包含中斷號(hào)與中斷處理程序的關(guān)聯(lián)關(guān)系。
-
查看中斷處理程序:一旦找到了中斷處理程序的地址,就可以利用x addr指令即可查看對(duì)應(yīng)地址的內(nèi)存內(nèi)容,即中斷程序的內(nèi)容。
4.如何靜態(tài)創(chuàng)建?gdt
?與?idt
??
-
創(chuàng)建 GDT 和 IDT 表項(xiàng):定義 GDT 和 IDT 表項(xiàng)的結(jié)構(gòu)。每個(gè)表項(xiàng)包含段描述符或中斷描述符的相關(guān)信息,例如段基址、段限制、訪問(wèn)權(quán)限等。根據(jù)需要,創(chuàng)建所有所需的表項(xiàng)。
-
初始化 GDT 和 IDT 表項(xiàng):為每個(gè)表項(xiàng)設(shè)置適當(dāng)?shù)闹?,包括段的起始地址、限制、特?quán)級(jí)別、類型(代碼段、數(shù)據(jù)段、中斷門(mén)等)等。
-
創(chuàng)建 GDT 和 IDT 表:將所有初始化的表項(xiàng)組合成 GDT 和 IDT 表,這些表通常存儲(chǔ)在內(nèi)存中。
-
加載 GDT 和 IDT:使用匯編代碼將 GDT 和 IDT 表的地址加載到處理器的 GDTR 和 IDTR 寄存器中。這通常涉及到匯編指令
lgdt
(加載 GDT)和lidt
(加載 IDT)。 -
啟用中斷:如果正在設(shè)置 IDT 以處理中斷,確保啟用中斷處理器,以便它可以響應(yīng)中斷。這可以通過(guò)設(shè)置處理器的中斷標(biāo)志位(IF)來(lái)完成。
-
編寫(xiě)中斷處理程序:如果設(shè)置了 IDT 來(lái)處理中斷,需要編寫(xiě)相應(yīng)的中斷處理程序。
-
匯編和鏈接:將所有匯編代碼和數(shù)據(jù)結(jié)構(gòu)匯編并鏈接成可執(zhí)行文件。這個(gè)文件將包含 GDT 和 IDT 表的初始化和加載代碼,以及任何必要的中斷處理程序。
-
加載到目標(biāo)系統(tǒng):將生成的可執(zhí)行文件加載到目標(biāo)系統(tǒng)的內(nèi)存中,并執(zhí)行以初始化 GDT 和 IDT 表。
5.如何從實(shí)模式切換到保護(hù)模式?
-
準(zhǔn)備 GDT 和 IDT:在保護(hù)模式下,需要配置全局描述符表(GDT)和中斷描述符表(IDT)。需要?jiǎng)?chuàng)建和初始化這些表,包括定義段描述符和中斷門(mén)。
-
加載 GDT 和 IDT:使用
lgdt
匯編指令加載 GDT 表的地址到GDTR
寄存器,并使用lidt
指令加載 IDT 表的地址到IDTR
寄存器。 -
設(shè)置 CR0 寄存器:將控制寄存器 CR0 的第0位(PE位)設(shè)置為1,以啟用保護(hù)模式。這可以通過(guò)執(zhí)行匯編指令
mov eax, cr0
,or eax, 1
,mov cr0, eax
來(lái)完成。 -
跳轉(zhuǎn)到新代碼段:在切換到保護(hù)模式后,執(zhí)行
far jump
指令以跳轉(zhuǎn)到新的代碼段。通常,會(huì)跳轉(zhuǎn)到新的代碼段以開(kāi)始執(zhí)行保護(hù)模式下的代碼。 -
設(shè)置棧指針:在保護(hù)模式下,通常需要重新設(shè)置棧指針(SP)以指向新的棧段。這是因?yàn)樵趯?shí)模式下,棧通常在段0x0000下,而在保護(hù)模式下通常會(huì)有不同的棧段。
-
編寫(xiě)保護(hù)模式代碼:一旦切換到保護(hù)模式,需要編寫(xiě)適用于該模式的代碼。
6.調(diào)試跟蹤?jmpi?0,8
?,解釋如何尋址??


??

????????運(yùn)行一步后發(fā)現(xiàn)跳轉(zhuǎn)到了0x0000處
?????????jmpi
指令是一條匯編指令,通常用于在x86架構(gòu)的實(shí)模式下進(jìn)行跳轉(zhuǎn)。這個(gè)指令的作用是無(wú)條件跳轉(zhuǎn)到一個(gè)新的代碼段,以執(zhí)行那里的指令
-
jmpi
指令:這是一個(gè)實(shí)模式下的匯編指令,用于在跳轉(zhuǎn)到新的代碼段時(shí),提供一個(gè)絕對(duì)的偏移地址。 -
尋址方式:
jmpi
指令的尋址方式是使用一個(gè)絕對(duì)的偏移地址。它的操作數(shù)是一個(gè)16位的偏移地址,其格式通常如下:jmpi <偏移地址> # 偏移地址指定了要跳轉(zhuǎn)到的新代碼段中的目標(biāo)指令
-
目標(biāo)地址計(jì)算:
jmpi
指令的目標(biāo)地址計(jì)算方式是將偏移地址與當(dāng)前代碼段的基址相加,然后跳轉(zhuǎn)到該地址,它允許跳轉(zhuǎn)到不同的代碼段。例如,如果當(dāng)前代碼段的基址是
0x0000
,偏移地址為0x0008
,那么目標(biāo)地址將是0x0000 + 0x0008 = 0x0008
。jmpi
指令執(zhí)行后,控制權(quán)將轉(zhuǎn)移到新代碼段的指定地址,開(kāi)始執(zhí)行那里的指令。
1.3 實(shí)驗(yàn)報(bào)告
1.請(qǐng)簡(jiǎn)述?head.s
?的工作原理
????????通常情況下,head.s
是引導(dǎo)加載程序(bootloader)的一部分,用于引導(dǎo)加載操作系統(tǒng)內(nèi)核,包含 32 位保護(hù)模式初始化設(shè)置代碼、時(shí)鐘中斷代碼、系統(tǒng)調(diào)用中斷代碼和兩個(gè)任務(wù)的代碼。
-
加載到內(nèi)存:
head.s
是一個(gè)匯編源代碼文件,經(jīng)過(guò)匯編和鏈接后,生成二進(jìn)制可執(zhí)行文件,通常是一個(gè)引導(dǎo)扇區(qū)(boot sector)。該文件必須存儲(chǔ)在引導(dǎo)設(shè)備(如硬盤(pán)或軟驅(qū))的引導(dǎo)扇區(qū)中。 -
引導(dǎo)加載程序:計(jì)算機(jī)啟動(dòng)時(shí),處理器會(huì)加載引導(dǎo)設(shè)備的引導(dǎo)扇區(qū)(通常位于磁盤(pán)的第一個(gè)扇區(qū))到內(nèi)存地址0x7C00。這個(gè)扇區(qū)通常包含了
head.s
的代碼。 -
設(shè)置環(huán)境:
head.s
開(kāi)始執(zhí)行,它的主要任務(wù)是設(shè)置一個(gè)適當(dāng)?shù)沫h(huán)境,以準(zhǔn)備加載操作系統(tǒng)內(nèi)核。這通常涉及到以下幾個(gè)步驟:- 初始化 GDT/IDT
- 設(shè)置系統(tǒng)定時(shí)器芯片 8253
- 初始化 TSS
- 跳轉(zhuǎn)到task0 的用戶態(tài)程序
- task0 或 task1 的用戶態(tài)程序在運(yùn)行時(shí),通過(guò)系統(tǒng)調(diào)用 int 0x80 向屏幕上打印字符A 或B
- 時(shí)鐘中斷發(fā)生時(shí),內(nèi)核的中斷處理程序?qū)崿F(xiàn)task0 和task1 的任務(wù)切換
初始化 GDT/IDT
????????設(shè)置 GDT/IDT 代碼如下,調(diào)用了兩個(gè)子程序setup_gdt
和setup_idt
:
# setup base fields of descriptors.
call setup_idt
call setup_gdt
movl $0x10,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt.
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss init_stack,%esp
/****************************************/
setup_gdt:
lgdt lgdt_opcode
ret
setup_idt:
lea ignore_int,%edx
movl $0x00080000,%eax
movw %dx,%ax /* selector = 0x0008 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea idt,%edi
mov $256,%ecx
設(shè)置系統(tǒng)定時(shí)器芯片
movb $0x36, %al # 控制字:設(shè)置通道 0 工作在方式 3、計(jì)數(shù)初值采用二進(jìn)制。
movl $0x43, %edx # 8253 芯片控制字寄存器寫(xiě)端口。
outb %al, %dx
movl $LATCH, %eax # 初始計(jì)數(shù)值設(shè)置為 LATCH(1193180/100),即頻率 100HZ。
movl $0x40, %edx # 通道 0 的端口。
outb %al, %dx # 分兩次把初始計(jì)數(shù)值寫(xiě)入通道 0。
movb %ah, %al
outb %al, %dx
跳轉(zhuǎn)到 task 0
# Move to user mode (task 0)
pushfl # 將EFLAGS壓棧
andl $0xffffbfff, (%esp) # EFLAGS 的NT 標(biāo)志位(第14 位)置0
popfl
movl $TSS0_SEL, %eax # 把任務(wù)0的TSS段選擇符加載到TR
ltr %ax
movl $LDT0_SEL, %eax # 把任務(wù)0的LDT加載到LDTR
lldt %ax
movl $0, current # 任務(wù)號(hào)
sti
pushl $0x17 # 數(shù)據(jù)段選擇符
pushl $init_stack # 棧指針
pushfl # EFLAGS
pushl $0x0f # 代碼段選擇符
pushl $task0 # task0程序入口
iret
最后執(zhí)行 iret 時(shí),iret 會(huì)把棧頂彈出,更新CS 和 IP,然后把下一個(gè)棧頂彈出,更新EFLAGS,然后把下兩個(gè)棧頂彈出,更新 SS 和ESP,此時(shí)便跳轉(zhuǎn)到 task0 下執(zhí)行。
task0 代碼
task0:
movl $0x17, %eax
movw %ax, %ds
movb $65, %al /* print 'A' */
int $0x80
movl $0xfff, %ecx
1: loop 1b
jmp task0
task0 和 task1 代碼唯一區(qū)別就是傳入的字符不同。它們都使用 int 80 系統(tǒng)調(diào)用打印字符,然后進(jìn)行 4095 次的空循環(huán)。?
2.請(qǐng)記錄?head.s
?的內(nèi)存分布狀況,寫(xiě)明每個(gè)數(shù)據(jù)段,代碼段,棧段的起始與終止的內(nèi)存地址
1)?棧段
-
棧段(stack segment):棧,用于函數(shù)調(diào)用和本地變量。通常在數(shù)據(jù)段的上方
-
點(diǎn)擊stack查看棧中內(nèi)容??????
-
起始地址為0x8000
-
終止地址為0x80c4+4-1,即0x80c7
2)數(shù)據(jù)段,代碼段
-
代碼段(text segment):引導(dǎo)加載程序的機(jī)器代碼,通常位于內(nèi)存的低地址
-
數(shù)據(jù)段(data segment):引導(dǎo)加載程序的數(shù)據(jù)和全局變量,可能位于代碼段的下方
-
點(diǎn)擊GDT查看全局描述符表 (GDT) 的內(nèi)容
-
Selector 0x0008
通常用于訪問(wèn)操作系統(tǒng)內(nèi)核的代碼段 -
Selector 0x0010
通常用于訪問(wèn)操作系統(tǒng)內(nèi)核的數(shù)據(jù)段 -
根據(jù)上圖我們可以看到數(shù)據(jù)段和代碼段的起始地址都是0x0,Size均為0x7FFFFF,終止地址=起始地址+Size
-
由此可得數(shù)據(jù)段和代碼段的
-
起始地址為:0x0
-
終止地址為:0x7FFFFF
-
3.簡(jiǎn)述?head.s
?57
?至?62
?行在做什么?
-
在第56行設(shè)置斷點(diǎn),然后繼續(xù)執(zhí)行程序
- 反匯編57至62行
-
57至62行代碼如下:
- init_stack的內(nèi)容如下,設(shè)置堆棧指針(SS:ESP)的初始值,以確保堆棧能夠正常工作
-
任務(wù)0的LDT如下(提供代碼段選擇子和數(shù)據(jù)段選擇子等):
?
sti
指令用于開(kāi)啟中斷,允許處理器響應(yīng)中斷請(qǐng)求。
pushl $0x17
將任務(wù) 0 的當(dāng)前局部數(shù)據(jù)段選擇符入棧。這個(gè)選擇符是用于任務(wù) 0 的堆棧段。
pushl $init_stack
將 init_stack
的地址入棧。這是為了保存任務(wù) 0 的堆棧指針。
pushfl
指令將標(biāo)志寄存器的值入棧。這個(gè)值包括處理器狀態(tài)標(biāo)志,如進(jìn)位標(biāo)志、溢出標(biāo)志等。
pushl $0x0f
將當(dāng)前局部代碼段選擇符入棧。這個(gè)選擇符通常用于指定任務(wù) 0 的代碼段。
pushl $task0
將任務(wù) 0 的入口地址入棧。這是為了保存任務(wù) 0 的執(zhí)行位置。
iret
是中斷返回指令,它會(huì)從棧中彈出保存的信息,包括代碼指針、代碼段選擇符、標(biāo)志寄存器值等,并使用這些信息切換到任務(wù) 0 的執(zhí)行上下文。這實(shí)現(xiàn)了從中斷處理程序返回到任務(wù) 0 的操作。
4.簡(jiǎn)述?iret
?執(zhí)行后,?pc
?如何找到下一條指令?
iret
指令用于從中斷處理程序返回到正常的程序執(zhí)行。iret
指令的執(zhí)行后,處理器會(huì)進(jìn)行下一跳指令的查找和執(zhí)行,下面是其執(zhí)行后尋找下一跳指令的過(guò)程:
-
iret 恢復(fù)寄存器狀態(tài):
iret
指令會(huì)從棧中彈出一系列值,包括代碼段選擇符(CS)、代碼指針(EIP)、標(biāo)志寄存器(EFLAGS),以及堆棧指針(ESP)的值。這些值都是在中斷處理過(guò)程中被保存的,用于恢復(fù)中斷前的狀態(tài)。 -
加載代碼段選擇符(CS):
iret
指令將代碼段選擇符(CS)從棧中加載到 CS 寄存器中。CS 寄存器包含了新的代碼段選擇符,它指示了中斷返回后應(yīng)執(zhí)行的代碼段。 -
加載代碼指針(EIP):
iret
指令將代碼指針(EIP)從棧中加載到 EIP 寄存器中。EIP 寄存器包含了下一條要執(zhí)行的指令的地址。 -
加載標(biāo)志寄存器(EFLAGS):
iret
指令將標(biāo)志寄存器(EFLAGS)從棧中加載到 EFLAGS 寄存器中。這些標(biāo)志用于控制處理器的狀態(tài)和行為。 -
堆棧指針(ESP)的修改:
iret
指令還會(huì)從棧中彈出堆棧指針(ESP)的值。這是為了確保棧指針正確地指向下一個(gè)棧幀。 -
執(zhí)行下一跳指令:一旦
iret
執(zhí)行完上述步驟,控制將傳遞到新的代碼段(由 CS 指定)中的新指令(由 EIP 指定)。處理器將從新代碼段的新指令地址開(kāi)始執(zhí)行,從而繼續(xù)程序的正常執(zhí)行,即繼續(xù)執(zhí)行任務(wù)0的程序
5.記錄?iret
?執(zhí)行前后,棧是如何變化的?
iret
執(zhí)行前
通過(guò)對(duì)head.s
57
至 62
行的分析,我們可以知道從57行sti
指令開(kāi)啟中斷開(kāi)始,程序開(kāi)始將任務(wù)0的堆棧段選擇符、堆棧指針的值、標(biāo)志寄存器(EFLAGS)的值、代碼段選擇符和代碼指針入棧。
至此在執(zhí)行 iret
指令之前,堆棧中包含了堆棧段選擇符、堆棧指針、標(biāo)志寄存器、代碼段選擇符和代碼指針。
iret
執(zhí)行后
iret
指令的目的是從中斷處理程序返回到正常程序執(zhí)行,它會(huì)恢復(fù)堆棧指針、代碼段選擇符、代碼指針、標(biāo)志寄存器等寄存器的值,以確保程序的控制流和狀態(tài)正確地切換到正常程序。
因此當(dāng)執(zhí)行 iret
后,堆棧的變化如下:
-
iret
指令會(huì)彈出之前被壓入棧的值,以恢復(fù)任務(wù) 0 的狀態(tài)。-
iret
彈出代碼指針(EIP)的值,指示了下一條要執(zhí)行的指令地址。 -
然后它彈出代碼段選擇符(CS),指示了代碼段的位置。
-
接著,它彈出標(biāo)志寄存器(EFLAGS)的值,以恢復(fù)標(biāo)志狀態(tài)。
-
最后,它彈出堆棧指針(ESP)的值,以確保棧指針正確指向下一個(gè)棧幀。
-
-
iret
將控制傳遞到任務(wù) 0 中的下一條指令,以繼續(xù)程序的正常執(zhí)行。
6.當(dāng)任務(wù)進(jìn)行系統(tǒng)調(diào)用時(shí),即?int?0x80
?時(shí),記錄棧的變化情況。
-
iret
指令執(zhí)行后,程序轉(zhuǎn)到了任務(wù)0的第一條指令
執(zhí)行 int 0x80
之前
-
當(dāng)任務(wù) 0 希望執(zhí)行系統(tǒng)調(diào)用時(shí),它會(huì)將系統(tǒng)調(diào)用號(hào)和相關(guān)參數(shù)加載到寄存器中。
-
寄存器中的內(nèi)容,包括系統(tǒng)調(diào)用號(hào)和參數(shù),通常在進(jìn)入內(nèi)核前被保存在寄存器中。
-
棧上包含了任務(wù) 0 正常執(zhí)行的棧幀,包括函數(shù)調(diào)用的參數(shù)、局部變量等。
執(zhí)行 int 0x80
之后
-
當(dāng)任務(wù) 0觸發(fā)
int $0x80
指令時(shí),處理器會(huì)執(zhí)行以下操作:-
壓入標(biāo)志寄存器(EFLAGS)的值。
-
壓入代碼段選擇符(CS)的值。
-
壓入返回地址,指向系統(tǒng)調(diào)用處理程序。
-
壓入系統(tǒng)調(diào)用號(hào)和參數(shù)。
-
-
進(jìn)入內(nèi)核態(tài)后,內(nèi)核會(huì)根據(jù)系統(tǒng)調(diào)用號(hào),從棧上獲取參數(shù),執(zhí)行相應(yīng)的系統(tǒng)調(diào)用服務(wù)。
-
系統(tǒng)調(diào)用處理程序執(zhí)行完后,它會(huì)將返回值存儲(chǔ)在一個(gè)特定的寄存器中,通常是
EAX
寄存器。 -
處理程序使用
iret
指令返回到用戶態(tài),這會(huì)將棧上的內(nèi)容彈出,恢復(fù)到int $0x80
指令執(zhí)行前的狀態(tài)。
注:
部分參考Lab1-調(diào)試分析 Linux 0.00 引導(dǎo)程序 | Kcxain's Blog (deconx.cn)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-734353.html
僅供備份使用文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-734353.html
到了這里,關(guān)于哈工大操作系統(tǒng)實(shí)驗(yàn)一--調(diào)試分析 Linux 0.00 引導(dǎo)程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!