QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2425|回复: 0
打印 上一主题 下一主题

关于Winnt/2k IDT的一些思考(续)

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:
) t2 p4 m  l5 e, t. U7 H--------------------------------------------------------------------------------2 `8 e9 |# X7 Q. j2 F/ [/ z6 S
作者:suxm < suxm@nsfocus.com >6 s% a8 V4 }& D  V, Q
主页:http://www.nsfocus.com
. S) N' H9 X" f2 I5 _. g; g, X日期:2001-09-10
6 h9 W6 f5 @7 @* p* n! K8 ~6 p# C6 j# F! y5 T" e& Y3 Q% Y: U( V
<<接上期>>& i" S4 x! M# c- ]+ a5 C9 w, L
& k! S  J+ }$ I8 ]5 h: f
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
; C# O5 p% F- i. G+ q# i( V. H
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]9 c8 Y" b0 @, f9 y' K: j0 w
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。- }$ R5 n0 i4 b4 {0 j
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。4 ]5 ~& I8 B7 Z
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。. K- m! m) W7 S: ?1 G+ |. ]. ~
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。4 _5 h. |" M; x; M7 ]- j
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
5 {5 r# x& g! c! S[ suxm: 去看看上一期月刊的图1 ]' d! p$ Y6 K5 s% y
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。1 ~# O5 g, c! A, m5 u

* M% u$ z$ ^, W2 [* `' F( D4 Z- R$ ?/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */
  ~# c9 R+ h( m: Y2 f2 B2 g#define HOOKINT 0x38
; b: a4 p) o% `6 q0 m7 [NTSTATUS DriverSpecificInitialization( )
+ O4 h; t% B$ _{
1 @2 ~- w- a) c0 b7 e2 P0 a2 cPIdtEntry_t IdtEntry;
0 M1 ~% O1 S3 @  N/ _  X9 wextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
/ L. e4 y2 J7 t; [/ e% @5 m; i6 u' G
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
( G) {! t) ?- |ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
, Q* \- B  y# ?ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);9 }8 \% a  D  J. O

: i+ F0 c! i: V2 g& w- w/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
: c6 }6 @; ~; P  Lif (!ServiceCounterTable)5 i" o' H$ x4 K- w
return STATUS_INSUFFICIENT_RESOURCES;2 x* y3 \+ m! x3 a4 r0 v
$ ~. d, Q  w4 f! _: {. K% S# {+ A
memset(ServiceCounterTable, 0, ServiceCounterTableSize);4 t+ A$ _7 d5 r
*ServiceCounterTable=NumberOfServices;; x- I# A  S! Z
/ h4 e3 u: J2 V2 @) q% k/ C
/* 获得IDTR Register 的Base Address和Limit*/3 s  H" `3 w+ }4 v. {0 A( s
_asm sidt buffer
+ Y. ^8 ^# {4 Y" n/ H1 wIdtEntry=(PIdtEntry_t)Idtr->Base;; ~$ [4 H* f4 r) n, S1 n
) m* U% n. B7 V; g' d% A; m' \1 x
/* HOOKINT就是我们想要hook的中断号3 b. ]: w' C! [! |% j
这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
3 p0 J4 j7 ^! b3 G/ ?4 A% P7 ZOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
; G1 J7 x- ^' u3 M6 h% [(IdtEntry[HOOKINT].OffsetLow);
% |6 N: f3 M9 h
" }8 k) u7 s- H4 J( V! V8 o; Q/* 在IDT表项的相应位置写入新的中断处理函数的地址
' P& Q4 _6 o+ ]: a8 [用新的中断处理函数替换原中断处理函数 */
$ v* G- z4 U& n. q$ ~' I_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */; P3 ~( E& B1 L4 Z# T
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;# Z/ ^- ?: w8 V4 V  m' I
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);8 m4 G, Z7 }) Z$ \$ L- t4 b1 @
_asm sti /* Enable中断请求 */8 h1 D  M: n1 p& m

) a0 T, w% [" g0 wreturn STATUS_SUCCESS;$ H+ u/ Z: @- k; `
}- l( t% u! }7 Y8 z

) w/ C. u2 s1 j. D3 k7 j. {上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。1 k! @; R+ L; m) m7 e
typedef struct InterruptGate
( h# y# k6 ?/ f, c- `, E6 H" n0 |* v4 ^{. Q7 F5 e- `; Y% @' L5 g
unsigned short OffsetLow; ) e$ q8 u$ O9 L2 Z* D
unsigned short Selector;
* E7 z0 d2 J7 t3 e1 n6 {unsigned char Reserved;& y2 n  @4 c2 ^* l/ [
unsigned char SegmentType;
5 f5 r% n) R  F% _4 iunsigned char SystemSegmentFlag;
+ i# h1 ~7 i: E( ?unsigned char Dpl;
) F! F4 L9 e  m' d/ O# @2 \6 v4 tunsigned char Present;+ F! V4 W4 B- a7 d$ u8 w
unsigned short OffsetHigh;9 E) \3 M! F# e( k
} InterruptGate_t;
+ m5 l* i' u6 J& r0 {5 |5 U9 z2 F; F/ U& G: M* d- D! a1 c
在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?; B# w- A5 A% R! p6 {/ @3 J0 @

4 t3 Z8 |/ ?* `) I, s3 i, ?" V[ 读者小强:Winnt和2K的IDT中有空表项吗?]. s( M' s# c+ ]; H$ \
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
- `1 C5 p$ q" d. q9 S' D下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
: C9 g  U$ t& C9 w6 }NTSTATUS AddInterrupt()
' c5 L3 N; i3 L& X{
% ]7 _1 u7 k. k4 v) B+ PPIdtEntry_t IdtEntry;
8 c: o' l) v* i( k" r# g
" |5 Y8 R4 V* L" ?: V! [/* 获得IDTR Register 的Base Address和Limit*/  z# X3 r) v1 O0 t) R- Q0 ~
_asm sidt buffer
; o" ^$ @' x7 Y3 {) x! L9 C( |. aIdtEntry=(PIdtEntry_t)Idtr->Base;
7 c6 _1 F3 e) c$ `2 D# K5 ?" qif((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))/ D9 h( b" I6 r4 I$ ^' y
return STATUS_UNSUCCESSFUL;
& E, x& g1 t2 s( g
/ Q1 f, b( p8 o$ `0 |% T1 N- p_asm cli3 A1 C* p! p7 {: N

* J" G/ }. e9 F/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
% s  c1 n: R9 `" N; m7 hIdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
9 K/ l) J9 ?0 I, T  NIdtEntry[ADDINT].Selector=8;* j- R1 y6 P  q' J
IdtEntry[ADDINT].Reserved=0;: b& `9 x1 x. K5 m5 k
IdtEntry[ADDINT].Type=0xE;# b2 g) m- [+ K7 U
IdtEntry[ADDINT].Always0=0;: ~5 X: g6 j6 ^: ?
IdtEntry[ADDINT].Dpl=3;
; v: Z+ `( _$ y7 \# BIdtEntry[ADDINT].Present=1;& J' H5 r: ^. ~3 S# B5 w' V* [: a' S
IdtEntry[ADDINT].OffsetHigh=, r4 B( e4 o. N6 c6 c
(unsigned short)((unsigned int) InterruptHandler>16);7 v$ V+ @$ [' M
_asm sti
- h3 j! G+ A6 d$ G. i0 ]5 m
$ |$ N& h( d3 P' Y4 \return STATUS_SUCCESS;7 i! ^2 p' X5 j% r
}# t* t, U- {& t" g

, e; o2 S6 m# l/ ]让我们就上面的代码提一些问题。3 a0 F  x/ M+ M: G' k+ n5 w
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
7 ?6 n. L$ Y( j' zA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
8 w7 {& r2 |: \, Q" Z5 R! y个段的名称就是8(CS=8)。+ M- H' j/ y4 _; A8 ?) j

0 |2 e/ G8 l; x: c3 PQ: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?) f+ H+ F  |8 ?4 I0 g
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
7 T- K" L( V- @" [* K; d. o应用程序的DPL=3,所以,可以直接触发这个中断。
* [% Q) F4 o0 t1 ]# k
7 B4 T$ ^9 i, eQ: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?% D! j  E! y3 _7 ~
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
- ]" s) o! e7 e' g- C% P:idt+ x4 E/ c# X$ U4 `, [' G% @
Int Type Sel:Offset Attributes Symbol/Owner
$ ?# M% i$ g6 v6 }  qIDTbase=80036400 Limit=07FF
; |1 A# }. ~2 q' S5 `$ N0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590; u3 v8 H/ z. H  a* @! o; c
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
; y' X: B; N' y% m: |! Z! I……% x  Q) x4 x" t/ o* O
可见,0xE表示IntG32。
( ?  X$ l# k% a+ Y7 O0 m
# `. r+ M0 L. R, A终于快到下课的时间了,让我再多说几句好吗?! u4 T) E; m/ K* i- g: G
通过对Winnt/2k IDT的分析,我们对IDT有了一些基本认识。上面介绍的通过修改IDT来hook中断的方法,是很危险的。笔者强烈建议读 者尽量通过Winnt/2k DDK官方资料提供的API来实现hook中断的功能。
zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-4-18 16:15 , Processed in 0.341442 second(s), 52 queries .

回顶部