很多游戲保護(hù)系統(tǒng)中,或一些殺毒軟件中,都會(huì)對(duì)該表進(jìn)行修改,從而改變系統(tǒng)函數(shù)調(diào)用流程來起到反外掛、反病毒的作用。同樣,病毒也在修改該表,從而修改系統(tǒng)函數(shù)調(diào)用流程來完成其自身的目的。這張非常關(guān)鍵的表叫作SSDT,即System Service Descriptor Table(系統(tǒng)服務(wù)描述表)。這張表的作用是把用戶層的Win32 API和內(nèi)核層的API建立一個(gè)關(guān)聯(lián)。在該表中維護(hù)非常多Native API,或稱本地API。下面通過WinDbg來查看該表。
使用WinDbg連接到虛擬機(jī)上,然后在命令提示符處輸入dd KeServiceDescriptorTable命令,會(huì)得到一些十六進(jìn)制的輸出。KeServiceDescriptorTable是Ntoskrnl.exe導(dǎo)出的一個(gè)指針,用來指向SSDT表。下面來查看命令的輸出結(jié)果:
kd> dd KeServiceDescriptorTable
80553fa0 80502b8c 00000000 0000011c 80503000
80553fb0 00000000 00000000 00000000 00000000
80553fc0 00000000 00000000 00000000 00000000
80553fd0 00000000 00000000 00000000 00000000
80553fe0 00002710 bf80c0b6 00000000 00000000
80553ff0 fc142a80 80e2d890 80cee0f0 806e2f40
80554000 00000000 00000000 c169a786 00009a34
80554010 3beab1c6 01cc052a 00000000 00000000
在該輸出中,第一行就是SSDT表,該表中的80502b8c是一個(gè)函數(shù)指針數(shù)組,該指針數(shù)組保存了所有Native API的函數(shù)地址,0000011c是數(shù)組的大小,80503000里面保存的是一個(gè)參數(shù)個(gè)數(shù)數(shù)組,與Native API相對(duì)應(yīng)。將SSDT定義成一個(gè)結(jié)構(gòu)體,具體如下:
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PULONG ServiceTableBase;
PULONG Reseave;
ULONG NumberOfServices;
PUCHAR ParamTableBase;
}SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
要想在驅(qū)動(dòng)中獲得該表,需要使用Notokrnl.exe導(dǎo)出的KeServiceDescriptorTable,將其定義如下:
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
有了上面的SSDT表和KeServiceDescriptorTable的定義,就可以編寫與SSDT相關(guān)的程序了,不過似乎還少點(diǎn)什么。表里面對(duì)應(yīng)的Native API到底是什么?用WinDbg來看一下,輸入dd 80502b8c,輸出結(jié)果如下:
kd> dd 80502b8c
80502b8c 8059a948 805e7db6 805eb5fc 805e7de8
80502b9c 805eb636 805e7e1e 805eb67a 805eb6be
80502bac 8060cdfe 8060db50 805e31b4 805e2e0c
80502bbc 805cbde6 805cbd96 8060d424 805ac5ae
80502bcc 8060ca3c 8059edbe 805a6a00 805cd8c4
80502bdc 80500828 8060db42 8056ccd6 8053600e
80502bec 806060d4 805b2c3a 805ebb36 8061ae56
80502bfc 805f0028 8059b036 8061b0aa 8059a8e8
全都是一些地址值比較接近的函數(shù)地址,為什么說是函數(shù)地址?因?yàn)檫@是函數(shù)指針數(shù)組。輸入u 8059a948命令,輸出如下:
kd> u 8059a948
nt!NtAcceptConnectPort:
8059a948 689c000000 push 9Ch
8059a94d 6838a14d80 push offset nt!_real+0x128 (804da138)
8059a952 e8b9e5f9ff call nt!_SEH_prolog (80538f10)
8059a957 64a124010000 mov eax,dword ptr fs:[00000124h]
8059a95d 8a8040010000 mov al,byte ptr [eax+140h]
8059a963 884590 mov byte ptr [ebp-70h],al
8059a966 84c0 test al,al
8059a968 0f84b9010000 je nt!NtAcceptConnectPort+0x1df (8059ab27)
從輸出可以看出,8059a948是NtAcceptConnectPort()函數(shù)的地址。再來看一個(gè)地址,輸入u 805e7db6命令,輸出如下:
kd> u 805e7db6
nt!NtAccessCheck:
805e7db6 8bff mov edi,edi
805e7db8 55 push ebp
805e7db9 8bec mov ebp,esp
805e7dbb 33c0 xor eax,eax
805e7dbd 50 push eax
805e7dbe ff7524 push dword ptr [ebp+24h]
805e7dc1 ff7520 push dword ptr [ebp+20h]
805e7dc4 ff751c push dword ptr [ebp+1Ch]
這次輸出的是NtAccessCheck()函數(shù)的反匯編代碼。在SSDT表中,第3個(gè)參數(shù)表明,這個(gè)數(shù)組的大小是0x11c,也就是數(shù)組最后一項(xiàng)的下標(biāo)是0x11b。再來看下標(biāo)為0x11b的數(shù)組項(xiàng)中保存的地址是多少。輸入命令dd 80502b8c + 11b * 4,80502b8c是數(shù)組的起始地址,11b是數(shù)組下標(biāo),那么乘4是什么原因呢?數(shù)組地址的定位是通過數(shù)組首地址+下標(biāo)×數(shù)組元素字節(jié)數(shù)得出的。一個(gè)函數(shù)的地址占用4字節(jié),因此要做乘4的操作。該命令輸出如下:
kd> dd 80502b8c + 11b * 4
80502ff8 805c2798 0000011c 2c2c2018 44402c40
80503008 1818080c 0c040408 08081810 0808040c
80503018 080c0404 2004040c 140c1008 0c102c0c
80503028 10201c0c 20141038 141c2424 34102010
80503038 080c0814 04040404 0428080c 1808181c
80503048 1808180c 040c080c 100c0010 10080828
80503058 0c08041c 00081004 0c080408 10040828
80503068 0c0c0404 28240428 0c0c0c30 0c0c0c18
再用u命令來查看805c2798處的反匯編代碼。輸入命令u 805c2798,輸出如下:
kd> u 805c2798
nt!NtQueryPortInformationProcess:
805c2798 64a124010000 mov eax,dword ptr fs:[00000124h]
805c279e 8b4844 mov ecx,dword ptr [eax+44h]
805c27a1 83b9bc00000000 cmp dword ptr [ecx+0BCh],0
805c27a8 740d je nt!NtQueryPortInformationProcess+0x1f (805c27b7)
805c27aa f6804802000004 test byte ptr [eax+248h],4
805c27b1 7504 jne nt!NtQueryPortInformationProcess+0x1f (805c27b7)
805c27b3 33c0 xor eax,eax
805c27b5 40 inc eax
數(shù)組中最后一項(xiàng)保存的是NtQueryPortInformationProcess()函數(shù)的地址。