内联挂钩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;
    }
};

挂接示例:

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;
}

 

你可能感兴趣的:(api)