其实所有的HOOK,都基本是一样道理。就是勾住你的目标函数,实现你自己的功能。只要你掌握了,HOOK的原理。剩下的就是寻找目标函数了。
今天回忆一下 HOOK SYSENTER,目的当然还是继续充实自己的BLOG,继续灌水。
一:认识SYSENTER
SYSENTER是一个东西?大家都知道调用门,陷阱门,任务门(这里没有照片!_!).。通过他们我们可以从R3到达R0。简单的说SYSENTER就是他们的替代品。SYSENTER是一个入的
过程,SYSEXIT是他的反过程。
我们以OpenProcess为例,看看他是怎么从R3-R0
kernel32!OpenProcess -> ntdll!ZwOpenProcess -> ntdll!KiFastSystemCall -> sysenter -> nt!KiFastCallEntry -> nt!NtOpenProcess -> nt!KiFastCallEntry> nt!
KiServiceExit -> sysexit -> ntdll!KiFastSystemCallRet -> kernel32!OpenProcess
现在来看看SYSENTER是怎么样处理的。
R3-R0需要把相关的工作交给R0层的。包括:设置CS,IP,SS,SP。SYSENTER有三个特殊的寄存器(MSR)来帮助我们完成。
SYSENTER_CS_MSR
SYSENTER_ESP_MSR
SYSENTER_EIP_MSR
他们的地址分别是:0x174,0x175,0x176。我们可以通过指令rdmsr/wrmsr。来读写这三个寄存器。相关代码如下:
_asm
{
mov ecx,0x176
rdmsr
mov OldKiFastCallEntry,eax
}
我们来看看SYSENTER的工作流程。
1. 装载SYSENTER_CS_MSR 到CS 寄存器,设置目标代码段
2. 装载SYSENTER_EIP_MSR到 EIP寄存器,设置目标指令
3. SYSENTER_CS_MSR+8 装载到SS寄存器 ,设置栈段
4. 装载SYSENTER_ESP_MSR 到ESP寄存器,设置栈帧
5. 切换RING0.
6. 清除 EFLAGS的 VM标志
7. 执行RING0例程
SYSEXIT的工作流程
1. SYSENTER_CS_MSR+16装载到 CS寄存器
2. 将EDX的值送入EIP
3. SYSENTER_CS_MSR+24 装载到SS寄存器
4. 将ECX的值送入ESP
5. 切换回RING3
6. 执行EIP处的RING3指令
这样就完成了R3-RO-R3的过程。
二:相关代码
//////////////////////////////////////////////////
// Hook SysEnter.cpp文件
extern "C"
{
#include
}
VOID UnloadDriver(PDRIVER_OBJECT pDriverObj);
ULONG OldKiFastCallEntry;
VOID NewKiFastCallEntry()
{
DbgPrint("一个系统调用...\n");
_asm
{
jmp DWORD PTR[OldKiFastCallEntry]
}
}
// 驱动程序加载时调用DriverEntry例程
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
pDriverObj->DriverUnload = UnloadDriver;
//
_asm
{
mov ecx,0x176
rdmsr
mov OldKiFastCallEntry,eax
mov eax,NewKiFastCallEntry
wrmsr
}
// 请视情况返回DriverEntry例程执行结果
return STATUS_SUCCESS;
}
VOID UnloadDriver(PDRIVER_OBJECT pDriverObj)
{
_asm
{
mov ecx,0x176
xor edx,edx
mov eax,OldKiFastCallEntry
wrmsr
}
}
=============================================================================================
呵呵,今天这篇内容少,比较简单。
SYSENETER是一条汇编指令,它是在Pentium® II 处理器及以上处理器中提供的,是快速系统调用的一部分。SYSENTER/SYSEXIT这对指令专门用于实现快速调用。在这之前是采用INT 0x2E来实现的。INT 0x2E在系统调用的时候,需要进行栈切换的工作。由于Interrupt/Exception Handler的调用都是通过 call/trap/task这一类的gate来实现的,这种方式会进行栈切换,并且系统栈的地址等信息由TSS提供。这种方式可能会引起多次内存访问 (来获取这些切换信息),因此,从PentiumII开始,IA-32引入了新指令:SYSENTER/SYSEXIT。 有了这两条指令,
从用户级到特权级的堆栈以及指令指针的转换,可以通过这一条指令来实现,并且,需要切换到的新堆栈的地址,以及相应过程的第一条指令的位置,都有一组特殊寄存器来实现,这类特殊寄存器在IA-32中称为MSR(Model Specific Register)。这里牵涉到3个特殊寄存器:
SYSENTER_CS_MSR: New code segment selector 0x174
SYSENTER_ESP_MSR: New Stack Pointer 0x175
SYSENTER_EIP_MSR: New Instruction Pointer 0x176
这里标出的3个16进制数分别对应这3个寄存器的地址,该地址用于Kernel debug时,通过rdmsr/wrmsr指令来读/写这3个寄存器。步骤如下:
1. 装载SYSENTER_CS_MSR 到CS 寄存器,设置目标代码段
2. 装载SYSENTER_EIP_MSR到 EIP寄存器,设置目标指令
3. SYSENTER_CS_MSR+8 装载到SS寄存器 ,设置栈段
4. 装载SYSENTER_ESP_MSR 到ESP寄存器,设置栈帧
5. 切换RING0.
6. 清除 EFLAGS的 VM标志
7. 执行RING0例程
1. SYSENTER_CS_MSR+16装载到 CS寄存器
2. 将EDX的值送入EIP
3. SYSENTER_CS_MSR+24 装载到SS寄存器
4. 将ECX的值送入ESP
5. 切换回RING3
6. 执行EIP处的RING3指令
我们在windbg中可以看看这个三个寄存器的情况,这个是我机器里的情况。
lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> rdmsr 175
msr[175] = 00000000`ba4e0000
lkd> rdmsr 174
msr[174] = 00000000`00000008
可以看到,我的机器里面当前SYSENTER_EIP_MSR,SYSENTER_ESP_MSR,SYSENTER_CS_MSR这三个寄存器的值。
我们在微软公开的内核WRK中发现关于这三个寄存器的设置,其中SYSENTER_EIP_MSR设置的值是KiFastCallEntry。
代码如下:
VOID
KiLoadFastSyscallMachineSpecificRegisters(
IN PLONG Context
)
/*++
Routine Description:
Load MSRs used to support Fast Syscall/return. This routine is
run on all processors.
Arguments:
None.
Return Value:
None.
--*/
{
PKPRCB Prcb;
UNREFERENCED_PARAMETER (Context);
if (KiFastSystemCallIsIA32) {
Prcb = KeGetCurrentPrcb();
//
// Use Intel defined way of doing this.
//
WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
WRMSR(MSR_SYSENTER_ESP, (ULONGLONG)(ULONG)Prcb->DpcStack);
}
}
看看我电脑的情况如下:
lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> u 8053dad0
nt!KiFastCallEntry:
8053dad0 b923000000 mov ecx,23h
8053dad5 6a30 push 30h
8053dad7 0fa1 pop fs
8053dad9 8ed9 mov ds,cx
8053dadb 8ec1 mov es,cx
8053dadd 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053dae3 8b6104 mov esp,dword ptr [ecx+4]
8053dae6 6a23 push 23h
下面是rootkit.com上的一个例子,这个例子有点不厚道,在你卸载的时候会bsod.我简单修改了下,贴代码如下:
#include "ntddk.h"
ULONG d_origKiFastCallEntry; // Original value of ntoskrnl!KiFastCallEntry
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
_asm
{
mov ecx, 0x176
xor edx,edx
mov eax, d_origKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
}
// Hook function
__declspec(naked) MyKiFastCallEntry()
{
__asm {
jmp [d_origKiFastCallEntry]
}
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
theDriverObject->DriverUnload = OnUnload;
__asm {
mov ecx, 0x176
rdmsr // read the value of the IA32_SYSENTER_EIP register
mov d_origKiFastCallEntry, eax
mov eax, MyKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
return STATUS_SUCCESS;
}
注意一点,大家用windbg的时候,配置symbol path,如图:
后面贴上一篇堕落天才写的文章链接:http://bbs.pediy.com/showthread.php?t=42705,
他inline hook 了KiFastCallEntry,采用detour方式,写得很不错。