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

我的地盘我做主
该用户从未签到
 |
文章内容:" \# t# l0 V- |5 }! m* e( Y
--------------------------------------------------------------------------------4 [# |( c8 A6 D. B3 u& u# V
作者:suxm < suxm@nsfocus.com >% x* d9 ` d9 X- m8 A3 F2 H' D
主页:http://www.nsfocus.com
5 N& k7 r, i7 S/ d日期:2001-08-13/ w9 Q( } y6 Y9 V+ _8 s! N
5 i; o2 J. [6 ?5 [5 [几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。- H6 L4 R3 B9 p2 ~. G9 a2 K" x! X L0 Z; ?
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。( v- x i x& x- g; A3 m! J4 m" Z: w
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。' c. w: D W( w( v5 P0 w
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
( A3 b' W. X( t" w$ |
3 C5 y% M9 X4 y/ t( d% [
1 @! p* S2 s8 E8 f8 J
W& f# m8 k% Q" \( _7 t' i2 E' g2 u其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。7 R+ I6 Q p" U
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。- w) c9 C1 D+ V$ V* v1 h
下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):6 p% W( b& `) r" b
int51 dw kb_int ;keyboard-handler offset
0 z! A+ b" ^& N0 \dw kb_sel ;keyboard-handler code selector& X* i1 B5 _9 \' i, i3 v8 ^
db 0
( A' ^1 i G! ^$ R" gdb 8eh ;386 interrupt-gate- Y8 H" U* i- D. R, V0 d
dw 0 ;offset is in first 64KB of segment
! ~% ?+ A& O- _是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?- A6 T9 z4 l1 x* x( a! k
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。- t Y+ W- G+ A/ o
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
* c" n& @; Y9 F& `答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。8 ? d/ K; @$ X* i) c" _ f4 |( B0 p
在单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的情况是大不相同 的。) W1 L: j) {; j. i/ Z B
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook9 t. |; ]5 p: A+ c
IRQL - }5 x, ^; ]+ \ x7 ]% W
1 3D 3D
9 q! w8 D$ n/ u8 J# E* R2 41 41
: {6 E: S: p# J# j4 j4 v" e3# X2 h/ l7 S0 Q7 D
4 51 51 514 A0 _0 `5 n8 y6 {$ g4 D; l7 ^
5 61 61 61
' a' T* b: O9 P( x0 U: W6 71 71
) B9 ^3 J: d# S4 q* z3 n( P: {; o7 z7 81/82 81/82 81) @9 G+ U H7 c& \
8 91/92 91/92 91/92" f/ @- T; E9 s9 E% K2 }
9 A2 A2 A18 O/ Y7 u# t8 j
10 B1 B1 B1/B2
& q) T/ Q: V, G9 ^8 j# P11( A; x. S3 N! [$ C) }
12 3F
3 q$ N' \2 [3 y, ]3 E13 3E 3E 3E 3E
, Q$ j, Q9 g' n8 H14' _1 I g- o7 n# e
15 3C 3C 3C 3C 3C
3 }5 P4 f( i4 ^. k, e16 3B 3B 3B1 }( V3 q7 Q. }: m7 f2 M0 l t
17 3A 3A$ N( O2 Y7 k* o& e9 ]9 T/ ^* P& _$ d( n
18 39 39
# o- u; {' F: }" v19
3 ~% z2 y, j" U @' P, p: ?20 37 37 37
" D& z1 I2 U# Q6 A3 r7 d2 d d21 36 36 36 36 36 361 W9 n. U6 Q3 g: ?
22 35 35
4 i- {# W+ A# X8 u1 m9 b0 b23 34 34 34 34 34 34
5 b# q+ X% X# P: T& f24 33
& r m/ ^9 a/ @7 F2 I( p25 7 K1 W) l% I3 f; B2 s; `# q5 X
26 31 31 31 31 31 31
+ n* y1 e2 ?, U$ }6 K27 C1 38 C1 38 38 38 38 C1 38
3 O" m/ o4 C; F e9 u, O28 D1 30 D1 30 30 30 30 D1 30
5 @2 Z! U) q7 k# l# `29 E1 E1 E1
; X2 z9 K" d3 P30 FE/FD FD/FE FD/FE
6 o" c' _; z. x) E8 u* e' N9 \0 }31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 327 b% u; ] _- f0 |8 M6 s
! C* E9 \ P3 k& F3 |
255 50 50 50
+ K( O, `+ e m* E! VDPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E! B0 j) B/ V6 r* o- ~
/ O* c# b7 A% p7 Q$ T4 F
机器配置:
5 k, ^! F2 R/ f- m! ~ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
7 M& W, A2 J7 g! ~2 h7 dBasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3
$ f& K. r- p$ q/ hBIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3
* @6 t& c9 ?/ DHugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1
6 P5 m% A& Y0 [4 S% u% YStat_02 - Pentium 166 MHz, NT 4.0 (free) SP3$ ?9 h7 L( g$ V! v1 d
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2' k) e0 j( K0 x7 p4 ~- J
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
2 Q- [/ E4 z9 a0 \1 fSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3
. x; t- [9 e6 e) P7 tNotebook - Pentium 200 MHz, NT 4.0 (free), SP37 t4 T" f7 X7 u
. Z. z- ]4 A, u+ [在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。8 h. n8 G# a1 l \( l4 `: D
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。, B. U/ ?: F/ ]. V2 [
:idt
9 K, p6 d5 x+ _# z& S' B$ oInt Type Sel:Offset Attributes Symbol/Owner
* C: P, c% a7 OIDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
& u% l3 Y2 Z8 c/ w0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
) d+ Q: T* x5 N# P% ^4 q0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0, e( D, U T: X9 Y
……
! L6 z& g( R) A% q& }00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
1 ~# r6 q9 z1 E. b% G/ {00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
: u- E' J+ L7 {* O5 P( `00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093
& g* \: ?; K& h2 j" `从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
4 d# d8 K$ d) g$ ^! ?在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。8 a9 \8 B7 a& l ] _
: m, n$ I! B4 n& H c! g% w; I
" i/ D+ ^4 {0 T; P
: f( b# ?& o. G' Q5 ]" m8 T提问:IDT表的Base Address怎样得到的呢?
. c- R; i0 z6 h' G+ s5 e答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
P& y$ y% F" E! l9 _http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:- |% i% f" y3 G% h( ^3 ?
SIDT - Store Interrupt Descriptor Table Register( F; v8 A0 T% Y8 v
. c% E6 ?, _8 g0 o- [Guido Wischrop提供了下面的代码可以读取IDT的所有256项。
7 `5 O; y. M( W$ z* w& D% Q8 f; Q5 Q, K0 u& H
UCHAR *mBuffer;3 j; t; B. |! r5 E% @
ULONG dummy;8 H4 ^5 g& f4 D. F( ]
……
8 U; T3 E6 D# d_asm
& D0 R( d3 Z/ t& w8 R{
9 v: i4 m; D$ D; w8 V" \mov esi,mBuffer : l s. n6 x5 g+ n d3 h
sidt [esi] //load IDTR to *mBuffer) c/ x0 k: h! Y$ N
movzx ecx,word ptr [esi] //load size of IDT to ECX0 c6 }' |/ V2 v# S- J) A
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX$ u4 [7 j/ N/ ?. f+ h+ Y) G; p5 |
inc ecx 8 a7 j0 m7 t6 ?2 F) L
mov edi,mBuffer //mBuffer is the target0 ~1 F, ~/ y9 d, \1 ^0 L
mov dummy,ecx //store size in dummy
* X- F, {+ c; Wmov esi,ebx //store IDT Base Address to ESI
" o: e% N. o: r+ Hshr ecx,2
$ d) A8 W! t- V( k3 M% d; ? erep movsd //copy IDT to mBuffer1 p: C1 P( e9 v, c
}
/ y! H# Q4 o1 D1 d; B3 @
1 v9 l+ Z, K3 q: E# ^这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。
7 ~. E ]1 H) i- L# ~% t8 j0 m我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
g7 e, N- @, ^' V9 X: t* d/ p(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
y0 m* k0 _' C+ H6 `4 ^' J1 _- s0 Q(2) Interrupts---> New9 h8 w2 A/ s3 r" o: w& d+ ?
Interrupt Number = 14(或别的) & Edge Triggered & Shared" e# O" W9 j3 ^# m( P5 k' x
(3) Listen to Interrupts
- O7 e- l& _* H2 D1 X如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。
9 G4 }. n8 r7 e# `8 [" S$ t# f" q(4) Generate Code
) V' @9 A2 Y4 x/ |(5) Build and Run
: \& p1 T2 d7 C(6) Enable Interrupt. q& X* n' Z! o" s$ X( c9 M+ U; R
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
. ~0 d2 N9 E/ u3 j0 N:intobj, ]$ D; ^1 Z5 Z
Object Service Service Affinity
" g5 o+ b" o' q: mAddress Vector Address Context IRQL Mode Mask Symbol
8 t9 O$ Y* K7 t- B" }, OFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
( l+ g( S. O# F3 d3 T# qFD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
2 h7 U8 j9 o: C, N* J5 F: e$ mFD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040
+ l+ s E1 x' uFD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
' r/ G! R6 t) n4 ~FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM. J1 n; e! U4 P! m* Y4 g% L7 z
NTICE: Exit32 PID=70 MOD=ps2_diag ]3 f. T! c; d8 S
( T6 q3 y0 ?- e0 P$ w$ t/ v从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
) B) ?& W; t9 u; M9 N) F2 {0 t0 v2 T1 ~6 p* T( f
( B" F2 N7 ?, k" o1 g9 ?0 `/ Q3 a O; g6 ^% N
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。
4 D9 K+ t' e- D, ~1 T I- e2 Z6 b. Z1 U; x, d4 v
一个问题马上被提出来了 |
zan
|