数学建模社区-数学中国

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

作者: 韩冰    时间: 2004-10-9 14:30
标题: 关于Winnt/2k IDT的一些思考(续)
文章内容:
* @; D+ O8 Z$ v/ J6 E8 v--------------------------------------------------------------------------------
9 K2 K$ x, G# f. G作者:suxm < suxm@nsfocus.com >2 E& i' D0 l' X0 |) \
主页:http://www.nsfocus.com
$ U+ C6 l; H' ]6 @! n日期:2001-09-10
+ K  ^& w# K! E4 J* F3 p) j8 k
9 ^4 L" b8 R9 N$ |  g$ r<<接上期>>
- W) n' G( r+ o6 }4 y  V$ j
' o" m! U' L8 L6 m9 K9 UIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。& |7 p2 J4 T4 {/ E* b

- @" d0 v# }2 i7 p5 f[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]( r) L" O* I' j9 ^: q# [+ [& V" K
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。2 s6 V8 V7 q7 U* E3 d
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。, N& k! P& l" z( d9 J% a1 L
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。8 h5 \5 Z$ ?9 V; L9 a
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。
& g1 U0 m/ {- M3 P3 M9 U[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]1 R9 Z# o$ U: f$ A/ U9 k
[ suxm: 去看看上一期月刊的图1 ]
* U5 W8 n8 i1 U6 X+ l# M: Y' D8 m下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
3 y! Q6 }( d- O, ~: q
, c# u4 ]5 F3 L+ F5 E6 X/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
. O$ w  t( U9 H. l: B3 K$ s. f#define HOOKINT 0x389 k: r/ |# C# M' K* V( I% p
NTSTATUS DriverSpecificInitialization( )* W; E$ L( Z' _
{
- x3 N, p  _8 B7 b1 \- ?, k; }PIdtEntry_t IdtEntry;! k$ [$ a5 f7 ~. z# n! D, t; I4 j
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;. E; J( q* M( s+ e: C4 X+ {

, I+ \  J. q  ]6 J3 l# q2 UNumberOfServices = KeServiceDescriptorTable->NumberOfServices;& T; m( Q0 F( e* v! n/ O  E* v
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);* E% N: v0 p+ V, b! Q3 r+ A( z
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
8 A4 P7 d9 _" v9 M3 y7 l, H$ Y0 R) V! G% ?. n" k; I
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */2 I9 r6 Y6 [: c0 u7 ~" k
if (!ServiceCounterTable)! `0 |! _/ V5 S8 c
return STATUS_INSUFFICIENT_RESOURCES;" W2 n% N% C& k# @: E5 ~/ v

6 n: v7 ^3 r+ pmemset(ServiceCounterTable, 0, ServiceCounterTableSize);
! e0 _7 {0 A; t*ServiceCounterTable=NumberOfServices;
% i8 @/ ?9 L6 N' @% E9 e) ^& K$ h5 g7 h, A" \9 K# n: n
/* 获得IDTR Register 的Base Address和Limit*/6 L; w! p# x! i: r+ D9 E; e! J# E
_asm sidt buffer
$ f. k/ I$ W! ?2 M% w$ FIdtEntry=(PIdtEntry_t)Idtr->Base;9 \- o$ S+ k7 u$ O; A0 S' x
* x# X: M' [1 U" A1 k) l
/* HOOKINT就是我们想要hook的中断号
8 x) b  ^1 e- m6 e这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/: a1 o: o0 K4 q! d# Z2 Q
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
* N7 J( a- z# c* _: ?4 L/ W% C" I(IdtEntry[HOOKINT].OffsetLow);3 ?+ c! m$ H5 S' }- b: u

" r( D2 d4 f0 G6 `' _/* 在IDT表项的相应位置写入新的中断处理函数的地址
! ~/ j8 x: G* c" Y用新的中断处理函数替换原中断处理函数 */1 |$ y; X1 F! q$ Z
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */4 E* S2 l* p  Q9 G
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
% c5 N% \8 F8 p3 \/ V& f. @* QIdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);5 S+ \; Q# q  n) p' C4 f
_asm sti /* Enable中断请求 */4 ^4 v4 E& |" [1 d! i3 D( D
/ C% O7 N  B9 h/ z$ X" w
return STATUS_SUCCESS;* U' P& l# E$ v8 j/ e5 C0 F7 S
}/ [9 `' G. S  X& H' s
8 h% {( ]- W% A
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
# c9 Q5 b; `7 W: F5 Ztypedef struct InterruptGate# |# f& V; [0 ?) p* r5 U8 C! |
{
3 p) h5 S1 H! K; Eunsigned short OffsetLow; 6 V( Z. Q4 B3 X4 k$ t
unsigned short Selector;
4 U% S0 ]2 G7 x" d. z: Ounsigned char Reserved;1 ?# U+ Z3 ~0 g1 L1 s/ B+ [( y  e
unsigned char SegmentType;0 b  A8 T6 C; ~: D/ c; j
unsigned char SystemSegmentFlag;
7 Y) v1 Z$ n% `7 o4 eunsigned char Dpl;& K- }5 ^3 U3 o3 [! `( R
unsigned char Present;- p$ J3 O* @) j" o3 q. u
unsigned short OffsetHigh;
( l2 f( B7 p  ?5 q* x6 M; ~} InterruptGate_t;' }$ j& h0 {5 e. F

2 Y1 K$ n2 h% O: g$ w) S9 W在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?6 O( j# l2 `+ ?* }! P& N/ X
; ?% p9 h3 v% d+ `7 H( r* L1 a. O) N
[ 读者小强:Winnt和2K的IDT中有空表项吗?]
0 t7 {& m; Q+ O9 j9 k% ?" D是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。$ o* i3 d& W: z) F7 S
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
( g' r2 M3 q4 ?* o0 y5 Z( Q$ PNTSTATUS AddInterrupt(), E$ p9 F& _7 q: Z6 y
{' v! I& A# b0 A0 u# X! z6 ~
PIdtEntry_t IdtEntry;
3 y. Y. `; Y6 ]' w4 J$ |" I  z# |
/* 获得IDTR Register 的Base Address和Limit*/
! u( X& ^0 G  i3 z_asm sidt buffer
4 x+ y1 |1 M$ @9 p8 _IdtEntry=(PIdtEntry_t)Idtr->Base;
* C( `! Z$ }4 Z' ?if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
4 s0 C- M4 D& \- Z/ qreturn STATUS_UNSUCCESSFUL;; |8 z! f2 T9 B+ \5 Z
' j1 v* j8 ~: s. f
_asm cli
6 E2 r* h; c& m% @0 W( Y. t) D. _& M2 L" e0 v
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
( B, H" S2 d6 m5 A  i, {IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
; s" Q* _3 A3 k; \0 eIdtEntry[ADDINT].Selector=8;
$ L1 f2 @' D4 _IdtEntry[ADDINT].Reserved=0;
* X$ m: k# Q/ m/ T3 X# }) KIdtEntry[ADDINT].Type=0xE;
' c4 |6 T; t4 ]0 [' C0 O& r- o) CIdtEntry[ADDINT].Always0=0;
8 L# W8 l( a! F* G7 \% DIdtEntry[ADDINT].Dpl=3;
+ l, R( [( U4 |# Q! C0 nIdtEntry[ADDINT].Present=1;) c' O0 C( |' f- i7 y, e( r  c4 Q
IdtEntry[ADDINT].OffsetHigh=/ K/ L, L2 s' C/ X
(unsigned short)((unsigned int) InterruptHandler>16);
9 n) n2 e% t: l8 b) i, p_asm sti+ ?6 @" c' D9 v7 L' Q2 g
) }" H+ `) n! P0 g$ D/ i
return STATUS_SUCCESS;
9 p& D( F* W3 I+ w' [}- O: O! r- G: R

2 M$ c( z6 r, p3 k$ _让我们就上面的代码提一些问题。
" l& P- j0 p0 }4 JQ: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?; A7 {0 E- m* p1 ]+ {
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这( p  a' l- x& B
个段的名称就是8(CS=8)。
! m0 o- ^' v' N! h
7 }& T3 W8 f6 D4 }Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?3 ]9 F( w0 ~1 j/ |6 L
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
. i* t/ F% e7 g' S0 f" O应用程序的DPL=3,所以,可以直接触发这个中断。" E6 k! L% M/ i+ G- {4 s
% W6 p# c! T1 E- R8 @7 M2 F
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?6 t( Y( s+ H" X" P
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下3 {2 U* k! i1 a' E
:idt
$ ?" x0 f, O$ Z! b, E5 ~: U4 WInt Type Sel:Offset Attributes Symbol/Owner5 t" F# N/ m* w3 r7 I
IDTbase=80036400 Limit=07FF/ f1 M' Z4 j. H$ _' _& [3 D
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
6 L0 T7 W3 B" t  l% p: z0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0( _9 _1 s, s8 i' F9 A
……
. D# k  b: Z& T1 @% K3 Q# K1 M可见,0xE表示IntG32。
; `: Z9 u. y' z) }6 ^9 t% `. s9 P) ]3 w2 b# I
终于快到下课的时间了,让我再多说几句好吗?+ q9 G6 b3 S' H; Q) Q7 ?6 X
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。




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