公司有个监控程序涉及到进程的保护问题,需要避免用户通过任务管理器结束掉监控进程,这里使用了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实现代码: ![]() 挂钩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,文章也已经提及了,相对简单
【进程保护知识扩展】 本文中提及是从应用层的角度来保护进程不被关闭,通过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
|
|