数学建模社区-数学中国
标题:
关于Winnt/2k IDT的一些思考
[打印本页]
作者:
韩冰
时间:
2004-10-9 14:29
标题:
关于Winnt/2k IDT的一些思考
文章内容:
0 e9 E- o3 D: q7 I$ } H8 e* l/ W {
--------------------------------------------------------------------------------
3 @# ~( ^- d6 \+ B9 V( q* \
作者:suxm < suxm@nsfocus.com >
" J( K) M& b" c( |5 a o
主页:http://www.nsfocus.com
+ K; F& O+ g/ G* g2 V
日期:2001-08-13
) O+ R0 C, P# N. Z
2 _' x4 @1 A9 K$ h
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
+ f1 f# U9 N- P1 R
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
, N! w, p1 X+ Q$ j, z A
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
" o2 X% K3 p% T6 L7 G$ t
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
9 M1 f9 t6 @& ^9 m4 P
" H5 T# S7 c2 d8 ^6 M) e! a
! H- ~( f2 N4 F1 g% q1 d
0 S/ g/ [$ C- r* B
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
/ E; c6 d& M: J S4 Q
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
% U' X5 A& L. P
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
0 X- n2 _4 m# l$ E$ X) g3 }% y. w2 r
int51 dw kb_int ;keyboard-handler offset
d8 U' j3 \5 i" u* q
dw kb_sel ;keyboard-handler code selector
z, [/ h0 e* T2 y; O
db 0
7 F. {( ~, S# v, h
db 8eh ;386 interrupt-gate
5 A' K/ Q7 B K& b& l4 m# _
dw 0 ;offset is in first 64KB of segment
: X& ^* |; f' P) \( e
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
8 }+ n0 [: T4 V. \8 X' @# d* X8 J
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。
! q) t) o! \5 V! a% L/ z
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
* o8 [( B% V$ s+ A
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
! a7 L/ {& p7 L% W9 ^
在单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 V& u- C4 h3 Z1 w3 y
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
3 y; J( n7 [# F7 h6 }% i0 F% b- }
IRQL
, I4 ^; W6 Z9 ]9 Z7 K9 P2 e
1 3D 3D
' \7 u0 J* ]* b/ n
2 41 41
: ?( v( `# y# Y& C5 b
3
5 t9 X$ _7 l( }, l# H$ [
4 51 51 51
. l& J8 F5 M) B6 I1 k9 H6 d4 k+ [
5 61 61 61
, {' p: {- h% H: i3 Q7 R0 i( h
6 71 71
, x: f" H& S \: ^
7 81/82 81/82 81
/ i% o0 |4 f% U
8 91/92 91/92 91/92
7 @' B. ?* d9 [0 d1 V" P. i& o
9 A2 A2 A1
2 w6 r% A7 ^3 ^; v
10 B1 B1 B1/B2
w) X' r9 ?2 o8 w K7 |
11
J: k6 @! M( \, k' F7 Q
12 3F
& d/ y7 [: c# u; m
13 3E 3E 3E 3E
5 \/ G; N- V9 ?. ^& r
14
" T2 f# Z* K/ K& ?' f) q7 p# x Q
15 3C 3C 3C 3C 3C
* y/ M {/ ^0 h- n0 P4 s/ E8 l) W
16 3B 3B 3B
# \9 C; v; h! P- i( ?" W7 ^
17 3A 3A
9 r7 Q @- n" H( i+ Z
18 39 39
. I; y1 H* C6 @6 a* h
19
6 n7 p# I# U# o* F) n: `* O
20 37 37 37
6 g5 Q; W& `- R
21 36 36 36 36 36 36
& X! }9 T: j7 |3 S
22 35 35
6 ?( V! @! [4 i
23 34 34 34 34 34 34
# ^3 r$ ^+ C' D) l: z
24 33
5 v5 Y. }! ^+ |# m) D9 S
25
* b3 G* H/ S6 q' l! E: K) M \
26 31 31 31 31 31 31
8 @2 o0 E) T9 o% u3 N. R
27 C1 38 C1 38 38 38 38 C1 38
; H& A4 o( X7 i8 s9 Q" i
28 D1 30 D1 30 30 30 30 D1 30
4 Z. U! p0 _ B5 M) C3 x
29 E1 E1 E1
h+ ]' Y; d% v6 h; C& y5 S
30 FE/FD FD/FE FD/FE
; s- a) h: T# M& A5 Q+ t
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
, k( ]( t8 V; {3 w: J0 D1 U
& m) T8 w* r8 \% P1 Z
255 50 50 50
- U' e3 F( U$ v3 V/ T
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
6 }" u$ G1 O$ w
2 ` {& F, t$ H3 E* O9 I
机器配置:
, N, @, p' G4 V8 R! B4 S1 L" N
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
2 ]/ s# o, ~ e. C% U* z, c: m
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
. d* z0 K, t* c) B! J6 |1 y6 a2 F
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
. S# Y& I5 v/ h& |# j
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
# ]. i+ }. W7 O' z
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
% a4 R6 T) X9 q8 ^( o! S
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
0 g4 g+ x9 g: S& V
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
6 \, V. _0 o4 w8 N
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
. }! t! T: `/ i5 |
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3
9 U, f9 V4 t1 G3 U4 |9 N' G
8 Z6 w* _6 |' w8 F/ J; L" K
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
* i# u4 H( ^3 _- v# r
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
% _- |. K/ x" H9 V! y
:idt
& r) n4 Z8 }$ U; ] n
Int Type Sel:Offset Attributes Symbol/Owner
/ E1 z4 l2 o! c6 X& w( b
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
1 L) b- }4 t3 U% `9 E ^
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
' Z- O2 M2 A# G! [
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
6 R- F8 D# X/ {+ s" I$ ]
……
6 r& f/ {: h" p+ z3 f
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
% g2 T, u/ X: u- P% w; [4 d
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
$ J) u4 n/ s) W0 F
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
/ h: Z) ]( w) y
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
: P7 m% N- Z; w; i: X/ k2 S
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
! u$ ~8 r8 h) B5 R& @
" S" l1 `) `9 n
' G( Q# C7 j3 l; I
8 u; u* ^- z4 _4 k5 O, {6 o( C- e
提问:IDT表的Base Address怎样得到的呢?
3 y' S$ Q2 h L* V/ T: P
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
0 m3 m1 {: {7 P0 z( @9 I$ U
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
" o4 L8 r$ q+ P- b+ {
SIDT - Store Interrupt Descriptor Table Register
4 I7 ^6 C* g z
1 l4 t7 n4 u6 L }2 {8 T
Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
. H! \2 @! \9 N% L- ^
; _1 d' v" r l8 w3 j
UCHAR *mBuffer;
$ W0 z. J% D- B6 i8 g
ULONG dummy;
; r( Y( a! }. G
……
" O' w, X8 F6 E p# |
_asm
# b. w: I* R3 Z7 _1 \
{
1 x) O( [8 K; _/ _& j6 ^4 H3 C
mov esi,mBuffer
- e3 k/ r- T( f& ?, M
sidt [esi] //load IDTR to *mBuffer
9 z" q& b8 H$ }, `* S% t
movzx ecx,word ptr [esi] //load size of IDT to ECX
. o/ J. N) N/ D
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
# s- q2 F" C& I6 z( @8 j
inc ecx
( R7 O6 L8 [: U
mov edi,mBuffer //mBuffer is the target
2 d$ r4 J9 K. J" K
mov dummy,ecx //store size in dummy
5 v# m. r+ }5 r4 G# V+ ^( T6 s; ~
mov esi,ebx //store IDT Base Address to ESI
" D b' ]3 o; O) f( b) {
shr ecx,2
, |# B8 h* O9 d* a( F. j
rep movsd //copy IDT to mBuffer
: y% S+ g- k9 U y$ j
}
6 E) d+ M, J8 |& {
* V. @! c3 O7 P# ^% z2 z) y
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
7 \# L9 u. g3 G9 t6 a
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
' j! z0 ~) A; Q& F+ z7 m) B
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
8 ^9 u0 L9 R2 J9 @8 Y3 d9 x
(2) Interrupts---> New
* s* x3 {$ o2 ?& b
Interrupt Number = 14(或别的) & Edge Triggered & Shared
' }8 |6 B* z2 Z% w
(3) Listen to Interrupts
- x+ m% m, _2 F
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
+ s: Q3 {$ I& L# L
(4) Generate Code
* y3 ~- y* Y" n) r
(5) Build and Run
/ ^* U; v& e: `
(6) Enable Interrupt
# d! h4 N1 a; R$ E& S! ?( M
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
% E9 k* h, b+ M& @
:intobj
- X5 k% z+ Z' ~! f, r3 u- N/ I
Object Service Service Affinity
7 z: |5 w; @) c) G* R$ X
Address Vector Address Context IRQL Mode Mask Symbol
; k( y2 u( Z$ N& ` e
FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
- a8 K8 t0 |. P5 D# Q& }! D C
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
8 l4 k# K: \( N* T
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
6 F; H* Q/ H: C4 Q
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
( ~2 S$ l7 t( B1 _! w6 q
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM
! ^ ?! I4 Q) B! E; M
NTICE: Exit32 PID=70 MOD=ps2_diag
# b* \, X Y8 N2 Y# h
; v1 b1 ~8 l& l9 z) l- w8 s' Y
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
* c( H8 H0 b7 X* Q
0 X6 O+ T' D* b
! | A3 u5 w1 h* h2 J9 R
3 C% k" H6 t6 O2 P
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
8 M" S; O8 D X9 A+ ?. R9 a
0 @* u/ l' n [
一个问题马上被提出来了
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5