Windows系統調用中API從3環到0環(上)


 Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.html

 

Windows系統調用中API從3環到0環(上)

如果對API在三環的部分不了解的,可以查看 Windows系統調用中的API三環部分(依據分析重寫ReadProcessMemory函數

這篇文章分為上下兩篇,其中上篇初步講解大體輪廓,下篇着重通過實驗來探究其內部實現,最終分析兩個函數(快速調用與系統中斷),來實現通過系統中斷直接調用內核函數。

 

一、結構體 _KUSER_SHARED_DATA

  該結構體看名字可知是用於內核層與用戶層來共享數據所使用的結構體。

   kd > dt _KUSER_SHARED_DATA
          ntdll!_KUSER_SHARED_DATA
          + 0x000 TickCountLowDeprecated : Uint4B
          + 0x004 TickCountMultiplier : Uint4B
          + 0x008 InterruptTime : _KSYSTEM_TIME
          + 0x014 SystemTime : _KSYSTEM_TIME
          + 0x020 TimeZoneBias : _KSYSTEM_TIME
          + 0x02c ImageNumberLow : Uint2B
          + 0x02e ImageNumberHigh : Uint2B
          ·······

   1)在User層和KerNel層分別定義了一個_KUSER_SHARED_DATA結構區域,用於User層和Kernel層共享某些數據。

   2)它們使用同一段頁,只是映射位置不同。雖然同一頁,但User只讀,Kernnel層可寫。

   3)它們使用固定的地址值映射,_KUSER_SHARED_DATA結構在User為:0x7ffe0000,在Kernel層為:0xffdf0000。

  

   通過windbg可以查看其中兩塊內存完全一樣(實質查看的是同一物理頁,掛在兩塊頁表PTT中。

    

 

二、分析 0X7FFE0300  這個地址

在 <&ntdll.NtReadVirtualMemory> 中調用 ntdll.KiFastSystemCall 函數 ,實質就是調用 0X7FFE0300 這個地址。
    77A162F8 >  B8 15010000  mov eax,0x115  // 對應操作系統內核中某一函數的編號。
    77A162FD    BA 0003FE7F  mov edx,0x7FFE0300  // 該地方是一個函數,該函數決定了什么方式進零環。
    77A16302    FF12         call dword ptr ds:[edx]  ; ntdll.KiFastSystemCall

  1)_KUSER_SHARED_DATA 在用戶層的位置為 0x7FFE0000,該地址為其+0x300位置

     +0x300 SystemCall       : Uint4B

  2)該成員保存着系統調用的函數入口,如果當前CPU支持快速調用。

    則存儲着ntdll.dll!KiFastSystemCall()函數地址;

    如果不支持快速調用,則存儲着ntdll.dll!KiIntSystemCall()函數地址。

  3)通過實驗驗證當前CPU是否支持快速調用:

    當通過eax=1來執行cupid指令時,處理器特征信息被存放在ecx和edx寄存器中,

    其中edx包含了SEP位(11位),該位指明了當前處理器是否支持sysenter/sysexit指令。

    

    如下圖,我們執行cupid指令,獲取edx 178BFBFF,拆分 11-8位 B 1011,故其11位為1,支持快速調用。

    這也驗證了我們上一篇文章中的分析結果。(最后是快速調用並非使用中斷門)

 

三、從3環進0環需要哪些寄存器改變

  1. CS的權限由3變為0,意味着需要新的CS
  2. SS與CS的權限永遠一致,需要新的SS
  3. 權限發生切換的時候,堆棧也一定會改變,需要新的ESP
  4. 進0環后的代碼位置,需要EIP

 

四、ntdll.dll!KiIntSystemCall() 分析

  我們使用ida來分析ntdll.dll!KiIntSystemCall()

  .text:77F070C0
  .text : 77F070C0                 public KiIntSystemCall
  .text : 77F070C0 KiIntSystemCall proc near; DATA XREF : .text : off_77EF61B8↑o
  .text : 77F070C0
  .text : 77F070C0 arg_4 = byte ptr  8
  .text : 77F070C0            // 之前調用該函數時 mov eax, 0x115,向eax傳入一個函數號
  .text : 77F070C0                 lea     edx, [esp + arg_4] // 當前參數的指針存儲在 edx中
  .text : 77F070C4                 int     2Eh; // 通過中斷門的形式進入到內核中
  .text:77F070C4; DS:SI->counted CR - terminated command string
  .text : 77F070C6                 retn
  .text : 77F070C6 KiIntSystemCall endp

  其在觸發 int 2eh中斷前用到兩個寄存器,一個是內核中調用函數的函數號,另外一個就是傳入參數的指針。

 

五. ntdll.dll!KiFastSystemCall()函數分析
  當CPU支持快速調用,則使用這個函數。(我們在上篇文章中已經用到了這個來重構WriteProcessMemory函數)
  .text:77F070B0                 public KiFastSystemCall
  .text:77F070B0 KiFastSystemCall proc near              ; DATA XREF: .text:off_77EF61B8↑o
  .text:77F070B0            // 之前調用該函數時 mov eax, 0x115,向eax傳入一個函數號
  .text:77F070B0                 mov     edx, esp // 將當前堆棧放入edx,用它來存儲參數
  .text:77F070B2                 sysenter
  .text:77F070B2 KiFastSystemCall endp

  觸發sysenter指令后,也用到兩個寄存器eax,edx,作用與使用中斷一樣。

 

  為什么叫快速調用?

    中斷門進入0環,需要的CS、EIP在IDT表中,需要查內存(SS與ESP由IDT表提供);

    而CPU如果支持sysenter指令時,操作系統會提前將CS/SS/ESP/EIP的值存儲在MSR寄存器中,

    sysenter指令執行時,CPU會將MSR寄存器中的值直接寫入寄存器中,沒有讀內存過程,本質時一樣的。

下一篇,我們探究其中的詳細細節。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM