作为一个安服仔,代码审计是一项必备的技能。说好听点是 code review,说直白点就是看代码。说起代码审计这件事,大家都比较关注 source、sink、漏洞模式,而对于代码审计的工具却谈及甚少。因此,本文就来抛砖引玉,谈谈笔者自己的经验。
写在前面
看代码和写代码有很大的一点不同,后者通常对于准确的代码补全是刚需,而看代码往往只需要跳转定义、交叉引用等一些基本功能。对于大部分人而言,看代码和写代码的一个相同之处就是使用 IDE 来看。
具体代码审计的习惯往往是因人而异的。有的人喜欢用 JetBrains 看,有的人喜欢用 VSCode 看,有的人喜欢用 SourceInsight 看,还有的人喜欢用 Notepad 来看。
此外,有一些通过本地构建代码索引并从浏览器阅读代码的方案,比如:
- https://github.com/oracle/opengrok
- https://github.com/livegrep/livegrep
而对于一些大型的代码工程,通常官方或者社区也构建了一些在线的代码阅读网站,比如:
- https://cs.android.com/ - Google Android 源码
- https://source.chromium.org/chromium - Google Chrome 源码
- http://aospxref.com/ - Android 源码,基于 opengrok
- https://elixir.bootlin.com/linux/latest/source - Linux 源码
各种阅读代码的方法都有它们的优点,笔者大部分也都尝试过,但大部分时间还是喜欢用 VIM 来看。即便切换到其他 IDE,也习惯性先装个 VIM Binding 插件,总感觉不用 HJKL 移动就难受。
虽然笔者是 VIM 爱好者,但并不排斥其他的 IDE。对于一些依赖完整的项目,比如 Java Maven 工程,也会优先使用 IDEA 去进行阅读,充分利用现代 IDE 的语言支持。
示例
VIM 的好处在于其本身只是一个文本编辑器,而不是完整的 IDE。很多时候我们审计的代码只是一些零散的 Broken Code,比如泄露的 iBoot 源码、某些 Jar 包反编译出来的代码等,面对这些代码很多 IDE 都无法做到完整的语言支持,进而也退化成了一个只能依赖 Ctrl F 的文本编辑器,既然如此还不如选择自己喜欢的。
如图是最近刚放出来的 MS-DOS 4.0 的源代码,虽然都是汇编,但使用 VIM 依然可以识别出大部分的符号,并且很方便搜索跳转:
MS-DOS 4.0 源码
后文会简单介绍一下实现的细节。
设计思路
作为一个笔者自用的代码审计小工具,一开始就有比较简单的设计目标:
- 主要在 macOS 和 Linux 桌面环境和命令行环境使用,支持在服务器中使用;
- 支持大部分 VIM 衍生应用,比如 NEOVIM、VIM-GTK、MacVIM 等,支持自定义快捷键;
- 支持对大部分编程语言构建索引方便跳转,对于冷门的语言也支持快速的搜索功能;
- 实现对漏洞扫描工具输出的集成,比如 semgrep、CodeQL 以及 SARIF 的支持;
对于前两个目标,我们只需要实现一个简单的 VIM 插件,并使用环境变量去触发这些快捷键的开启。因为代码审计本身大都只需要在“只读”模式下,因此很多 VIM 的编辑命令可以腾出来用作其他功能。
对于语言支持,目前选用的是 ctags 和 cscope 卧龙凤雏两兄弟。尽管年代久远,但实际效果还是不错的,许多商业工具实际上也是在此基础上进行优化。
对于工具集成,主要使用的是 VIM 的 quickfix 功能,即前面图片中下方区域。只需要针对特定工具的输出定义 errorformat 即可显示。然后使用快捷键映射 cnext/cprev 可以快速的在结果中进行跳转。
依赖
要实现上面的功能,我们首选需要引入一些社区中的优秀插件作为依赖:
- Plug ‘vim-scripts/taglist.vim’ - 用于在左边显示符号信息;
- Plug ‘skywind3000/asyncrun.vim’ - 在 VIM 中异步运行命令;
如果只需要模糊搜索的支持,也可以引入神器 FZF:
- Plug ‘junegunn/fzf’
- Plug ‘junegunn/fzf.vim’
以及一些常用的 SHELL 命令行工具:
- ctags - Universal Ctags;
- cscope - 查找代码定义,符号;
- ripgrep - Rust 实现的 grep 搜索;
- fzf - 模糊搜索;
构建索引
通常使用 ctags 和 cscope 构建索引的过程如下:
代码语言:javascript复制cd src
ctags --fields= l
find . -name "*.c" -o -name "*.cpp" -o -name "*.h" > cscope.files
cscope -b -q -k
不过这会在源码目录生成 .tags
和 cscope.out
等索引文件。虽然我们可以指定在其他地方生成数据库,但是每次都执行一遍总归是比较繁琐,因此笔者写了一个简单的 Python 脚本用于管理代码对应的数据库。
由于数据库保存到非标准的位置,那么就需要在 VIM Script 中指定对应的 cscope 和 ctags 路径,顺便也就定义一些快捷键了。
这么一来二去,也就有了这个项目:audit.vim。
audit.vim
该项目定义为一个基于 VIM 的轻量级的代码审计工具,为了尊重每个人不同的快捷键习惯,因此采用了自定义安装的方式,即只需将 plugin/audit.vim
放到 $HOME/.vim/plugin
中,额外的插件可以根据个人需求进行安装,比如使用 VimPlug 的可以直接把 Plug
写到自己的 vimrc 中。
配套的 avim.py
脚本用于管理代码的索引文件:
$ avim.py -h
usage: avim.py [-h] [-g] {make,rm,info,open,lv} ...
lightweight code audit system with vim
positional arguments:
{make,rm,info,open,lv}
sub actions
make create new audit session from current directory
rm remove audit session
info show info of audit sessions
open vim wrapper to open files
lv live grep without tags and cscope
options:
-h, --help show this help message and exit
-g use gvim instead of vim
索引文件统一放在 $HOME/audit.vim
目录中,防止污染目标源码。同时 open
会以 READ-ONLY 模式打开 VIM 并设置好对应的 ctags 和 cscope 路径。目前为了防止审计超大型代码时候引入过多的无效文件,使用的是文件后缀白名单模式。虽然在 tag 生成方面可能有所遗漏,但搜索还是全量的,在实践中效果还算不错。
如果你平时审计过程中有沉淀过一些漏洞模式,比如 semgrep 规则,那么可以很容易使用 AsyncRun 的方式去进行快速扫描和漏洞验证,感兴趣的可以自行尝试。
最后
好了这就是本期灌水的所有内容了,你们一般都习惯用什么方式来进行代码审计呢,快点在评论区跟小编一起聊聊吧!(逃