日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

进程线程005 SwapContext函数分析

發(fā)布時間:2025/3/21 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进程线程005 SwapContext函数分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 線程切換與TSS
      • 內(nèi)核堆棧
      • 內(nèi)核堆棧結(jié)構(gòu)
      • 調(diào)用API進零環(huán)
      • SwapContext代碼分析
    • 線程切換與FS
      • SwapContext代碼分析
    • SwapContext的其他問題
      • SwapContext有幾個參數(shù) 怎么判斷出來的?
      • SwapContext在哪里實現(xiàn)了線程切換?
      • 0環(huán)的ExceptionList是在哪里備份的

線程切換與TSS

SwapContext這個函數(shù)是Windows線程切換的核心,無論是主動切換還是系統(tǒng)時鐘導(dǎo)致的線程切換,最終都會調(diào)用這個函數(shù)

在這個函數(shù)除了切換堆棧以外,還做了很多其他事情,下面就來學(xué)習(xí)一下線程切換與TSS的關(guān)系

內(nèi)核堆棧

每一個線程都有一個內(nèi)核堆棧,當API調(diào)用進零環(huán)的時候,必然要切換堆棧這個堆棧就是當前線程的零環(huán)堆棧。那這個線程零環(huán)的堆棧去哪里找呢?

KTHREAD結(jié)構(gòu)體中有三個成員:InitialStack是當前堆棧的棧底,KernelStack是當前堆棧的棧頂,StackLimit是堆棧的邊界。也就是說如果我們找到了這三個成員也就找到了內(nèi)核堆棧。

內(nèi)核堆棧結(jié)構(gòu)

內(nèi)核堆棧從結(jié)構(gòu)上大體分成兩部分,第一部分從InitialStack開始的0x210個字節(jié)存儲的是當前線程用到的浮點寄存器的值。

從0x210再往后就是Trap_Frame結(jié)構(gòu)體。完整結(jié)構(gòu)如圖:

調(diào)用API進零環(huán)

普通調(diào)用:通過TSS.ESP0得到零環(huán)堆棧

快速調(diào)用:從MSR得到一個臨時的0環(huán)棧,代碼執(zhí)行后仍然通過TSS.ESP0得到當前線程的0環(huán)棧

我們找到KiFastCallEntry的代碼,0FFDFF000的位置是KPCR,首先找到KPCR偏移為0x40的位置TSS,然后再找到TSS偏移4的位置ESP0,把這個值賦給了當前的esp。然后才開始往堆棧里壓入值。

那么問題來了,**TSS中的ESP0來自于哪?**答案在SwapContext的代碼里。

SwapContext代碼分析

Intel設(shè)計TSS的目的是為了任務(wù)切換(線程切換),但Windows與Linux并沒有使用,而是采用堆棧來保存線程的各種寄存器。

那么這里就有一個問題,**一個CPU只有一個TSS,但是線程很多,如何用一個TSS來保存所有線程的ESP0呢?**答案都在SwapContext的代碼里

找到SwapContext中與TSS相關(guān)的代碼,ebx就是當前CPU對應(yīng)的結(jié)構(gòu)體KPCR,通過KPCR找到TSS存到ecx里,

TSS偏移4的位置是ESP0,接著將eax存儲到ESP0,繼續(xù)往上找一下eax的值。

首先將目標線程的棧底存儲到eax

此時eax指向上圖的的InitStack

接著減去0x210

此時eax指向Trap_Frame結(jié)構(gòu)

接著再減去0x10,也就是4個成員

Trap_Frame最底下的四個成員是給虛擬8086模式用的。通過剛才的計算得出,當前的eax指向的是0x078的位置SS

.text:00469B1C mov ecx, [ebx+40h] ; 通過KPCR取出TSS .text:00469B1F mov [ecx+4], eax ; 將Trap_Frame.ESP0存到TSS.ESP0

這就是SwapContext函數(shù)對TSS的使用,它會將Trap_Frame.ESP0存到TSS.ESP0。

到這里,解決了之前提出的兩個問題

**TSS中的ESP0來自于哪?**來自于0環(huán)的Tram_Frame結(jié)構(gòu)體

**一個CPU只有一個TSS,但是線程很多,如何用一個TSS來保存所有線程的ESP0呢?**在發(fā)生線程切換的時候,SwapContext會將目標線程的ESP0存到TSS中,然后開始切換線程。就是說TSS永遠存儲的是當前線程的ESP0

TSS中除了ESP0之外還用到了一個值就是CR3,SwapContext會將當前TSS中的CR3修改為目標進程的CR3,然后切換CR3。

下面一行代碼將當前線程的IO權(quán)限位圖存到了TSS里。這個成員在Windows2000以后不用了

**結(jié)論:**Intel提供的TSS在Windows里只有三個成員是有意義的:ESP0 CR3和IO權(quán)限位圖

線程切換與FS

FS:[0]寄存器在3環(huán)的時候指向TEB,進入0環(huán)后FS:[0]指向KPCR。

系統(tǒng)中同時存在很多個線程,這就意味著FS:[0]在3環(huán)的時候指向的TEB要有多個,有一個線程就要有一個TEB,但是在實際使用中我們發(fā)現(xiàn)在3環(huán)查看不同線程的FS寄存器時,FS的段選擇子都是相同的,那么是如何實現(xiàn)通過一個FS寄存器指向多個TEB呢?

答案依然在SwapContext函數(shù)的代碼里。

SwapContext代碼分析

找到SwapContext與TEB相關(guān)的代碼

首先取出目標線程的TEB 放到eax里

.text:00469B67 mov ecx, [ebx+3Ch] ; 通過KPCR找到GDT表

接著通過KPCR找到GDT表,存到ecx

.text:00469B6A mov [ecx+3Ah], ax

這里的ax是TEB的低16位,

而ecx+3A就是段描述符低4個字節(jié)的16-31位Base Address,也就是將TEB的低16位寫到段描述符的16-31位。

.text:00469B6E shr eax, 10h

將eax右移16位,這樣就能得到TEB地址的高16位。因為低16位已經(jīng)寫到段描述符里了。

.text:00469B71 mov [ecx+3Ch], al ; 將低8位寫到段描述符0-7的位置 .text:00469B74 mov [ecx+3Fh], ah ; 將高8位寫到段描述符31-24的位置

高16位又分成兩部分寫到段描述符中,先將低8位寫到段描述符0-7的位置,再將高8位寫到段描述符31-24的位置。

通過這幾行代碼就將新的線程的TEB的地址寫到了當前GDT表的段描述符的基址中。

這就回答了剛才的問題:如何實現(xiàn)通過一個FS寄存器指向多個TEB?

因為FS段選擇子雖然沒有發(fā)生變化,但是在線程切換的時候,會修改段選擇子所指向的段描述符的基址為新的線程的TEB的地址。

SwapContext的其他問題

SwapContext有幾個參數(shù) 怎么判斷出來的?

四個參數(shù),但真正有用的只有三個,分別是:

  • esi:當前線程結(jié)構(gòu)體ETHREAD指針
  • edi:要切換的線程結(jié)構(gòu)體ETHREAD指針
  • ebx:KPCR

首先找到SwapContext的父函數(shù)KiSwapContext

0FFDFF01Ch存儲的就是KPCR,所以參數(shù)ebx就是KPCR

.text:004699CE mov edi, [ebx+124h] ; KPCR+0x124是當前線程的KTHREAD結(jié)構(gòu)體

ebx是KPCR,KPCR+0x124的位置就是當前線程的KTHREAD結(jié)構(gòu)體

.text:004699CC mov esi, ecx ; ecx是上一層調(diào)用的函數(shù)傳進來的 是要切換線程的KTHREAD

而esi來自于ecx,ecx是上一層調(diào)用的函數(shù)傳進來的 是要切換線程的KTHREAD

找到上一層的函數(shù),ecx來自于eax,是KiFindReadyThread的返回值,這個函數(shù)會查找一個就緒線程返回KTHREAD結(jié)構(gòu)體

SwapContext在哪里實現(xiàn)了線程切換?

線程切換的本質(zhì)就是切換堆棧

這行代碼將目標線程的KernelStack存到ESP里,這行代碼以后另一個線程復(fù)活

0環(huán)的ExceptionList是在哪里備份的

這行代碼會將ExceptionList存儲到ecx,然后將ExceptionList保存到堆棧

總結(jié)

以上是生活随笔為你收集整理的进程线程005 SwapContext函数分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。