zoukankan      html  css  js  c++  java
  • windows:进程查杀

    windows平台中,某些进程做了各种保护,比如hook了terminateProcess,又或者注册了进程终止函数的回调。当调用这些API或任务管理器终止该进程时,会被绕过,典型如某些杀毒软件,怎么才能终止这些进程了?

    进程是由线程组成的,如果该进程名下所有线程都终止,此进程也会被windows回收和注销,终止进程的问题就转化成了终止线程;但如果直接调用terminateThread,同样面临terminateProcess被hook的窘境。深入逆向分析terminateThread后发现,真正终止线程的函数是PspTerminateThreadByPointer,整个调用逻辑为:NtTerminateThread->PsTerminateSystemThread->PspTerminateThreadByPointer,其中PsTerminateSystemThread是导入未文档化函数,可在驱动层掉用MmGetSystemRoutineAddress函数获取地址,进而得到PspTerminateThreadByPointer的地址(当然也能使用https://www.cnblogs.com/theseventhson/p/13024325.html该方法获取),核心函数如下:

    1、根据起始和终止地址、特征码查找代码偏移

    PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
    {
        PVOID pAddress = NULL;
        PUCHAR i = NULL;
        ULONG m = 0;
    
        // 扫描内存
        for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
        {
            // 判断特征码
            for (m = 0; m < ulMemoryDataSize; m++)
            {
                if (*(PUCHAR)(i + m) != pMemoryData[m])
                {
                    break;
                }
            }
            // 判断是否找到符合特征码的地址
            if (m >= ulMemoryDataSize)
            {
                // 找到特征码位置, 获取紧接着特征码的下一地址
                pAddress = (PVOID)(i + ulMemoryDataSize);
                break;
            }
        }
    
        return pAddress;
    }

    2、(1)PsTerminateSystemThread是导出未文档化的函数,可以直接用MmGetSystemRoutineAddress得到函数地址

          (2)windbg中根据PsTerminateSystemThread进一步查找PspTerminateThreadByPointer:这里用了e8作为特征码,直接定位到“e8dcf0fbff      call    nt!PspTerminateThreadByPointer (fffff803`d01c6210)”这行代码;

     kd> u 0xfffff803`d0207110   //下面偏移x20 = 32byte处

    nt!PsTerminateSystemThread:

    fffff803`d0207110 4883ec28        sub     rsp,28h

    fffff803`d0207114 8bd1            mov     edx,ecx

    fffff803`d0207116 65488b0c2588010000 mov   rcx,qword ptr gs:[188h]

    fffff803`d020711f f7417400040000  test    dword ptr [rcx+74h],400h

    fffff803`d0207126 0f84a0630e00    je      nt!PsTerminateSystemThread+0xe63bc (fffff803`d02ed4cc)

    fffff803`d020712c 41b001          mov     r8b,1

    fffff803`d020712f e8dcf0fbff      call    nt!PspTerminateThreadByPointer (fffff803`d01c6210)

      熟悉x86汇编的都知道:e8是call的硬编码,后面dcf0fbff是目标地址当对于当前的偏移,偏移为0xfffbf0dc。这个偏移很大,根据经验判断应该是个负数,0n-266020,那么PspTerminateThreadByPointer的计算方法:

      PspTerminateThreadByPointer =  当前地址 + 4 + 偏移(负数)

    =0xfffff800`63f7e130 + 0x4 + 0n-266020

    = ‭FFFFF80063F7E134‬ + 0n-266020

    = -8,794,415,832,780 - 266020 //统一转成10进制

    = -8,794,416,098,800‬

    = FFFF F800 63F3 D210‬

    继续windbg查一下:发现这个地址确实是PspTerminateThreadByPointer的,没错:

    kd> u 0xfffff800`63f3d210

    nt!PspTerminateThreadByPointer:

    fffff800`63f3d210 48895c2408      mov     qword ptr [rsp+8],rbx

    fffff800`63f3d215 48896c2410      mov     qword ptr [rsp+10h],rbp

    fffff800`63f3d21a 4889742418      mov     qword ptr [rsp+18h],rsi

    fffff800`63f3d21f 57              push    rdi

    fffff800`63f3d220 4883ec30        sub     rsp,30h

    fffff800`63f3d224 8b81d0060000    mov     eax,dword ptr [rcx+6D0h]

    fffff800`63f3d22a 418ae8          mov     bpl,r8b

    fffff800`63f3d22d 488bb920020000  mov     rdi,qword ptr [rcx+220h]

      详细代码如下(这里pSpecialData用E8就好):

    PVOID SearchPspTerminateThreadByPointer(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
    {
        UNICODE_STRING ustrFuncName;
        PVOID pAddress = NULL;
        LONG lOffset = 0;
        PVOID pPsTerminateSystemThread = NULL;
        PVOID pPspTerminateThreadByPointer = NULL;
    
        // 先获取 PsTerminateSystemThread 函数地址
        RtlInitUnicodeString(&ustrFuncName, L"PsTerminateSystemThread");
        pPsTerminateSystemThread = MmGetSystemRoutineAddress(&ustrFuncName);
        if (NULL == pPsTerminateSystemThread)
        {
            ShowError("MmGetSystemRoutineAddress", 0);
            return pPspTerminateThreadByPointer;
        }
    
        // 然后, 查找 PspTerminateThreadByPointer 函数地址
        pAddress = SearchMemory(pPsTerminateSystemThread,
            (PVOID)((PUCHAR)pPsTerminateSystemThread + 0xFF),//搜索255字节长度
            pSpecialData, ulSpecialDataSize);
        if (NULL == pAddress)
        {
            ShowError("SearchMemory", 0);
            return pPspTerminateThreadByPointer;
        }
    
        // 先获取偏移, 再计算地址
        lOffset = *(PLONG)pAddress;//0n-266020。注意这里向前跳,偏移是负数,有符号
        pPspTerminateThreadByPointer = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
    
        return pPspTerminateThreadByPointer;
    }

    3、得到PspTerminateThreadByPointer地址:

    PVOID GetPspLoadImageNotifyRoutine()
    {
        PVOID pPspTerminateThreadByPointerAddress = NULL;
        RTL_OSVERSIONINFOW osInfo = { 0 };
        UCHAR pSpecialData[50] = { 0 };
        ULONG ulSpecialDataSize = 0;
    
        pSpecialData[0] = 0xE8;
        ulSpecialDataSize = 1;
    
        // E8
        pSpecialData[0] = 0xE8;
        ulSpecialDataSize = 1;
    
    
        // 根据特征码获取地址
        pPspTerminateThreadByPointerAddress = SearchPspTerminateThreadByPointer(pSpecialData, ulSpecialDataSize);
        return pPspTerminateThreadByPointerAddress;
    }

    4、现在可以强杀进程了:

    // 强制结束指定进程
    NTSTATUS ForceKillProcess(HANDLE hProcessId)
    {
        PVOID pPspTerminateThreadByPointerAddress = NULL;
        PEPROCESS pEProcess = NULL;
        PETHREAD pEThread = NULL;
        PEPROCESS pThreadEProcess = NULL;
        NTSTATUS status = STATUS_SUCCESS;
        ULONG i = 0;
    
    #ifdef _WIN64
        // 64 位
        typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);
    #else
        // 32 位
        typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);
    #endif
    
        // 获取 PspTerminateThreadByPointer 函数地址
        pPspTerminateThreadByPointerAddress = GetPspLoadImageNotifyRoutine();
        if (NULL == pPspTerminateThreadByPointerAddress)
        {
            ShowError("GetPspLoadImageNotifyRoutine", 0);
            return FALSE;
        }
        // 获取结束进程的进程结构对象EPROCESS
        status = PsLookupProcessByProcessId(hProcessId, &pEProcess);
        if (!NT_SUCCESS(status))
        {
            ShowError("PsLookupProcessByProcessId", status);
            return status;
        }
        // 遍历所有线程, 并结束所有指定进程的线程
        for (i = 4; i < 0x80000; i = i + 4)
        {
            status = PsLookupThreadByThreadId((HANDLE)i, &pEThread);
            if (NT_SUCCESS(status))
            {
                // 获取线程对应的进程结构对象
                pThreadEProcess = PsGetThreadProcess(pEThread);
                // 结束指定进程的线程
                if (pEProcess == pThreadEProcess)
                {
                    ((PSPTERMINATETHREADBYPOINTER)pPspTerminateThreadByPointerAddress)(pEThread, 0, 1);
                    DbgPrint("PspTerminateThreadByPointer Thread:%d
    ", i);
                }
                // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏
                ObDereferenceObject(pEThread);
            }
        }
        // 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏
        ObDereferenceObject(pEProcess);
    
        return status;
    }

     5、测试环境:

     

  • 相关阅读:
    c++中的stack实现
    非虚函数是静态绑定
    函数返回const,以便控制访问
    析构函数为虚函数
    c++中初始化列表顺序和声明顺序一致
    define的误用
    模板就是让编译器帮你写代码
    mysql代码中设置变量
    拼接index
    python import vs from import
  • 原文地址:https://www.cnblogs.com/theseventhson/p/13024490.html
Copyright © 2011-2022 走看看