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

我的地盘我做主
该用户从未签到
 |
文章内容:8 u4 G2 L f* v1 ?- c
--------------------------------------------------------------------------------7 d! y- Y2 P: `% J) t1 B+ ]
作者:suxm < suxm@nsfocus.com >% ?' i" y8 U# M
主页:http://www.nsfocus.com) H, E8 ?# x8 Y e; W
日期:2001-08-13
* j+ v0 z& q4 z- S$ b, N! Q x8 N4 J
- X' X4 c6 }9 N2 h+ E几乎每一个不太守纪律的Windows developer都想在Windows中挂一个ISR,就象在DOS下那样,接管Windows的中断,然后,做一些离奇 的事情。由于ISR是很特殊的程序(常驻性、触发性、Ring0级),所以有很多很特殊的用途。最基本的用途,应该算是硬件驱动开发了:要 想接管硬件中断,没有ISR是不行的。8 G# H% s z4 ~6 X0 U
迄今为止,在Windows 3.x & 9X下,ISR的编写、调试已经不是秘密了。但是在Winnt/2k下,这还是很痛苦的事情。我听一个国外的哥们 说,他本科毕业论文就是<>, 怎么样,faint了吧?呵呵。
$ S1 e' @3 i5 V$ R4 C: o0 s让我们先来回忆一下Win9x下ISR是怎么回事(参见我的文章<>)。
' [( k8 ^3 K+ F$ W* R$ H- vWin9x下,IDT中的每一项,也就是每一个描述符,都定义了256个中断中的一个。还记得实模式下MS-DOS环境中的中断向量表吧(就是 那张从内存的0000:0000开始的向量表,每一个表项有4个字节,一共有256个中断向量)?在保护模式下,中断描述表IDT代替了中断向 量表IVT。虽说中断向量表IDT可以容纳8192个中断描述符,可是CPU能利用的只有处于前面的256个。所以中断描述表(IDT)的长度限 制应该是7FFh( ),
( ^/ i6 w/ q8 J! T8 a2 }3 D, M. S, x# m( R
K9 B/ J. [. |0 c; p2 @0 B
+ ]$ W/ c) l: Y其实中断描述表(IDT)中可以有两种描述符:Interrupt gate描述符、Trap gate描述符(更确切的应该说有三种:Interrupt gate描述 符、Trap gate描述符、Task gate描述符,但是一般来说是不会包含Task gate描述符的)。图1显示了Trap/Interrupt gate描述符的结构。这里 提到了gate这个词,一般译作“门”。如果有人跟你说起“中断门”,千万不要大惊小怪,“中断门”更形像地直意了中断调用的过程: 中断调用就像就经过一扇门一样,这个门就是中断描述符,因为中断描述符中有DPL等权限盘查的标志,所以要想通过这扇门调用相应的 中断服务程序是需要一定的资格的(CPL<=DPL)。- J) ?6 m. u, ?
Trap和Interrupt gate非常相似,一般来说Trap gate是用来捕获系统异常,而Interrupt gate用来响应中断。在具体的实现上,只有一点不 同:Interrupt gate会将IF置为0,这样可以屏蔽硬件中断。但是Trap gate却不会改变IF的值。
6 A9 ^. K) w& W. L$ Y, ]下面的代码示意了如何设置一个键盘中断描述符(interrupt 51h):) p& V' Y& P, H' `- U& d
int51 dw kb_int ;keyboard-handler offset
$ M3 e* h: g; g) ^" g6 `/ ~0 cdw kb_sel ;keyboard-handler code selector2 ~1 f! P' X4 u0 U/ w) h/ U4 D3 B
db 0
3 [1 [. S; {4 t' V! r w/ ldb 8eh ;386 interrupt-gate
! C+ S4 z' @3 t" X' n' l& Ldw 0 ;offset is in first 64KB of segment$ o: n7 n6 y; e) w
是不是有些困惑?为什么键盘中断变成int 51h了?以前在DOS下不是int 9h吗?
6 }; Z5 W; ~5 P0 T这个问题的答案是这样的,由于在保护模式下,有很多中断号分配给了系统异常处理(比如说int 9h分配给了CPU No NPX异常处理), 所以,硬件中断处理的中断号都作了调整:实模式下的中断号08h——0Fh和70h——77h(即硬件中断号IRQ 0~16)对应着保护模式下的中断 号50h——5Fh。8 o$ [( Y" u3 I* H. h- R
提问:Win9x中,是如何区分硬件中断IRQ 12和int 5Ch 调用(NetBIOS调用)的呢?
( N2 \2 I1 \: j: |5 Z答案:由于IDT的50h——5Fh中断描述符的DPL=0,这是专门用来截获int指令的,而硬件中断不管DPL是多少都可以被调用。Win9x就 是通过这点区别来分辨到底是int 5Ch这条指令还是IRQ 12的硬件中断,从而调用系统服务或ISR。
5 w! F6 m( J% V* u- w5 i4 L在单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的情况是大不相同 的。0 v5 F4 R Q: J2 }4 Z
Aladin BasysGate BIG_WWW Hugo_C STAT_02 STAT_13 OTTO SanderD Notebook
0 T& r0 X2 y; V: E9 ]4 QIRQL ! z6 Y* c% ~+ F5 _
1 3D 3D
) W3 i i Z# w9 L) Z' R1 M2 41 41
7 f1 g5 F- A9 p; x; _32 P0 X) l. F. w1 ]/ _% L+ |
4 51 51 51% N$ O8 ~# c1 \# N
5 61 61 61 f& W# @3 T! ~5 P0 {7 G% \
6 71 712 H8 v/ b8 Q/ H$ y: X! q* Q r
7 81/82 81/82 81
) y8 S3 z8 C _4 p. a# [8 91/92 91/92 91/92
) W3 B0 N _4 ~0 B! [6 a9 A2 A2 A1
7 A, D w. n. l* R10 B1 B1 B1/B29 b3 N$ B% q0 t* B) T# L
11
: L% ]. s* n: ~12 3F
9 E! A% G6 p* X9 K" f- I% _13 3E 3E 3E 3E5 K+ n9 ]' I4 h! T3 p W' D
14
6 Q2 g" p& s; e; ^15 3C 3C 3C 3C 3C' v% }; w& @6 u; h. Q
16 3B 3B 3B( q4 |! i( i q3 X7 B* C
17 3A 3A
7 s: l) \1 Y* ?( T! U* A9 f, ` P18 39 39
% h1 O2 |, E/ r19' t& E U% V: w4 X
20 37 37 37+ i/ V- ~" g% J3 E0 k7 `* @8 w
21 36 36 36 36 36 36( \# Q1 C% x- ^* t/ f( }! l
22 35 35$ ~2 O9 f* L" L2 c1 V; K) T
23 34 34 34 34 34 34
" R1 R% A& M6 h3 z24 332 K6 D7 j+ U/ p' Z
25 % |- Q6 V5 H) h6 _
26 31 31 31 31 31 31
1 t( L: w+ e. B) D& M27 C1 38 C1 38 38 38 38 C1 38
; o0 ~0 E: B$ V# }2 e28 D1 30 D1 30 30 30 30 D1 30
, n: Z/ V) [* m( ]29 E1 E1 E1
0 T! m+ R- n. X30 FE/FD FD/FE FD/FE
6 Z( f/ A1 {* z! h+ x" S31 1F/FF 32 1F/FF 32 32 32 32 1F/FF 327 q5 n! J: a% H/ F1 ?
6 N. [% Z) S2 d7 w5 F$ w- ^255 50 50 50
P* w4 n. b7 s. N; S) ]DPL3 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E 2A-2E
/ ?1 {, X$ J; w }% h. b/ d$ D; w& I. A4 e
机器配置:. p" U4 q1 L) o" x5 ?, J, a
ALADIN - Dual Pentium II 266 MHz, NT 4.0 (free) SP3
" V2 ^1 Y1 c9 Z$ _$ r. a! _BasysGate - Pentium II 233 MHz, NT 4.0 (free) SP3& s& ~5 D4 {* K0 S& A3 {
BIG_WWW - Dual Pentium II 266 Mhz, NT 4.0 (free) SP3$ Z8 ?7 j9 ] p) n' k" O3 b* s
Hugo_C - AMD K6 200 Mhz, NT 4.0 (checked) SP1- V9 o# K. x _. X: \7 t- ?. A9 D
Stat_02 - Pentium 166 MHz, NT 4.0 (free) SP3
# _& L& p# p4 }* uStat_13 - Pentium Pro 200 Mhz, NT 4.0 (free) SP2
" `- I5 z) _' j9 N x( X# JOTTO - Pentium II 300 MHz, NT 4.0 (free) SP42 o% A" x# a3 n I$ D
SanderD - Dual Pentium 166 MHz, NT 4.0 (free), SP3/ W1 m6 P( q" {% V% g9 |
Notebook - Pentium 200 MHz, NT 4.0 (free), SP3; j/ n; M* u( j9 h. e' L
0 W% H% b$ D) A3 c* n
在Win2k下,实模式下的中断号08h----9Fh和70h----77h不再对应着保护模式下的中断号50h------5Fh或30h-----3Fh。那么,究竟是怎样一种 映射关系呢,至今还没有公开的答案。我们可以用HalGetInterruptVector(参见Win2k DDK Help)这个函数获得硬件中断对应于Win2k的 中断号(即IDT中的位置)。但是这不是固定映射的关系,也许明天开机的时候,就会得到不同的返回值。! e! G0 Q6 v' N4 f. q' W8 r
Win2k下,在SoftIce中用:idt命令可以看到当前系统的IDT状态。
4 C$ @- C O- h: Q9 `( G:idt
+ ^4 p2 l$ u6 JInt Type Sel:Offset Attributes Symbol/Owner
7 D3 z/ ]; d& R4 pIDTbase=80036400 Limit=07FF 从这里我们可以看到IDT的Base Address和Limit: l$ x8 _ Y& R# z
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+05906 r, P% V3 S3 [; G8 t+ u
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
+ u0 I* L) U! M- @4 N r; H……
3 q5 r/ b$ t' [* u9 Q( I' f00FD IntG32 0008:804646F2 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
2 N; ^( B6 a3 T y: e$ a2 @00FE IntG32 0008:804646F9 DPL=0 P ntoskrnl!ExReleaseResourceForThread+092
/ u3 X6 r3 w L1 a" c0 U3 k00FF IntG32 0008:80464700 DPL=0 P ntoskrnl!ExReleaseResourceForThread+093: O5 h3 s$ F$ Z0 U. \: G$ U3 @
从上面的输出我们可以看到在Win2k下,IDT的有效长度还是7FFh,这与前面讲到的Win9x下的IDT的长度是一样的。其实这是由CPU 只能利用IDT的前256项决定的。
8 w% \& x2 b: R4 o在Win2k下,在命令行敲入winmsd.exe,可以看到自己机器上硬件中断号的分布状况。, X, c3 D& T2 q, Q8 Z4 ]) k/ P# r; O& e
' u0 v h2 {& N$ z3 u8 Q' p- G/ C- |+ u
: `2 s5 ?0 l+ W, r- J. j7 m7 p4 a
提问:IDT表的Base Address怎样得到的呢?
# J" `7 D$ O+ j, [4 V答案:sidt指令,比如说sidt [eax],就是把IDT 寄存器(其中含有IDT的Base Address)中的值放到[eax]指向的地址。请看一下+ Z) s; W5 p- J) o( p0 v0 m" s
http://developer.intel.com/design/intarch/techinfo/Pentium/instform.htm的说明:- F9 G) G& c7 ?: h7 P' n
SIDT - Store Interrupt Descriptor Table Register
0 w# M' {8 s8 U T3 h. q; Y' G
0 K6 Q1 `) J, |, z. Q+ UGuido Wischrop提供了下面的代码可以读取IDT的所有256项。 s% b+ Y. |+ _( v" s# t; U
2 t9 V, p, l: cUCHAR *mBuffer;
7 t$ p7 B! n4 A% p! {' _, sULONG dummy;
6 y& y. w' u' c( o' E' D0 H9 R% w……9 R+ T% h" |6 `1 {9 I6 c y+ u& x
_asm" G: e) L6 Z2 S5 P2 d/ Z
{
; v: I F8 R3 J1 zmov esi,mBuffer
' d% b, Z" ?! \# n- jsidt [esi] //load IDTR to *mBuffer
, E# h. X2 |. k O/ ^# Cmovzx ecx,word ptr [esi] //load size of IDT to ECX
* _/ ~8 n5 D' z2 b" J9 \; omov ebx,dword ptr [esi+2] //load Base Address of LDT to EBX {/ {5 o# g; F6 Q, U) c4 _
inc ecx . @% X+ s# g: V. J2 w- n( F
mov edi,mBuffer //mBuffer is the target
1 J4 U. r$ O7 emov dummy,ecx //store size in dummy* t0 s( C$ B5 N6 c2 p9 D4 S
mov esi,ebx //store IDT Base Address to ESI5 c! q: O6 S- w, f) ^. m e4 ^$ F" h% U
shr ecx,2
, n) i6 b- _0 i! R; ?2 Mrep movsd //copy IDT to mBuffer
5 B9 E; Q5 J+ _. \+ J# @}! H: @5 E4 t# z+ d. \- c
E0 D5 C0 h4 G; F1 X5 W这样我们就可以得到想要的IDT中的某一项,那么,我们是不是可以通过改写内存的方式直接替换相应的IDT表项呢?答案是肯定 的,有人声称这样能成功。我觉是没有太大的必要,因为通过标准的Kernel API调用也可以实现。. a/ ?& \9 P: x- ?+ \2 W) F
我们可以用Windriver生成代码来简单地体会一下挂硬件中断ISR的感觉。
0 K( O0 V% y5 K(1) Windriver---> Driver Wizard---->New Project----->ISA CARD
( R) h( a* a1 G) b( ^(2) Interrupts---> New' `8 ^$ y5 c2 C& f, q4 `/ c/ [
Interrupt Number = 14(或别的) & Edge Triggered & Shared i, E4 ^; U0 l/ i) ]" R8 y/ b
(3) Listen to Interrupts
8 ?! k) M4 _4 C, M$ T如果没有出现错误,则表明这个硬件中断可以挂我们的ISR。2 n* b' j+ A1 V% L. Z0 j
(4) Generate Code
4 n, @. u0 ?1 D! |2 T9 P(5) Build and Run
% P4 f( c/ m' g2 o* a1 x, a(6) Enable Interrupt7 `6 Q7 _2 O8 E( K t
(7) 切换到SoftIce中去(Ctrl + D),然后敲:intobj命令,可以看到如下的输入信息:
0 A# \: W' H8 Y* I! a+ W' q, J:intobj* c- U' w1 F/ E
Object Service Service Affinity
+ V# }8 o9 H( d, VAddress Vector Address Context IRQL Mode Mask Symbol
" r% f6 J& K4 W7 y9 _6 }FD349508 31 F7861900 FD4683E0 1A Edge 01 i8042prt!.text+1600
0 Y/ w, v3 B0 r4 h; O( OFD348D88 3C F786798C FD35D020 0F Edge 01 i8042prt!PAGEMOUC+020C9 T4 T# g! {2 d m3 K: U
FD189708 3D FC8F22C0 00000001 0E Edge 01 WINDRVR!.text+2040$ O, C0 V% m) H, p) Q! I- B2 v, E
FD48F788 3E FD10FE42 FD4BD030 0D Edge 01 atapi!.text+5AE2
1 g: e( |+ J9 M2 B6 x8 yFD34A888 3F FD057AA0 FD34E0DC 0C Level 01 NDIS!PAGENDSM. J8 B! Q4 S8 n0 Q$ U$ w
NTICE: Exit32 PID=70 MOD=ps2_diag! y ~1 ?! X" q/ c. q7 `
o! a; R' `; r, J
从上面可以看到,0Eh(即14)号硬件中断被映射到了IDT的3Dh项。还不相信吗?好,让我们来测试一下。在SoftIce中敲入如下的指 令:genint 3d,看到什么了?呵呵,屏幕显示如下:: y5 ~: N5 h- }# z3 I1 T
* _2 L& L& O/ n' _: T
* ]4 e& f" D3 Z" E( {- O8 [% @) M
我们用:genint 3d这条指令模拟了一次硬件中断,结果表明我们的ISR确实是挂在了IDT的3Dh项处。在Softice中再运行几次genint 3d试试 看,屏幕上会输出什么样的结果呢?当然是Got Interrupt0 number 2,Got Interrupt0 number 3之类的信息啦。 ]% e7 H! x1 Z7 |
1 q5 b* p$ S4 A' n8 h一个问题马上被提出来了 |
zan
|