数学建模社区-数学中国
标题:
关于Winnt/2k IDT的一些思考(续)
[打印本页]
作者:
韩冰
时间:
2004-10-9 14:30
标题:
关于Winnt/2k IDT的一些思考(续)
文章内容:
+ r1 F ~$ w/ H% w$ ?6 v
--------------------------------------------------------------------------------
2 ^9 Q# p" q) i5 u
作者:suxm < suxm@nsfocus.com >
/ m# V. s6 |. x! U% v
主页:http://www.nsfocus.com
# s q! ^6 W! l
日期:2001-09-10
. K9 p' o& y& o
. Y# b9 @* B6 v: ~6 q$ X
<<接上期>>
4 k0 W2 F. |6 e3 l. o! F9 Z& `& M9 V
" O% {) ?4 p+ m$ r# o( O- c
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
# ?, `/ L' Q% {
) C" I4 Y: [9 \; F# z: p
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]
" @9 Z1 v! H+ M4 m# \9 G2 r2 k
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。
4 D v8 b6 _2 c7 c G
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
# b x7 n6 I9 a+ w3 o3 w5 z8 n
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
/ L9 t: F& g* h* I* ?
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。
* Z8 d# s, S0 t$ U* t! V
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
6 q7 i0 {- Y+ M4 A8 F$ X
[ suxm: 去看看上一期月刊的图1 ]
/ f! ?" \; u$ d( J
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
: S3 B" b" L' S9 r2 P) A& e! H' U8 W
) s- g! m" Y# D- p5 u
/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
/ H# e% }" N" d0 X$ m
#define HOOKINT 0x38
* S' u* J% K( z; q, ~- ~
NTSTATUS DriverSpecificInitialization( )
4 ^0 |2 U3 O3 z( q6 W
{
2 `# A; K2 } G0 [! z
PIdtEntry_t IdtEntry;
/ V- b' u( d' j5 s- [
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
- e' K& t0 x) w* K# e6 _
- T; V8 F3 n) V% K9 J
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
3 x, K1 H8 e1 t9 X* S/ O" M
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
) S( @ Q! H4 N; w) V9 c
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
3 V! O# A/ k8 S4 p8 u- \0 n
$ A- q9 q/ N* ~6 b5 n. |
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
) H' j& g) J. r" ^; E! I5 [
if (!ServiceCounterTable)
0 V; r6 }' \* l ~2 R
return STATUS_INSUFFICIENT_RESOURCES;
& G) Y b# R5 g( w
/ C, G3 g! I' k4 @' r2 H
memset(ServiceCounterTable, 0, ServiceCounterTableSize);
% W3 U. ?- L' F; X
*ServiceCounterTable=NumberOfServices;
/ H5 K: j/ D8 A8 k8 C0 w; ~
& M, ^% W+ N( ^' r8 D( @
/* 获得IDTR Register 的Base Address和Limit*/
- J2 X* Y3 C% E1 `# |* {9 P, O
_asm sidt buffer
% b' f, ^9 J8 n- v' z
IdtEntry=(PIdtEntry_t)Idtr->Base;
7 Q9 H7 g7 A+ ~; I4 C( P
- H# k6 v- }- @$ K! }" H
/* HOOKINT就是我们想要hook的中断号
3 C# [" o: H, l* i0 ^/ g9 k2 k- u0 t" E
这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
) j$ i# ]! Q# w5 A1 a2 F* n0 c
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
0 U8 C$ l# h' _! a. _& q1 c
(IdtEntry[HOOKINT].OffsetLow);
0 k% `, h) p7 }' `; p X" r3 L
* d% s) \% P+ X
/* 在IDT表项的相应位置写入新的中断处理函数的地址
* U/ C4 O9 }) E8 x7 W" F
用新的中断处理函数替换原中断处理函数 */
7 Q1 i, ~# ^% l9 i8 ?2 J) r
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
) S+ P; [# A. |" D% {5 ~; C1 l' ]
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
: s6 f6 C( p: j2 X( l+ }
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
7 h1 d6 {& ~6 S# Z, \# Q
_asm sti /* Enable中断请求 */
7 {" c$ z2 s7 y! J- J
; Y4 K3 ~7 L( h4 q' A5 S, l
return STATUS_SUCCESS;
( U6 I- w! z+ n: U. {
}
- a4 m$ N* ^" F7 P& s
8 L! z5 U3 b5 X2 w8 @, E3 F
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
+ m( x( v; m* x* D! t! ]
typedef struct InterruptGate
& D2 D+ {5 p" t
{
; B! T0 M2 R- b, L* x
unsigned short OffsetLow;
, C9 z/ ]) b9 S2 A, U
unsigned short Selector;
; z. t4 u# @8 N
unsigned char Reserved;
( w' V+ M7 c1 Z& P& O' F: G
unsigned char SegmentType;
: _# Z) F' U b" }
unsigned char SystemSegmentFlag;
2 [; U" Q. s! b6 z9 `, w
unsigned char Dpl;
. t- o$ ]& {* @: q
unsigned char Present;
! l8 R) f' r1 j1 V
unsigned short OffsetHigh;
- ]! Q' E4 h1 G. z3 c, ]
} InterruptGate_t;
- }3 \: F& x2 v! |* I& K" w" }
/ v9 k* B& @ Z+ d: A
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
' J4 r) O3 ] }) W+ x0 ]
& I# G/ c/ K5 D6 J
[ 读者小强:Winnt和2K的IDT中有空表项吗?]
, L" j. o* j: ]0 o& b
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
# O, ~8 ^9 i$ d- g2 a) M9 p' f
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
3 T7 G4 o9 p( U2 F2 a" m
NTSTATUS AddInterrupt()
/ D! q' g& o2 P+ A) }/ N
{
- w2 ]1 n% L: [" {7 f1 Y: n G
PIdtEntry_t IdtEntry;
# t: Q, b9 R. {( I( E" r
$ p0 L1 k$ B6 P& }6 e+ l# c- ^
/* 获得IDTR Register 的Base Address和Limit*/
8 Y3 X0 |7 `$ r& g% q3 C- F- B
_asm sidt buffer
$ K/ x0 n) o, V5 Z2 i* _ J
IdtEntry=(PIdtEntry_t)Idtr->Base;
) q* N/ D/ F9 K' e. O8 ?, G) u
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
/ {4 d! _9 u& Y: J
return STATUS_UNSUCCESSFUL;
8 r" }; u( a2 s9 w" @
( ~& T s* n1 t5 P% C* Q
_asm cli
4 v& v9 f# C, \) E5 Z5 A8 T
2 q9 q; R- k" Q1 O% t. n% C" D
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
7 e# N5 U% X3 V0 y# P) e
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
& M6 o4 u9 g* b- |- N
IdtEntry[ADDINT].Selector=8;
7 @9 O5 ^: g' E: x" d& g" x
IdtEntry[ADDINT].Reserved=0;
" w# o& x2 @; r# ~4 ^# n
IdtEntry[ADDINT].Type=0xE;
1 k) O! j4 S" P F! V8 K5 [# J3 W2 a
IdtEntry[ADDINT].Always0=0;
2 Q, \, @# i% J9 `; }9 c
IdtEntry[ADDINT].Dpl=3;
+ T3 g8 R7 E4 x0 [% o$ \! V
IdtEntry[ADDINT].Present=1;
$ ~7 @. J) j6 h* Q
IdtEntry[ADDINT].OffsetHigh=
; }5 A* b. u) K. t& z8 N! L/ E- {
(unsigned short)((unsigned int) InterruptHandler>16);
: C. J# t1 ]9 B/ ]) Y
_asm sti
$ @. \- t, e1 r
0 j8 U: K- h* A7 U! k0 P3 W& g5 v
return STATUS_SUCCESS;
# v x# e! [) |5 L4 q% W
}
/ ^% N8 `6 Z* C6 r4 X
) h$ Z) A, n0 ^* ]/ e* z) Q/ t: j
让我们就上面的代码提一些问题。
6 f1 O: Z3 I' v- f1 O2 V5 S {, c
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
1 p2 A: q# L* E& C, o
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
# x o( v8 U! }5 `) H. B
个段的名称就是8(CS=8)。
: |* R# w/ ]( ^. B' y: ~
8 W! [3 x& r! m, v9 k, ^
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
, r N' `# h; a
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
) i2 B& |( z3 @. J, x4 w3 o! p. _
应用程序的DPL=3,所以,可以直接触发这个中断。
' ` j- h" Y6 s8 v# ?% M: x* H
+ P- t% \+ T/ w; c
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
+ B$ Q& E# N; R x/ ?1 n4 V* T
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
/ }2 j- M! ^# D3 v7 M
:idt
! x% m F" @. E: _( \
Int Type Sel:Offset Attributes Symbol/Owner
, Z( t" ~# Z7 ~1 j1 |% h( V
IDTbase=80036400 Limit=07FF
3 i, e' W8 f5 r# u& j( v
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
! `) h: U% m# M. ]. V# B
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
2 v. z% P* x9 ?# M5 i( m0 C
……
( V, e2 U. R/ T* ^& A2 U
可见,0xE表示IntG32。
; i8 O$ K9 `9 R5 r
& A4 [" X7 l: y9 h# l3 D1 N) M- @
终于快到下课的时间了,让我再多说几句好吗?
, ?5 R D3 M% ?
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5