码迷,mamicode.com
首页 > 编程语言 > 详细

C++ EAT / Hook

时间:2015-07-21 14:49:47      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:

EAT与IAT比较类似,我相信会IAT的肯定很多,起初我想写在C#上面 不过与

C# 遍历DLL导出函数 的方法很相似,只是两者在内存中的映射方式不同而已

Heh,首先我们需要把DLL映射到地址内存空间去 否则没有办法去置换函数,

当然EAT有一些缺点,它必须在软件调用GetProcAddress函数之前替换DLL中

的函数,所以则出现了对GetProcAddress函数的一个Hook,否则只可改变

GetProcAddress返回的内容、是不是感到很惆怅

 

EAT全称为“Export address table”其核心则是利用PE与DLL的一些相关特性

实现的一个技术 首先我们通过LoadLibraryA函数把一个有效的DLL映射到内

存中,PE文件中第一个字节是MS-DOS信息头即IMAGE_DOS_HEADER

 

IMAGE_NT_HEADER = (pDosHearder + pDosHearder->e_lfanew);

// IMAGE_NT_HEADER::OptionalHeader

pOptionalHeader = (pDosHearder + pDosHearder->e_lfanew + 24);

上面是x86的一个OptionalHeader的对称,这里是不想再多做一次转换直接

跳过去会很快捷、所以加上24但恰好指向OptionalHeader、而我们只需要

它其它的对我们而言根本没有任何用、所以跳过就可以

#include "stdafx.h"

#include <Windows.h>

BOOL EATHook(LPCSTR strLibraryName, LPCSTR strMethodName, PVOID newMethodAddress)
{
	HMODULE hRemoteModule = LoadLibraryA(strLibraryName);

	PIMAGE_DOS_HEADER pDosHearder = (PIMAGE_DOS_HEADER)hRemoteModule;
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)hRemoteModule + pDosHearder->e_lfanew + 24);
	PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pDosHearder +
		pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

	LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames);
	LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions);
	PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals);

	for (int i = 0; i < pExportDirectory->NumberOfNames; i++)
	{
		LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);
		if (_stricmp(strInsideName, strMethodName) == 0)
		{
			DWORD pflOldProtect;
			VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect);
			pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule;
			break;
		}
		pAddressOfNameOrdinals++;
	}
	return FALSE;
}

既然是EAT,我们看第一个字母全名为Export那么我们肯定要去找包含导出的DLL函数的

PE中的数据目录,恰好有那么一个IMAGE_DIRECTORY_ENTRY_EXPORT但是拿出来

的地址是RVA / 相对虚拟地址所以你必须要ToVA / 到虚拟地址、但是DLL已经被映射到虚

拟内存中 所以我们不需要使用RvaToVa、我们只需要

 

 (DWORD)pDosHearder +
  pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress

(DWORD)hRemoteModule + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress

则可以得到,PIMAGE_EXPORT_DIRECTORY pExportDirectory函数导出目录,但是我

们得到了它却不可以耍花架子,我们还有继续下面的代码否则没法混、

	LPDWORD pAddressOfNames = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfNames);
	LPDWORD pAddressOfFunctions = (LPDWORD)((DWORD)hRemoteModule + pExportDirectory->AddressOfFunctions);
	PUSHORT pAddressOfNameOrdinals = (PUSHORT)((DWORD)hRemoteModule + pExportDirectory->AddressOfNameOrdinals);

pAddressOfNames // 导出函数名

pAddressOfFunctions // 导出函数地址

pAddressOfNameOrdinals // 导出函数序列

 

上面定义三种不同的指针,但是都预先把地址进行了一个处理,这是为了方便后面的使用

而不必一直加上(DOWRD)hRemoteModule那样子会很累的、

pExportDirectory->NumberOfNames // 导出函数计数(Length)

 

LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);

上面是获取DLL导出的函数名,pAddressOfNames[i] 返回的地址为虚拟相对地址(Rva)

在这里只需要如下去做、则可以得到DLL导出的函数名是什么,当然这个是c_str风格字

串,你懂的‘\0‘结尾,_strlen对其有效,不过在本代码中也用不到_strlen、

下面是首先比较两个字符串是否相等,主要看看需要具体去Hook那个函数

	for (int i = 0; i < pExportDirectory->NumberOfNames; i++)
	{
		LPCSTR strInsideName = (LPCSTR)((DWORD)hRemoteModule + pAddressOfNames[i]);
		if (_stricmp(strInsideName, strMethodName) == 0)
		{
			DWORD pflOldProtect;
			VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals], 0x1000, PAGE_READWRITE, &pflOldProtect);
			pAddressOfFunctions[*pAddressOfNameOrdinals] = (DWORD)newMethodAddress - (DWORD)hRemoteModule;
			break;
		}
		pAddressOfNameOrdinals++;
	}

 

&pAddressOfFunctions[*pAddressOfNameOrdinals] 从上面的代码你应该看出了端倪

对的,DLL函数导出的地址你不可以直接通过&pAddressOfFunctions[i]解决问题,因

为在PE中,它是通过Ordinals定位函数位置,但是它与i=0; i++是相序的、有点绕哈

 

VirtualProtect(&pAddressOfFunctions[*pAddressOfNameOrdinals],

                         0x1000, PAGE_READWRITE, &pflOldProtect);

上面是修改函数的内存保护,0x1000没有太大意义、填4 / 8也是可以的

 

pAddressOfFunctions[*pAddressOfNameOrdinals] =

                      (DWORD)newMethodAddress - (DWORD)hRemoteModule;

好吧,这里填写相对虚拟地址、指的是新的函数地址与这个模块的地址做一个加减

不管返回的是负数还是正数都可以,没有那么多讲究、后面这句话也是必要的

pAddressOfNameOrdinals++; 你可以任性的把使用 *pAddressOfNameOrdinals

修改为pAddressOfNameOrdinals[i] 但是绝对不可以不写,这是原则性上的问题、

 

HANDLE WINAPI CB_GetModuleHandleW(LPCTSTR strLibraryName)
{
	return NULL;
}

我们写一个Hook的处理函数,GetModuleHandleW函数的一个处理函数,什么都不

用写,只需要打个断点看看有没有执行就可以了,因为没有太大的意义重点在这里

int _tmain(int argc, _TCHAR* argv[])
{
	EATHook("kernel32.dll", "GetModuleHandleW", CB_GetModuleHandleW);
	LPDWORD pfnGetModuleHandleW = (LPDWORD)GetProcAddress(GetModuleHandleA("kernel32"), "GetModuleHandleW");
	_asm
	{
		push 0
		call pfnGetModuleHandleW
	}
	return 0;
}

上面是利用EAT先把GetModuleHandleW函数进行挂钩,然后通过GetProcAddress

函数得到pfnGetModuleHandleW,为了方便不想使用函数指针,直接嵌入两行汇编

代码进去直接解决问题,然后F5调试,如果CB_GetModuleHandleW函数被执行 那

么恭喜你成功了,如果没有说明你出错了、哈哈

 

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

C++ EAT / Hook

标签:

原文地址:http://blog.csdn.net/u012395622/article/details/46984679

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!