API hook这个东西其实DOS时代就有了,那个时候叫中断表。这个的工作机理和虚函数表类似,就是一堆函数指针,每个人调用的时候调用的是最后一个更新函数指针的代码。基于DOS的32位Windows版本(例如Windows95)用这个来转移DOS程序的16位的文件系统操作到32位的文件系统驱动(当然,如果有16位的TSR和驱动,还得把控制权交回去)。在Windows下也有类似的东西,叫做SSDT。基本上这些东西被杀毒软件用来截获文件操作,被病毒用来防止自己被发现,等等。这个有个先来后到的机制,因为先下手的可以替换所有可以用来防止被替换的函数,比如防病毒软件在病毒试图加载的时候就可以判断:“啊,这个是已知病毒,我应该杀掉”,病毒在杀毒软件试图加在的时候也可以判断:“啊,这个是杀毒软件,我最好hook这个软件的系统调用,把自己隐藏起来”。
但是计算机上有两个杀毒软件的时候,就会有冲突了:函数表里面在病毒加载的时候是这个样子:对于两个杀毒软件都挂钩的函数,函数表是Windows->杀毒软件甲->杀毒软件乙(假定甲先加载,而且乙会调用被hook之前的函数),对于只有一个杀毒软件挂钩的函数,函数表或者是Windows->杀毒软件甲,或者是Windows->杀毒软件乙。这样在应用程序执行一个函数的时候,有时候执行的是杀毒软件甲的代码,有时候两个都执行。只有一层的执行还好,嵌套执行的时候,如果调用杀毒软件甲的不再是系统而是杀毒软件乙,这可能造成杀毒软件甲运行异常,例如如果杀毒软件乙做了一个沙盒,杀毒软件甲访问文件的时候路径可能就是经过杀毒软件乙虚拟的路径,而出现文件找不到、配置无法保存的情况,严重的时候甚至造成蓝屏死机。
即使不是杀毒软件,也可能因为抢占系统资源而造成冲突。比如IE的地址栏是没有API的。3721、百度搜霸、CNNIC的中文实名等工具条要抢占用户的地址栏怎么办呢,就是用自己的窗口过程替换掉IE的。如果安装了一个,在按回车的时候就不会跳到另一个的网站。当然,技术上还有一些别的冲突。这种冲突会造成用户抱怨产品不能用。3721的解决方案是在网络实名插件里面去检测用户有没有安装百度,有的话就会把百度的删除掉。这当然会引起百度抗议,最后起诉到法院,3721败诉。
那安全软件和各种浏览器又是在什么资源上造成冲突的呢?是WinInet。这个是IE的网络层,很多浏览器都自己hook了做沙盒、脚本过滤、隐私模式或者多IE版本模拟。和前面提到的一样,一个自己hook WinInet函数的软件不可能针对市面上所有其他WinInet函数的软件做兼容性测试(比如我手头就有几个软件懒得自己写文件下载,用的杀毒软件很感兴趣的URLDownloadToFile来做的下载),所以出问题的几率还会比较大,尤其是在自己hook WinInet,造成WinInet的行为和系统的不同那个的软件里面。本来只hook自己进程的浏览器可以互不干扰,不过一些安全软件会把自己加载到每个进程然后hook掉可以hook的API,这甚至会连Flash的WinInet调用也劫持掉了,造成Flash工作不正常或者这造成浏览器在安全软件运行的时候经常崩溃。然后就有做了沙盒的安全浏览器防止安全软件被加载到自己的进程,引起安全软件公司抗议,不过目前还只是在法院打口水仗,没有像浏览器工具栏公司们那样互相起诉不正当竞争的行为。更新:好像金山已经起诉奇虎不正当竞争了。
总而言之,API Hook是一种非常强大的工具,可以用来接管系统和其他软件,但是强大的能力意味着更多的责任,挂钩函数作为系统API的替代物,需要在比应用软件更多的场景下测试。遗憾的是,病毒制造者也会使用这种工具,使得需要挂钩的函数经常很多;而且使用系统未公开的行为也意味着在系统更新的时候,API Hook的软件经常需要被重新编码和测试,远期的总成本比没有API Hook的软件要高得多。除非是有Visual Assist这样的市场占有率、经验、人力和时间,通常还是开发只使用正规API的软件比较容易维护。