数学建模社区-数学中国
标题:
关于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.com
5 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 I
NumberOfServices = 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 i
if (!ServiceCounterTable)
, y: [8 G! q0 q. c( r
return 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. }, A
IdtEntry=(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- N
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
- l% ?2 T; K' Q: K$ a- v0 k, q
IdtEntry[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! u
return 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 v
typedef struct InterruptGate
/ S. C! N" [4 x" C1 i/ F4 h
{
1 G" h+ c, U: P4 `! t" f
unsigned 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! E
unsigned char Present;
( ~5 X: T& N5 d) C! M P/ P& K
unsigned 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; j
NTSTATUS 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 buffer
5 @/ @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! B
IdtEntry[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% Q
IdtEntry[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 [+ [* X
return 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- g
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
; }6 g2 m0 V2 K9 j1 ]* r
A:按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 N
Q: 上面的代码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 e
Int Type Sel:Offset Attributes Symbol/Owner
: R" w; S/ v* c; M/ X
IDTbase=80036400 Limit=07FF
1 c% t- ?2 t2 T
0000 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