zoukankan      html  css  js  c++  java
  • 【Hook技术】实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展

    【Hook技术】实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展

    公司有个监控程序涉及到进程的保护问题,需要避免用户通过任务管理器结束掉监控进程,这里使用了HOOK技术,通过Hook OperProcess来实现进程的保护.

    正常的结束进程的流程是(应用层)

     a.OpenProcess 打开进程,获取进程的句柄.

     b.将a获取的进程句柄传递给TerminateProcess,最后由TermianteProcess来完成进程的关闭.

    ps:TerminateProcess又会调用系统的NtTerminateProcess,然后逐步深入内核层,最终调用内核API完成进程的关闭和进程相关资源的释放.

    (在应用层大多数进程的关闭都是走上面流程的,包括任务管理器,所以我们知道在Opernprocess和TerminateProcess间,我们只要Hook OpenProcess,让程序无法打开进程获取到句柄就可以了,那样TerminateProcess调用自然也就失败了)

    HANDLE WINAPI OpenProcess(
      _In_  DWORD dwDesiredAccess,
      _In_  BOOL bInheritHandle,
      _In_  DWORD dwProcessId      //进程ID,我们所关注的
    );

    由OpenProcess函数原型我们知道,第三个参数dwProcessId标识要打开进程的ID,而我们可以通过拦截OpenProcess,然后判断dwProcess是否为保护的进程ID.

    例如:我们自定义的Hook_OpenProcess

    复制代码
    HANDLE WINAPI Hook_OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId)
    {
        //判断打开进程的权限和PID,其中lpData->dwProcessId存储我们要保护的进程ID
        if(dwDesiredAccess == PROCESS_TERMINATE && dwProcessId == lpData->dwProcessId)
        { 
                    //为保护的进程直接返回NULL给TerminateProcess
            return NULL;
        }
            
           
        return OpenProcess(dwDesiredAccess,bInheritHandle,dwProcessId);
    }
    复制代码

    定制好了自己的OpenProcess的函数,接下来我们要做的就是如何将原函数(OpenProcess)替换为我们自己的Hook_OpenProcess,只有程序在调用OpenProcess时候先走我们的Hook_OpenProcess那么保护才能顺序的进行. 而替换的主要方法主要有:

    1. 通过Patch  IAT表,要求对PE文件格式熟悉

    2. 通过修改原函数的入口处几个字节,实现跳转到我们定制的函数,这种技术我们称为inline hook,主要是通过修改入口前5个字节或者7个字节,或者patch函数的中部代码,也可以patch尾部.

    我们这里用的是第一种方法,这里HOOK类,使用的《核心编程》中的CAPIHook,例如:

    复制代码
    class QHookSrv
    {
    public:
       // Hook a function in all modules
       QHookSrv(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook, 
          BOOL fExcludeAPIHookMod);
    
       // Unhook a function from all modules
       ~QHookSrv();
    
       // Returns the original address of the hooked function
       operator PROC() { return(m_pfnOrig); }
    
    private:
       static PVOID sm_pvMaxAppAddr; // Maximum private memory address
       static QHookSrv* sm_pHead;    // Address of first object
       QHookSrv* m_pNext;            // Address of next  object
    
       PCSTR m_pszCalleeModName;     // Module containing the function (ANSI)
       PCSTR m_pszFuncName;          // Function name in callee (ANSI)
       PROC  m_pfnOrig;              // Original function address in callee
       PROC  m_pfnHook;              // Hook function address
       BOOL  m_fExcludeAPIHookMod;   // Hook module w/CAPIHook implementation?
    
    private:
       // Replaces a symbol's address in a module's import section
       static void WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, 
          PROC pfnOrig, PROC pfnHook, BOOL fExcludeAPIHookMod);
    
       // Replaces a symbol's address in all module's import sections
       static void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, 
          PROC pfnOrig, PROC pfnHook, HMODULE hmodCaller);
    };
    复制代码

    QHookSrv实现代码:

     View Code

    PVOID QHookSrv::sm_pvMaxAppAddr = NULL;
    const BYTE cPushOpCode = 0x68; // The PUSH opcode on x86 platforms

    QHookSrv* QHookSrv::sm_pHead = NULL;

    QHookSrv::QHookSrv(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook, BOOL fExcludeAPIHookMod)
    {
    if(sm_pvMaxAppAddr == NULL)
    {
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    sm_pvMaxAppAddr = si.lpMaximumApplicationAddress;
    }

    m_pNext = sm_pHead; // The next node was at the head
    sm_pHead = this; // This node is now at the head

    // Save information about this hooked function
    m_pszCalleeModName = pszCalleeModName;
    m_pszFuncName = pszFuncName;
    m_pfnHook = pfnHook;
    m_fExcludeAPIHookMod = fExcludeAPIHookMod;
    m_pfnOrig = GetProcAddress(GetModuleHandleA(pszCalleeModName), m_pszFuncName);

    if (m_pfnOrig > sm_pvMaxAppAddr) {
    // The address is in a shared DLL; the address needs fixing up
    PBYTE pb = (PBYTE) m_pfnOrig;
    if (pb[0] == cPushOpCode) {
    // Skip over the PUSH op code and grab the real address
    PVOID pv = * (PVOID*) &pb[1];
    m_pfnOrig = (PROC) pv;
    }
    }

    // Hook this function in all currently loaded modules
    ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook,
    m_fExcludeAPIHookMod);
    }

    QHookSrv::~QHookSrv()
    {
    // Unhook this function from all modules
    ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig, m_fExcludeAPIHookMod);

    // Remove this object from the linked list
    QHookSrv* p = sm_pHead;
    if (p == this) { // Removing the head node
    sm_pHead = p->m_pNext;
    } else {

    BOOL fFound = FALSE;

    // Walk list from head and fix pointers
    for (; !fFound && (p->m_pNext != NULL); p = p->m_pNext) {
    if (p->m_pNext == this) {
    // Make the node that points to us point to the our next node
    p->m_pNext = p->m_pNext->m_pNext;
    break;
    }
    }
    }
    }

    static HMODULE ModuleFromAddress(PVOID pv)
    {
    MEMORY_BASIC_INFORMATION mbi;
    return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
    ? (HMODULE) mbi.AllocationBase : NULL);
    }

    void QHookSrv::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, BOOL fExcludeAPIHookMod)
    {
    HMODULE hmodThisMod = fExcludeAPIHookMod ? ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL;
    HANDLE m_hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
    if(m_hSnapshot != INVALID_HANDLE_VALUE)
    {
    MODULEENTRY32 me = { sizeof(me) };
    for (BOOL fOk = Module32First(m_hSnapshot, &me); fOk; fOk = Module32Next(m_hSnapshot, &me))
    {
    // NOTE: We don't hook functions in our own module
    if (me.hModule != hmodThisMod)
    {
    // Hook this function in this module
    ReplaceIATEntryInOneMod(pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
    }
    }
    CloseHandle(m_hSnapshot);
    }
    }

    void QHookSrv::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
    PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {

    // Get the address of the module's import section
    ULONG ulSize;
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

    if (pImportDesc == NULL)
    return; // This module has no import section


    // Find the import descriptor containing references to callee's functions
    for (; pImportDesc->Name; pImportDesc++) {
    PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
    if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
    break; // Found
    }

    if (pImportDesc->Name == 0)
    return; // This module doesn't import any functions from this callee

    // Get caller's import address table (IAT) for the callee's functions
    PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
    ((PBYTE) hmodCaller + pImportDesc->FirstThunk);

    // Replace current function address with new function address
    for (; pThunk->u1.Function; pThunk++) {

    // Get the address of the function address
    PROC* ppfn = (PROC*) &pThunk->u1.Function;

    // Is this the function we're looking for?
    BOOL fFound = (*ppfn == pfnCurrent);

    if (!fFound && (*ppfn > sm_pvMaxAppAddr)) {

    // If this is not the function and the address is in a shared DLL,
    // then maybe we're running under a debugger on Windows 98. In this
    // case, this address points to an instruction that may have the
    // correct address.

    PBYTE pbInFunc = (PBYTE) *ppfn;
    if (pbInFunc[0] == cPushOpCode) {
    // We see the PUSH instruction, the real function address follows
    ppfn = (PROC*) &pbInFunc[1];

    // Is this the function we're looking for?
    fFound = (*ppfn == pfnCurrent);
    }
    }

    if (fFound) {
    // The addresses match, change the import section address
    WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
    sizeof(pfnNew), NULL);
    return; // We did it, get out
    }
    }
    }

    挂钩OpenProcess :

    //Hook_OpenProcess为定制的拦截函数
    QHookSrv g_OpenProcess("kernel32.dll", "OpenProcess",(PROC)Hook_OpenProcess,TRUE);

    有了IAT hook类,也有了拦截函数,接下来我们要做的就是如何patch所有的进程中duiOpenProcess的调用了,这里我们通过安装全局的WH_SHELL钩子来实现

    应用层消息钩子安装通过API:

    复制代码
    HHOOK WINAPI SetWindowsHookEx(
      _In_  int idHook,             //钩子的类型(消息钩子)
      _In_  HOOKPROC lpfn,    //钩子回调,当前响应消息被触发时候调用
      _In_  HINSTANCE hMod, //实例模块句柄,一般是DLL句柄
      _In_  DWORD dwThreadId //0标识全局钩子,非0标识局部钩子
    );
    复制代码

    消息钩子的卸载:

    //只有一个参数,通过SetWindowsHookEx返回的钩子句柄
    BOOL WINAPI UnhookWindowsHookEx(
      _In_  HHOOK hhk
    );

    : 本Demo中钩子的安装和卸载过程

    复制代码
    //安装钩子 ,由Dll导出
    //参数pid标识保护进程的ID
    BOOL InstallHook(DWORD pid)
    {    
        BOOL bResult=FALSE;
        if(!glhHook)
        {
            glhHook = SetWindowsHookEx(WH_SHELL,ShellHookProc,glhInstance, 0);
            if(glhHook!=NULL)
            {
                //´´½¨ÄÚ´æ¹²Ïí
                hMapping=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x100,"PCMONITOR."); 
                if(hMapping != NULL) 
                { 
                    lpData=(LPSHWP_STRUCT)MapViewOfFile(hMapping,FILE_MAP_ALL_ACCESS,0,0,0); 
                    lpData->dwProcessId = pid; 
                }
                bResult=TRUE;
            }
        }
        return bResult; 
    }
    
    //卸载钩子,UninstallHook由Dll导出
    BOOL UninstallHook()
    {    
        BOOL bResult=FALSE;
        if(glhHook)
        {
            bResult= UnhookWindowsHookEx(glhHook);
            if(bResult)
            {
                glhHook=NULL;
            }
        }
        return bResult;
    }
    复制代码

    消息回调函数,回调不进行任何操作调用CallNextHookEx将消息传递下一个处理程序

    static LRESULT WINAPI ShellHookProc(int code, WPARAM wParam, LPARAM lParam) 
    {
        return ::CallNextHookEx(glhHook, code, wParam, lParam);
    }

    +

    NET(C#)中的调用:

    复制代码
            // QomoHookSrv.dll 
            [DllImport("QomoHookSrv.dll", CallingConvention=CallingConvention.Cdecl)]
            private extern static bool InstallHook( int processID);
            [DllImport("QomoHookSrv.dll", CallingConvention=CallingConvention.Cdecl)]
            private extern static bool UninstallHook();
    
            private void install_hook_when_app_startup() {
                //获取进程的ID
                int pid = System.Diagnostics.Process.GetCurrentProcess().Id;
                //安装钩子
                InstallHook(pid);
            }
    
            private void uninstall_hook_when_app_closed() {
                UninstallHook();
            }
    复制代码

    源码仅包含HOOK DLL代码(核心代码), 测试工程请自行code,文章也已经提及了,相对简单
    我是Source

    【进程保护知识扩展】

    本文中提及是从应用层的角度来保护进程不被关闭,通过Hook OperProcess 和TerminateProcess并不是安全了,应用程进程的关闭有些不是走该流程的,比如说直接调用NtTerminateProcess或者通过NtSetSystemInformation等

    进程关闭的流程大致为(系统API间的调用关系);

    通过上述流程相应的进程保护方法:

    1.应用层Hook OpenProcess/TerminateProcess ,或者调用RtlSetprocessiscritical

    2.内核层方式实现进程保护相对方式比较多点:

      a. Hook NtTerminaterProcess/ZwTerminateProcess(两个是一样的,在R3是为NtTerminateProcess,内核就有NtTerminateProcess转为ZwTerminateProcess调用),这种拦截可以通过修改内核表SSDT,或者InlineHook实现

      b.Hook ObReferenceObjectByHandle  ,网上有很多关于该API的HoOK源码,也是进程保护中常用的一种手段,因为后续的很多操作都是需要通过调用该函数返回的进程对象体

      c. Hook PspTerminateThreadByPointer/PspTerminateProcess/PspExitThread ,pspXX函数是比较底层的API了,而未导出,获取他们地址需要通过硬编码的方式,所以不同的系统可能存在问题,所以这种方法是相对比较危险的,但是确实很有效的

     d. Hook 插APC函数,例如:KeInitializeApc等这种方法也相对比较实用多,网上也有很多这方面的例子,不过大多数都是通过插APC的方式来强制结束进程的.


    专注于: Net分布式技术,移动服务端架构及系统安全学习及研究  by Andy


     
     
    分类: System Hacker
  • 相关阅读:
    win10 UWP button
    内网分享资源
    内网分享资源
    CF724F Uniformly Branched Trees
    win10 UWP FlipView
    win10 UWP FlipView
    win10 UWP FlipView
    搭建阿里云 centos mysql tomcat jdk
    搭建阿里云 centos mysql tomcat jdk
    win10 UWP 申请微软开发者
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3311279.html
Copyright © 2011-2022 走看看