- 在线时间
- 0 小时
- 最后登录
- 2007-9-23
- 注册时间
- 2004-9-10
- 听众数
- 3
- 收听数
- 0
- 能力
- 0 分
- 体力
- 9975 点
- 威望
- 7 点
- 阅读权限
- 150
- 积分
- 4048
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 1893
- 主题
- 823
- 精华
- 2
- 分享
- 0
- 好友
- 0

我的地盘我做主
该用户从未签到
 |
文章内容:( d7 G, y+ m# O
--------------------------------------------------------------------------------; z- i( C L* I: g4 n
作者:suxm < suxm@nsfocus.com >, ~0 s* ]. D% Z9 p8 J
主页:http://www.nsfocus.com* G2 V/ U/ f& [$ G# C
日期:2001-08-13
( ]: ~) u( ]7 w) I, U% [! X" B6 d; V( \0 n
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。( T3 C. _& n. \7 q3 F8 ?1 Z! K
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。. |: h z M, K5 }! b& [( b! w
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。$ t- f& r5 c) g4 R
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),, f e: x' B! h4 a; q% J _# y
5 \' B1 a9 b& R- L
9 P3 Z; }: T0 _0 l+ @% L- d$ |6 ]4 v
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
- E. ?2 i' A- x: g/ e8 i" aTrap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。* |: F. `- _) g
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
1 W# @3 {4 c5 g4 I" w8 }int51 dw kb_int ;keyboard-handler offset" D' S' Z# z% @1 r# y5 z! g% R
dw kb_sel ;keyboard-handler code selector
, _3 K, v! b% Z) ?db 0 ' {% N0 `' b" q9 }* w; n( W4 \
db 8eh ;386 interrupt-gate2 Z' j0 G# ?1 s7 x1 V3 u8 Y, n
dw 0 ;offset is in first 64KB of segment" j! X, N c% U5 J
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
# K' F7 ] M+ p. h这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。0 L! B0 B: X1 o' Z2 M& a( g
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
8 j f0 c% L3 }, H+ B1 K答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
2 D: J, _8 J, h: \在单CPU的Winnt下,实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断号30h——3Fh(看 到有些资料上说Winnt下这个映射关系不是确定的,但是我见过的单CPU的Winnt中基本上都是这样的映射关系,而双CPU的情况下,情况 确实有所不同)。从下面这张统计表(http://www.wischrop-net.de/nt/kapitel7.htm#7_2)可以看到单CPU的情况与双CPU的情况是大不相同 的。' n) W6 n% ]7 y9 W
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook7 ]; v9 v1 O4 I1 R% {" f1 q
IRQL - ~) i; c) d/ ~
1 3D 3D$ p/ _ j+ u2 i; ^6 L
2 41 41 t9 `+ J! E$ ~. h/ c' L( D
3
6 W" W' E6 e4 n% S4 51 51 51
$ Z7 ?+ c5 J, a! Y; c7 l' x% I5 61 61 61
4 [* r( S; D# i" l: t: I" j& G6 71 71 A8 p' G- O* K$ L9 q0 u
7 81/82 81/82 81+ e# V8 U) s* W+ ?' m
8 91/92 91/92 91/92
5 t7 k* G- f6 b$ P# ]1 F' f9 A2 A2 A1! n" x( A" ^3 U1 G3 T; l, y
10 B1 B1 B1/B20 n$ P5 R9 b3 E9 g6 g O
11: G5 @, _. v- e) B
12 3F # O* E! G$ B0 T# q
13 3E 3E 3E 3E
6 o2 L9 I, p% f8 X8 ^14 ^2 S- |7 ~" F0 N+ C
15 3C 3C 3C 3C 3C9 I2 r- }* @$ U3 M% Q$ [+ Y; ^0 m
16 3B 3B 3B2 }( U$ e5 }% |, _/ @1 m3 X+ @
17 3A 3A
' o \- o: q, c ^# E18 39 399 y* D4 M2 C, O; _; C2 x; Z, w o$ i
195 X z' T( @0 E/ W, ~
20 37 37 37
) N; T( Y- C" y) d5 B. A# ~21 36 36 36 36 36 36
4 O- E3 E6 f% w" d22 35 35$ }1 `9 V9 l$ T( Q
23 34 34 34 34 34 34* x6 Y( z T$ @' I
24 33
. y% b& |6 W; P/ g) Z n6 w25
6 |7 s; o5 y7 k5 |26 31 31 31 31 31 31
7 g# M8 X) O5 i7 G9 |27 C1 38 C1 38 38 38 38 C1 38
6 o! `, P% {: r1 @28 D1 30 D1 30 30 30 30 D1 30$ V0 Y7 V j9 I# r' J; R& C* I0 q
29 E1 E1 E1
: B( V6 C U7 c, q$ d4 R* m1 e30 FE/FD FD/FE FD/FE8 M# Q6 h) M" L' B# ~6 A
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
; ]7 N4 y- _9 M( q0 g( b# Q1 A( L6 b% a9 g0 V
255 50 50 50
5 f. k- V! q+ bDPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
T. b' S* v; n7 |; Y
; q8 b5 F" D4 u7 U+ H( {5 R6 e机器配置:
) C4 l/ \+ y/ r' h* ~ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3. u8 M' c3 S7 R! \1 R2 @
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3 A% f( k8 \4 U- T& G; c) O
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
) K3 \- Z6 H) h% Z* ZHugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
+ w4 @- V! X- S. N7 g! P; \Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
3 x% _. O9 T- [4 k) B% d3 D* g8 r3 EStat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2& Q. l) w: w6 t+ y) ^( `: N' j
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
) N0 r' _& l' gSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3$ L; F: r9 t4 S: w
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3
0 D( c0 a1 s$ ?) C
; }% M1 y( _' w% V# H9 v在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
# E+ o, W a# ]7 A" F3 VWin2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
, K1 e4 q# |" S% @:idt6 R( X9 @; O" w# R/ f
Int Type Sel:Offset Attributes Symbol/Owner% N) \$ o- z2 g
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit- t j; @( D8 a( g( |0 t: V
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590- ]& \$ c, i; A# C0 t
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
& f& C4 n# ^, g……
_+ k3 p* I/ {9 |% ~9 X. O00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092 `5 _: B' L3 h2 e4 l; G* y! |
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092; w1 d& e2 `, H, g6 j8 j6 u- X
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093" \. B4 w9 j# E, Q
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。2 J& ?) L* z9 c6 k) ?* h z
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。9 L( w( O2 K: G0 M7 M, f
( U/ I) [0 _" \, A
@* `1 B: a+ m4 Y
5 t. `: D0 a5 u8 k0 c5 w提问:IDT表的Base Address怎样得到的呢?( P( L, l6 D5 k* ~ e0 z# ~0 Z2 B
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
1 S( u1 F/ A5 u _ l9 Bhttp://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
* {2 b! ~/ J7 O! ?( F0 l3 ^SIDT - Store Interrupt Descriptor Table Register4 }. [" I" N1 U, a( ]6 ]& i/ j
! a0 J+ W. x3 i% R4 BGuido Wischrop提供了下面的代码可以读取IDT的所有256项。
, ]9 N6 O$ R5 |8 {: t
, T8 F1 l6 P1 D+ {UCHAR *mBuffer;4 z: b. N/ y1 j% r
ULONG dummy;
; Y% C5 m; v( a" e$ [ o. Y……
0 x3 q1 }2 e: t. s: E_asm
$ G1 @1 G/ t7 t9 \, ^# Q{
/ J% t4 l2 u1 q v5 X, U% ]+ \mov esi,mBuffer
: \9 |* k/ x+ U2 C6 lsidt [esi] //load IDTR to *mBuffer% B* S4 h$ M# A$ [# c. P( I- t
movzx ecx,word ptr [esi] //load size of IDT to ECX4 B% U! P5 c8 v$ T- x. P
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX1 j7 y! N6 ?+ }" R
inc ecx
" v! t- M$ p/ W0 Q( omov edi,mBuffer //mBuffer is the target
" |' n* t1 n. ]5 y6 Y) S5 dmov dummy,ecx //store size in dummy3 L. ~$ @) P6 C! N# p0 s
mov esi,ebx //store IDT Base Address to ESI
( q: ?- c$ @% A6 R/ |. ]5 n& gshr ecx,2
+ d& x! W6 Q& Hrep movsd //copy IDT to mBuffer: J1 J. d: ~" ~2 u
}
' K8 Q. l3 d+ I; e7 _) n1 U% F. s3 k) A
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
6 {1 H0 v9 _! q% d我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。5 w& X1 G _3 u8 h5 l8 S! W6 l7 i
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD2 L3 l$ \$ ^5 X
(2) Interrupts---> New
/ U. T) _1 x ]8 g0 ~Interrupt Number = 14(或别的) & Edge Triggered & Shared3 _$ {# Z" M: [8 d
(3) Listen to Interrupts+ N1 g& u4 a2 y" K9 w; O: K2 h
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。. r6 U! U* S% i" H( ~
(4) Generate Code; ^8 i( n7 V$ {2 A
(5) Build and Run
u$ Z/ C0 Q/ }+ }(6) Enable Interrupt) b+ U7 P# Q9 G- F: g0 J
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:& p% g2 S! [" _) A; n- Y
:intobj* o: D/ g' K9 h
Object Service Service Affinity3 z* @" o- {$ O1 |' T2 W; k+ i
Address Vector Address Context IRQL Mode Mask Symbol
6 U8 K6 R5 T0 j& D5 k/ hFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
/ b; \( Z5 Y) }5 g7 c8 j+ W8 o. }FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
7 Z; R, T1 u) N, v9 P$ ~/ g3 [5 [; UFD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+20406 d* l7 P' J$ M3 A' d9 [
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
! [6 P3 I# U, R6 B% k! x' l' e- Z0 k7 @FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM
* {" [( f1 q% F4 L( Z SNTICE: Exit32 PID=70 MOD=ps2_diag
" D0 d% `5 i' u" \6 M; ?0 P
& m9 N7 R; A* m( g( d从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
& v0 I, d% s' p+ ]; v# W4 k# W, H
( R: p0 H) }0 v, p2 }! ^" C" s6 N4 y/ b8 @; p2 J5 e3 n \6 C
" S" q* ~& H( B: T2 x
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
* t# v8 j; W# ^$ N2 [4 p- P
% @# I- d: f5 c2 t0 f* D9 m一个问题马上被提出来了 |
zan
|