Page 18 - 《软件学报》2024年第6期
P. 18

2594                                                       软件学报  2024  年第  35  卷第  6  期


                    • 轻量快速. 该工具应该尽可能轻量化, 可以以较快的速度分析出结果, 提升效率.
                    • 适用性强. 该工具应该尽可能独立于           Go  的编译工具链, 可以支持不同       Go  版本编译器生成的二进制, 支持在
                 未修改编译运行时的原生         Go  二进制上进行检测.
                    • 低误漏报. 有效降低误报率可以大大减少人工筛选漏洞的时间. 同时, 减少漏报率可以确保该工具可以有效
                 发现潜在的漏洞.
                    为达到上述设计目标, 对        DBI-Go  的设计的主要思路是结合       Go  语言的特性来快速原型化. 整个设计中的关键
                 主要聚集于两点: (1) 程序分析方式的选择; (2) 如何结合并利用第 2 节抽象并总结出的违例判定规则.
                    • 分析方式的选择. 目前学术界已经开发了多种工具                [17,18,33,34]  来识别  Go  应用程序中的各类漏洞, 主要使用两
                 种分析技术——静态分析和动态分析. 静态分析无需执行即可分析                      Go  源代码. 然而, 静态分析在识别方面的覆盖
                 范围有限. 此外, 由于不精确的指针分析, 静态分析可能会引入许多误报或漏报. 当在实际的 Go 应用程序中进行
                 大范围分析时, 这种不精确性会迅速累积. 因此, 单纯的静态分析难以满足                     DBI-Go  的低误漏报的设计目标.
                    现有的动态分析通过额外的运行时信息监视                 Go  程序的执行, 可以减少误报和漏报. 但是这些动态方法需要
                 修改  Go  编译器或运行时来收集必要的数据, 与           Go  的编译运行时强耦合. 当       Go  的编译运行时发生改变时, 这些
                                                    插桩信息
                 动态方法很容易失效. 这种要求很大程度上阻碍了这些工具在实际生产环境中的                        Go 应用上的使用, 也不符合       DBI-Go
                 的适用性强的设计目标.
                    在 Go 应用程序的二进制代码上进行动态分析可以摆脱这些问题, 既可以在运行时监视 Go 程序的执行, 又无
                 需修改 Go 的编译运行时. 因此, DBI-Go 以动态二进制插桩作为主要的分析方式.
                    • 如何利用规则     1  和规则  2. 确定了程序分析方式之后, 就可以着手设计            DBI-Go. 第  2  节抽象总结的规则   1  和
                 规则  2  仅提供了判定的理论. 若要使用规则          1  和规则  2, 通过观察其形式, 不难发现, 在二进制代码上使用该规则
                 必须考虑以下两个关键问题.
                    (1) 如何识别存储指针的指令        store addr dst , addr: 这类指令改变内存之间的引用关系, 潜在可能违背逃逸不变式.
                    (2) 如何获取 Go 运行时    Goroutine 的栈信息: 由第  1.1.1  节可知, Go 程序执行期间 Goroutine 的栈地址区间不
                 是一成不变的, 因此需要有途径准确获取当前时刻的栈信息.
                    这两个关键点也是程序分析的难点. 第 3.2 节将介绍使用静态分析识别写入指针的                          store 指令的挑战以及我
                 们的解决方案. 第 3.3 节将介绍如何从较为封闭的 Go 运行时中获取 Go 程序运行时 Goroutine 的栈信息.
                    DBI-Go 的整体架构如图      6  所示. DBI-Go 主要由两个组件组成.

                                                                                      规则1: 栈->栈外
                                                                     Go ABI规范        规则2: 浅栈->深栈
                                                                          先验知识      先验知识
                           StoreFinder        bb1 cmp1               Go运行时栈     Go运行时    运行时
                              Store指针的       bb2      bb3             信息识别       栈信息     验证器
                              控制流识别          store   ca11
                                              bb4
                                               回调函数的插桩                                              Log
                              函数、指令流                                 程序运行信息             验证信息
                                                                                                整理  文件
                            反汇编信息                                   运行时                         输出
                            及符号信息        插桩信息                        信息               StoreValidator
                       输入                                       程序
                  Go可执           反汇编API                        开始执行  程序运行时信息API Pin
                  行程序                     Pin     插桩API

                                                   图 6 DBI-Go  整体架构

                    (1) StoreFinder: 它使用静态分析提取 Go 二进制程序中存入指针的 store 指令, 并在二进制代码上插桩 (第
                 3.2 节).
                    (2) StoreValidator: 它以运行时回调函数的形式识别违反 Go 逃逸不变式的内存引用漏洞, 并输出错误日志信
   13   14   15   16   17   18   19   20   21   22   23