QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:. ]- h9 V  y+ |& R$ a
--------------------------------------------------------------------------------
+ [; \1 P0 v6 D% b4 k5 |' I作者:suxm < suxm@nsfocus.com >, L! w1 ~3 i! M$ X. K  ~$ S' l
主页:http://www.nsfocus.com! o# p0 x+ I0 u! {
日期:2001-09-10
* h  y0 u# Y5 B" n* U1 P
; H4 r5 Y8 e& f0 G( s6 I<<接上期>>% }, X5 z  ?6 S! I/ r- I
, Z( x2 f  X, N: w; g. ~
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。  O% G* x  X4 Q' ~% g' F

1 S! l0 j$ H  H9 N. i! D1 G3 p) D1 X[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]  e- R/ `- M9 W; Y
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。/ {+ y4 K+ i( D; a0 `" D, k
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。) A- U# M/ L4 L% `. ?2 g7 T
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。' q# h5 }5 F2 k+ n7 x* E
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。/ o" {! O* [7 A3 ]
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]' C, n2 _$ G" e; k6 S
[ suxm: 去看看上一期月刊的图1 ]+ Z+ r, I: m) A) ]
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
6 H5 I& V7 t* [- q& C
+ D* H& j: R* G& \/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */* ^2 l4 r$ S- M$ m* h
#define HOOKINT 0x38
4 Q" E" e; v7 j- y! |NTSTATUS DriverSpecificInitialization( )* H4 i) _6 q. k4 A( n+ `" `- w; R" I
{  l8 n" ^( D% g! I3 J
PIdtEntry_t IdtEntry;
0 u2 F1 Q; H* H3 L7 m+ t3 Cextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
) M7 R4 C* k- t0 V* @% ?6 P; M! R+ Z' R2 A6 x
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;" P$ S9 C6 n/ V: D1 {6 `  K
ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);2 G6 \. f- o  G* w  e1 {5 `0 z
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
: ?. V- ^2 X* ]) u' @& [3 D# D6 T6 Q# }6 X
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
4 B. w0 [( l, H: E: x6 [9 V" _+ s+ Yif (!ServiceCounterTable)0 n* j/ G3 z) Z3 F: v; d& a/ X, I- X
return STATUS_INSUFFICIENT_RESOURCES;! A" ^, f* |! b. m% ]6 p0 g
% G: a. A- j- {  p( z& D( L
memset(ServiceCounterTable, 0, ServiceCounterTableSize);% I! O1 C6 G, D2 k0 P2 @0 C
*ServiceCounterTable=NumberOfServices;2 \! F) s  t/ j$ _8 y3 f

7 p( [% Y7 d7 l3 C! o1 H/* 获得IDTR Register 的Base Address和Limit*/( G8 {) p0 u; g0 r- K; O* i% t
_asm sidt buffer6 S8 K' C, {9 X- B* t8 M0 x! b
IdtEntry=(PIdtEntry_t)Idtr->Base;/ [4 R+ Q6 Y0 |6 I1 A* b' m

! W' V6 l2 z5 r) w& ?$ \* o. g/* HOOKINT就是我们想要hook的中断号
+ R/ ^1 l7 ^5 i这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
8 p+ d- h- `0 j3 zOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
) T5 Z$ S3 q$ X0 q! H! C+ ~* N' ?(IdtEntry[HOOKINT].OffsetLow);; U" P1 C6 T$ P# D* k
, F3 J/ O. S- s1 D/ Q0 S
/* 在IDT表项的相应位置写入新的中断处理函数的地址
: ^8 K$ Q# d" L用新的中断处理函数替换原中断处理函数 */
& E7 ^6 K2 t2 J_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
5 u, W* i5 J+ M' M# L3 b; ^IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
$ @9 I6 ]; b8 E7 h  r) ?IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);6 r3 e5 q" L0 {- e% V
_asm sti /* Enable中断请求 */. c5 v4 d9 ?  }* j0 F# T* t
) G8 P; e' ?6 I- O
return STATUS_SUCCESS;
. I1 |8 _; M/ m9 {; L- |6 }}5 C5 }" G. V. B* y7 d* `

: }- Y2 M3 l4 X$ b上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
4 k+ {. C7 B: O  Rtypedef struct InterruptGate
  n8 J  Z2 V+ D5 D: f{
- V% \' s8 ~4 M. Y1 I4 }: Y% munsigned short OffsetLow;
& W2 ?! m" {4 }6 v; }: g  D* Munsigned short Selector;
0 X7 g4 y( K' m1 @6 Wunsigned char Reserved;
, a5 U) i1 S- p3 o; vunsigned char SegmentType;. s9 O3 f4 _4 I5 ?
unsigned char SystemSegmentFlag;' z7 h. `1 x/ s
unsigned char Dpl;
% e' B! m3 B- @( ounsigned char Present;
- y2 a1 C+ N7 z( g4 Gunsigned short OffsetHigh;
% _8 N- S2 V, C! G" N} InterruptGate_t;
' O  n5 i, p- x& ]" |% |
  N# U7 d, y7 r. x  H4 r, p在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
- H3 V9 _4 Q) r& C  d/ W, m* k! ?- Z+ `( q$ K
[ 读者小强:Winnt和2K的IDT中有空表项吗?]% M7 R6 q% O. @0 T
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。3 S6 f, Y# G: R% S5 ^6 Q/ H: |
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
+ b$ a2 e6 \6 J4 G: U* QNTSTATUS AddInterrupt()
7 q) X; N1 L- M5 r2 u{6 _7 a. H  [) T; [/ z1 l7 m" U
PIdtEntry_t IdtEntry;' P, E  g# D" _

5 z2 b/ h2 F" T  g8 _8 K2 _' _/* 获得IDTR Register 的Base Address和Limit*/. n! f1 u6 `0 g% A! Y  V
_asm sidt buffer1 @" |9 G, I8 u5 f+ i* }3 e8 J  a
IdtEntry=(PIdtEntry_t)Idtr->Base;9 M5 P- {, A5 p1 E- G2 G6 O
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))$ n) t" I6 N" A
return STATUS_UNSUCCESSFUL;
& l' l: `$ C' X  M5 d3 h. ?, u4 z* P+ z% }5 C' T& Q' g8 @) x6 L
_asm cli
8 P; \& n2 d2 d% o% Q6 x6 a6 J4 S3 W3 g
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */  ?4 T1 e0 L  a4 i7 V
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;# ^" M/ K2 h5 p+ y
IdtEntry[ADDINT].Selector=8;" I! ?( S' A1 E0 h. ^
IdtEntry[ADDINT].Reserved=0;
# }5 Q- K! N  X  y' KIdtEntry[ADDINT].Type=0xE;/ P* U6 N" D, i, m/ Z1 g3 y
IdtEntry[ADDINT].Always0=0;9 {* \) Z1 _4 _8 @. J" I/ V* {
IdtEntry[ADDINT].Dpl=3;7 l) N2 J8 [& y0 B/ u3 u
IdtEntry[ADDINT].Present=1;: j  h) k8 {" Q
IdtEntry[ADDINT].OffsetHigh=5 a* M2 {9 y$ D' y/ ^+ X2 [
(unsigned short)((unsigned int) InterruptHandler>16);
& v3 G# x, n' `9 X( W8 m. b_asm sti, }. J2 ?: |2 C3 C) w" {# E

% A5 _4 {0 T# w' g+ S- k2 vreturn STATUS_SUCCESS;2 c% O9 V. Z6 j* ?4 E
}+ @8 F0 P4 {" t! [% [2 V9 v" H% G1 `
$ u7 k7 X# s- H) N
让我们就上面的代码提一些问题。
1 e0 S& {' t" W' r! R! `0 fQ: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
  y0 I8 f( D* x5 i& }A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
6 A. E' A8 n0 q7 l个段的名称就是8(CS=8)。
; y# c5 Q0 W& X' p  T6 w: a3 A  L- O. k) k
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?. ^1 I3 ?" V3 F. ^: q
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
& l9 c# q. c6 w5 A应用程序的DPL=3,所以,可以直接触发这个中断。& C5 ^. ~, K& ^. @* @
- ^. C  C0 T8 @0 `  l: u
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?( B1 j0 D9 J! m' x. n
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
& u; t* N" a/ s. v  M# G7 v+ P! y:idt4 ~1 `; F  W0 u7 z: J) D% h
Int Type Sel:Offset Attributes Symbol/Owner% d* }1 S5 W7 R+ Z
IDTbase=80036400 Limit=07FF
8 P% \: F6 t  ?0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
, L$ l9 v: T" t) G$ E& _+ T0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
+ M; u2 L- }9 @' U5 R……
# F; M( j8 ]7 ?; |可见,0xE表示IntG32。8 m1 f- r/ p6 ^! w3 i8 q. X

5 C& X: S9 S6 j, y- U  |; j; D0 p7 m0 B终于快到下课的时间了,让我再多说几句好吗?# B# ]5 x" B, F
通过对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-8-27 22:50 , Processed in 0.275337 second(s), 52 queries .

回顶部