数学建模社区-数学中国

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

作者: 韩冰    时间: 2004-10-9 14:30
标题: 关于Winnt/2k IDT的一些思考(续)
文章内容:
9 q( M. v' p" K; X. X--------------------------------------------------------------------------------
# @7 C' a  r7 I  w/ W+ K# h作者:suxm < suxm@nsfocus.com >" A3 F. z$ j) X: x# E
主页:http://www.nsfocus.com5 t4 a& }0 ], G
日期:2001-09-10
( J$ l1 u1 g3 k( C8 j! O
! r" b4 y9 Y( N/ E<<接上期>>0 |4 O0 p  H, L3 h# r! C% p
, j4 E+ ]& n5 n9 j+ y0 ?
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
/ H" [0 c+ A0 _: ?3 X" H0 @2 Q. O0 O1 \. g/ t3 Y' H$ L
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]1 \" k- R( s, [/ @: d: m7 _
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。& l4 W% Z8 x" v: u. F& Y$ b; @2 v
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。% h' n, g8 r* |% N. ~* [! Y& j1 a& _
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。/ b9 O: Y) S6 w
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。/ L  `) |! ?4 k, L" ^9 Q
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]' ^3 W1 f# Q. v$ P% P4 s7 ^6 [0 q
[ suxm: 去看看上一期月刊的图1 ]
) j. G# {  h. W# u- _7 ~1 F下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
; K. B) }: @5 Q$ H# {6 B$ H( F  B
/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */% A& W/ w% s, f  k4 n3 X+ G% b
#define HOOKINT 0x38; j  r* g( g7 R7 }3 l3 H( g
NTSTATUS DriverSpecificInitialization( )7 S1 E: L$ C) |
{3 g) C$ J# ?! a+ h4 K
PIdtEntry_t IdtEntry;* {8 x8 u, c6 U% U
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
8 C2 k& P! G5 E8 k: n
' `8 c9 W$ z) s/ `" F+ ^7 INumberOfServices = KeServiceDescriptorTable->NumberOfServices;' ~6 u8 k- Y2 W; p
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);4 I9 U* y# `5 X' f; J$ z0 y$ B
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);* S; S- I. F9 v: l5 b% U1 ~

: h2 i6 i  ?6 |% {/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
. R. i2 i) w9 s8 H; x7 iif (!ServiceCounterTable)
, y: [8 G! q0 q. c( rreturn STATUS_INSUFFICIENT_RESOURCES;: a* Y: n+ H, N. \6 l$ s1 H8 D5 e
: R- E! l* i0 i, ^9 y
memset(ServiceCounterTable, 0, ServiceCounterTableSize);
9 |/ S: c$ u- d5 n8 X0 R9 W  P; [*ServiceCounterTable=NumberOfServices;" L6 s+ [) _7 E3 h7 c7 r/ C

$ t$ A9 v* I4 T+ w+ D8 w/* 获得IDTR Register 的Base Address和Limit*/
4 C7 |! j9 `5 w0 |_asm sidt buffer
# R3 g  U7 t7 d( J2 T. }, AIdtEntry=(PIdtEntry_t)Idtr->Base;
# L7 O8 b+ C6 \7 i6 x
3 u# E$ j/ r: x- h, y1 N. p/* HOOKINT就是我们想要hook的中断号
7 n; A7 n: N; L: k这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/3 N& ~( y+ c! b; t; a
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
) C1 a2 {  `. c4 X, }(IdtEntry[HOOKINT].OffsetLow);' D' n$ T& v3 q5 z; u

4 W6 ?/ @# ~' H% T9 W/* 在IDT表项的相应位置写入新的中断处理函数的地址
$ P- r" V# f( w8 A/ R用新的中断处理函数替换原中断处理函数 */' W9 k6 x# {  O1 P$ X
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
& f; |( q- g! P- NIdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
- l% ?2 T; K' Q: K$ a- v0 k, qIdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);9 Z) m. v7 n0 y. b4 j/ U% g
_asm sti /* Enable中断请求 */  T, L- w- u; z% H

$ }$ X( ]4 x5 J! ureturn STATUS_SUCCESS;$ t* v, l/ J6 C  q! q
}9 d3 p# Y( i: F& A5 r
3 F& |" O+ q1 e
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
, z' j& @  |) C* I$ @  X& p1 F5 vtypedef struct InterruptGate
/ S. C! N" [4 x" C1 i/ F4 h{
1 G" h+ c, U: P4 `! t" funsigned short OffsetLow; ! ~5 i! Z4 N( j0 r$ f
unsigned short Selector;4 G* X( F. \* x9 [* T& y) o/ Q- ~
unsigned char Reserved;8 x% x9 B# c4 h
unsigned char SegmentType;2 a: r' c3 I' C3 \' ?) k) S
unsigned char SystemSegmentFlag;7 t+ n* U5 a, P/ ^6 [6 y( o
unsigned char Dpl;
5 k1 N, g0 a0 E/ Y. T$ z$ F! Eunsigned char Present;
( ~5 X: T& N5 d) C! M  P/ P& Kunsigned short OffsetHigh;
$ P- i8 R  I' ?6 H} InterruptGate_t;
! {( l: `* X; v2 K  X* V4 r
6 O6 t3 G: z9 A6 n+ {6 s在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
+ C' i; O5 @/ M/ k- ]( t! [: E! g3 G' L" W* M9 L% O
[ 读者小强:Winnt和2K的IDT中有空表项吗?]. m+ f: ~6 G( Q. w8 C
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
& Y, f) Z' i& E0 i3 b) r% J0 Y下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
% ~. J9 W, h9 A3 C$ a; P$ T; jNTSTATUS AddInterrupt()
7 D- R; q* L; j" T6 o{& Z  @6 U$ ]. d$ H- V; L" k
PIdtEntry_t IdtEntry;
$ N  w6 b! G. n/ r5 j& T: s3 ~4 A% O2 X9 I
/* 获得IDTR Register 的Base Address和Limit*/
+ w  L9 \" m& F_asm sidt buffer5 @/ @0 G7 j/ m$ T/ \
IdtEntry=(PIdtEntry_t)Idtr->Base;: V1 a; F0 n! Z
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0)). u1 I5 c' [" F9 e
return STATUS_UNSUCCESSFUL;
, P) k9 |5 k$ c# L3 i$ j- u3 S% n: Q, H6 N" k
_asm cli+ i3 G! V: I6 N
  h, X, h& V' ]
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */; X5 c* M6 l1 |! n* @
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
: `( g+ u( z8 Q" P3 h! BIdtEntry[ADDINT].Selector=8;4 T4 N* X7 s" Z$ g: H. C  @
IdtEntry[ADDINT].Reserved=0;8 w: v  b! R1 [
IdtEntry[ADDINT].Type=0xE;
. m6 E. g7 L- z" }IdtEntry[ADDINT].Always0=0;. E) y( w1 K% k5 O. p
IdtEntry[ADDINT].Dpl=3;
& j5 o0 q) F" t7 W% QIdtEntry[ADDINT].Present=1;
. [, b3 W, G0 K% \- l- }9 Z+ _IdtEntry[ADDINT].OffsetHigh=
  e1 ]0 _0 r0 O5 \. V(unsigned short)((unsigned int) InterruptHandler>16);! q7 Z# d, ]  n9 N( \' {2 b4 L
_asm sti' i9 M5 B# Y1 h5 n' y4 t7 X

7 n/ ~+ N% r% ~9 e) b7 [+ [* Xreturn STATUS_SUCCESS;
% x; n& Z- {7 k9 ~}
& d  L# r* P, z4 n1 @6 n
6 B; S$ s% z$ A让我们就上面的代码提一些问题。
8 X) M6 S* P( @7 W- gQ: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
; }6 g2 m0 V2 K9 j1 ]* rA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
7 V1 g' @' |! c; x8 {3 {: h个段的名称就是8(CS=8)。* \6 p7 C% W$ I$ U+ G

+ K9 w0 s6 ^5 B2 o  W* b2 NQ: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?3 {9 W. ~7 P3 N" }; l
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为! z5 w# Y4 S: d6 |4 p3 x
应用程序的DPL=3,所以,可以直接触发这个中断。; y% k9 X7 f/ i; Z
. C+ ?2 _: D& L7 a
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
- o" ~/ t% ?% k6 M/ V% ^A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下  F0 j) q6 ?* `' {/ O
:idt
1 r6 Z. ?8 X! B, H9 eInt Type Sel:Offset Attributes Symbol/Owner: R" w; S/ v* c; M/ X
IDTbase=80036400 Limit=07FF
1 c% t- ?2 t2 T0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590( i  e' `9 t3 }, B- Y$ ^
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0# y& g5 V; A! @  D# i2 t
……0 U% S4 x, K- s- f+ O5 |
可见,0xE表示IntG32。
. a; m# _- Y# f$ x! J: C4 t3 T; ^  R+ H" W5 ]
终于快到下课的时间了,让我再多说几句好吗?
$ J/ k4 V6 o/ Q' v0 `$ Q& m4 t通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。




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