一种针对线程的隐蔽式SSDT Hook
phpskycn2018/09/21软件综合 IP:云南

这篇文章介绍了本科大一时(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):



_KiFastCallEntry        proc

;
; Sanitize the segment registers
;
        mov     ecx, KGDT_R3_DATA OR RPL_MASK
        push    KGDT_R0_PCR
        pop     fs
        mov     ds, ecx
        mov     es, ecx

;
; When we trap into the kernel via fast system call we start on the DPC stack. We need
; shift to the threads stack before enabling interrupts.
;
        mov     ecx, PCR[PcTss]        ;
        mov     esp, [ecx]+TssEsp0

        push    KGDT_R3_DATA OR RPL_MASK   ; Push user SS
        push    edx                         ; Push ESP
        pushfd
Kfsc10:
        push    2                           ; Sanitize eflags, clear direction, NT etc
        add     edx, 8                      ; (edx) -> arguments
        popfd                               ;
.errnz(EFLAGS_INTERRUPT_MASK AND 0FFFF00FFh)
        or      byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags

        push    KGDT_R3_CODE OR RPL_MASK    ; Push user CS
        push    dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address
        push    0                           ; put pad dword for error on stack
        push    ebp                         ; save the non-volatile registers
        push    ebx                         ;
        push    esi                         ;
        push    edi                         ;
        mov     ebx, PCR[PcSelfPcr]         ; Get PRCB address
        push    KGDT_R3_TEB OR RPL_MASK     ; Push user mode FS
        mov     esi, [ebx].PcPrcbData+PbCurrentThread   ; get current thread address
;
; Save the old exception list in trap frame and initialize a new empty
; exception list.
;

        push    [ebx].PcExceptionList       ; save old exception list
        mov     [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list
        mov     ebp, [esi].ThInitialStack

;
; Save the old previous mode in trap frame, allocate remainder of trap frame,
; and set the new previous mode.
;
        push    MODE_MASK                  ; Save previous mode as user
        sub     esp,TsPreviousPreviousMode ; allocate remainder of trap frame
        sub     ebp, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH
        mov     byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user
;
; Now the full trap frame is build.
; Calculate initial stack pointer from thread initial stack to contain NPX and trap.
; If this isn't the same as esp then we are a VX86 thread and we are rejected
;

        cmp     ebp, esp
        jne     short Kfsc91

;
; Set the new trap frame address.
;
        and     dword ptr [ebp].TsDr7, 0
        test    byte ptr [esi].ThDebugActive, 0ffh ; See if we need to save debug registers
        mov     [esi].ThTrapFrame, ebp   ; set new trap frame address

        jnz     Dr_FastCallDrSave       ; if nz, debugging is active on thread

Dr_FastCallDrReturn:                       ;

        SET_DEBUG_DATA                  ; Note this destroys edi
        sti                             ; enable interrupts

?FpoValue = 0

;
; (eax) = Service number
; (edx) = Callers stack pointer
; (esi) = Current thread address
;
; All other registers have been saved and are free.
;
; Check if the service number within valid range
;

_KiSystemServiceRepeat:
        mov     edi, eax                ; copy system service number
        shr     edi, SERVICE_TABLE_SHIFT ; isolate service table number
        and     edi, SERVICE_TABLE_MASK ;
        mov     ecx, edi                ; save service table number
        add     edi, [esi]+ThServiceTable ; compute service descriptor address
        mov     ebx, eax                ; save system service number
        and     eax, SERVICE_NUMBER_MASK ; isolate service table offset

这里有一个值得注意的地方:SSDT的地址并非是固定或者存在某个全局变量之中,而是通过PCR拿到当前线程的KTHREAD之后,在KTHREAD之中获取。

KTHREAD的结构如下(位于\base\ntos\inc\ke.h):




typedef struct _KTHREAD {

    //
    // The dispatcher header and mutant listhead are fairly infrequently
    // referenced.
    //

    DISPATCHER_HEADER Header;
    LIST_ENTRY MutantListHead;

    //
    // The following fields are referenced during context switches and wait
    // operatings. They have been carefully laid out to get the best cache
    // hit ratios.
    //

    PVOID InitialStack;
    PVOID StackLimit;
    PVOID KernelStack;

    KSPIN_LOCK ThreadLock;
    union {
        KAPC_STATE ApcState;
        struct {
            UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
            BOOLEAN ApcQueueable;
            volatile UCHAR NextProcessor;
            volatile UCHAR DeferredProcessor;
            UCHAR AdjustReason;
            SCHAR AdjustIncrement;
        };
    };

    KSPIN_LOCK ApcQueueLock;

#if !defined(_AMD64_)

    ULONG ContextSwitches;
    volatile UCHAR State;
    UCHAR NpxState;
    KIRQL WaitIrql;
    KPROCESSOR_MODE WaitMode;

#endif

    LONG_PTR WaitStatus;
    union {
        PKWAIT_BLOCK WaitBlockList;
        PKGATE GateObject;
    };

    BOOLEAN Alertable;
    BOOLEAN WaitNext;
    UCHAR WaitReason;
    SCHAR Priority;
    UCHAR EnableStackSwap;
    volatile UCHAR SwapBusy;
    BOOLEAN Alerted[MaximumMode];
    union {
        LIST_ENTRY WaitListEntry;
        SINGLE_LIST_ENTRY SwapListEntry;
    };

    PRKQUEUE Queue;

#if !defined(_AMD64_)

    ULONG WaitTime;
    union {
        struct {
            SHORT KernelApcDisable;
            SHORT SpecialApcDisable;
        };

        ULONG CombinedApcDisable;
    };

#endif

    PVOID Teb;
    union {
        KTIMER Timer;
        struct {
            UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];

            //
            // N.B. The following bit number definitions must match the
            //      following bit field.
            //
            // N.B. These bits can only be written with interlocked
            //      operations.
            //
    
#define KTHREAD_AUTO_ALIGNMENT_BIT 0
#define KTHREAD_DISABLE_BOOST_BIT 1
    
            union {
                struct {
                    LONG AutoAlignment : 1;
                    LONG DisableBoost : 1;
                    LONG ReservedFlags : 30;
                };
        
                LONG ThreadFlags;
            };
        };
    };

    union {
        KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
        struct {
            UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0];
            BOOLEAN SystemAffinityActive;
        };

        struct {
            UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1];
            CCHAR PreviousMode;
        };

        struct {
            UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2];
            UCHAR ResourceIndex;
        };

        struct {
            UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3];
            UCHAR LargeStack;
        };

#if defined(_AMD64_)

        struct {
            UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0];
            ULONG ContextSwitches;
        };

        struct {
            UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1];
            volatile UCHAR State;
            UCHAR NpxState;
            KIRQL WaitIrql;
            KPROCESSOR_MODE WaitMode;
        };

        struct {
            UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2];
            ULONG WaitTime;
        };

        struct {
            UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3];
             union {
                 struct {
                     SHORT KernelApcDisable;
                     SHORT SpecialApcDisable;
                 };
         
                 ULONG CombinedApcDisable;
             };
        };

#endif

    };

    LIST_ENTRY QueueListEntry;

    //
    // The following fields are accessed during system service dispatch.
    //

    PKTRAP_FRAME TrapFrame;
    PVOID CallbackStack;
    PVOID ServiceTable;

#if defined(_AMD64_)

    ULONG KernelLimit;

#endif

    //
    // The following fields are referenced during ready thread and wait
    // completion.
    //

    UCHAR ApcStateIndex;
    UCHAR IdealProcessor;
    BOOLEAN Preempted;
    BOOLEAN ProcessReadyQueue;

#if defined(_AMD64_)

    PVOID Win32kTable;
    ULONG Win32kLimit;

#endif

    BOOLEAN KernelStackResident;
    SCHAR BasePriority;
    SCHAR PriorityDecrement;
    CHAR Saturation;
    KAFFINITY UserAffinity;
    PKPROCESS Process;
    KAFFINITY Affinity;

    //
    // The below fields are infrequently referenced.
    //

    PKAPC_STATE ApcStatePointer[2];
    union {
        KAPC_STATE SavedApcState;
        struct {
            UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
            CCHAR FreezeCount;
            CCHAR SuspendCount;
            UCHAR UserIdealProcessor;
            UCHAR CalloutActive;

#if defined(_AMD64_)

            BOOLEAN CodePatchInProgress;

#elif defined(_X86_)

            UCHAR Iopl;

#else

            UCHAR OtherPlatformFill;

#endif

        };
    };

    PVOID Win32Thread;
    PVOID StackBase;
    union {
        KAPC SuspendApc;
        struct {
            UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0];
            SCHAR Quantum;
        };

        struct {
            UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1];
            UCHAR QuantumReset;
        };

        struct {
            UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG];
            ULONG KernelTime;
        };

        struct {
            UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1];
            PVOID TlsArray;
        };

        struct {
            UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2];
            PVOID BBTData;
        };

        struct {
            UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH];
            UCHAR PowerState;
            ULONG UserTime;
        };
    };

    union {
        KSEMAPHORE SuspendSemaphore;
        struct {
            UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
            ULONG SListFaultCount;
        };
    };

    LIST_ENTRY ThreadListEntry;
    PVOID SListFaultAddress;

#if defined(_WIN64)

    LONG64 ReadOperationCount;
    LONG64 WriteOperationCount;
    LONG64 OtherOperationCount;
    LONG64 ReadTransferCount;
    LONG64 WriteTransferCount;
    LONG64 OtherTransferCount;

#endif

} KTHREAD, *PKTHREAD, *PRKTHREAD;

#if !defined(_X86AMD64_) && defined(_AMD64_)

C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + 16) == FIELD_OFFSET(KTHREAD, Win32kTable));
C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + 8) == FIELD_OFFSET(KTHREAD, KernelLimit));
C_ASSERT((FIELD_OFFSET(KTHREAD, Win32kTable) + 8) == FIELD_OFFSET(KTHREAD, Win32kLimit));

#endif

也就是说,在完成系统服务调用的过程中,系统内核实际使用的是SSDT地址是当前线程的PKTHREAD->ServiceTable,只要修改了这个值,也能实现SSDT Hook,并且这个值仅对当前线程有效,并不会产生全局效应,hook的隐蔽性更强。

配合内核重载,这种形式的SSDT Hook可以保护制定的线程,绕过其他程序的SSDT/Kernel Inline Hook(对于Object Hook无效)。

三、针对线程的隐蔽式SSDT Hook实现

首先实现一些辅助函数和数据结构:

typedef struct _KSERVICE_TABLE_DESCRIPTOR 
{ 
    PULONG_PTR Base;  
    PULONG Counter; 
    ULONG Limit;  
    PUCHAR Number; 
}KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE,**PPKSYSTEM_SERVICE_TABLE;

PEPROCESS PsLookUpProcessByName(PUCHAR TragetProcessName){

    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;

    
    do{
        
        RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName((PEPROCESS)ProcessLinks.Flink) - Offset_ActiveProcessLinks_KPROCESS));

        if(RtlEqualString(&TragetName,&ProcessName,0)){

            return  (PEPROCESS)RtlPointerAddOffset(ProcessLinks.Flink,- Offset_ActiveProcessLinks_KPROCESS);/*(PEPROCESS)(ProcessLinks.Flink - Offset_ActiveProcessLinks_KPROCESS);*/
        }

        ProcessLinks = *ProcessLinks.Flink;
    }while(ProcessLinks.Flink != ProcessLinksHead.Flink);

    KdPrint(("PsLookUpProcessByName() -- Can't Find Traget Process\n"));

    return NULL;

}

PKTHREAD PsGetGUIThread(){

    
    PEPROCESS PTragetProcess;
    LIST_ENTRY ThreadList;
    LIST_ENTRY ThreadListHead;
    PULONG PWin32Thread;

    KdPrint(("PsGetGUIThread()\n"));

    PTragetProcess = PsLookUpProcessByName((PUCHAR)"csrss.exe");
    if(NULL == PTragetProcess){
        KdPrint((" PsGetGUIThread() -- Can't Find Traget Process\n"));
        return NULL;
    }
    ThreadList = *(PLIST_ENTRY)((PUCHAR)PTragetProcess + Offset_ThreadListHead_KPROCESS);
    ThreadListHead = ThreadList;
    
    
    do{

        PWin32Thread = (PULONG)((PUCHAR)ThreadList.Flink + (Offset_Win32Thread_KTHREAD - Offset_ThreadListEntry_KTHREAD));
        if(0 != *PWin32Thread){

            KdPrint(("PsGetGUIThread() -- return %x\n",(PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD)));

            return (PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD);
        }

        ThreadList = *ThreadList.Flink;

    }while(ThreadList.Flink != ThreadListHead.Flink);

    KdPrint(("PsGetGUIThread() -- Can't Find GUI Thread\n\n\n"));

    return NULL;
}

重载内核并初始化SSDT:

NTSTATUS KeInitSSDTData(PDRIVER_OBJECT   DriverObject){

    NTSTATUS Status;
    PKTHREAD PCUIThread;
    PKTHREAD PGUIThread;


    KdPrint(("InitSSDTData()\n"));
    if(SSDIF&&SDIF){

        Status = STATUS_UNSUCCESSFUL;
        return Status;
    }

    PCUIThread = PsGetCUIThread();
    PGUIThread = PsGetGUIThread();

    if(!(PCUIThread && PGUIThread)){

        KdPrint(("InitSSDTData() -ERROR to get threadinfo \n"));
        KdPrint(("PCUIThread:%x\n",PCUIThread));
        KdPrint(("PGUIThread:%x\n",PGUIThread));
        Status = STATUS_UNSUCCESSFUL;
        return Status;
    }


    
    Status = ReLoadNtos(DriverObject,RetAddress);

    if(!(NT_SUCCESS(Status))){

        KdPrint(("InitSSDTData() -ERROR to reload ntos \n"));

        return Status;
    }

    Status = ReloadWin32K(DriverObject);
    if(!(NT_SUCCESS(Status))){

        KdPrint(("InitSSDTData() -ERROR to reload Win32k \n"));

        return Status;
    }


    KdPrint(("Safe_ServiceDescriptorTable:%x \n",Safe_ServiceDescriptorTable));
    __try{
        
        PSystemSSDT = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PCUIThread + (Offset_ServiceTable_KTHREAD));
        PSystemSSDTShadow = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PGUIThread + (Offset_ServiceTable_KTHREAD));

    }
    __except(EXCEPTION_EXECUTE_HANDLER){

        KdPrint(("InitSSDTData() -ERROR ACCESS_VIOLATION \n"));
        return STATUS_ACCESS_VIOLATION;
    
    }

    PSafeSSDT = (PKSYSTEM_SERVICE_TABLE)Safe_ServiceDescriptorTable;

    PSafeSSDTShadow = (PKSYSTEM_SERVICE_TABLE) ExAllocatePool(PagedPool,32);

    if(NULL == PSafeSSDTShadow){
        
        Status = STATUS_UNSUCCESSFUL;
        return Status;
    }

    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Base = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Base;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Counter = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Counter;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Limit = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Limit;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Number = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Number;

    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Base = Safe_ServiceDescriptorShadowSSDTTable->ServiceTable;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Counter = Safe_ServiceDescriptorShadowSSDTTable->CounterTable;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Limit = Safe_ServiceDescriptorShadowSSDTTable->TableSize;
    (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Number = Safe_ServiceDescriptorShadowSSDTTable->ArgumentTable;

    SSDIF = 1;//Set flag

    SDIF = 1;//Set flag

    ProcessInfo.ProcessCount = 0;
    ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)&ProcessInfo;
    ProcessInfo.ProcessListHead.Flink = (PLIST_ENTRY)&ProcessInfo;

    ExInitializeFastMutex(&ProcessInfo.FastMutex);
    //ObvInitVirtualHandleMoudle();
    Status = InitializeCommonVariables();

#ifdef DEBUGINFO

    KdPrint(("ProcessInfoTable:%x\n",&ProcessInfo));

#endif

    return STATUS_SUCCESS;
}

替换PKTHREAD->ServiceTable:

NTSTATUS ReplaceServiceTableOfThread(PKTHREAD PTragetThread){

    PULONG PWin32Thread;
    PULONG* PServiceTable;
    PWin32Thread = (PULONG)((PUCHAR)PTragetThread + Offset_Win32Thread_KTHREAD);
    PServiceTable = (PULONG*)((PUCHAR)PTragetThread + Offset_ServiceTable_KTHREAD);

    __try{

        if(0 == *PWin32Thread){

            //On CUIThread.Use SSDT 

            if(0 == SDIF){
                //The Date of My SSDT have not be inited
                return STATUS_UNSUCCESSFUL;
            }
            if(*PServiceTable != (PULONG)PSafeSSDT){

                *PServiceTable = (PULONG)PSafeSSDT;
            }
        }
        else{
            if(0 == SSDIF){
                //The Date of My SSDT  have not be inited
                return STATUS_UNSUCCESSFUL;
            }
            if(*PServiceTable != (PULONG)PSafeSSDTShadow){

                *PServiceTable = (PULONG)PSafeSSDTShadow;
            }
        }
        
    }
    __except(EXCEPTION_EXECUTE_HANDLER){

        KdPrint(("ReplaceServiceTableOfThread() -ERROR ACCESS_VIOLATION \n"));
        return STATUS_ACCESS_VIOLATION;
    
    }


    return STATUS_SUCCESS;
}

处理指定进程(作为暴露给外界的接口,参数为进程名):

NTSTATUS KeReplaceServiceTableOfProcessCheck(PEPROCESS TragetProcess){


    NTSTATUS Status;
    PLIST_ENTRY NextEntry;
    PPROCESS_PROTECT_INFO_ENTRY PProcessInfoEntry;
    PVOID HandleTable;


    ExAcquireFastMutex(&(ProcessInfo.FastMutex));

    //Check that the TragetProcess is not in the table
    NextEntry = ProcessInfo.ProcessListHead.Flink;

    while(NextEntry != (PLIST_ENTRY)&(ProcessInfo.ProcessListHead)){
    
        PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)NextEntry;

        if(PProcessInfoEntry->Process == TragetProcess){

            //The TragetProcess was in the table,just return success

            Status = STATUS_SUCCESS;
            goto End;
        }

        NextEntry = PProcessInfoEntry->ProcessList.Flink;
    }

    //We ture that is a new process
    //Now,Init process info and protect the trager process
    
    //�ڽ�����Ϣ������ӱ���

    PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)ExAllocatePool(PagedPool,sizeof(PROCESS_PROTECT_INFO_ENTRY));
    
    if(!PProcessInfoEntry){
    
        Status = STATUS_NO_MEMORY;
        goto End;
    }

    Status = ObvCreateVirtualHandleTable(TragetProcess,(PPVIRTUAL_HANDLE_TABLE)&HandleTable);

    if(!NT_SUCCESS(Status)){

        ExFreePool(PProcessInfoEntry);
        goto 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++;

    //--
    Status = KeReplaceServiceTableOfProcess(TragetProcess);

    //##########Hide Process###############
    //
    //Status = PsHideProcess(TragetProcess);

    //#####################################

    //Check that is success to replace the ServiceTable
    if(!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);

    return Status;

}

NTSTATUS KeReplaceServiceTableOfProcessByName(char* ProcessName){

    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    PEPROCESS PTragetProcess = PsLookUpProcessByName((PUCHAR)ProcessName);

    KdPrint((" ReplaceServiceTableOfProcessByName()\n"));

    if(PTragetProcess == NULL){
        return Status;
    }
    
    Status = KeReplaceServiceTableOfProcessCheck(PTragetProcess);

    return Status;
}

进程/线程创建通知,防止冲突造成蓝屏:

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);
        if(!NT_SUCCESS(Status)){//PID->PEPROCESS
            
            return;
        }
        
    //RObfDereferenceObjectSafe(Process);
    ObDereferenceObject(Process);
        if(!Create){
        //���̹ر�֪ͨ
        //�������Ƿ�Ϊ�ܱ������̣������ͷ���Ӧ��Դ

        
        ExAcquireFastMutex(&(ProcessInfo.FastMutex));
        
        NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)ProcessInfo.ProcessListHead.Flink;

        while(NextProcess != (PPROCESS_PROTECT_INFO_ENTRY)&ProcessInfo.ProcessListHead){

            if(NextProcess->Process == Process){

    
                HandleTable = (PVIRTUAL_HANDLE_TABLE)NextProcess->HabdleTable;
                
                //����վ����ָ��
                NextProcess->HabdleTable = NULL;

                Status = ObvFreeVirtualHandleTable(HandleTable);
                
                (NextProcess->ProcessList.Flink)->Blink = (NextProcess->ProcessList.Blink);
                (NextProcess->ProcessList.Blink)->Flink = (NextProcess->ProcessList.Flink);
                ProcessInfo.ProcessCount--;
                ExFreePool(NextProcess);

                //�Ѿ��ͷŵ��ˣ�ֱ���ͷ��� + ����
                //�����غ������Ϊδ֪��
                ExReleaseFastMutex(&ProcessInfo.FastMutex);
                return;
            
            }
            NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)NextProcess->ProcessList.Flink;
        }

        

        ExReleaseFastMutex(&ProcessInfo.FastMutex);
    
    }
    /*
    else if(FocusOn){
    
        RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName(Process)));
        if(!strcmp(ProcessName.Buffer,(char*)ProcessSpyOn)){
        
            KeReplaceServiceTableOfProcessCheck(Process);
        }
    }*/
        
        return;
}

四、其他部分

代码来自当年自己做的小项目,年代有点久远很多细节想不起来了,那时候编码习惯也不是很好,注释不多而且还保留了大段的废弃代码;当年写了文档但是毁于今年一场小地震造成的硬盘损毁。
伸手党没法直接拿去用,里面包含了对object hook的处理部分,下次有空再写。
当年配合简单的进程断链隐藏可以通过大部分常见的检测方法而且未被发现,代码里用了不少硬编码,如果用在用户量大的产品中,最好通过搜索实现结构偏移定位。
另外,Windows NT 6.0 64位系统下该方法失效,因为SSDT获取不再通过PKTHREAD->ServiceTable。

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

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

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

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

CV

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

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