Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
Windows系統調用中的現場保存windows
咱們以前介紹過三環進零環的步驟,經過中斷或者快速調用來實現。函數
可是咱們是否考慮過CPU從三環進入零環時,其三環的寄存器該如何保存。spa
這一篇文件就來介紹其系統調用中的(三環)現場保存的問題。操作系統
1、幾個重要的結構體介紹線程
1. _Ktrap_frame3d
該結構體簡單來講用於三環的寄存器保存,存儲於零環,由操做系統維護,每一個線程都有本身的 _Ktrap_frame 結構體(ethread+0x108處)。指針
詳細信息能夠查看這篇文章:解析windows內核每日一講 陷阱調度調試
咱們以前講過進入0環時獲取新的四個寄存器很是重要, SS\CS\EIP\ESPcode
以下圖,當進入零環時,操做系統會獲取ESP值,該值指向_Ktrap_frame結構體,將舊的SS\CS\EIP\ESP\EFLAG依次壓入(0x78-0x68處),以後ESP +0x070 ErrCode處。
以後便進入獲取的EIP來執行內核函數。
2. _ETHREAD 結構體
該結構體保存了和線程相關的信息,位於0環(其並不是三環的 TEB,線程環境塊)
在_ETHREAD結構體第一個成員是 _KTHRAD,能夠看出其大小0x200,裏面保存了線程中的一些信息
kd > dt _ethread
ntdll!_ETHREAD
+ 0x000 Tcb : _KTHREAD
+ 0x200 CreateTime : _LARGE_INTEGER
····
kd > dt _kthread
ntdll!_KTHREAD
+ 0x000 Header : _DISPATCHER_HEADER
···
+0x128 TrapFrame : Ptr32 _KTRAP_FRAME
3. KPCR結構體(kernel processor control region 內核線程控制區)
有一篇文章,裏面大致介紹了該結構體 --> [Windows內核分析]KPCR結構體介紹 (CPU控制區 Processor Control Region)
簡單來講,KPCR結構體中保存着 關於CPU 的信息,一個核有一個本身私有的KPCR結構體,八核則每一個核有本身的單獨的KPCR結構體
咱們須要用到最後一個成員中的中的CurrentThread來獲取當前線程的_KTHREAD結構體。
kd > dt _kpcr
ntdll!_KPCR
+ 0x000 NtTib : _NT_TIB
+ 0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
····
+0x120 PrcbData : _KPRCB
kd > dt _KPRCB
ntdll!_KPRCB
+ 0x000 MinorVersion : Uint2B
+ 0x002 MajorVersion : Uint2B
+ 0x004 CurrentThread : Ptr32 _KTHREAD
+ 0x008 NextThread : Ptr32 _KTHREAD
···
2、分析 nt!KiSystemService 內核函數
該函數位於ntkrnlpa.exe真正的內核中(並不是ntdll.dll),一開始但願使用IDA反彙編查看其代碼,可是沒法顯示。
咱們只能採起"曲線救國"的方法,經過int 2eh中斷號查找GDT表,從表中獲取中斷描述符,將中斷描述符拼接拆分,獲取 函數的EIP 地址。
以後,咱們使用 Windbg 的U指令,來查看該其該函數在內存中的反彙編代碼
反彙編代碼可能有點長,咱們在正式分析以前須要明確幾個注意事項。
0)這些反彙編代碼的目的就是將 _Ktrap_frame 結構體中的寄存器填滿,而後將[KTHREAD+0x128]處舊的TRAPFRAME切換爲這個新的KTRAPFRAME而後調用內核函數。
1)函數開始時ESP位於_Ktrap_framec+0x070 ErrCode處。ErroCode這裏沒有,所以能夠看待彙編代碼中第一行使用 push 0。
2)FS寄存器在0環時是指向本身的KPCR,而在3環時保存的是該線程的TEB結構體。(R3能夠查看:利用C++實現模塊隱藏)
所以在反彙編代碼 83e8cff6 、83e8cffb 是手動將FS寄存器存儲KPCR。
原理是以30h做爲段選擇子查找GDT這張表,找出段描述符,而後加載到fs段寄存器中。
3) 0x83e8d007處 KPCR:[124] 爲當前線程的ETHREAD結構體(看一中的表,KTHREAD也即ETHREAD的頭部),將其存儲到寄存器esi中,以後要常用。
4)0x83e8d026處修改esp,將其指向 _Ktrap_frame 結構體第一個成員,0x83e8d036 處也將ebp修改成 _Ktrap_frame 結構體第一個成員。
5)由於 esi 指向ETHREAD, [esi+128h] 則表示當前線程的_Ktrap_frame,在進入內核時,其保存了一箇舊的frame。
0x83e8d038將獲取舊的_Ktrap_frame
0x83e8d03e將舊的Frame放入新的Frame的edx寄存器中
0x83e8d049將新的Frame掛靠在ETHREAD+0x128處,這樣完成了新舊替換。
下圖三個箭頭依次對應上面三個階段,這樣經過FS寄存器就能夠查找到該FRAME了,回去只須要找到這裏完成替換便可。
6)Frame前部分紅員和調試有關,所以0x83e8d045這裏須要判斷是否在調試狀態,若是處於調試狀態,則jmp進一個地址,將寄存器入棧再回來。
硬件調試,其實就是經過這個部分。對於反調試,這裏能夠作些文章...
7)還有部分涉及權限切換,0x83e8d030 好比有些API可讓3環也可讓0環權限調用,但有的不讓(看保護模式調用門等知識),此時就會進行位運算判斷"先前模式"的權限級別。
8)當這些工做所有完成以後,會執行 83e8d06d e9dd000000 jmp nt!KiFastCallEntry+0x8f (83e8d14f)
對,你沒看錯,就是快速調用時的函數,至關於中斷多走了一步,處理了上面的一些信息,而快速調用提早就能夠處理好直接調用 nt!KiFastCallEntry+0x8f函數。
3、nt!KiSystemService函數的反彙編解讀
1 nt!KiSystemService: 2 // 壓棧 按照 _Ktrap_frame 結構中寄存的值 3 83e8cfee 6a00 push 0 // errorcode 填入零來進行對齊 4 83e8cff0 55 push ebp 5 83e8cff1 53 push ebx 6 83e8cff2 56 push esi 7 83e8cff3 57 push edi 8 83e8cff4 0fa0 push fs 9 10 // 根據30h這個值做爲段選擇子,查找gdt這張表,找出段描描述符,將其加載到fs段寄存器中 11 // 30h -> 0011 0000 -> index = 00110 | TI = 0 |00 -> TI=0查LDT表,查索引爲6查出來的是 834093f7`ac003748 這個值。 12 // 834093f7`ac003748 段描述符,根據段描述符屬性拼接起來的 83f7ac00 13 // 83f7ac00 指向的是 KPCR,FS在零環的時候,指向KPCR,再也不是三環時線程TEB這個結構體 14 83e8cff6 bb30000000 mov ebx,30h 15 83e8cffb 668ee3 mov fs,bx 16 83e8cffe bb23000000 mov ebx,23h 17 83e8d003 8edb mov ds,bx 18 83e8d005 8ec3 mov es,bx 19 20 21 // 有些API 0環和三環均可以調用,經過下面操做能夠查看原來調用的是0環仍是3環,再進行調用API的權限驗證 22 // 驗證完該線程,若是是0環,則bl爲0;若是是3環,則bl=1 23 83e8d007 648b3524010000 mov esi,dword ptr fs:[124h] // 將當前CPU正在跑的線程放到esi中 24 83e8d00e 64ff3500000000 push dword ptr fs:[0] // 把老的EXCEPTION_LIST 存入 _Ktrap_frame 結構體中(fs上邊那部分) 25 83e8d015 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh // 將如今EXCPTION_LIST放入異常鏈表 26 83e8d020 ffb63a010000 push dword ptr [esi+13Ah] // 將老的"先前模式"也保存到棧中 27 83e8d026 83ec48 sub esp,48h // 將當前ESP提高到 _Ktrap_frame 第一個成員 28 83e8d029 8b5c246c mov ebx,dword ptr [esp+6Ch] // 取出3環壓入的參數CS _KTRAP_FRAME + 0x6c,指向三環原來的CS值 29 83e8d02d 83e301 and ebx,1 // 將原來CS的值進行與運算, 0環最低爲爲0,3環最低爲爲1 30 83e8d030 889e3a010000 mov byte ptr [esi+13Ah],bl // 新的"先前模式",原來是三環模式爲1;原來是零環模式爲0 31 83e8d036 8bec mov ebp,esp // ebp 一樣指向_Ktrap_frame第一個成員 32 83e8d038 8b9e28010000 mov ebx,dword ptr [esi+128h] // esi經過0x83e8d007處[KPCR+124],指向Ethread // Ethread+128h 指向的是 _KTRAP_FRAME這個結構體指針 33 83e8d03e 895d3c mov dword ptr [ebp+3Ch],ebx // 將該 _KTRAP_FRAME地址暫時保存在 edx 中 34 83e8d041 83652c00 and dword ptr [ebp+2Ch],0 35 83e8d045 f64603df test byte ptr [esi+3],0DFh // 判斷是否屬於調試狀態 一個位 +0x003 DebugActive : UChar 36 83e8d049 89ae28010000 mov dword ptr [esi+128h],ebp // 由於新的 trap_frame地址已經發生變化,因此要將新的trapFrame放到線程放到這個位置 37 38 83e8d04f fc cld 39 83e8d050 0f859afeffff jne nt!Dr_kss_a (83e8cef0) // 在0x83e8d045處根據判斷結果,若是是調試狀態,則進行跳轉。 40 // 若爲調試,則跳過去將調試相關的寄存器也存入_KTRAP_FRAME這個結構體 41 // 不然不是調試器,則將中是-1。(硬件斷點使用這個,反調試很管用) 42 83e8d056 8b5d60 mov ebx,dword ptr [ebp+60h] // 3環的EBP 43 83e8d059 8b7d68 mov edi,dword ptr [ebp+68h] // 3環的EIP 44 83e8d05c 89550c mov dword ptr [ebp+0Ch],edx // edx存放着三環參數的指針 45 // {mov edx,esp;sysenter} 46 83e8d05f c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h // 操做系統須要用的標誌 47 83e8d066 895d00 mov dword ptr [ebp],ebx // 3環的ebp存儲到DbgEbp的位置 48 83e8d069 897d04 mov dword ptr [ebp+4],edi // 3環的eip存放到 DbgEip 49 83e8d06c fb sti // 關閉中斷 50 83e8d06d e9dd000000 jmp nt!KiFastCallEntry+0x8f (83e8d14f) // 取出系統調用號,3環傳進來的。 51 // 注意,這個就是快速調用首先進入的函數,至關於中斷多走了這一步,系統調用直接調用。
4、nt!KiFastCallEntry的反彙編代碼
該反彙編代碼閱讀量一樣很大,找時間必定給讀出來,若是感興趣能夠自行百度相關解讀。
1 nt!KiFastCallEntry: 2 83e8d0c0 b923000000 mov ecx,23h 3 83e8d0c5 6a30 push 30h 4 83e8d0c7 0fa1 pop fs 5 83e8d0c9 8ed9 mov ds,cx 6 83e8d0cb 8ec1 mov es,cx 7 83e8d0cd 648b0d40000000 mov ecx,dword ptr fs:[40h] 8 83e8d0d4 8b6104 mov esp,dword ptr [ecx+4] 9 83e8d0d7 6a23 push 23h 10 83e8d0d9 52 push edx 11 83e8d0da 9c pushfd 12 83e8d0db 6a02 push 2 13 83e8d0dd 83c208 add edx,8 14 83e8d0e0 9d popfd 15 83e8d0e1 804c240102 or byte ptr [esp+1],2 16 83e8d0e6 6a1b push 1Bh 17 83e8d0e8 ff350403dfff push dword ptr ds:[0FFDF0304h] 18 83e8d0ee 6a00 push 0 19 83e8d0f0 55 push ebp 20 83e8d0f1 53 push ebx 21 83e8d0f2 56 push esi 22 83e8d0f3 57 push edi 23 83e8d0f4 648b1d1c000000 mov ebx,dword ptr fs:[1Ch] 24 83e8d0fb 6a3b push 3Bh 25 83e8d0fd 8bb324010000 mov esi,dword ptr [ebx+124h] 26 83e8d103 ff33 push dword ptr [ebx] 27 83e8d105 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh 28 83e8d10b 8b6e28 mov ebp,dword ptr [esi+28h] 29 83e8d10e 6a01 push 1 30 83e8d110 83ec48 sub esp,48h 31 83e8d113 81ed9c020000 sub ebp,29Ch 32 83e8d119 c6863a01000001 mov byte ptr [esi+13Ah],1 33 83e8d120 3bec cmp ebp,esp 34 83e8d122 7597 jne nt!KiFastCallEntry2+0x49 (83e8d0bb) 35 83e8d124 83652c00 and dword ptr [ebp+2Ch],0 36 83e8d128 f64603df test byte ptr [esi+3],0DFh 37 83e8d12c 89ae28010000 mov dword ptr [esi+128h],ebp 38 83e8d132 0f8538feffff jne nt!Dr_FastCallDrSave (83e8cf70) 39 83e8d138 8b5d60 mov ebx,dword ptr [ebp+60h] 40 83e8d13b 8b7d68 mov edi,dword ptr [ebp+68h] 41 83e8d13e 89550c mov dword ptr [ebp+0Ch],edx 42 83e8d141 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h 43 83e8d148 895d00 mov dword ptr [ebp],ebx 44 83e8d14b 897d04 mov dword ptr [ebp+4],edi 45 83e8d14e fb sti 46 83e8d14f 8bf8 mov edi,eax 47 83e8d151 c1ef08 shr edi,8 48 83e8d154 83e710 and edi,10h 49 83e8d157 8bcf mov ecx,edi 50 83e8d159 03bebc000000 add edi,dword ptr [esi+0BCh] 51 83e8d15f 8bd8 mov ebx,eax 52 83e8d161 25ff0f0000 and eax,0FFFh 53 83e8d166 3b4708 cmp eax,dword ptr [edi+8] 54 83e8d169 0f8333fdffff jae nt!KiBBTUnexpectedRange (83e8cea2) 55 83e8d16f 83f910 cmp ecx,10h 56 83e8d172 751a jne nt!KiFastCallEntry+0xce (83e8d18e) 57 83e8d174 8b8e88000000 mov ecx,dword ptr [esi+88h] 58 83e8d17a 33f6 xor esi,esi 59 83e8d17c 0bb1700f0000 or esi,dword ptr [ecx+0F70h] 60 83e8d182 740a je nt!KiFastCallEntry+0xce (83e8d18e) 61 83e8d184 52 push edx 62 83e8d185 50 push eax 63 83e8d186 ff154c99fb83 call dword ptr [nt!KeGdiFlushUserBatch (83fb994c)] 64 83e8d18c 58 pop eax 65 83e8d18d 5a pop edx 66 83e8d18e 64ff05b0060000 inc dword ptr fs:[6B0h] 67 83e8d195 8bf2 mov esi,edx 68 83e8d197 33c9 xor ecx,ecx 69 83e8d199 8b570c mov edx,dword ptr [edi+0Ch] 70 83e8d19c 8b3f mov edi,dword ptr [edi] 71 83e8d19e 8a0c10 mov cl,byte ptr [eax+edx] 72 83e8d1a1 8b1487 mov edx,dword ptr [edi+eax*4] 73 83e8d1a4 2be1 sub esp,ecx 74 83e8d1a6 c1e902 shr ecx,2 75 83e8d1a9 8bfc mov edi,esp 76 83e8d1ab 3b351c97fb83 cmp esi,dword ptr [nt!MmUserProbeAddress (83fb971c)] 77 83e8d1b1 0f832e020000 jae nt!KiSystemCallExit2+0xa5 (83e8d3e5) 78 83e8d1b7 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] 79 83e8d1b9 f6456c01 test byte ptr [ebp+6Ch],1 80 83e8d1bd 7416 je nt!KiFastCallEntry+0x115 (83e8d1d5) 81 83e8d1bf 648b0d24010000 mov ecx,dword ptr fs:[124h] 82 83e8d1c6 8b3c24 mov edi,dword ptr [esp] 83 83e8d1c9 89993c010000 mov dword ptr [ecx+13Ch],ebx 84 83e8d1cf 89b92c010000 mov dword ptr [ecx+12Ch],edi 85 83e8d1d5 8bda mov ebx,edx 86 83e8d1d7 f6050869f88340 test byte ptr [nt!PerfGlobalGroupMask+0x8 (83f86908)],40h 87 83e8d1de 0f954512 setne byte ptr [ebp+12h] 88 83e8d1e2 0f858c030000 jne nt!KiServiceExit2+0x17b (83e8d574) 89 83e8d1e8 ffd3 call ebx 90 83e8d1ea f6456c01 test byte ptr [ebp+6Ch],1 91 83e8d1ee 7434 je nt!KiFastCallEntry+0x164 (83e8d224) 92 83e8d1f0 8bf0 mov esi,eax 93 83e8d1f2 ff156801e583 call dword ptr [nt!_imp__KeGetCurrentIrql (83e50168)] 94 83e8d1f8 0ac0 or al,al 95 83e8d1fa 0f853b030000 jne nt!KiServiceExit2+0x142 (83e8d53b) 96 83e8d200 8bc6 mov eax,esi 97 83e8d202 648b0d24010000 mov ecx,dword ptr fs:[124h] 98 83e8d209 f68134010000ff test byte ptr [ecx+134h],0FFh 99 83e8d210 0f8543030000 jne nt!KiServiceExit2+0x160 (83e8d559) 100 83e8d216 8b9184000000 mov edx,dword ptr [ecx+84h] 101 83e8d21c 0bd2 or edx,edx 102 83e8d21e 0f8535030000 jne nt!KiServiceExit2+0x160 (83e8d559) 103 83e8d224 8be5 mov esp,ebp 104 83e8d226 807d1200 cmp byte ptr [ebp+12h],0 105 83e8d22a 0f8550030000 jne nt!KiServiceExit2+0x187 (83e8d580) 106 83e8d230 648b0d24010000 mov ecx,dword ptr fs:[124h] 107 83e8d237 8b553c mov edx,dword ptr [ebp+3Ch] 108 83e8d23a 899128010000 mov dword ptr [ecx+128h],edx