数学建模社区-数学中国
标题:
关于Winnt/2k IDT的一些思考
[打印本页]
作者:
韩冰
时间:
2004-10-9 14:29
标题:
关于Winnt/2k IDT的一些思考
文章内容:
+ t' |( B/ j- |; l
--------------------------------------------------------------------------------
# _8 D; U2 y/ i) q6 ^5 k
作者:suxm < suxm@nsfocus.com >
1 {; J% }% g" ]- T
主页:http://www.nsfocus.com
8 T# M2 k! E! U" z6 O. e
日期:2001-08-13
$ U# R5 a8 s! l( ?7 C( \
1 \" M. q, u' V- u) S
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
1 U$ w8 @- l. M7 \& M' V2 n- I$ X
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
2 l! E* S8 q; Y# V2 i
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
3 m$ E: j! v$ V2 G: d& l' G
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
9 G7 Q/ W+ U) [5 \- J$ z# W0 \
7 k6 ?. S9 X0 o
# I4 q: x# L; R: y4 ? Z* f* w
& [5 J) G4 g$ j3 _3 j% g
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
) W1 W7 G7 d( }( R
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
( i: F3 X* m! N
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
8 P4 _, V f* a
int51 dw kb_int ;keyboard-handler offset
( J% x; P1 Z) l5 X& k" x# \: P
dw kb_sel ;keyboard-handler code selector
, a B' d: K) k! j: E+ C" d4 ?% d4 l
db 0
X- n! b( _( t% l& w4 U
db 8eh ;386 interrupt-gate
# F6 Y( v" Y. F3 P2 b
dw 0 ;offset is in first 64KB of segment
- B( p& N8 I4 d+ K
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
% X d$ P6 s4 s! f5 r$ A* s/ A( u$ U3 ^
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。
) q& U8 t2 N' _9 R( p
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
" H7 @, H1 a$ A
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
; ]) B1 a# `' Q6 a- ^2 w
在单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的情况是大不相同 的。
9 C9 R0 P8 E. g; q1 O0 D N4 Y
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
S( L! W/ P$ r( B
IRQL
! L. e- B( e( z4 F
1 3D 3D
) s- A4 V" x. C/ W, ]
2 41 41
. Q H9 C' J1 b: ^" w
3
- N" T4 e9 V6 T; T
4 51 51 51
" _! ]- z6 O4 U8 V& B) S# V, p% \/ ?
5 61 61 61
; N V6 K8 }- E
6 71 71
- b- w# N- H6 b: x( C# j: f* g
7 81/82 81/82 81
1 \+ F' Y3 _+ x: _# e
8 91/92 91/92 91/92
& o/ R: A8 j3 b# S+ D0 h8 A' K6 A
9 A2 A2 A1
# H: A2 ~" d) y' F7 R% l- A: @" w
10 B1 B1 B1/B2
9 \1 D2 Y! U6 x7 T& `
11
& x( ^$ Z1 N u
12 3F
" B; \$ h2 }" Y3 H/ V6 u: }/ N
13 3E 3E 3E 3E
6 e( D3 N# Z: h* B8 w0 p) B
14
$ f# b) b6 D! G5 e
15 3C 3C 3C 3C 3C
) E9 [& ]! M0 ?$ i: w: R* D
16 3B 3B 3B
# e# i/ B# [9 v" y* j' b
17 3A 3A
5 R( v: O6 ]$ I/ h3 P [
18 39 39
+ ~! l( Z8 Q2 N- a2 a- X3 i7 t; c+ l
19
/ {- @6 O5 N( B5 w4 v
20 37 37 37
9 @! j5 M/ Y5 p5 @$ z& @; l
21 36 36 36 36 36 36
' ?8 ~" | h6 D+ x! E( F* G
22 35 35
/ t; J7 Z5 D/ [& t; P
23 34 34 34 34 34 34
4 y, T7 ~1 o1 o. P- ~$ u3 q9 r' O
24 33
( d* U- L M* E0 {9 s& {$ i
25
1 C, j, W) m6 `1 |) P( [1 k+ u6 V
26 31 31 31 31 31 31
$ e" u! r: ]$ v6 q3 Q$ _8 y9 B
27 C1 38 C1 38 38 38 38 C1 38
4 X' j" k/ [# x l6 h
28 D1 30 D1 30 30 30 30 D1 30
# y1 X( }; T1 w( ?- M5 T# v
29 E1 E1 E1
1 k& K& [2 f6 X- Z' Z% W* G
30 FE/FD FD/FE FD/FE
+ p/ h9 Q+ ?( p. ? E0 |5 V$ B5 i$ C
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
$ S3 ^7 a. @/ z% R! `
& ?/ S8 V" u! \1 {
255 50 50 50
$ D, |' D4 x! D5 d4 M+ w! c
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
. W! I) W3 x: U5 d% Y( J
8 ^/ d' b: @- j% L6 A# {$ ~
机器配置:
7 ~# f2 b; H+ e P6 j
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
, J, K7 c8 T5 N% g. |" B( |9 H; c
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
4 y/ N, B' [: X. c
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
; h$ L( S _% }* r
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
# w9 V) C5 a1 f0 U6 v2 q. v- _6 c
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
# w% y" ~9 \- N% i+ D- h
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
$ v5 j2 H; ]+ g2 b* U+ w: T
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
1 o7 N& I2 R( _$ Q( S
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
" [ i& N2 j0 E0 s& \# c7 j9 o6 n
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3
, |- z, e0 b2 o; ?
" s2 ^5 X- |4 G5 w! a
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
5 Q$ p) w/ P9 _) d
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
7 v- i. [# I, `7 [+ O
:idt
6 i9 M4 | o, x; \ W1 F
Int Type Sel:Offset Attributes Symbol/Owner
& M" ~ U5 ^7 l E- h7 S" B$ ^% d
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
K* p# G& { _( q0 ^3 f. d7 T
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
" z1 R0 o% Y6 Y9 P2 L
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
: w! n' y( P G
……
+ L( z5 y( I. B* H/ J
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
: h1 S/ v9 y5 N* @- ?% _1 d2 T8 [
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
) M: S, U& M) a7 b5 R$ S
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
9 e _- d6 k" p
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
) h# p( O: p1 W
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
9 j4 x7 k; o4 d" [+ T
* x& M" ]# j6 c/ s' q7 q7 f* ~
& [6 o/ ?. T* h
5 P; n% O1 Y6 M! B
提问:IDT表的Base Address怎样得到的呢?
1 o$ Z! j- e) [4 T# Z5 L# @
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
& X- c# ^7 A. L2 b
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
1 W% ~! {( y& c
SIDT - Store Interrupt Descriptor Table Register
9 b' p* F& v+ S' w( P
; H! b0 _0 {4 i
Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
/ U+ M1 B! L8 g$ G! {' K8 w* u+ B' @% l
9 j/ B) ~' d+ y V8 ?
UCHAR *mBuffer;
* w! q2 g1 u# _
ULONG dummy;
1 l6 V/ J$ V; h* `! o. r
……
9 d7 a$ ^& e( p4 z" { Y$ O
_asm
1 V6 n( R: t' X9 l& U# @. n& {" d* b
{
7 w5 [- \2 J) G5 M
mov esi,mBuffer
0 @( y5 `% N6 |
sidt [esi] //load IDTR to *mBuffer
9 G* l% n }$ N
movzx ecx,word ptr [esi] //load size of IDT to ECX
, P" [2 F8 U$ J- @
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
2 o* k. @$ `, H# T
inc ecx
( d' j; v0 Q: R, M' ]
mov edi,mBuffer //mBuffer is the target
l: z1 n, g, A: Y( G
mov dummy,ecx //store size in dummy
! U0 A0 ?3 u0 Z) Q( X5 x7 m
mov esi,ebx //store IDT Base Address to ESI
t" q6 K& v" ]' B
shr ecx,2
% f8 Q4 H# x: t: H* a
rep movsd //copy IDT to mBuffer
& T y. \* A9 s7 i) f
}
3 n5 I1 X, H; p& o3 P c3 v
' q: i4 F( T* k! ~1 k* j% I* _$ s
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
/ v) O$ g" l# S. @
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
. C+ W% Q1 [' k( J: x" n/ S Y
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
) s3 B% z" ~7 B! \/ Q* \0 G
(2) Interrupts---> New
% l" D4 s/ z$ S4 L' v7 H
Interrupt Number = 14(或别的) & Edge Triggered & Shared
/ H! _+ Y& R/ E; e
(3) Listen to Interrupts
: ?" V. v' T" K8 |9 u# n
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
* V7 ?* `/ K3 y5 p! s9 V
(4) Generate Code
p% Y! z+ ?2 \& `9 K( Y! n
(5) Build and Run
1 v$ S; Y3 N; | e
(6) Enable Interrupt
' e% M; B7 H0 X" w. _% |
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
$ W; t, c' t% g$ w* ^& j+ k
:intobj
! {) H( O8 l) N1 A& d5 L
Object Service Service Affinity
8 K! O$ B6 s$ o1 F; M
Address Vector Address Context IRQL Mode Mask Symbol
4 i `, M5 x/ {4 \/ \6 w. L0 ]
FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
8 B6 U3 T% B8 A0 [, O: J
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
0 n7 l, b3 _4 D; @0 ~
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
4 c' c0 j7 l" d/ p
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
$ j- a) U. t+ t* z
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM
( s) @7 `6 d9 R1 G+ F
NTICE: Exit32 PID=70 MOD=ps2_diag
- x, w' b+ A. ]- v9 S7 a
7 g7 ]8 _: c& }: L& ?; S0 Z
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
# a/ m. q7 u7 D- A, x
' k" R% r N+ s5 _
( T1 r: [ e- Y$ W- [
. O# P& O. t; t$ ~+ \. a
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
/ P7 n4 X7 R3 v0 G8 i5 G; ^
" A0 D- v; F0 U6 M5 {9 l
一个问题马上被提出来了
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5