前兩天和dvff談論的一些技術總結。
程序代碼:
////////////////////////////////////////////////////////////////////////////////////
/
// HOOKAPI DEMO PROGRAM
// 文件名:HOOKTEST.C
//
// 描述: 演示WINNT下HOOK系統API過程及方法
// (擴充為HOOK任意函數的方法)
//
// 作者:東海一魚
//
// 時間: 2010.7.22
//
// 使用編譯器: VC2003
//
// 使用第三方庫: NULL
//
//
// Bug、修復紀錄:
// 1、2010.8.4 增加HOOK私有函數代碼(被hook函數名TestFn,hook函數名MyHookFn2)
//
// 2、2010.8.6 增加HOOK類虛擬函數代碼(被hook函數名TestVirtualFn,
// hook函數名TestVirtualFn2)
//
// HOOK類成員函數,主要難度在於成員函數地址的獲得。
// 普通成員函數指針在VC6下無法進行強制類型轉換,
// 解決手段見GetClassFnAddress輔助函數。
// 類虛擬成員函數HOOK需要獲得類實例對象虛表中的虛擬函數指針。
////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
// #pragma pointers_to_members(best_case)
#ifdef _X86_
#define FLATJMPCODE_LENGTH 5 // x86 平坦內存模式下,絕對跳轉指令長度
#define FLATJMPCMD_LENGTH 1 // 機械碼0xe9長度
#define FLATJMPCMD 0xe9
#else
#error WINVER ERRORSLECT ONLY USES ON X86!!!
#endif
///////////////////////////////////////////////////////////////////////////
// 測試類函數HOOK
class TestHookFnClass
{
public:
virtual void TestVirtualFn( char* szPlay);
virtual void TestVirtualFn2( char* szPlay);
void TestHookFn( char* szPlay);
void TestHookFn2( char* szPlay);
};
void TestHookFnClass::TestHookFn( char* szPlay)
{
printf( " %s\n ",szPlay);
}
void TestHookFnClass::TestHookFn2( char* szPlay)
{
printf( " %s - %s\n ",szPlay, " 類函數已被HOOK! ");
}
void TestHookFnClass::TestVirtualFn( char* szPlay)
{
printf( " %s\n ",szPlay);
}
void TestHookFnClass::TestVirtualFn2( char* szPlay)
{
printf( " %s - %s\n ",szPlay, " Virtual2 HOOKS ");
}
//////////////////////////////////////////////////////////////////////// /
#ifdef _X86_
// 利用C變參特性,進行類成員函數指針類型轉換
LPVOID GetClassFnAddress(...) // Add 2010.8.6
{
LPVOID FnAddress;
__asm
{
lea eax,FnAddress
mov edx,[ebp+ 8]
mov [eax],edx
}
return FnAddress;
}
#endif
// 獲得類虛擬成員函數指針
LPVOID GetClassVirtualFnAddress(LPVOID pthis, int Index) // Add 2010.8.6
{
LPVOID FnAddress;
*( int*)&FnAddress = *( int*)pthis; // lpvtable
*( int*)&FnAddress = *( int*)(( int*)FnAddress + Index);
return FnAddress;
}
//////////////////////////////////////////////////////////////////////// /
// 我的新API函數
int __stdcall MyHookFn(HWND hwnd, char* sztext, char* szTitle, int stly)
{
return printf( " %s - %s\n ",sztext, " 原API函數已被HOOK! "); //
}
// 我的新私有樁函數
void MyHookFn2( char* szTxt)
{
char* pBuf = ( char*)malloc(strlen(szTxt) + sizeof( char*) * 32);
__try
{
sprintf(pBuf, " %s - %s ",szTxt, " 原函數已被HOOK! ");
printf( " %s\n ",pBuf);
}
__finally
{
free(pBuf);
}
}
// 私有測試函數
void TestFn( char* szTitle)
{
printf( " %s ",szTitle);
}
///////////////////////////////////////////////////////////////////// /
// HOOK函數
BOOL HookApi(LPVOID ApiFun,LPVOID HookFun)
{
BOOL IsSuccess = FALSE;
DWORD TempProtectVar; // 臨時保護屬性變量
MEMORY_BASIC_INFORMATION MemInfo; // 內存分頁屬性信息
VirtualQuery(ApiFun,&MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
PAGE_READWRITE,&MemInfo.Protect)) // 修改頁面為可寫
{
*(BYTE*)ApiFun = FLATJMPCMD;
*(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
(DWORD)ApiFun - FLATJMPCODE_LENGTH;
VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
MemInfo.Protect,&TempProtectVar); // 改回原屬性
IsSuccess = TRUE;
}
return IsSuccess;
}
int main( int argc, char** argv)
{
HMODULE hDll;
LPVOID OldFun;
TestHookFnClass* TestClass = new TestHookFnClass();
__try
{
hDll = GetModuleHandle( " User32.dll ");
OldFun = GetProcAddress(hDll, " MessageBoxA "); // 要HOOK的API函數
if(OldFun)
{
if(HookApi(OldFun,MyHookFn)) // 如果HOOK成功
MessageBoxA( 0, " call Api MessageBox ", " Is Hookd? ",MB_OK); // 調用原API,測試HOOK
if(HookApi(TestFn,MyHookFn2))
TestFn( " Private Function Hook Test "); // 調用私有函數,測試HOOK
if(HookApi(GetClassFnAddress(TestHookFnClass::TestHookFn),
GetClassFnAddress(TestHookFnClass::TestHookFn2)))
TestClass->TestHookFn( " Hook Class Member Function Test! "); // 調用類成員函數,測試HOOK
if(HookApi(GetClassVirtualFnAddress(TestClass, 0),
GetClassFnAddress(TestHookFnClass::TestHookFn2)))
TestClass->TestVirtualFn( " Call Class Virtual Function Test! "); // 調用類虛擬函數,測試HOOK
if(HookApi(GetClassVirtualFnAddress(TestClass, 0),
GetClassVirtualFnAddress(TestClass, 1)))
TestClass->TestVirtualFn( " Call Class Virtual Function Test2! "); // 同上,樁函數為另一虛函數
}
}
__finally
{
if(hDll)
FreeLibrary(hDll);
if(TestClass)
delete(TestClass);
}
return 0;
}
// HOOKAPI DEMO PROGRAM
// 文件名:HOOKTEST.C
//
// 描述: 演示WINNT下HOOK系統API過程及方法
// (擴充為HOOK任意函數的方法)
//
// 作者:東海一魚
//
// 時間: 2010.7.22
//
// 使用編譯器: VC2003
//
// 使用第三方庫: NULL
//
//
// Bug、修復紀錄:
// 1、2010.8.4 增加HOOK私有函數代碼(被hook函數名TestFn,hook函數名MyHookFn2)
//
// 2、2010.8.6 增加HOOK類虛擬函數代碼(被hook函數名TestVirtualFn,
// hook函數名TestVirtualFn2)
//
// HOOK類成員函數,主要難度在於成員函數地址的獲得。
// 普通成員函數指針在VC6下無法進行強制類型轉換,
// 解決手段見GetClassFnAddress輔助函數。
// 類虛擬成員函數HOOK需要獲得類實例對象虛表中的虛擬函數指針。
////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
// #pragma pointers_to_members(best_case)
#ifdef _X86_
#define FLATJMPCODE_LENGTH 5 // x86 平坦內存模式下,絕對跳轉指令長度
#define FLATJMPCMD_LENGTH 1 // 機械碼0xe9長度
#define FLATJMPCMD 0xe9
#else
#error WINVER ERRORSLECT ONLY USES ON X86!!!
#endif
///////////////////////////////////////////////////////////////////////////
// 測試類函數HOOK
class TestHookFnClass
{
public:
virtual void TestVirtualFn( char* szPlay);
virtual void TestVirtualFn2( char* szPlay);
void TestHookFn( char* szPlay);
void TestHookFn2( char* szPlay);
};
void TestHookFnClass::TestHookFn( char* szPlay)
{
printf( " %s\n ",szPlay);
}
void TestHookFnClass::TestHookFn2( char* szPlay)
{
printf( " %s - %s\n ",szPlay, " 類函數已被HOOK! ");
}
void TestHookFnClass::TestVirtualFn( char* szPlay)
{
printf( " %s\n ",szPlay);
}
void TestHookFnClass::TestVirtualFn2( char* szPlay)
{
printf( " %s - %s\n ",szPlay, " Virtual2 HOOKS ");
}
//////////////////////////////////////////////////////////////////////// /
#ifdef _X86_
// 利用C變參特性,進行類成員函數指針類型轉換
LPVOID GetClassFnAddress(...) // Add 2010.8.6
{
LPVOID FnAddress;
__asm
{
lea eax,FnAddress
mov edx,[ebp+ 8]
mov [eax],edx
}
return FnAddress;
}
#endif
// 獲得類虛擬成員函數指針
LPVOID GetClassVirtualFnAddress(LPVOID pthis, int Index) // Add 2010.8.6
{
LPVOID FnAddress;
*( int*)&FnAddress = *( int*)pthis; // lpvtable
*( int*)&FnAddress = *( int*)(( int*)FnAddress + Index);
return FnAddress;
}
//////////////////////////////////////////////////////////////////////// /
// 我的新API函數
int __stdcall MyHookFn(HWND hwnd, char* sztext, char* szTitle, int stly)
{
return printf( " %s - %s\n ",sztext, " 原API函數已被HOOK! "); //
}
// 我的新私有樁函數
void MyHookFn2( char* szTxt)
{
char* pBuf = ( char*)malloc(strlen(szTxt) + sizeof( char*) * 32);
__try
{
sprintf(pBuf, " %s - %s ",szTxt, " 原函數已被HOOK! ");
printf( " %s\n ",pBuf);
}
__finally
{
free(pBuf);
}
}
// 私有測試函數
void TestFn( char* szTitle)
{
printf( " %s ",szTitle);
}
///////////////////////////////////////////////////////////////////// /
// HOOK函數
BOOL HookApi(LPVOID ApiFun,LPVOID HookFun)
{
BOOL IsSuccess = FALSE;
DWORD TempProtectVar; // 臨時保護屬性變量
MEMORY_BASIC_INFORMATION MemInfo; // 內存分頁屬性信息
VirtualQuery(ApiFun,&MemInfo, sizeof(MEMORY_BASIC_INFORMATION));
if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
PAGE_READWRITE,&MemInfo.Protect)) // 修改頁面為可寫
{
*(BYTE*)ApiFun = FLATJMPCMD;
*(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
(DWORD)ApiFun - FLATJMPCODE_LENGTH;
VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
MemInfo.Protect,&TempProtectVar); // 改回原屬性
IsSuccess = TRUE;
}
return IsSuccess;
}
int main( int argc, char** argv)
{
HMODULE hDll;
LPVOID OldFun;
TestHookFnClass* TestClass = new TestHookFnClass();
__try
{
hDll = GetModuleHandle( " User32.dll ");
OldFun = GetProcAddress(hDll, " MessageBoxA "); // 要HOOK的API函數
if(OldFun)
{
if(HookApi(OldFun,MyHookFn)) // 如果HOOK成功
MessageBoxA( 0, " call Api MessageBox ", " Is Hookd? ",MB_OK); // 調用原API,測試HOOK
if(HookApi(TestFn,MyHookFn2))
TestFn( " Private Function Hook Test "); // 調用私有函數,測試HOOK
if(HookApi(GetClassFnAddress(TestHookFnClass::TestHookFn),
GetClassFnAddress(TestHookFnClass::TestHookFn2)))
TestClass->TestHookFn( " Hook Class Member Function Test! "); // 調用類成員函數,測試HOOK
if(HookApi(GetClassVirtualFnAddress(TestClass, 0),
GetClassFnAddress(TestHookFnClass::TestHookFn2)))
TestClass->TestVirtualFn( " Call Class Virtual Function Test! "); // 調用類虛擬函數,測試HOOK
if(HookApi(GetClassVirtualFnAddress(TestClass, 0),
GetClassVirtualFnAddress(TestClass, 1)))
TestClass->TestVirtualFn( " Call Class Virtual Function Test2! "); // 同上,樁函數為另一虛函數
}
}
__finally
{
if(hDll)
FreeLibrary(hDll);
if(TestClass)
delete(TestClass);
}
return 0;
}
