QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:
$ O- L# A; v# G4 S--------------------------------------------------------------------------------% J4 I$ R6 A2 |; o/ ]
作者:suxm < suxm@nsfocus.com >
: B* H; u& r: l4 s主页:http://www.nsfocus.com
! Q+ \- X2 z6 e2 s日期:2001-09-10
( p0 q& x: s; B; o8 h, [3 n% E5 f* j5 K( v4 C( w  L8 z6 r3 {
<<接上期>>
; S: Y6 k/ l6 l- r
  e2 B6 A& Y# r& xIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
9 B3 D8 C( e9 j8 x' y3 j+ j( x
" g3 C3 S; z1 ?$ \[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]2 T! e) L9 d  H, M& z2 i
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。0 D6 i" b( H& [% u
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
% z+ H- z3 M7 w* e2 J5 R# d: ^在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
3 Q  q4 K) H! w0 Q通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。
$ h# Q; s7 V9 ?7 w- b) ?. s- n! h[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]3 a; J# y& O! j3 r8 I( E! C
[ suxm: 去看看上一期月刊的图1 ]8 @  t' F* ^' F
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。  P0 a: z9 P( ]# F0 Y: P/ u" o

# R( M# p( B: Y/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
9 C" }' ]$ g9 P$ {& V6 Q#define HOOKINT 0x38
( A9 Q; l3 l: i: ~4 PNTSTATUS DriverSpecificInitialization( )
  r# p% I" f7 q! `{% x3 v2 V! n+ K$ ?3 y" a
PIdtEntry_t IdtEntry;
2 k9 |- I# B9 B) p! \  b' zextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;- O# x# a% ^' i. c5 A
  K" v3 B( K1 A3 H
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;/ k8 l$ x1 p6 c  D
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
, w% H; ?/ k8 t6 l" aServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);) \) [/ O: P6 M# ?3 [1 \- c1 F* q

) }$ X. A. R& ]" v$ J% V/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */2 G, e' y1 v" W0 L
if (!ServiceCounterTable)
/ J5 A" z) E4 ~4 L- xreturn STATUS_INSUFFICIENT_RESOURCES;# o+ f) [' X8 D7 Q8 a9 F

% m) ~0 V* b0 b" P/ h6 rmemset(ServiceCounterTable, 0, ServiceCounterTableSize);4 q5 Q* O. M9 E- ?. F
*ServiceCounterTable=NumberOfServices;
& q$ V! f9 [  W5 `2 e% r; m
  _5 _& H% \! Z$ h6 ?/* 获得IDTR Register 的Base Address和Limit*/6 i0 c: z6 d0 d! p
_asm sidt buffer4 M# u% F1 Q) |( A  A0 p1 k7 V
IdtEntry=(PIdtEntry_t)Idtr->Base;! b  f6 I0 p6 l% {3 `! E
8 g' G. v; M" ]3 \
/* HOOKINT就是我们想要hook的中断号
# K3 f" w0 e: y6 L这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/# e! z* ]$ l0 z8 _$ D8 `
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|$ Q6 K& P) l0 O1 D- C
(IdtEntry[HOOKINT].OffsetLow);, G( e' V1 W$ B4 O  F
" Q  m1 \6 `( l; g2 e
/* 在IDT表项的相应位置写入新的中断处理函数的地址
1 X* s3 w1 I1 g用新的中断处理函数替换原中断处理函数 */
" R$ ^8 d+ G. I% x9 w) m_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
: G1 s/ f, }. `1 ]9 Z; M0 s7 YIdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
1 d- V& f% x" N# a9 T: LIdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);% e: L  q# z& }0 H! c+ [
_asm sti /* Enable中断请求 */8 Z& ?; C, J6 Z4 M* x

: p3 v0 c, `3 x5 X: B/ o+ p0 Ereturn STATUS_SUCCESS;
! f6 d" |, V0 K1 \# ?, }; K1 n}0 z: V% Q- u) d& ~" D: x/ Q$ S! A
. w: S+ o) }* m5 B- d
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
$ Q& E% j" [2 q9 L5 \9 ?* itypedef struct InterruptGate
8 p! g7 P+ Q/ n* n$ z{8 _) T7 Y% b* w0 `, w8 ~
unsigned short OffsetLow; 1 D( Z+ W- t2 M) E  X
unsigned short Selector;
8 C: Q  E$ a' z" a* F1 ^2 kunsigned char Reserved;2 I* V* s. v; Y' e, s
unsigned char SegmentType;. p; \3 u  ~/ |6 Y: X9 |0 P8 ]$ ^
unsigned char SystemSegmentFlag;. z7 b8 o" n9 H5 l- y2 R& }
unsigned char Dpl;8 R. X+ f8 R, a  \
unsigned char Present;7 C8 \! x+ l( _7 F; L$ q& _8 i
unsigned short OffsetHigh;
8 k# ]( k& t6 j0 F% l} InterruptGate_t;
! T0 g" r& P/ y/ Q9 p3 W& B5 _: |( ]
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
$ \$ O# m1 v; D& |* K! r$ f' D, I5 {2 D+ m/ d  J# Y
[ 读者小强:Winnt和2K的IDT中有空表项吗?]) X" P) W* L9 F# T' i
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
1 U& n7 F6 Z% r, E下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
! y6 W& l( y5 l9 W: z$ S4 uNTSTATUS AddInterrupt()
8 \0 l7 l* [$ ?+ z8 s- E{& o; `% q3 O3 S; i3 }
PIdtEntry_t IdtEntry;
" |) u: `- U( l3 X& s
& t9 {& W" }% @3 I/* 获得IDTR Register 的Base Address和Limit*/7 c/ Y6 p' N9 v+ y
_asm sidt buffer, a8 R" L4 j% w* Y; ?9 i, e4 e, D
IdtEntry=(PIdtEntry_t)Idtr->Base;
! q3 M7 F% v  h* n& \# ~; X& M$ o2 xif((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
7 `" ?. @: o3 u1 ]; y& ereturn STATUS_UNSUCCESSFUL;& F0 a, k3 Q+ N  ]3 ]/ U

5 \/ H* w- Q4 N9 H% W. a6 w9 H# ^_asm cli, I$ M+ h5 I3 O4 {8 d6 V$ \3 C
4 `5 j8 O: |/ u! F  ~1 D0 S
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
6 |0 \; Y" h% J3 V7 ?IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
- C/ O; W7 ^7 G4 U: R- {3 M  z: TIdtEntry[ADDINT].Selector=8;
9 D0 c8 E8 p# {3 C9 CIdtEntry[ADDINT].Reserved=0;; g. X& x. _" y
IdtEntry[ADDINT].Type=0xE;
! l7 I, p/ _0 n# tIdtEntry[ADDINT].Always0=0;: K- d1 ~, ~& R  W1 |/ F
IdtEntry[ADDINT].Dpl=3;
$ h1 a4 N$ {7 y) GIdtEntry[ADDINT].Present=1;
  a+ r0 {7 r# ]8 m# _0 J$ J) qIdtEntry[ADDINT].OffsetHigh=
' ?# X8 ~* I) g8 i(unsigned short)((unsigned int) InterruptHandler>16);6 N& C! T; G5 ?: o) B$ U
_asm sti
  w4 [3 k0 v; a2 u; {2 m/ [" t) ]6 z9 F7 V/ Z# ~* s
return STATUS_SUCCESS;! F/ u9 e' Y  s/ y2 t; T* M/ m8 U
}
- W3 C/ M% M0 o, Z1 D! s* b. l/ Q$ n- o5 P. s$ E0 U# B2 u
让我们就上面的代码提一些问题。: m1 x& a0 p8 w( `
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
; H: E8 K( o' Y3 p' lA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
, h3 ^6 E  ^3 S0 @. ~: Q个段的名称就是8(CS=8)。& L( m( p; d( e) t
' y" T+ P2 v2 r- p/ Q5 J5 J; @
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
* z7 i2 v1 @8 c( l/ y) ?/ uA:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为- ^: |1 H( D9 Y" e- k. ^
应用程序的DPL=3,所以,可以直接触发这个中断。, M6 N; F  {* m# H

- ]" _6 M* v5 s+ R/ ]' C0 H+ JQ: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
. ]9 w0 C, J) W8 I7 A; A0 XA:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下" a6 p- h( Z% b
:idt
6 y3 F" j& F6 i. a( N& [! d8 }, ZInt Type Sel:Offset Attributes Symbol/Owner
# K0 [0 k% w$ KIDTbase=80036400 Limit=07FF
  a, _1 l  d. x9 w" y& f/ c0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590- q& [1 P+ x! o
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
. L% B9 B5 x0 Z4 I- d……
; I. R* z% ?& S% }8 R# i可见,0xE表示IntG32。& S6 B" p% u. h: n  Q9 s

( ]2 F3 I/ }$ n2 p' c( c终于快到下课的时间了,让我再多说几句好吗?+ ^6 U- I) c0 t  P. ^6 w
通过对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-10-18 08:52 , Processed in 1.483169 second(s), 51 queries .

回顶部