关于idt的基本知识就不进行赘述了,先看一个例子
0x1000: mov eax,x1006: Int ;------->进入内核,找到中断处理例程KiTrap03
0x1007: Mov eax,
这段代码执行,触发3号中断,然后开始执行KiTrap03例程,要知道,执行完中断以后还是要回到原来的程序处继续执行的,也就是我们这的Mov eax, 1的指令,显然,发生中断时的寄存器环境就要被保存,便于之后的恢复程序运行,这里,就出现了一个结构_KTRAP_FRAME,陷阱帧。
kd> dt _KTRAP_FRAME
nt!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B //硬件自动填充
+0x06c SegCs : Uint4B //硬件自动填充
+0x070 EFlags : Uint4B //硬件自动填充
//后面都是可选的,只有当我们运行的程序处于虚拟86模式异常才会有
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
整个陷阱帧结构记录中断发生时的寄存器环境,其中,结构体最后面的几个成员只有运行于虚拟86模式下才会存在,而Eip,SegCs和EFlags三个成员由硬件自动填充,然后剩下的成员才是由KiTrap03自己构建的陷阱帧,我们可以分析下KiTrap03的代码,由于在wrk中只有asm文件,没有C源代码,用ida进行分析
.text:00436C50 _KiTrap03 proc near ; DATA XREF: INIT:0077317Co
.text:00436C50
.text:00436C50 var_2= word ptr -
.text:00436C50 arg_4= dword ptr
.text:00436C50
.text:00436C50 push ; Trap_frame.Errcode
.text:00436C52 mov [esp++var_2], ; Trap_frame.Errcode = 0
.text:00436C59 push ebp ; Trap_frame.Ebp
.text:00436C5A push ebx ; Trap_frame.Ebx
.text:00436C5B push esi ; Trap_frame.Esi
.text:00436C5C push edi ; Trap_frame.Edi
.text:00436C5D push fs ; Trap_frame.SegFs
.text:00436C5F mov ebx, 30h
.text:00436C64 mov fs, bx
.text:00436C67 mov ebx, large fs: ; fs对应处理器相关的_KPCR结构,kpcr,那么得到的是
.text:00436C67 ; kpcr.NtTib.ExceptionList
.text:00436C6E push ebx ; Trap_frame.ExceptionList
.text:00436C6F sub esp, ; Trap_frame.PreviousPreviousMode,在后面填充
.text:00436C72 push eax ; Trap_frame.Eax
.text:00436C73 push ecx ; Trap_frame.Ecx
.text:00436C74 push edx ; Trap_frame.Edx
.text:00436C75 push ds ; Trap_frame.SegDs
.text:00436C76 push es ; Trap_frame.SegEs
.text:00436C77 push gs ; Trap_frame.SegGs
.text:00436C79 mov ax, 23h ; ???
.text:00436C7D sub esp, 30h ; 继续增大栈空间,刚好是整个Trap_Frame的大小
.text:00436C80 mov ds, ax ; ???
.text:00436C83 mov es, ax
.text:00436C86 mov ebp, esp ; ebp指向Trap_frame
.text:00436C88 test [esp+68h+arg_4], 20000h ; Trap_frame.EFlags,0x2000代表EFLAGS_V86_MASK,
.text:00436C88 ; 标记虚拟86模式
.text:00436C90 jnz short V86_kit3_a ; 如果是虚拟86模式跳转
.text:00436C92
.text:00436C92 loc_436C92: ; CODE XREF: V86_kit3_a+25j
.text:00436C92 mov ecx, large fs:124h ; ecx = CurrentThread
.text:00436C92 ; +0x004 CurrentThread : Ptr32 _KTHREAD 当前线程 ecx
.text:00436C99 cld
.text:00436C9A and dword ptr [ebp+2Ch], ; Trap_frame.Dr7清零
.text:00436C9E test byte ptr [ecx+], 0DFh
.text:00436CA2 jnz Dr_kit3_a
.text:00436CA8
.text:00436CA8 loc_436CA8: ; CODE XREF: Dr_kit3_a+Dj
.text:00436CA8 ; Dr_kit3_a+79j
.text:00436CA8 mov ebx, [ebp+60h]
.text:00436CAB mov edi, [ebp+68h]
.text:00436CAE mov [ebp+0Ch], edx ; Trap_frame.DbgArgPointer = edx
.text:00436CB1 mov dword ptr [ebp+], 0BADB0D00h ; Trap_frame.DbgArgMark = 0BADB0D00h
.text:00436CB8 mov [ebp+], ebx ; Trap_frame.DbgEbp = kTrap_frame.Ebp
.text:00436CBB mov [ebp+], edi ; Trap_frame.DbgEip = kTrap_frame.Eip
.text:00436CBE cmp ds:_PoHiberInProgress,
.text:00436CC5 jnz short loc_436CCE
.text:00436CC7 lock inc ds:_KiHardwareTrigger
.text:00436CCE
.text:00436CCE loc_436CCE: ; CODE XREF: _KiTrap03+75j
.text:00436CCE mov eax,
.text:00436CD3
.text:00436CD3 loc_436CD3: ; CODE XREF: _KiDebugService+7Aj
.text:00436CD3 test byte ptr [ebp+72h],
.text:00436CD7 jnz short loc_436D08
.text:00436CD9 test byte ptr [ebp+6Ch], ; CS 最后一位,判断是否为UserMode
.text:00436CDD jnz short loc_436CE7 ; 如果是UserMode,跳转
.text:00436CDF test byte ptr [ebp+71h], ; 判断Eflags.IF位是否存在
.text:00436CE3 jz short loc_436CEF
.text:00436CE5 jmp short loc_436CEE
.text:00436CE7 ; ---------------------------------------------------------------------------
.text:00436CE7
.text:00436CE7 loc_436CE7: ; CODE XREF: _KiTrap03+8Dj
.text:00436CE7 cmp word ptr [ebp+6Ch], 1Bh
.text:00436CEC jnz short loc_436D08
.text:00436CEE
.text:00436CEE loc_436CEE: ; CODE XREF: _KiTrap03+95j
.text:00436CEE ; _KiTrap03+C9j
.text:00436CEE sti ; IF标志位存在
.text:00436CEF
.text:00436CEF loc_436CEF: ; CODE XREF: _KiTrap03+93j
.text:00436CEF ; _KiTrap03+D6j
.text:00436CEF mov esi, ecx ; esi=CurrentThread
.text:00436CF1 mov edi, edx
.text:00436CF3 mov edx, eax
.text:00436CF5 mov ebx, [ebp+68h] ; ebx = kTrap_frame.Eip
.text:00436CF8 dec ebx ; "eip-1"是因为int 3本身占一个字节
.text:00436CF9 mov ecx, ; ???在下层函数中会用到,到时候就知道了
.text:00436CFE mov eax, 80000003h ; 异常类型(STATUS_BREAKPOINT)
.text:00436D03 call CommonDispatchException ; 处理异常
.text:00436D08
.text:00436D08 loc_436D08: ; CODE XREF: _KiTrap03+87j
.text:00436D08 ; _KiTrap03+9Cj
.text:00436D08 mov ebx, large fs:124h
.text:00436D0F mov ebx, [ebx+50h]
.text:00436D12 cmp dword ptr [ebx+148h],
.text:00436D19 jz short loc_436CEE
.text:00436D1B push
.text:00436D1D call _Ki386VdmReflectException_A@4 ; Ki386VdmReflectException_A(x)
.text:00436D22 test ax, 0FFFFh
.text:00436D26 jz short loc_436CEF
.text:00436D28 jmp Kei386EoiHelper@0
.text:00436D28 _KiTrap03 endp
很明显,在KiTrap03中没有进行太多的处理,只是判断是否是虚拟86模式来构建了一个不同的陷阱帧结构,之后调用了CommonDispatchException 函数来处理异常。
在虚拟86模式下将会跳转到V86_kit3_a,我们可以看下实现
.text:00436C28 V86_kit3_a proc near ; CODE XREF: _KiTrap03+40j
.text:00436C28 mov eax, [ebp+84h]
.text:00436C2E mov ebx, [ebp+88h]
.text:00436C34 mov ecx, [ebp+7Ch]
.text:00436C37 mov edx, [ebp+80h]
.text:00436C3D mov [ebp+50h], ax ; Trap_frame.SegFs = Trap_frame.V86Fs
.text:00436C41 mov [ebp+30h], bx ; Trap_frame.SegGs = Trap_frame.V86Gs
.text:00436C45 mov [ebp+34h], cx ; Trap_frame.SegEs = Trap_frame.V86Es
.text:00436C49 mov [ebp+38h], dx ; Trap_frame.SegDs = Trap_frame.V86Ds
.text:00436C4D jmp short loc_436C92
.text:00436C4D V86_kit3_a endp
可以看到V86_kit3_a中没有进行处理,只是将陷阱帧中保存的寄存器的值对应为虚拟86模式特有的值。
然后跟入CommonDispatchException 函数
.text:0043641C CommonDispatchException proc near ; CODE XREF: _KiTrap00-253p
.text:0043641C ; _KiTrap00-247p _KiTrap00-23Bp
.text:0043641C ; _KiTrap03+B3p _KiTrap0E+21Ap
.text:0043641C ; sub_671B78+24p
.text:0043641C
.text:0043641C var_50= dword ptr -50h
.text:0043641C var_4C= dword ptr -4Ch
.text:0043641C var_48= dword ptr -48h
.text:0043641C var_44= dword ptr -44h
.text:0043641C var_40= dword ptr -40h
.text:0043641C var_3C= byte ptr -3Ch
.text:0043641C
.text:0043641C sub esp, 50h ; EXCEPTION_RECORD结构空间,ExceptionRecord
.text:0043641F mov [esp+50h+var_50], eax ; eax是上层调用设置的错误码,这里是
.text:0043641F ; ExceptionRecord->ExceptionCode = eax
.text: xor eax, eax
.text: mov [esp+50h+var_4C], eax ; ExceptionRecord->ExceptionFlags = 0
.text: mov [esp+50h+var_48], eax ; ExceptionRecord->ExceptionRecord = 0
.text:0043642C mov [esp+50h+var_44], ebx ; ebx是上层调用设置的异常发生处地址,
.text:0043642C ; ExceptionRecord->ExceptionAddress = ebx
.text: mov [esp+50h+var_40], ecx ; ExceptionRecord->NumberParameters = ecx
.text: ; 表示ExceptionRecord->ExceptionInformation数组的数量
.text: cmp ecx, ; ExceptionRecord->NumberParameters为零跳转
.text: jz short loc_436445
.text: lea ebx, [esp+50h+var_3C] ; ebx = ExceptionRecord->ExceptionInformation
.text:0043643D mov [ebx], edx ; 开始填充ExceptionInformation的信息
.text:0043643F mov [ebx+], esi
.text: mov [ebx+], edi
.text:
.text: loc_436445: ; CODE XREF: CommonDispatchException+1Bj
.text: mov ecx, esp ; ecx = ExceptionRecord
.text: test byte ptr [ebp+72h], ; ebp还是指向TrapFrame,TrapFrame->EFlags,
.text: ; 这里是EFlags的高2字节,判断是否为虚拟86模式
.text:0043644B jz short loc_436454
.text:0043644D mov eax, 0FFFFh ; ????
.text: jmp short loc_436457
.text: ; ---------------------------------------------------------------------------
.text:
.text: loc_436454: ; CODE XREF: CommonDispatchException+2Fj
.text: mov eax, [ebp+6Ch] ; eax = TrapFrame->SegCs
.text:
.text: loc_436457: ; CODE XREF: CommonDispatchException+36j
.text: and eax, ; Cs的低两位得到处理器模式
.text:0043645A push ; FirstChance
.text:0043645C push eax ; PreviousMode
.text:0043645D push ebp ; TrapFrame
.text:0043645E push ; ExceptionFrame
.text: push ecx ; void *
.text: call _KiDispatchException@20 ; 对异常进行分发处理
.text: mov esp, ebp ; 修正esp,然后执行退出操作
.text: jmp Kei386EoiHelper@0
.text: CommonDispatchException endp
可以看到CommonDispatchException也没有进行处理,只是构建了一个ExceptionRecord的结构体,就对异常进行了分发处理,进入了熟悉的KiDispatchException函数。
了解了陷阱帧的构建,再看idt表的基本结构,每个CPU的核心都有自己的idt表,
typedef struct _IDTR
{
USHORT limit; //整个表所占内存大小
ULONG base; //IDT表项起始地址
}IDTR,*PIDTR;
在我的虚拟机上 limit= 0x7ff (包含0) 0x800 = 2048 Entry每项大小8字节,就2048/8 = 256 成员,idt表有256个例程
可以使用kd> !idt -a 命令来查看idt表的详细信息,可以发现就是有256个例程
idt表中的每一项对应一种中断处理例程,结构体如下,我们最关心的是LowOffset和HiOffset这两个成员,他们组成了处理例程地址的高16位和低16位
typedef struct _IDTENTRY
{
unsigned short LowOffset;
unsigned short selector;
unsigned char retention:;
unsigned char zero1:;
unsigned char gate_type:;
unsigned char zero2:;
unsigned char interrupt_gate_size:;
unsigned char zero3:;
unsigned char zero4:;
unsigned char DPL:;
unsigned char P:;
unsigned short HiOffset;
} IDTENTRY,*PIDTENTRY;
利用MAKELONG 的宏,可以得到处理例程的真正地址
#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
idt表的获得可以通过sidt指令或者时_KPCR中的成员获得,在内核态fs段寄存器是指向_KPCR结构。
kd> dt _kpcr
......
+0x038 IDT ; Ptr32 _KIDTENTRY
......
下面的代码打印了每次触发INT 3断点的地址,可以用OD下普通的断点进行测试,可以打印出断点的地址。
ULONG_PTR g_OrigKiTrap03;
KIRQL Irql; _declspec(naked) void NewKiTrap03()
{ __asm
{
//测试
//jmp g_OrigKiTrap03 //构建Trap03的异常帧
//保存现场环境,和原始Trap03一样
push ;ErrorCode
push ebp
push ebx
push esi
push edi
push fs
mov ebx,30h
mov fs,bx
mov ebx,dword ptr fs:[]
push ebx
sub esp,
push eax
push ecx
push edx
push ds
push es
push gs sub esp,30h //esp此时就指向陷阱帧 push esp //FilterExceptionInfo自己清理了 call FilterExceptionInfo //过滤函数 add esp , 0x30
pop gs
pop es
pop ds
pop edx
pop ecx
pop eax
add esp ,
pop ebx
pop fs
pop edi
pop esi
pop ebx
pop ebp
add esp , 0x4
jmp g_OrigKiTrap03
}
} VOID __stdcall FilterExceptionInfo(PX86_KTRAP_FRAME pTrapFrame)
{ //eip的值减一过int3,汇编代码分析中dec,
DbgPrint("Eip:%x\r\n",(pTrapFrame->Eip)-);
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryString)
{
NTSTATUS Status = STATUS_SUCCESS;
IDTR Idtr;
PIDTENTRY pIdtArray = NULL;
ULONG_PTR Index = ; DriverObject->DriverUnload = UnloadDriver;
__asm sidt Idtr
//虚拟机是单核的,只用一个就可以了
if(KeGetIdt(&pIdtArray))
{
DbgPrint("%x---%x\r\n",Idtr.base,Idtr.limit);
for (Index =;Index<(Idtr.limit+)/sizeof(IDTENTRY);Index++)
{
DbgPrint("TrapHandle[%d]:%x\r\n",Index,MAKELONG(pIdtArray[Index].LowOffset,pIdtArray[Index].HiOffset));
} g_OrigKiTrap03 = MAKELONG(pIdtArray[].LowOffset,pIdtArray[].HiOffset); WPOFF();
pIdtArray[].LowOffset = (ULONG_PTR)NewKiTrap03 & 0xFFFF; //低16位
pIdtArray[].HiOffset = (ULONG_PTR)NewKiTrap03 >> ; //高16位
WPON(); } //limit 0x7ff (包含0) 0x800 = 2048 Entry每项大小8字节,就2048/8 = 256 成员
//!idt -a 0ff = 256
//MAKELONG
//#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
return Status;
} BOOLEAN KeGetIdt(PIDTENTRY *pIdtArray)
{
ULONG Index,Affinity,CurrentAffinity;
pfnKESETAFFINITYTHREAD fnpKeSetAffinityThread; UNICODE_STRING usFuncName;
PIDTENTRY pIdtEntry; RtlInitUnicodeString(&usFuncName,L"KeSetAffinityThread");
fnpKeSetAffinityThread = (pfnKESETAFFINITYTHREAD)MmGetSystemRoutineAddress(&usFuncName); if (fnpKeSetAffinityThread==)
{
return FALSE;
} Affinity = KeQueryActiveProcessors();
//KeQueryActiveProcessors获取处理器相关的位图
//(这里的位图可以理解为个数,比如返回1代表一个处理器,返回3表示两个处理器,返回7表示三个处理器,依此类推。
//也就是说从有多少个处理器,那么Affinity的值就会从低位到高位依此填充多少位) CurrentAffinity = ;
Index = ;
while(Affinity)
{
//下面只是个简单的算法,使当前线程运行到不同的处理器上
Affinity &= ~CurrentAffinity;
fnpKeSetAffinityThread(PsGetCurrentThread(),(KAFFINITY)CurrentAffinity);
CurrentAffinity <<= ; __asm{
push eax
mov eax,fs:[0x38]
mov pIdtEntry,eax
pop eax
}
//得到我们要的东西
pIdtArray[Index] = pIdtEntry;
Index++;
} return TRUE;
} VOID WPOFF()
{
ULONG_PTR cr0 = ;
Irql = KeRaiseIrqlToDpcLevel();
cr0 =__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0); } VOID WPON()
{ ULONG_PTR cr0=__readcr0();
cr0 |= 0x10000;
__writecr0(cr0);
KeLowerIrql(Irql);
} VOID UnloadDriver(PDRIVER_OBJECT DriverObject)
{
//恢复
PIDTENTRY pIdtEntry;
if (g_OrigKiTrap03 && KeGetIdt(&pIdtEntry))
{
WPOFF();
pIdtEntry[].LowOffset = g_OrigKiTrap03 & 0xFFFF;
pIdtEntry[].HiOffset = g_OrigKiTrap03 >> ;
WPON();
}
}