我以前用Spy++能輕易捕捉360軟件界面,除了一些應用DHTML制作的窗體.昨天我再用Spy++捕捉的時候捕捉不到了,甚至連最外圍的對話框都捕捉不到,顯然是做了類似攔截API的處理.下面我也模擬一下這種效果,讓自己的程序窗口不能被捕捉.
Spy++之類的程序一般通過API函數WindowFromPoint和ChildWindowFromPoint來獲取指定位置的窗口句柄。攔截一下WindowFromPoint函數,如果捕捉到的是自己程序的窗口,而且實施捕捉的進程不是自己程序的進程,那就直接返回NULL(這樣自己的程序捕捉自己的窗口就不會受影響).攔截API我直接用微軟的Detour庫,使用起來方便.
由於是攔截所有進程地址空間的WindowFromPoint函數,我借助於全局WH_SHELL鈎子,因此攔截操作放在一單獨的DLL項目中.先封裝一下Detour操作CInterceptSpyFun類:
////////////////////h文件///////////////////////////
class CInterceptSpyFun { private: //是否已經攔截 BOOL m_bIntercepted; public: //保存要屏蔽WindowFromPoint函數的進程ID static DWORD m_dwValidProcessID; public: CInterceptSpyFun( ); ~CInterceptSpyFun( ); BOOL IsIntercepted() { return this->m_bIntercepted; } /* * 攔截操作 * dwValidProcessID: 待屏蔽WindowFromPoint函數的進程ID * 返回攔截成功與否 */ BOOL Intercept( DWORD dwValidProcessID ); /* * 取消攔截,還原成原先的操作 */ void UnIntercept(); };
///////////////////////////cpp////////////////////////////
DWORD CInterceptSpyFun::m_dwValidProcessID = 0; //讓Real_WindowFromPoint指針指向實際上的WindowFromPoint函數地址 DETOUR_TRAMPOLINE( HWND WINAPI Real_WindowFromPoint( POINT pt ), WindowFromPoint ); /* * 自定義WindowFromPoint函數的處理 */ HWND WINAPI Mine_WindowFromPoint( POINT pt ) { //調用實際上的WindowFromPoint函數,取得窗口句柄 HWND hWnd = Real_WindowFromPoint( pt ); //獲取窗口所屬的進程ID DWORD dwProcessID(0); ::GetWindowThreadProcessId( hWnd, &dwProcessID ); if( ( CInterceptSpyFun::m_dwValidProcessID == dwProcessID ) && ( ::GetCurrentProcessId() != CInterceptSpyFun::m_dwValidProcessID ) ) { //如果窗口屬於指定的進程並且是被不是指定進程的其他進程調用WindowFromPoint訪問時,返回NULL return NULL; } return hWnd; } CInterceptSpyFun::CInterceptSpyFun( ) { m_bIntercepted = FALSE; } CInterceptSpyFun::~CInterceptSpyFun( ) { } BOOL CInterceptSpyFun::Intercept( DWORD dwValidProcessID ) { CInterceptSpyFun::m_dwValidProcessID = dwValidProcessID; //Detour庫攔截處理 m_bIntercepted = DetourFunctionWithTrampoline( (PBYTE)Real_WindowFromPoint, (PBYTE)Mine_WindowFromPoint ); return m_bIntercepted; } void CInterceptSpyFun::UnIntercept() { if( m_bIntercepted ) { //取消攔截 DetourRemove( (PBYTE)Real_WindowFromPoint,(PBYTE)Mine_WindowFromPoint ); m_bIntercepted = FALSE; } }
dwValidProcessID(要攔截WindowFromPoint函數的進程ID)需要在LoadLibrary之后,安裝鈎子之前傳遞,並且需要保存到共享節中以達到在所有的進程中數據共享的目的.
#pragma data_seg(".unspy")
HHOOK hHook = NULL;
DWORD dwValidProcessID = 0;
#pragma data_seg()
#pragma comment(linker,"/section:.unspy,rws")
HOOK句柄和dwValidProcessID 都保存到共享節”.unspy”中。
設置dwValidProcessID 的導出函數:
extern"C" __declspec( dllexport ) void SetValidProcessID( DWORD dwProcessID )
{
dwValidProcessID = dwProcessID;
}
聲明攔截類的全局變量:
CInterceptSpyFun interceptSpy;
HMODULE hDllModule = NULL; //保存DLL模塊句柄
SHELL鈎子處理:
LRESULT CALLBACK CustomShellProc (int nCode, WPARAM wParam, LPARAM lParam) { if( !interceptSpy.IsIntercepted() ) { //攔截API interceptSpy.Intercept( dwValidProcessID ); } return ::CallNextHookEx( hHook, nCode, wParam, lParam ); } extern"C" __declspec( dllexport ) void InstallHook( ) { hHook = ::SetWindowsHookEx( WH_SHELL , CustomShellProc ,(HINSTANCE)hDllModule, 0 ); } extern"C" __declspec( dllexport ) void UninstallHook() { if( hHook != NULL ) { ::UnhookWindowsHookEx( hHook ); } hHook = NULL; }
取消攔截操作應在卸載DLL的時候:
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { hDllModule = hModule; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: { interceptSpy.UnIntercept(); } break; } return TRUE; }
至此,DLL部分已經完成.在需要屏蔽WindowFromPoint函數的程序中需加載該DLL,調用DLL的SetValidProcessID,將當前的進程ID傳入,隨后安裝鈎子:
m_hInstance = ::LoadLibrary(_T("AvoidSpyLib.dll")); if ( m_hInstance == NULL ) { ::MessageBox(NULL,_T("LoadLibrary Failed"),_T(""),MB_OK); } if( m_hInstance == NULL ) return 0; typedef void (*PSetValidProcessID)( DWORD dwProcessID ); PSetValidProcessID pSetFunc; pSetFunc = (PSetValidProcessID)::GetProcAddress( m_hInstance , "SetValidProcessID"); if ( pSetFunc != NULL ) { (*pSetFunc)( ::GetCurrentProcessId() ); } typedef void (*PInstallHook)( ); PInstallHook pInstallFunc; pInstallFunc = (PInstallHook)::GetProcAddress( m_hInstance , "InstallHook"); if ( pInstallFunc != NULL ) { (*pInstallFunc)(); } //卸載鈎子 if( m_hInstance != NULL ) { typedef void (*PUninstallHook)( ); PUninstallHook pFunc; pFunc = (PUninstallHook)::GetProcAddress( m_hInstance , "UninstallHook"); if ( pFunc != NULL ) { (*pFunc)(); } ::FreeLibrary( m_hInstance ); }
全部完工,運行了一下,呵呵,和360軟件的效果一樣,Spy++再也捕捉不到界面的任何東西了.