导语:Code hook是用于将计算机的执行流重定向以修改软件的技术。通常来说,软件开发者是能通过hook,查看与系统进程进行交互的过程。
Code hook是用于将计算机的执行流重定向以修改软件的技术。通常来说,软件开发者是能通过hook,查看与系统进程进行交互的过程。Code hook可以执行各种各样善意和恶意的功能,包括:
修复bug 功能监控 禁用数字权限管理系统 捕获键盘事件 隐藏进程和文件(例如rootkit,它的功能是在安装目标上隐藏自身及指定的文件、进程和网络链接等信息)
防病毒行业使用Code hook来监视系统运行的潜在恶意行为,通过嵌入防护检查来保护应用程序,并通过沙盒或虚拟化来隔离恶意进程,不过Code hook还可以被攻击者使用,例如,Rootkit能持久并毫无察觉地驻留在目标计算机中,对系统进行操纵、并通过隐秘渠道收集数据的程序。Rootkit的三要素就是:隐藏、操纵、收集数据,另外攻击者还能通过hook浏览器来窃取用户登录银行的凭证或利用伪装的网上银行页面进行欺诈。
比如你去淘宝给手机充值,攻击者会将充值订单伪装成商家宝贝的订单页面,这个时候付款就中招了,目前实现这个原理的软件越来越先进,完全自动化了,根本看不出来。当你点击付款的时候,通过hook技术已经将骗子的充值付款页面打开了,同时隐藏正常的付款页面,全是自动完成的。
其实利用Code hook进行恶意行为其实很简单,就是通过重写目标函数的前几个字节来插入hook从而劫持执行流。通常,插入JMP或CALL就能转移到将执行新功能的新代码,并且根据预期的行为,可以返回到原始功能以完成其执行功能。
渗透测试
渗透测试程序在初始访问系统后执行的第一件事情之一是获取系统用户密码的hash值。虽然漏洞并不是什么时候都能利用,但如果有系统用户密码,则意味着攻击者可以任意扩展访问权限。 meterpreter shell是Metasploit框架的一部分,可以使用内置的hashdump功能获得用户密码的hash值。然而,目前安全软件开发商已经发现了这类技术并且已经实现了各种检测和防止机制。
而Code hook就是这类技术之一,反恶意软件会将Code hook插入到Windows API中以检测和防止hashdump命令的执行。
如果事情到此为止,则渗透测试也将完成。如果能够删除安全hook,则恶意软件可以逃避检测和预防措施,继续深入网络实施攻击。
目前这样的技术也确实存在。在2017年2月举办的RSA大会上的“Hacking Exposed Live”视频中,网络安全初创企业Cylance的创始人兼首席执行官Stuart McClure就演示了Universal Unhooker Attack live,视频请点击以下链接:https://www.youtube.com/embed/QZ6mEq6DQOo
下面我们将介绍如何删除内存中的hook以绕过保护机制。
Code Hook函数跳转
下图描述了Code Hook的一般情况。原始函数的前五个字节被改为JMP,然后跳转到包含用于执行新功能的hook code中。一旦hook code完成,它就会调用包含原始函数字节的trampoline,从当前codeTrampoline跳到修改后HOOK函数的地址:
其他Hook方法也可以重写表(导出地址表,全局偏移表,过程链接表)中的目标函数的地址,由原始函数跳转到将执行的新函数。
让PatchGuard保护失效
PatchGuard就是Windows Vista的内核保护系统,防止任何非授权软件试图“修改”Windows内核,也就是说,Vista内核的新型金钟罩。
用于检测Windows操作系统以收集系统行为信息的最常用方法是通过内核级hook, 实现内核级 hook 对于拦截、分析、跟踪系统内核起着致关重要的作用,通过检查参数和返回值来监控系统的行为。
然而,在具体的实践中,大多数内核级 hook都包含错误的行为,这导致系统非常不稳定,比如经典的电脑蓝屏,是微软的 Windows 系列操作系统在无法从一个系统错误中恢复过来时,为保护电脑数据文件不被破坏而强制显示的屏幕图像。
于是Microsoft发布了PatchGuard,并在2005年引入了64位Windows操作系统,以保护Windows内核的完整性。 PatchGuard能够有效防止内核模式驱动改动或替换Windows内核的任何内容,第三方软件将无法再给Windows Vista内核添加任何“补丁”,如中断描述符表,全局描述符表,系统服务描述符表等。
返回用户模式
但是PatchGuard的漏洞已被发现,甚至被恶意软件如Uroboros rootkit所利用,所以安全软件供应商必须采取保守的方法,并依赖于用户模式下的hook而不是内核模式下的hook。这是因为Microsoft可以很快更新PatchGuard来阻止恶意软件绕过。
安全软件将hook恶意软件常用的特定用户空间中的API函数。例如,安装在winsock.connect上的code hook 可以检查网站的IP和端口,并决定是应该允许还是阻止连接。安装在OpenProcess,VirtualAllocEx,WriteProcessMemory和CreateRemoteThread上的hook 组合会同时检测恶意进程注入。
然而,依赖用户模式下的hook却有有一个致命的缺点,即这些hook可以很容易地被删除。与此同时,执行攻击行为的用户模式下的hook反而增加了攻击,并引入了额外的漏洞,如enSilo研究团队所研究的那样。
解除内存中的程序
删除用户模式下的hook已经是一种众所周知的技术了。不过在过去,这些技术主要集中在通过查找已知的hook模式或签名来从特定产品去除hook。
通用的unhook技术是通过模拟Windows加载器来加载来自磁盘的每个二进制文件(DLL和EXE)的副本,并将这些副本与内存中的原始映像进行比较来解决去除hook的问题。如果存储器中的原始图像不同于副本,则确认其已被hook或修改。
执行这种技术有一些技巧:
•必须手动导入,以避免引入其他hook •API集必须解析为支持Windows 7+ •重新定位史必须使用内存中原始映像的基址,而不是副本
导入问题的解决
当加载EXE或DLL时,Windows PE加载程序负责解析导入函数的地址。通用unhook代码在将副本加载到内存时不能使用Windows PE加载程序,因此它必须手动解析导入函数的地址。 Windows提供了用于执行这些解决方案的GetProcAddress API。
然而,这个 API 函数可以被hook以提供其他hook函数的地址,这将破坏我们的导入。相反,unhook还必须通过遍历加载的模块列表并遍历导出地址表来实现GetProcAddress功能。
API设置模式
自从Microsoft引入了从Windows 7开始的API集合的概念,作为他们的“MinWin”努力的一部分,Microsoft重组和清理内部Windows体系结构。 API集是虚拟DLL,以“api-”或“ext-”开头的模块,映射到包含实际实现的逻辑DLL。
Unhook code在解析导入函数的地址时必须支持API集。例如,如果代码需要解析LoadAppInitDlls函数,它必须加载api-ms-win-core-appinit-l1-1-0.dll。
下图演示了解决API Set Schema版本2(Win 7)的虚拟DLL的过程。进程环境块(PEB)包含API集映象指示字(map pointer)lpApiSetMap,指向一个API_SET_NAMESPACE_ARRAY结构,每个数组项包含一个偏移量(注意:每个偏移量被添加到lpApiSetMap以获得虚拟地址),一旦找到匹配的模块名称,DataOffset(没有api-前缀的虚拟DLL名称的Unicode字符串)将指向一个API_SET_VALUE_ARRAY结构。这样每个条目就都包含了一个提供虚拟DLL应用的逻辑DLL的偏移名称。
比如下面这个例子,api-ms-win-core-appinit-l1-1-0.dll先是定向到kernel32.dll,然后再重定向到kernelbase.dll。
重新定位
可移植可执行(PE)文件格式通过定义IMAGE_BASE_RELOCATION表来支持重新定位地址,IMAGE_BASE_RELOCATION表由IMAGE_DIRECTORY_ENTRY_BASERELOC重定位。当加载诸如EXE或DLL的PE文件时,Windows PE加载器遍历表中的每个条目并执行必要的重定位计算。
在典型的重定位操作中,PE加载器会删除在PE可选报头中定义的映像基,并且在加载模块的基地址中添加。unhook code会执行类似的操作,但是要使用原始图像的基地址而不是副本的基地址。
Unhook操作
Unhook code会将副本与原始图像进行比较。如果检测到有任何更改,代码将用副本覆盖原始图像,从而删除该原始图像中安装的所有hook。
封装Unhook code
Unhook code被封装为反射DLL中,以提供灵活的使用。反射DLL可以被注入到meterpreter的进程中,作为线程本地存储(TLS)回调附加到任意二进制文件,或甚至被插入到具有UPX的打包二进制文件中。
Meterpreter提供了一个反射式DLL注入模块(post / windows / manage / reflective_dll_inject),用于在已经被入侵的端口上注入Unhook的DLL,并允许执行hashdump命令。接下来就是修改meterpreter有效负载以嵌入Unhook code,这样所有的meterpreter进程中,用户空间的hook都将被删除。
安全研究人员Borja Merino曾制作了一个Python脚本,用于将shellcode注入到32位Windows可执行文件中作为TLS回调,详细请参考 http://www.shelliscoming.com/2015/06/tls-injector-running-shellcodes-through.html。反射式hook DLL就非常接近shellcode,并且对tlsInjector.py脚本稍加修改一些bootstrapping shellcode,我们就能够将hook code注入任意32位Windows可执行文件作为TLS回调。
利用UPX
UPX (the Ultimate Packer for eXecutables)是一款先进的可执行程序文件压缩器,是恶意软件用于混淆和规避杀毒软件签名的利器。通过扩展UPX可以将我们的unhook 的DLL作为附加部分插入压缩包中的二进制文件,并修改解码器的stub 的结构以在跳入原始二进制之前调用反射加载器。
现在,任何人都可以以各种方式打包unhook DLL来绕过磁盘签名和规避基于行为的检测系统。
Cylance将继续探索使用unhook DLL的新方法和技术来进行深入研究。此外,Cylance刚刚在GitHub上发布了它的unhook工具:https://github.com/CylanceVulnResearch/ReflectiveDLLRefresher