内联挂钩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;
}
};
挂接示例:
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;
}