QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:& ~% Q, L8 b& x+ p& Q  T
--------------------------------------------------------------------------------
0 Z' l4 n1 U9 ?! P作者:suxm < suxm@nsfocus.com >; b2 C( F+ p8 s
主页:http://www.nsfocus.com+ ]3 M& O) s7 G; M. h: M
日期:2001-09-10
. a  A: U" T- E$ ?; e" O( w% L1 ]" o, x1 R* U: j
<<接上期>>
" p/ U& I8 g# t
; G1 W% h% H; P: B# a* T" ^3 h8 VIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。. |  q+ V3 _8 W4 I

8 z8 c* i+ M% Q% v! r2 L8 W9 y4 c[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]1 C3 K# W! q" ~) Q/ c3 p
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。# n  t: S6 I& s
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。* k- I% q1 x% y2 s* j8 B
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。/ S; s/ v0 p; L0 l
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。' e+ [" l% \9 |! v0 S( t& n
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]8 b; r& X1 X) p( ^1 g
[ suxm: 去看看上一期月刊的图1 ]
4 x. U3 `% @) Y+ d下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
) Y% {4 ^: F  J9 `7 L4 ]8 j' Y  c2 e) {) W# _* `
/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */0 _7 K/ G1 H: a4 V
#define HOOKINT 0x38# c! G/ J: S+ z4 N# n. b5 t. U1 \
NTSTATUS DriverSpecificInitialization( )
: _& `$ K' [. [{# C. }- v7 O7 G# P
PIdtEntry_t IdtEntry;) E: M5 p5 ?- P, E% _, V; U
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
. _. y1 f: ~& A) z0 k2 ^$ `( i) h/ v% f( S5 L$ f# v
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;  b, x9 m" t3 N7 N' O
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);& K3 m/ O7 ~* _
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
# r% V: ?) w+ ~' Y( t4 h, l& q- l1 V) ~. n6 a# ~" c) c. h; w1 b* y6 P
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
. r& r5 \7 q+ S1 [& z. u: Cif (!ServiceCounterTable)3 d; O' x2 n1 t7 Q
return STATUS_INSUFFICIENT_RESOURCES;
7 h. C, H- e& u' f3 u, E0 p4 n; |) }6 p8 k8 [0 W
memset(ServiceCounterTable, 0, ServiceCounterTableSize);
; Y, v# m* p; a3 G- f*ServiceCounterTable=NumberOfServices;
! R: P4 B4 e0 F' B: W* |& _9 [7 V0 J1 X- l6 q6 g1 ]
/* 获得IDTR Register 的Base Address和Limit*/- K7 A8 X& A, |/ [& Q0 I
_asm sidt buffer
4 }( U, U# m  @: U; Y- d! u  gIdtEntry=(PIdtEntry_t)Idtr->Base;
& K& K, Y( |: E& }- Q7 k7 P+ i4 \0 I- y3 v$ a( a
/* HOOKINT就是我们想要hook的中断号6 G" j, X; R* B/ P7 G# B
这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
8 E4 e! N) Z1 S* p5 K! AOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|$ ?% i  a  d  H0 d$ T5 [% q& o
(IdtEntry[HOOKINT].OffsetLow);
: m( E. u$ c  Y* s- d
3 V* p6 A1 U9 B5 T4 V' T% U# K# [/* 在IDT表项的相应位置写入新的中断处理函数的地址, o7 z8 b# `$ S4 T2 K
用新的中断处理函数替换原中断处理函数 */
9 E# N6 C1 U% ~" u9 b_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */. g9 q8 |+ Q; ^$ C( }8 I
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;* k, H  X3 u1 l$ Q! ?/ ?
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
+ e5 N0 b) P# s$ h9 L_asm sti /* Enable中断请求 */
& R9 n4 K4 L# o* [6 U9 u
- @9 l) V- D; xreturn STATUS_SUCCESS;
+ C; X$ h! a2 c9 c+ \1 S. |}" q1 `! r1 A, r
, W* S8 q& b9 V! l, T" F+ d
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。) o, |5 z" @$ X! f7 K% Z. i, N
typedef struct InterruptGate, ?! i3 U3 [% p4 U4 d: r: {% [
{
0 v  y4 i3 S. H* gunsigned short OffsetLow;
+ n) w, ?; M2 G' _1 ?unsigned short Selector;
! \) d' a- ^: _. z" i- ^unsigned char Reserved;
! T0 g. C  X$ y1 b9 x$ H$ O2 Uunsigned char SegmentType;
' q" b( I: x0 [" T+ \unsigned char SystemSegmentFlag;
, c- X* ~& k" S1 e1 a: K% D" Funsigned char Dpl;$ f- f' k3 X. P  C  H/ ]$ z
unsigned char Present;# z& U; b" l5 h  \: J" ^; S
unsigned short OffsetHigh;
4 E0 Z! }2 n' B) D6 g: i} InterruptGate_t;
, L7 ?) l9 _6 a- G8 ^5 s5 f  b" [
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
! p7 X8 M: T1 r( M: Y% u* E' U6 z9 q; E+ Z1 c
[ 读者小强:Winnt和2K的IDT中有空表项吗?]
, o* h4 L' g* T9 G  a4 _是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
6 w8 L2 G) J- U5 M+ R5 Z下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
+ ]& i( D3 r4 |! W1 {1 _% }NTSTATUS AddInterrupt()
" \$ M* v  F( |{" [5 w2 V9 I! Z# P8 d: [
PIdtEntry_t IdtEntry;
8 w3 V! k6 L  [4 ~7 w# @% d( @2 @/ d2 H; z
/* 获得IDTR Register 的Base Address和Limit*/# O  a2 C- R" X  N
_asm sidt buffer
; `/ O2 q: j3 z( O& }IdtEntry=(PIdtEntry_t)Idtr->Base;
- L6 M. @/ s* H- _if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0)). q, }8 S4 A1 s
return STATUS_UNSUCCESSFUL;
& s( O( P4 A) u) r' \) U! Z; L5 w9 V1 H) I0 E
_asm cli& V9 g0 C8 P9 _2 }$ o

; O: f6 ]/ Z" ~& q" [2 @$ H/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
, C+ n' y: I- {IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;5 J0 H; @5 n% L. u
IdtEntry[ADDINT].Selector=8;- Q+ }; a: a- O0 U
IdtEntry[ADDINT].Reserved=0;
) O) k1 M1 J& p( Z  f& z5 VIdtEntry[ADDINT].Type=0xE;" X8 B" Q8 v9 h; Q
IdtEntry[ADDINT].Always0=0;3 r4 x$ c0 |( p1 m# x
IdtEntry[ADDINT].Dpl=3;" C: @1 A1 n1 z  T0 A
IdtEntry[ADDINT].Present=1;- B" T1 l1 b  S9 X0 b' \" x
IdtEntry[ADDINT].OffsetHigh=. t' Z3 K7 p4 Y$ o8 B3 |. _
(unsigned short)((unsigned int) InterruptHandler>16);
8 y7 P& I$ {* K% }_asm sti; |- f: K1 m% r, ^, y" U
6 c' c8 q9 e: H5 A7 b
return STATUS_SUCCESS;
! y) Q$ ^% N6 T% e* ?' u+ Y* Q}" x  Z: G7 S, h# k8 q. X

/ l  x2 a) Q+ N- ?: U5 J9 d让我们就上面的代码提一些问题。" k& U: x- o4 D( O
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
( J& r2 e5 u* J4 s) BA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这  l( P* ]! g: p- ^) s! |  t
个段的名称就是8(CS=8)。
6 k1 u9 p0 [! ]# E
9 e6 i' T$ s" Y% B0 gQ: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
& Z1 \& q4 ?/ h/ I) }) c6 v4 rA:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为7 }4 J6 w* v8 w
应用程序的DPL=3,所以,可以直接触发这个中断。
, k/ r  A1 H9 E9 @. O3 q- ?9 @! Y1 B# T2 S
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
, N" \. S! _! ~A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下4 E; p/ b" u# `! x1 z) I
:idt
5 a  D( S( z+ i( A& E, ^. MInt Type Sel:Offset Attributes Symbol/Owner9 g) C5 O, A. w9 `* R1 P4 S
IDTbase=80036400 Limit=07FF! U% y  O$ `' a6 \9 P( B- g' A
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
# ?& i6 \$ _1 d9 D" L0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
+ C( D) [, w5 t……7 O4 n2 I  O  j0 i$ O
可见,0xE表示IntG32。. z& d! z' Q8 p1 o& X; x2 ~
% w5 a& i; g: C" G
终于快到下课的时间了,让我再多说几句好吗?6 P0 w/ R& X+ e" N/ b" b
通过对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-8-28 19:15 , Processed in 0.279888 second(s), 52 queries .

回顶部