QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:" T# C# s& a2 a/ S
--------------------------------------------------------------------------------
, l& Z$ g6 e" ^. E) N作者:suxm < suxm@nsfocus.com >
1 @- `7 j- R7 a8 L, ?6 z! r+ a5 C主页:http://www.nsfocus.com
0 r5 t" Y  U4 s% p  d: ]( q日期:2001-09-107 [  F/ x# U$ H9 J" h0 _) \; @
+ a) s; ~) }0 ^6 x0 @
<<接上期>>
+ h  E5 S4 A) z0 W
2 V$ ], X/ L5 P& KIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
7 Z( ?/ Q' `% s+ `7 T9 k4 j% y9 V' y" V' m6 O
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]
" X' h! J, L2 V: q: E呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。+ Y! O' A' g+ [( c1 U$ C6 X: [4 s" y
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
# \% V: T( y3 ~. Y; ?9 P在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。$ I: A9 n% }: e# i! ]& ~- G& L% ~
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。- G) F1 J. W2 J6 J/ S8 ?% B; Q
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
* c2 n/ x9 d/ m: \7 V* x[ suxm: 去看看上一期月刊的图1 ]( c2 D& |& c6 Q
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。  R  n7 b+ q9 h3 q% ~9 V$ S

) b% w/ Z9 S; q3 m! m! c# J4 i& M/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */+ p) e5 n$ G$ B$ X1 g$ D
#define HOOKINT 0x38- @( R; r/ d+ T
NTSTATUS DriverSpecificInitialization( ), Q* J+ j  p9 }* k, e: _  Y
{
0 J& k2 K$ c8 t' p- A7 p& [3 pPIdtEntry_t IdtEntry;
9 L* @+ J6 R- L; j6 Oextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
4 V! C, b4 C! ?1 l/ M
# J4 n1 i9 ^& x/ f( {8 MNumberOfServices = KeServiceDescriptorTable->NumberOfServices;
+ L5 Y9 ^: q. d! u7 T3 `1 ]. W5 L; eServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);6 F' Y- Z. ?; G+ U  v# Z" d- E  D
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
- A/ l' C5 T, r9 ^8 M% B, M+ @( S" g7 O% z6 v' I# ?7 q
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
" M8 Z' m: L4 X0 C& R/ D* k% Qif (!ServiceCounterTable)( x6 ]3 R( m- o# ^! d3 H3 r
return STATUS_INSUFFICIENT_RESOURCES;
, p. Z9 y5 ]3 K( X) R+ i
0 B9 V2 N" O( _% }: L8 j5 Fmemset(ServiceCounterTable, 0, ServiceCounterTableSize);5 {0 H/ U' ~0 i3 i1 o( K0 _( I
*ServiceCounterTable=NumberOfServices;3 J& r" T* f9 e7 ]
# w# Y3 F! \- Q- s
/* 获得IDTR Register 的Base Address和Limit*/
$ {2 U3 O& H5 T. g* F  ?. __asm sidt buffer& {4 g6 r' D& n$ v! |  E0 r
IdtEntry=(PIdtEntry_t)Idtr->Base;" ?$ v+ t$ Z" Z& s6 j
- |! Y( Q4 V& a2 a
/* HOOKINT就是我们想要hook的中断号
) N; U  x; M1 W! v* Z这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
2 v( E: ~; S# M- X2 f9 VOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
( J* h7 Q, |' _' q) d8 N(IdtEntry[HOOKINT].OffsetLow);- w( ]" Q- f- F" f% v- h+ v1 C
" d6 F( Z0 x8 v' S, n+ x2 n
/* 在IDT表项的相应位置写入新的中断处理函数的地址* p6 i1 c* C5 [) d7 `4 v& N
用新的中断处理函数替换原中断处理函数 */) m( d0 H3 [+ D. x: [; A0 d; h
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */
0 S" f; f4 V$ P% gIdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;: E- ~0 C6 @, f2 U
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);, D1 w2 _5 H1 C  x* I
_asm sti /* Enable中断请求 */# W* v2 x9 @2 ^: G
9 |3 s7 Z8 k9 U9 u$ D0 w
return STATUS_SUCCESS;/ ?) G/ G* L1 I& s: \: [/ _
}
; ^; ~) {; q$ ]/ J* {% F% X, R6 U
( t+ l4 l; j4 e- G7 M, x上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。
: B" V9 m$ f* I4 ctypedef struct InterruptGate' D+ f' U( P+ N" [$ y, N
{
/ q) u5 S+ v* zunsigned short OffsetLow;
, l# R: S* g" d; Dunsigned short Selector;
1 w) m* X/ O6 c5 Q4 }% S9 u+ t) h  [unsigned char Reserved;7 P5 V: n4 D% R: `# T. K
unsigned char SegmentType;
- I2 a. l! _* U. O/ ?; runsigned char SystemSegmentFlag;9 ]: r1 E8 O4 r( Z
unsigned char Dpl;, Z  |4 V& Z) W' i( a% W, v$ Y. k
unsigned char Present;
# |4 C# T3 B: _& L, l4 N; vunsigned short OffsetHigh;
- _2 v, r. ~6 [! ]; `- P} InterruptGate_t;
+ {+ |* Y2 n6 t* I; I: b, b
& y: Z, c: E" p$ Z在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?5 X3 h6 |- l- ]/ K
# S+ W% s+ w, O; O
[ 读者小强:Winnt和2K的IDT中有空表项吗?]
0 f3 k# O. H1 x' G8 B7 ^2 S+ k; m& U3 |是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。
0 N) U7 m8 t4 s4 G# M下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。* P( O6 l2 w+ S7 H6 L% `
NTSTATUS AddInterrupt()
3 u# @8 O2 ^% d9 i# \& B{
4 ~( E8 e- h* T- k( o, FPIdtEntry_t IdtEntry;' `& e% i1 A7 S  T$ R) _6 I
( J7 t* q' \9 @$ F
/* 获得IDTR Register 的Base Address和Limit*/5 d- o7 B, d# x2 n  {
_asm sidt buffer
# K# _  h3 |) n# O" ^. J: |IdtEntry=(PIdtEntry_t)Idtr->Base;
6 o5 W& l4 t  ?7 s8 U% |if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))- `6 O5 V6 b; @- U; G; O
return STATUS_UNSUCCESSFUL;
& A) r0 v# P  n! [" Z3 Y3 o6 |* [! ~, a
_asm cli$ E" _1 x% N$ v  \# A* Q
$ o" B* {- K) F
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */
" d5 `: ^# O$ v7 m$ ?+ e6 W' yIdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;" x4 h2 f- ~8 Z% p) O9 ~$ r
IdtEntry[ADDINT].Selector=8;# o. k+ d4 r" \& u$ A
IdtEntry[ADDINT].Reserved=0;. b$ |7 I$ n: B9 V1 E4 `& _
IdtEntry[ADDINT].Type=0xE;
# @4 Q0 e" ~3 h; Q/ o% f0 n& yIdtEntry[ADDINT].Always0=0;
5 F8 A1 `! P3 E# C4 iIdtEntry[ADDINT].Dpl=3;
/ S9 ?5 _) `7 a; O; KIdtEntry[ADDINT].Present=1;+ C& q1 \1 |. z6 U
IdtEntry[ADDINT].OffsetHigh=* ]+ T3 m7 O5 t. C: k' H
(unsigned short)((unsigned int) InterruptHandler>16);
- Q$ M: d; c9 r, K4 m4 |* E% l_asm sti7 l4 m1 v' i' d/ Z! ^# m0 n6 l2 [

/ t7 r  C# }# d! K. g+ u5 y# x+ Nreturn STATUS_SUCCESS;% ]3 U. ]5 n7 A  A3 K7 v% [% i
}
3 Y8 ?6 v, M" h$ h3 n7 u+ Y9 a4 T- L, @* Q- K7 f3 ?1 n3 n
让我们就上面的代码提一些问题。, {& F8 s7 S- E1 w# n! m/ }
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?9 W0 T# v4 b: w
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这: t9 w/ O5 d6 n0 B" h, ]; {; @
个段的名称就是8(CS=8)。  q8 I5 R+ a2 X& U) f

) B/ ?. d. J% m: C! y% P/ @Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?$ _$ j' B& B$ r3 j, J; Q
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为/ v9 b8 Q/ u: P. u
应用程序的DPL=3,所以,可以直接触发这个中断。
. B  T) V, Q8 U, l! k
% L/ d9 J& `- }2 ~Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?
( ~4 w% G% ?! A' ^4 hA:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下
% p: o7 \; b! l/ d6 B:idt. E2 R6 C7 M4 n7 E0 n2 i; ^
Int Type Sel:Offset Attributes Symbol/Owner
: d3 ?& a, T& G& jIDTbase=80036400 Limit=07FF4 z) M4 k/ i3 v8 {  x! K
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590* Q) R8 J$ T  m' z& m
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0: z3 I; E. U, |$ Q0 a
……- A/ r- K# C& _9 S* X. H1 \( [
可见,0xE表示IntG32。
& l! R: Y- n2 J6 Q% X. }, Y+ X$ b+ ?) w
终于快到下课的时间了,让我再多说几句好吗?
, Z# i5 G: |- I- {通过对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-14 05:30 , Processed in 0.399936 second(s), 51 queries .

回顶部