内联挂钩API
内联挂钩API原理在于用JMP指令替换目标函数地址的前几个字节,当程序运行到这里就JMP到自己的函数中.需要注意的是自己的函数和目标函数的调用规范、参数、返回值都要一致以保持堆栈平衡.
/*
* 通过修改函数开始的指令,使函数执行跳到指定函数来达到拦截API的目的
* 跳转的新函数的调用规范必须和要拦截的函数一致
*/
class CAPIHook
{
private:
BOOL m_bAllowHook; //是否允许拦截
PROC m_pfnOld; //保存要替换的函数地址
BYTE m_byNew[8]; //保存替换前8个字节的值
BYTE m_byOld[8]; //保存替换前的函数前8个字节
BOOL InitHookData( PROC pfnOld, PROC pfnNew )
{
ATLASSERT( ( pfnOld != NULL ) && ( pfnNew != NULL ) );
// mov eax, [新地址] --机器码为: B8 [地址值]
// jmp eax --机器码为: FF E0
BYTE byNew[8] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 };
memcpy( m_byNew, byNew, _countof( byNew ) );
*(DWORD*)( m_byNew + 1 ) = (DWORD)pfnNew; //将新函数地址填充到2、3、4、5字节
m_pfnOld = pfnOld;
if( m_pfnOld == NULL )
return FALSE;
memcpy( m_byOld, m_pfnOld, _countof(m_byOld) );
return TRUE;
}
public:
CAPIHook()
{
m_bAllowHook = FALSE;
}
CAPIHook( PROC pfnOld, PROC pfnNew )
{
this->HookAPI( pfnOld, pfnNew );
}
CAPIHook( LPCTSTR lpszModName, LPCSTR lpszFunName, PROC pfnNew )
{
this->HookAPI( lpszModName, lpszFunName, pfnNew );
}
/*
* 拦截API,传入要拦截的源函数地址和自指定的函数地址
* pfnOld:源函数地址
* pfnNew:自指定的函数地址
*/
BOOL HookAPI( PROC pfnOld, PROC pfnNew )
{
return m_bAllowHook = this->InitHookData( pfnOld, pfnNew );
}
/*
* 拦截API,传入要拦截的源函数的模块句柄和函数名称和自指定的函数地址
* lpszModName:源函数所在的模块句柄
* lpszFunName:源函数名称
* pfnNew:自指定的函数地址
*/
BOOL HookAPI( LPCTSTR lpszModName, LPCSTR lpszFunName, PROC pfnNew )
{
HMODULE hMod = ::GetModuleHandle( lpszModName ) ;
if( hMod == NULL )
hMod = ::LoadLibrary( lpszModName );
if( hMod == NULL )
return m_bAllowHook = FALSE;
PROC pfnOld = (PROC)::GetProcAddress( hMod, lpszFunName );
if( pfnOld != NULL )
{
return m_bAllowHook = this->InitHookData( pfnOld, pfnNew );
}
}
/*
* 拦截,用构造好的JMP指令替换函数开始处的几个字节
*/
BOOL StartHook()
{
if( !m_bAllowHook )
return FALSE;
//首先修改虚拟内存属性,使之可读写,之后再把跳转指令写入
DWORD dwOldProtect(0);
BOOL bRet = ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), PAGE_READWRITE, &dwOldProtect );
bRet = bRet && ::WriteProcessMemory( ::GetCurrentProcess(), (LPVOID)m_pfnOld, m_byNew, _countof(m_byNew), NULL );
bRet = bRet && ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), dwOldProtect, NULL );
return bRet;
}
/*
* 取消拦截,还原函数开始处被替换的字节
*/
BOOL StopHook()
{
if( !m_bAllowHook )
return FALSE;
//首先修改虚拟内存属性,使之可读写,之后再把恢复指令写入
DWORD dwOldProtect(0);
BOOL bRet = ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), PAGE_READWRITE, &dwOldProtect );
bRet = bRet && ::WriteProcessMemory( ::GetCurrentProcess(), (LPVOID)m_pfnOld, m_byOld, _countof(m_byOld), NULL );
bRet = bRet && ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), dwOldProtect, NULL );
return bRet;
}
};
* 通过修改函数开始的指令,使函数执行跳到指定函数来达到拦截API的目的
* 跳转的新函数的调用规范必须和要拦截的函数一致
*/
class CAPIHook
{
private:
BOOL m_bAllowHook; //是否允许拦截
PROC m_pfnOld; //保存要替换的函数地址
BYTE m_byNew[8]; //保存替换前8个字节的值
BYTE m_byOld[8]; //保存替换前的函数前8个字节
BOOL InitHookData( PROC pfnOld, PROC pfnNew )
{
ATLASSERT( ( pfnOld != NULL ) && ( pfnNew != NULL ) );
// mov eax, [新地址] --机器码为: B8 [地址值]
// jmp eax --机器码为: FF E0
BYTE byNew[8] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 };
memcpy( m_byNew, byNew, _countof( byNew ) );
*(DWORD*)( m_byNew + 1 ) = (DWORD)pfnNew; //将新函数地址填充到2、3、4、5字节
m_pfnOld = pfnOld;
if( m_pfnOld == NULL )
return FALSE;
memcpy( m_byOld, m_pfnOld, _countof(m_byOld) );
return TRUE;
}
public:
CAPIHook()
{
m_bAllowHook = FALSE;
}
CAPIHook( PROC pfnOld, PROC pfnNew )
{
this->HookAPI( pfnOld, pfnNew );
}
CAPIHook( LPCTSTR lpszModName, LPCSTR lpszFunName, PROC pfnNew )
{
this->HookAPI( lpszModName, lpszFunName, pfnNew );
}
/*
* 拦截API,传入要拦截的源函数地址和自指定的函数地址
* pfnOld:源函数地址
* pfnNew:自指定的函数地址
*/
BOOL HookAPI( PROC pfnOld, PROC pfnNew )
{
return m_bAllowHook = this->InitHookData( pfnOld, pfnNew );
}
/*
* 拦截API,传入要拦截的源函数的模块句柄和函数名称和自指定的函数地址
* lpszModName:源函数所在的模块句柄
* lpszFunName:源函数名称
* pfnNew:自指定的函数地址
*/
BOOL HookAPI( LPCTSTR lpszModName, LPCSTR lpszFunName, PROC pfnNew )
{
HMODULE hMod = ::GetModuleHandle( lpszModName ) ;
if( hMod == NULL )
hMod = ::LoadLibrary( lpszModName );
if( hMod == NULL )
return m_bAllowHook = FALSE;
PROC pfnOld = (PROC)::GetProcAddress( hMod, lpszFunName );
if( pfnOld != NULL )
{
return m_bAllowHook = this->InitHookData( pfnOld, pfnNew );
}
}
/*
* 拦截,用构造好的JMP指令替换函数开始处的几个字节
*/
BOOL StartHook()
{
if( !m_bAllowHook )
return FALSE;
//首先修改虚拟内存属性,使之可读写,之后再把跳转指令写入
DWORD dwOldProtect(0);
BOOL bRet = ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), PAGE_READWRITE, &dwOldProtect );
bRet = bRet && ::WriteProcessMemory( ::GetCurrentProcess(), (LPVOID)m_pfnOld, m_byNew, _countof(m_byNew), NULL );
bRet = bRet && ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), dwOldProtect, NULL );
return bRet;
}
/*
* 取消拦截,还原函数开始处被替换的字节
*/
BOOL StopHook()
{
if( !m_bAllowHook )
return FALSE;
//首先修改虚拟内存属性,使之可读写,之后再把恢复指令写入
DWORD dwOldProtect(0);
BOOL bRet = ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), PAGE_READWRITE, &dwOldProtect );
bRet = bRet && ::WriteProcessMemory( ::GetCurrentProcess(), (LPVOID)m_pfnOld, m_byOld, _countof(m_byOld), NULL );
bRet = bRet && ::VirtualProtect( (LPVOID)m_pfnOld, _countof(m_byOld), dwOldProtect, NULL );
return bRet;
}
};
挂接示例:
int WINAPI MyMessageBoxA( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
CAPIHook apiHook( (PROC)MessageBoxA, (PROC)MyMessageBoxA );
apiHook.StartHook();
int WINAPI MyMessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
CString str;
str.Format("fangkm_%s", lpText );
apiHook.StopHook();
int nRet = MessageBoxA( hWnd, str, lpCaption, uType);
apiHook.StartHook();
return nRet;
}
CAPIHook apiHook( (PROC)MessageBoxA, (PROC)MyMessageBoxA );
apiHook.StartHook();
int WINAPI MyMessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType )
{
CString str;
str.Format("fangkm_%s", lpText );
apiHook.StopHook();
int nRet = MessageBoxA( hWnd, str, lpCaption, uType);
apiHook.StartHook();
return nRet;
}