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

我的地盘我做主
该用户从未签到
 |
文章内容:
# \( i( R2 G0 O- ~1 m2 b4 R. N. d--------------------------------------------------------------------------------2 A9 q# Q. G+ t5 s$ L! j
作者:suxm < suxm@nsfocus.com >1 a$ h1 B7 Y4 S& ?
主页:http://www.nsfocus.com% X5 G7 Q% x' \+ b8 c" i' e, r
日期:2001-08-133 r0 y7 w1 W6 o! e. N
) W& F/ C b5 ^: j% u0 |" J
几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。+ W; _" U9 H! \1 n) q1 @
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。/ R9 t5 [% V1 C8 j
让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。/ \1 c$ `- H: B3 f2 B( O$ Y l: p! L
Win9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
3 ^5 i, ^" T* D9 ]) _6 z% }/ l
. a" v! L9 e1 c3 r1 X {$ j8 h8 h+ K4 |$ Q3 w1 Y
& D Q2 O; F1 u- R
其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。2 t R% ~( z7 K0 }8 B
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
! o! Q% v- A# c1 x6 l. u; p0 F5 y下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):% E# W1 ?! `5 ]! J
int51 dw kb_int ;keyboard-handler offset
) {( D" x! L: t7 L! Gdw kb_sel ;keyboard-handler code selector
. x4 z- Q! m. A2 ndb 0
- p- |6 C5 B* T B T7 ldb 8eh ;386 interrupt-gate
+ T1 B/ u u$ Idw 0 ;offset is in first 64KB of segment
8 x- ]8 x5 m f& q是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?6 y) J d/ _9 l6 v& }1 S5 G6 k
这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。
" `7 z& V* z0 U提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
" h: _6 T1 T/ C1 e( G" r答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
8 k! @, u | N" c2 v' Z在单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的情况是大不相同 的。
) F( t- \$ l# J. o2 dAladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
* p) ?4 m8 V# n- A. f5 EIRQL ! t: D! r/ V6 g) E. M
1 3D 3D
. D. |% \. M" ^% l2 41 41
* l/ ^- `% P0 S: P* G39 G" m4 j% O, `, k- z: R1 G8 d
4 51 51 51' P4 Q7 d- H, G2 Q: Y. k; u9 M
5 61 61 61
# g! \4 f; o* S: ?% K' \6 71 71 j* ~2 B, c3 D
7 81/82 81/82 81
) f+ o. l- |% ?' @/ Y8 91/92 91/92 91/922 ^' _* Y% q- f) j/ h2 c3 z
9 A2 A2 A19 [; e- K, e/ K! y+ s8 M
10 B1 B1 B1/B2
' P0 C* \9 n1 h11) ^) |( [" N& S8 p- B/ Z, {1 {& U
12 3F
5 {3 Q1 c" q5 r3 W/ M13 3E 3E 3E 3E
% V& W+ y6 S* K5 v14
, z0 L4 ?! a) _& F" U; y15 3C 3C 3C 3C 3C. L* m! ~! i. h
16 3B 3B 3B
2 a3 R% Z: I( v2 J( f( d17 3A 3A" f" f9 n! I/ q% ]
18 39 398 o6 n$ d4 N/ n
19
) }6 F4 ?# z M6 W1 \" y) k7 S+ r20 37 37 37
& ]+ g! Y& I0 N. m) V; f21 36 36 36 36 36 36
: m1 S% F( @, `3 R22 35 35
7 I( o( J% |! ^6 S( C a0 f/ g$ Y23 34 34 34 34 34 34: w# a7 S" S1 g9 b
24 33
, ?$ ?% g1 f. O7 J& u) M/ {25
9 s4 G& o9 }, |7 X26 31 31 31 31 31 31
+ L" F1 J' {3 B1 N27 C1 38 C1 38 38 38 38 C1 38
3 [. [& p6 B z6 L3 ~7 ?28 D1 30 D1 30 30 30 30 D1 30
6 U0 ^' v$ X4 ^. ?6 G+ L+ n29 E1 E1 E1/ c7 u d9 K4 g1 d
30 FE/FD FD/FE FD/FE3 A7 R: G: G* O- q0 |& N
31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 32
* @& i+ W# u K8 a
' D3 D. v4 s8 k! }255 50 50 507 t! ?, ~6 v2 G$ \4 B- a
DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E% k0 r- h! t/ m4 M. I
4 _- v, _) Q, g, v5 u0 o机器配置:6 n( v E' J) \7 h- h
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3+ i' i( S+ d6 Q* e
BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3& J& n/ j9 ]' r
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3* ^( A A- E8 `' s
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1) y4 c6 C4 Z/ K5 ^7 z+ Z! {$ m
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP32 _! W/ U I9 p5 H( R2 [: `
Stat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2# C) |4 w: q* s3 H( h! a
OTTO - Pentium II 300 MHz, NT 4.0 (free) SP4
3 r* {! l q0 `. hSanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP32 A, g) d: Q0 C0 o) c( X: L6 M
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3
, a2 w& v9 c. @
& B* y- y9 |& c3 g* v/ l* r在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。
1 k& x0 l! I T; |* HWin2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
0 K. U$ B6 b# C" {5 s" G9 d. M+ e:idt
6 q4 b. Y( _" wInt Type Sel:Offset Attributes Symbol/Owner, y3 @$ \% e! \& c1 j
IDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit
- y6 R5 B E, E- s& S0 z0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590 Y' g' ?" f2 U/ @7 h; n/ U
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0' j2 p: r" I( G6 m5 h& ]
……& u: E/ _& A& _! ]/ N3 F: Y% B9 L
00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
+ Q. ^5 l5 m2 P. B8 b! |, _0 y00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092& w. W$ e6 ~3 s
00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093. ~: q0 o3 q' d
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
6 f& v Z6 p" E: S$ M- T$ { f在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。
9 g9 } i* L% g2 `6 p; T; z9 c" I {+ p4 t5 D# ]* c
4 [( b5 D3 |7 f' q+ \& Q2 n' ?
6 V1 z' c: u5 I提问:IDT表的Base Address怎样得到的呢?; h" l+ k# O% p# z0 Q
答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下
6 O8 U6 X( l3 F2 h Chttp://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:& j% h$ k% g, {5 m1 P! @# `
SIDT - Store Interrupt Descriptor Table Register$ J. p+ H" i% o5 V" b
, M o* n' ^5 I# m$ D" s. d# y1 fGuido Wischrop提供了下面的代码可以读取IDT的所有256项。* `) ~" c5 W" r
( B! k' [0 Q9 j. Z$ e& ~2 KUCHAR *mBuffer;
5 V: H) L4 o2 P- n( rULONG dummy;
2 `# d! @( t& L" i' ^; z……8 n, N0 y* [7 V. ]- L
_asm5 _4 g( A2 [- y9 P) z
{
7 w9 K9 j/ u. t tmov esi,mBuffer 9 J8 X1 n# ^5 f7 C' d
sidt [esi] //load IDTR to *mBuffer
; z9 Z; C5 v( a9 U, }" gmovzx ecx,word ptr [esi] //load size of IDT to ECX5 j+ v0 x6 `! r# a' ^
mov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX
, g1 L7 O" d$ e3 C- I/ jinc ecx + r6 G$ n- ~$ F% c
mov edi,mBuffer //mBuffer is the target7 Z& L; n# Q. }# U. G
mov dummy,ecx //store size in dummy! k7 S- H w) X' b( u* U6 b" `
mov esi,ebx //store IDT Base Address to ESI
3 O! k7 A3 Q9 |0 K6 yshr ecx,2 3 {, ^# A) c' {$ H8 c
rep movsd //copy IDT to mBuffer4 {& I, v* r3 w5 m
}; \3 w7 n/ F: K- F) Z* k
+ [% f) j' N; G4 |4 R1 x2 ]
这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。, G; m! }/ t- P" n0 u" g$ |
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
; e7 Y5 M6 \* |/ r6 g0 `(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
( F% z9 s" H- Y& V8 \7 J- S(2) Interrupts---> New
* n' P2 a$ I0 t+ R1 k* ]4 y6 xInterrupt Number = 14(或别的) & Edge Triggered & Shared
* u. ]6 }8 \- E$ _, [! Z/ M(3) Listen to Interrupts; _! @" P! I) G# L. @, K/ R
如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。1 D: t3 Y+ q- r" `( K
(4) Generate Code
8 I- X$ S& h) P7 A(5) Build and Run
2 K0 H8 l3 k1 K( L T(6) Enable Interrupt
7 p4 _2 u4 G( {6 |(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:6 c4 Z: ~9 b" a
:intobj
1 ?* k3 i b6 k, `Object Service Service Affinity x7 |( H' Y1 T( S3 I3 B
Address Vector Address Context IRQL Mode Mask Symbol
% U( t. q* H3 WFD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+16004 w9 K. ?, E. `4 A! B+ [1 t' m4 G
FD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C
2 q5 J. N% G! v% v5 K/ n4 hFD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040* Q7 b0 C1 O% m& n9 d
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE29 m/ l* h, Q0 ^) N( n: ]0 |( f
FD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM$ N( H2 t+ `7 \6 S2 E
NTICE: Exit32 PID=70 MOD=ps2_diag
8 h7 k+ k" d1 w X* i3 q1 {7 G0 M6 w' e
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:
9 r5 a( I: R+ `9 O) e
" s" E, y- T$ E6 e6 q
- O. S5 [# i/ C |% A2 G( E( c/ U0 J6 z+ R; E0 R
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。$ W4 t( m- T; `# i; t6 T/ |
[% U" c8 @' f7 N- ~' w. e5 z. e9 I一个问题马上被提出来了 |
zan
|