5.用户APC执行过程
當產生系統調用、中斷或者異常,線程在返回用戶空間前都會調用, _KiServiceExit函數,在_KiServiceExit會判斷是否有要執行的用戶APC,如果有則調用KiDeliverApc函數(第一個參數為1)進行處理。
執行用戶APC時的堆棧操作
處理用戶APC要比內核APC復雜的多,因為,用戶APC函數要在用戶空間執行的,這里涉及到大量換棧的操作:
當線程從用戶層進入內核層時,要保留原來的運行環境,比如各種寄存 囚器,棧的位置等等(_Trap_Frame),然后切換成內核的堆棧,如果正常返回, 您恢復堆棧環境即可。
但如果有用戶APC要執行的話,就意味著線程要提前返回到用戶空間去,執行,而且返回的位置不是線程進入內核時的位置,而是返回到其他的位置,每處理一個用戶APC都會涉及到:
內核->用戶空間->再回到內核空間
堆棧的操作比較復雜,如果不了解堆棧的操作細節不可能理解用戶APC是如何執行!
KiDeliverApc函數分析
無論是內核APC還是用戶APC首先執行的都是這個函數
取完內核APC后取用戶APC,然后判斷你用戶APC是不是空的,不為空就跳轉。
KilnitializeUserApc函數分析:備份CONTEXT
線程進0環時,原來的運行環境(寄存器棧頂等)保存到Trap_Frame結構體中,如果要提前返回3環去處理用戶APC,就必須要修改Trap _Frame結構體:
比如:進0環時的位置存儲在EIP中,現在要提前返回,而且返回的并不,是原來的位置,那就意味著必須要修改EIP為新的返回位置。還有堆棧ESP也要修改為處理APC需要的堆棧。那原來的值怎么辦呢?處理完APC后該如何返回原來的, 位置呢?
KilnitializeUserApc要做的第一件事就是備份:
將原來Trap Frame的值備份到一個新的結構體中(CONTEXT),這個功能由其子函數KeContextFromKframes來完成。
kd> dt _CONTEXT nt!_CONTEXT+0x000 ContextFlags : Uint4B+0x004 Dr0 : Uint4B+0x008 Dr1 : Uint4B+0x00c Dr2 : Uint4B+0x010 Dr3 : Uint4B+0x014 Dr6 : Uint4B+0x018 Dr7 : Uint4B+0x01c FloatSave : _FLOATING_SAVE_AREA+0x08c SegGs : Uint4B+0x090 SegFs : Uint4B+0x094 SegEs : Uint4B+0x098 SegDs : Uint4B+0x09c Edi : Uint4B+0x0a0 Esi : Uint4B+0x0a4 Ebx : Uint4B+0x0a8 Edx : Uint4B+0x0ac Ecx : Uint4B+0x0b0 Eax : Uint4B+0x0b4 Ebp : Uint4B+0x0b8 Eip : Uint4B+0x0bc SegCs : Uint4B+0x0c0 EFlags : Uint4B+0x0c4 Esp : Uint4B+0x0c8 SegSs : Uint4B+0x0cc ExtendedRegisters : [512] UChar0環備份的CONTEXT復制到3環堆棧
APC要執行的4個值放到圖上面堆棧中
1
2.3.4
ntdll.KiUserApcDispatcher分析
1、當用戶在3環調用QueueUserAPC函數來插入APC時,不需要提供 NormalRoutine,這個參數是在QueueUserAPC內部指定的:
BaseDispatchAPC
2、 ZwContinue函數的意義:
Frame結構體。就像從來沒有修改過一樣。ZwContinue后面的代碼不會執行,線程從哪里進0環仍然會從哪里回去。
總結
總結
以上是生活随笔為你收集整理的5.用户APC执行过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4.内核APC执行过程
- 下一篇: 2.自旋锁