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

我的地盘我做主
该用户从未签到
 |
文章内容:
! c( c1 V1 b9 p8 `$ k. \--------------------------------------------------------------------------------
7 }8 h8 t" Q6 D9 p5 t+ P作者:suxm < suxm@nsfocus.com >
3 Z* F7 m, b8 z8 g6 ]% q主页:http://www.nsfocus.com
0 C' X* r# ? _日期:2001-08-136 O6 W5 V$ G* Q( n( S# z
+ D* ~* y0 N$ \, ~& X
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。% W& g* b& z) C1 H! W
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
! n2 ~% K; W# }# |# O, A- }让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
4 c0 M0 R H+ B0 u% }: pWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
& I" F6 r+ W; j% c/ o7 l8 j
6 m. z7 X/ r8 ~6 y( F5 R2 E! q4 i8 A
" L) Q5 r4 u, f! j) s8 o" i4 M1 @" c其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。' V5 z" [& G# @: U# U% c- i
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。7 @# [' {! `0 b* _$ u
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):! |) ]4 v, K& R; O
int51 dw kb_int ;keyboard-handler offset- h7 f2 o: T i9 A. ^+ s- T0 [/ z
dw kb_sel ;keyboard-handler code selector+ _1 Y8 h2 }5 s n ]/ Y
db 0
7 T; X& T+ P; |& f. S5 @# M/ ]db 8eh ;386 interrupt-gate
" |6 k- }1 i4 A' Wdw 0 ;offset is in first 64KB of segment
* [' G c4 `- e2 {8 d J& M是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
5 D0 W) Q9 i& u4 V! ?5 x这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。 v1 _, b& q7 {
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
, J+ m& s! d( s" d2 v答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。* H8 F( [3 d' |
在单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的情况是大不相同 的。
$ S& q/ U$ g6 a" N: V5 U% yAladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook1 j7 `: Z7 K4 |4 l, J
IRQL
! C9 e9 ^ }4 h8 }7 D/ b1 3D 3D
1 v7 |2 d' l" V; R: s2 41 41
+ r {4 E0 O7 Y7 J3; ?2 j8 O6 E2 H/ Z$ ^
4 51 51 513 T4 u# u! p: ~
5 61 61 61
" d X! C( G* S+ R6 71 71
$ N2 O% s: A# h. `' v1 d7 81/82 81/82 81, W# e' N n' B- j$ e8 }6 I
8 91/92 91/92 91/92
( k' T1 Z% y% P! N9 A2 A2 A1
2 w/ Z6 G9 E# i( A( r2 {) F7 R3 `2 T10 B1 B1 B1/B2- m: A( n1 x( X B
11/ c3 a* o& l u2 }+ t
12 3F
6 o/ K; C/ e+ U( b) H% d13 3E 3E 3E 3E
( ]% ?1 G( F/ J' a9 z1 _14
( i! ]7 b Z) J15 3C 3C 3C 3C 3C. e0 }; g- M- P( j, R5 ]
16 3B 3B 3B0 Z* Q0 u. |6 Q
17 3A 3A, P K3 M) y6 C* z+ A
18 39 39% K& K4 d! z1 {' K ]
19
) ]- _6 s O: d7 x/ ~9 K4 U+ S20 37 37 371 o: l: w2 Q8 V9 Y1 {% U
21 36 36 36 36 36 36
7 U: ~5 J7 b7 C; K. b! |22 35 35
1 t& ~2 K; h& s$ d. A8 g: Q- [23 34 34 34 34 34 346 X6 O; u7 e. E# b
24 33
5 E7 t; L0 ?! I! C4 t25
: v8 i# G5 [, W1 I% c( b! r26 31 31 31 31 31 31# Y+ i% P4 \' n* a
27 C1 38 C1 38 38 38 38 C1 381 w6 e" e% x, T9 U
28 D1 30 D1 30 30 30 30 D1 30
8 @4 D# D& n1 \29 E1 E1 E1+ G) ?7 Q( a* D {' b8 |
30 FE/FD FD/FE FD/FE- J/ S/ k6 C2 Z5 k% w" t4 h
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32' i* ]4 X* D- n' F2 |* J* q# V' _8 ~
# l% m9 t- c# L255 50 50 50 b, N7 c; K$ C0 S1 o' U
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E5 u) Y8 p, f( L$ C. Z3 m
8 l& e/ x# Z& k0 n U4 d6 w7 s
机器配置:- p/ _$ U V$ I( e4 k5 ~- q1 z& {
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
6 E+ Z. o+ Y1 e" f# m5 |BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3- P8 }4 z4 A% v) d
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP35 \5 g# x* u- D8 W
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
. o- ^$ i2 Q$ H0 {7 [* y0 |% WStat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
1 H1 A, I; @" x% V, p* u: rStat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
! ~2 K% w# a/ k5 ~OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
, l( o1 F2 U2 x: V1 OSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3: t" k# q- q3 T
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3, @- v- R# Z- ]$ y+ l) M1 |
* o* f5 a( R" O在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
+ X, u( _) @: s0 xWin2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
7 e2 Q* t; e2 j, D:idt: H6 D* r2 \6 S. ^, Y- h7 s$ Z
Int Type Sel:Offset Attributes Symbol/Owner% @& c. x! D6 x% U
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit( G9 J, B$ X1 n" O& V; i
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
% C3 E2 r: f: O& `, j0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
9 w7 z; q ]0 C- Y( M. k* U- J$ \……
0 b1 {0 U7 e0 n7 Y' ]00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092& m: L e6 j9 F! C0 q
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092( W- i8 @, X3 x9 L, h& T. C! x6 g
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093( s. C! J% ]9 [3 |! O) F" P7 v
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。4 O6 J4 n8 ?. S0 q3 O+ e
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。- ~- |. ^+ S- e7 X
, |4 l7 `( P0 v0 B6 D# e ]8 p: q- C* h1 @; \
7 N7 r' {4 ~9 G2 n) L v
提问:IDT表的Base Address怎样得到的呢?
+ U/ J t- N9 G答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下" m+ J, Z2 \) t0 X
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
' `! o7 A1 ]9 L4 ~( L+ [8 dSIDT - Store Interrupt Descriptor Table Register
1 B3 m \( J" C( r* H1 D
) F' B+ t3 L, [4 VGuido Wischrop提供了下面的代码可以读取IDT的所有256项。* I( t; M) G2 t
! R5 V1 U4 d7 m' j7 w* N4 M
UCHAR *mBuffer;! ?/ T) V) s) n8 ]: g. G) H
ULONG dummy;
" N3 A$ b! r$ c' F# M' t. c* X3 Z……% \4 g. m: ]8 |/ h7 t
_asm
0 d1 ?$ c' }7 n+ L{7 ]0 e/ Q' O" j! S& e
mov esi,mBuffer ( A" h( o3 F" E* Q8 M0 j
sidt [esi] //load IDTR to *mBuffer( z2 [$ o: z! t
movzx ecx,word ptr [esi] //load size of IDT to ECX
. r3 e8 X" F/ D4 _mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
5 ^2 p, b, t2 _+ D% Q/ K6 _inc ecx / h. V% _' Q: s/ i7 Q
mov edi,mBuffer //mBuffer is the target
; k; t1 R9 a$ N0 _( Nmov dummy,ecx //store size in dummy4 k1 S4 \" f; L2 J/ ~8 R5 O
mov esi,ebx //store IDT Base Address to ESI
- o/ O7 I8 l4 C' b3 A6 Eshr ecx,2 ! A& i0 ^6 D5 D2 `) |0 Y2 j( ]% X
rep movsd //copy IDT to mBuffer9 s2 E; J1 h7 Z' z! ?! f
}; w5 @! K1 T$ u7 \. x
# ~4 @6 W; X5 @, a5 W( B- l
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。4 V" p9 K; K6 S; g5 D" a3 J3 B
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。* A; ]" R# u ~5 [ r
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD6 x/ {4 W% Y3 N$ c5 h
(2) Interrupts---> New
0 ~: p! Z2 G( D& ?8 L, yInterrupt Number = 14(或别的) & Edge Triggered & Shared9 M, |3 ?( w7 w2 j
(3) Listen to Interrupts
) e' Z; Y0 z. X/ G7 h; h* j8 Q如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。/ Y- o x( G, L/ R4 b. O
(4) Generate Code: O; ^5 S: X9 P8 Z' H- W
(5) Build and Run, r( X' V/ l I3 y
(6) Enable Interrupt! ]9 S" @" y) t5 }/ y2 k4 H
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:7 K: X. s7 r( ^) o
:intobj
( K3 K, ?# j; E. bObject Service Service Affinity
0 p) H0 m8 R E: `% KAddress Vector Address Context IRQL Mode Mask Symbol
) u# a/ V; O6 ]+ O7 V0 t8 b8 f2 KFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
) X& N0 ~3 }. J8 x1 l& r/ VFD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C ~% [2 o- L( I2 V
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040# q: i, b3 {" Y) a A% J
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2( J8 s' u" l) v* \' R2 k7 \
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM ~" t: K" N4 ?0 O/ }% i; r
NTICE: Exit32 PID=70 MOD=ps2_diag+ e; p* I4 s# l3 X+ u, z9 Z: e8 j
& I* [3 E6 d, E' @- A从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
9 ?+ s0 f) ]9 E
1 a4 ]% i4 q0 E( X$ V% _" u/ q; b& j8 p2 E0 T0 Y
/ @5 l9 k8 e& ?7 O( b% l) s
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
5 j2 W0 P { Y1 J# t
# P6 O" [+ Z! V) u4 a一个问题马上被提出来了 |
zan
|