Chrome Early Loading Framework

Chrome Early Loading Framework,顾名思义,就是Chromium浏览器启动早期执行一些事情的框架,对应的是Chrome_elf.dll模块,以下用ELF缩写代替来Chrome Early Loading Framework。最初研究ELF的初衷是想看看Chromium拦截第三方模块注入的机制有没有漏洞,目前看来ELF框架非常合理。

ELF介绍

执行时机

ELF执行的时机非常早,早于程序的入口点函数wWinMain,也早于全局对象的初始化。通常只有隐式链接动态库的DllMain函数才能在如此早的时机运行,ELF也正是基于这点实现的。另外为了确保ELF能够在程序启动的最早时机里被执行,Chromium有非常巧妙的设计。编译Chrome.exe之后,调用build/win/reorder-imports.py脚本去修改Chrome.exe的导入表,把Chrome_elf.dll模块的导入顺序设置成第一,因为程序隐式加载dll的顺序是按照导入表的顺序来执行的。这样运行Chrome.exe的时候,chrome_elf.dll会被第一个加载到进程里,然后执行Chrome_elf.dll里面的DllMain。
我们可以用Visual Studio附带的Dumpbin.exe工具来查看一个Chrome.exe的导入表。运行dumpbin.exe /imports chrome.exe 就会输出Chrome.exe的导入表:

dumpbin_chrome

如上图所示Chrome.exe的第一个导入模块就是Chrome_efl.dll。

ELF主要功能

ELF模块需要尽可能的早执行,它主要做两件事:

  • 初始化Breakpad崩溃处理逻辑。
  • 初始化拦截第三方模块注入逻辑。

Chromium中是有些崩溃可能是由于全局的对象初始化或者是wWinMain早期逻辑导致的,Breakpad只有在这些逻辑之前初始化才能捕获这些错误。目前只有ELF能够确保足够早的时机,所以BreakPad理所当然的在ELF中被初始化。
有些第三方模块加载到浏览器,会导致浏览器崩溃。为此Chromium有个拦截dll黑名单机制,根据模块的名称来拦截它注入到浏览器里。这个任务也需要非常早的时机进行,否则就不能拦住那些在早期就注入到进程里的第三方模块。

拦截第三方模块注入功能的实现

一般第三方模块要注入到浏览器,是利用系统的一些机制LoadLibrary或者是内存映射到浏览器进程里面。以LoadLibray的调用栈来讲:

归根结底,Loadlibrary和内存映射系统底层都会调用到NtMapViewOfSection这个函数。于是ELF模块对NtMapViewOfSection函数实现一个inline hook,在BlNtMapViewOfSectionImpl里面获取映射到浏览器进程里面的第三方模块名,然后根据黑名单来屏蔽这些模块的注入。

获取代码运行所在模块的句柄

Inline Hook需要知道当前代码运行所在模块的句柄。正常情况下一个动态库dll的代码运行时获取它的句柄,可以通过GetModuleHandle或者GetModuleHandleEx API来获取。这个两个API都需要把dll模块名当做参数传进去才能获取句柄值。但是dll文件可能被人重命名,所以代码里不能百分百确定当前dll的文件名。
另外一种办法是在DllMain入口函数里保存hinstDLL值。但是不一定所有的Dll都有DllMain,所以这个方法也是不一定百分百可靠。
还有种方法是调用VirtualQuery接口获取MEMORY_BASIC_INFORMATION值。MEMORY_BASIC_INFORMATION结构体里面的MEMORY_BASIC_INFORMATION.AllocationBase的值就是模块基地址,可以转换成模块的句柄。
Chromium用的是另外一种办法。Microsoft编译器会生成一个__ImageBase的伪变量,__ImageBase代表着模块的DOS头,也就是模块的起始地址。换言之,也就是这个模块的基地址,这个模块的句柄。另外这种方法还可以确定静态链接库代码被编译到模块的句柄。

Inline hook的实现

ELF的inline hook没有使用Detuors等现有的库,而是使用Sandbox工程里面自己实现的一套自己inline hook机制,其原理也是替换目标函数头部前几个字节指令,调用目标函数的时候这些替换的指令会把跳转到执行我们自己实现的函数里。Sandbox工程为了控制进程权限,已经有了一套完善的Inline Hook机制,支持从XP到Win10的各版本Windows系统,也支持x86和x64进程。Detuors只有Professional版本才支持x64进程,售价是9999.95美金。
ELF中建立一个.crthunk的段,inline hook的一部分跳转代码会在动态生成到这个段里。sandbox:: ServiceResolverThunk替换NtMapViewOfSection函数里前头几个指令,令其跳转执行blacklist::BlNtMapViewOfSection函数。

ELF的局限以及解决办法

从拦截第三方模块注入技术的角度来看,ELF是非常合理。一是ELF逻辑运行的时机最早,可以在其它第三方模块加载之前就建立防范,二是ELF在调用NtMapViewOfSection里面加拦截逻辑,系统的调用比较底层,拦截的比较彻底。

然而ELF实际中却不能完全防止第三方模块的注入。原因有以下几个:

  1. ELF根据代码中的一个黑名单来拦截第三方模块。每次更新这个黑名单都需要重新编译和发布浏览器,流程比较繁琐,耗时比较长。如果某个注入浏览器的第三方模块导致浏览器启动就马上崩溃,这么长的修复时间是我们所不能接受的。
  2. ELF根据黑名单里面的模块名来拦截第三方模块,如果某个恶意模块每次都生成一个随机文件名,那么ELF的拦截机制对于它来说就是失效的。
  3. 目前ELF拦截总是滞后于现实的情况,对于不存在于黑名单中的恶意模块,缺少一个主动拦截的机制。

对于ELF的以上的局限,目前有以下应对策略:

  1. 增加黑名单的云控机制。通过网络下发黑名单,ELF可以及时的对新情况作出反应。
  2. 可以根据文件的哈希值来拦截第三方模块,这样就可以解决文件名的漏洞。
  3. 可以根据通配符来拦截某一特征文件名的第三方模块,根据文件数字签名来拦截某公司的模块,根据模糊哈希或者其他特征码算法来拦截未知的可疑模块。

《Chrome Early Loading Framework》有1个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注