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

我的地盘我做主
该用户从未签到
 |
文章内容:
" K0 R, k6 r3 G--------------------------------------------------------------------------------
- P' ]& i) @. Q; {, H; ]) i作者:suxm < suxm@nsfocus.com >
- k' b9 ~, t2 O. v主页:http://www.nsfocus.com
/ }! P. D- K8 _" S日期:2001-08-13
3 v0 c5 [: C0 h% D: _) G( B: J) N% n: V9 u) l
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
( E( T8 w4 w" D; b! @& a2 j# f迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
# H: I6 h" `9 i9 I让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。7 h, R2 O: a/ f; b
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
2 B/ M( P3 y8 N0 D3 K/ E/ z9 O; ?
( G+ z+ Z" ] r% n% k, r
3 h1 g6 a4 j* W6 q4 q1 x* j, r+ U& L4 k; [6 Y
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
1 ` N" e. r7 CTrap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。9 H- c+ N( J" ~8 h4 K
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
* V* Z. r% z! U' g( _int51 dw kb_int ;keyboard-handler offset
. w; y/ H w9 [+ l8 tdw kb_sel ;keyboard-handler code selector6 w' A0 `9 S4 W
db 0
' D. X; z' F+ r4 ndb 8eh ;386 interrupt-gate6 n. |3 [' M9 d5 h5 j. G; L0 l6 U2 m
dw 0 ;offset is in first 64KB of segment
% N* B- B% G' ?) I* D4 d是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
) q& y$ s1 U- {, |这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。4 e D( Q1 E( f' ~, t# ]3 `
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?& @6 B1 x9 V' M3 P
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。) z- u) v9 b! P: q6 ?; b
在单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, y# A% v- X# x9 ?
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook4 K% p7 L! H' }; z* ?
IRQL & K2 N) E: |7 S
1 3D 3D; r+ W4 O4 `( F4 I. C
2 41 41
3 B, E( n, d# _! Y! a3 {8 V' M3
5 P6 p- t, G! B, B+ U" k4 51 51 51( S0 B: X1 E; m: Z7 u
5 61 61 61/ }' [5 `1 |3 u. J0 c
6 71 71' |' n$ A9 \% g. f4 b7 k6 M
7 81/82 81/82 81
' s | T0 I4 \4 _$ F# b8 91/92 91/92 91/92
& k3 q9 b$ I1 e9 A2 A2 A1( Q y# V; i0 `- j) @
10 B1 B1 B1/B2
8 I2 Q5 e7 d3 i5 o" n112 o x2 c; x- v: ^
12 3F
0 P! [, ?6 X ~13 3E 3E 3E 3E
5 s1 ^; ^ W- X6 u G" \' ?4 E4 X141 W/ N8 p& K; X" ~/ }- `7 i
15 3C 3C 3C 3C 3C3 R- O. r' l- H2 L9 P! R
16 3B 3B 3B! D$ V7 c$ m {# c' k U& b
17 3A 3A
$ H0 W! I8 n: c! E18 39 39
# U8 W% g' q' u% ^19
* T, {+ c$ E$ y+ f4 T. K9 A20 37 37 373 b+ Y6 `/ J1 b: Y: o4 b
21 36 36 36 36 36 36
2 b7 a* l/ a( a2 Y22 35 35
9 f, @7 D. ]: P* j# p- P/ ?23 34 34 34 34 34 34; M( Y$ m' m9 M6 y
24 33
. k7 J9 z) n8 Y25
) x- h1 Y! W" u+ S6 G& y4 F26 31 31 31 31 31 31
$ m$ {( ?; z1 M) B5 b* S' I- y- g27 C1 38 C1 38 38 38 38 C1 38
% n* S% ^( W- L# v) k# a2 W: o28 D1 30 D1 30 30 30 30 D1 30
) v h0 @) d9 i, }# L- S% m/ {3 d29 E1 E1 E1
6 k$ }: R: f6 O( f* R9 y$ G5 |/ y1 o30 FE/FD FD/FE FD/FE
$ e2 p% j4 K) j$ c6 k31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
6 ?! E0 z( ~" s$ g
$ |: _" ?- k2 [5 r4 f7 I255 50 50 50
. Q/ E& l$ H2 {# c6 `! bDPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E9 b7 t L/ j% r4 Q( l
0 t& f+ t! p2 L# U4 I
机器配置:
7 |' j; F0 t1 n! V$ U3 `5 ~ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3! P, ~7 ^+ v/ V0 Y2 v
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
4 s4 u3 E% o) k& X; aBIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
/ J7 w$ _ |" D( L) ~Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
$ k6 M/ h, L3 ^( j, Y7 [3 oStat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
5 }* _+ A+ h+ H( k8 U6 M0 k) UStat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP26 P3 \( j, D; G3 O0 n( X
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
% D5 W& g. ~' \4 nSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
4 b9 c1 j- f: x9 KNotebook - Pentium 200 MHz, NT 4.0 (free), SP3/ y" Y+ y4 i2 p% s H6 z
, T# [& L' d# ]5 m' E7 B( n
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。/ u2 z; O# q7 e( E+ {5 u
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。& x% n# h [* G' @; O0 o
:idt
, u! P" A( O: dInt Type Sel:Offset Attributes Symbol/Owner
; z: ?5 y5 f4 k, B6 L1 bIDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit; ]; J& ^" |% k9 f8 K/ G; T
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590: ^) [' T8 q$ I
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0' e" N( I1 J! o7 K) o
……
) J9 Y! n& G6 ^' I! b- M! p9 j00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
5 w S( @' V* U* n; D& p00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+0924 V# U, D4 a8 s
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
, Y8 Z- X/ F/ \" C5 P& \从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
2 i% Z' [0 M: o/ c* ]" [, A8 r在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。( j8 G7 u' D$ J. z( L6 c) B
, C% L2 Q. _5 {% @ e6 ?' p& c- W
6 ^7 P4 [' D g; D6 F! o" A' V9 [& V1 D2 M* a
提问:IDT表的Base Address怎样得到的呢?
; q: F7 c7 a2 p; x) g& D答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
6 D+ y8 w1 m4 f. a6 k1 d9 Yhttp://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
$ d! s# e' Z# m2 e5 Z6 pSIDT - Store Interrupt Descriptor Table Register
* t# v7 N* C( U0 D! Z' U: V. `% o2 Y
Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
3 g$ Q2 ^9 U0 O, W0 W
/ e5 X) D# {5 A2 }9 x' PUCHAR *mBuffer;
, D8 O- m" B5 {; R* P. TULONG dummy;
8 B$ a# y1 i" r0 `! l……
- ]0 {6 v! D/ t_asm
) R+ u9 G" K g& }{
4 ^+ ]- Y/ a7 i' `: I6 b8 nmov esi,mBuffer
" e1 z5 ^( ~0 g* }/ l4 r* Csidt [esi] //load IDTR to *mBuffer# A3 |1 n: R6 n ^
movzx ecx,word ptr [esi] //load size of IDT to ECX
$ Q" f: v4 ?- `' umov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX# A) e( w+ e% F/ H
inc ecx
# v; ^3 t6 s8 z( b) p' |: |5 jmov edi,mBuffer //mBuffer is the target% e y2 r4 f! Y5 S
mov dummy,ecx //store size in dummy; l. y' y! M7 Y% w
mov esi,ebx //store IDT Base Address to ESI. B( {" F6 R9 l+ j0 X) X- |7 A
shr ecx,2
0 q: G- d2 K7 R! L8 Frep movsd //copy IDT to mBuffer
6 V" g8 ^8 _; `& i$ l% Z}
- N4 W7 h% a' ~
3 r0 N" ~8 x# Q! e* I这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。- _8 _+ g, r0 {; d$ J) v8 `
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。, w8 G# K: V7 `9 w- }! z
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD. x7 V& |" u2 d9 G# r0 Y% B+ S1 o
(2) Interrupts---> New
W7 I% U) g% n; U- uInterrupt Number = 14(或别的) & Edge Triggered & Shared
# Q" \* A4 N6 _# s/ A(3) Listen to Interrupts
* k ~& `% p0 U6 B如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。3 @, [% M4 O! `. F4 P
(4) Generate Code
( Z% t; I0 v# Q. K e1 E, f$ O: }(5) Build and Run
4 m% y) ]+ X* C8 M6 H% M: T; p( N9 F(6) Enable Interrupt
! n8 F# a& ?! G! M& u(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:, L2 ]1 B" M1 }4 b
:intobj- Z0 e' X0 f0 V4 v, X# x D9 B
Object Service Service Affinity
1 r! ?; t4 f/ y) wAddress Vector Address Context IRQL Mode Mask Symbol
( K7 ^3 y% w2 d+ s$ ^FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
7 e" F" x! i0 `6 Q% cFD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C0 @/ f( I6 t4 K3 J7 J e
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
7 l( ?7 t9 t& x, x/ `! M TFD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
) M2 x; l+ `. O% w/ [. a& }0 pFD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM
% I: U& x2 v% h3 _NTICE: Exit32 PID=70 MOD=ps2_diag
+ `7 G6 w& W4 P* V: m, u$ E( q; q, E! }1 @; U' f$ j3 s* R2 D. y
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
- |: r5 {/ z, s+ o7 ]/ |3 x; m6 i. c* a2 `- j0 j9 B0 T4 c
+ n2 s* C/ I5 ~) u8 X5 G: A$ B- z* h/ Y! r! e6 C9 Q
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。& I" j X6 K, ~9 ]( r7 g
. y$ X3 `* U2 }' F9 G5 E一个问题马上被提出来了 |
zan
|