使用MinHook进行APIHook

一、Hook概述

1.1 什么是Hook

“Hook” 翻译过来的意思是“挂钩” “钩子”,在程序执行的时候,在适当的位置对程序运行流程进行监控拦截即为Hook技术。

1.2 什么是APIHook

API Hook就是Hook技术的一种具体形式,实际上API Hook是改变API运行结果的一种技术手段,在Windows下进行开发就会用到Windows系统提供的API,对系统API进行拦截,API Hook是想要改变其他进程运行流程的常用技术之一。

1.3 Hook过程理解

举个例子,如果我们要去Hook Windows的API “MessageBoxA”,Hook前后的程序流程可如下图所示:
在这里插入图片描述
可以看到,Hook之后原程序想去调用本来的MessageBoxA,却最后调用到我们的MyMessageBoxA,在调用我们自身的MyMessageBoxA以后,在MyMessageBox函数中,可以选择是否再去调用原MessageBoxA函数。

通过上述方式,我们需要做的就是让程序在Call MessageBoxA地址的时候,最终转到的是我们的函数地址,整个过程就是一个API Hook的过程

二、Hook分类

2.0 分类概述

Hook主要可以分为三类:Address HookInline Hook基于异常处理的Hook,当然有些病毒的异常行为与Hook的理念是一样的,只是方式不同而已。

2.1 Address Hook

Address Hook是指通过修改数据进行Hook的方法,修改的数据一般是函数的地址或者地址偏移量。一半这种数据是存储在各类或者结构中,也可能是某个指定地址或者特殊寄存器中。但所有数据修改都是在某个时刻成为程序执行过程中的eip。因此只要把地址替换成我们的事先准备的替代函数,就可以实现Hook。

2.2 Inline Hook

Inline Hook是指直接修改指令的Hook方式,思想是转移程序的执行流程,使其进行函数调用逻辑的变化,一般是使用jmp,call,retn之类的转移指令去实现。

2.3 基于异常处理的Hook

当程序执行过程中发生异常,系统内核的异常处理程序nt!KiDispatchException就会开始工作。没有内核调试器存在的情况下,系统会把异常处理过程交给用户态的异常处理过程。
也就是说我们在程序中安装异常处理过程,在Hook位置写入一条会引起异常的指令,然后让程序在执行到这异常位置时,跳转到我们事先安装的异常处理过中程,进而实现该位置的Hook。

三、MinHook库介绍

经过以上对Hook方式的简单介绍我们可以理解Hook究竟是什么,以及实现Hook的几种方式,详细的原理介绍在《加密与解密》第13章都有解释。不再过多的原理解释,实际上Git上有很多已经封装好的Hook实现代码,学会如何使用库比自己基于原理实现整个Hook过程简单很多。。

3.1 MinHook代码

git地址:MinHook
官方文档说明:MinHook - The Minimalistic x86/x64 API Hooking Library
MinHook就是通过Inline Hook实现的,通过生成库文件,在我们的项目中包含头文件以及对应库文件就可以实现Hook。

3.2 文件目录介绍

在这里插入图片描述
build目录:包含各种版本解决方案,可以选择对应的版本进行生成
include目录:需要包含到我们项目的头文件目录
在这里插入图片描述
每一个对应版本的解决方案目录介绍:
lib目录:生成对应的静态库目录,包含x64与x86
bin目录:生成对应的动态库目录,包含x64与x86

3.3 解决方案描述

在这里插入图片描述
可以直接生成第一个项目libMinHook,生成库文件,把需要的头文件MinHook.h包含到自己的项目中,需要注意的是根据需要Hook的进程是64位还是32位而选择对应生成库文件版本,否则Hook会不生效
生成第二个项目是直接可以生成静态库对应的dll,可以用于动态Loadlibrary,但是还是建议直接生成静态库直接生成到我们自己的项目中,因为我们自己的项目是需要生成一个注入目标进程的dll。

3.4 头文件定义中文说明:

/*
 *  MinHook - The Minimalistic API Hooking Library for x64/x86
 *  Copyright (C) 2009-2017 Tsuda Kageyu.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 *  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
    #error MinHook supports only x86 and x64 systems.
#endif

#include <windows.h>

// MinHook 错误码.
typedef enum MH_STATUS
{
    // 未知错误,不应退出
    MH_UNKNOWN = -1,

    // 成功
    MH_OK = 0,

    // 已经初始化过
    MH_ERROR_ALREADY_INITIALIZED,

    // 没有初始化或者已经退出初始化
    MH_ERROR_NOT_INITIALIZED,

    // 已创建指定目标函数的挂钩
    MH_ERROR_ALREADY_CREATED,

    // 尚未创建指定目标函数的挂钩
    MH_ERROR_NOT_CREATED,

    // 指定目标函数的挂钩已启用
    MH_ERROR_ENABLED,

    // 尚未启用或已禁用指定目标函数的挂钩
    MH_ERROR_DISABLED,

    // 指定的指针无效。它指向未分配和/或不可执行区域的地址
    MH_ERROR_NOT_EXECUTABLE,

    // 无法挂接指定的目标函数
    MH_ERROR_UNSUPPORTED_FUNCTION,

    // 内存分配失败
    MH_ERROR_MEMORY_ALLOC,

    // 更改内存保护失败
    MH_ERROR_MEMORY_PROTECT,

    // 未加载指定的模块
    MH_ERROR_MODULE_NOT_FOUND,

    // 找不到指定的函数
    MH_ERROR_FUNCTION_NOT_FOUND
}
MH_STATUS;

// 可以作为参数传递
#define MH_ALL_HOOKS NULL

#ifdef __cplusplus
extern "C" {
#endif

    // 初始化MinHook库,在程序调用开始前必须初始化
    MH_STATUS WINAPI MH_Initialize(VOID);

    // 卸载MinHook库,在程序调用结束后必须卸载
    MH_STATUS WINAPI MH_Uninitialize(VOID);

    // 创建一个目标函数的钩子
    // Parameters:
    //   pTarget     [in]  指向原函数(目标函数)的指针
    //   pDetour     [in]  指向用来替代目标函数的函数指针
    //   ppOriginal  [out] 指向调用原函数入口的指针,此参数可为NULL          
    MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);

    // 创建一个目标API函数的钩子
    // Parameters:
    //   pszModule   [in]  目标函数所在的模块名
    //   pszProcName [in]  目标函数名
    //   pDetour     [in]  指向用来替代目标函数的函数指针
    //   ppOriginal  [out] 指向调用原函数入口的指针,此参数可为NULL
    MH_STATUS WINAPI MH_CreateHookApi(
        LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);

    // 创建一个目标API函数的钩子
    // Parameters:
    //   pszModule   [in]  目标函数所在的模块名
    //   pszProcName [in]  目标函数名
    //   pDetour     [in]  指向用来替代目标函数的函数指针
    //   ppOriginal  [out] 指向调用原函数入口的指针,此参数可为NULL
    //   ppTarget    [out] 指向目标函数的指针,它将与其他函数一起使用,此参数可为NULL
    MH_STATUS WINAPI MH_CreateHookApiEx(
        LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);

    // 移除已创建的钩子
    // Parameters:
    //   pTarget [in] 目标函数的指针
    MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);

    // 启用已创建的钩子
    // Parameters:
    //   pTarget [in] 指向目标函数的指针
    //                如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将一次性启用。
    MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);

    // 禁用已创建的钩子
    // Parameters:
    //   pTarget [in] 指向目标函数的指针
    //                如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将一次性禁用。
    MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);

    // 添加启用创建的钩子放到队列中
    // Parameters:
    //   pTarget [in] 指向目标函数的指针
    //                如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将排队等待启用。
    MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);

    // 排队禁用已创建的钩子
    // Parameters:
    //   pTarget [in] 指向目标函数的指针
    //                如果这个参数是 MH_ALL_HOOKS,那么所有创建的钩子都将排队等待禁用。
    MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);

    // 一次性应用所有排队的更改
    MH_STATUS WINAPI MH_ApplyQueued(VOID);

    // 通过状态返回钩子名
    const char * WINAPI MH_StatusToString(MH_STATUS status);

#ifdef __cplusplus
}
#endif

四、Hook实现过程

4.1 步骤介绍

我们要实现Hook,需要我们自己生成一个可以注入目标进程的独立的dll,有了可以注入目标进程的dll以后,我们就需要一个注射器来把我们的dll注入到目标进程中。

把dll注入目标进程的目的是为了获取目标进程中的目标函数的地址,进程空间都是相互独立的,想实现Hook就需要把dll注入到目标进程中,然后获取到目标进程中windows API的函数地址,或者是其他目标进程使用的其他开源库中的函数地址。

  • 注入进程的dll:实现注入后的Hook操作
  • 注射器:为了把dll注入到目标进程中
  • 目标进程:含有目标Hook函数的被注入进程

4.2注入进程的dll代码

新建一个dll项目,把静态库libMinHook编译进去,添加头文件,然后进行Hook代码的编写,这里我们简单Hook一个MessageboxA,大体代码如下所示,还可以去看MinHook的头文件调用其他接口。

#include "../include/MinHook.h"
#if defined _M_X64
#pragma comment(lib, "libMinHook.x64.lib")
#elif defined _M_IX86
#pragma comment(lib, "libMinHook.x86.lib")
#endif
typedef int (WINAPI* MESSAGEBOXA)(HWND, LPCSTR, LPCSTR, UINT);

MESSAGEBOXA fpMessageBoxA = NULL;//指向原MessageBoxA的指针
//用来替代原函数的MessageBox函数
int WINAPI DetourMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	//这里只做简单的修改参数,其实可以做很多事情,甚至不去调用原函数
	return fpMessageBoxA(hWnd, "Hooked!", lpCaption, uType);
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		do
		{
			//初始化
			if (MH_Initialize() != MH_OK)
			{
				OutputDebugStringA("Initialize err");
				break;
			}
			//创建Hook
			if (MH_CreateHook(MessageBoxA, &DetourMessageBoxA,
				reinterpret_cast<LPVOID*>(&fpMessageBoxA)) != MH_OK)
			{
				OutputDebugStringA("MH_CreateHook err");
				break;
			}
			//使目标函数Hook生效
			if (MH_EnableHook(MessageBoxA) != MH_OK)
			{
				OutputDebugStringA("MH_EnableHook err");
				break;
			}
			//使目标函数Hook失效
			//if (MH_DisableHook(MessageBoxA) != MH_OK)
			//{
			//	break;
			//}
			// 卸载MinHook
    		//if (MH_Uninitialize() != MH_OK)
  			//{
    		//  break;
    		//}

		} while (false);

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

注入工具就简单生成一个,用之前的远程线程注入方式就可以
简单的把注入的代码贴一下,我用MFC去写的一个工具Button控件响应代码

void CInjectProcessDlg::OnBnClickedHook1()
{
	DWORD hpid = 0;								// 目标进程pid
	HANDLE htargetprocess = NULL;				// 目标进程句柄
	LPVOID lpAddr;								// 目标线程申请内存空间的指针
	BOOL bResult = FALSE;						// 注入结果
	do
	{
		hpid = GetProcessIDByName(L"TargetProcess.exe");
		if (hpid == 0)
		{
			break;
		}

		htargetprocess = OpenTargetProcess(hpid);
		if (htargetprocess == NULL)
		{
			break;
		}

		if (TagetAlloc(htargetprocess, lpAddr) == FALSE)
		{
			break;
		}

		if (WriteDLLToTarget(htargetprocess, lpAddr, L"Hookdll.dll") == FALSE)
		{
			break;
		}

		PTHREAD_START_ROUTINE pnfStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryW");
		if (pnfStartAddr == NULL)
		{
			break;
		}

		if (CreateThreadInTarget(htargetprocess, pnfStartAddr, lpAddr) == FALSE)
		{
			break;
		}
		bResult = TRUE;
	} while (false);
}

4.3效果

写个目标进程

#include <iostream>
#include <Windows.h>

int main()
{
    MessageBoxA(NULL, "Orgin MessageBox", "tip", NULL);	//注入前调用
    Sleep(10000);	//简单Sleep一下
    MessageBoxA(NULL, "Orgin MessageBox", "tip", NULL);	//注入后调用

}

注入前
在这里插入图片描述
注入后:
在这里插入图片描述
通过PrecessExplorer可以清楚看到进程以及模块关系
进程模块:

4.4 拓展

以上例子只是一个简单的Hook了MessageBoxA,但实际上,我们可以通过一些工具看到一些进程中调用的其他API,或者使用的一些开源库的一些API接口,我们只要获取到想Hook的函数地址,就可以不限于仅仅Hook Windows提供的一些API。
Hook能做的事情很多,比如开发补丁,信息截获,安全防护等。

五、结尾

这个文章也是很简单入门的介绍Hook,实际上Hook技术还是有很强的拓展性,能做的事情很多,也可以做坏事,技术没有好坏,只是看使用者用来做什么了。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值