V8 Pwn Basics 0: Environment Build
怎么有人写完两个题了才来环境搭建…
简介
v8 是 Google 用 C++ 开发的一个开源 JavaScript 引擎. 简单来说, 就是执行 js 代码的一个程序. Chromium, Node.js 都使用 v8 解析并运行 js.
v8 的开源仓库在 这里
JavaScript 是解释语言, 需要先翻译成字节码后在 VM 上运行. V8 中实现了一个 VM. 出于性能考虑, 目前的引擎普遍采用一种叫做 Just-in-time (JIT) 的编译技术, V8 也是. JIT 的思想在于, 如果一段代码反复执行, 那么将其编译成机器代码运行, 会比每次都解释要快得多.
v8 解释和编译 JS 代码的流程如上图所示.
- 解析器 将 JS 代码转换为 抽象语法树 (AST)
- 解释器 将 AST 转换成 字节码, 并在 VM 中执行
- 编译器 将一些字节码优化编译成二进制机器码并执行
v8 的解释器叫 Ignition, 是一个 VM, 编译器叫 TurboFan. 新版还中间还有一个非优化编译器, 叫 Sparkplug.
环境搭建
编译
首先需要 depot_tools. 这个工具库是专门搞 Chromium 开发用的, 里面有一堆程序和脚本.
|
|
接着 clone v8, 并且下载工具. 可以用 depot_tools 中的 fetch
, 它会在当前目录下生成一些文件如 .cipd
, .gclient
, .gclient_entries
, .gclient_previous_sync_commits
clone 完成后进入 v8 目录, 使用 gclient sync
下载工具.
|
|
每个 commit 的工具可能是不同的, 当切换了分支后, 记得重新 gclient sync
一下.
下载完工具后, 运行 ./build/install-build-deps.sh
脚本安装依赖.
执行 ./tools/dev/gm.py x64.release
可以使用预设的选项编译 release 版本, 将 release 换成 debug 可以编译 debug 版本. 这样编译出来的文件在 ./out/x64.release
或者 ./out/x64.debug
下.
也可以自行设置编译选项, 然后编译. 用 ./tools/dev/v8gen.py $target.$version -- options
来生成 $target
架构的 $version
版本的配置文件. 如 ./tools/dev/v8gen.py x64.release
. 生成的文件会在 ./out.gn/
下的对应目录里. 更多用法可以看 官方文档.
无论是用 gm 还是 v8gen, 生成的文件中包含一个编译选项. 在 ./out/
或者 ./out.gn/
对应目录下的 args.gn
. 比如 gm 默认生成的 release 版本选项如下:
is_component_build = false
is_debug = false
target_cpu = "x64"
use_goma = false
goma_dir = "None"
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
v8_enable_verify_heap = true
一般来说用默认的选项就可以. v8_enable_object_print
是支持打印 JSObject 在 v8 中的内存表示, 调试的时候会用到. (不过一般还是先用 debug 版本调).
DCHECK
is_debug = true
编译选项会设置 DCHECK 宏, 它负责一些简单的安全检查, 如判断数组是否越界. 而题目往往编译的 release 版本, 如果在利用中有这种行为, 不会有什么影响. 但是用 debug 版本调试时会直接 assert. 不幸的是没有选项能够取消设置 DCHECK. 如果还需要在 debug 版本下调试以获得良好体验的话, 可以手动 patch 一下. 在 src/base/logging.h
中找到 DCHECK 定义的地方:
|
|
直接把 do while 中的代码给删掉就行.
调试
启动参数
d8 的一些启动参数可以支持打印信息. 比如 --trace-opt
可以打印编译优化时的信息.
natives-syntax
d8 带 --allow-natives-syntax
参数启动的话, 可以在 js 脚本中写一些调试用的函数, 这些函数通常以 %
开头, 如 %DebugPrint()
显示对象信息, %DebugPrintPtr()
显示指针指向的对象信息, %SystemBreak()
下断点等. 在 src/runtime/runtime.h
中可以找到所有的 natives syntax.
debug 版本下输出的内容较多, release 版本只有少部分信息.
gdb
d8 内实现了一些辅助调试的函数, 并且提供了 gdb 脚本, 以供在 gdb 中调用. 在 .gdbinit
source 一下 tools/gdbinit
和 tools/gdb-v8-support.py
即可.
用的较多的是 job $value
这个命令. 如果 $value
是个 带标记的指针, 则会显示 JSObject 在内存中的表示, 输出与 %DebugPrint()
类似.
其他命令可以在 tools/gdbinit
中查看.