我以前用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   =  ;

//让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();
::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, );
} 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 ; 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++再也捕捉不到界面的任何东西了.

阻止SPY++类似的程序捕捉软件窗口的更多相关文章

  1. QT添加程序图标及窗口图标

    程序图标 材料准备 图标文件:*.ico文件,存放在源文件同一目录下,如"myapp.ico" 写入图标 向*.pro文件中,独立一行写入"RC_ICONS = *.ic ...

  2. js捕捉IE窗口失去焦点事件,判断离开页面刷新或关闭的方法

    js捕捉IE窗口失去焦点事件,判断离开页面刷新或关闭的方法 javascript如何捕捉IE窗口失去焦点事件window.onblur = function(e) { //you code}; 弹框的 ...

  3. WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

    原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...

  4. C# WinForm 应用程序 开启Console窗口

    /********************************************************************************* * C# WinForm 应用程序 ...

  5. 4.“写程序” 这个活动大多数情况下是个人行为。 我们听说的优秀程序员似乎都是单打独斗地完成任务。同学们在大学里也认识一些参加ACM 比赛的编程牛人, 他们写的ACM 比赛的程序是软件么? “写程序” 和 ”做软件“ 有区别么? 请采访这些学生。

    ACM的题库的编程都只能算做程序,不能算软件.写程序和做软件区别还是很大的.程序是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合.为实现预期目的而进行操作的一系列语句和指令.而软件是程 ...

  6. 卖萌的极致!脸部捕捉软件FaceRig让你化身萌宠

    FaceRig是一款以摄像头为跟踪设备捕捉用户脸部动作并转化为数据套用在其他动画模型上的一款软件,能够应用于一些日常的视频社交软件或网站,比如视频通话软件Skype和直播网站Twitch.FaceRi ...

  7. C# Process打开程序并移动窗口到指定位置

    process.start只是按指定的参数来运行一个程序,而这个程序自己运行起来是什么样子的就不是Process所能处理的了,不过当程序运行起来后倒是可以通过Process的MainWindowHan ...

  8. WinForm程序启动控制台窗口Console

    本文转载:http://blog.csdn.net/oyi319/article/details/5753311 2.WinForm程序和控制台窗口Console 如果你调试过SharpDevelop ...

  9. 基于NSIS脚本开发的安装程序制作软件:易量安装

    原文 基于NSIS脚本开发的安装程序制作软件:易量安装 前几天“萝卜”给我推荐了一款安装程序制作工具——易量安装. 易量安装是一款安装程序制作软件,基于著名的NSIS(Nullsoft Scripta ...

随机推荐

  1. WinForm DataGridView新增加行

      1.不显示最下面的新行 通常 DataGridView 的最下面一行是用户新追加的行(行头显示 * ).如果不想让用户新追加行即不想显示该新行,可以将 DataGridView 对象的 Allow ...

  2. 阮一峰 IaaS,PaaS,SaaS 的区别

    链接: IaaS,PaaS,SaaS 的区别 作者: 阮一峰 日期: 2017年7月23日 越来越多的软件,开始采用云服务. 云服务只是一个统称,可以分成三大类. IaaS:基础设施服务,Infras ...

  3. Java:多线程,Semaphore同步器

    1. 背景 类java.util.concurrent.Semaphore提供了一个计数信号量.通过Semaphore类,可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如 ...

  4. SharePreference 注册 registerOnSharedPreferenceChangeListener 无法回调的问题

    以前一直没有用过 registerOnSharedPreferenceChangeListener 回调方法,今天用到了,就设置了下,结果发现不起作用,因为一直没有回调. 代码: mSp = this ...

  5. Atitit 项目的主体设计与结构文档 v5

    Atitit 项目的主体设计与结构文档 v5 1. 版本历史说明2 2. 功能大概说明2 3. 实现的目标3 3.1. cross device跨设备(pc 手机 平板)3 3.2. cross sc ...

  6. 最NB的发现 LINUX 下玩teamviewer 命令行设置密码

    cd /opt/teamviewer/tv_bin/ [root@666 tv_bin]# ls desktop script teamviewerd TVGuiSlave.32 xdg-utils ...

  7. OpenStack的基本概念与架构图

    https://blog.csdn.net/zjluobing/article/details/51489325 OpenStack项目是一个开源的云计算平台,旨在实现很简单,大规模可伸缩,功能丰富. ...

  8. Java map双括号初始化方式的问题

    关于Java双括号的初始化凡是确实很方便,特别是在常量文件中,无可替代.如下所示: Map map = new HashMap() { { put("Name", "Un ...

  9. django rest_framework入门

    1.rest_framework的作用 1)可以对orm和非orm资源序列化 2)支持restful风格编程(POST,PUT,PATCH) 3)使用类视图编写API的view,而不是函数视图,类视图 ...

  10. 使用IntelliJ IDEA搭建kafka源码环境时遇到Output path错误解决办法

    kafka源码环境搭建好之后,需要在IntelliJ IDEA开发工具中以debug方式启动kafka服务器来测试消息的生产和消费. 但是在启动kafka.Kafka类中的main方法(也就是运行 k ...