OSX下调试Flash插件

其实无论调试什么,都会发现lldb的功能朴实的让人心急如焚。比如Windows调试器基本都会自动记录上一次的断点信息,每次调试时根据模块位置重新下好端点。

lldb可能天生就是为源码调试准备的,一旦没有源码,根据模块名+偏移的下端点方式它是无论如何都不能识别。好在它提供了python接口,方便开发调试插件弥补自身的缺陷。

所以与其说lldb是个调试器,不说它是个SDK,只有基于它开发出来的图形调试器才具备实用性。

那就来看看一些在Windows调试时不值得一提的简单操作,在lldb下该如何达阵

附加Flash进程


用Safari打开指定页面后,执行以下脚本lldb就会附加到包含Flash的进程上了

pid=$(ps aux | grep WebKit.Plugin | grep 64 | awk '{print $2}'|sort| tail -1) 
lldb -p $pid

搜索内存中的指定常数


由于没有查看memory layout的命令,只能借助vmmap,先找到比如malloc的内存区域,然后再逐一生成查找命令。以下命令用于从WebKit找到HeapSpray的特定字符:

pid=$(ps aux | grep Build | grep web | awk '{print $2}' | sort | tail -1)
vmmap $pid | grep "WebKit Malloc " | grep "-" | awk '{print $3}' | awk -F '-' '{print "memory find -e '$1'","0x" $1, "0x" $2}

在模块固定偏移下断点


这个听起来是最稀松平常的任务了,比如打算在Flash的0x78A4C0偏移处下一个断点,而且要在每次lldb附加后自动完成。首先要编写一个lldb的插件,完成Flash模块基地址的查找和断点地址的计算,最后下断点:

import lldb
import shlex
import optparse
import time

def obreak(debugger, command, result, dict):
  command_args = shlex.split(command)
  parser = create_obreak_options()
  try:
    (options, args) = parser.parse_args(command_args)
  except:
   return
  if len(args) > 0:
    offset = int(args[0], 16)
  else:
    offset = 0x78A4C0
  target = debugger.GetSelectedTarget()
  base = 0
  for mod in target.modules:
    if mod.file.GetFilename() == "FlashPlayer-10.6":
      for sec in mod.sections:
        if sec.name == "__TEXT":
          base = sec.get_addr().load_addr
  address = base+offset
  target.BreakpointCreateByAddress(address) 

def create_obreak_options():
  usage = "usage: %prog -f offset"
  description='''break on offset_belongs_to_Flash'''
  parser = optparse.OptionParser(description=description, prog='obreak',usage=usage)
  parser.add_option("-f", "--offset", dest="offset", help="break on certain offset of Flash", metavar="OFFSET")
  return parser

def __lldb_init_module (debugger, dict):
  parser = create_obreak_options()
  obreak.__doc__ = parser.format_help()
  debugger.HandleCommand('command script add -f %s.obreak obreak' % __name__)

但这还不够,还需要在.lldbinit.rc文件中增加一些辅助代码,保证上述插件的自动执行

command script import ~/path_to_obreak.py
target stop-hook add -o "script '--loading script--'"
target stop-hook add
obreak
target stop-hook disable 2
target stop-hook disable 3

OSX下调试WebKit

Safari的解析和渲染引擎WebKit是开源的项目,并提供了很多脚本方便调试。在OSX下分析一个漏洞还是头一回,用到的技巧大都取自WebKit官方的一篇JS引擎漏洞分析

准备环境


首先当然是要下载Webkit的源码,以前写过如何在Ubuntu环境里下载编译WebKit,当时只考虑了最新版本,所以直接从官网下载代码压缩包。

但如果是分析漏洞,一般要根据testcase的描述找到对应版本的WebKit,然后用svn和git下载指定版本的代码。

官方的建议的命令是

svn co -r 200796 http://svn.webkit.org/repository/webkit/trunk webkitDir

由于我查看的testcase信息来自于WebKit的Github镜像,所以用git下载的源码。

不过国内访问git的速度实在不怎么样,最后就用VPS从美国先git clone好整个源码树,然后压缩传回本地

根据testcases找到对应的branch编号以后,再切换过去

git reset --hard 6711d17

原理分析


为了尽快分析清楚漏洞的原理,当然要直接调用WebKit去解析testcase,先编译debug的程序:

./Tools/Scripts/set-webkit-configuration --asan
./Tools/Scripts/build-webkit --debug

然后就可以使用DumpRenderTree直接调用编译好的WebKit来解析引发问题的testcase了

VM=WebKitBuild/Debug/ && DYLD_FRAMEWORK_PATH=$VM lldb $VM/DumpRenderTree LayoutTests/js/regress-155776.html

真实环境下的调试


一旦问题分析清楚,必须回到Safari下才算是真正的利用过程起点。编译release版的程序:

./Tools/Scripts/build-webkit --release

然后使用提供的辅助脚本,就能自动将WebKit库注入到浏览器中,使Safari运行在刚刚编译好的WebKit之上:

./Tools/Scripts/run-safari