一种针对线程的隐蔽式SSDT Hook
phpskycn2018/09/21软件综合 IP:云南
<code class="hljs cpp"><br></code>

这篇文章介绍了本科大一时(2012年)发现的一种隐蔽式SSDT Hook方法,仅适用于32位的Windows系统。当时还没有Windows10,64位系统的普及率也不像现在这么高,当时主要涉及的是32位的Windows 7和Windows XP,代码在Windows XP SP3、Windows 2003 SP1/SP2、Windows 7 SP1、Windows 8下测试通过。

一、SSDT Hook

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。

[修改于 5年8个月前 - 2018/09/21 12:58:33]

来自:计算机科学 / 软件综合
0
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
phpskycn
专家 老干部 学者 机友 笔友
文章
402
回复
4591
学术分
8
2009/03/15注册,23天21时前活动

CV

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}