使用HOOK技术做进程保护
整体分析
整体思路:
- 给系统安装鼠标消息钩子做dll注入
- 成功注入之后,使用内联hook技术hook OpenProcess以及TerminateProcess
- 当任务管理器需要关闭进程的时候,会先调用OpenProcess打开进程,再调用TerminateProcess结束进程。我们hook OpenProcess的时候判断任务管理器打开的进程是不是我们要保护的进程,如果是的话保留进程句柄。在任务管理器调用TerminateProcess的时候,判断一下要关闭的句柄和我们保存的句柄,如果相同则直接返回true,否则调用原来的TerminateProcess结束进程。
这里的问题是,任务管理器如何知道我们要保护的进程的pid。我的思路是在我的主进程在创建个文件映射保存pid,在dll注入到其他进程的时候打开文件映射获取PID
MFC dll代码
// MonitorDll.h: MonitorDll DLL 的主标头文件
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含 'pch.h' 以生成 PCH"
#endif
#include "resource.h" // 主符号
#include "privilege.h"
// CMonitorDllApp
// 有关此类实现的信息,请参阅 MonitorDll.cpp
//
class CMonitorDllApp : public CWinApp
{
public:
CMonitorDllApp();
// 重写
public:
virtual BOOL InitInstance();
int ExitInstance();
DECLARE_MESSAGE_MAP()
};
// MonitorDll.cpp: 定义 DLL 的初始化例程。
//
#include "pch.h"
#include "framework.h"
#include "MonitorDll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
/*
全局变量
*/
// 共享变量
#pragma data_seg("Share")
HWND g_hwnd = NULL; // 主窗口句柄,加载HOOK时传入
HINSTANCE hInstance = NULL; // 本DLL的实例句柄
HHOOK hhook = NULL; // 鼠标钩子句柄
//DWORD g_dwProcessId; // 进程id
HANDLE g_hProcess = NULL; // 保存本进程在远进程中的句柄
CString g_dwProcessIdStr;
CString g_hProcessStr;
#pragma data_seg()
#pragma comment(linker,"/section:Share,rws")
// 其他变量定义
HANDLE hProcess = NULL; // 当前进程句柄
bool bIsInjected = false; // 保证只注入一次
#define CODE_LENGTH 5 // 入口指令长度
// TerminateProcess
typedef BOOL(WINAPI* TypeTerminateProcess)(_In_ HANDLE hProcess, _In_ UINT uExitCode); //Kernel32.dll
TypeTerminateProcess oldTerminateProcess = NULL;
FARPROC pfOldTerminateProcess = NULL;
BOOL WINAPI MyTerminateProcess(_In_ HANDLE hProcess, _In_ UINT uExitCode);
BYTE oldCodeTermPro[CODE_LENGTH]; // 原API入口
BYTE newCodeTermpro[CODE_LENGTH]; // 新API入口
// OpenProcess
typedef HANDLE(WINAPI* TypeOpenProcess)(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId);
TypeOpenProcess oldOpenProcess = NULL;
FARPROC pfOldOpenProcess = NULL;
HANDLE WINAPI MyOpenProcess(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId);
BYTE oldCodeOpenPro[CODE_LENGTH];
BYTE newCodeOpenPro[CODE_LENGTH];
BOOL WINAPI HookLoad(HWND hwnd, DWORD dwProcessId); // 关于dll hook 操作
VOID WINAPI HookUnload();
VOID Inject();
VOID HookOn();
VOID HookOff();
//BOOL SetPrivilege(
// HANDLE hToken, // access token handle
// LPCTSTR lpszPrivilege, // name of privilege to enable/disable
// BOOL bEnablePrivilege // to enable or disable privilege
//);
LRESULT CALLBACK MouseProc( // 鼠标钩子子过程调用
int nCode, // hook code
WPARAM wParam,// message identifier
LPARAM lParam // mouse coordinates
);
BOOL WriteMemory(LPVOID lpAddress, BYTE* pcode, size_t length); //将长度为 length 的 pcode 写入地址 lpAddress 的进程内存中
//
//TODO: 如果此 DLL 相对于 MFC DLL 是动态链接的,
// 则从此 DLL 导出的任何调入
// MFC 的函数必须将 AFX_MANAGE_STATE 宏添加到
// 该函数的最前面。
//
// 例如:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // 此处为普通函数体
// }
//
// 此宏先于任何 MFC 调用
// 出现在每个函数中十分重要。 这意味着
// 它必须作为以下项中的第一个语句:
// 出现,甚至先于所有对象变量声明,
// 这是因为它们的构造函数可能生成 MFC
// DLL 调用。
//
// 有关其他详细信息,
// 请参阅 MFC 技术说明 33 和 58。
//
// CMonitorDllApp
BEGIN_MESSAGE_MAP(CMonitorDllApp, CWinApp)
END_MESSAGE_MAP()
// CMonitorDllApp 构造
CMonitorDllApp::CMonitorDllApp()
{
// TODO: 在此处添加构造代码,
// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的 CMonitorDllApp 对象
CMonitorDllApp theApp;
// CMonitorDllApp 初始化
BOOL CMonitorDllApp::InitInstance()
{
CWinApp::InitInstance();
hInstance = AfxGetInstanceHandle(); // 获取本dll句柄
/*
先提高权限,再获取进程句柄。
因为只有权限足够,才能获取到当前进程的句柄。
*/
//HANDLE hToken;
//BOOL bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
//if (bRet == FALSE) {
// AfxMessageBox(_T("权限提升失败"));
//}
//SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
CEnablePriv priv;
priv.EnableDebugPriv();
priv.GetAdminPriv();
DWORD dwPid = ::GetCurrentProcessId();
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
if (hProcess == NULL) {
CString str;
str.Format(_T("OpenProcess fail!!, error code = [%d]"), GetLastError());
AfxMessageBox(str);
return FALSE;
}
//获取任务管理器的pid
//打开文件Mapping
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"PID");
if (NULL == hMapFile)
{
return FALSE;
}
//创建view
PVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 16);
if (NULL == pBuf)
{
return FALSE;
}
g_dwProcessIdStr = (LPWSTR)pBuf;
//MessageBox(0, g_dwProcessIdStr, _T("要保护的进程"), 0);
//取消Mapping, 关闭句柄
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
CString str_pid;
str_pid.Format(_T("%d"), dwPid);
AfxMessageBox(str_pid);
//g_dwProcessIdStr = str_pid;
Inject(); // 开始注入
return TRUE;
}
//
// 实例退出函数。退出时,一定要记得恢复原函数地址!!!
//
int CMonitorDllApp::ExitInstance()
{
HookOff(); //要记得恢复原函数地址
return CWinApp::ExitInstance();
}
/*
鼠标钩子子过程,目的是加载本dll到使用鼠标的程序.
鼠标钩子的作用:当鼠标在某程序窗口中时,就会加载我们这个dll。
即使本DLL随着鼠标钩子注入到目标进程中。
*/
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
return CallNextHookEx(hhook, nCode, wParam, lParam);
}
/*
安装钩子。
主调程序传入窗口句柄和进程id。
*/
BOOL WINAPI HookLoad(HWND hwnd, DWORD dwProcessId) {
BOOL ret = FALSE;
g_hwnd = hwnd;
hhook = ::SetWindowsHookEx(WH_MOUSE, MouseProc, hInstance, 0);
if (hhook == NULL) {
return FALSE;
}
else {
return TRUE;
}
}
/*
卸载钩子。
注:卸载钩子之前,一定要记得恢复原函数地址!!!
*/
VOID WINAPI HookUnload() {
HookOff(); // 恢复原函数地址
if (hhook != NULL) {
UnhookWindowsHookEx(hhook);
}
if (hInstance != NULL) {
FreeLibrary(hInstance);
}
}
/*
注入函数。
主要完成原函数地址的保存,保存到 oldCode_[]中;
新入口地址的计算,保存到newCode_[]中,即 jmp xxxx 指令。
新入口地址 = 新函数地址 - 原函数地址 - 指令长度
最后一定要记得HookOn!!
*/
VOID Inject() {
if (bIsInjected == TRUE) {
return;
}
bIsInjected = TRUE;// 保证只注入一次
// TerminateProcess
HMODULE hmodleKernel32;
hmodleKernel32 = ::LoadLibrary(_T("Kernel32.dll"));
if (NULL == hmodleKernel32) {
AfxMessageBox(_T("加载Kernel32.dll失败"));
return;
}
// 获取原函数地址
oldTerminateProcess = (TypeTerminateProcess)GetProcAddress(hmodleKernel32, "TerminateProcess");
if (NULL == oldTerminateProcess) {
AfxMessageBox(_T("获取TerminateProcess函数失败"));
return;
}
pfOldTerminateProcess = (FARPROC)oldTerminateProcess;
// 保存原函数入口
_asm
{
lea edi, oldCodeTermPro
mov esi, pfOldTerminateProcess
cld
mov ecx, CODE_LENGTH
rep movsb
}
// 替换新函数入口
newCodeTermpro[0] = 0xe9;
_asm
{
lea eax, MyTerminateProcess
mov ebx, pfOldTerminateProcess
sub eax, ebx
sub eax, CODE_LENGTH
mov dword ptr[newCodeTermpro + 1], eax
}
// OpenProcess
oldOpenProcess = (TypeOpenProcess)GetProcAddress(hmodleKernel32, "OpenProcess");
if (NULL == oldOpenProcess) {
AfxMessageBox(_T("获取OpenProcess地址失败"));
return;
}
pfOldOpenProcess = (FARPROC)oldOpenProcess;
_asm
{
lea edi, oldCodeOpenPro
mov esi, pfOldOpenProcess
cld
mov ecx, CODE_LENGTH
rep movsb
}
newCodeOpenPro[0] = 0xe9;
_asm
{
lea eax, MyOpenProcess
mov ebx, pfOldOpenProcess
sub eax, ebx
sub eax, CODE_LENGTH
mov dword ptr[newCodeOpenPro + 1], eax
}
HookOn(); //填充完毕,开始HOOK
}
/*
将长度为 length 的 pcode 写入地址 lpAddress 的进程内存中
*/
BOOL WriteMemory(LPVOID lpAddress, BYTE* pcode, size_t length) {
ASSERT(hProcess != NULL);
DWORD dwtemp, dwOldProtect, dwRet, dwWrited;
dwRet = VirtualProtectEx(hProcess, lpAddress, length, PAGE_READWRITE, &dwOldProtect);
CString logInfo;
if (0 == dwRet) {
logInfo.Format(_T("WriteMemory :: Call VirtualProtectEx fail, eror code = [%d]\n\n"), GetLastError());
AfxMessageBox(logInfo);
return FALSE;
}
dwRet = WriteProcessMemory(hProcess, lpAddress, pcode, length, &dwWrited);
if (0 == dwRet || 0 == dwWrited) {
logInfo.Format(_T("WriteMemory :: Call WriteProcessMomory fail, error code = [%d]\n\n"), GetLastError());
AfxMessageBox(logInfo);
return FALSE;
}
dwRet = VirtualProtectEx(hProcess, lpAddress, length, dwOldProtect, &dwtemp);
if (0 == dwRet) {
logInfo.Format(_T("WriteMemory :: Recover Protect fail, error code = [%d]\n\n"), GetLastError());
AfxMessageBox(logInfo);
return FALSE;
}
return TRUE;
}
/*
开始HOOK。
即,将Inject 初始化好的入口地址进行写入进程内存中。
这里,将新函数入口 newCode_[],写入内存中。
这样一来,在原函数被调用的时候,就会跳转到我们新函数的位置。
注: 这里处理的函数,是当前需要替换的所有函数,所以只在Inject()函数中调用,
即进行初始化的时候用到该函数。
*/
VOID HookOn() {
BOOL ret;
ret = WriteMemory(pfOldTerminateProcess, newCodeTermpro, CODE_LENGTH);
if (FALSE == ret) {
AfxMessageBox(_T("HookOn :: Fail to write pfOldTerminateProcess"));
}
ret = WriteMemory(pfOldOpenProcess, newCodeOpenPro, CODE_LENGTH);
if (FALSE == ret) {
AfxMessageBox(_T("HookOn :: Fail to write pfOldOpenProcess"));
}
}
/*
停止HOOK。
恢复原函数地址。
注:这里处理的是所有替换的函数,所以一般情况下只有在卸载HOOK函数中调用
*/
VOID HookOff() {
ASSERT(hProcess != NULL);
BOOL ret;
ret = WriteMemory(pfOldTerminateProcess, oldCodeTermPro, CODE_LENGTH);
if (FALSE == ret) {
AfxMessageBox(_T("HookOff :: fail to recover pfOldTerminateProcess \n\n"));
}
ret = WriteMemory(pfOldOpenProcess, oldCodeOpenPro, CODE_LENGTH);
if (FALSE == ret) {
AfxMessageBox(_T("HookOff :: fail to recover pfOldOpenProcess"));
}
////取消Mapping, 关闭句柄
//UnmapViewOfFile(pBuf);
//CloseHandle(hMapFile);
}
BOOL WINAPI MyTerminateProcess(_In_ HANDLE hProcess, _In_ UINT uExitCode) {
BOOL ret;
if (hProcess == g_hProcess) {
AfxMessageBox(_T("不能关闭受保护进程哦!!"));
ret = TRUE;
}
else {
WriteMemory(pfOldTerminateProcess, oldCodeTermPro, CODE_LENGTH);
ret = oldTerminateProcess(hProcess, uExitCode);
WriteMemory(pfOldTerminateProcess, newCodeTermpro, CODE_LENGTH);
}
return ret;
}
//
// 自己定义的打开进程函数。
// 若当前打开进程为受保护进程,则记录下该远程调用句柄。
//
HANDLE WINAPI MyOpenProcess(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId) {
HANDLE hProcess = NULL;
WriteMemory(pfOldOpenProcess, oldCodeOpenPro, CODE_LENGTH);
hProcess = oldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
CString dwProcessIdStr;
dwProcessIdStr.Format(_T("%d"), dwProcessId);
if (dwProcessIdStr == g_dwProcessIdStr) {
g_hProcess = hProcess;
}
WriteMemory(pfOldOpenProcess, newCodeOpenPro, CODE_LENGTH);
return hProcess;
}
; MonitorDll.def: 声明 DLL 的模块参数。
LIBRARY
EXPORTS
; 此处可以是显式导出
HookLoad
HookUnload
调用程序
// MyWindowDlg.h: 头文件
//
#pragma once
// CMyWindowDlg 对话框
class CMyWindowDlg : public CDialogEx
{
// 构造
public:
CMyWindowDlg(CWnd* pParent = nullptr); // 标准构造函数
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_MYWINDOW_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
HINSTANCE m_hinstHookDll; // MonitorDll的实例句柄
void HookLoad(); // 加载HOOK
void HookUnload(); // 卸载HOOK
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnClose(); // 关闭程序的时候卸载DLL !!!!!
DECLARE_MESSAGE_MAP()
};
// MyWindowDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "MyWindow.h"
#include "MyWindowDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMyWindowDlg 对话框
CMyWindowDlg::CMyWindowDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MYWINDOW_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMyWindowDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CMyWindowDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()
// CMyWindowDlg 消息处理程序
BOOL CMyWindowDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
//创建进程id的文件映射
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 16, L"PID");
if (NULL == hMapFile || INVALID_HANDLE_VALUE == hMapFile)
{
return TRUE;
}
//创建view
PVOID pBuf = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 16);
if (NULL == pBuf)
{
return TRUE;
}
CString g_processId;
g_processId.Format(_T("%d"), GetCurrentProcessId());
//将共享数据复制到文件映射中
wcscpy_s((PWCHAR)pBuf, 8, g_processId.GetBuffer());
HookLoad(); // 加载HOOK
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CMyWindowDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
HookUnload(); // 退出窗口,要卸载HOOK
CDialogEx::OnClose();
}
void CMyWindowDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CMyWindowDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMyWindowDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMyWindowDlg::HookLoad() {
m_hinstHookDll = ::LoadLibrary(_T("MonitorDll.dll"));
CString loginfo;
if (NULL == m_hinstHookDll) {
loginfo.Format(_T("加载 MonitorDll.dll失败,错误代码 = [%d] "), GetLastError());
AfxMessageBox(loginfo);
return;
}
typedef BOOL(WINAPI* LoadMonitor)(HWND hwnd, DWORD dwProcessId);
LoadMonitor loadMonitor = NULL;
loadMonitor = (LoadMonitor)::GetProcAddress(m_hinstHookDll, "HookLoad");
if (NULL == loadMonitor) {
loginfo.Format(_T("获取函数 HookLoad 失败,错误代码 = [%d]"), GetLastError());
AfxMessageBox(loginfo);
}
if (loadMonitor(m_hWnd, GetCurrentProcessId())) {
loginfo.Format(_T("HOOK加载成功"));
AfxMessageBox(loginfo);
}
else {
loginfo.Format(_T("HOOK加载失败"));
AfxMessageBox(loginfo);
}
}
/*
卸载HOOKDLL
*/
void CMyWindowDlg::HookUnload() {
CString logInfo;
if (m_hinstHookDll == NULL) {
m_hinstHookDll = LoadLibrary(_T("MonitorDll.dll"));
if (NULL == m_hinstHookDll) {
logInfo.Format(_T("加载 MonitorDll.dll失败,错误代码 = [%d]"), GetLastError());
AfxMessageBox(logInfo);
return;
}
}
typedef VOID(WINAPI* UnloadHook)();
UnloadHook unloadHook = NULL;
unloadHook = (UnloadHook)::GetProcAddress(m_hinstHookDll, "HookUnload");
if (NULL == unloadHook) {
logInfo.Format(_T("获取函数 HookUnload 失败,错误代码 = [%d]"), GetLastError());
AfxMessageBox(logInfo);
return;
}
unloadHook();
}
坑点
- 32位的程序测试的时候需要使用32位的任务管理器
- 在任务管理器进程这个子窗口结束进程并不会调用TerminateProcess,所以hook不到的,在详细信息窗口结束进程会调用TerminateProcess

浙公网安备 33010602011771号