在32位Windows中咱們有不少定位SSDT的方法,最直接的就是利用導出符號來找到SSDT。再有就是經過在nt!KeAddSystemServiceTable函數中進行反彙編搜索。但是在64位WINDOWS中這兩種方法都行不通。在64位Windows中不在導出SSDT了,同時nt!KeAddSystemServiceTable中也再也不出SSDT了(此處說明有誤,應該爲不出現直接的SSDT地址了,我的水平有限感受計算太麻煩。)。這樣要HOOK SSDT表就出現了第一個問題如何找到它?我想了三種思路。
思路1:
對每種版本的系統肯定一個ntoskrnl到SSDT的硬編碼偏移。呵呵,這種方法可能比較傻但最簡單直接有效。就是維護起來比較很是麻煩,要對每個版本進單獨處理,若是不一樣補丁版本有變化也要處理。
思路2:
64位WINDOWS中存放着每一個服務例程的入口偏移(這個偏移是相對於SSDT的開始地址的)。我取一個通常不被HOOK的服務例程入口偏移,而後從ntoskrnl的開始地址一直搜索到結束來查找這四個字節,而後根據服務例程的索引定位到SSDT的開始位置。可能對不一樣版本的操做系統其偏移和索引是不一樣的須要分別進行處理,可是相比第一種思路要通用一些。
思路3:
仍是利用反彙編的方法,可是nt!KeAddSystemServiceTable函數中已經找不到直接的SSDT地址了。後來我想能不能映像搜索到SSDT的地址。
kd> dq nt!KeServiceDescriptorTable
fffff800`03eab840 fffff800`03c75b00 00000000`00000000
fffff800`03eab850 00000000`00000191 fffff800`03c7678c
fffff800`03eab860 00000000`00000000 00000000`00000000
fffff800`03eab870 00000000`00000000 00000000`00000000
fffff800`03eab880 fffff800`03c75b00 00000000`00000000
fffff800`03eab890 00000000`00000191 fffff800`03c7678c
fffff800`03eab8a0 fffff960`00111c00 00000000`00000000
fffff800`03eab8b0 00000000`0000033b fffff960`0011391c
kd> lm m nt
start end module name
fffff800`03c03000 fffff800`041e0000 nt (pdb symbols)
kd> s -q fffff800`03c03000 l600000 fffff800`03eab840
很不幸我沒有找到任何相關的信息。呵呵,可是我賊心不是死,相信確定在某處會有引用的。因而我搜索了一下全部名稱中含有Service單詞的符號。
kd> x nt!Ki*Service*
fffff800`03c73e40 nt!KiServiceInternal = <no type information>
fffff800`03c7415b nt!KiSystemServiceExit = <no type information>
fffff800`03c73fde nt!KiSystemServiceStart = <no type information>
fffff800`03c76788 nt!KiServiceLimit = <no type information>
fffff800`03c73b00 nt!KiDebugServiceTrap = <no type information>
fffff800`03c74140 nt!KiSystemServiceCopyEnd = <no type information>
fffff800`03c75b00 nt!KiServiceTable = <no type information>
fffff800`03c74037 nt!KiSystemServiceGdiTebAccess = <no type information>
fffff800`03c73ff2 nt!KiSystemServiceRepeat = <no type information>
fffff800`03c740d0 nt!KiSystemServiceCopyStart = <no type information>
fffff800`03c706f0 nt!KiServiceLinkage = <no type information>
fffff800`03c73d40 nt!KiSystemServiceHandler = <no type information>
終於在nt!KiSystemServiceRepeat 中找到SSDT的信息。
nt!KiSystemServiceRepeat:
fffff800`03c73ff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`03eab840)]
fffff800`03c73ff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`03eab880)]
fffff800`03c74000 f7830001000080000000 test dword ptr [rbx+100h],80h
fffff800`03c7400a 4d0f45d3 cmovne r10,r11
fffff800`03c7400e 423b441710 cmp eax,dword ptr [rdi+r10+10h]
fffff800`03c74013 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`03c74302)
fffff800`03c74019 4e8b1417 mov r10,qword ptr [rdi+r10]
fffff800`03c7401d 4d631c82 movsxd r11,dword ptr [r10+rax*4]
可是一個新的問題也同時產生了,nt!KiSystemServiceRepeat並非系統的一個導出函數。那我怎麼找到它呢?後來一想雖然它不導出但應該至少存在一條從外部到它的一個調用路徑吧!那我就經過這條路徑找到它不就好了。想到此就對它下了個斷點看看都是誰會調用它。結果和我想的同樣,不過發現我真是一個菜鳥,找到SSDT值過高興竟沒有仔細看它的彙編代碼。nt!KiSystemServiceRepeat就是完成根據調用號從SSDT中得到服務例程入口並調用的。那就說每個ZwXXX類的函數都會調用到它,而Zw類的函數是系統導出的這樣咱們能夠經過任何一個Zw類的函數來找到nt!KiSystemServiceRepeat從而定位到SSDT。因而我手工嘗試了一下。下面是嘗試的過程。
kd> u nt!ZwClose l20
nt!ZwClose:
fffff800`03c6d640 488bc4 mov rax,rsp
fffff800`03c6d643 fa cli
fffff800`03c6d644 4883ec10 sub rsp,10h
fffff800`03c6d648 50 push rax
fffff800`03c6d649 9c pushfq
fffff800`03c6d64a 6a10 push 10h
fffff800`03c6d64c 488d059d300000 lea rax,[nt!KiServiceLinkage (fffff800`03c706f0)]
fffff800`03c6d653 50 push rax
fffff800`03c6d654 b80c000000 mov eax,0Ch
fffff800`03c6d659 e9e2670000 jmp nt!KiServiceInternal (fffff800`03c73e40)
fffff800`03c6d65e 6690 xchg ax,ax
在ZwClose中(其它相似)跳轉到了nt!KiServiceInternal 咱們再跟蹤nt!KiServiceInternal
nt!KiServiceInternal:
fffff800`03c73e40 4883ec08 sub rsp,8
fffff800`03c73e44 55 push rbp
fffff800`03c73e45 4881ec58010000 sub rsp,158h
fffff800`03c73e4c 488dac2480000000 lea rbp,[rsp+80h]
fffff800`03c73e54 48899dc0000000 mov qword ptr [rbp+0C0h],rbx
fffff800`03c73e5b 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi
fffff800`03c73e62 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi
fffff800`03c73e69 fb sti
fffff800`03c73e6a 65488b1c2588010000 mov rbx,qword ptr gs:[188h]
fffff800`03c73e73 0f0d8bd8010000 prefetchw [rbx+1D8h]
fffff800`03c73e7a 0fb6bbf6010000 movzx edi,byte ptr [rbx+1F6h]
fffff800`03c73e81 40887da8 mov byte ptr [rbp-58h],dil
fffff800`03c73e85 c683f601000000 mov byte ptr [rbx+1F6h],0
fffff800`03c73e8c 4c8b93d8010000 mov r10,qword ptr [rbx+1D8h]
fffff800`03c73e93 4c8995b8000000 mov qword ptr [rbp+0B8h],r10
fffff800`03c73e9a 4c8d1d3d010000 lea r11,[nt!KiSystemServiceStart (fffff800`03c73fde)]
fffff800`03c73ea1 41ffe3 jmp r11
fffff800`03c73ea4 666666666666660f1f840000000000 nop word ptr [rax+rax]
fffff800`03c73eb3 66666666660f1f840000000000 nop word ptr [rax+rax]
在這個函數中又跳轉到了nt!KiSystemServiceStart,這裏4c8d1d3d010000 這條指令須要參考一下x64彙編中有關Rex.w前綴及Mod r/m尋址方面的知識來解釋這個偏移。在這裏就是fffff800`03c73ea1+013d就是nt!KiSystemServiceStart入口地址。接下來再從這個地址開始反彙編。
nt!KiSystemServiceStart:
fffff800`03c73fde 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp
fffff800`03c73fe5 8bf8 mov edi,eax
fffff800`03c73fe7 c1ef07 shr edi,7
fffff800`03c73fea 83e720 and edi,20h
fffff800`03c73fed 25ff0f0000 and eax,0FFFh
nt!KiSystemServiceRepeat:
fffff800`03c73ff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`03eab840)]
fffff800`03c73ff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`03eab880)]
fffff800`03c74000 f7830001000080000000 test dword ptr [rbx+100h],80h
fffff800`03c7400a 4d0f45d3 cmovne r10,r11
fffff800`03c7400e 423b441710 cmp eax,dword ptr [rdi+r10+10h]
fffff800`03c74013 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`03c74302)
fffff800`03c74019 4e8b1417 mov r10,qword ptr [rdi+r10]
fffff800`03c7401d 4d631c82 movsxd r11,dword ptr [r10+rax*4]
fffff800`03c74021 498bc3 mov rax,r11
fffff800`03c74024 49c1fb04 sar r11,4
fffff800`03c74028 4d03d3 add r10,r11
fffff800`03c7402b 83ff20 cmp edi,20h
fffff800`03c7402e 7550 jne nt!KiSystemServiceGdiTebAccess+0x49 (fffff800`03c74080)
fffff800`03c74030 4c8b9bb8000000 mov r11,qword ptr [rbx+0B8h]
終於找到咱們要找到函數了,4c8d1547782300,4c8d1d80782300根據這兩條指令我就能夠定位到SSDT的位置了。終於撥雲見日了。這兩條指令都加了REX.W前綴的根據具體字段的意義解釋它就可找到SSDT,4c8d1547782300這條指令的64當即數尋址應該是fffff800`03c73ff9+237847這樣來擴展,就是RIP+偏移。相相似4c8d1d80782300指令中的當即數應該被擴展成fffff800`03c74000+237880,經過DQ命令查看就是SSDT的內容。
kd> dq fffff800`03c74000+237880
fffff800`03eab880 fffff800`03c75b00 00000000`00000000
fffff800`03eab890 00000000`00000191 fffff800`03c7678c
fffff800`03eab8a0 fffff960`00111c00 00000000`00000000
fffff800`03eab8b0 00000000`0000033b fffff960`0011391c
fffff800`03eab8c0 00000000`7771fdd6 00000000`00000000
fffff800`03eab8d0 fffff800`00a01400 fffff800`00a013b0
fffff800`03eab8e0 00000000`00000002 00000000`00005bdb
fffff800`03eab8f0 00000000`00023f05 00000000`00000000
kd> dq nt!KeServiceDescriptorTableShadow
fffff800`03eab880 fffff800`03c75b00 00000000`00000000
fffff800`03eab890 00000000`00000191 fffff800`03c7678c
fffff800`03eab8a0 fffff960`00111c00 00000000`00000000
fffff800`03eab8b0 00000000`0000033b fffff960`0011391c
fffff800`03eab8c0 00000000`7771fdd6 00000000`00000000
fffff800`03eab8d0 fffff800`00a01400 fffff800`00a013b0
fffff800`03eab8e0 00000000`00000002 00000000`00005bdb
fffff800`03eab8f0 00000000`00023f05 00000000`00000000
兩種方法找到位置同樣。我這裏的輸出都針對WIN7 64位系統的調用,經過對2003進行調試發現這樣的方法也適用。由於沒有暫時沒有其它版本操做系統。在其它操做系統上的適用性怎麼樣就無從知道了。我的推測XP,VISTA也應該差很少但須要事實證實。不過Win7與2003中SSDT表項內容略有不一樣,雖然都是四個節最後四位是例程的參數個數,但在2003上偏移的計算是SSDT基址+表項值&0xFFFFFFF0,而win7上則變成了SSDT基址+表項值>>4。若是HOOK表項仍是要作一個分別對待。我是第一次發帖,你們輕拍啊。上面闡述若有錯誤請個位大俠多多指教,小弟不勝感激。函數