VCプログラミングでグローバルフックのロードを阻止する
Hook(フック)技術の紹介と応用;最近多くのネットユーザーがHookについて討論していることを発見して、そこでこの欄の文章を整理して、一部は私が長い間コレクションしていた文章で、すでに出典と作者を忘れて、幸いにも通りかかった作者に注意してもらって、ここで謝りました!Hook(フック)の文章と技術のまとめ、私は人のために、誰もが私のために!
VCプログラミングでグローバルフックのロードを阻止する
まず、グローバルフックがどのように私たちのプログラムに入ったのかを話します.プログラムAにWH_がインストールされている場合GETMESSAGEのグローバルフック、フック関数がB.dllにある場合、他のプログラムがGetMessage関数を呼び出して自分のメッセージキューからメッセージを取り出すと、システムはプログラムAがWH_をインストールしていることを発見するGETMESSAGEのグローバルフックは、GetMessageを呼び出すプロセスがB.dllをロードしているかどうかをチェックし、そうでない場合はLoadLibraryを呼び出してロードし、B.dllのフックプロセスを呼び出します.これにより、フックdllはGetMessageを呼び出すすべてのプロセスにロードされます.
私たちがしなければならない仕事は、システムがLoadLibraryを呼び出したときに失敗させることです.これには2つの問題があります. LoadLibrary関数は今回失敗しましたが、次のシステムはロードを試みます.効率に影響を与える可能性がありますが、失敗させなくても、メッセージがあるたびに、システムはそのフックプロセスを呼び出します.どの方法が効率に影響しますか.これは知らないで、ほほほ; はどのようにLoadLibraryを失敗させたか知っていますか?失敗させるわけにはいかないでしょう.死ぬのは惨めです.(.研究によると、正常なロードdll関数呼び出しはkernel 32.dllから来ていますが、ロードフックプロセスだけはuser 32.dllで行われています(winxpシステムでは、これからもそうかどうか分かりません)を参照してください.LoadLibrary関数の戻りアドレスを判断できます.user 32.dllのアドレス空間であればフックdllのロードとみなされ、直接0を返すことができます. 次に、APIブロックについて説明します.user 32.dllではLoadLibraryExWを使用してフックdllをロードしているので、このような関数をブロックするだけでいいです.3つのステップに分けられます. LoadLibraryExWに代わる関数を提供します.名前をnewLoadLibraryExWと仮定します.関数の原型はLoadLibraryExWとそっくりで、このプロセス内のすべてのLoadLibraryExWの呼び出しはここに移ります.
は空間を提供し、この空間の開始アドレスがfakeLoadLibraryExWであると仮定し、LoadLibraryExW関数の前のNバイトを保存し、jmp命令で(LoadLibraryExW+N)アドレスにジャンプして実行を継続する.ここで、Nは7を取り、具体的な原因は以下の通りである. LoadLibraryExW関数の最初の5バイトを修正し、1つのjmp命令で私たちのnewLoadLibraryExW関数の先頭にジャンプします.ここでは5バイトしか使われていませんが、まずLoadLibraryExW関数の最初の2つの命令を見てみましょう:
全部で7バイトです.最初の5バイトだけを修正して、fakeLoadLibraryExW関数から6バイト目にジャンプして実行することはできません.3番目の命令である8バイト目にジャンプするのは、前のステップNが7を取った理由です.図を描いて、修正前:
変更後:
以下はカプセル化されたクラスです.使用時にクラスのグローバル変数を定義し、PatchLoadLibrary関数を呼び出せばいいです.
注意1:どのように関数の戻りアドレスを知っていますか?私達はすべて知っていて、関数が呼び出す時、先にパラメータをスタックに入れて、それから戻りアドレスをスタックに入れて、このようにして、私達の関数の中で、espは関数の戻りアドレスを指すべきです.しかし関数を返す時元のスタックと関数の中で伝達のパラメータを簡単に引用するために、コンパイラは普通はすべて発生します2つのコマンド:
push ebp
mov ebp,esp
まずebpをスタックに入れて、元のespをebpレジスタに保存します.そうすれば、私たちの戻りアドレスは[ebp+4]、最初のパラメータは[ebp+8]、2番目は[ebp+0 xC]です.
注2:共通点のAPI Hookを書くためには、簡単なpatchの上位5バイトまたは上位7バイトではなく、異なる命令に基づいてpatchのどのくらいのバイトが必要かを分析する必要があります.マイクロソフトのDetoursの実装を参照してください.
VCプログラミングでグローバルフックのロードを阻止する
まず、グローバルフックがどのように私たちのプログラムに入ったのかを話します.プログラムAにWH_がインストールされている場合GETMESSAGEのグローバルフック、フック関数がB.dllにある場合、他のプログラムがGetMessage関数を呼び出して自分のメッセージキューからメッセージを取り出すと、システムはプログラムAがWH_をインストールしていることを発見するGETMESSAGEのグローバルフックは、GetMessageを呼び出すプロセスがB.dllをロードしているかどうかをチェックし、そうでない場合はLoadLibraryを呼び出してロードし、B.dllのフックプロセスを呼び出します.これにより、フックdllはGetMessageを呼び出すすべてのプロセスにロードされます.
私たちがしなければならない仕事は、システムがLoadLibraryを呼び出したときに失敗させることです.これには2つの問題があります.
HMODULE WINAPI newLoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)
{
// 1
DWORD dwCaller;
__asm push dword ptr [ebp+4]
__asm pop dword ptr [dwCaller]
// User32.dll
//m_dwUser32Low m_dwUser32Hi user32.dll
if(dwCaller > m_dwUser32Low && dwCaller < m_dwUser32Hi)
{
//TRACE something hint infomation
return 0;
}
return rawLoadLibraryExW(lpLibFileName,hFile,dwFlags);
}
//
push 34h?//6A 34
push 7C80E288h?//68 88 E2 80 7C
全部で7バイトです.最初の5バイトだけを修正して、fakeLoadLibraryExW関数から6バイト目にジャンプして実行することはできません.3番目の命令である8バイト目にジャンプするのは、前のステップNが7を取った理由です.図を描いて、修正前:
変更後:
以下はカプセル化されたクラスです.使用時にクラスのグローバル変数を定義し、PatchLoadLibrary関数を呼び出せばいいです.
//***********************************************************************************//
// FileName : GBlockHookDll.h
// Author :
// Date : 2006.10.07
//***********************************************************************************//
#pragma once
#include
#pragma comment(lib,"psapi.lib")
class GBlockHookDll
{
public:
GBlockHookDll()
{
MODULEINFO user32ModInfo = {0};
// user32.dll
GetModuleInformation(GetCurrentProcess(),GetModuleHandle("user32.dll"),&user32ModInfo,sizeof(user32ModInfo));
m_dwUser32Low = (DWORD)user32ModInfo.lpBaseOfDll;
m_dwUser32Hi = (DWORD)user32ModInfo.lpBaseOfDll+user32ModInfo.SizeOfImage;
}
void PatchLoadLibrary()
{
//LoadLibraryExW
//7C801AF1 6A 34 push 34h
//7C801AF3 68 88 E2 80 7C push 7C80E288h
LPVOID* pfnRaw = (LPVOID*)&rawLoadLibraryExW;
LPVOID fnNew = (LPVOID)newLoadLibraryExW;
BYTE* fnRaw = (BYTE*)*pfnRaw;
//1 save the first 7 bytes
const int nFirstBytes = 7;
BYTE* fnFake = (BYTE*)fakeLoadLibraryExW;
memcpy(fnFake,*pfnRaw,nFirstBytes);
fnFake[nFirstBytes] = 0xE9; //jmp to rawAddr+nFirstBytes
*(UINT32*)(fnFake + nFirstBytes+1) = (UINT32)fnRaw+nFirstBytes - (UINT32)(fnFake + nFirstBytes + 5);
//2 modify the raw to jmp to fnNew
DWORD dwOldProtect = 0;
VirtualProtect(fnRaw,nFirstBytes,PAGE_READWRITE,&dwOldProtect); //
*fnRaw = 0xE9;
*(UINT32*)(fnRaw+1) = (UINT32)fnNew - (UINT32)(fnRaw + 5);
VirtualProtect(fnRaw,nFirstBytes,dwOldProtect,0);
//3 change the rawPointer
*pfnRaw = fnFake;
}
private:
static HMODULE WINAPI newLoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)
{
//get the return address
DWORD dwCaller;
__asm push dword ptr [ebp+4]
__asm pop dword ptr [dwCaller]
if(dwCaller > m_dwUser32Low && dwCaller < m_dwUser32Hi)
{
#ifdef _DEBUG
UINT uLenWide = lstrlenW(lpLibFileName);
char* pNewChar = new char[uLenWide + 1];
memset(pNewChar,0,uLenWide+1);
WideCharToMultiByte(CP_ACP,0,lpLibFileName,-1,pNewChar,uLenWide,NULL,NULL);
TRACE2(".......................LoadLibrary:return addr 0x%x,%s ",dwCaller,pNewChar);
TRACE("Blocked......................./n");
delete []pNewChar;
#endif
return 0;
}
return rawLoadLibraryExW(lpLibFileName,hFile,dwFlags);
}
private:
static DWORD m_dwUser32Low; //user32.dll
static DWORD m_dwUser32Hi; //user32.dll +ImageSize
static BYTE fakeLoadLibraryExW[12]; //save first bytes of the raw function,and jmp back to that function
// LoadLibraryExW , fakeLoadLibraryExW
static HMODULE (WINAPI *rawLoadLibraryExW)( LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags );
};
DWORD GBlockHookDll::m_dwUser32Low = 0;
DWORD GBlockHookDll::m_dwUser32Hi = 0;
BYTE GBlockHookDll::fakeLoadLibraryExW[12] = {0};
HMODULE (WINAPI *GBlockHookDll::rawLoadLibraryExW)(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags) = LoadLibraryExW;
注意1:どのように関数の戻りアドレスを知っていますか?私達はすべて知っていて、関数が呼び出す時、先にパラメータをスタックに入れて、それから戻りアドレスをスタックに入れて、このようにして、私達の関数の中で、espは関数の戻りアドレスを指すべきです.しかし関数を返す時元のスタックと関数の中で伝達のパラメータを簡単に引用するために、コンパイラは普通はすべて発生します2つのコマンド:
push ebp
mov ebp,esp
まずebpをスタックに入れて、元のespをebpレジスタに保存します.そうすれば、私たちの戻りアドレスは[ebp+4]、最初のパラメータは[ebp+8]、2番目は[ebp+0 xC]です.
注2:共通点のAPI Hookを書くためには、簡単なpatchの上位5バイトまたは上位7バイトではなく、異なる命令に基づいてpatchのどのくらいのバイトが必要かを分析する必要があります.マイクロソフトのDetoursの実装を参照してください.