SSDT Hook是一种经典的Windows内核劫持手段,通过改变Windows用户层的应用程序(RING3)通过SYSENTER指令调用内核功能时进入内核层(RING0)后的系统服务分发(System Services Descriptor)流程,实现对用户层应用程序调用内核功能的拦截。
最古老的SSDT Hook实现是修改内核内存中实现系统服务分发的例程,对其进行kernel inline hook,后来又有人提出修改系统服务分发表(System Services Descriptor Table)的表项。这两种方式均属于全局Hook,缺点十分明显:容易被其它内核权限的程序发现,只需要对相关的例程和SSDT的表项进行校验,即可发现异常之处。
Windows的用户程序在需要调用内核服务时,先将需要调用的内核服务号存入eax寄存器中,随后调用sysenter指令(对于Pentium ii之前的CPUS是int 0x2e)切换到内核模式。sysenter会完成RING3-RING0的切换工作并保存一些信息,随后载入MSR中设定的CS、ESP、EIP的值。这部分操作在ntdll.dll中实现(可以自己编程替代),之后进入内核模式(RIN0),此时EIP指向_KiFastCallEntrt()(对于SYSENTER指令进入),在建立了一个陷阱帧并设置了相关寄存器的值之后进入真正的处理系统服务的例程_KiSystemServiceRepeat(),在WRK 1.2中相关代码如下(\base\ntos\ke\i386\XXXXXXXm):
<code class="hljs x86asm">_KiFastCallEntry proc <span class="hljs-comment">;</span> <span class="hljs-comment">; Sanitize the segment registers</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ecx</span>, KGDT_R3_DATA <span class="hljs-keyword">OR</span> RPL_MASK <span class="hljs-keyword">push</span> KGDT_R0_PCR <span class="hljs-keyword">pop</span> <span class="hljs-built_in">fs</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ds</span>, <span class="hljs-built_in">ecx</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">es</span>, <span class="hljs-built_in">ecx</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; When we trap into the kernel via fast system call we start on the DPC stack. We need</span> <span class="hljs-comment">; shift to the threads stack before enabling interrupts.</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ecx</span>, PCR[PcTss] <span class="hljs-comment">;</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">esp</span>, [<span class="hljs-built_in">ecx</span>]+TssEsp0 <span class="hljs-keyword">push</span> KGDT_R3_DATA <span class="hljs-keyword">OR</span> RPL_MASK <span class="hljs-comment">; Push user SS</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">edx</span> <span class="hljs-comment">; Push ESP</span> <span class="hljs-keyword">pushfd</span> <span class="hljs-symbol">Kfsc10:</span> <span class="hljs-keyword">push</span> <span class="hljs-number">2</span> <span class="hljs-comment">; Sanitize eflags, clear direction, NT etc</span> <span class="hljs-keyword">add</span> <span class="hljs-built_in">edx</span>, <span class="hljs-number">8</span> <span class="hljs-comment">; (edx) -> arguments</span> <span class="hljs-keyword">popfd</span> <span class="hljs-comment">;</span> <span class="hljs-meta">.errnz</span>(EFLAGS_INTERRUPT_MASK <span class="hljs-keyword">AND</span> <span class="hljs-number">0FFFF00FFh</span>) <span class="hljs-keyword">or</span> <span class="hljs-built_in">byte</span> <span class="hljs-built_in">ptr</span> [<span class="hljs-built_in">esp</span>+<span class="hljs-number">1</span>], EFLAGS_INTERRUPT_MASK/<span class="hljs-number">0100h</span> <span class="hljs-comment">; Enable interrupts in eflags</span> <span class="hljs-keyword">push</span> KGDT_R3_CODE <span class="hljs-keyword">OR</span> RPL_MASK <span class="hljs-comment">; Push user CS</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">dword</span> <span class="hljs-built_in">ptr</span> <span class="hljs-built_in">ds</span>:[USER_SHARED_DATA+UsSystemCallReturn] <span class="hljs-comment">; push return address</span> <span class="hljs-keyword">push</span> <span class="hljs-number">0</span> <span class="hljs-comment">; put pad dword for error on stack</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">ebp</span> <span class="hljs-comment">; save the non-volatile registers</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">ebx</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">esi</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">push</span> <span class="hljs-built_in">edi</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ebx</span>, PCR[PcSelfPcr] <span class="hljs-comment">; Get PRCB address</span> <span class="hljs-keyword">push</span> KGDT_R3_TEB <span class="hljs-keyword">OR</span> RPL_MASK <span class="hljs-comment">; Push user mode FS</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">esi</span>, [<span class="hljs-built_in">ebx</span>].PcPrcbData+PbCurrentThread <span class="hljs-comment">; get current thread address</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; Save the old exception list in trap frame and initialize a new empty</span> <span class="hljs-comment">; exception list.</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">push</span> [<span class="hljs-built_in">ebx</span>].PcExceptionList <span class="hljs-comment">; save old exception list</span> <span class="hljs-keyword">mov</span> [<span class="hljs-built_in">ebx</span>].PcExceptionList, EXCEPTION_CHAIN_END <span class="hljs-comment">; set new empty list</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ebp</span>, [<span class="hljs-built_in">esi</span>].ThInitialStack <span class="hljs-comment">;</span> <span class="hljs-comment">; Save the old previous mode in trap frame, allocate remainder of trap frame,</span> <span class="hljs-comment">; and set the new previous mode.</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">push</span> MODE_MASK <span class="hljs-comment">; Save previous mode as user</span> <span class="hljs-keyword">sub</span> <span class="hljs-built_in">esp</span>,TsPreviousPreviousMode <span class="hljs-comment">; allocate remainder of trap frame</span> <span class="hljs-keyword">sub</span> <span class="hljs-built_in">ebp</span>, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH <span class="hljs-keyword">mov</span> <span class="hljs-built_in">byte</span> <span class="hljs-built_in">ptr</span> [<span class="hljs-built_in">esi</span>].ThPreviousMode,MODE_MASK <span class="hljs-comment">; set new previous mode of user</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; Now the full trap frame is build.</span> <span class="hljs-comment">; Calculate initial stack pointer from thread initial stack to contain NPX and trap.</span> <span class="hljs-comment">; If this isn't the same as esp then we are a VX86 thread and we are rejected</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">cmp</span> <span class="hljs-built_in">ebp</span>, <span class="hljs-built_in">esp</span> <span class="hljs-keyword">jne</span> short Kfsc91 <span class="hljs-comment">;</span> <span class="hljs-comment">; Set the new trap frame address.</span> <span class="hljs-comment">;</span> <span class="hljs-keyword">and</span> <span class="hljs-built_in">dword</span> <span class="hljs-built_in">ptr</span> [<span class="hljs-built_in">ebp</span>].TsDr7, <span class="hljs-number">0</span> <span class="hljs-keyword">test</span> <span class="hljs-built_in">byte</span> <span class="hljs-built_in">ptr</span> [<span class="hljs-built_in">esi</span>].ThDebugActive, <span class="hljs-number">0ffh</span> <span class="hljs-comment">; See if we need to save debug registers</span> <span class="hljs-keyword">mov</span> [<span class="hljs-built_in">esi</span>].ThTrapFrame, <span class="hljs-built_in">ebp</span> <span class="hljs-comment">; set new trap frame address</span> <span class="hljs-keyword">jnz</span> Dr_FastCallDrSave <span class="hljs-comment">; if nz, debugging is active on thread</span> <span class="hljs-symbol"> Dr_FastCallDrReturn:</span> <span class="hljs-comment">;</span> SET_DEBUG_DATA <span class="hljs-comment">; Note this destroys edi</span> <span class="hljs-keyword">sti</span> <span class="hljs-comment">; enable interrupts</span> ?FpoValue = <span class="hljs-number">0</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; (eax) = Service number</span> <span class="hljs-comment">; (edx) = Callers stack pointer</span> <span class="hljs-comment">; (esi) = Current thread address</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; All other registers have been saved and are free.</span> <span class="hljs-comment">;</span> <span class="hljs-comment">; Check if the service number within valid range</span> <span class="hljs-comment">;</span> <span class="hljs-symbol"> _KiSystemServiceRepeat:</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">edi</span>, <span class="hljs-built_in">eax</span> <span class="hljs-comment">; copy system service number</span> <span class="hljs-keyword">shr</span> <span class="hljs-built_in">edi</span>, SERVICE_TABLE_SHIFT <span class="hljs-comment">; isolate service table number</span> <span class="hljs-keyword">and</span> <span class="hljs-built_in">edi</span>, SERVICE_TABLE_MASK <span class="hljs-comment">;</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ecx</span>, <span class="hljs-built_in">edi</span> <span class="hljs-comment">; save service table number</span> <span class="hljs-keyword">add</span> <span class="hljs-built_in">edi</span>, [<span class="hljs-built_in">esi</span>]+ThServiceTable <span class="hljs-comment">; compute service descriptor address</span> <span class="hljs-keyword">mov</span> <span class="hljs-built_in">ebx</span>, <span class="hljs-built_in">eax</span> <span class="hljs-comment">; save system service number</span> <span class="hljs-keyword">and</span> <span class="hljs-built_in">eax</span>, SERVICE_NUMBER_MASK <span class="hljs-comment">; isolate service table offset</span></code>
这里有一个值得注意的地方:SSDT的地址并非是固定或者存在某个全局变量之中,而是通过PCR拿到当前线程的KTHREAD之后,在KTHREAD之中获取。
KTHREAD的结构如下(位于\base\ntos\inc\ke.h):
<code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-number">_</span>KTHREAD { <span class="hljs-comment">//</span> <span class="hljs-comment">// The dispatcher header and mutant listhead are fairly infrequently</span> <span class="hljs-comment">// referenced.</span> <span class="hljs-comment">//</span> DISPATCHER_HEADER Header; LIST_ENTRY MutantListHead; <span class="hljs-comment">//</span> <span class="hljs-comment">// The following fields are referenced during context switches and wait</span> <span class="hljs-comment">// operatings. They have been carefully laid out to get the best cache</span> <span class="hljs-comment">// hit ratios.</span> <span class="hljs-comment">//</span> PVOID InitialStack; PVOID StackLimit; PVOID KernelStack; KSPIN_LOCK ThreadLock; <span class="hljs-keyword">union</span> { KAPC_STATE ApcState; <span class="hljs-keyword">struct</span> { UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH]; BOOLEAN ApcQueueable; <span class="hljs-keyword">volatile</span> UCHAR NextProcessor; <span class="hljs-keyword">volatile</span> UCHAR DeferredProcessor; UCHAR AdjustReason; SCHAR AdjustIncrement; }; }; KSPIN_LOCK ApcQueueLock; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> !defined(_AMD64_)</span> ULONG ContextSwitches; <span class="hljs-keyword">volatile</span> UCHAR State; UCHAR NpxState; KIRQL WaitIrql; KPROCESSOR_MODE WaitMode; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> LONG_PTR WaitStatus; <span class="hljs-keyword">union</span> { PKWAIT_BLOCK WaitBlockList; PKGATE GateObject; }; BOOLEAN Alertable; BOOLEAN WaitNext; UCHAR WaitReason; SCHAR Priority; UCHAR EnableStackSwap; <span class="hljs-keyword">volatile</span> UCHAR SwapBusy; BOOLEAN Alerted[MaximumMode]; <span class="hljs-keyword">union</span> { LIST_ENTRY WaitListEntry; SINGLE_LIST_ENTRY SwapListEntry; }; PRKQUEUE Queue; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> !defined(_AMD64_)</span> ULONG WaitTime; <span class="hljs-keyword">union</span> { <span class="hljs-keyword">struct</span> { SHORT KernelApcDisable; SHORT SpecialApcDisable; }; ULONG CombinedApcDisable; }; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> PVOID Teb; <span class="hljs-keyword">union</span> { KTIMER Timer; <span class="hljs-keyword">struct</span> { UCHAR TimerFill[KTIMER_ACTUAL_LENGTH]; <span class="hljs-comment">//</span> <span class="hljs-comment">// N.B. The following bit number definitions must match the</span> <span class="hljs-comment">// following bit field.</span> <span class="hljs-comment">//</span> <span class="hljs-comment">// N.B. These bits can only be written with interlocked</span> <span class="hljs-comment">// operations.</span> <span class="hljs-comment">//</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> KTHREAD_AUTO_ALIGNMENT_BIT 0</span> <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> KTHREAD_DISABLE_BOOST_BIT 1</span> <span class="hljs-keyword">union</span> { <span class="hljs-keyword">struct</span> { LONG AutoAlignment : <span class="hljs-number">1</span>; LONG DisableBoost : <span class="hljs-number">1</span>; LONG ReservedFlags : <span class="hljs-number">30</span>; }; LONG ThreadFlags; }; }; }; <span class="hljs-keyword">union</span> { KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + <span class="hljs-number">1</span>]; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0]; BOOLEAN SystemAffinityActive; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1]; CCHAR PreviousMode; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2]; UCHAR ResourceIndex; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3]; UCHAR LargeStack; }; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(_AMD64_)</span> <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0]; ULONG ContextSwitches; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1]; <span class="hljs-keyword">volatile</span> UCHAR State; UCHAR NpxState; KIRQL WaitIrql; KPROCESSOR_MODE WaitMode; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2]; ULONG WaitTime; }; <span class="hljs-keyword">struct</span> { UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3]; <span class="hljs-keyword">union</span> { <span class="hljs-keyword">struct</span> { SHORT KernelApcDisable; SHORT SpecialApcDisable; }; ULONG CombinedApcDisable; }; }; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> }; LIST_ENTRY QueueListEntry; <span class="hljs-comment">//</span> <span class="hljs-comment">// The following fields are accessed during system service dispatch.</span> <span class="hljs-comment">//</span> PKTRAP_FRAME TrapFrame; PVOID CallbackStack; PVOID ServiceTable; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(_AMD64_)</span> ULONG KernelLimit; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-comment">//</span> <span class="hljs-comment">// The following fields are referenced during ready thread and wait</span> <span class="hljs-comment">// completion.</span> <span class="hljs-comment">//</span> UCHAR ApcStateIndex; UCHAR IdealProcessor; BOOLEAN Preempted; BOOLEAN ProcessReadyQueue; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(_AMD64_)</span> PVOID Win32kTable; ULONG Win32kLimit; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> BOOLEAN KernelStackResident; SCHAR BasePriority; SCHAR PriorityDecrement; CHAR Saturation; KAFFINITY UserAffinity; PKPROCESS Process; KAFFINITY Affinity; <span class="hljs-comment">//</span> <span class="hljs-comment">// The below fields are infrequently referenced.</span> <span class="hljs-comment">//</span> PKAPC_STATE ApcStatePointer[<span class="hljs-number">2</span>]; <span class="hljs-keyword">union</span> { KAPC_STATE SavedApcState; <span class="hljs-keyword">struct</span> { UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH]; CCHAR FreezeCount; CCHAR SuspendCount; UCHAR UserIdealProcessor; UCHAR CalloutActive; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(_AMD64_)</span> BOOLEAN CodePatchInProgress; <span class="hljs-meta">#<span class="hljs-meta-keyword">elif</span> defined(_X86_)</span> UCHAR Iopl; <span class="hljs-meta">#<span class="hljs-meta-keyword">else</span></span> UCHAR OtherPlatformFill; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> }; }; PVOID Win32Thread; PVOID StackBase; <span class="hljs-keyword">union</span> { KAPC SuspendApc; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0]; SCHAR Quantum; }; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1]; UCHAR QuantumReset; }; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG]; ULONG KernelTime; }; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1]; PVOID TlsArray; }; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2]; PVOID BBTData; }; <span class="hljs-keyword">struct</span> { UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH]; UCHAR PowerState; ULONG UserTime; }; }; <span class="hljs-keyword">union</span> { KSEMAPHORE SuspendSemaphore; <span class="hljs-keyword">struct</span> { UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH]; ULONG SListFaultCount; }; }; LIST_ENTRY ThreadListEntry; PVOID SListFaultAddress; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(_WIN64)</span> LONG64 ReadOperationCount; LONG64 WriteOperationCount; LONG64 OtherOperationCount; LONG64 ReadTransferCount; LONG64 WriteTransferCount; LONG64 OtherTransferCount; <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> } KTHREAD, *PKTHREAD, *PRKTHREAD; <span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> !defined(_X86AMD64_) && defined(_AMD64_)</span> C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + <span class="hljs-number">16</span>) == FIELD_OFFSET(KTHREAD, Win32kTable)); C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + <span class="hljs-number">8</span>) == FIELD_OFFSET(KTHREAD, KernelLimit)); C_ASSERT((FIELD_OFFSET(KTHREAD, Win32kTable) + <span class="hljs-number">8</span>) == FIELD_OFFSET(KTHREAD, Win32kLimit)); <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></code>
也就是说,在完成系统服务调用的过程中,系统内核实际使用的是SSDT地址是当前线程的PKTHREAD->ServiceTable,只要修改了这个值,也能实现SSDT Hook,并且这个值仅对当前线程有效,并不会产生全局效应,hook的隐蔽性更强。
配合内核重载,这种形式的SSDT Hook可以保护制定的线程,绕过其他程序的SSDT/Kernel Inline Hook(对于Object Hook无效)。
三、针对线程的隐蔽式SSDT Hook实现首先实现一些辅助函数和数据结构:
<code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-number">_</span>KSERVICE_TABLE_DESCRIPTOR { PULONG_PTR Base; PULONG Counter; ULONG Limit; PUCHAR Number; }KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE,**PPKSYSTEM_SERVICE_TABLE; <span class="hljs-function">PEPROCESS <span class="hljs-title">PsLookUpProcessByName</span><span class="hljs-params">(PUCHAR TragetProcessName)</span></span>{ PEPROCESS Process; LIST_ENTRY ProcessLinksHead; LIST_ENTRY ProcessLinks; ANSI_STRING TragetName; ANSI_STRING ProcessName; Process = PsGetCurrentProcess(); RtlInitAnsiString(&TragetName,(PCSZ)TragetProcessName); ProcessLinks = *(PLIST_ENTRY)((PUCHAR)Process + Offset_ActiveProcessLinks_KPROCESS); ProcessLinksHead = ProcessLinks; <span class="hljs-keyword">do</span>{ RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName((PEPROCESS)ProcessLinks.Flink) - Offset_ActiveProcessLinks_KPROCESS)); <span class="hljs-keyword">if</span>(RtlEqualString(&TragetName,&ProcessName,<span class="hljs-number">0</span>)){ <span class="hljs-keyword">return</span> (PEPROCESS)RtlPointerAddOffset(ProcessLinks.Flink,- Offset_ActiveProcessLinks_KPROCESS);<span class="hljs-comment">/*(PEPROCESS)(ProcessLinks.Flink - Offset_ActiveProcessLinks_KPROCESS);*/</span> } ProcessLinks = *ProcessLinks.Flink; }<span class="hljs-keyword">while</span>(ProcessLinks.Flink != ProcessLinksHead.Flink); KdPrint((<span class="hljs-string">"PsLookUpProcessByName() -- Can't Find Traget Process\n"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; } <span class="hljs-function">PKTHREAD <span class="hljs-title">PsGetGUIThread</span><span class="hljs-params">()</span></span>{ PEPROCESS PTragetProcess; LIST_ENTRY ThreadList; LIST_ENTRY ThreadListHead; PULONG PWin32Thread; KdPrint((<span class="hljs-string">"PsGetGUIThread()\n"</span>)); PTragetProcess = PsLookUpProcessByName((PUCHAR)<span class="hljs-string">"csrss.exe"</span>); <span class="hljs-keyword">if</span>(<span class="hljs-literal">NULL</span> == PTragetProcess){ KdPrint((<span class="hljs-string">" PsGetGUIThread() -- Can't Find Traget Process\n"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; } ThreadList = *(PLIST_ENTRY)((PUCHAR)PTragetProcess + Offset_ThreadListHead_KPROCESS); ThreadListHead = ThreadList; <span class="hljs-keyword">do</span>{ PWin32Thread = (PULONG)((PUCHAR)ThreadList.Flink + (Offset_Win32Thread_KTHREAD - Offset_ThreadListEntry_KTHREAD)); <span class="hljs-keyword">if</span>(<span class="hljs-number">0</span> != *PWin32Thread){ KdPrint((<span class="hljs-string">"PsGetGUIThread() -- return %x\n"</span>,(PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD))); <span class="hljs-keyword">return</span> (PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD); } ThreadList = *ThreadList.Flink; }<span class="hljs-keyword">while</span>(ThreadList.Flink != ThreadListHead.Flink); KdPrint((<span class="hljs-string">"PsGetGUIThread() -- Can't Find GUI Thread\n\n\n"</span>)); <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; }</code>
重载内核并初始化SSDT:
<code class="hljs cpp"><span class="hljs-function">NTSTATUS <span class="hljs-title">KeInitSSDTData</span><span class="hljs-params">(PDRIVER_OBJECT DriverObject)</span></span>{ NTSTATUS Status; PKTHREAD PCUIThread; PKTHREAD PGUIThread; KdPrint((<span class="hljs-string">"InitSSDTData()\n"</span>)); <span class="hljs-keyword">if</span>(SSDIF&&SDIF){ Status = STATUS_UNSUCCESSFUL; <span class="hljs-keyword">return</span> Status; } PCUIThread = PsGetCUIThread(); PGUIThread = PsGetGUIThread(); <span class="hljs-keyword">if</span>(!(PCUIThread && PGUIThread)){ KdPrint((<span class="hljs-string">"InitSSDTData() -ERROR to get threadinfo \n"</span>)); KdPrint((<span class="hljs-string">"PCUIThread:%x\n"</span>,PCUIThread)); KdPrint((<span class="hljs-string">"PGUIThread:%x\n"</span>,PGUIThread)); Status = STATUS_UNSUCCESSFUL; <span class="hljs-keyword">return</span> Status; } Status = ReLoadNtos(DriverObject,RetAddress); <span class="hljs-keyword">if</span>(!(NT_SUCCESS(Status))){ KdPrint((<span class="hljs-string">"InitSSDTData() -ERROR to reload ntos \n"</span>)); <span class="hljs-keyword">return</span> Status; } Status = ReloadWin32K(DriverObject); <span class="hljs-keyword">if</span>(!(NT_SUCCESS(Status))){ KdPrint((<span class="hljs-string">"InitSSDTData() -ERROR to reload Win32k \n"</span>)); <span class="hljs-keyword">return</span> Status; } KdPrint((<span class="hljs-string">"Safe_ServiceDescriptorTable:%x \n"</span>,Safe_ServiceDescriptorTable)); <span class="hljs-number">__</span><span class="hljs-keyword">try</span>{ PSystemSSDT = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PCUIThread + (Offset_ServiceTable_KTHREAD)); PSystemSSDTShadow = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PGUIThread + (Offset_ServiceTable_KTHREAD)); } <span class="hljs-number">__</span>except(EXCEPTION_EXECUTE_HANDLER){ KdPrint((<span class="hljs-string">"InitSSDTData() -ERROR ACCESS_VIOLATION \n"</span>)); <span class="hljs-keyword">return</span> STATUS_ACCESS_VIOLATION; } PSafeSSDT = (PKSYSTEM_SERVICE_TABLE)Safe_ServiceDescriptorTable; PSafeSSDTShadow = (PKSYSTEM_SERVICE_TABLE) ExAllocatePool(PagedPool,<span class="hljs-number">32</span>); <span class="hljs-keyword">if</span>(<span class="hljs-literal">NULL</span> == PSafeSSDTShadow){ Status = STATUS_UNSUCCESSFUL; <span class="hljs-keyword">return</span> Status; } (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">0</span>].Base = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[<span class="hljs-number">0</span>].Base; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">0</span>].Counter = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[<span class="hljs-number">0</span>].Counter; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">0</span>].Limit = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[<span class="hljs-number">0</span>].Limit; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">0</span>].Number = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[<span class="hljs-number">0</span>].Number; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">1</span>].Base = Safe_ServiceDescriptorShadowSSDTTable->ServiceTable; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">1</span>].Counter = Safe_ServiceDescriptorShadowSSDTTable->CounterTable; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">1</span>].Limit = Safe_ServiceDescriptorShadowSSDTTable->TableSize; (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[<span class="hljs-number">1</span>].Number = Safe_ServiceDescriptorShadowSSDTTable->ArgumentTable; SSDIF = <span class="hljs-number">1</span>;<span class="hljs-comment">//Set flag</span> SDIF = <span class="hljs-number">1</span>;<span class="hljs-comment">//Set flag</span> ProcessInfo.ProcessCount = <span class="hljs-number">0</span>; ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)&ProcessInfo; ProcessInfo.ProcessListHead.Flink = (PLIST_ENTRY)&ProcessInfo; ExInitializeFastMutex(&ProcessInfo.FastMutex); <span class="hljs-comment">//ObvInitVirtualHandleMoudle();</span> Status = InitializeCommonVariables(); <span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> DEBUGINFO</span> KdPrint((<span class="hljs-string">"ProcessInfoTable:%x\n"</span>,&ProcessInfo)); <span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span> <span class="hljs-keyword">return</span> STATUS_SUCCESS; }</code>
替换PKTHREAD->ServiceTable:
<code class="hljs cpp"><span class="hljs-function">NTSTATUS <span class="hljs-title">ReplaceServiceTableOfThread</span><span class="hljs-params">(PKTHREAD PTragetThread)</span></span>{ PULONG PWin32Thread; PULONG* PServiceTable; PWin32Thread = (PULONG)((PUCHAR)PTragetThread + Offset_Win32Thread_KTHREAD); PServiceTable = (PULONG*)((PUCHAR)PTragetThread + Offset_ServiceTable_KTHREAD); <span class="hljs-number">__</span><span class="hljs-keyword">try</span>{ <span class="hljs-keyword">if</span>(<span class="hljs-number">0</span> == *PWin32Thread){ <span class="hljs-comment">//On CUIThread.Use SSDT </span> <span class="hljs-keyword">if</span>(<span class="hljs-number">0</span> == SDIF){ <span class="hljs-comment">//The Date of My SSDT have not be inited</span> <span class="hljs-keyword">return</span> STATUS_UNSUCCESSFUL; } <span class="hljs-keyword">if</span>(*PServiceTable != (PULONG)PSafeSSDT){ *PServiceTable = (PULONG)PSafeSSDT; } } <span class="hljs-keyword">else</span>{ <span class="hljs-keyword">if</span>(<span class="hljs-number">0</span> == SSDIF){ <span class="hljs-comment">//The Date of My SSDT have not be inited</span> <span class="hljs-keyword">return</span> STATUS_UNSUCCESSFUL; } <span class="hljs-keyword">if</span>(*PServiceTable != (PULONG)PSafeSSDTShadow){ *PServiceTable = (PULONG)PSafeSSDTShadow; } } } <span class="hljs-number">__</span>except(EXCEPTION_EXECUTE_HANDLER){ KdPrint((<span class="hljs-string">"ReplaceServiceTableOfThread() -ERROR ACCESS_VIOLATION \n"</span>)); <span class="hljs-keyword">return</span> STATUS_ACCESS_VIOLATION; } <span class="hljs-keyword">return</span> STATUS_SUCCESS; }</code>
处理指定进程(作为暴露给外界的接口,参数为进程名):
<code class="hljs php">NTSTATUS KeReplaceServiceTableOfProcessCheck(PEPROCESS TragetProcess){ NTSTATUS Status; PLIST_ENTRY NextEntry; PPROCESS_PROTECT_INFO_ENTRY PProcessInfoEntry; PVOID HandleTable; ExAcquireFastMutex(&(ProcessInfo.FastMutex)); <span class="hljs-comment">//Check that the TragetProcess is not in the table</span> NextEntry = ProcessInfo.ProcessListHead.Flink; <span class="hljs-keyword">while</span>(NextEntry != (PLIST_ENTRY)&(ProcessInfo.ProcessListHead)){ PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)NextEntry; <span class="hljs-keyword">if</span>(PProcessInfoEntry->Process == TragetProcess){ <span class="hljs-comment">//The TragetProcess was in the table,just return success</span> Status = STATUS_SUCCESS; <span class="hljs-keyword">goto</span> End; } NextEntry = PProcessInfoEntry->ProcessList.Flink; } <span class="hljs-comment">//We ture that is a new process</span> <span class="hljs-comment">//Now,Init process info and protect the trager process</span> <span class="hljs-comment">//�ڽ�����Ϣ������ӱ���</span> PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)ExAllocatePool(PagedPool,sizeof(PROCESS_PROTECT_INFO_ENTRY)); <span class="hljs-keyword">if</span>(!PProcessInfoEntry){ Status = STATUS_NO_MEMORY; <span class="hljs-keyword">goto</span> End; } Status = ObvCreateVirtualHandleTable(TragetProcess,(PPVIRTUAL_HANDLE_TABLE)&HandleTable); <span class="hljs-keyword">if</span>(!NT_SUCCESS(Status)){ ExFreePool(PProcessInfoEntry); <span class="hljs-keyword">goto</span> End; } PProcessInfoEntry->Process = TragetProcess; PProcessInfoEntry->HabdleTable = HandleTable; PProcessInfoEntry->ProcessList.Flink = (PLIST_ENTRY)&ProcessInfo.ProcessListHead; PProcessInfoEntry->ProcessList.Blink = (PLIST_ENTRY)ProcessInfo.ProcessListHead.Blink; ((PPROCESS_PROTECT_INFO_ENTRY)(PProcessInfoEntry->ProcessList.Blink))->ProcessList.Flink = (PLIST_ENTRY)PProcessInfoEntry; ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)PProcessInfoEntry; ProcessInfo.ProcessCount++; <span class="hljs-comment">//--</span> Status = KeReplaceServiceTableOfProcess(TragetProcess); <span class="hljs-comment">//##########Hide Process###############</span> <span class="hljs-comment">//</span> <span class="hljs-comment">//Status = PsHideProcess(TragetProcess);</span> <span class="hljs-comment">//#####################################</span> <span class="hljs-comment">//Check that is success to replace the ServiceTable</span> <span class="hljs-keyword">if</span>(!NT_SUCCESS(Status)){ ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)PProcessInfoEntry->ProcessList.Blink; ((PPROCESS_PROTECT_INFO_ENTRY)(PProcessInfoEntry->ProcessList.Blink))->ProcessList.Flink = (PLIST_ENTRY)&ProcessInfo.ProcessListHead; ExFreePool(PProcessInfoEntry); } End: ExReleaseFastMutex(&ProcessInfo.FastMutex); <span class="hljs-keyword">return</span> Status; } NTSTATUS KeReplaceServiceTableOfProcessByName(char* ProcessName){ NTSTATUS Status = STATUS_UNSUCCESSFUL; PEPROCESS PTragetProcess = PsLookUpProcessByName((PUCHAR)ProcessName); KdPrint((<span class="hljs-string">" ReplaceServiceTableOfProcessByName()\n"</span>)); <span class="hljs-keyword">if</span>(PTragetProcess == <span class="hljs-keyword">NULL</span>){ <span class="hljs-keyword">return</span> Status; } Status = KeReplaceServiceTableOfProcessCheck(PTragetProcess); <span class="hljs-keyword">return</span> Status; }</code>
进程/线程创建通知,防止冲突造成蓝屏:
<code class="hljs php">VOID PsCreateProcessNotify( HANDLE ParentId, HANDLE ProcessID, BOOLEAN Create) { NTSTATUS Status; PEPROCESS Process; PPROCESS_PROTECT_INFO_ENTRY NextProcess; PVIRTUAL_HANDLE_TABLE HandleTable; ANSI_STRING ProcessName; Status = PsLookupProcessByProcessId(ProcessID,&Process); <span class="hljs-keyword">if</span>(!NT_SUCCESS(Status)){<span class="hljs-comment">//PID->PEPROCESS</span> <span class="hljs-keyword">return</span>; } <span class="hljs-comment">//RObfDereferenceObjectSafe(Process);</span> ObDereferenceObject(Process); <span class="hljs-keyword">if</span>(!Create){ <span class="hljs-comment">//���̹ر�֪ͨ</span> <span class="hljs-comment">//�������Ƿ�Ϊ�ܱ������̣������ͷ���Ӧ��Դ</span> ExAcquireFastMutex(&(ProcessInfo.FastMutex)); NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)ProcessInfo.ProcessListHead.Flink; <span class="hljs-keyword">while</span>(NextProcess != (PPROCESS_PROTECT_INFO_ENTRY)&ProcessInfo.ProcessListHead){ <span class="hljs-keyword">if</span>(NextProcess->Process == Process){ HandleTable = (PVIRTUAL_HANDLE_TABLE)NextProcess->HabdleTable; <span class="hljs-comment">//����վ����ָ��</span> NextProcess->HabdleTable = <span class="hljs-keyword">NULL</span>; Status = ObvFreeVirtualHandleTable(HandleTable); (NextProcess->ProcessList.Flink)->Blink = (NextProcess->ProcessList.Blink); (NextProcess->ProcessList.Blink)->Flink = (NextProcess->ProcessList.Flink); ProcessInfo.ProcessCount--; ExFreePool(NextProcess); <span class="hljs-comment">//�Ѿ��ͷŵ��ˣ�ֱ���ͷ��� + ����</span> <span class="hljs-comment">//�����غ������Ϊδ֪��</span> ExReleaseFastMutex(&ProcessInfo.FastMutex); <span class="hljs-keyword">return</span>; } NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)NextProcess->ProcessList.Flink; } ExReleaseFastMutex(&ProcessInfo.FastMutex); } <span class="hljs-comment">/* else if(FocusOn){ RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName(Process))); if(!strcmp(ProcessName.Buffer,(char*)ProcessSpyOn)){ KeReplaceServiceTableOfProcessCheck(Process); } }*/</span> <span class="hljs-keyword">return</span>; }</code>
四、其他部分
代码来自当年自己做的小项目,年代有点久远很多细节想不起来了,那时候编码习惯也不是很好,注释不多而且还保留了大段的废弃代码;当年写了文档但是毁于今年一场小地震造成的硬盘损毁。
伸手党没法直接拿去用,里面包含了对object hook的处理部分,下次有空再写。
当年配合简单的进程断链隐藏可以通过大部分常见的检测方法而且未被发现,代码里用了不少硬编码,如果用在用户量大的产品中,最好通过搜索实现结构偏移定位。
另外,Windows NT 6.0 64位系统下该方法失效,因为SSDT获取不再通过PKTHREAD->ServiceTable。