Page 202 - 《软件学报》2025年第10期
P. 202
郑炜 等: 基于抽象语法树变异的漏洞样本生成方法 4599
位置也是有差异的. 局部变量基本上都是在函数中声明的, 它们所在的位置都会在函数节点的子节点 CASTCom-
poundStatement 中, 而全局变量所在位置是在类里面以及函数之外, 所以在该变异过程中会将函数中的局部变量
节点及其子树进行剪切, 将原本的局部变量变异成与函数节点为兄弟节点的全局变量. 局部变量和全局变量在抽
象语法树中的名称和位置以及变异前后的变化都如图 5 中 (a) 和 (b) 过程所示.
CASTTranslationUnit
CASTFunctionDefinition CASTSimpleDeclaration
char* data (b)
CASTCompoundStatement CASTSimpleDeclSpecifie char CASTDeclarator* data
CASTPointer* CASTName data
CASTFunctionCallExpression
CASTDeclarationStatement
CASTIdExpression free CASTExpression data
char* data (a)
CASTSimpleDeclaration
CASTName free CASTName data
char* data
CASTSimpleDeclSpecifie char CASTDeclarator* data
CASTPointer* CASTName data
图 5 抽象语法树的局部变量全局化操作
该算子适用场景: 用于需要将局部变量提升为全局变量的场景, 以便在不同作用域之间共享变量, 模拟复杂的
变量使用场景. 该算子主要目的: 通过将局部变量变为全局变量, 增加变量的生命周期和作用域, 提升漏洞样本的
复杂性. 该算子输入内容为原始代码的 AST, 输出内容为局部变量转换为全局变量后的 AST.
(3) SD 变异算子
SD 变异算子的变异规则为删除语句, 这里默认为只删除一条语句的变异操作. 在代码中删除语句的操作一般
为删除某一行, 这种操作比较容易产生错误的变异体, 比如在 for 循环语句中, 循环体会有很多语句, 而在按行的
方式进行删除时可能恰好把 for 所在的那一行删除了, 那么这个变异体就是无法编译的无效变异体. 在抽象语法
树中, 删除语句是按照语句节点来删除的, 语句节点作为根节点, 它的子树中包含了该语句节点的全部信息, 在删
除语句节点时, 会将以语句节点作为根节点的子树都给删掉. 而 for 循环语句节点的子树包括循环体里的全部内
容, 所以在抽象语法树层面删除 for 循环语句时会将整个 for 循环里的内容全部删掉. 选定需要删除的点主要依据
以下几个规则: 优先选择删除那些在语法树中独立存在且对周围代码影响最小的语句, 如独立的函数调用或变量
声明语句; 优先删除非控制流语句, 如简单的赋值语句或函数调用语句, 避免直接删除控制流语句如 if、for 等; 删
除的位置应该避免影响代码的主要逻辑结构, 如避免删除主函数中的核心逻辑或关键路径上的语句; 在满足以上
规则的前提下, 可以引入一定的随机性来选择删除的点, 增加变异样本的多样性. 虽然删除普通的语句也有可能造
成代码出现问题, 但从抽象语法树层面进行删除变异产生的无效变异体会比从代码层面变异少很多. 删除子树的
效果如图 6 所示, 图 6 将 free(data) 左侧深色背景的 sem.wait() 子树进行了删除.
该算子适用场景: 用于需要删除语句的场景, 尤其是在简化代码逻辑或去除冗余代码的情况下. 该算子主要目
的: 通过删除特定的语句, 减少代码复杂度, 同时可能引入新的缺陷. 该算子输入内容为原始代码的 AST, 输出内
容为删除目标语句后的 AST.
(4) LCR 变异算子
LCR 变异算子替换逻辑链接符的规则是将代码中的逻辑运算符“&&”换成“||”, 或者将“||”换成“&&”, 同时将“!”

