想写这个程序主要是因为看了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与用户层通信询问是否放行。
1 #include "ntddk.h" //环境:WDK 7.1 WIN XP SP3 测试通过 2 #include <ntdef.h> //注意我分发函数中是故意留空白的,不要直接拿来编译 3 4 #define NtSetEventID 219 5 6 typedef struct ServiceDescriptorEntry { 7 unsigned int *ServiceTableBase; 8 unsigned int *ServiceCounterTableBase; 9 unsigned int NumberOfServices; 10 unsigned char *ParamTableBase; 11 } ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t; 12 13 __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable; 14 15 NTSTATUS ZwSetEvent(__in HANDLE EventHandle,__out_opt PLONG PreviousState); 16 17 PVOID AddressOfFuncAddress; 18 HANDLE HandleTemp = (HANDLE)0x288C58F1; 19 ULONG NtSetEventAddress, TheHookAddress,KiFastCallEntryAddress; 20 ULONG YesOrNo=0; 21 ULONG AddressOffset = 0; 22 ULONG TempDword,Test; 23 ULONG ProcessPID = 0; 24 ULONG pid, DispatchAddress, TempBufferCopy; 25 PULONG DwordAddress = 0; 26 INT8 i; 27 INT8 *PbyteAddress; 28 KSPIN_LOCK MySpinLock1; 29 KSPIN_LOCK MySpinLock2; 30 KSPIN_LOCK MySpinLock3; 31 KIRQL TempKirql1; 32 KIRQL TempKirql2; 33 ULONG DebugAddress = 0,FuncAddress; 34 //设置SSDT HOOK 用来栈回溯得到KiFastCallEntry地址 35 Check() 36 NTSTATUS HookSSDT(PVOID FuncAdress) 37 { 38 ULONG OutTemp = 0; 39 NtSetEventAddress = (ULONG)KeServiceDescriptorTable.ServiceTableBase[NtSetEventID]; 40 DebugAddress=(ULONG)&KeServiceDescriptorTable.ServiceTableBase[NtSetEventID]; 41 KeInitializeSpinLock(&MySpinLock1); 42 KeAcquireSpinLock(&MySpinLock1,&TempKirql1); 43 _asm { 44 cli 45 push eax 46 mov eax, cr0 47 and eax, not 10000h 48 mov cr0, eax 49 pop eax 50 } 51 KeServiceDescriptorTable.ServiceTableBase[NtSetEventID] = (ULONG)FuncAdress; 52 __asm { 53 push eax 54 mov eax, cr0 55 or eax, 10000h 56 mov cr0, eax 57 pop eax 58 sti 59 } 60 KeReleaseSpinLock(&MySpinLock1,&TempKirql1); 61 ZwSetEvent(HandleTemp, 62 &OutTemp); 63 return STATUS_SUCCESS; 64 } 65 //解除SSDT HOOK 66 void UnhookSSDTHook() 67 { 68 KeInitializeSpinLock(&MySpinLock2); 69 KeAcquireSpinLock(&MySpinLock2,&TempKirql1); 70 _asm { 71 cli 72 push eax 73 mov eax, cr0 74 and eax, not 10000h 75 mov cr0, eax 76 pop eax 77 }; 78 KeServiceDescriptorTable.ServiceTableBase[NtSetEventID] = NtSetEventAddress; 79 80 _asm { 81 push eax 82 mov eax, cr0 83 or eax, 10000h 84 mov cr0, eax 85 pop eax 86 sti 87 }; 88 KeReleaseSpinLock(&MySpinLock2,&TempKirql1); 89 return; 90 } 91 ULONG TheDispatchFunc(ULONG arg1, ULONG arg2, ULONG arg3) 92 { 93 if (arg3== (ULONG)KeServiceDescriptorTable.ServiceTableBase) 94 { 95 //这里可以根据栈中参数进行做判断 96 } 97 return arg2; 98 99 } 100 //被挂载在KiFastCallEntry中(作为一个中转函数) 101 __declspec(naked) void InlineFunc() 102 { 103 104 105 _asm { 106 pushad 107 pushfd 108 109 push edi//服务表基址 110 push ebx//服务地址 111 push eax//服务序号 112 call TheDispatchFunc 113 mov FuncAddress,eax 114 115 popfd 116 popad 117 //补全被覆盖的函数 118 mov ebx, FuncAddress 119 sub esp, ecx 120 shr ecx, 2 121 122 123 jmp TempBufferCopy 124 125 }; 126 127 } 128 //被挂载在SSDT的函数,需判断伪句柄 129 //并且要进行Inline HOOK 130 __declspec(naked) void SSDTFunc() 131 { 132 _asm { 133 push eax 134 mov eax, [esp + 8] 135 mov TempDword, eax 136 pop eax 137 } 138 139 if (TempDword == HandleTemp) 140 { 141 _asm { 142 push eax 143 mov eax, [esp + 4] 144 mov KiFastCallEntryAddress, eax 145 pop eax 146 }; 147 UnhookSSDTHook(); 148 } 149 else 150 { 151 //_asm {int 3}; 152 _asm { 153 jmp NtSetEventAddress 154 }; 155 } 156 for (i = 0; i < 200; i++) 157 { 158 159 if (*((PULONG)KiFastCallEntryAddress)==0xe9c1e12b) 160 { 161 TheHookAddress = KiFastCallEntryAddress; 162 YesOrNo = 1; 163 break; 164 } 165 KiFastCallEntryAddress--; 166 } 167 if (YesOrNo) 168 { 169 AddressOffset = (ULONG)InlineFunc - 5 - (ULONG)TheHookAddress; 170 PbyteAddress = (INT8 *)TheHookAddress; 171 *PbyteAddress = 0xe9; 172 DwordAddress = (PULONG)((ULONG)TheHookAddress + 1); 173 *DwordAddress = AddressOffset; 174 TempBufferCopy = (ULONG)PbyteAddress + 5; 175 } 176 //_asm {int 3}; 177 _asm{retn 0x8 } 178 } 179 NTSTATUS UnloadFunc(PDRIVER_OBJECT MyDriver, PUNICODE_STRING RegPath) 180 { 181 //清理必要资源 182 return STATUS_SUCCESS; 183 } 184 NTSTATUS DriverEntry(PDRIVER_OBJECT MyDriver, PUNICODE_STRING RegPath) 185 { 186 NTSTATUS Status = 0; 187 AddressOfFuncAddress=ExAllocatePool(NonPagedPool, 4); 188 MyDriver->DriverUnload = UnloadFunc; 189 Status = HookSSDT((PVOID)SSDTFunc); 190 if (!NT_SUCCESS(Status)) 191 { 192 return 1; 193 } 194 return STATUS_SUCCESS; 195 }