虚函数HOOK

看来一些资料,觉得这篇写的比较好,做此笔记。

栗子如下:

#pragma once
#include <iostream>
using namespace std;

class A
{
public:
	int iVal1;
	int iVal2;

	virtual void print1()
	{
		cout<<"iVal1(A) = "<<iVal1<<endl;
	}

	virtual void print2()
	{
		cout<<"iVal2(A) = "<<iVal2<<endl;
	}

	virtual void print_all()
	{
		cout<<"iVal1(A) = "<<iVal1<<"\t"<<"iVal2(A) = "<<iVal2<<endl;
	}

	virtual void print_extern(int ext)
	{
		cout<<"ext(A) = "<<(ext+0)<<endl;
	}
};


class B
{
public:
	int iVal1;
	int iVal2;

	virtual void print1()
	{
		cout<<"iVal1(B) = "<<iVal1<<endl;
	}

	virtual void print2()
	{
		cout<<"iVal2(B) = "<<iVal2<<endl;
	}

	virtual void print_all()
	{
		cout<<"iVal1(B) = "<<iVal1<<"\t"<<"iVal2(B) = "<<iVal2<<endl;
	}

	virtual void print_extern(int ext)
	{
		cout<<"ext(B) = "<<(ext+100)<<endl;
	}

};
大家应该比较熟悉这两个类的内存结构,如图:

Mission 1:对象a去调用类B的函数
方法:寄存器ecx中放入对象a的地址,然后找到类B相应的函数指针,调用之。

// 1.对象a去调用类B的函数
void CallAMethodWithBObject(A* pA, B* pB)
{
	_asm
	{
		push eax                              // 暂存eax寄存器到堆栈中
		push ecx                              // 暂存ecx寄存器到堆栈中

		mov eax,dword ptr [pB]				  // pB对象地址所指的内容放入eax中(指向pb虚表)
		mov ecx,dword ptr [pA]                // pA虚表的地址放入ecx中

		mov eax,dword ptr [eax]               // pB虚表的地址放入eax中(pB对象的第一个元素)
		add eax,12                            // 虚表地址+12,即b对象的第4个函数B::print_extern()的地址
		push  4
		call dword ptr [eax]				  // 调用类B的函数B::print_all(),但是实际上传入a对象的变量(this指针放在ecx中)

		pop ecx                               // 从堆栈中弹回ecx
		pop eax                               // 从堆栈中弹回eax
	}
}
Mission 2:交换对象a和对象b的所有虚函数
方法:很简单,就是交换两个对象的虚表指针,如图。


代码如下:

// 2.交互两个对象的虚表地址
void exchangVB(A* pA, B* pB)
{
	_asm
	{
		push eax
		push ecx
		push esi
		push edi

		mov esi, dword ptr[pB]
		mov edi, dword ptr[pA]

		mov eax, dword ptr[esi]
		mov ecx, dword ptr[edi]

		mov dword ptr[esi], ecx
		mov dword ptr[edi], eax

		pop edi
		pop esi
		pop ecx
		pop eax
	}
}

Mission 3:交换类A和类B的第二个和第四个函数
方法:需要修改虚表,交换类A和类B的第二、四个函数,如图:


代码如下:

// 3.交换类A和类B的第二个和第四个函数
void ExchangeMethod(A* pA, B* pB)
{
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
	long** pplVrtableA = (long**)(pA);
	long** pplVrtableB = (long**)(pB);

	MEMORY_BASIC_INFORMATION mbiA = {0};
	if (VirtualQueryEx(hProcess, (LPVOID)(*pplVrtableA), &mbiA, sizeof(mbiA)) != sizeof(mbiA))
		return;

	MEMORY_BASIC_INFORMATION mbiB = {0};
	if (VirtualQueryEx(hProcess, (LPVOID)(*pplVrtableB), &mbiB, sizeof(mbiB)) != sizeof(mbiB))
		return;

	DWORD dwOldProtectA = 0;
	DWORD dwOldProtectB = 0;
	if(!::VirtualProtectEx(hProcess, mbiA.BaseAddress, mbiA.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectA)) 
		return;

	if(!::VirtualProtectEx(hProcess, mbiB.BaseAddress, mbiB.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectB)) 
		return;

	_asm
	{
		push eax                        //压入需要使用的寄存器到堆栈中
		push ecx
		push esi
		push edi

		mov esi,dword ptr [pA]          //a对象指针
		mov edi,dword ptr [pB]			//b对象指针
		mov esi,dword ptr [esi]         //a对象虚表地址
		mov edi,dword ptr [edi]         //b对象虚表地址

		add esi,4
		add edi,4

		mov eax,dword ptr [esi]         //交换第二个函数地址:print2
		mov ecx,dword ptr [edi]
		mov dword ptr [edi],eax
		mov dword ptr [esi],ecx

		add esi,8
		add edi,8

		mov eax,dword ptr [esi]        //交换第四个函数地址:print_extern
		mov ecx,dword ptr [edi]
		mov dword ptr [edi],eax
		mov dword ptr [esi],ecx

		pop edi
		pop esi
		pop ecx
		pop eax
	}

	DWORD dwTemp = 0;
	::VirtualProtectEx(hProcess, mbiA.BaseAddress, mbiA.RegionSize, dwOldProtectA, &dwTemp);
	::VirtualProtectEx(hProcess, mbiB.BaseAddress, mbiB.RegionSize, dwOldProtectB, &dwTemp);

	CloseHandle(hProcess);
}

Mission 4:hook类A的第三个函数
方法:比较麻烦,但是也是不难弄的。将类A的第三个虚函数指针替换成我们自己定义的地址,如同:


代码如下:

void* g_pSaveAddr = 0;

void load_hook(A* pA, DWORD hook_proc)
{
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
	long** pplVrtableA = (long**)(pA);

	MEMORY_BASIC_INFORMATION mbiA = {0};
	if (VirtualQueryEx(hProcess, (LPVOID)(*pplVrtableA), &mbiA, sizeof(mbiA)) != sizeof(mbiA))
		return;

	DWORD dwOldProtectA = 0;
	if(!::VirtualProtectEx(hProcess, mbiA.BaseAddress, mbiA.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectA)) 
		return;

	_asm
	{
		push eax
		push ecx

		mov eax,dword ptr [pA]              //获取对象指针
		mov eax,dword ptr [eax]             //获取虚表地址

		add eax,8                           //获取虚表中类A第三个函数指针的地址
		mov ecx,dword ptr [eax]             //取出类A第三个函数指针
		mov dword ptr [g_pSaveAddr],ecx		//保存到g_pAddr变量中
		mov ecx, dword ptr hook_proc		//替换为hook_proc指针
		mov dword ptr [eax],ecx

		pop ecx
		pop eax
	}

	cout<<"A::print_all() hooked!"<<endl;

	DWORD dwTemp = 0;
	::VirtualProtectEx(hProcess, mbiA.BaseAddress, 4, dwOldProtectA, &dwTemp);
	CloseHandle(hProcess);
}

void unload_hook(A* pA)
{
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
	long** pplVrtableA = (long**)(pA);

	MEMORY_BASIC_INFORMATION mbiA = {0};
	if (VirtualQueryEx(hProcess, (LPVOID)(*pplVrtableA), &mbiA, sizeof(mbiA)) != sizeof(mbiA))
		return;

	DWORD dwOldProtectA = 0;
	if(!::VirtualProtectEx(hProcess, mbiA.BaseAddress, mbiA.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtectA)) 
		return;

	_asm
	{
		push eax
		push ecx

		mov eax,dword ptr [pA]              //获取对象指针
		mov eax,dword ptr [eax]             //获取虚表地址
		add eax,8                          //获取虚表中类A第三个函数指针的地址
		mov ecx,dword ptr [g_pSaveAddr]		//取出事先保存的A::print_all()地址
		mov dword ptr [eax],ecx

		pop ecx
		pop eax
	}

	cout<<"A::print_all() unhooked!"<<endl;

	DWORD dwTemp = 0;
	::VirtualProtectEx(hProcess, mbiA.BaseAddress, 4, dwOldProtectA, &dwTemp);
	CloseHandle(hProcess);
}
主函数如下:

int _tmain(int argc, _TCHAR* argv[])
{
	A a;
	a.iVal1 = 0;
	a.iVal2 = 1;

	B b;
	b.iVal1 = 1000;
	b.iVal2 = 1001;

	A* pA = &a;
	B* pB = &b;

	//CallAMethodWithBObject(pA, pB);
    //ExchangeMethod(pA, pB);
	//pA->print1();
	//pA->print2();
	//pA->print_all();
	//pA->print_extern(-200);

	//cout<<endl;

	//pB->print1();
	//pB->print2();
	//pB->print_all();
	//pB->print_extern(-200);

	load_hook(pA, DWORD(&hook_proc));
	pA->print_all();

	unload_hook(pA);
	pA->print_all();

	getchar();

	return 0;
}

本文引用:http://bbs.pediy.com/showthread.php?t=71775

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Detours是微软开发的一个函数库, 用于修改运行中的程序在内存中的影像,从而即使没有源代码也能改变程序的行为。具体用途是: 拦截WIN32 API调用,将其引导到自己的子程序,从而实现WIN32 API的定制。 为一个已在运行的进程创建一新线程,装入自己的代码并运行。 ---- 本文将简介Detours的原理,Detours库函数的用法, 并利用Detours库函数在Windows NT上编写了一个程序,该程序能使有“调试程序”的用户权限的用户成为系统管理员,附录利用Detours库函数修改该程序使普通用户即可成为系统管理员 (在NT4 SP3上)。 一. Detours的原理 ---- 1. WIN32进程的内存管理 ---- 总所周知,WINDOWS NT实现了拟存储器,每一WIN32进程拥有4GB的存空间, 关于WIN32进程的存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点: ---- (1) 进程要执行的指令也放在存空间中 ---- (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序 ---- (3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码 ---- 2. 拦截WIN32 API的原理 ---- Detours定义了三个概念: ---- (1) Target函数:要拦截的函数,通常为Windows的API。 ---- (2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。 ---- (3) Detour 函数:用来替代Target函数函数。 ---- Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5作为Trampoline函数。例子如下: 拦截前:Target _ Function: ;Target函数入口,以下为假想的常见的子程序入口代码 push ebp mov ebp, esp push eax push ebx Trampoline: ;以下是Target函数的继续部分 …… 拦截后: Target _ Function: jmp Detour_Function Trampoline: ;以下是Target函数的继续部分 …… Trampoline_Function: ; Trampoline函数入口, 开头的5个字节与Target函数相同 push ebp mov ebp, esp push eax push ebx ;跳回去继续执行Target函数 jmp Target_Function+5 ---- 3. 为一个已在运行的进程装入一个DLL ---- 以下是其步骤: ---- (1) 创建一个ThreadFuction,内容仅是调用LoadLibrary。 ---- (2) 用VirtualAllocEx为一个已在运行的进程分配一片存,并把权限更改为可读可写可执行。 ---- (3) 把ThreadFuction的二进制机器码写入这片存。 ---- (4) 用CreateRemoteThread在该进程上创建一个线程,传入前面分配的存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码。 二. Detours库函数的用法 ---- 因为Detours软件包并没有附带帮助文件,以下接口仅从剖析源代码得出。 ---- 1. PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction) ---- 功能:从一DLL中找出一函数的入口地址 ---- 参数:pszModule是DLL名,pszFun

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值