ゆとりーなの日記

日記的な事を書いて行くと思はれる

仮想関数テーブルを元に戻す輩がおった

昨日COMメソッドの一部をフックする手法を紹介しましたが、あれと同じ原理でIDirect3DDevice::EndSceneをフックすると、途中でフックが切れる的な現象に見舞われてしまいました。どうやら何者かがせっかく書き換えた仮想関数テーブルを元に戻してしまうらしいのです。そのような不届きものの正体はすぐに判明しました。このページアスペクト比固定拡大ツールの仕組み - Tari Lari Runに普通に載ってました。
そいつの正体は、

IDirect3DDevice9::BeginStateBlock
IDirect3DDevice9::EndStateBlock

です。MSDNで調べたら、なにやらメソッドを記録できるとか書いてあります。これは確かに仮想関数テーブルを戻さなきゃまずそうな気もしますね。
解決策も普通に載ってましたのでそれを参考に実装したのがこちら。

// IDirect3DDevice9::BeginStateBlockはIDirect3DDevice9生成時にフックしておく

// 書き換え後のIDirect3DDevice9::BeginStateBlock
HRESULT WINAPI beginStateBlock(IDirect3DDevice9 *device) {
	HRESULT hr((*original_begin_state_block)(device));
	original_end_state_block = device->lpVtbl->EndStateBlock;
	// オリジナルを呼んだあとにvtblを書き換えてIDirect3DDevice9::EndStateBlockをフック
	DWORD old_protect;
	VirtualProtect(reinterpret_cast<void *>(device->lpVtbl), sizeof(device->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
	device->lpVtbl->EndStateBlock = endStateBlock;
	VirtualProtect(reinterpret_cast<void *>(device->lpVtbl), sizeof(device->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
	return hr;
}

// 書き換え後のIDirect3DDevice9::EndStateBlock
HRESULT WINAPI endStateBlock(IDirect3DDevice9 *device, IDirect3DStateBlock9 ** pp) {
	HRESULT hr((*original_end_state_block)(device, pp));
	original_end_scene = device->lpVtbl->EndScene;
	original_begin_state_block = device->lpVtbl->BeginStateBlock;
	// オリジナルを呼んだあとにvtblを書き換えてIDirect3DDevice9::BeginStateBlockと目的のメソッドをフック
	DWORD old_protect;
	VirtualProtect(reinterpret_cast<void *>(device->lpVtbl), sizeof(device->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
	device->lpVtbl->EndScene        = endScene;
	device->lpVtbl->BeginStateBlock = beginStateBlock;
	VirtualProtect(reinterpret_cast<void *>(device->lpVtbl), sizeof(device->lpVtbl), PAGE_EXECUTE_WRITECOPY, &old_protect);
	return hr;
}

これでいまのところ勝手にフックが解除されるという現象は見られなくなりました。