数学建模社区-数学中国
标题:
关于Winnt/2k IDT的一些思考
[打印本页]
作者:
韩冰
时间:
2004-10-9 14:29
标题:
关于Winnt/2k IDT的一些思考
文章内容:
) P: ^1 N5 Y# R3 T" T3 i
--------------------------------------------------------------------------------
6 C2 p1 J2 T& p
作者:suxm < suxm@nsfocus.com >
$ |2 L7 I3 M2 N
主页:http://www.nsfocus.com
R" c* Y$ I4 S8 Q
日期:2001-08-13
( m. V& I' Q) J- h; ]
. V7 q% k$ T4 w/ ^
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
5 _' i( ]- f& C t) f5 J
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
$ K) a2 y! L B# {3 z% z
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
* ~4 s1 m, m' @, e7 d$ p" Z2 Z1 n/ x
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
8 h" z) O8 H) Z. g
4 b( x, K: z: F. E7 O* [1 w9 r1 W
5 B% c' | Y0 H6 G; ?
2 Y, Q7 `' S, Z+ [5 h& {
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
F, c( N2 t; x: d e4 L) ~/ s; _
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
* H% \* P" p2 Q2 M$ _: y; u
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
% h+ j x$ d6 o% w7 U' l
int51 dw kb_int ;keyboard-handler offset
2 P* c7 C" j0 q
dw kb_sel ;keyboard-handler code selector
( y) g0 E! W- b2 e" m, l" `* ^% T
db 0
* R; a1 x, L$ V1 l2 R- E# V
db 8eh ;386 interrupt-gate
* d+ v& \2 f5 ~" L
dw 0 ;offset is in first 64KB of segment
" U& }& s e% z9 A
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
. z# U2 b6 N& b) b
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。
* F" |* n" z D# Z, O
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
- {1 A9 M( Q, T/ J
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
0 Q' }6 A+ z6 [
在单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的情况是大不相同 的。
1 ]+ B1 N4 J' x" h% t4 N. O
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
s1 u( n& W# t. }$ {$ B
IRQL
2 p4 K6 | W+ T! A7 o# e# E) c
1 3D 3D
# }8 n2 h, `& l; U1 }
2 41 41
9 ^% o& R6 p: q
3
4 R9 c& L* [ ?! x) s/ X
4 51 51 51
% M* t0 h7 F }3 P- z. {) O
5 61 61 61
! n$ `$ Y# }& l3 E. B3 G3 I
6 71 71
$ O7 S, ?2 z- I% k
7 81/82 81/82 81
* y' f ^2 Y/ T0 l
8 91/92 91/92 91/92
) Y. v7 G5 c& W f% j9 x2 {
9 A2 A2 A1
v5 N [4 r! x; |- N
10 B1 B1 B1/B2
. w( d0 {- D2 ?+ i9 v3 n" `
11
% {+ G( x' c* f. M( U" }; \
12 3F
8 O: n0 N# Q; S9 k! v# B' u
13 3E 3E 3E 3E
S4 [( j1 Q- G
14
; u; H; |7 L! q0 w y
15 3C 3C 3C 3C 3C
7 E3 L' c( X' }
16 3B 3B 3B
5 `2 ~/ _% W1 h" D0 ?6 t
17 3A 3A
' G0 `* H" f# ^' U2 f
18 39 39
8 x. m1 a' f( `2 i) D8 z8 m
19
' ], n/ ?6 U8 w- T6 ^1 K+ o2 d
20 37 37 37
" H3 m' R* @9 t7 L! O, ]( q
21 36 36 36 36 36 36
8 U, m; N. @" p, J3 {6 J1 E4 Y
22 35 35
9 _) |, [7 T' c+ v2 J+ E, G5 a: l
23 34 34 34 34 34 34
) n5 c9 K2 Q8 ^0 j7 v
24 33
4 r% Q+ c& U5 r9 R8 K0 _% g
25
: k& z& B$ w( x( X8 o' Y
26 31 31 31 31 31 31
. L* S5 Q1 }( _. @4 [6 y
27 C1 38 C1 38 38 38 38 C1 38
, @/ w4 B+ i2 i4 V {) }# h
28 D1 30 D1 30 30 30 30 D1 30
: y5 B& G' J% p! k
29 E1 E1 E1
& I2 i( q* ^3 y" R/ @9 I, Q
30 FE/FD FD/FE FD/FE
& v6 {1 X6 {7 Y' f# E
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
( F ?4 _/ Q% m; Q7 t6 i
) V) ~& F) B* C2 X9 L
255 50 50 50
% S2 x; ~8 u8 h
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
* q) z( C3 b7 c" S. V
4 A$ \! f% A, ?& V( n0 d
机器配置:
( t7 A" N( e: N0 Z: ]" e
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
2 D+ Y9 t) h/ w0 l
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
) e5 l1 _0 L8 [' K
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
5 `( t/ L0 j$ x# \ i
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
; J7 e" Y8 V. W% R6 S4 s0 _
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
# S5 | c$ z& Q* t# f% U" U& W
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
: Z4 ]* }& d/ f
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
S- ~) H' U6 \) K4 x+ L
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
( d: x* U- ^. t* p {& g
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3
& V: y6 J G0 c+ P2 ]
4 J) \3 U8 t: K8 x4 c7 j0 [% K
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
) J: m. ^8 g) T, E
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
/ \, i2 q3 e5 ~, v8 [! c
:idt
5 n. o3 w9 W$ v' x+ `% D
Int Type Sel:Offset Attributes Symbol/Owner
0 \1 M9 G4 d) W+ T
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
! h4 ?6 P5 P+ U$ b; C# \
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
3 s. J& V# V- t" Y9 f4 ?
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
) m4 Q' @1 B& S
……
9 I$ C& b; _" k2 c- v6 o
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
/ t* j! I M8 M1 J3 K+ N9 V
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
- n9 A0 A8 q3 {/ W/ z; p1 p
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
4 O/ [$ l6 [& x4 J& B9 A, I$ `
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
8 ~) X$ R- Q0 L! V: C, d; ?& \+ Y# R
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
$ m2 a, N; j6 i1 N# d$ A
9 O# i- K, @) y5 d- C( G' O g
' J. c$ P: ^- J/ C4 _* ~! l
7 ^8 F! P2 e8 Q/ R
提问:IDT表的Base Address怎样得到的呢?
9 a# U: p# M& e2 k+ M. X0 B; f
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
$ {4 R3 D6 R4 m$ d1 o
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
/ B2 ~5 x: H# ^ Y3 u; D
SIDT - Store Interrupt Descriptor Table Register
! Q; l! Z* R; R) D8 O. S
+ g6 s* ~- h) g/ N3 v
Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
1 \( u8 T! q$ \
. j! m+ `; x& i. K' U; K
UCHAR *mBuffer;
' j9 B9 g# r7 ?! s5 I
ULONG dummy;
& ~, `% Z8 h& E
……
& @8 ]! y9 I( e# N% d. M: j1 K
_asm
, U" s/ A) t* k& K, j
{
) u2 Q$ L" m) b
mov esi,mBuffer
' L6 h7 n# k! O- \% o
sidt [esi] //load IDTR to *mBuffer
7 P( x- C8 I( }- I) @
movzx ecx,word ptr [esi] //load size of IDT to ECX
5 `. M0 c$ `& S& ]6 S
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
2 } Z; i8 R. P0 {
inc ecx
3 l2 e, l/ Z! y7 L
mov edi,mBuffer //mBuffer is the target
. u+ v M. f: S' c h, z
mov dummy,ecx //store size in dummy
1 A; l% `6 U% e; b4 f
mov esi,ebx //store IDT Base Address to ESI
$ h& H1 \, W! K2 d$ O* J4 P: J8 m; O% Z
shr ecx,2
$ e% C. D" {1 @/ Y
rep movsd //copy IDT to mBuffer
9 c3 P, ?3 c, `( m! l3 b' H
}
1 v0 I/ M8 R( z; {0 z# M/ f
( j7 {$ E5 Y# C5 |. p' r
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
( }% \8 e8 o# I
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
+ v: W t: d5 T# Z& N5 O" b
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
3 W& @( a7 m# P* `/ K+ i
(2) Interrupts---> New
( A' Y/ {' q, V/ ]0 A
Interrupt Number = 14(或别的) & Edge Triggered & Shared
6 P/ t: g4 @5 |: u
(3) Listen to Interrupts
& }4 l) N. J5 Y
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
- T6 E. `0 Y8 }" b- O. N& H
(4) Generate Code
3 F% J" X" E# n* N$ Y2 v+ ?
(5) Build and Run
5 B4 B% [: x/ N8 b0 g% |
(6) Enable Interrupt
! X% g) J6 o |( Z
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
9 i& J" G- s% Q% X
:intobj
$ {4 D3 {* K2 Q
Object Service Service Affinity
3 B% F" O) A J& ^
Address Vector Address Context IRQL Mode Mask Symbol
* D: R3 Q6 j. l
FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
& y1 S( f& p0 |7 H
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
6 g+ w* {+ b! S1 K v* _8 {
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
3 t! D( v" [! M, p2 r. m
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
! X# O: }4 A( F, a& j+ D0 z
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM
4 X0 m( e) Z% I! U; {
NTICE: Exit32 PID=70 MOD=ps2_diag
5 X; D" b; S' m( u6 i) n+ ~( Q, E
; U, `. {& ~# O7 k+ q) S
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
; M) w$ h6 ?" `. k: `/ W# [
4 u' @# g) M1 l
1 }8 y2 q2 Y Y1 m$ }# [
1 G9 o! Z) L6 I
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
y. r- E* I4 M9 d/ \
* }3 ]1 I6 G! m9 h
一个问题马上被提出来了
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5