数学建模社区-数学中国

标题: 关于Winnt/2k IDT的一些思考(续) [打印本页]

作者: 韩冰    时间: 2004-10-9 14:30
标题: 关于Winnt/2k IDT的一些思考(续)
文章内容:) L) e3 v4 j3 _% Y9 x6 x& x; H
--------------------------------------------------------------------------------
. F9 s* e; Z; C, v! ^" a$ Q作者:suxm < suxm@nsfocus.com >. a5 o6 Y. S2 f. p; H& |1 @* \  W* @
主页:http://www.nsfocus.com
) y7 \3 \# {; m日期:2001-09-10
* {0 N( f+ x: B" I* z0 y. d, A' f# F4 r
<<接上期>>. J9 \. R& p" z" E3 [7 C
- R- M" M( H9 w5 `* ^/ D6 a6 f
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
3 q+ m7 Q* q9 |6 o% P9 g- i; t. J2 _. R& @
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]
+ C7 I+ q2 c4 Z8 x, U2 U4 t& Z呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。
" c6 u6 F% @3 c7 V" [( a! Z; f下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
" u0 L6 S" W- |' @% b% H8 w8 T; X在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
/ F7 T  N. i$ Z% Y" Q4 F( _通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。5 ]$ V$ [6 C  B0 N# U4 I* e" |9 Z
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]5 G' m" K1 i1 }) n( f* t. l
[ suxm: 去看看上一期月刊的图1 ]* `( u& [& r# w% i
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。% ]) A$ L: z0 f( C5 [9 ]+ k9 ~

( Z2 D% a; x! h. S( b& l/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 *// {: q& c2 Q7 {2 O: F
#define HOOKINT 0x381 D; }' z" v  @' P* @0 l9 x: ^
NTSTATUS DriverSpecificInitialization( )2 {* ~3 W6 d" L0 u6 {' s- u/ J
{
1 a$ Q  R; j9 f4 g' OPIdtEntry_t IdtEntry;
& l) G: Q8 _/ Q* P& [& `2 Cextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;9 z; U, C3 t: f$ Y0 F' B& D& |
# ]0 Q3 N, [( L
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;9 N' m% t( \* B- {3 s+ @
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
/ W% H" a9 I9 e8 KServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
5 ~- J5 B5 X, N& h7 h9 q3 \/ z8 o; l8 m  q" m
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
4 p- q: I* F" O6 Eif (!ServiceCounterTable)# ]5 w6 J* o; c. @- Q& Y7 y8 m
return STATUS_INSUFFICIENT_RESOURCES;
1 W; C; L- L2 ^, F( @" y0 ?
7 ]: s. |. t/ dmemset(ServiceCounterTable, 0, ServiceCounterTableSize);. Z( R* P7 e* J- v; R; H
*ServiceCounterTable=NumberOfServices;  r: G6 b) Y( S
; T' a: k6 f  Q- G" X  [2 k
/* 获得IDTR Register 的Base Address和Limit*/. K( r0 n) ~* T5 U$ E6 O/ S
_asm sidt buffer% [$ j/ {% k* M
IdtEntry=(PIdtEntry_t)Idtr->Base;
+ ~" a" k9 ]8 H* s! g% }( [1 }# M3 X* Z3 J7 g) p
/* HOOKINT就是我们想要hook的中断号
( V6 |; j2 z8 q4 Z) o8 m+ F! I这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
" y* D, o) d  M& VOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|, P+ [6 X, t* h9 |* w2 ^
(IdtEntry[HOOKINT].OffsetLow);# C+ \6 g  q0 g. {
) r/ O' `7 I% w4 p
/* 在IDT表项的相应位置写入新的中断处理函数的地址
& v" z, c1 g8 _' J" P9 L用新的中断处理函数替换原中断处理函数 */( P8 h- m8 m: V( P/ G
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */2 z) h9 G$ X( f, [" ^$ t" ~
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
, U" Z3 ?, F7 [) z) C9 dIdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);9 @$ j7 c. f7 M7 l# m3 H
_asm sti /* Enable中断请求 */1 f4 K# ?! S$ J, a

1 P" I( l  h; n! f; }+ L7 x' |2 B4 p8 Qreturn STATUS_SUCCESS;
% {* `1 M# H. U4 _2 B2 D}5 z" J! m! q: n+ H
3 f2 W4 `) h9 I5 ~
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
% R# t. c! l4 Xtypedef struct InterruptGate" z& p# S' G' }0 g, f2 G
{" G0 H4 ?, K7 ^, @' P
unsigned short OffsetLow;
6 W3 `- V7 ]# N: Gunsigned short Selector;
- j- n7 j5 \. q/ }4 G: a7 |, ^unsigned char Reserved;2 o1 J9 t4 X& l' @2 U0 A
unsigned char SegmentType;+ m1 K3 [2 p3 \4 }* V
unsigned char SystemSegmentFlag;
. t, b  \5 ~" b+ |2 Runsigned char Dpl;
& ~4 K7 K( T2 t. Funsigned char Present;6 v4 \9 z6 L, F
unsigned short OffsetHigh;
8 d8 ^5 T3 c) H} InterruptGate_t;
' I* x+ T  U. G. K- J5 X6 h' X7 t: v6 v8 U
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
8 Y2 F: T) |. t( R6 b! f" y' F: ~* R1 K, R
[ 读者小强:Winnt和2K的IDT中有空表项吗?]
* }0 c8 K# @/ D% u; t/ H是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。# N4 a, ^2 b  |3 o
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
: a  Y* O/ @. C' J8 ?NTSTATUS AddInterrupt()
( ~% K+ w' Y; x{: X+ E/ C) _  G+ s$ J6 Q9 {* t/ j0 \
PIdtEntry_t IdtEntry;
. T: Q" u, D5 M; l2 m1 |0 h2 q/ M: [8 j) r
/* 获得IDTR Register 的Base Address和Limit*/0 Z* M9 ?7 K4 y
_asm sidt buffer
6 o1 P5 L# M% m" Q/ i- V) |5 @IdtEntry=(PIdtEntry_t)Idtr->Base;
& p$ t7 Y9 ^8 V; \) F; [2 Bif((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))9 R4 b' D' J) E. f$ k9 W1 n: h9 _
return STATUS_UNSUCCESSFUL;* C3 H: J* U4 T
5 v4 P: A+ g9 J$ J" D4 `# s3 M
_asm cli
8 d0 U. T: |0 f  p8 ?$ _
$ n6 W8 Q4 e7 `: h9 C& ?/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */  v( l! I3 w* T5 h
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
2 P" ]# v0 B& ?3 a8 VIdtEntry[ADDINT].Selector=8;
; w8 }8 v7 R' `2 W3 V' _. ]1 B$ ^IdtEntry[ADDINT].Reserved=0;
' H1 p+ t* X+ t: hIdtEntry[ADDINT].Type=0xE;
, p8 X6 r( U+ \( F- uIdtEntry[ADDINT].Always0=0;; z4 P. F. E8 _
IdtEntry[ADDINT].Dpl=3;
+ C4 O/ G3 s- S9 FIdtEntry[ADDINT].Present=1;4 L0 L& R4 `: [" h
IdtEntry[ADDINT].OffsetHigh=, U1 X# C5 ?* h) b
(unsigned short)((unsigned int) InterruptHandler>16);3 J6 c: e" E( v% e
_asm sti
  {7 f1 [1 t8 H( ?5 T$ e0 a+ ?# j6 F- l  I: E# q! w' ~
return STATUS_SUCCESS;
  Z" s5 W2 }3 u- l}& M, {7 k5 ^; i/ x9 t" D2 K+ a% k

4 }0 t, C/ e& z+ A  R, r8 C" ^让我们就上面的代码提一些问题。
# `4 Q  e" m7 j  O1 \: f- H- V6 Y7 ^Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?" c' h( ?- R: v# ^  b0 j0 M
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
, k' M: r0 x" h1 L& O个段的名称就是8(CS=8)。
& W9 |) T& e2 n4 K0 B' [: B8 |) ^' u4 f9 d) Z
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?  q8 n5 H! M  c. B. [- Q  ^
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为" [, G$ |2 G  }$ I( ?: p  p
应用程序的DPL=3,所以,可以直接触发这个中断。+ \+ r2 L% D* c# X2 G# o. v
* {( e7 u. e& n/ J, {! k5 p; G# x# f
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?# {. Y8 _0 l" p4 J. w5 Y2 X
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下5 M7 ~5 k; r6 F4 ]
:idt
& [" n! U+ z- H6 z0 @+ B" ~Int Type Sel:Offset Attributes Symbol/Owner
: H" I% z. W) a  ?6 ?IDTbase=80036400 Limit=07FF
/ ?, |4 I7 D9 F# q( t$ T0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
! a+ x3 s3 W/ `* J# O0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
7 U/ \7 C, b& L9 U- H  J……
1 ^/ _! A; \7 P可见,0xE表示IntG32。  j% K  V( G: j% L

: s/ f5 S, B) n5 j终于快到下课的时间了,让我再多说几句好吗?7 [: s' ?7 k5 J! C$ K' ^8 l4 @
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5