【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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM