逆向学习之Hook技术

一、什么是HOOK

Hook是用来获取或者更改程序执行时的某些数据,或者更改程序执行流程的一种技术

Hook还有一种说法叫做“挂钩子”


二、HOOK的两种形式

修改函数代码

  • Inline Hook

修改函数地址

  • IAT HOOK
  • SSDT HOOK
  • IDT HOOK
  • EAT HOOK
  • IRP HOOK

三、IAT HOOK

简介

IAT表:全称Import Address Table,中文名为导入地址表

每一个进程都有这样一个IAT表,而这个IAT表存储着当前这个模块所用到的所有api函数地址

将程序拖入OD里,随便找一个调用系统api的函数,然后数据跟随内存地址,会发现这些api函数在IAT表里的位置都是挨在一起的

只要将这些函数的地址修改成指向我们自行设置的函数地址,就能实现所谓的IAT Hook

请添加图片描述

DLL代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。"
#include <windows.h>


//创建与Messagebox相同的函数指针,注意要设置相同的函数参数
typedef int (WINAPI *PfnMsgA)(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType);

PfnMsgA g_OldPfnMsgA = nullptr;  //定义一个指向原先messagebox函数的空指针(nullptr)


//解析PE文件结构
HMODULE hModImageBase = GetModuleHandle(NULL); //获取当前的ImagBase(基址)
PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;  //获取NT头
PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;  //获取标准PE头
PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;  //获取扩展PE头
DWORD dwExportLocal = pOptHead->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; //找到导入表的偏移(RVA)
PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)GetModuleHandle(NULL) + dwExportLocal);  //获取导入表


//定义自己设置的Messagebox函数
int WINAPI MyMessageBox(_In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType)
{

	char szHookText[] = "Hook成功";  
	if (g_OldPfnMsgA != nullptr)
	{
		return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//调用以前的
	}
	return 0;
}

//设置IATHook
void SetIatHook()
{
	MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
	PVOID pHookAddress = nullptr;  //定义一个指向hook地址的空指针
	pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //将要hook的地址指向Messagebox函数
	
	if (nullptr == pHookAddress)  
	{
		OutputDebugString(TEXT("获取函数地址失败"));   
		MessageBoxA(NULL, "获取函数地址失败HOOK", NULL, NULL);

		return;
	}

	g_OldPfnMsgA = (PfnMsgA)pHookAddress; //指向旧函数的指针.  
	
	

	//寻找IAT表的位置.
	
	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;   
	DWORD *pFirstThunk; //导入表子表,也就是IAT存储函数地址的表.
	
	//遍历导入表
	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
	{
		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到IAT表的偏移地址
		pFirstThunk = (DWORD *)dwTemp; //指向IAT表的指针
		while (*pFirstThunk != NULL)
		{	
			//遍历IAT表里的子表,若指针指向的是就函数的地址,则将其修改成我们的函数地址
			if (*pFirstThunk == (DWORD)g_OldPfnMsgA)
			{	
				DWORD oldProtected;
				VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected); //设置该内存区域属性为可写可读可执行
				dwTemp = (DWORD)MyMessageBox;
				memcpy(pFirstThunk, (DWORD *)&dwTemp, 4); //将旧函数地址修改成自己的函数地址
				VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
			}
			pFirstThunk++; //遍历IAT表
		}
		pCurrent++; //遍历导入表
	}

}


//恢复导入表
void UnIatHook()
{
	MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
	PVOID pHookAddress = nullptr;
	pHookAddress = MyMessageBox;
	if (nullptr == pHookAddress)
	{
		OutputDebugString(TEXT("获取函数地址失败"));
		MessageBoxA(NULL, "恢复函数地址失败HOOK", NULL, NULL);
		return;
	}

	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
	DWORD* pFirstThunk; //指向
	
	//遍历导入表
	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
	{
		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
		pFirstThunk = (DWORD*)dwTemp; 
		while (*pFirstThunk != NULL)
		{
			//遍历子表
			if (*pFirstThunk == (DWORD)MyMessageBox) //如果是我们的函数地址.则
			{
				//找到要修改的导入表了,修改内存保护属性.写入我们新的函数地址.
				DWORD oldProtected;
				VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
				dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
				memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中.
				VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
			}
			pFirstThunk++; //继续遍历.
		}
		pCurrent++; //每次是加一个导入表结构.
	}

}


BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		SetIatHook();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:

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

四、Inline Hook

简介

Inline Hook又称为超级Hook, 比起IAT Hook, 它的实用性更强以及更加容易过检测

Inline Hook为什么被称为是修改函数代码的Hook呢? 简单来说Hook就是jmp, 之所以InlineHook能够实现任意地址的hook, 是因为可以通过jmp指令实现来回的跳转

例如, 你要在某个地址进行Hook, 那就将此地址的汇编指令修改成jmp, 跳转到你要执行的函数, 然后再jmp回到hook地址的下一行地址

下面有一个简单的实例给给大伙演示

一个简单的实例

首先用C语言创建一个简单的加法程序

#include<Windows.h>
#include<cstdio>

int ADD(int a,int b) {
	return a + b;
}


int main() {
	int a, b;
	scanf("%d,%d",&a, &b);
	printf("结果是:%d", ADD(a, b));
}

将程序拖入OD中,并且找到Add函数的call的地址

请添加图片描述

再此call处下个断点,然后运行程序输入数据,这里我们输入1,2,然后F7进入此call
请添加图片描述

进入call后查看右下角的堆栈窗口可以查看到我们输入的函数实参

请添加图片描述

如果将此call堆栈里的参数值给修改了,那么就可以实现修改函数返回值

首先在函数头部地址添加一个JMP汇编指令,跳转的地址是任意的,只要这个地址周围没有指令,这里以跳转到401045为例

请添加图片描述

红色部分的汇编指令就是我们自行创建的,以此来实现修改堆栈中的函数参数值

这里也要注意平衡堆栈,原先的call是有push ebpmov ebp,esp指令的,由于添加了jmp指令导致它们被覆盖,所以这里要添上

最后的jmp指令再跳转回到401003

请添加图片描述

最后程序运行结果为9,因为我们把参数从原先的1和2, 修改成了4和5

请添加图片描述


任意地址Hook

代码下载地址

链接:https://pan.baidu.com/s/1M2rPk2aNs74vIOyRl44n8A
提取码:c5ff

C++代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <Windows.h>
#include <stdio.h>
#define BEA_ENGINE_STATIC 
#define BEA_USE_STDCALL
#include "BeaEngine.h"   //反汇编引擎
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"BeaEngine.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")


int gJmpRetAddress;  //跳转返回到的地址

int gHookAddress;  //要被HOOK的地址

int gLen;  //HOOK代码写入后被覆盖掉的字节数

byte gOldArray[256] = { 0 };    //用于存储HOOK代码写入后被覆盖掉的字节数组


void __declspec(naked) MyFunction()
{


	//打开浏览器
	ShellExecute(NULL, "open", "https://www.baidu.com", NULL, NULL, SW_SHOWNORMAL);
	
	//恢复原始字节
	for (size_t i = 0; i < gLen; i++)
	{
		*((byte*)(gHookAddress + i)) = gOldArray[i];
	}
	_asm jmp gHookAddress
}

int CalcJmpAddress(int targetAddress, int hookAddress)
{
	return targetAddress - hookAddress - 5;
}



//任意地址HOOK函数
void SupperHook(int hookAddress)
{	
	DISASM disam;  //定义一个反汇编引擎
	disam.EIP = hookAddress;  //将当前EIP设置成hook的地址
	gLen = Disasm(&disam);  //当前EIP的硬编码字节数
	while (gLen < 5)  
	{
		disam.EIP += gLen;
		gLen += Disasm(&disam);
	}

	memcpy_s(gOldArray, gLen, (void*)hookAddress, gLen);  //
	
	//计算HOOK执行完毕后jmp返回的地址
	gJmpRetAddress = hookAddress + gLen;
	//得到HOOK需要跳转到的函数地址
	int targetAddress = (int)&MyFunction;
	//计算jmp xxxxxxxx这个指令中的xxxxxxxx的值
	//计算公式:目标地址-源地址-5
	int value = CalcJmpAddress(targetAddress, hookAddress);

	//修改内存页属性为可读可写可执行
	DWORD oldProtect;
	VirtualProtect((LPVOID)hookAddress, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);
	//将要HOOK的地址第一个字节改成Jmp
	*((byte*)hookAddress) = 0xE9;
	//将跳转目标地址写入
	*((int*)(hookAddress + 1)) = value;
}



DWORD WINAPI Thread(_In_ LPVOID lpParameter) {
	int hookAddress;
	AllocConsole();  //创建控制台
	freopen("CONIN$", "r+t", stdin); //将stdin设备输入的数据重定向至控制台输入
	freopen("CONOUT$", "w+t", stdout); //将stdin设备输出的数据重定向至控制台输出
	while (true)
	{
		printf("请输入要HOOK的地址:");
		scanf("%08X", &hookAddress);
		//执行HOOK
		SupperHook(hookAddress);
		printf("HOOK执行完毕\n\n");
	}
	return true;
	SupperHook(hookAddress);
	return true;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
	case DLL_PROCESS_ATTACH: {
		HANDLE hTread = CreateThread(NULL, 0, Thread, (LPVOID)NULL, 0, NULL);
		
		
	}
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}



易语言代码

请添加图片描述

操作演示

这里就用C++代码的hook来作演示

首先新建一个测试dll注入的易语言程序, , 并将此程序放到InlineHook.dll的所处目录

请添加图片描述

请添加图片描述

将testdll.exe拖入OD中, 跳转到按钮时间特征码处, 这里就用按钮时间特征码的起始地址来作Hook的地址

请添加图片描述

运行testdll.exe程序, 输入要hook的地址后, 再返回OD查看hook地址的汇编指令变化,由原先的push ebp 变成了 jmp指令

请添加图片描述

请添加图片描述

返回testdll.exe程序界面, 点击按钮后触发Hook并执行我们自己的函数, 这里我的函数是打开浏览器

请添加图片描述

  • 7
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
React Hook是React 16.8版本引入的一种新的特性,它可以让你在无需编写类组件的情况下使用状态和其他React特性。TypeScript是一种静态类型检查的JavaScript超集,可以帮助我们在开发过程中发现并修复潜在的错误。 要深入学习React Hook和TypeScript技术栈,你可以按照以下步骤进行: 1. 学习React基础知识:在学习React Hook之前,确保你对React的基础知识有一定的了解。理解React组件、生命周期、状态管理等概念是很重要的。 2. 学习TypeScript基础知识:如果你还不熟悉TypeScript,可以先学习一些基础知识,比如类型注解、接口、泛型等。掌握这些概念可以帮助你更好地使用TypeScript进行开发。 3. 学习React Hook:阅读React官方文档中关于React Hook的内容,并尝试编写一些简单的Hook。掌握useState、useEffect、useContext等常用的Hook函数,并理解它们的使用方法和原理。 4. 使用TypeScript编写React Hook:在掌握了React Hook的基本知识后,你可以开始使用TypeScript编写React Hook。使用TypeScript可以为你的代码提供类型检查和智能提示,减少潜在的错误。 5. 实践项目:选择一个小型的项目或者练习,使用React Hook和TypeScript进行开发。通过实践项目可以帮助你更好地理解和掌握这两个技术栈。 6. 深入学习进阶内容:一旦你对React Hook和TypeScript有了基本的了解,你可以进一步学习一些进阶内容,比如自定义Hook、使用第三方库、使用Context API等。 记住,深入学习任何技术栈都需要时间和实践。通过不断地阅读文档、编写代码和解决问题,你会逐渐掌握React Hook和TypeScript技术栈。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值