内联挂钩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]  =  { 0xB80x000x000x000x000xFF0xE00x00 };
        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;
}
原文地址:https://www.cnblogs.com/fangkm/p/1555912.html