过年前打算入手一台手提,挑选对比了不少型号,终于买了一台 HP541 手提,主要是看上它价格比较低廉,不过有着还算靠谱的性能,特别是 IO 性能非常令人满意。验货时已经觉得它外表非常普通,而且快捷键非常少,很明显连触摸板的控制键都没有,不过那时对这个也不太在意,反正基本上都是用鼠标操作,安装完触摸板驱动后还把它禁用了,免得打字的时候拇指不小心碰到它导致误操作。
清明节放假三天回了三水,因为家里有无线路由器,因此可以休闲地坐在大厅里一边看电视一边把手提放在大腿上上网,不过比较苦闷的每次从电脑桌上拿到大厅里的时候都要先打开触摸板控制界面,然后将触摸板启用,之后才能将鼠标拔掉,否则的话就出现鼠标拔掉了但触摸板也用不了的尴尬场面。
虽然 HP541 的模具有局限,不过山人自有妙计,我想到可以用 Windows 的组合键来控制触摸板的开关。大部分的触摸板都是使用 synaptics 的驱动的,于是到 找了 synaptics 的 sdk 和 samples 。打开 samples 看了一下,只有 com 形式的调用,并非驱动级的调用。当时也没太在意就兴冲冲地建了一个 mfc 工程打算丰富一下它的功能,至少可以随 windows 启动、最小化时显示任务栏图标和通过组合键来控制它的开关吧。
花了半天的时间把工具写好并测试过没什么问题了,但却发现了一个致命的弱点,那就是务必要让 SynTPEnh.exe (安装好 synaptics 驱动后随 windows 启动的进程)先运行,否则采用 synaptics sdk 里那几个 samples 的做法是无法初始化和加载 synaptics 的 com 指针的,用 Ollydbg 调试了一下 SynTPEnh.exe ,发现原来 SynTPEnh.exe 会在运行时通过 LoadLibrary 加载 %Winroot%/System32 目录里的 Syntcom.dll 的,看来这个才是正主。我的原意是用我写的这个工具来替代 SynTPEnh.exe 的,但现在如果要两个进程同时存在,这样多恶心啊。
现在星期一至星期五的晚上一般是逛街和散步,就算用电脑也只是玩一下游戏,因此一直到这个周六才继续搞。增加全局快捷键一般是用键盘钩子的,钩子是一定要通过 dll 来加载的。但怎样安装这个钩子呢?想了很多种方法,想过使用创建远程线程的方式将钩子注入到 SynTPEnh.exe 中,也想到过用注册表来注入,但都否定了,因为这些方法都新开一个进程或线程来安装钩子,最终想到修改 SynTPEnh.exe ,将安装钩子的代码插入到 SynTPEnh.exe 的某个地方里。
步骤如下:
1 建立一个名为 HOOK 的 vc dll 工程, dll 导出一个函数 InitializeHook ,在此函数里先初始化 synaptics 的 com 指针,然后使用 SetWindowsHookEx 安装低级键盘钩子;在 dll 的退出函数里卸载钩子。
2 在钩子回调函数里检测是否设定的组合键(这里是 Win 键和加号键的组合),如果是的话就将触摸板禁用 / 启动。
3 由于要在 SynTPEnh.exe 的某处插入 InitializeHook 这个函数,因此要增加 SynTPEnh.exe 的空间,切记新开辟的空间要以 DWORD 对齐。还需要修改 SynTPEnh.exe 的导入表,将 HOOK 的 InitializeHook 导入到 SynTPEnh.exe 里,当然你也可以显式地通过 LoadLibrary 在 SynTPEnh.exe 里加载这个 dll ,不过那要写多几行代码了。这里建议使用 Topo 和 Stud_PE 这两个工具,不但可以极大地增加你对 PE 格式的了解,而且用起来非常方便。
4 使用反汇编工具,例如 OllyDbg ,将 call dword ptr [5020F9] 这个指令写到 SynTPEnh.exe 文件里的某个地方。一开始我打算修改 SynTPEnh.exe 的入口点,先执行 call dword ptr [5020F9] 这个指令,然后再跳回原来的入口,但发现这样行不通,因为此时 SynCom.dll 还没加载呢。经过一轮的调试逆向摸索,发现 SynTPEnh.exe 除了主线程外,还新开了 3 个线程和 1 个定时器,分别用作加载任务栏图标、加载驱动和响应用户的触摸使任务栏图标活动起来。我分别以 CreateThread 和 Shell_NotifyIcon 等 Win32 API 作断点观察了很久,将 call dword ptr [5020F9] 这个指令放到线程创建完毕或者图标创建完毕之后调用,但还是失败,总是无法初始化 synaptics 的 com 指针。最后出动到 IDA Pro ,将 Call 指令放到 WinMain 函数的消息泵前面,结果还是饮恨,代码如下:
sub_4310E0((int)&v18);
//Call 指令放在这里
PostMessageW((HWND)*(&dword_4781C0 + 56), 0x47Fu, 0, 0);
while ( GetMessageW(&Msg, 0, 0, 0) )
{
if ( !*(&dword_4781C0 + 58) || !IsDialogMessageW((HWND)*(&dword_4781C0 + 58), &Msg) )
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
}
5 此路不通,而且我调试了半天也没发现到底是调用了 SynCom.dll 里那个函数来初始化驱动的。我想到一个比较折衷的方法,在钩子的回调函数里发现用户是按下了预设的组合键时(默认是 Win 键和加号键),先检测一下 SynAPI 的 com 指针实例化是否成功,如果还没实例化则先实例化。看代码:
void InitializeSynDevice ()
{
if ( bFoundDevice == FALSE )
{
CoInitialize ( NULL ); // 这里是关键,应该将这行移到 DLL 的入口。
if ( CoCreateInstance ( _uuidof ( SynAPI ), 0,
CLSCTX_INPROC_SERVER , _uuidof ( ISynAPI ), ( void **) & pAPI ))
{
//com 初始化失败
AfxMessageBox ( _T ( "Fail to Initialize Com" ));
return ;
}
// 初始化 synapi
if ( pAPI -> Initialize () == SYNE_FAIL )
{
AfxMessageBox ( _T ( "Fail To Initialize SynApi" ));
bFoundDevice = FALSE ;
return ;
}
long lHandle = -1;
// 查找设备
if ( pAPI -> FindDevice ( SE_ConnectionAny , SE_DeviceTouchPad , & lHandle ) || pAPI -> CreateDevice ( lHandle , & pDevice ))
{
AfxMessageBox ( _T ( "Find Device is failing" ));
bFoundDevice = FALSE ;
return ;
}
bFoundDevice = TRUE ;
}
} 结果令我大跌眼镜,竟然还是不行。每一次按 Win 键和加号组合键,总是提示: Fail to Initialize Com 。看看时间,是时候跟老婆去产检了,回来后在楼下跟街坊打了一会麻将,虽然胡了几把,但感觉还是很不爽也很不甘心。于是跑回楼上继续捣鼓,突然灵光一闪:当 SynAPI 还没实例化的时候,每一次按下组合键都会调用一次: CoInitialize(NULL) ,是否由于多次调用 CoInitialize(NULL) 而导致 Com 失败呢?于是将 CoInitialize(NULL) 移到 DLL 的入口,果然成功了!下面附上使用 Ollydbg 修改 SynTPEnh.exe 的截图。


发布评论