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

我的地盘我做主
该用户从未签到
 |
文章内容:6 q" A- i- N/ R
--------------------------------------------------------------------------------
) ?8 H/ g0 \! s. j4 F0 V作者:suxm < suxm@nsfocus.com >
* c6 p7 X; C1 d7 B. f" x主页:http://www.nsfocus.com
1 w5 j1 y/ |! u2 F& q& c9 S日期:2001-08-13
6 m5 M: K# ^7 H8 o: }4 P" a6 X- M9 @! k0 b8 m
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。+ Q: N0 }' s/ d2 w2 q; D
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
3 e" ~8 _5 A' I% U7 B& f, d' c让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
8 _( U9 A1 j( `, d& N$ |# D3 LWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
" c' u$ E- _# l0 s9 E8 B- C
4 X' B! M1 l8 n2 T
4 K; a: v; v' l8 O; M. ?
; E& z. N% B- m0 Y d3 |# K其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
( Y, q, N/ v& aTrap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。7 l9 ? q2 R, N( S* P. s
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):) o$ x% k1 j1 k, s& P7 j; Y7 m, R
int51 dw kb_int ;keyboard-handler offset
5 S0 B: H; [5 L5 L4 z( }7 jdw kb_sel ;keyboard-handler code selector5 ?- i2 C' h/ h7 }! p* r: g+ y# ^
db 0 5 v8 H2 d# k4 S) c) M: R
db 8eh ;386 interrupt-gate: t& r5 R! F z: O4 L) w
dw 0 ;offset is in first 64KB of segment
4 u- h* ?3 [( f7 H5 A3 ]6 q% ?! F是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?2 U! A% H# ]: V/ |7 }$ Y; ^, y
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。
" b/ V9 }" ]3 h1 T提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?/ L* |6 F( Z4 `: |- O# ?3 p) W
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。: U3 D5 |8 b# E1 S7 n3 S. k
在单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的情况是大不相同 的。
5 Z) I) K2 R8 w5 Q# m, vAladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook9 L$ t# a/ Y( l( C7 G D) Q/ A2 V
IRQL
; j* R# d' l, ^3 A; B1 3D 3D- ^4 G" F$ a/ [! U+ S s% E+ H) Z
2 41 41
/ c( @: S7 q0 S$ w1 ~3
1 E; I! l) N) `$ S4 51 51 51
- |0 c7 P! [& T5 V5 L4 l8 o; J( M5 61 61 61
$ `* w+ o8 p' n. @% l* o8 C* q1 N) T6 71 71! J2 H8 X1 r. Q2 C4 w0 c6 U
7 81/82 81/82 817 Q I6 B, z+ J& t
8 91/92 91/92 91/92" m6 W q, T/ d4 g% d
9 A2 A2 A1
4 N. l: `# @0 R+ B8 j10 B1 B1 B1/B2
: R( G& ?- p( E8 M* I% f' n: ^ k11# l& F5 X _- @5 ]% ?' d2 {
12 3F
& E- M. c% U2 C- a' N13 3E 3E 3E 3E! o6 P/ |. Z, ^$ o
141 a& R( o2 I e. U/ a7 _, k& D0 o
15 3C 3C 3C 3C 3C
) n! B" N' P7 H9 O16 3B 3B 3B( H9 H, C- X) H: J4 W8 E
17 3A 3A
! n" ?: n& ^* z; H# V$ M18 39 390 n* D* K6 o! [8 @! m
19
( z% v( A3 g# D/ ^20 37 37 371 S M5 {0 R* J$ X' a* t
21 36 36 36 36 36 367 w0 ?/ Q( s2 b" e# I. y
22 35 35: Q& q! L9 C: J% W5 r7 W* T3 P
23 34 34 34 34 34 34$ ~: m/ e7 l Y5 _6 P
24 33* w4 J# z2 [1 R* N1 c
25 : N, k8 r z; }+ X
26 31 31 31 31 31 31 P( I* L9 e9 [5 a6 o
27 C1 38 C1 38 38 38 38 C1 382 R; D& O2 I5 T9 y, ]" S; y, [
28 D1 30 D1 30 30 30 30 D1 30 ^( z ~$ O' W$ ?8 q
29 E1 E1 E1
% |, ]( \( i+ P6 p30 FE/FD FD/FE FD/FE
. ^& N: n+ w2 \ c5 z31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32" m ?# B- z, U7 i
2 f3 \/ a& T9 ~, f( A255 50 50 50
* D) G1 d0 c5 x( ADPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E8 |) J; ?. m" C! i
- @9 t. Q) P! M8 w0 p6 c) u& k
机器配置:
Y6 g; H. b7 Q; i. S: K9 [ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
+ y; r( L- S1 a4 g' t8 wBasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
8 G- _+ x; r% p0 U0 KBIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
: V S! u" |! k( Q2 CHugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
Z' ^9 g: f* | B' v$ s3 K3 N, uStat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
+ `3 J& k, V }# ?. ? wStat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP21 p6 q( h% E' K6 F
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
6 }: q* F0 [8 k6 ]3 vSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3% s: C, ~/ @+ a1 ?
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3. D, a7 R" p9 g. t
e+ C/ D3 O! A w: r在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。& d1 F3 M h6 j. M
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
0 w3 d2 N) G; M& w2 d+ B:idt
8 L4 o) t \6 A0 hInt Type Sel:Offset Attributes Symbol/Owner2 S7 y1 s, B7 S; |: p# y
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit7 {; B2 J8 g e
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
% P2 |! P, {# h1 f! D/ T0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0. G* D* @# J) K: e
……
! X/ e" N2 K- d7 H1 U9 S00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
( M& I6 F t3 D8 R00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092* ?$ \9 O) Y" }3 z
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+0930 _: ~5 J- F" N: H
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
' y7 ^& ]( G+ w( ]. j9 E( N在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。0 \4 Y5 a: G+ ]9 W( I
- S: r l, ?6 m
0 R0 L5 R- S9 U I, d0 V' O: c/ C; M) m7 w; ]5 S
提问:IDT表的Base Address怎样得到的呢?
. V- Q5 f0 n+ H答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下 S' S' R5 U+ W
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:+ j6 r1 r! _8 G; z* W; }
SIDT - Store Interrupt Descriptor Table Register
3 T* R$ {0 v- X _! h1 W
# m" f# E7 G. mGuido Wischrop提供了下面的代码可以读取IDT的所有256项。
X5 u5 W# h5 @) u5 q, K7 l( F* Y" e6 R/ K
UCHAR *mBuffer;
( ?, i6 p1 z( R) TULONG dummy;
* W6 Q3 k2 ]: c3 O g……/ t. Z. }$ e& |/ x' l
_asm
* c4 y% ? h( O! O9 `/ S{5 L3 K: m! n5 ]$ l+ U6 i. n
mov esi,mBuffer
6 ]" ^0 @% Z7 f: J* Z- N% [sidt [esi] //load IDTR to *mBuffer
3 h& m; Q- z& `9 w6 Q; O3 z wmovzx ecx,word ptr [esi] //load size of IDT to ECX/ y9 }, M* P2 g8 r! s2 w
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX' u+ y$ E/ O& o/ J2 c
inc ecx * b+ S9 P& C. I0 b& y
mov edi,mBuffer //mBuffer is the target5 t! d" p' P6 c$ h3 \& Y7 }
mov dummy,ecx //store size in dummy* C2 `, c, F# G( M% w+ I& A; t
mov esi,ebx //store IDT Base Address to ESI% Z- ?1 F# b* k$ _+ M+ }: q
shr ecx,2 ( l0 w6 P4 W0 N$ P l' I9 |
rep movsd //copy IDT to mBuffer6 h% f( c8 W9 B$ C5 S
}
9 j: i e$ o# u% Z- ^% H, U3 y5 p9 f# z
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
! E# C( c, x2 |" Y8 I5 Q我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。, z9 e( }1 y5 Z. S. S1 i
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD a9 B+ [/ g! D! n
(2) Interrupts---> New9 a7 K: n1 A3 X
Interrupt Number = 14(或别的) & Edge Triggered & Shared& b3 ?1 l o; z# U
(3) Listen to Interrupts
; `9 B" S0 D' h如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。+ C" V+ ?$ h, P1 i
(4) Generate Code3 T: `& n6 r( I8 G) d' J
(5) Build and Run% F. O4 Z- C4 U5 k% T2 a: J
(6) Enable Interrupt
- ~; Z( E5 [4 q5 k& d(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
6 H- y; t% j ?:intobj1 |& O2 k' J- A' r
Object Service Service Affinity
7 ?! V8 q% r, U' h" hAddress Vector Address Context IRQL Mode Mask Symbol! e" S3 E+ b* W
FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600$ k, x% w. r. E- }' |6 K3 R
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
6 |; w# A) N: z. i- BFD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040: u8 L9 E& Y6 L1 v1 Q- U/ [0 f
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2# r3 x8 K7 @9 K2 _1 a
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM7 l$ p5 {" z* C. t; v/ |6 s0 j
NTICE: Exit32 PID=70 MOD=ps2_diag4 n7 j% |* {# i
' {$ I" R. B7 M从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
1 C7 t. g9 m( e, G0 k4 J! K; b0 `: Y3 S/ F3 O
3 G& J( F- x3 m" e$ ?8 R/ Y7 W7 l5 z7 q# F3 D0 ]1 k0 f# W8 ]
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
' B3 i/ q8 j/ w4 Z* q" ^, n
2 t! g4 s) `4 [一个问题马上被提出来了 |
zan
|