QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2429|回复: 0
打印 上一主题 下一主题

关于Winnt/2k IDT的一些思考(续)

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:
" n( n1 L* e5 t( X. h& O--------------------------------------------------------------------------------
7 d: F: o% T0 ^% A) L: ^4 z作者:suxm < suxm@nsfocus.com >$ M! q: A" k+ ?$ }0 [
主页:http://www.nsfocus.com
7 ^$ B( e5 U% x6 F8 C日期:2001-09-104 o6 b, @4 a/ O9 G2 n

( Z) B" j. y$ z7 q<<接上期>>
. n' y5 Q- o9 |( E
4 g! z8 M5 f& l( Z6 VIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。; S5 e9 F8 R( v2 H

4 p+ a! I, `% W5 N) v2 F. ]; @% n; u[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]. `# k5 u" d" D, a. G
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。
! p  ?: t+ P0 u+ n+ B( z下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
( U' Z2 `5 J' v$ c1 s/ Z. E5 x在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。6 [+ J4 p1 ~9 u7 @
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。
6 n3 ^, m. V* T[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
1 t" e; E/ n( I: y1 l' r: V[ suxm: 去看看上一期月刊的图1 ]
& Z" _- S9 }& v下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
! x. P* G% s* L3 f$ ]' ^6 _2 ?
- o4 ]8 M( E  I, R! u/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
- y" q; i5 o! G7 i1 @7 {# V#define HOOKINT 0x38
* X2 M% L" c7 G8 K8 q7 I) INTSTATUS DriverSpecificInitialization( ): j7 n  t5 h5 e
{
+ B! {1 ?7 \8 i& b; v9 @; G6 WPIdtEntry_t IdtEntry;/ r* t2 C6 e" c% s
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;7 ^1 a% r* j1 t
1 r. C$ p# m. i1 S) C
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
; ]! v1 j$ k5 P4 B( K% U! `- IServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);- I: ?0 Q: f$ S+ r
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);& W# f% G- n4 s7 X7 P

% L8 a' s4 \/ J, a6 i3 W% P/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
- p2 ~2 B2 R9 aif (!ServiceCounterTable)
* A5 @8 o8 ?! S+ y7 Y1 S; ~return STATUS_INSUFFICIENT_RESOURCES;
7 ]! M' n/ `' A. C. q3 d, Y# d& @
# y3 J3 x3 r2 X: l$ W6 {4 t' r7 Mmemset(ServiceCounterTable, 0, ServiceCounterTableSize);
# T. y3 [. Q! W! `*ServiceCounterTable=NumberOfServices;8 s! t( `. X, y6 S% Y2 I

6 ], O( R6 r; R: ^. C: h/* 获得IDTR Register 的Base Address和Limit*/1 [# J% x. L+ U$ Q$ v
_asm sidt buffer. m- g( V* l, S
IdtEntry=(PIdtEntry_t)Idtr->Base;8 Q; [  i$ y$ \* G3 i* Y  C
9 t3 e* n1 n0 l* P' F. \
/* HOOKINT就是我们想要hook的中断号
; H1 M6 O$ w5 U9 ?7 @这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/; M3 ~4 M& M9 v( D. d
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|) S* K: e( Z! q2 Y! @
(IdtEntry[HOOKINT].OffsetLow);5 [5 y: H2 y5 u; s8 P8 _
9 B/ h: T) M% Q6 k. H& y
/* 在IDT表项的相应位置写入新的中断处理函数的地址
4 {" T# @+ v5 Q/ G/ Z. q% v3 p用新的中断处理函数替换原中断处理函数 */" A$ f  {- m0 ]* Q5 G0 o
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
6 o* M1 p1 h" z3 K' |6 nIdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
& R* c- {9 o- G" vIdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);( e. N9 s0 E& H0 b" U* v* K
_asm sti /* Enable中断请求 */
$ s2 F( c/ {+ n" ]4 o) e
/ e, p0 x- m) }' s/ i' jreturn STATUS_SUCCESS;) w" [7 T. c2 ]# L; i. s, Y. Y4 y
}% o3 w' r0 y0 r/ A7 a
: i3 e, W' O( W  K* h# w7 @$ p
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
% S. a( W/ X, r, P$ N" ~/ [% Itypedef struct InterruptGate+ k' J- f- l- w3 H! Y3 X" L( z3 m
{
/ s) Q. g6 }3 [3 t8 `unsigned short OffsetLow;
1 g3 d9 G' W) ^1 O( ^unsigned short Selector;
. K8 M! W. C1 B7 ~3 |6 [unsigned char Reserved;
, w7 v8 b1 n, \! K  q7 W3 Q/ Kunsigned char SegmentType;9 J5 [1 Y; T1 L! ]5 B
unsigned char SystemSegmentFlag;
% ]+ v5 ~5 R1 `9 Nunsigned char Dpl;9 S2 m- g; O  `9 t) F
unsigned char Present;
2 J% M! j4 _8 U! ~unsigned short OffsetHigh;5 b, \& Z' @. U
} InterruptGate_t;
# V; U, _5 H" q$ @' J4 o3 J9 V3 }. c3 g9 l, l1 {& k6 g
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?& }9 Y- v7 z% a

- I: u5 M' n, k5 y7 T[ 读者小强:Winnt和2K的IDT中有空表项吗?]' z+ M4 H1 b0 V1 H; o
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
! t, B3 p# ^5 \* N6 @下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
: r3 h" ~3 c# J! _6 CNTSTATUS AddInterrupt(), I) m, ]; F8 P
{/ }5 L0 x* v! M2 D* K! z7 }) j- q
PIdtEntry_t IdtEntry;( y, q! V/ |1 H9 _$ y8 x

2 m9 E( q. }7 |+ B0 [, Q/* 获得IDTR Register 的Base Address和Limit*/4 s% }7 Y2 O6 T$ X+ @
_asm sidt buffer
# J6 ?5 j0 i! |( jIdtEntry=(PIdtEntry_t)Idtr->Base;
; z4 j2 e3 E* o$ N- H9 ?if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
/ q8 S0 C0 S% F( j+ treturn STATUS_UNSUCCESSFUL;
/ s8 h5 J% j$ P: m% k. [
6 L; k8 X6 Z- l+ j& I4 A_asm cli9 c2 M. Q0 b) Z4 Y$ T

. T/ y4 ~" a/ W( h/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
5 }1 q) A. J+ a( q, VIdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;1 Y& H" v4 ^! ^! Q/ D, p
IdtEntry[ADDINT].Selector=8;" F5 N, h' D, }7 W9 O
IdtEntry[ADDINT].Reserved=0;, [3 U  I7 R8 U, |: \/ ^
IdtEntry[ADDINT].Type=0xE;) A. _2 T# G" c( r* z
IdtEntry[ADDINT].Always0=0;. C/ W: e9 Y% [: B3 d; @' }" e
IdtEntry[ADDINT].Dpl=3;
; n2 l, `7 A/ s: S: pIdtEntry[ADDINT].Present=1;
8 W- j+ ]; i* d  i1 v3 [* }- }2 uIdtEntry[ADDINT].OffsetHigh=
1 Z1 z- j! g- O- M' B; B. R(unsigned short)((unsigned int) InterruptHandler>16);0 w1 x" F6 i5 r. E; V
_asm sti( _& z/ I9 _+ ]; {& V0 I

7 B- q" z: e1 @5 K* a. T3 Hreturn STATUS_SUCCESS;+ H! @: p' q' Q4 z; f
}
! S4 C: \. W; i9 i- ?- |  S% M% f: T; v$ n$ W+ l9 q4 @, {" R
让我们就上面的代码提一些问题。
5 f+ h; N2 y$ u2 y' {0 f9 iQ: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
/ @( D* ~( k8 O& J; c6 `8 `  u( NA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这* G8 ~' Q* r7 n$ h  K; G0 V, I+ z
个段的名称就是8(CS=8)。
8 h$ U& r% t$ T5 R! V
& Y4 }: O/ h4 ~Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
$ Y2 O+ p' }: X+ O7 mA:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
9 D! `+ J0 q, u1 U6 l应用程序的DPL=3,所以,可以直接触发这个中断。6 {  P& z3 e+ {! S- E8 W% n1 B" g9 X

* N) R6 h! S! ^9 i2 G# }Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
0 A. b+ Z6 Q( J$ l5 UA:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下: L! u* |2 w' C4 ^9 }3 I% |
:idt
; V1 U( j- T' c5 {. T( QInt Type Sel:Offset Attributes Symbol/Owner
; W: }' t3 L- Y3 L3 }IDTbase=80036400 Limit=07FF
; d! E9 l) b5 A3 ]- X0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590) U/ N% B1 I5 K! i+ n. @) Z) ^
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
' U4 J' s9 z0 o6 T- B% i……
; Z; l; v9 P6 M! ^# ?可见,0xE表示IntG32。7 Z# M4 @5 x, I- }" H. A% a

( r: W7 Y( w4 `: Y) p终于快到下课的时间了,让我再多说几句好吗?8 m3 A7 ], _5 V# q
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-4-19 02:56 , Processed in 0.286455 second(s), 51 queries .

回顶部