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

我的地盘我做主
该用户从未签到
 |
文章内容:1 ~% y0 e- k) f; `% o2 }
--------------------------------------------------------------------------------
$ H$ g/ O: E6 t3 g) U& o作者:suxm < suxm@nsfocus.com >
* O. g) X4 l- ^# t" @/ [主页:http://www.nsfocus.com
# | `: @) q1 _! U. Y$ f/ U5 `日期:2001-08-13
/ k* F$ U9 l! q2 K i+ S) v( K* n& \" {
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
2 b5 U h3 b" V5 Z' T迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
9 Z9 j; J5 G* _* o: h( a让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
: ]+ N) a- g9 U* bWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
' j. e2 U p5 Y$ m+ d" J
) B8 ?( H3 u5 ^& `7 k" f* o" M
1 f% Z" K* [) z0 T% e) P1 ?
5 K. s' s6 r6 O) T其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。: E0 F; J" b( k9 t; \" U. W
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。8 z) J2 B( S. ^. Z$ f
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
) N# ]/ Z) b. J! q6 T& F1 fint51 dw kb_int ;keyboard-handler offset! M4 l+ j& a* D$ p! G( {, W
dw kb_sel ;keyboard-handler code selector
2 {: |. \) ?, p+ Z; V9 p V$ Gdb 0 ! P! F6 g4 q7 @8 V$ S/ k
db 8eh ;386 interrupt-gate
. q; J" ~. i/ A3 D7 Y% w7 M. ?5 w5 Adw 0 ;offset is in first 64KB of segment/ m$ Z5 y- k8 e* r0 x
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
7 a( E. X/ @! Q- b这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。1 j. M2 e* T8 r6 k
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?* v; u% Q5 P$ Y u4 D: _
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
# g* y4 x8 Y6 H7 B: Y' ] m在单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的情况是大不相同 的。
# {( K; m$ [$ R" u; b: UAladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook! ^% @9 y1 T( m) a6 Y" K
IRQL
% m( a: ~( g$ [* F/ y1 3D 3D
2 T* x7 F V3 W" }2 41 41
+ q. J4 a8 z c2 J) k4 j3) G1 L& q9 H/ L9 _
4 51 51 51
* g7 {! p& w% ?/ e+ H6 S5 61 61 61
9 s) \- ?3 j z6 71 71" V8 C% t' V0 B7 r
7 81/82 81/82 81
- M3 Q+ P3 ~+ `) C8 91/92 91/92 91/92
, R/ X6 T. j% p U1 j9 A2 A2 A1
! I5 h( i- n% d# S0 J8 h10 B1 B1 B1/B23 n |3 E+ _- i Y2 z/ }5 Q& T
11
8 _& O1 o5 f M! @: j& Y12 3F
/ |4 E. {. C2 M% o4 R9 A# m+ G13 3E 3E 3E 3E
# z% `1 [$ s# \6 h3 N5 @7 g; V8 r! w14
5 I: x0 x% J/ _15 3C 3C 3C 3C 3C" r7 j, N/ M; m" m# h( B" `, c# I
16 3B 3B 3B
9 b8 x0 M7 a6 x( l3 V17 3A 3A
4 `& l# |5 L7 p V18 39 39
- Y5 E @/ u' l) n, y- ^5 d19
X9 [$ j9 X, \2 r F8 J3 h20 37 37 37
+ K w" p' d+ \' Y# ~9 s21 36 36 36 36 36 369 ~, M" n7 d5 Z1 U* }
22 35 35
2 \, i* j. f* q* v23 34 34 34 34 34 34
8 E0 v- H4 _9 q$ g24 333 _8 i4 A1 }- T C+ V# N
25
o: x- ^# @0 f* f. G1 x26 31 31 31 31 31 311 B* r9 l. Q. t5 D, c
27 C1 38 C1 38 38 38 38 C1 38
! v% U* b+ g1 d28 D1 30 D1 30 30 30 30 D1 30
. X% `! p% C; E6 N3 C# {( @ Q0 y: O) b29 E1 E1 E1
) w* ]" t3 U' x* W" D4 Z30 FE/FD FD/FE FD/FE, m& P. z. i) r% z# H
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 328 l# g; _) N' N! `1 U
$ Y- |) |0 c7 |1 P
255 50 50 50( {+ R. B, U. Q# [8 c, {0 S
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
; a" v% d, z8 e2 e2 f! {+ Q" \+ M. I$ @% n9 y6 L
机器配置: }& @2 L8 {9 Q/ ~3 q7 I# d4 _$ r
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP32 T1 V" S# S! w+ {2 j
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3& k, J$ a! p3 m% ~ U
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
3 {' s9 Z% ~; r9 @; l6 S mHugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1# d/ z; s9 F# [4 X* j9 D+ k+ ^" L& B
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP36 J' O x; a1 G$ y% I
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
+ H/ M& a; i% Y8 ^& |2 wOTTO - Pentium II 300 MHz, NT 4.0 (free) SP4( N+ V- W. v; O$ [" k
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
- f" y7 O! Q. O' P6 ?Notebook - Pentium 200 MHz, NT 4.0 (free), SP35 k. w8 l3 J4 `/ P9 ~; R3 K! P
/ w4 r" v: a B3 @9 O' R7 `; f
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。5 T" U" w( o3 s1 Z3 ?" o
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
+ f2 A: m ]. \:idt
7 `6 Y1 O. h- F$ tInt Type Sel:Offset Attributes Symbol/Owner: H$ A. P- C+ K% Y1 k" ~
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
4 C) [6 S/ _* c3 a7 Y2 m+ T& H" Z0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
$ T; z3 x! V1 A0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
1 V) V# H$ Y/ a" Z" Z5 i……, t2 d u" P4 o ^ ~
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092. B- o+ U$ X4 B% j
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
6 r( E8 `0 P4 H( `+ ^2 D00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
5 t% |4 b# @6 K% U% i) c从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
' ~# ^1 D2 L. N/ z* a( g在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
8 e8 \* Y, m) A, ?: a1 J- Z- l7 g- C( ?$ i
+ H4 d/ s- Q* B9 C! P
/ q4 Z- ^# {2 q8 U' r5 v' Y7 I3 W
提问:IDT表的Base Address怎样得到的呢?/ a3 y% ?8 @4 ^$ q. \/ o
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
) I5 z' j. i& S3 p- lhttp://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
. L$ w4 T! ~+ BSIDT - Store Interrupt Descriptor Table Register! Q2 n5 H. p+ v
/ @" _5 w* m% ?" u( D G5 \' o
Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
: o' T( N+ C3 d2 [* r$ e( {+ q, P# T+ ]5 c9 ?7 ]7 N
UCHAR *mBuffer;3 z4 o7 j/ V8 l5 b7 P4 u) n! T+ n, y
ULONG dummy;
5 v. S$ A8 A9 Q+ |8 ~……1 O8 v. F ]6 p3 t2 w$ G
_asm( v- i# b8 O m: [. {7 X$ b
{
4 s! n7 s6 J2 l& w! q% V; l! Ymov esi,mBuffer
+ j5 v* s3 [3 k( I- {; E0 R/ @sidt [esi] //load IDTR to *mBuffer
8 H6 ]2 b i* c7 n3 Emovzx ecx,word ptr [esi] //load size of IDT to ECX
% V+ ?' U0 c J0 m3 Omov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX- g' B$ c4 q% Y, h# f
inc ecx
+ L- f0 N1 e8 Y* K2 m" gmov edi,mBuffer //mBuffer is the target6 s. b9 y! k" ^5 p, `* @2 u- J( C9 t
mov dummy,ecx //store size in dummy( S6 `% b! o! d- H v7 E5 s' U
mov esi,ebx //store IDT Base Address to ESI
; |- g h5 n0 ashr ecx,2
6 x+ a" P( |& a: |rep movsd //copy IDT to mBuffer
9 c& J/ E1 ~4 z8 G6 P8 y}) g" Z5 O6 u" M) B6 b
9 E F. C2 N" d0 R# |9 ^/ ]* ^
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。% \) G2 z' y1 b) h& W% a$ A5 F: F% ]" ?
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
9 V1 h8 ]% o; b(1) Windriver---> Driver Wizard---->New Project----->ISA CARD) H8 t! t7 D) ~9 u x* \6 a% M5 V' u
(2) Interrupts---> New
: N% O3 K9 S) H7 F* ?Interrupt Number = 14(或别的) & Edge Triggered & Shared
1 d; H0 h* S1 r& g(3) Listen to Interrupts
; L/ E: J |$ F# N6 j1 i* `如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
- m) Q7 Q- w1 u1 ^( u3 y# Z(4) Generate Code
' e+ h0 E2 B4 |4 @5 ^/ H7 p3 g" c(5) Build and Run
: J$ A# S6 E$ y(6) Enable Interrupt$ B7 k' t% f/ }" B
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:7 k7 O2 M2 b7 _( a) ~6 y
:intobj, a% W X0 d$ t" V) F0 a
Object Service Service Affinity
9 \; f A) |$ y. A8 ~+ |Address Vector Address Context IRQL Mode Mask Symbol
: C7 q+ R: N N1 O0 B& r* qFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600; R- r+ a5 X, n* E( O: }& o
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C# k0 A* R, w8 @$ `" P! D
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
0 r% i: c: N8 g: E2 c# OFD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
4 w' @5 S9 R4 q" G" ZFD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM! X$ O% e' N2 X2 Q* E* t4 z+ R
NTICE: Exit32 PID=70 MOD=ps2_diag6 Y/ V: r1 I5 ?- ^
8 y1 _* U' L6 U' a/ V% ~
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
/ k6 ^7 }% x) Z9 Y- l7 M( m
6 Q% U+ v; z& O# `: W) G2 {
) L2 J8 y- A' [& m3 k! F8 j( H% i ]" ~ A8 t5 N
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
( D) G! }9 W. A( ?0 x7 W+ S& a; E3 d$ o& V) F1 c6 d
一个问题马上被提出来了 |
zan
|