【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实现代码:

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


 
 

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

  1. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

  2. [C#防止反编译].NET 产品版权保护方案 (.NET源码加密保护)

    [C#防止反编译].NET 产品版权保护方案 (.NET源码加密保护) 标签: .net加密产品c#dll工具 2011-03-24 21:06 27009人阅读 评论(13) 收藏 举报 分类: C ...

  3. 在Winform窗体中使用WPF控件(附源码)

    原文:在Winform窗体中使用WPF控件(附源码) 今天是礼拜6,下雨,没有外出,闲暇就写一篇博文讲下如何在Winform中使用WPF控件.原有是我在百度上搜索相关信息无果,遂干脆动手自己实现. W ...

  4. c++实现游戏开发中常用的对象池(含源码)

    c++实现游戏开发中常用的对象池(含源码) little_stupid_child2017-01-06上传   对象池的五要素: 1.对象集合 2.未使用对象索引集合 3.已使用对象索引集合 4.当前 ...

  5. chrome浏览器开发者工具F12中某网站的sources下的源码如何批量保存?

    目录 chrome浏览器 开发者工具F12中某网站的sources下的源码如何批量保存 1. 常用保存Sources源码的两种方法 1.1单个文件 1.2 单个页面 2. 问题 3.解决方案 chro ...

  6. 调试过程中发现按f5无法走进jdk源码

    debug 模式 ,在fis=new FileInputStream(file); 行打断点 调试过程中发现按f5无法走进jdk源码 package com.lzl.spring.test; impo ...

  7. .NET 产品版权保护方案 (.NET源码加密保护) (转载)

    说 明:你希望自己用.net辛辛苦苦做出来的软件被人轻易破解吗?你希望自己花了大量人力物力用.net开发出来的产品被竞争对手轻易获取核心代码吗?这是 一篇比较详尽地介绍如何保护自己的.net源代码的文 ...

  8. CI框架源码阅读笔记6 扩展钩子 Hook.php

    CI框架允许你在不修改系统核心代码的基础上添加或者更改系统的核心功能(如重写缓存.输出等).例如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = ...

  9. 第一次作业:基于Linux操作系统深入源码进程模型分析

    1.Linux操作系统的简易介绍 Linux系统一般有4个主要部分:内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使 ...

随机推荐

  1. 6天通吃树结构—— 第五天 Trie树

    原文:6天通吃树结构-- 第五天 Trie树 很有段时间没写此系列了,今天我们来说Trie树,Trie树的名字有很多,比如字典树,前缀树等等. 一:概念 下面我们有and,as,at,cn,com这些 ...

  2. 【Socket计划】使用C++实现Server结束Client结束

    我是在Visual Stdio 2013两人的建立project.编译如下两个人main文件,然后测试 服务器:Server.cpp #include <WINSOCK2.H> #incl ...

  3. android--解--它们的定义tabhost(动态添加的选项+用自己的主动性横向滑动标签+手势切换标签页和内容特征)

    在本文中,解决他们自己的定义tabhost实现,并通过代码集成动态加入标签功能.自己主动标签横向滑动功能.和手势标签按功能之间切换. 我完成了这个完美的解决方案一起以下: 1.定义tabwidget布 ...

  4. 开源搜索引擎Iveely 0.8.0

    开源搜索引擎Iveely 0.8.0 这是一篇博客,不是,这是一篇开源人的心酸和喜悦,没有人可以理解我们的心情,一路的辛酸一路的艰辛,不过还好,在大家的支持下,总算是终见天日,谢谢那些给予我们无私帮助 ...

  5. EasyUI Combobox 默认设置

    /** *绑定运营商,默认设置, 演出CMCC, 传统的价值观念1 */ $('#operatingId').combobox({ url:'data_url', valueField:'id', t ...

  6. 了解了解你自己的话zookeeper(从那时起,纠正了一些说法在线)

    1,先看看官方的定义吧: ZooKeeper is a distributed, open-source coordination service for distributed applicatio ...

  7. SSAS系列——【01】准备知识

    原文:SSAS系列--[01]准备知识 关于SQL Server 产品,我从2004年就开始使用了,SQL Server 2K,2K5,2K8,到如今已经准6年了,说来惭愧,这六年来所涉及的内容都是在 ...

  8. std::move()

    #include <iostream> #include <utility> #include <vector> #include <string> i ...

  9. 【Heritrix基础教程2】Heritrix基本介绍

    1.版本号说明 (1)最新的版本号:3.3.0 (2)最新release版本号:3.2.0 (3)重要历史版本号:1.14.4 3.1.0及之前的版本号:http://sourceforge.net/ ...

  10. poj 3273 Monthly Expense (二分)

    //最大值最小 //天数的a[i]值是固定的 不能改变顺序 # include <algorithm> # include <string.h> # include <s ...