QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:
/ H5 B- P' t9 e* F--------------------------------------------------------------------------------
0 I, x* \6 e% o8 X, K1 W1 N作者:suxm < suxm@nsfocus.com >
7 t" M  R8 j, ?' e! x! P+ U' ]主页:http://www.nsfocus.com
/ L* G$ _6 [3 B2 B# t5 W( Z) q日期:2001-09-10! Y* N0 g" v' [* V) p; b; Q+ x
8 U3 h  a' z  S* Q
<<接上期>>) c0 H: H8 ~6 m" R9 `6 T: h

0 C, X* T( A# C" R* q- F" b, u% U* zIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
6 {! {) y  d( Q1 E; m7 D
; }6 Y( A' {" u[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]
+ Q' e( k) K9 R) P9 E' ]% @呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。
; Q3 F- H3 W$ t# `下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。) Z. D8 O* v: H/ A4 d* T4 C
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
+ Z3 L8 L7 R8 Z, ]! i- k$ V通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。2 R# l$ f0 f" U+ o7 ^& k
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
0 z4 @6 A+ l, e0 F[ suxm: 去看看上一期月刊的图1 ]
) t- L. b' A* h下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
9 @6 R/ [. E( \) m$ F: T% O6 V7 c1 `, U, A( b- B
/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */& `) S( Q1 Y3 m* M# h. ~2 N
#define HOOKINT 0x38# p* H5 q( B" V6 p* Z1 f9 o
NTSTATUS DriverSpecificInitialization( )! w2 P& p3 }% J0 c8 h  Q$ E, F
{  S* ~1 u2 |7 B( M' m
PIdtEntry_t IdtEntry;- W* J3 M5 I6 T+ q
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
# `3 v0 m7 ?/ T! l' _& h- Q9 h
# j1 g5 B4 j/ i1 |NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
$ f8 v, h. L2 I% {, zServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
" M; B( z- O0 j2 z) U, n, U& ]ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);4 S) f5 H; V' x; t
* b3 t4 `# P# D, a
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */4 S5 Q% G  U8 Y2 \9 h% C
if (!ServiceCounterTable)
9 |# d0 e( g& T6 x4 U: Ireturn STATUS_INSUFFICIENT_RESOURCES;; v, H* f3 b) L- e4 z5 J
3 v; t3 L3 ]- {, p0 |
memset(ServiceCounterTable, 0, ServiceCounterTableSize);
+ R0 h' \7 n/ c# h& d- b*ServiceCounterTable=NumberOfServices;# ?& X. d; D0 |$ ^+ G
9 u; k4 s5 ?9 }0 v
/* 获得IDTR Register 的Base Address和Limit*/2 c+ d6 Q  p$ s
_asm sidt buffer- O- e0 s( [3 p/ x8 S  S4 g
IdtEntry=(PIdtEntry_t)Idtr->Base;  I( q; O7 n8 M6 Q
+ ]1 A$ }8 w9 j' h) q4 V' a# n5 U
/* HOOKINT就是我们想要hook的中断号
4 H+ d# O. C/ l  v* V这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/  m3 }3 x2 D9 N. X
OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|  P2 L* u: F* I! k
(IdtEntry[HOOKINT].OffsetLow);
7 E& S3 X- Z+ S9 O3 u- q
5 E# W$ b- x9 K2 x/* 在IDT表项的相应位置写入新的中断处理函数的地址0 L3 }  w; Y  z  {3 r' U
用新的中断处理函数替换原中断处理函数 */- G7 _# Z$ M9 m& c7 h: u; D( u
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */$ ~) W- D; a, U6 _
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
) e6 z* \- ^! w; u4 C+ @IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
; N# r6 u6 r$ b+ B_asm sti /* Enable中断请求 */& t' Q$ Z( u% @- X6 f/ h, n
2 z2 b7 j+ y+ P* ?" n
return STATUS_SUCCESS;% a" e2 [4 {3 P
}" c8 a+ q; x: B/ P! A
5 O2 W# r8 z/ M. k' h$ o6 D& C
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。: i$ v( ~9 y: V  j: V; H
typedef struct InterruptGate/ e2 @9 i5 h( r& D8 ^% U+ p
{
% C* U. w7 _2 o* G& S- \unsigned short OffsetLow;
& H8 S+ h2 Q( a  k" }4 bunsigned short Selector;
1 J9 M$ d) F2 p! Z& Iunsigned char Reserved;
) g- l. o- B, M% O# ^: O3 W8 wunsigned char SegmentType;
8 i0 T" g, e- n. y1 J/ Cunsigned char SystemSegmentFlag;
4 i' o: e2 W4 F4 Uunsigned char Dpl;! C1 D/ T+ o1 ~0 W, U. ?* B; F8 z
unsigned char Present;
1 x9 V5 X4 l! \unsigned short OffsetHigh;8 ?' U/ M" @4 N1 ?8 e
} InterruptGate_t;
# A8 E' \$ X% {# K8 p1 }5 O& C
( h% c- ?) o- X/ u$ D! y! L在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?) L9 I9 e) e1 ^6 Z3 C0 O* d% _& c

+ s. `0 H! t! Q3 D( s* J[ 读者小强:Winnt和2K的IDT中有空表项吗?]
1 F4 p8 g, S9 a: q是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。& [5 |  B) w+ E4 z6 u' ?  o
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。1 \3 |2 r. c- p7 J
NTSTATUS AddInterrupt()
6 N/ B& x' U. ^- ^0 j: E. Z6 q# [* f{
% a! [9 c0 z" l: h$ Y" pPIdtEntry_t IdtEntry;4 g; O! i& r0 |# [; a6 k& V" x
# }7 d/ S7 Z, F' N! \
/* 获得IDTR Register 的Base Address和Limit*/
4 F2 A: v& B" P7 p9 n7 [- T+ g$ ]_asm sidt buffer" r- j2 l, @) s( y
IdtEntry=(PIdtEntry_t)Idtr->Base;$ Y% Q8 e' K% }; b2 T' B
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))2 O9 g8 `& |! b- x5 C
return STATUS_UNSUCCESSFUL;  Q! g7 q. J, A0 U

; P% l2 A, b( \7 l- S6 R_asm cli- v$ b8 e/ h0 x0 n1 q; Y8 x

; B& D1 A+ v8 s/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */9 i# A) C! G9 B* O
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;+ t9 s3 Z# p* q' p( `+ m; F
IdtEntry[ADDINT].Selector=8;( w( V" B, ?% j1 \1 [
IdtEntry[ADDINT].Reserved=0;! v: y1 Y9 s$ j; D
IdtEntry[ADDINT].Type=0xE;
& n4 s: I2 @' JIdtEntry[ADDINT].Always0=0;
' n7 b4 M  f9 a5 z' z1 NIdtEntry[ADDINT].Dpl=3;
/ z+ F6 p; U, C0 C/ j- B, ~, cIdtEntry[ADDINT].Present=1;
2 T( K) |  L0 u! ^& dIdtEntry[ADDINT].OffsetHigh=
6 K% Q9 e+ _. ]% d( @& r" H+ @(unsigned short)((unsigned int) InterruptHandler>16);
9 W3 E! H4 O2 i0 y2 I_asm sti( n, L$ v2 T% N

! e: P, z. z% s$ Z9 y# c" [return STATUS_SUCCESS;; E3 E8 f0 V" V: s) g+ e
}
0 P, U, O9 [, e! x% \3 G5 ^, @% Q0 b  s/ c) i: j4 _0 H7 I
让我们就上面的代码提一些问题。
) {8 I. F; O+ y6 z7 ^! ?+ wQ: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?
6 F0 G4 U8 M" V& N2 q! zA:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这
  \( C9 a$ @: D2 `# A, a. e个段的名称就是8(CS=8)。
: E3 u- e/ t8 @9 k) R/ O# @2 H; J) y* {: \
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?4 ?7 G% R3 [; j& G1 a2 m0 ?2 p9 P2 y
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为
( m& }$ y. `/ C3 T, _. }应用程序的DPL=3,所以,可以直接触发这个中断。
3 f8 w6 K+ `: p3 l, U$ N- [" J9 N- p8 ]" a( }
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?7 ~; S8 D5 s) s' d# t) T
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
2 g- g; C" [% v( Q+ [0 L9 i2 f& }, b:idt
  u; H: n+ |: U* V  sInt Type Sel:Offset Attributes Symbol/Owner
* [9 h: z# M7 v3 n9 rIDTbase=80036400 Limit=07FF1 C# j! ]* G& k% m, X
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590( h* N; F& }; w* G9 X! V9 G
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0
1 |" g& o: c' L$ O/ X5 I/ U……
! r6 }3 ^# t# {9 I: b: A% @可见,0xE表示IntG32。8 P  y) T3 N! A- {$ x- {
& ]) `  g* Z6 U9 C6 s
终于快到下课的时间了,让我再多说几句好吗?
4 Z( A4 E. r! m) N7 S# n通过对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-6-15 01:49 , Processed in 0.425503 second(s), 51 queries .

回顶部