QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:" z# P; Q( h8 t& l) O9 C* I8 t
--------------------------------------------------------------------------------, V9 m; e& Y* g( X" w; T8 A
作者:suxm < suxm@nsfocus.com >" \  L8 K! Q- U! l
主页:http://www.nsfocus.com5 I/ u+ c3 y0 T8 Z+ F
日期:2001-09-10; Q3 H0 f3 v2 R$ F. w3 K% x

/ n( s, E+ p6 |# e5 P6 |0 |<<接上期>>
- d- @$ O* d4 e6 B3 b3 e
; D1 v# X# F0 K* l; N- qIDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。
4 e; @! Y2 t: ?3 p; J4 f1 ?; Q& v* U$ j* l9 `7 ?8 p. {
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]  M8 u' I% X/ y2 G; P5 W; e
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。  Z- \3 U; z: g5 z8 V) s
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。
/ r  g; i9 u" H1 S% n9 r在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。: a' h+ P$ S3 A, m( k
通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。  y- }9 A0 d- V9 F# W( |* p
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]9 L, o# p3 I3 I/ b' `8 ~
[ suxm: 去看看上一期月刊的图1 ]
. c& {8 h5 D, w; z* ]下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
7 _3 A, q6 W5 J& A/ S1 A" T
5 k+ y) z/ _- n- A7 o& C/ m: W/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */9 \5 F% C0 H. s6 \/ L* L8 h
#define HOOKINT 0x38
7 D7 |  M) w& fNTSTATUS DriverSpecificInitialization( )
* n- `0 a$ Z* @: U# Q{5 V9 _- n& U; A, O( J8 i
PIdtEntry_t IdtEntry;
5 m: {$ \7 Q$ \: f, y, `  Rextern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
, G6 E- {' ^  z5 u5 c! j3 x0 U- n0 a4 Z7 Y& K
NumberOfServices = KeServiceDescriptorTable->NumberOfServices;
# H+ h7 u7 h7 z! W+ IServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);
) G2 J7 }8 o- |- cServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);: o2 T1 Z% }1 y' ^* w2 `6 m" k

( b4 G; J' n! x7 b# X8 |- ?% b! X/ @/ l/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */9 _3 o9 k; k4 S" G- V, K* O
if (!ServiceCounterTable)7 Y( X6 O: q+ x  |1 z9 @) D  n$ v
return STATUS_INSUFFICIENT_RESOURCES;
, B, c2 ?: l; q% u/ @6 `: ?' U) Q
memset(ServiceCounterTable, 0, ServiceCounterTableSize);
: O  H, j4 \/ h# B5 y*ServiceCounterTable=NumberOfServices;) g+ R* K- o3 Z7 Y0 K! m
7 h/ n) C6 H7 }/ @  i
/* 获得IDTR Register 的Base Address和Limit*/
$ m/ J3 C6 c8 C& c8 e1 p2 I* T_asm sidt buffer
2 ~2 c8 e* k" n. MIdtEntry=(PIdtEntry_t)Idtr->Base;
' W$ d& ~3 F2 N# K7 L' P: _+ e! `
/* HOOKINT就是我们想要hook的中断号
1 \) O5 E: X6 t0 M这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
& \( N2 i4 I+ v6 f8 v' Q/ p7 `OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|. s8 d& m/ L+ M6 ?' N7 _. o2 }" m
(IdtEntry[HOOKINT].OffsetLow);, R7 i, o7 J+ @0 j2 B/ f0 J) \

  ]9 \; ]3 w7 F" m2 Z- ^/* 在IDT表项的相应位置写入新的中断处理函数的地址' m, e- e6 V% M- H  F6 `
用新的中断处理函数替换原中断处理函数 */
! D0 }' V% `3 O. a9 \_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */& J- B4 |8 r: X& K% f( A6 ^& M% V
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;; d  @* d6 Q. q7 ^( ^
IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
) A# |) |1 i) G' e7 @! @_asm sti /* Enable中断请求 */
/ a4 X$ g4 U) D( I0 f
/ O" n( k& M( W4 M- o. Treturn STATUS_SUCCESS;/ G' I1 x4 ?8 A/ I5 f
}* r7 u8 t/ K/ ?% T% e: [
% d8 r3 d8 ^  G4 u
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。' E8 v9 c8 [% T) \
typedef struct InterruptGate
" b  I) A- f7 \9 [{5 b) L6 }! r/ }
unsigned short OffsetLow;
2 F! I/ P2 \: \unsigned short Selector;
. z) I# J: R5 H9 E, E! Lunsigned char Reserved;- f, v. {1 u3 R9 e
unsigned char SegmentType;
4 ~3 l6 _% i& p- @& U2 Z8 Wunsigned char SystemSegmentFlag;+ ?- E, ?! k0 ?4 e7 r* {- X
unsigned char Dpl;
6 V' O5 a4 a. w! P% H6 ]unsigned char Present;6 o! L6 j( a4 @
unsigned short OffsetHigh;
2 k6 z; d2 D; ~9 I. v& d4 ?} InterruptGate_t;9 M( q# f4 O' {, d

. P1 A& ]) y4 P% Q: |0 j在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?
% c+ s; R# o! R9 p" c$ }
! N8 K8 w( Z1 q* U9 M& M[ 读者小强:Winnt和2K的IDT中有空表项吗?]
4 @9 l4 L& |2 H1 D+ Z9 G是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。) ^# g) X: F0 R9 J1 N. ^( o
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。2 `) Y5 o( t3 E8 f* |" L: h
NTSTATUS AddInterrupt()+ H! [- g+ ?/ i  Z3 h$ C# u# l4 r
{/ z/ l, w; x; A# S6 x! L! _8 j$ i
PIdtEntry_t IdtEntry;0 @) }9 O& x+ ]+ D
& t, c- Y7 N7 {* W
/* 获得IDTR Register 的Base Address和Limit*/) V5 n. v% H& s2 {& q
_asm sidt buffer
4 P- R* I7 [, Y- ]1 e- AIdtEntry=(PIdtEntry_t)Idtr->Base;2 ~+ Q# I4 A1 U
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))
+ e; ^% Q/ D& z* t! }" a  c! Breturn STATUS_UNSUCCESSFUL;+ p  i  ~( E, g

$ k2 R& D! \& e2 G+ |_asm cli
4 f9 K& s( w2 f6 C0 V# y3 ^8 b4 `
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */  H  T: n+ C+ q6 ?1 R
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;1 a# o2 u; [; L2 S
IdtEntry[ADDINT].Selector=8;
* w5 Z% n, u9 @$ E+ N: LIdtEntry[ADDINT].Reserved=0;
, X! C( {& W/ ^$ M/ r9 l8 |IdtEntry[ADDINT].Type=0xE;: c. h) T, p' y6 v- Z+ |
IdtEntry[ADDINT].Always0=0;
# f- F: W1 J# H, _; Q4 J( n) @IdtEntry[ADDINT].Dpl=3;4 L* E' h/ t. B  k/ z  k* J
IdtEntry[ADDINT].Present=1;
7 n9 L0 i6 m- {8 l, z& a" \7 fIdtEntry[ADDINT].OffsetHigh=
8 e. L/ y# K# p" x* O! |8 I4 I2 M9 g8 j(unsigned short)((unsigned int) InterruptHandler>16);# d: ]9 ^: e$ T  X, ]
_asm sti
; P$ ^3 `$ H. X* c' W- W4 D! f6 i9 u
return STATUS_SUCCESS;
+ D; m5 S  f" V2 q}5 }# N7 E7 \2 N5 i

8 T, v+ _; W5 R让我们就上面的代码提一些问题。* ~% {- ]; ^, r- P! E6 _0 d/ w
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?; d* A( Y0 e5 q. W0 ]
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这' e' E7 K% @* u2 M0 @
个段的名称就是8(CS=8)。/ T4 m7 |% f+ u8 m$ s; d
4 K" t; r( v6 o
Q: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?
5 \/ S# c1 d! d2 U0 a2 _A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为: G9 j# I% {3 Y( \1 r: `6 J
应用程序的DPL=3,所以,可以直接触发这个中断。/ _/ ?0 y( |9 }1 J+ b
2 L: f7 H* b+ ]& C. C- i2 C
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?3 t8 P& b/ M6 \6 ~4 L
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下- @6 w% H; k+ A$ T2 _1 K; P8 e
:idt
; R$ `/ M' M) g  IInt Type Sel:Offset Attributes Symbol/Owner8 f1 [6 A8 Y5 \" E  X
IDTbase=80036400 Limit=07FF( o- v/ a/ G/ \6 {/ n
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590
) R$ o- i) P) C. @) {0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E02 y8 o  y' e" @- P8 B7 A3 {$ f
……
* O: P- x( \4 a- l- ^; Q可见,0xE表示IntG32。
' t" S- Q6 x8 z& v  c) ]  }/ v, B$ b& q7 u
终于快到下课的时间了,让我再多说几句好吗?, n. _, M4 W# H
通过对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-22 09:27 , Processed in 0.543610 second(s), 52 queries .

回顶部