仮想関数呼び出しに対応しました。

SEXYHOOK 0.6
http://code.google.com/p/sexyhook/downloads/list

注意:gccでは仮想関数のフックに失敗します...今のところVCのみ対応です

//仮想/純粋仮想メソッドの定義
class Parent 
{
public:
	virtual int f() 
	{
		return 1;
	}
	virtual int g() =0;
};

class Child : public Parent 
{
public:
	virtual int f() 
	{
		return 2;
	}
	virtual int g() 
	{
		return 3;
	}
};

int cf,cg,pf,pg;
Child child;

printf("まだフックしていない\r\n");
cf = child.f();
cg = child.g();
pf = ((Parent*)&child)->f();
pg = ((Parent*)&child)->g();
printf("child.f : %d\r\n",cf);
printf("child.g : %d\r\n",cg);
printf("((Parent*)&child)->f : %d\r\n",pf);
printf("((Parent*)&child)->g : %d\r\n",pg);

{
	//ここからフック
	SEXYHOOK_CLASS_HOOK_0_BEGIN(int,Child::g)
	{
		return 103;	//Child::g
	}
	SEXYHOOK_CLASS_END_VCALL(child); //thisを渡す

	printf("Child.gをフック\r\n");
	cf = child.f();
	cg = child.g();
	pf = ((Parent*)&child)->f();
	pg = ((Parent*)&child)->g();
	printf("child.f : %d\r\n",cf);
	printf("child.g : %d\r\n",cg);
	printf("((Parent*)&child)->f : %d\r\n",pf);
	printf("((Parent*)&child)->g : %d\r\n",pg);
	SEXYHOOK_ASSERT(cg == 103);
	SEXYHOOK_ASSERT(pg == 103);
}

仮想関数をフックする場合は、 SEXYHOOK_CLASS_END_VCALL(child); と定義してください。
普通のメソッドをフックする場合もSEXYHOOK_CLASS_END_VCALL(child) とつけても害はないので、どんどん定義しましょう。

仮想関数の呼び出しはこのようになります。

Child::f は、関数の内部ではなく、 vcallになります。

lea ecx,[child]	//thisポインタを積む
call Child::f	//Child::fの実態は関数ではなく、thisからのジャンプテーブル

Child::f: (vcall) //thisポインタからのジャンプテーブル
	mov         eax,dword ptr [ecx]
	jmp         dword ptr [eax + 0]
		//実態
		Child::f()
		{
		     return 1;
		}
||<<

マシン語はこんな感じみたいです。
>|asm|
vcall:
00402BA0   mov         eax,dword ptr [ecx]
00402BA2   jmp         dword ptr [eax]
8B 01 FF 20

 or (2番目virtualメソッドの場合)

004025D0   mov         eax,dword ptr [ecx]
004025D2   jmp         dword ptr [eax+4]
8B 01 FF 60 044バイト目が 60 の場合、次の1バイトが +4 バイト等をしている数字

SEXYHOOKは、 飛び先が vcallであれば、 ユーザからもらった クラスのthisポインタを使って正式なアドレスを計算します。

//フックされる関数の実領域を求める.
uintptr_t overraideFunctionAddr = 0;
if (*((unsigned char*)inFunctionAddress+0) == 0xe9)
{
	//フック関数も ILT経由で飛んでくる場合
	//0xe9 call [4バイト相対アドレス]
	uintptr_t jmpaddress = *((uintptr_t*)((unsigned char*)inFunctionAddress+1));
	overraideFunctionAddr = (((uintptr_t)inFunctionAddress) + jmpaddress) + 5;	//+5は e9 00 00 00 00 (ILTのサイズ)

	//仮想関数の vcallだった場合...
	if (
			*((unsigned char*)overraideFunctionAddr+0) == 0x8B
		&&	*((unsigned char*)overraideFunctionAddr+1) == 0x01
		&&	*((unsigned char*)overraideFunctionAddr+2) == 0xFF
		)
	{
		int plusAddress = 0;
		if (*((unsigned char*)overraideFunctionAddr+3) == 0x20)
		{
			//[[this] + 0] にジャンプ
			plusAddress = 0;
		}
		else if (*((unsigned char*)overraideFunctionAddr+3) == 0x60)
		{
			//[[this] + ?] にジャンプ
			plusAddress = (int) *((unsigned char*)overraideFunctionAddr+4);	//4バイト目の1バイト分が加算する値
		}
		else
		{
			//[[this] + ?] にジャンプを計算出来ませんでした...
			SEXYHOOK_BREAKPOINT;
		}
		//C言語のおせっかいで、ポインタは型分プラスしてしまうので、ポインタのサイズで割っとく.
		plusAddress = plusAddress / sizeof(void*);

		//このような関数に一時的に飛ばされている場合...
		//			vcall:
		//			00402BA0   mov         eax,dword ptr [ecx]
		//			00402BA2   jmp         dword ptr [eax]
		//8B 01 FF 20
		//
		// or
		//
		//004025D0   mov         eax,dword ptr [ecx]
		//004025D2   jmp         dword ptr [eax+4]
		//8B 01 FF 60 04
		if ( inVCallThisPointer == NULL )
		{
			//vcallのフックには、 thisポインタが必要です。
			//SEXYHOOK_CLASS_END_VCALL(thisClass) を利用してください。
			SEXYHOOK_BREAKPOINT;
		}

		/*
			//こういう演算をしたい  inVCallThisPointer = &this;
		_asm
		{
			mov ecx,inVCallThisPointer;
			mov ecx,[ecx];
			mov ecx,[ecx];
			mov overraideFunctionAddr,ecx;
		}

				 or 
		_asm
		{
			mov ecx,inVCallThisPointer;
			mov ecx,[ecx];
			mov ecx,[ecx+4];			//+? は定義された関数分 virtualの数だけ増えるよ
			mov overraideFunctionAddr,ecx;
		}
		*/
		//多分こんな感じ,,,泣けてくるキャストだ.
		overraideFunctionAddr = (uintptr_t) *((void**)*((void***)inVCallThisPointer) + plusAddress);
		//そこにあるのは  関数の本体 jmp への命令のはず.
		if (*((unsigned char*)overraideFunctionAddr+0) != 0xe9)
		{
			//vcallの解析に失敗しました...
			SEXYHOOK_BREAKPOINT;
		}
		//ついでなので関数の中を書き換えるため、関数の実体へのアドレスを求める.
		jmpaddress = *((uintptr_t*)((unsigned char*)overraideFunctionAddr+1));
		overraideFunctionAddr = (((uintptr_t)overraideFunctionAddr) + jmpaddress) + 5;	//+5は e9 00 00 00 00 (ILTのサイズ)
	}
}

void*** のキャストなんてドン引きですね。orz
こんな感じです。

余計に 64bit対応から遠ざかった気がしますが、気にしないことにしましょう。