一. 诡异的中毒现象
在成品检验科文员办公室的一台电脑上折腾半个小时后,计算机维护部门的技术员只觉得眼皮不停狂跳,因为从刚开始接手这个任务开始,他就一直在做无用功:他随身带的U盘里引以为豪的众多维护工具包在这台机器上全军覆没,无论他直接在U盘上运行还是随便复制到哪个目录里,系统都是报告“找不到文件”或者直接没有运行起来的反应,他第一次感受到了恐惧,文件分明就好好的在眼皮底下,可它们就是“找不到”或死活不肯执行,莫非是在这台机器上被病毒破坏了?他只好打开网页尝试重新下载,但是他很快就绝望了,刚下载的查杀工具同样也不能使用。
无奈之下他只好在众多文员的期待下说出了大部分号称上门维护电脑的高手们常用的一句话,一般情况下这句话马上能让大部分用户接受残酷的现实,允许其重装系统,并为这次重装系统付出50元的价格,这句话就是:“系统文件严重损坏了,没法修了,只能重装。”
装完系统和常用办公软件后,他像一个贼似的赶紧离开了办公室,生怕多呆一会儿就会惹来什么麻烦似的,而他却不知道,“麻烦”早已在他刚才使用的U盘上安家了。回到自己的电脑前,他刚右键点击U盘,就看见鼠标忙碌的状态比平时久了点,然后托盘区里的杀毒软件和网络防火墙都消失了,他心里一慌张,赶紧运行超级巡警,系统却报告“找不到文件”,他一下子呆在了电脑前:瘟神跟上门来了……
古语云:道高一尺,魔高一丈。这句经典哲理在网络上得到了迅速的延伸应用。今年初,一种早已有之的系统调试功能被应用到病毒技术上,从而摇身一变成为恶魔的代言人,普通用户很快就面临了一场莫名其妙的病毒灾难,这就是“映像劫持”。
二. 我本将心向明月,奈何明月照……
“映像劫持”,也被称为“IFEO”(Image File Execution Options,其实应该称为“Image Hijack”,后面章节会详细提到,至少也应该称为IFEO Hijack而不是只有“IFEO”自身!),它的存在自然有它的理由,在WindowsNT架构的系统里,IFEO的本意是为一些在默认系统环境中运行时可能引发错误的程序执行体提供特殊的环境设定,系统厂商之所以会这么做,是有一定历史原因的,在Windows NT时代,系统使用一种早期的堆(Heap,由应用程序管理的内存区域)管理机制,使得一些程序的运行机制与现在的不同,而后随着系统更新换代,厂商修改了系统的堆管理机制,通过引入动态内存分配方案,让程序对内存的占用更为减少,在安全上也保护程序不容易被溢出,但是这些改动却导致了一些程序从此再也无法运作,为了兼顾这些出问题的程序,微软以“从长计议”的态度专门设计了“IFEO”技术,它的原意根本不是“劫持”,而是“映像文件执行参数”!
IFEO设定了一些与堆分配有关的参数,当一个可执行程序位于IFEO的控制中时,它的内存分配则根据该程序的参数来设定,那么如何使一个可执行程序位于IFEO的控制中呢?答案很简单,Windows NT架构的系统为用户预留了一个交互接口,位于注册表的“HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options”内,使用与可执行程序文件名匹配的项目作为程序载入时的控制依据,最终得以设定一个程序的堆管理机制和一些辅助机制等,大概微软考虑到加入路径控制会造成判断麻烦与操作不灵活的后果,也容易导致注册表冗余,于是IFEO使用忽略路径的方式来匹配它所要控制的程序文件名,例如IFEO指定了对一个名为“小金.EXE”的可执行程序文件进行控制,那么无论它在哪个目录下,只要它名字还叫“小金.EXE”,它就只能在IFEO的五指山里打滚了。
说了半天都只是纯粹的概念,那么IFEO到底是怎么样发挥作用的呢?例如有一个程序文件名为“lk007.exe”,由于使用了旧的堆管理机制,它在新系统里无法正常运行甚至出现非法操作,为了让系统为其提供旧的堆管理机制,我们需要IFEO来介入,则需执行以下步骤:
1. 确保在管理员状态下执行regedit.exe,定位到以下注册表项:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options
2. 在“Image File Execution Options”下建立一个子键,名为“lk007.exe”,不区分大小写。现在确保位于HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Optionslk007.exe下,建立一个字符串类型的注册表项,名为“DisableHeapLookAside”,值为“1”
3. 再次运行lk007.exe查看运行情况,如果真的是由于堆管理机制引发的问题,则程序得以正常运行,否则该程序问题不属于IFEO能够干涉的范围,或者需要尝试搭配其他的参数使用。
目前已知的IFEO参数有:
ApplicationGoo
Debugger
PageHeapFlags
DisableHeapLookAside
DebugProcessHeapOnly
PageHeapSizeRangeStart
PageHeapSizeRangeEnd
PageHeapRandomProbability
PageHeapDllRangeStart
PageHeapDllRangeEnd
GlobalFlag
BreakOnDllLoad
ShutdownFlags
说白了,IFEO本质是系统厂商为某些可能以早期设计模式运行的软件提供一种保全措施而设计出来的产物,并对其加以扩充形成了一套可用于调试程序的简易方案,如“BreakOnDllLoad”参数可设定在载入某个DLL时设置断点,便于程序员调试ISAPI接口;带有“Range”字样的几个参数则用于限制堆的大小等。
而里面有一个导致了今天这种局面的参数:Debugger。或许微软当初的用意是便于程序员能够通过双击某个设置了IFEO控制列表的执行体文件来直接调用调试器对其进行调试,而不用再通过繁琐的打开调试器再进行文件载入来实现调试,提高了工作效率。
为了使得IFEO能够影响到任何一个程序启动请求,NT架构中将IFEO的优先权设置得很高,基本上,当用户要求执行某个程序时,系统首先判断该程序文件是否可执行体,然后就到IFEO的入口项进行文件名配对了,直到通过IFEO这一步后,进程才真正开始申请内存创建起来。
如果系统在IFEO程序列表里匹配了当前运行的文件名,它就会读取文件名下的参数,这些参数在未被人为设置之前均有个默认值,而且它们也具备优先权,“Debugger”的优先权是最高的,所以它是第一个被读取的参数,如果该参数未被设置,则默认不作处理,如果设置了这个参数,情况就变得复杂了……
三. 罪魁祸首“Debugger”
前面一章里大家应该都了解IFEO的本质了,从实际现象来说,把IFEO直接称为“映像劫持”未免有点冤枉它了,因为里面大部分参数并不会导致今天这种局面的发生,惹祸的参数只有一个,那就是“Debugger”,将IFEO视为映像劫持,大概是因为国内一些人直接套用了“Image File Execution Options”的缩写罢,在相对规范的来自Sysinternals的专业术语里,利用这个技术的设计漏洞进行非法活动的行为应该被称为“Image Hijack”,这才是真正字面上的“映像劫持”!
Debugger参数,直接翻译为“调试器”,它是IFEO里第一个被处理的参数,其作用是属于比较匪夷所思的,系统如果发现某个程序文件在IFEO列表中,它就会首先来读取Debugger参数,如果该参数不为空,系统则会把Debugger参数里指定的程序文件名作为用户试图启动的程序执行请求来处理,而仅仅把用户试图启动的程序作为Debugger参数里指定的程序文件名的参数发送过去!光是这个概念大概就足够一部分人无法理解了,所以我们放简单点说,例如有两个客人在一起吃自助餐,其中一个客人(用户)委托另一个客人(系统)去拿食物时顺便帮自己带点食物回来(启动程序的请求),可是系统在帮用户装了一盘子食物并打算回来时却发现另一桌上有个客人(Debugger参数指定的程序文件)居然是自己小学里的暗恋对象!于是系统直接端着原本要拿给用户的食物放到那桌客人那里共同回忆往事去了(将启动程序请求的执行文件映像名和最初参数组合转换成新的命令行参数……),最终吃到食物的自然就是Debugger客人(获得命令行参数),至此系统就忙着执行Debugger客人的启动程序请求而把发出最初始启动程序请求的用户和那盘食物(都送给Debugger客人做命令行参数了)给遗忘了。
在系统执行的逻辑里,这就意味着,当一个设置了IFEO项Debugger参数指定为“notepad.exe”的“iexplore.exe”被用户以命令行参数“-nohome bbs.nettf.net”请求执行时,系统实际上到了IFEO那里就跑去执行notepad.exe了,而原来收到的执行请求的文件名和参数则被转化为整个命令行参数“C:Program FilesInternet ExplorerIEXPLORE.EXE - nohome bbs.nettf.net”来提交给notepad.exe执行,所以最终执行的是“notepad.exe C:Program FilesInternet ExplorerIEXPLORE.EXE - nohome bbs.nettf.net”,即用户原来要执行的程序文件名iexplore.exe被替换为notepad.exe,而原来的整串命令行加上iexplore.exe自身,都被作为新的命令行参数发送到notepad.exe去执行了,所以用户最终看到的是记事本的界面,并可能出现两种情况,一是记事本把整个iexplore.exe都作为文本读了出来,二是记事本弹出错误信息报告“文件名不正确”,这取决于iexplore.exe原来是作为光杆司令状态请求执行(无附带运行命令行参数)的还是带命令行参数执行的。
Debugger参数存在的本意是为了让程序员能够通过双击程序文件直接进入调试器里调试自己的程序,曾经调试过程序的朋友也许会有一个疑问,既然程序启动时都要经过IFEO这一步,那么在调试器里点击启动刚被Debugger参数送进来的程序时岂不是又会因为这个法则的存在而导致再次产生一个调试器进程?微软并不是傻子,他们理所当然的考虑到了这一点,因此一个程序启动时是否会调用到IFEO规则取决于它是否“从命令行调用”的,那么“从命令行调用”该怎么理解呢?例如我们在命令提示符里执行taskmgr.exe,这就是一个典型的“从命令行调用”的执行请求,而我们在点击桌面上、普通应用程序菜单里的taskmgr.exe时,系统都会将其视为由外壳程序Explorer.exe传递过来的执行请求,这样一来,它也属于“从命令行调用”的范围而触发IFEO规则了。为了与用户操作区分开来,系统自身加载的程序、调试器里启动的程序,它们就不属于“从命令行调用”的范围,从而绕开了IFEO,避免了这个加载过程无休止的循环下去。
从编程角度来说明“命令行调用”,那就是取决于启动程序时CreateProcess是使用lpCommandLine(命令行)还是lpApplicationName(程序文件名)来执行,默认情况下大部分程序员编写的调用习惯是lpCommandLine——命令行调用
BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes。
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
由于Debugger参数的这种特殊作用,它又被称为“重定向”(Redirection),而利用它进行的攻击,又被称为“重定向劫持”(Redirection Hijack),它和“映像劫持”(Image Hijack,或IFEO Hijack)只是称呼不同,实际上都是一样的技术手段。
讲解完Debugger参数的作用,现在我们来看看“映像劫持”到底是怎么一回事,遭遇流行“映像劫持”病毒的系统表现为常见的杀毒软件、防火墙、安全检测工具等均提示“找不到文件”或执行了没有反应,于是大部分用户只能去重装系统了,但是有经验或者歪打正着的用户将这个程序改了个名字,就发现它又能正常运行了,这是为什么?答案就是IFEO被人为设置了针对这些流行工具的可执行文件名的列表了,而且Debugger参数指向不存在的文件甚至病毒本身!
以超级巡警的主要执行文件AST.exe为例,首先,有个文件名为kkk.exe的恶意程序向IFEO列表里写入AST.exe项,并设置其Debugger指向kkk.exe,于是系统就会认为kkk.exe是AST.exe的调试器,这样每次用户点击执行AST.exe时,系统执行的实际上是作为调试器身份的kkk.exe,至于本该被执行的AST.exe,此刻只能被当作kkk.exe的执行参数来传递而已,而由于kkk.exe不是调试器性质的程序,甚至恶意程序作者都没有编写执行参数的处理代码,所以被启动的永远只有kkk.exe自己一个,用户每次点击那些“打不开”的安全工具,实际上就等于又执行了一次恶意程序本体!这个招数被广大使用“映像劫持”技术的恶意软件所青睐,随着OSO这款超级U盘病毒与AV终结者(随机数病毒、8位字母病毒)这两个灭杀了大部分流行安全工具和杀毒软件的恶意程序肆虐网络以后,一时之间全国上下人心惶惶,其实它们最大改进的技术核心就是利用IFEO把自己设置为各种流行安全工具的调试器罢了,破解之道尤其简单,只需要将安全工具的执行文件随便改个名字,而这个安全工具又不在乎互斥量的存在,那么它就能正常运行了,除非你运气太好又改到另一个也处于黑名单内的文件名去了,例如把AST.exe改为IceSword.exe。
小知识:互斥量
为了预防用户简单的更改一个文件名就使得大部分安全工具破笼而出,一些木马还同时采用了一种被称为“互斥量”的技术来彻底阻止安全工具运行。在系统里,有一类特殊的系统对象被称为“互斥量”(Mutex),它们的存在是为了减少系统开销而设,例如一些工具在运行时会检测是否已经有另一个自己的副本在运行,要做这种检测最有效率的方法就是在第一次运行时创建一个互斥量,以后再运行时检测即可,这实际上是很简单的方法,因为系统会为我们保存已经创建的互斥量,直到程序请求销毁互斥量,否则它将一直存在。这样一来,问题又出现了,如果恶意程序掌握了一些安全工具的互斥量并伪造呢?这些安全工具就会因为检测到“自身已经运行”而放弃继续执行的权利,恶意程序就没有克星了。
而那些双击时明明程序文件就在眼前,系统却报错说“找不到文件”,又是什么回事呢?这也是IFEO的另一种应用罢了,其秘诀是将Debugger参数指向一个不存在的文件位置,这样系统就会因为找不到这个调试器而无法顺利执行下去,如果系统老老实实报出“找不到调试器”的错误提示那倒还好了,但就不知道微软是出于对IFEO这个东西存在的事实掩盖还是什么苦衷,却死活不肯承认是Debugger指向的调试器不存在导致的错误,而是把已经被“变异”成为命令行参数无法进入系统创建进程机制的原执行请求作为“找不到的文件”给报了回去,于是未曾了解过IFEO的用户只能莫名其妙的看着眼前就存在而系统“不承认”的安全工具发愣了。
作者:小金 转载请勿斩首,谢谢
四. 防范“映像劫持”
好了,前面说了这么多,大概又吓得一批人开始冒冷汗了吧,我们现在就来学习如何防范和破解“映像劫持”。
判断你的机器是否被劫持
最简单的方法是逐个运行你常用的安全工具,检查是否出现“无法找到文件”或者干脆直接没了反应的,当然,执行结果和预期差别太大的也要被怀疑为劫持,例如你执行IceSword.exe反而是你的QQ运行了,那就不必我多说了。
其实只要注册表编辑器regedit.exe、regedt32.exe没有被劫持,那我们直接用它进入“HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options”这个注册表项并展开里面的子项列表一个个看下来确认是否出现Debugger参数或其他可能影响程序运行的堆管理参数,便可得知机器是否被劫持。
如果注册表编辑器被劫持了怎么办?直接改个名就能用了啊……
更简单的方法,是使用Sysinternals的Autoruns,点击它的“Image Hijacks”选项卡,即可看到被劫持的程序项了。
追查劫持来源
如果不幸你的机器上已经成为“映像劫持”的受害者,请记录好Debugger指向的程序位置,也不要试图再执行那些安全工具,首先应该尝试删除受影响的IFEO项,然后刷新注册表看看数据是否马上恢复了,如果马上恢复,则说明后台里有程序正在实时判断和写入IFEO,这时候必须拿出Sysinternals出品的注册表监控工具Regmon或类似工具,设置Filter为你正在尝试删除的安全工具的IFEO项,很快就能发现具体是什么进程在操作注册表了,然后将IceSword改名(如果已经被劫持),在它的进程列表里将相应进程终止掉。如果这个进程立即又重生了呢?再终止一次,然后迅速点击IceSword的“监视进线程创建”,你就能发现上一次捣乱的程序是什么名字了,将它记录下来,再开启一个Sysinternals的工具“Process Explorer”,在对应进程上点击右键选择“Suspend”,这个进程就会被挂起,用IceSword和它配合把相关恶意进程都挂起后,再使用IceSword的文件功能里的“强制删除”,在这个程序还没来得及反应时就把它们的本体给歼灭,这时候再返回Process Explorer里按照大小排列从最大的一个守护进程开始Kill Process即可,由于没有了映像文件存在,它们意欲重新建立木马帝国的贼心也就无法实现了。
如果查杀过程更为复杂的话,请自行参阅相关文章,这里就不再赘述了。
如此实现“免疫”?不被推荐的做法
由于AV终结者搞得人心惶惶,一时间网络上开始流传“免疫映像劫持”甚至“利用映像劫持免疫大部分常见病毒”的做法,对于这些方法的最初提供者,我相信他们的出发点是好的,只是,从严格的角度来看,这却是不可取的。
首先是“免疫映像劫持”,具体的方法是,例如免疫威金病毒“logo_1.exe”,则在IFEO列表里建立一个“logo_1.exe”项,然后设定它的Debugger参数为它自身即“logo_1.exe”,根据原作者解释,其原理是递归死循环:“当Debugger的值等于本身时,就是调用自身来调试自己,结果自己不是调试器,又来一次,递归了,就进入了死循环,也就不能启动了。”
这种方法虽然有效(最后的现象是“找不到文件”),但是它会导致系统在短时间内陷入一个CreateProcess循环和命令参数的字符串累加状态,会消耗一定的资源,最终没能执行程序是因为系统使用CreateProcess启动的实例会被它自身代替执行,从而造成死循环,而且命令行的长度是有系统限制的,到一定范围就会出错了,尤其在可以接受命令行参数的程序里,你甚至会发现硬盘狂转了好一会儿才弹出错误提示,这段时间里就是在死循环传递状态了,最终由于超过系统限制的命令行长度而导致系统传递执行请求时出错,才得以跳出这个死循环,换一个角度来看,如果系统没有限制命令行长度,那么这个操作很可能直接导致系统所有资源都消耗在这个自己反复执行自己的“调试器”上了。
至于“利用映像劫持免疫大部分常见病毒”的做法,发起号召者模仿“映像劫持”后门屏蔽大部分常用安全工具的原理,搜集了许多流行危害程序的可执行文件名加以前面提到的递归死循环方法达到目的,如果不计较前面提到的递归死循环缺点,似乎这个方法是可行的。
然而这真的可行吗?世界上存在许多与某些系统文件同名同姓的社交型后门,如位置不同而名字相同的命令提示符输入法控制程序“conime.exe”(被OSO超级U盘病毒借用名字)、重要程序Rundll32.exe被某些木马替换为自身、甚至IE浏览器主体iexplore.exe也存在被木马伪造文件名的案例,如此一来不知道多少正常的系统程序可能会被这份“免疫列表”给误杀了,我仅仅粗略浏览了一下就发现msiexec.exe居然存在“免疫列表”中,要知道这可是微软安装程序的主执行体啊……
争议话题:是否禁止IFEO列表权限?
同时,网上还流传着一个让初级用户看不懂的做法,那就是关闭IFEO列表的写入权限,具体操作如下:
执行32位注册表编辑器regedt32.exe
定位到HKLMSOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options
确保焦点在Image File Execution Options上,选择“安全”—“权限”
将出现的用户列表内所有带有“写入”的权限去掉,确定退出
这样一来,任何对IFEO的写入操作都失效了,也就起了免疫效果。这个方法对一般用户而言还是不错的,除非遭遇到一些特殊的需要往里面写入堆管理参数的程序,否则我建议一般用户还是禁止此项,从而杜绝一切IFEO类病毒来袭。
而争议正在于此,因为从长计议来看,用户很可能会遇到需要往IFEO列表里写入数据的正常程序,彻底禁止了IFEO的写入可能会导致不可预见的后果,既然如此,我们就采取折中的方法好了,使用HIPS(主机入侵防御系统)的注册表防御体系RD(Registry Defend),为我们提供一种两者都能兼顾的IFEO管理方法!
以SSM为例,首先确保RD体系模块已经开启,然后添加新监视规则,“键路径”指向HKLMSOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options,“操作”为“当更改时报警”,记得“包含值”选上,最后把“子键深度最大值”设为“3”,点击确定生成新的监视规则,然后在“规则”主界面里确保“存取”、“删除”、“写入”的操作均为疑问状态,这是表示在相关键值被进行写入操作时弹出消息询问用户,最后点击“应用设定”让规则生效,从此只需开着SSM,映像劫持就离你远去了。
五. 千篇一律的结语
道高一尺,魔高一丈,当几乎所有可能利用的启动项都被安全工具翻了个遍以后,与安全对立的技术也就不得不往上提高一个档次,于是无论什么歪门邪道的招数,只要能够被利用,哪怕它原意是好的,也会被改写定义,从这次的映像劫持事件可以看出,这个系统远远不如我们想像中那么容易被掌握,尤其对普通用户而言,这次技术的误用简直是他们的灭顶之灾!在安全技术与反安全技术斗争激烈的今天,我们用户越来越有在夹缝中生存的感觉了,当年轻松就可以得到的随便开多少个网页都不会带来一个病毒的日子早已远去,要想在这个疯狂的世界里得以保全,我们只能借助于各种工具的守护,和学习更多本来可以不用接触的安全知识,难道这真的要变成互联网的生存法则吗?
=================================
后记:
发现一个有趣的现象,在百度上搜索映像劫持,会得到一堆结果返回的网页里都有这么一句话:
映像劫持原理:
Windows NT系统在执行一个从命令行调用的可执行文件运行请求时,首先会检查这是否是一个可执行文件,如果是,又是什么格式的,然后就会检查是否存在。
这段话看着是不是莫名其妙?没头没尾的,经过我耐心查找,终于在一个角落找到了原始出处,感谢tombkeeper的撰文(节选):
Windows NT系统在执行一个从命令行调用的可执行文件运行请求时,首先会检查这是否是一个可执行文件,如果是,又是什么格式的,然后就会检查是否存在:
[HKLMSOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution OptionsImageName]
如果存在,首先会试图读取这个键值:
[HKLMSOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution OptionsImageName]
"Debugger"="debug_prog"
如果存在,就执行“debug_prog ImageName”
很明显,被流传的“原理”来自tombkeeper的某篇文章中的前一段,或许抄袭者不明白后面接着的注册表键值是什么意思,有什么重要作用,就想当然的截断了,于是,这篇抄袭不完整的断章取义文章经过一传十,十传百的散播途径后,终于成为了铺天盖地的“真理”,也难怪那么多人对IFEO感觉很好奇,因为光看那些大部分文章都是只有这么一段的,不信大家可以找找“IFEO”关键字,看看里面对“原理”的描述,真无语了,哎……