从Hook虚函数到HOOK COM API

// HookVtable.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <memory.h>
#include <windows.h>

using namespace std;

class Test{
private:
	HANDLE m_hProcess;
	DWORD m_dwOldProtectFlag;
	ULONG_PTR** m_pplVrtable;
	ULONG_PTR m_OldProcAddress;
	size_t m_sizeRead;
	MEMORY_BASIC_INFORMATION mbi;
public:
	Test() :m_hProcess(NULL),
		m_dwOldProtectFlag(0),
		m_pplVrtable(NULL),
		m_OldProcAddress(0),
		m_sizeRead(0)
	{
		ZeroMemory(&mbi, sizeof(mbi));
	}
	~Test()
	{
	};
	virtual void fun1()
	{
		cout <<this<<":"<< "fun1 called!" << endl;
	}
	virtual void fun2()
	{
		cout << this << ":" << "fun2 called!" << endl;
	}
	virtual void fun3()
	{
		cout << this << ":" << "fun3 called!" << endl;
	}
	void Hook(ULONG_PTR dwAddFun)
	{
		m_pplVrtable = (ULONG_PTR**)(this);
		m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
		if (VirtualQueryEx(m_hProcess, (LPVOID)(*m_pplVrtable), &mbi, sizeof(mbi)) != sizeof(mbi))
			return;
		if (!VirtualProtectEx(m_hProcess, mbi.BaseAddress, 16, PAGE_EXECUTE_READWRITE, &m_dwOldProtectFlag))
			return;
		ReadProcessMemory(m_hProcess, &(*m_pplVrtable)[1], &m_OldProcAddress, sizeof(ULONG_PTR), &m_sizeRead);
		(*m_pplVrtable)[1] = dwAddFun;

	}
	void UnHook()
	{
		DWORD dwTemp = 0;
		(*m_pplVrtable)[1] = m_OldProcAddress;
		VirtualProtectEx(m_hProcess, mbi.BaseAddress, 16, m_dwOldProtectFlag, &dwTemp);
		CloseHandle(m_hProcess);
	}

};

void MyFun()
{
	cout << "This is fake function" << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Test* pA = new Test;
	Test* pA2 = new Test;
	pA->Hook((ULONG_PTR)MyFun);
	pA->fun1();
	pA->fun2();
	pA2->fun2();
	pA->UnHook();
	pA->fun1();
	pA->fun2();
	pA2->fun2();
	delete pA;
	return 0;
}

 

同一个进程下 某个类的虚函数列表是唯一的, 所有类对象共享 ,类对象的this指针指向的第一个地址就是虚函数表的地址。

修改表中某个虚函数的地址就能修改所有类对象的函数跳转 (虚函数的索引 是虚函数在类里面的排序位置 由0开始)。

以上就是HOOK COM API的基本原理

测试一下吧

ITextServices* pTextServices = NULL;
typedef HRESULT(ITextServices::*TrueTxSendMessage)(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult);
TrueTxSendMessage g_func = NULL;


HRESULT STDMETHODCALLTYPE FakeTxSendMessage(
	UINT msg,
	WPARAM wparam,
	LPARAM lparam,
	LRESULT *plresult)
{
	MessageBox(NULL, _T("FakeTxSendMessage"), _T("FakeTxSendMessage"), MB_OK);
	return (pTextServices->*(g_func))(msg,wparam,lparam,plresult);
}

HANDLE m_hProcess = NULL;
DWORD** m_pplVrtable = NULL;
DWORD m_dwOldProtectFlag = 0;
DWORD m_OldProcAddress = 0;
DWORD m_sizeRead = 0;
MEMORY_BASIC_INFORMATION mbi = {0};
void Hook(DWORD dwAddFun, ITextServices* pITextServices)
{
	m_pplVrtable = (DWORD**)(pITextServices);
	m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ::GetCurrentProcessId());
	if (VirtualQueryEx(m_hProcess, (LPVOID)(*m_pplVrtable), &mbi, sizeof(mbi)) != sizeof(mbi))
		return;
	if (!VirtualProtectEx(m_hProcess, mbi.BaseAddress, 16, PAGE_EXECUTE_READWRITE, &m_dwOldProtectFlag))
		return;
	ReadProcessMemory(m_hProcess, &(*m_pplVrtable)[3], &m_OldProcAddress, sizeof(DWORD), &m_sizeRead);
	memcpy(&g_func, &m_OldProcAddress, sizeof(DWORD));
	(*m_pplVrtable)[3] = dwAddFun;

}
void UnHook()
{
	DWORD dwTemp = 0;
	(*m_pplVrtable)[3] = m_OldProcAddress;
	VirtualProtectEx(m_hProcess, mbi.BaseAddress, 16, m_dwOldProtectFlag, &dwTemp);
	CloseHandle(m_hProcess);
}

do
	{
		HRESULT hr;

		IUnknown* pUnk = NULL;


		// Create an instance of the application-defined object that implements the ITextHost interface.
		MyTextHost* pTextHost = new MyTextHost(g_hWnd);
		MyTextHost* pTextHost2 = new MyTextHost(g_hWnd);
		

		if (pTextHost == NULL)
			break;
		PCreateTextServices TextServicesProc = NULL;
		HMODULE hmodRichEdit = LoadLibrary(_T("Msftedit.dll"));

		// Retrieve the IID_ITextServices interface identifier from Msftedit.dll. 
		IID* pIID_ITS = NULL;
		if (hmodRichEdit)
		{
			pIID_ITS = (IID*)(VOID*)GetProcAddress(hmodRichEdit, "IID_ITextServices");
			TextServicesProc = (PCreateTextServices)GetProcAddress(hmodRichEdit, "CreateTextServices");
		}

		if (TextServicesProc)
		{
			hr = TextServicesProc(NULL, pTextHost, &pUnk);
		}

		// Create an instance of the text services object.
		//hr = CreateTextServices(NULL, pTextHost, &pUnk);

		if (FAILED(hr))
			break;

		

		// Retrieve the ITextServices interface.    
		hr = pUnk->QueryInterface(*pIID_ITS, (void **)&pTextServices);
		

		Hook((ULONG_PTR)FakeTxSendMessage, pTextServices);
		//UnHook();

		if (TextServicesProc)
		{
			hr = TextServicesProc(NULL, pTextHost2, &pUnk);
			hr = pUnk->QueryInterface(*pIID_ITS, (void **)&pTextServices);
		}

		pUnk->Release();

		
		hr = pTextServices->TxSendMessage(WM_SETFOCUS, 0, 0, 0);

		if (FAILED(hr))
			break;

		

	} while (0);

 

注意事项:

1,保存原有虚函数的指针类型需要为类成员函数指针类型

typedef HRESULT(ITextServices::*TrueTxSendMessage)(UINT msg, WPARAM wparam, LPARAM lparam, LRESULT *plresult);

否则异常

2,要保证调用约定前后一致

HRESULT STDMETHODCALLTYPE FakeTxSendMessage(
    UINT msg,
    WPARAM wparam,
    LPARAM lparam,
    LRESULT *plresult)
{
    MessageBox(NULL, _T("FakeTxSendMessage"), _T("FakeTxSendMessage"), MB_OK);
    return (pTextServices->*(g_func))(msg,wparam,lparam,plresult);
}

否则异常

Thanks~

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
发出的红包

打赏作者

Leen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值