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

我的地盘我做主
该用户从未签到
 |
文章内容:. Q2 n( J' u% L" O/ K
--------------------------------------------------------------------------------9 E# h' e# J: Q8 A' d: g0 V% F
作者:suxm < suxm@nsfocus.com ># K0 @9 y' A' Q* _8 w
主页:http://www.nsfocus.com
; P8 ]4 F# b: E4 D日期:2001-08-13
% Y! M' U- C) {) d9 q) X, z8 Z$ `' r+ x/ }
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。
& c! D9 S4 F/ D迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。" i1 @6 e: b" v
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
$ q. U3 C' K/ r' g6 vWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
+ [' d! C9 Z! v. g) ^1 B& W, E1 J6 Y
9 p. }8 |1 d9 E8 K" ~- r2 `; W) s6 G( V9 b" v3 T/ @, X
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。" i+ C* O- ?+ o
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
! Y. O2 j: ]4 c" b1 r! [: l$ Q下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):1 ?/ v4 D1 K. ^/ o
int51 dw kb_int ;keyboard-handler offset
s5 z# Q( O% |5 q; L1 r( Sdw kb_sel ;keyboard-handler code selector
3 ?3 m( z. ]% O6 Mdb 0
4 U$ E6 S$ [* h5 b' P Qdb 8eh ;386 interrupt-gate
% v" G; M q" w. ^' y. hdw 0 ;offset is in first 64KB of segment
3 j+ J# C8 i' o% }5 ]/ K* m( H5 j是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?' Y7 L) r; h( R/ T
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。0 i+ A( {3 s" f& M, e: \4 M
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?" Y/ Z( S" l5 b8 T7 t& u! S. ]! ?9 y
答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。8 e) @# w9 @8 @$ s, T
在单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的情况是大不相同 的。9 l0 M3 t: b4 @1 I
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
# D8 r6 _. V8 V+ TIRQL / l' X4 }! X; `$ T
1 3D 3D9 y; }: S# ]5 i5 T/ }
2 41 41! h& Z; Q9 ^5 x4 h% X" K: J- `% S
3
: g+ Q3 y; t1 n4 @1 b$ \) r8 x( b! R4 51 51 51
8 r9 Z, c6 J: Z3 p0 y% g5 61 61 61/ ]( M( Y- n& L
6 71 71# n3 ~! d5 @! Y' B
7 81/82 81/82 81
6 \4 A! W1 t( r8 91/92 91/92 91/92
" \; C' u3 S& b" L4 X( Z9 A2 A2 A1# M! T4 x8 j8 ~, B& {' `) w# J
10 B1 B1 B1/B2
( I4 X; z1 E+ a# j& B+ ~2 K11
' L* ?- `, A- Y }! t) G12 3F 1 q, z$ A: \. Y9 F8 y3 G+ q
13 3E 3E 3E 3E& z5 K" Y" }" W! b+ I3 _+ U
14
1 f. k. \% Z1 X% N0 e3 x! F15 3C 3C 3C 3C 3C* w! Y ]% F8 R- Y" I6 D6 [8 |
16 3B 3B 3B
/ c4 o. F/ R# }& {17 3A 3A( ~( b3 {( B7 b2 E4 H. }
18 39 39
% E. q5 @" y, `) R! S9 V: y19
" ]4 O P3 `$ g/ a+ c20 37 37 37
; N N+ l* f8 Z$ t k21 36 36 36 36 36 36
9 F- i @) {2 }$ |* Q22 35 35
& D) X; G- K) f# i23 34 34 34 34 34 34
\& r8 c: e9 W7 Z2 v24 33
! ^& ~& [& j& a: ]' f. E$ B t25 ) A) N5 x$ q% T4 E. a" G8 c
26 31 31 31 31 31 31+ R& i. x) U; e' y* p, V% z# }( k
27 C1 38 C1 38 38 38 38 C1 384 |0 k+ [! ~! U) L
28 D1 30 D1 30 30 30 30 D1 30
; g" z8 G$ u8 R! M# b$ {; p1 w( ~29 E1 E1 E1# Z' T/ \% G+ B6 O% @& x$ ]- J
30 FE/FD FD/FE FD/FE
- Q. A3 l4 Z/ P' s31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32% z) h8 l# v* N0 N6 v, s$ {( c
5 e- s! I- F {; U; f8 N5 m. O255 50 50 50
, z% }. X- y; W5 y! F8 fDPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E, z* S( C9 a6 q" r" u4 y
, H% m* h$ [. G0 |6 J机器配置:( p& i3 m: L: Z* [! @0 s J7 ^. h/ h
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3( Q# [. L3 k2 S7 j
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
J6 Z$ X! n7 J/ WBIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
9 o9 z7 y, C/ w3 t9 dHugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1 }8 [" q5 S/ Q* j0 Q" t4 b
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3- q+ t/ w; e0 ~" u1 b3 |
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2* w n, i J W/ K1 O
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP45 j0 U! l5 q' S5 k" `
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
# y. J, |: V! M& P( t4 e, yNotebook - Pentium 200 MHz, NT 4.0 (free), SP3 d1 J7 r$ c7 R; r* S" v
: c6 l& t9 y; D. c9 Q3 Y
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。2 s4 P3 W1 v- T9 c8 G( A' W2 R
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。/ K6 t6 b0 F8 c: h3 p- N6 U6 z% `' n
:idt
# {; R: m9 q7 uInt Type Sel:Offset Attributes Symbol/Owner9 ]4 W& { a8 W2 N1 [
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit% N& N3 v2 h1 M* |* t; ^# o
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
0 h, W1 o& ], [. T2 S, Y0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
7 Y$ ~9 C8 ~- K! `7 i……6 `% }- C y5 q# j, u
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+0928 r, p" `3 ?/ T* J- C4 d, G
00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
! ~3 ^2 r0 e& Q! {& _% z00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
, o1 P5 j9 F4 ~8 F3 T从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。: g" x: e- _/ P' Y# w9 R
在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
3 i* P- H X2 r: Q m# F" O. @0 Q
: q) ]" B) R, h* v6 U
! ~9 a; l% O/ s* p0 G提问:IDT表的Base Address怎样得到的呢?
' t4 S, ~+ o+ C9 K: q答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下& A6 W" B/ ] t, ?( ]& c
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:
+ h6 C7 u9 M9 Y0 s D. U, `SIDT - Store Interrupt Descriptor Table Register8 V4 K Z w) I& g, R0 u7 W3 h
& H- w% C5 v9 M% RGuido Wischrop提供了下面的代码可以读取IDT的所有256项。
: }( D1 F7 y3 j# \( |7 N/ i! l* R+ T* A( u
UCHAR *mBuffer;) \, k# F0 |, }0 b
ULONG dummy;% i, C4 ]: f2 M) s' ~* f
……
: W# U+ e2 L, t2 E4 r b% [_asm
, V( a/ G: p- |, L9 _{
' v0 ]0 r7 p+ y: G4 Emov esi,mBuffer . ^% c/ D3 V; x7 y" B# t
sidt [esi] //load IDTR to *mBuffer
& X6 D3 D Y7 c1 smovzx ecx,word ptr [esi] //load size of IDT to ECX
' \0 u' r d U, B9 L; u6 |mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX6 |# m1 h- w+ C1 p
inc ecx
$ C* D. L1 z$ y) c. a6 ^mov edi,mBuffer //mBuffer is the target% i' u+ S% Z, C0 W% i
mov dummy,ecx //store size in dummy# P% z, v0 \* T- C4 F) z7 v
mov esi,ebx //store IDT Base Address to ESI5 O9 R9 i2 A8 f' i/ W
shr ecx,2
9 A# O! \' e' }rep movsd //copy IDT to mBuffer6 t: `2 i7 N$ P, J2 K2 Q
}
! N1 L" d& h% {7 B" v& E# {
% Q+ c7 h0 r8 u/ `- P这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
5 q* Z0 j1 F$ C# A3 U; P9 k我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。( z3 |4 g7 ?8 A) u4 L, L
(1) Windriver---> Driver Wizard---->New Project----->ISA CARD2 j( ]/ ^/ t) _& N o$ u
(2) Interrupts---> New8 ]: h/ W( g! u! z+ s l
Interrupt Number = 14(或别的) & Edge Triggered & Shared( \( A; N2 l- ~) u& F. X
(3) Listen to Interrupts1 X* j3 W7 I/ B4 N" i Z
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
9 M) q1 e' ^8 V7 Q(4) Generate Code
% K! v- v$ l) W1 [* v* G(5) Build and Run- Q, Y4 m9 f3 y$ o5 D0 l
(6) Enable Interrupt+ C _+ H* P5 J
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
; C6 e( i2 ]7 O5 x$ z5 r:intobj5 A q/ G5 i: N! J& D
Object Service Service Affinity
0 ^9 {- E7 a3 X; X; ZAddress Vector Address Context IRQL Mode Mask Symbol
/ Z! H7 D' {# FFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600) q) z! R' F( D' v. x1 ]- i
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C c! P% M. b0 `
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
1 k% p' ?) s D% KFD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
( ^; J% }9 t* M: Q* ZFD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM1 n8 | g1 u4 Z. g3 N! h5 K! h7 |
NTICE: Exit32 PID=70 MOD=ps2_diag
. x; H# \& ]: G* Q) s
) f4 F- o% }! C! i9 `" H从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:8 q2 \, ?- v h6 r. ?' F- X3 I$ f
; K( ~ a% s, Z* {. q; J5 L7 R" G
* _- t" f/ i/ S X4 ?
: E+ B& F" g5 Z; _我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。. @* u( b& @0 T" S" A! t
( z+ @8 ~" X7 S7 U一个问题马上被提出来了 |
zan
|