给你带来与众不同的特别感受

简单说说SSDT (转贴)

上一篇 / 下一篇  2008-06-10 19:54:10 / 个人分类:杂志相关

简单说说SSDT
爱好者博墅 v h#`j&I"u;xJc YE,R
论技术,我还差得远,而且网上关于SSDT的文章也多不胜数。但是还是想自己写一下,因为我想试试我能不能用最简单的语言来描述SSDT——这个对一般来人来说比较神秘的属于内核的地带。引用EVA说的一句话,“以为写个驱动就是内核,还远着了”——大概是这么个意思,记得不是很清楚。 爱好者博墅kQ'])dk6j M*j\*F

o2p;t)mHu%K0关于SSDT,描述得最清楚的应该算《SSDT Hook的妙用-对抗ring0 inline hook》一文了,作者是堕落天才。这里引用一下他写的开头部分,略有个别字符的修改:
7jB*nB\0
`3{;u-iL0内核中有两个系统服务描述符表,一个是KeServiceDescriptorTable,由ntoskrnl.exe导出,一个是KeServieDescriptorTableShadow,没有导出。这两者都是一个结构体,结构下面会给出。他们的区别是,KeServiceDescriptorTable仅有 ntoskrnel一项,而KeServieDescriptorTableShadow则包含了ntoskrnel和win32k。一般的Native API的服务地址由KeServiceDescriptorTable分派,而gdi.dll和
*l7SP @ u2i9`#Da0user.dll的内核API调用服务地址,由 KeServieDescriptorTableShadow分派。还有要清楚一点的是win32k.sys只有在GUI线程中才加载,一般情况下是不加载的。 爱好者博墅v{ |H5b%iY5}']H*y2}

(?9vgI!l:I0他们的结构如下: 爱好者博墅 A,I4`$@|6U\:Dzo.f
代码:
爱好者博墅7~&j*i-T&L/QT
typedef struct _SYSTEM_SERVICE_TABLE 爱好者博墅KeUrd2D
{
kxm$STn`;a/bz3~&y0    PVOID ServiceTableBase;    //这个指向系统服务函数地址表 爱好者博墅&\q{@W$L
    PULONG ServiceCounterTableBase; 爱好者博墅[+c BS D^AY3S6?
    ULONG NumberOfService;     //服务函数的个数,NumberOfService*4 就是整个地址表的大小 爱好者博墅)F-Y8L`'WG
    ULONG ParamTableBase; 爱好者博墅:u uU:J/h$f J
}SYSTEM_SERVICE_TABLE,*PSYSTEM_SERVICE_TABLE;
5sY7C!A&f"^~0
oz9Q6w,av0typedef struct _SERVICE_DESCRIPTOR_TABLE 爱好者博墅RK([P/f+XO\Xx
{ 爱好者博墅$\j5i7Cq#P
    SYSTEM_SERVICE_TABLE ntoskrnel;    //ntoskrnl.exe的服务函数
Z&?yEG(qW2\l0    SYSTEM_SERVICE_TABLE win32k;    //win32k.sys的服务函数,(gdi.dll/user.dll的内核支持)
*\(hh"l M3N'M&b1?2Nq0    SYSTEM_SERVICE_TABLE NotUsed1;
sq4H\0u6q0    SYSTEM_SERVICE_TABLE NotUsed2; 爱好者博墅CLoL tIBop
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
ILI7v9hKo!bz0
爱好者博墅2N#`'O2{B m

:Lly,\cZg6}0当系统需要使用一个本机API的时候,就会去查找SYSTEM_DESCRIPTOR_TABLE这个表,也就是由ntoskrnl.exe导出的KeServiceDescriptorTable: 爱好者博墅x%x/N1PX7J1Y

JU/{1N X_"J7j0代码:

)qJ T.G})B gcg ZB0nt!RtlpBreakWithStatusInstruction: 爱好者博墅*z;L d3z0m}\
80527fc8 cc              int     3 爱好者博墅.n qI.n@4](zV H{
kd> dd KeServiceDescriptorTable 爱好者博墅+IY,ii7[ `}
80553380  805021fc 00000000 0000011c 80502670 爱好者博墅px7S/C5pv
80553390  00000000 00000000 00000000 00000000 爱好者博墅:Z,N/lh3Jx._Khj
805533a0  00000000 00000000 00000000 00000000
[+k,~;B*Spo#iu0805533b0  00000000 00000000 00000000 00000000
{x}"s_InwD$k4F0805533c0  00002710 bf80c227 00000000 00000000 爱好者博墅@(^ ktdy
805533d0  f9e6da80 f963a9e0 816850f0 806e0f40
P1IOUU8U;h0805533e0  00000000 00000000 00000000 00000000
/rLA0Iu0805533f0  97c5ac40 01c7abf5 00000000 00000000 爱好者博墅/}T u1v3g
爱好者博墅:Oa"am5k1S-V
可以看到,KeServiceDescriptorTable的地址是80553380。现在看看这个地址保存的是什么,因为这个地址的值就是SYSTEM_SERVICE_TABLE的起始地址。好了,我们看到这个地址保存的是805021fc,那么也就是说,系统服务的地址表起始地址为805021fc了。看看这个表是些什么鬼东西:
R$F0oQ5U!wLQ6?5E0代码:

Z^8l Q3v0Q'U0kd> dd 805021fc
SJ a3LK/~2p+se0805021fc  80599746 805e6914 805ea15a 805e6946 爱好者博墅m;{P}1dv@T)D'g
8050220c  805ea194 805e697c 805ea1d8 805ea21c 爱好者博墅E1^2k0}A"g!uU/n`
8050221c  8060b880 8060c5d2 805e1cac 805e1904
ah]@m{ E:LS t08050222c  805ca928 805ca8d8 8060bea6 805ab334 爱好者博墅yh8W5gqON$K E!r e1{
8050223c  8060b4be 8059dbbc 805a5786 805cc406 爱好者博墅 ]TLxAW D_B
8050224c  804ffed0 8060c5c4 8056be64 805353f2 爱好者博墅&o W"M$f/r SxF
8050225c  80604b90 805b19c0 805ea694 80619a56
#z0^V|8B'ku%q08050226c  805eeb86 80599e34 80619caa 805996e6
Y8Xc{ G@V0

]t2m|^0这个过程是这样的,最开始是SYSTEM_DESCRIPTOR_TABLE(80553380)保存了SYSTEM_SERVICE_TABLE的地址(805021fc),SYSTEM_SERVICE_TABLE的地址(805021fc)又保存了很多地址,这个地址就是系统服务的地址了,类似NtOpenProcess这样的ring0的函数地址。这样,系统就可以方便的找到每一个ring0函数去调用。 爱好者博墅-S ^/gsO+g6b
爱好者博墅t@0aW3vd
我们先看看第一个地址80599746是个什么函数,反汇编一下:
]$t+u/|/wC,~;e#L0代码:
爱好者博墅y\V3_ y Lo
kd> u 80599746
V U:F*Y"zW7l*t,]m0nt!NtAcceptConnectPort: 爱好者博墅4@5[!|kc
80599746 689c000000      push    9Ch 爱好者博墅z }2nmGYfD+B
8059974b 6820a14d80      push    offset nt!_real+0x128 (804da120)
YQ5v![@K(x$o080599750 e8abebf9ff      call    nt!_SEH_prolog (80538300)
N{8L|)O080599755 64a124010000    mov     eax,dword ptr fs:[00000124h] 爱好者博墅K-iF R"p
8059975b 8a8040010000    mov     al,byte ptr [eax+140h]
)rm E5G N4bc#x080599761 884590          mov     byte ptr [ebp-70h],al 爱好者博墅o4Q VE}{ Q
80599764 84c0            test    al,al 爱好者博墅7Q#gZe9qr&e
80599766 0f84b9010000    je      nt!NtAcceptConnectPort+0x1df (80599925)
9Z1tr4kgG+B0

3^*pPkkv+?0原来是NtAcceptConnectPort函数,第二个805e6914呢?我们也看一下, 爱好者博墅$_4b`1{(Yb
代码:

r&QvZ O n0kd> u 805e6914 爱好者博墅cQ)[,I!r
nt!NtAccessCheck: 爱好者博墅 w0pR(NG6iY
805e6914 8bff            mov     edi,edi 爱好者博墅RokC;eq+Vj
805e6916 55              push    ebp 爱好者博墅N&z!O'b.yQY
805e6917 8bec            mov     ebp,esp 爱好者博墅T)lo3ByMR1b$C-@q
805e6919 33c0            xor     eax,eax 爱好者博墅0I ` i&e&a(M!Z
805e691b 50              push    eax 爱好者博墅(U3p/k;z|jM
805e691c ff7524          push    dword ptr [ebp+24h]
v.TcWz5q-[0805e691f ff7520          push    dword ptr [ebp+20h] 爱好者博墅1b4EA!lOb#M\
805e6922 ff751c          push    dword ptr [ebp+1Ch] 爱好者博墅8o4T_?v:iI&g
爱好者博墅,ZnkC#l P7{\;iVK
原来是NtAccessCheck函数。
~'V;|lrX(V0
9H-?JR)Pt&i0这样我们可以清楚的看到,在这个起始地址为0x805021fc的表中,保存了各个ring0函数的地址。下面我来做个简单的比喻。
Raj0B,a#jvox0爱好者博墅j!E;J&T;f\sFo eC
从前有一个很大的帮派,名字叫做Windows,功能很多并且很强大。因为这些各方面的能力由各个专人负责,他们一个人做一件事情。随着人员增多,帮主发现联系起来越来越困了。有一天帮主要找竟然NtOpenProcess来调查一下他的一个手下是不是别的帮派派来的间谍,但是他发现NtOpenProcess跑不见了。
_$l:b.H#J#f%I0
~@ dE.zrT7Q0于是军师就想出了一个好办法来解决这个问题:先建立一个封闭的密室,这个密室只有八袋长老以上的人才能进去。密室中间有一张纸条,上面写着一个地址——温家堡,还有这个地址放着多少人的联系信息等内容。这个密室就是Ntdll.dll,这个纸条就是SYSTEM_DESCRIPTOR_TABLE,上写的地址就是SYSTEM_SERVICE_TABLE,也就是温家堡了。这个温家堡是一个有很多大房间的地方,每个房子有个房间号 爱好者博墅fY._w*CJ:E2N
,房间里面又放着一张纸条,上面写着各个手下的住所。比如说编号为7A的房间,里面放的是NtOpenProcess的家庭住址。 爱好者博墅8w(aq Msj
爱好者博墅~rW#h0z ?8q
这样一来,帮主要找人就容易了。先去密室找到纸条,看看上面写的是温家堡还是白云城,那个地方有多少个人的联系信息等。如果是温家堡就跑到那里去,看看要找谁,找NtOpenProcess就去7A房间。在这个房间里一看,啊,里面写着NtOpenProcess现在就住在密室的旁边……搞定。
7h E$K#e] {0爱好者博墅.evE0T6r0J
这里就有一个新的问题,帮主假设这个里面写的东西都是正确的,没有被人改过。于是就有了别派的间谍发现了,偷偷溜进密室,然后根据纸条的内容,又跑到温家堡。进到7A房间,神不知鬼不觉的把里面记录的NtOpenProcess的地址改成了自己的家。于是,帮主再找人,发现找到对头家里去了。这个就是传说中的SSDT Hook了。 爱好者博墅 \(@R W8i
爱好者博墅QI_X0ttW
攻击者进入ring0之后,找到KeServiceDescriptorTable地址的值,即SYSTEM_SERVICE_TABLE的地址(进入密室,找到纸条写的地址——温家堡)。然后改写SYSTEM_SERVICE_TABLE中一个特定函数的地址为自己定义的函数入口处,截获了系统调用(来到温家堡,改掉7A房间里面写的住所,改成自己家)。一次HOOK就完成了。
&` W9k!NDR_;B a0
Af/Q1P'Z&Gb!X0下面我给一段简单的代码,演示怎么样让一个特定的PID不会被杀死。这段代码基本和《SSDT Hook的妙用-对抗ring0 inline hook》一文一样,我只是注释了一下而已,另外在MyNtOpenProcess处加了个判断是不是某个特定PID的功能。
zD4D+IB|:s0代码:

4lg:{7o"T p0
8T;H%I`V8Z:Nq;v0/*
Mh7l-LqV*B0演示HOOK系统服务调用表中的NtOpenProcess函数,保护需要保护的进程被,防止被杀掉 爱好者博墅6W5{B:g)R%evvI
*/
o)t!B-q#LW1Q6a p"W ?n0
(P%H8?(xaSb0#include<ntddk.h> 爱好者博墅 \0Jw"vA~!p
爱好者博墅.e7Y*M4O@&N
/* 爱好者博墅2xRAe2tV2B{B
KeServiceDescriptorTable仅有ntoskrnel一项,没有包含win32k,而且后面的两个字段都没有使用,所
OK_'| `!f8I0
d%NgXk4je ^?U0以为了简便直接把SystemServiceDescriptorTable定义成SYSTEM_SERVICE_TABLE,免得访问多个结构体的 爱好者博墅R4vQC'lT5u

,B cY `h"O0字段,麻烦。这里明白就行了。 爱好者博墅B'X{1c'?C8@
*/ 爱好者博墅d6i;x O(rY8N?j
typedef struct _SystemServiceDescriptorTable
}\"o&hk r0{
X3J!A y$n6K5\/EW+O(K0    PVOID    ServiceTableBase;
7tY6V[%U:zr0    PULONG    ServiceCounterTableBase; 爱好者博墅$QiNut
    ULONG    NumberOfService;
(_ b!y5^!M&s0    ULONG    ParamTableBase; 爱好者博墅G)I+c M7^)C@\{
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
_WV9f I0爱好者博墅a x/q7K"j$C8XXUn
// KeServiceDescriptorTable为ntoskrnl.exe导出
a6A-a9H$@&s,v4PZ0extern    PSystemServiceDescriptorTable    KeServiceDescriptorTable; 爱好者博墅p.c;?0U*jb

`:U,TZZh8f\)v J0// 定义一下NtOpenProcess的原型,下面如果用汇编调用就不用定义了,但是我想尽量不用汇编
j!Y3~} Z(M0{&d2J0typedef    NTSTATUS    (__stdcall *NTOPENPROCESS)( OUT PHANDLE ProcessHandle, 爱好者博墅,v'K+|1e-sm9F9_
                                                
"{ x3j)`0M O'eH0爱好者博墅Ir)V {:pI:H
IN ACCESS_MASK AccessMask,
Xm(S]W"}-w8xg;IU0                                                
V&x!v^8A ] [?0爱好者博墅 _WB Y$aEZ5H_
IN POBJECT_ATTRIBUTES ObjectAttributes, 爱好者博墅]K:`4T/E3R9CnJ5q&P c
                                                 爱好者博墅`wk`~V)q
爱好者博墅k3C m\'Iu*h!z1l$Ov
IN PCLIENT_ID ClientId
:^2v/iRe$F;U0                                                 爱好者博墅D^ JU6bu#J

6h)P_"zz;a0);
&_{(rjt+kFM0
^c&G(EE:])Z;@0NTOPENPROCESS    RealNtOpenProcess; 爱好者博墅(Z3lm&W P L"N!^Q.r%z/b

sJZ|l!K4~P ]0// 定义函数原型 爱好者博墅l WS(c)Eq3~I
VOID Hook();
W t\1b*m#w0VOID Unhook(); 爱好者博墅YN*R X3\Q3yM
VOID OnUnload(IN PDRIVER_OBJECT DriverObject); 爱好者博墅0f%u7~ qM J5S U
爱好者博墅"C1hSjb)ChK
// 真实的函数地址,我们会在自定义的函数中调用
'oz/{\8lP0ULONG    RealServiceAddress; 爱好者博墅:f+J ge1d4I)Vh

"^ K hW4kP0// 需要被驱动保护的进程ID
8Ty'KQ J9_Er#H+s0HANDLE    MyPID;
-L.HQ?E lY {0
y {8RO {:w0// 自定义的NtOpenProcess函数 爱好者博墅fdBv~w)@i c
NTSTATUS __stdcall MyNtOpenProcess( OUT    PHANDLE ProcessHandle,
X S]7dyuM9Mn0                    IN    ACCESS_MASK DesiredAccess,
hR*`5\L(M Rl0                    IN    POBJECT_ATTRIBUTES ObjectAttributes, 爱好者博墅]gr+L+p-Ys I
                    IN    PCLIENT_ID ClientId )
e(z`Y7u C0{
d5K)T [&k0j1@|Y9My0    NTSTATUS    rc;
u&]G:Lgs8S0    ULONG        PID; 爱好者博墅t AmM7wE7e&qr
    
2HccZ:K-K0    //DbgPrint( "NtOpenProcess() called.\n" );
b)bGGD;X0    
pg2Gby1_9k {0    rc = (NTSTATUS)(NTOPENPROCESS)RealNtOpenProcess( ProcessHandle, DesiredAccess, 爱好者博墅T ?5w{^o~%k\7TEw

N w)|3Z3i0ObjectAttributes, ClientId ); 爱好者博墅s,~] U!D
    
T3_%c(mP8T$JpT0    if( (ClientId != NULL) ) 爱好者博墅8Mbz.cr\
    {
[C9HzYe'pn0        PID = (ULONG)ClientId->UniqueProcess; 爱好者博墅1O E*sJ])Qt
        //DbgPrint( "%d was opened,Handle is %d.\n", PID, (ULONG)ProcessHandle ); 爱好者博墅%?,An]9WMU^p F
        
Y\/K.?-}\GYIb0        // 如果进程PID是1520,直接返回权限不足,并将句柄设置为空 爱好者博墅._"T:Yf7U"JA r
        if( PID == 1520 )
5J N N+G!xh0~QAH0        { 爱好者博墅*NHL!T:t}
            DbgPrint( "Some want to open pid 1520!\n" );
!_*i*QAP?u0            
%uuj/rs0            ProcessHandle = NULL;
H[4~yS5I4lk4]'X0                        
(O:c5gh;u7i KZ}0            rc = STATUS_ACCESS_DENIED;
:Qu!ZMN0        }
BRBeq.d:o@#}0    } 爱好者博墅ab xk-X"R0b W?z-}-w
    
$eL,~4LK0    return rc; 爱好者博墅 w k!E'}E
}
+ql/C CN0爱好者博墅a5tt&K)]_`-ZK
// 驱动入口
Ei |h?!\*a0NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath )
3U}(C[0Se1~|%iQ0{ 爱好者博墅)lC2R n}r,{N1m
    DriverObject->DriverUnload = OnUnload; 爱好者博墅 bR.z{{(b:Tqs1l!{

v2T5`pd+Z1\^0    Hook();
,y$I%H)S1Z&Jx0    
f3N*S O;`RR0    return STATUS_SUCCESS; 爱好者博墅'gla`2t'L
} 爱好者博墅c2xuY9{,V/o
爱好者博墅_U D)Y0I:cU/G#e
// 驱动卸载 爱好者博墅(q%JjM(E J o/eq|1} n
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
6g$} ~o'd6Y E0{ 爱好者博墅Et1N6FlDr7Q'T
    Unhook( );
,r1z HLtrx&J Lk0}
9R%X6hH*Tq2U#b0
2HiSk]0w0//  此处修改SSDT中的NtOpenProcess服务地址 爱好者博墅s/K{ ]5Tg2gm
VOID Hook() 爱好者博墅z ^b J)X4WS
{
`p0y~wJ0    ULONG            Address; 爱好者博墅;Ybe$C.Vji]dR
    
U UC&f(PM8VU/V0    // 0x7A为Winxp+SP2下NtOpenProcess服务ID号 爱好者博墅 Eb5TG{7a
    // Adress是个地址A,这个地址的数据还是一个地址B,这个地址B就是NtOpenProcess的地址了
`Oj#q1u0    // (ULONG)KeServiceDescriptorTable->ServiceTableBase就是温家堡的第一个房间
?v'L I E.eN0    // Address是第7A个房间。 爱好者博墅8Sj/@ igOFC
    Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4; 爱好者博墅xJ)T:tn^W
爱好者博墅(T4]lZ#~
    // 取得地址A的值,也就是NtOpenProcess服务的地址了,保存原来NtOpenProcess的地址以后恢
O%bFK6{0爱好者博墅B6\kD;Mwc
复用 爱好者博墅iwiB)h'L
    RealServiceAddress = *(ULONG*)Address;
$`4skN;I6R~0    
&YKliX0    RealNtOpenProcess = (NTOPENPROCESS)RealServiceAddress; 爱好者博墅G$b| G"}A8`;B~
     爱好者博墅%d"GH_V[@"F
    DbgPrint( "Address of Real NtOpenProcess: 0x%08X\n", RealServiceAddress );
r-B&?G { qB%G0
l9LO0JF:om0    DbgPrint(" Address of MyNtOpenProcess: 0x%08X\n", MyNtOpenProcess ); 爱好者博墅 ^N#x6[KX dt5[i4j
爱好者博墅Vj,HV$}f$G^
    // 去掉内存保护
bI7UJ,Z*@9ep0    __asm 爱好者博墅V+F_'Xe9v-J%x_zP
    { 爱好者博墅GA,im E TS!X9q
        cli 爱好者博墅8d"z$jy9b
        mov    eax, cr0 爱好者博墅4HJx"V C'FaJ8B
        and    eax, not 10000h
/w+?)gv(n0        mov    cr0, eax
#p#s8P'?/L:b)Kx8|g0    }
T/[%F3~D-O}0    
j} KI1Q` wP;O0    // 修改SSDT中NtOpenProcess服务的地址 爱好者博墅]FviZ.MS
   *((ULONG*)Address) = (ULONG)MyNtOpenProcess;
:X2P2D||0
$O Sf aW C0    // 恢复内存保护
g-V2PG*Y8?"c0    __asm 爱好者博墅 ` FS R T8mg*M*rsF
    { 爱好者博墅P}c({V3t7l \
        mov    eax, cr0 爱好者博墅 ` _Z.pq-x#_+Fo
        or    eax, 10000h
8Sz]0f?l ?0        mov    cr0, eax
i)ER4pF@b0        sti 爱好者博墅s7l`yT:{
    }
+CWe#zN Mh0}
p gFHDK.|jH0爱好者博墅)x3ej(o!~T5B+\
////////////////////////////////////////////////////// 爱好者博墅KKd } doc
VOID Unhook()
KT |p9Q9M0{
4X_$}Q W&M]A0   ULONG   Address; 爱好者博墅(N(]2O+f2ON ? ]Wp
   Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
khP:~;}mLd0
!C%LJKP2x/J,@0    __asm
/M-po_ mgc&u0    {
V6{UM'c$P1y}0        cli 爱好者博墅{,P] ?t
        mov    eax, cr0
N {1D Y[ gu+e o0        and    eax, not 10000h
#ed,f.o c z'OA2U.Q-n@ @0        mov    cr0, eax 爱好者博墅s @6W_ ~ C0P|!rh
    } 爱好者博墅he,}d.qMHT
爱好者博墅6s(OC;wt-H'{)x
    // 还原SSDT
&zy Zq`6O4U0    *((ULONG*)Address) = (ULONG)RealServiceAddress; 爱好者博墅$oW d'd8h"`Pe8o
    
T)lZ,P6k0b0    __asm 爱好者博墅H p XU8v-X-_L Z,b
    {
nN!A|6a0        mov    eax, cr0
5G;t1c;[%T1W7n~0        or    eax, 10000h
'iV6nCy O qZ]0        mov    cr0, eax 爱好者博墅w&EudkQgw E0b
        sti 爱好者博墅?hn1K+eM*| t
    } 爱好者博墅RBEJ"[;_

#FUj)IL'Vz`1G2P0    DbgPrint("Unhook"); 爱好者博墅c G$GL1LX,Qk
}

TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

我的栏目

日历

« 2008-09-05  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 103485
  • 日志数: 161
  • 建立时间: 2006-06-03
  • 更新时间: 2008-08-04

RSS订阅

Open Toolbar