QQ登录

只需要一步,快速开始

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

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

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2004-10-9 14:30 |只看该作者 |倒序浏览
|招呼Ta 关注Ta
文章内容:: {, `/ c5 M$ C; d9 t
--------------------------------------------------------------------------------
: E: \7 D# e5 T作者:suxm < suxm@nsfocus.com >! g# G% h: J+ `9 [) {# d1 f
主页:http://www.nsfocus.com+ R0 B7 d+ {& Z# u& T5 a
日期:2001-09-10+ i4 R$ n) B+ e) m/ d
( J! p! f& C" ]. m2 {9 E
<<接上期>>
3 h9 ]2 Y1 K7 r4 _6 z# d+ v" f/ _: `9 k9 u/ l
IDT是定义硬件中断映射的表,当硬件中断发生的时候,CPU会直接把控制权交到IDT的相应ISR中去运行,根本不去关心是否有Kernel Interrupt Object(CPU根本不知道Kernel Interrupt Object是什么)。8 p3 f. P8 f( H/ Z6 b) [
% S( R) ?! I% k8 S) Y0 G2 i& A' G6 e
[ 读者小强:啊???!!!想不到Interrupt Object这么不重要???那Interrupt Object在中断发生的过程中扮演着什么角色呢?]) a0 ?# H3 j6 j8 H  t2 m0 r, v
呵呵,别忙,Interrupt Object还是很重要的。当你调用IoConnectInterrupt()的时候,Kernel会构造Interrupt Object. 然后Kernel把Interrupt Object的地址放到IDT的相应表项中。这就是说,当中断发生的时候,CPU会把控制权交到IDT相应表项所对应的ISR中,而这个ISR,就 是Interrupt Object中的代码。现在明白了吧?我们可以这样理解,中断发生的时候,相应的Interrupt Object被调用。- u$ z3 c2 t, ^7 S
下面我们将要演练一下如何在NT下hook系统中断,以使读者对以上基础知识理解得更加深刻。( l/ N& R8 F0 |- G/ a2 w( i) B
在开始激动人心的练习之前,我们有必要再强调一下上面的几个概念:Trap/Interrupt gate描述符、IDTR、Interrupt Object。
4 f- u, X, n% g7 N通过sidt这条汇编指令,可以获得IDTR的内容,由此,可以得到IDT的Base Address(基址)和Limit(最大的长度限制)。一旦得到IDT 的Base Address,我们就可以找到想要hook的中断号,然后@#@!$!@$@#$@#%$^$……,对不起,刚才晕倒了,太激动了。然后,我们改变 那个我们想要替换的IDT表项的Code Segment Selector和Offset,当然,别忘了备份好被替换的Code Segment Selector和Offset。并要确保新 的中断处理函数能正常调用原来的中断处理函数(这就相当于把新的中断处理函数插在了原中断处理函数之前,但是不影响原中断处理函 数的使用)。+ [. J# O, O0 L. p4 j
[ 读者小强:什么是IDT表项的Code Segment Selector和Offset呀?]
0 O) R+ c/ A2 x! K[ suxm: 去看看上一期月刊的图1 ]
8 o' E6 `: J* T- y( W下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、替换并备份IDT表项”的功能。
% E1 v4 J6 p7 p' Q9 L5 g
% o( P5 ?8 i+ T- X$ H7 O/* 在Winnt和Win2k下,RTC时钟中断被映射到IDT的第0x38项 */; l2 E5 \+ s  l  H, v0 o2 b" t
#define HOOKINT 0x38
; F! V, k0 y3 q1 l8 wNTSTATUS DriverSpecificInitialization( )* B) {2 X8 K1 r
{
4 X& \; [2 H$ C" Y& OPIdtEntry_t IdtEntry;( u0 p* a: f- @9 u! K. p; ^- e9 t
extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;# Z. N2 w7 h* H& W. w& e+ c, D

" I9 F' A, O' T4 Y% Y( r* bNumberOfServices = KeServiceDescriptorTable->NumberOfServices;
3 ^! A; U! Y1 Z& V; H8 M0 ]ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int);& A8 H' |& `: Q, A$ M; O) e
ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
8 @5 M) ~* p2 u3 K7 A# {# g. n8 F; Y# k! b% N
/* 有必要检查一下,有时候内存不足会导致ExAllocatePool返回NULL */
/ U8 Y2 D) _! n9 Gif (!ServiceCounterTable)+ @- V" x9 r- k7 G5 Q1 D. u/ C  ~% I
return STATUS_INSUFFICIENT_RESOURCES;' A- K; J6 ]* W2 H8 B7 u- Y: M

2 Q2 G3 C1 k1 K4 bmemset(ServiceCounterTable, 0, ServiceCounterTableSize);' W. P/ \$ t; h
*ServiceCounterTable=NumberOfServices;6 m' Y* y' W7 J9 r
4 s4 L6 M9 @: l7 ?! J
/* 获得IDTR Register 的Base Address和Limit*/
1 A5 m/ ?/ o  p. M, S_asm sidt buffer/ e7 M( i* h/ M* J
IdtEntry=(PIdtEntry_t)Idtr->Base;. R6 m& x# K& q2 x% X6 ]% m2 m
/ ^& g8 Q4 _. F/ T) k/ s" B+ @
/* HOOKINT就是我们想要hook的中断号
$ o! A' m, L) _9 ?- F% Z8 W这条语句的作用是找到我们要hook的IDT表项,然后备份原来的内容*/
8 Z6 H1 N# U. {# b% q1 AOldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)|
6 B7 @8 \' k; i! \) @  x, B(IdtEntry[HOOKINT].OffsetLow);9 e2 V& H2 ?. e' m$ C2 Z
5 H8 d! Z# ?& o; a) G
/* 在IDT表项的相应位置写入新的中断处理函数的地址# ^$ O, [5 ?3 i7 ]6 q
用新的中断处理函数替换原中断处理函数 */3 K+ y1 ]6 j) r
_asm cli /* 这里注意,因为中断说不准会在这时候发生,所以要先diable中断 */' {/ o* N6 H7 ~" {
IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler;
2 I' [2 E4 u2 O( |IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16);
/ s9 G" v2 p+ Z' y( t0 r& X" _- D$ k_asm sti /* Enable中断请求 */
$ }2 ^1 A; q1 ]3 _5 W5 u
5 K) E/ m8 K! a' K: _# E0 @return STATUS_SUCCESS;
" G5 C0 S% H! h9 n# q5 c8 E7 ~}
- l; f) E+ G% r0 L. e3 V. a3 o" R' F( z% M/ B9 S# G+ g
上面是对IDT表项进行hook,我们只是修改了相应表项的Code Segment Offset。经过替换后,新的中断处理函数与原中断处理函数的“性 质相似”,即表项中其余的字段相同。如果读者对这句话不理解,那就请看IDT表项的数据结构。  v  O5 b! Z7 U) e0 b, I5 V3 ^
typedef struct InterruptGate  ^( _' A( d5 ~& ~8 J. I. p; r
{9 X$ D! }9 r& y8 ~
unsigned short OffsetLow;
- A2 d3 x  ^2 b4 [unsigned short Selector;2 \2 k; E- b; O7 S% l
unsigned char Reserved;
6 V( y; D$ m+ M/ sunsigned char SegmentType;
3 G+ S- p2 _' y: \  gunsigned char SystemSegmentFlag;  b' k/ d3 p# |$ C! \2 w+ T
unsigned char Dpl;$ ^3 [8 S- ~- l$ i3 g
unsigned char Present;* m& Z2 k2 m# i% M4 X
unsigned short OffsetHigh;1 E; u, `: u( \
} InterruptGate_t;
8 f: I1 U1 g$ w) V, M+ c
( g3 v: O* m: a, Z: {- D, }' s在上面的例子中,OffsetLow和OffsetHigh被改写了,从而hook了中断。让我们思考这样一个问题:如果IDT的相应表项没有内容呢,也就 是说,如果我们想让IDT的空表项指向我们的中断处理函数,又该怎么办呢?4 N( l# i0 ~; u+ Q( r; y

- l, ^1 y4 ^7 r[ 读者小强:Winnt和2K的IDT中有空表项吗?]4 V* z9 Y; Q& Y: }2 K; @
是的,比如说,在Winnt下,IDT表项的第22h---29h就是空表项。 这就是说我们可以给Winnt加入新的中断。但是,这里有了新的难度, 我们必须认真理解和填写上面数据结构InterruptGate中的所有字段。0 B4 W4 X. Z. _- m) u( Q& T/ ?7 C: ?
下面这些代码出自《Undocumented WindowsNT》,实现上述“搜索IDT、填写IDT表项所有字段”的功能。
/ X4 j8 j- c9 J+ ~) Y$ I5 KNTSTATUS AddInterrupt()
4 ?* p, D1 I+ }/ v{5 _& _/ [" R" q% x3 e- K
PIdtEntry_t IdtEntry;' r( Q' W1 I) L
" E9 k6 g# T: G/ O% _" O" c$ m
/* 获得IDTR Register 的Base Address和Limit*/
* w, |, P9 R* u  d_asm sidt buffer
5 V' d7 ^# K* N4 x8 IIdtEntry=(PIdtEntry_t)Idtr->Base;, }% ~# Y6 M6 y8 V% t# F
if((IdtEntry[ADDINT].OffsetLow!=0)||(IdtEntry[ADDINT].OffsetHigh!=0))9 e4 T. K! E) L& Y# a
return STATUS_UNSUCCESSFUL;
* T& j$ e# P9 ~3 f( S6 @
% K  n  ?* H5 ^$ d" I_asm cli
5 C+ R" p+ S; D+ o+ v8 V3 W4 V& U* u# [6 k  Z& @( o" P
/* 填写IDT表项的所有字段,使新的IDT表项指向我们的中断处理函数 */8 D+ g2 W  _) {2 i* I' `2 ^
IdtEntry[ADDINT].OffsetLow=(unsigned short)InterruptHandler;
% B6 E; r* x$ C) P0 i# |IdtEntry[ADDINT].Selector=8;) D& \) H  D  A7 o
IdtEntry[ADDINT].Reserved=0;9 [( E7 L* ?4 b2 I" W% h
IdtEntry[ADDINT].Type=0xE;$ R7 O$ R! M( D8 m$ I- o
IdtEntry[ADDINT].Always0=0;4 t/ Y# A: [' N3 o; P
IdtEntry[ADDINT].Dpl=3;
( C% u- L, P  J, r2 CIdtEntry[ADDINT].Present=1;
( ?3 }. O- E. a  qIdtEntry[ADDINT].OffsetHigh=
. m0 u) d- V) V- L9 {) R! V(unsigned short)((unsigned int) InterruptHandler>16);/ \) N" u0 E4 ^; x. c& |
_asm sti
) o0 T, P# y6 r2 f" t/ Q7 J3 s; g" C) @( [; y9 _% B) O
return STATUS_SUCCESS;
( w) K6 @" a% ]$ }0 u" G}
9 }9 R0 m& ?2 ~9 ?8 ?, s! p
/ d( H/ ~6 m6 V# ~让我们就上面的代码提一些问题。9 U9 N% b, f, s9 G, `
Q: 上面的代码IdtEntry[ADDINT].Selector=8; 这是怎么回事?+ R, g9 w1 G; J0 Y. E# ^$ T( U
A:按Ctrl+D进到Softice里,你会发现,所以的system代码,都存在于一个段中。这$ `9 U% f/ E5 \0 p+ ]
个段的名称就是8(CS=8)。. r+ C' J9 c1 ~+ t

' C/ p- a0 n& e7 R; oQ: 上面的代码IdtEntry[ADDINT].Dpl=3; 这是怎么回事?. K" V' n9 D! c8 S/ Y3 J" P; H
A:问得好,小强。这说明,这个中断可以在应用程序中,通过int xxx的形式触发。因为6 D; T4 H- ?% m/ c  O: H+ Z
应用程序的DPL=3,所以,可以直接触发这个中断。
7 }  u% M/ ?& a+ k+ s8 i; g; b' ^. a$ d' p2 ?
Q: 上面的代码IdtEntry[ADDINT].Type=0xE; 这是怎么回事?% B# z' h. y* h2 w1 U" g. A( {7 u
A:按Ctrl+D进到Softice里,然后敲入指令idt。输出如下, g8 ~8 a! U8 B# g) }
:idt
% r% ?& L% B) aInt Type Sel:Offset Attributes Symbol/Owner
: W  @, p  u- D' O; nIDTbase=80036400 Limit=07FF1 O) e9 Y5 m) X0 H! T- b: O) P( z
0000 IntG32 0008:80465946 DPL=0 P ntoskrnl!Kei386EoiHelper+0590$ o6 `% Z; X% o5 k+ L) \
0001 IntG32 0008:80465A96 DPL=3 P ntoskrnl!Kei386EoiHelper+06E0" X$ g; C* ^3 o. F  O* ?; {
……4 @" E& z. Z. X' c7 L, H: |% U7 S
可见,0xE表示IntG32。
' b* w/ h/ {7 i( X. h6 G5 q" C: _! H0 |. ?
终于快到下课的时间了,让我再多说几句好吗?
- M% Q! G% F- a" f4 }! J6 `" {& |通过对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-28 20:28 , Processed in 0.811181 second(s), 51 queries .

回顶部