系統調用
進程 ——> 調用OS API;OS進程管理 ——> 調配進程。
僅從用戶進程角度,OS就像是一個被動響應的運行時庫。Windows提供了一個系統調用界面作爲外層,即Win32API;Linux的CRuntime庫標準,Windows也支持。用戶進程通過調用這些OS接口,可以部分操作系統內核。
硬件] 設備驅動] 核心層] 管理層] 系統服務界面,中斷入口(不公開)] 系統DLL(公開)] ——>API
用戶進程沒有調用內核函數(特權指令)的權限,只有調用API,通過API函數內的“自陷中斷”進入“系統態”調用系統函數。或者是“異常中斷”出錯進入OS運行。
Win32API{
__asm{
push ebp KiSystemService()
mov eax, 151 //系統函數號 { 中斷,保護CPU環境,進入系統態
lea edx, 9[ebp] //用戶區參數棧地址 copy棧參數塊到系統內存
int 0x2e //中斷id,進入IDT表對應函數 ——> 執行eax寄存器裏的id調用函數
pop ebp 返回結果,copy回用戶區
ret 10 //返回結果參數,9+1 關中斷,基本是開頭的逆操作 }
}
} //如果用戶參數少,可以直接在寄存器中傳,用戶指定fastcall方式
除外部IO外,自陷和異常也是IDT表中的中斷項(255個,外設中斷預留100個左右),自陷中斷有:
int 0x3 (2c\ 2d) KiTrap3() ,DEBUG調試用
0x2e KiSystemService() ,系統函數調用
0x2b 內核可以調用用戶空間的子程序,子程序執行完再返回內核,就用這個中斷
0x2a 獲取精確CPU時間,輕型調用,普通中斷開銷大,無法取到精確的時間
通常的特權指令調用都通過KiSystemService() ,通過系統函數調用號id跳轉到函數地址上。Windows核心和Liunx類似,有200多個核心函數,但MS又把視窗界面函數也移入內核中。
另外:不同的用戶進程,有各自的系統堆棧空間,供其調用的內核函數使用(是彼此獨立的)。
windows內核情景分析--系統調用
Windows的地址空間分用戶模式與內核模式,低2GB的部分叫用戶模式,高2G的部分叫內核模式,位於用戶空間的代碼不能訪問內核空間,位於內核空間的代碼卻可以訪問用戶空間
一個線程的運行狀態分內核態與用戶態,當指令位於用戶空間時,就表示當前處於內核態,當指令位於內核空間時,就處於內核態.
一個線程由用戶態進入內核態的途徑有3種典型的方式:
1、 主動通過int 2e(軟中斷自陷方式)或sysenter指令(快速系統調用方式)調用系統服務函數,主動進入內核
2、 發生異常,被迫進入內核
3、 發生硬件中斷,被迫進入內核
現在討論第一種進入內核的方式:(又分爲兩種方式)
1、 通過老式的int 2e指令方式調用系統服務(因爲老式cpu沒提供sysenter指令)
如ReadFile函數調用系統服務函數NtReadFile
Kernel32.ReadFile() //點號前面表示該函數的所在模塊
{
//所有Win32 API通過NTDLL中的系統服務存根函數調用系統服務進入內核
NTDLL.NtReadFile();
}
NTDLL.NtReadFile()
{
Mov eax,152 //我們要調用的系統服務函數號,也即SSDT表中的索引,記錄在eax中
If(cpu不支持sysenter指令)
{
Lea edx,[esp+4] //用戶空間中的參數區基地址,記錄在edx中
Int 2e //通過該自陷指令方式進入KiSystemService,‘調用’對應的系統服務
}
Else
{
Lea edx,[esp +4] //用戶空間中的參數區基地址,記錄在edx中
Sysenter //通過sysenter方式進入KiFastCallEntry,‘調用’對應的系統服務
}
Ret 36 //不管是從int 2e方式還是sysenter方式,系統調用都會返回到此條指令處
}
Int 2e的內部實現原理:
該指令是一條自陷指令,執行該條指令後,cpu會自動將當前線程的當前棧切換爲本線程的內核棧(棧分用戶棧、內核棧),保存中斷現場,也即那5個寄存器。然後從該cpu的中斷描述符表(簡稱IDT)中找到這個2e中斷號對應的函數(也即中斷服務例程,簡稱ISR),jmp 到對應的isr處繼續執行,此時這個ISR本身就處於內核空間了,當前線程就進入內核空間了
Int 2e指令可以把它理解爲intel提供的一個內部函數,它內部所做的工作如下
Int 2e
{
Cli //cpu一中斷,立馬自動關中斷
Mov esp, TSS.內核棧地址 //切換爲內核棧,TSS中記錄了當前線程的內核棧地址
Push SS
Push esp
Push eflags
Push cs
Push eip //這5項工作保存了中斷現場【標誌、ip、esp】
Jmp IDT[中斷號] //跳轉到對應本中斷號的isr
}
明白了IDT,就可以看到0x2e號中斷的isr爲KiSystemService,顧名思義,這個中斷號專用於提供系統服務。
https://www.cnblogs.com/jadeshu/articles/10663613.html
利用Windbg初步解析系統調用
https://cloud.tencent.com/developer/news/240246
Windbg初步解析系統調用
其實我以前沒怎麼用過,編程的時候很少調試,即便調試也是依賴編譯器的調試。
最近要解析系統調用,解析的時候出了點問題,沒有辦法解析到NtReadFile系統調用中讀的內容。這事兒很奇怪,其他的系統調用都可以解析到,這個就不行。
於是,我寫了一個簡單的讀文件程序進行測試。代碼如下
代碼很簡單,就不說了。64位debug模式下編譯通過後,用windbg打開,
設置斷點:bu ntdll!ZwReadFile
執行g命令,開始運行
連續按F10執行到syscall完成
R命令查看寄存器值
dq rsp+0x30得到buffer地址
da [buffer地址] 就可以看到buffer的內容了。如下所示,內容很簡單,是“1 2 3 4 5 6 7 8 9 0”,和我在hello.txt中寫入的一樣。