想写这个程序主要是因为看了KSSD的一篇帖子,http://bbs.pediy.com/showthread.php?t=108378
讲 的是360保险箱保护游戏账号的原理,实际上就是对各种请求的拦截。这个帖子是大约6年前的了,我简单的看了一下现在的360保险箱应该不再采用这种方法了。
这里主要的思路就是HOOK住系统服务的分发,这已经不是什么新鲜的手法了。比方说,很多外挂作者都使用了内核重载来突破游戏保护的重重HOOK,内核重载也是劫持KiFastCallEntry来实现劫持服务分发的。这篇文章只是写来做一个练习。
基本流程是
1.应用层:
负责安装驱动模块并与驱动通信,由用户选择是否要放行指定操作
2.内核层:
(1)设置SSDT HOOK并调用HOOK 函数,进行栈回溯获取KiFastCallEntry()基址
(2)利用KiFastCallEntry()基址暴力搜索找到HOOK点位置并设置Inline Hook
(3)判断服务请求是否合法,合法则放行,不合法则传递消息给用户层,由用户决定是否放行
其实以上三步就是360保险箱的做法,程序里的有些内容因为不知道该怎么写,所以直接由那篇帖子的汇编分析逆写出来了。
其实网上有很多HOOK KiFastCallEntry的代码,但是我没有找到有劫持ebx来劫持服务分发的例子,所以我写的就是
sub esp, ecx
shr ecx, 2
mov ebx,FuncAddress
结果一直蓝屏,调了整整两天也没找到哪里的问题
后来在群里问到要把顺序改成这样
mov ebx,FuncAddress
sub esp, ecx
shr ecx, 2
结果真的不会触发BSOD了,不知道原理是什么,因为我觉得两种应该没有本质的区别(无论是寄存器还是堆栈我都没有改掉),可是第一种就是不行。
本来应该生成一个设备对象与应用层通信的,结果被蓝屏弄的实在搞不下去,就以后再说吧,核心的程序就是这些了。
想法就是根据SSDT索引得知是什么函数,然后再用在内核栈中索引出SSDT函数的参数(因为在call ebx时参数肯定已经入栈了),最后根据情况用IRP与用户层通信询问是否放行。
#include "ntddk.h" //环境:WDK 7.1 WIN XP SP3 测试通过
#include <ntdef.h> //注意我分发函数中是故意留空白的,不要直接拿来编译 #define NtSetEventID 219 typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t; __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable; NTSTATUS ZwSetEvent(__in HANDLE EventHandle,__out_opt PLONG PreviousState); PVOID AddressOfFuncAddress;
HANDLE HandleTemp = (HANDLE)0x288C58F1;
ULONG NtSetEventAddress, TheHookAddress,KiFastCallEntryAddress;
ULONG YesOrNo=;
ULONG AddressOffset = ;
ULONG TempDword,Test;
ULONG ProcessPID = ;
ULONG pid, DispatchAddress, TempBufferCopy;
PULONG DwordAddress = ;
INT8 i;
INT8 *PbyteAddress;
KSPIN_LOCK MySpinLock1;
KSPIN_LOCK MySpinLock2;
KSPIN_LOCK MySpinLock3;
KIRQL TempKirql1;
KIRQL TempKirql2;
ULONG DebugAddress = ,FuncAddress;
//设置SSDT HOOK 用来栈回溯得到KiFastCallEntry地址
Check()
NTSTATUS HookSSDT(PVOID FuncAdress)
{
ULONG OutTemp = ;
NtSetEventAddress = (ULONG)KeServiceDescriptorTable.ServiceTableBase[NtSetEventID];
DebugAddress=(ULONG)&KeServiceDescriptorTable.ServiceTableBase[NtSetEventID];
KeInitializeSpinLock(&MySpinLock1);
KeAcquireSpinLock(&MySpinLock1,&TempKirql1);
_asm {
cli
push eax
mov eax, cr0
and eax, not 10000h
mov cr0, eax
pop eax
}
KeServiceDescriptorTable.ServiceTableBase[NtSetEventID] = (ULONG)FuncAdress;
__asm {
push eax
mov eax, cr0
or eax, 10000h
mov cr0, eax
pop eax
sti
}
KeReleaseSpinLock(&MySpinLock1,&TempKirql1);
ZwSetEvent(HandleTemp,
&OutTemp);
return STATUS_SUCCESS;
}
//解除SSDT HOOK
void UnhookSSDTHook()
{
KeInitializeSpinLock(&MySpinLock2);
KeAcquireSpinLock(&MySpinLock2,&TempKirql1);
_asm {
cli
push eax
mov eax, cr0
and eax, not 10000h
mov cr0, eax
pop eax
};
KeServiceDescriptorTable.ServiceTableBase[NtSetEventID] = NtSetEventAddress; _asm {
push eax
mov eax, cr0
or eax, 10000h
mov cr0, eax
pop eax
sti
};
KeReleaseSpinLock(&MySpinLock2,&TempKirql1);
return;
}
ULONG TheDispatchFunc(ULONG arg1, ULONG arg2, ULONG arg3)
{
if (arg3== (ULONG)KeServiceDescriptorTable.ServiceTableBase)
{
//这里可以根据栈中参数进行做判断
}
return arg2; }
//被挂载在KiFastCallEntry中(作为一个中转函数)
__declspec(naked) void InlineFunc()
{ _asm {
pushad
pushfd push edi//服务表基址
push ebx//服务地址
push eax//服务序号
call TheDispatchFunc
mov FuncAddress,eax popfd
popad
//补全被覆盖的函数
mov ebx, FuncAddress
sub esp, ecx
shr ecx, jmp TempBufferCopy }; }
//被挂载在SSDT的函数,需判断伪句柄
//并且要进行Inline HOOK
__declspec(naked) void SSDTFunc()
{
_asm {
push eax
mov eax, [esp + ]
mov TempDword, eax
pop eax
} if (TempDword == HandleTemp)
{
_asm {
push eax
mov eax, [esp + ]
mov KiFastCallEntryAddress, eax
pop eax
};
UnhookSSDTHook();
}
else
{
//_asm {int 3};
_asm {
jmp NtSetEventAddress
};
}
for (i = ; i < ; i++)
{ if (*((PULONG)KiFastCallEntryAddress)==0xe9c1e12b)
{
TheHookAddress = KiFastCallEntryAddress;
YesOrNo = ;
break;
}
KiFastCallEntryAddress--;
}
if (YesOrNo)
{
AddressOffset = (ULONG)InlineFunc - - (ULONG)TheHookAddress;
PbyteAddress = (INT8 *)TheHookAddress;
*PbyteAddress = 0xe9;
DwordAddress = (PULONG)((ULONG)TheHookAddress + );
*DwordAddress = AddressOffset;
TempBufferCopy = (ULONG)PbyteAddress + ;
}
//_asm {int 3};
_asm{retn 0x8 }
}
NTSTATUS UnloadFunc(PDRIVER_OBJECT MyDriver, PUNICODE_STRING RegPath)
{
//清理必要资源
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT MyDriver, PUNICODE_STRING RegPath)
{
NTSTATUS Status = ;
AddressOfFuncAddress=ExAllocatePool(NonPagedPool, );
MyDriver->DriverUnload = UnloadFunc;
Status = HookSSDT((PVOID)SSDTFunc);
if (!NT_SUCCESS(Status))
{
return ;
}
return STATUS_SUCCESS;
}