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

我的地盘我做主
该用户从未签到
 |
文章内容:0 k8 Z: K* @* V: @# j
--------------------------------------------------------------------------------; B. q# @1 b3 |0 M& ?
作者:suxm < suxm@nsfocus.com >
5 d5 _6 }9 ]8 o. Q$ g主页:http://www.nsfocus.com
/ Y. W* Y/ f# {日期:2001-08-13
; m* Y3 {6 J9 }* Z; T+ [; ~
3 u) {( e/ b. |0 S; s) Q% s几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
& n: |. m) ~7 N/ Z6 y' O* |迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。8 F0 n* ]7 q. I! Q
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
# r) K9 W; ?' _3 l0 A/ X' T* yWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),1 K! U9 V7 S* _
/ ]( O( A$ Y- z7 y$ i- T
2 C M9 x0 H% m( Y4 t; t0 g5 ?
# I- Y- q5 }) y其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。
2 a! T7 _1 Y! A$ uTrap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
5 |& c7 s3 O+ D; g# w2 b) W& \下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):
$ F; ?0 d8 R i4 L3 B3 z# K. ^int51 dw kb_int ;keyboard-handler offset _; U, S j7 e M3 ^1 h9 L
dw kb_sel ;keyboard-handler code selector3 L u* x- R d( v2 |! I0 e3 B( s
db 0 , Z3 _! ]1 ^: c R# A& B
db 8eh ;386 interrupt-gate+ t: h/ h' @0 L& R$ M: g
dw 0 ;offset is in first 64KB of segment
: V4 s# A# D* ?2 a# @: ~( T: d是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?# h' h2 S; R/ u+ I9 y* r B
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。1 H: { k' V0 X8 m1 H+ z* i5 t
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
R: B: `. |9 |( V答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。8 p0 W1 W) o8 i( @$ 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的情况是大不相同 的。7 ^( @. b; o, ^* u& {# g' A4 }
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
6 `# m8 K" f& y c! x) H+ fIRQL
6 T% \1 i# x7 R4 K1 l! ]1 3D 3D
: h/ o3 V0 H( {6 a7 |* I" k2 41 41
2 L2 c3 ~1 E% J; y! v1 f9 |9 C4 |3
( c, b+ ~( ?- \& d. J2 r7 {" o4 51 51 51: Y+ |" s* t8 i7 n$ \9 i# J; Z
5 61 61 61
, ~( ^* B. u# j0 o6 71 71
, _" K7 Z3 |0 D' V7 81/82 81/82 81
* ]$ y4 Q+ x7 ]/ u8 91/92 91/92 91/92
6 v1 g, N& ?" N8 F9 W, ^: n9 A2 A2 A1
% ?' C3 d. l/ B; N2 Y10 B1 B1 B1/B2/ y* [$ |) z8 M. j) r
11
: n, q, R0 X* ]- \12 3F . O8 d, E, g9 G2 `8 \2 ]
13 3E 3E 3E 3E0 T, m6 s' L: }% S/ F4 K z
14' P% N% H* g" _* n& @, w' k
15 3C 3C 3C 3C 3C$ |8 L! [* g8 d3 u# X5 D0 |6 y
16 3B 3B 3B5 D3 V3 r# k; t' ^9 t7 C
17 3A 3A/ e& B4 Q$ T% f a4 E; J) S
18 39 39; P4 v+ N3 U! b) C3 n. o
19
& D( e" Z: j7 T8 C& W8 Y1 K20 37 37 37
6 H+ W8 G \/ {8 s: Y: m: I- \; M21 36 36 36 36 36 36
( ?& x, y8 w' i22 35 35/ B8 T# L/ R* D# @0 ]' T. f
23 34 34 34 34 34 34$ B3 _" z D* G4 o9 I
24 33
4 w; S7 U; W, ~, F& a2 D25 " d7 r9 k* Z2 K" Z
26 31 31 31 31 31 316 [2 ]. R$ w0 c1 s* n$ d
27 C1 38 C1 38 38 38 38 C1 38* M# P. a4 {" l8 _: k6 I
28 D1 30 D1 30 30 30 30 D1 30% j1 W3 F' ~' y% [" u1 ]- {8 c
29 E1 E1 E1
4 ]4 ~9 _/ @( `/ r' K# J30 FE/FD FD/FE FD/FE
8 B( H' e/ V4 [* s0 ~* Q31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32 {) h) C6 l, K; L B8 A
& M- J8 [2 Z2 b O+ ?0 |4 V% s255 50 50 50
( {9 G( s5 ^2 oDPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
* M% E; b4 u( J
1 C$ q+ Y y( n% J# J, L" }, i机器配置:9 H, @! Y, p( ~# Y4 @: M1 J
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3( K: t: ]. u h- {" |
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP34 ?1 ~* n) Y, S" H6 R" d5 a
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3 V) E1 t6 M* ~2 |, B4 @1 x
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
$ N& w$ r! M2 O2 N( ?! SStat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
5 V5 w3 c" G- d" ^# q. y, U" ?Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP27 y5 f" M& D" V: M( a
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
7 h9 I4 s8 K1 z* H% mSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
; c0 v" N( r, |6 J. bNotebook - Pentium 200 MHz, NT 4.0 (free), SP3
2 b( @0 U+ F; o. l# x$ G4 i; `+ q5 E/ r* [
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。: C7 ]7 ^1 ~! v5 v8 f. V# P$ w
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
; O, C* M- r, i: _:idt( D0 a+ P4 }1 m( F0 C
Int Type Sel:Offset Attributes Symbol/Owner
; S" H7 f/ P! T6 K: C4 @# kIDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
% M" o* [& v; w5 h5 L: d# v0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+05907 C) y9 X1 U" G) F$ b
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0! b8 K0 z+ I) ~/ w3 k3 k" I
……4 O }5 D9 Q1 G2 Y) Y& r% E
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
+ F, ?" a2 i0 a% E9 M* Y00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
+ y5 q7 a) X+ r00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093- a; J( s, U, |& h
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。7 a* l2 V+ g& P$ y1 Y2 ? i
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
: z1 I4 I _$ ^* }2 }! f6 D5 \! n4 n! _9 a X
; g/ P) a2 x! P0 L! Y' w- @
% Y+ m" x3 v4 ?7 x提问:IDT表的Base Address怎样得到的呢?6 c6 U$ C) h0 s0 {% u& u
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下* u8 p8 F: f. z( r. P5 S, _: O
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:/ s/ h2 t1 O5 w v0 ]) @1 u
SIDT - Store Interrupt Descriptor Table Register* E* T, |0 q6 v, f
9 p) n( D) h) ?, U+ G" f1 lGuido Wischrop提供了下面的代码可以读取IDT的所有256项。
3 G3 S) N' M) b& f
3 E0 b8 h# z) a% L7 _' i o* ^UCHAR *mBuffer;
1 m% Z& [" x- E+ D. DULONG dummy;
( t. ?, u; W( ~* D* c……
# `8 ~8 l& W2 d: ?_asm
6 K! s7 G* d) x+ a* q _: q" y{' J0 G1 [+ F/ j* c. c0 r$ ~
mov esi,mBuffer
: m3 N$ q! U' ?- }! b: Zsidt [esi] //load IDTR to *mBuffer8 t g% E0 X& c \+ V; D8 V$ e
movzx ecx,word ptr [esi] //load size of IDT to ECX7 S P8 m) `1 j/ N7 S7 k' g# E# h! z
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
! x$ E6 u- ]0 r* m0 C6 rinc ecx ) V+ T& S% J& e7 ]1 A4 h9 r! x
mov edi,mBuffer //mBuffer is the target; k% e1 P5 W7 J( M1 C: |
mov dummy,ecx //store size in dummy0 c' A/ l- g8 T4 X
mov esi,ebx //store IDT Base Address to ESI, i6 k/ A8 Z1 g; p4 e* m$ R
shr ecx,2 n2 \; E' j6 I u: t& R8 W
rep movsd //copy IDT to mBuffer
2 f1 G2 `6 f9 i}
! O" W# M! E+ G" x
9 r& _8 G/ r1 z: t/ D# p7 Y' R这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。4 s& v) |8 w8 l/ Y0 `- ^: u9 X
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。$ B) X6 J: |& n9 I$ c+ R
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD$ n) f+ g( Y; P! n
(2) Interrupts---> New0 T N F( {. a" x, U8 j% O
Interrupt Number = 14(或别的) & Edge Triggered & Shared7 N5 ^: I, v* z. i# l# u1 K
(3) Listen to Interrupts
# U% o: O y! J1 X$ s2 I2 T7 V如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
& x5 m; N U! ^, e) |% w(4) Generate Code/ H! t O- p5 w& l) [( x% I
(5) Build and Run! ~8 e6 ~' I. U- p. Y
(6) Enable Interrupt0 P& z- D6 A4 t0 e/ z H! s
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
3 V1 ~, q7 X, d:intobj
" |/ i7 H; |) R1 ?* m9 U2 }; SObject Service Service Affinity
6 f. E4 u' e. cAddress Vector Address Context IRQL Mode Mask Symbol9 K' @. D4 t, \, x" W) ]3 r1 ^
FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
% f W$ _1 J! X7 a9 fFD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
E4 t/ W8 ~" Y+ S& IFD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040+ Q( Q/ [0 j3 `
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2* v4 e5 M5 ]" p9 j% q; n
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM K( L) @/ z6 ]7 d7 E/ i
NTICE: Exit32 PID=70 MOD=ps2_diag& m! \0 |8 s1 a
4 j% j, t8 I' D! T从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
$ z, j {: c1 J3 y/ j3 |; k( {
' V0 P# K: S8 {- b7 w. b P5 ?. C9 |
7 _3 {! a+ F; m9 ?我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
8 |& W% S) o% X& C3 w" f4 d) g! {$ j
一个问题马上被提出来了 |
zan
|