Windows內核系統調用分析

系統調用

    進程 ——> 調用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中寫入的一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章