RoPGen:通过代码风格转换获得鲁棒的代码作者归属模型

RoPGen_towards robust code authorship attribution via automatic coding style transformation

原文链接:https://arxiv.org/abs/2202.06043

介绍

源代码作者身份识别攻击方式有1. 利用对抗样本,2. 利用编码风格模仿/隐藏

目前遇到的挑战有

  • 需要探索出模仿目标作者风格的新攻击
  • 设计争对上边新攻击的有效防御,并且能够适应一系列神经网络结构

提出了RoPGen框架,利用了数据增强梯度增强

  • 数据增强:增加训练样本的数量和多样性,有两种方法
    • 模仿其他作者的代码风格
    • 不改变作者身份的条件下,对编码风格进行小幅度的干扰
  • 梯度增强:通过对CNN的梯度产生扰动,学习具有多样化表示的鲁棒DL模型

编码风格的概念

编码风格属性

  • 程序布局:代码缩进、空行、括号和注释
  • 词法属性:token(标识符、关键字、操作符和常量)、变量名平均长度、变量的数量和for循环语句的数量
  • 句法属性:程序的AST语法树,包括句法结构和树形结构
  • 语义属性:程序的控制流以及数据流,入for、while、if, else if等

利用编码风格属性作为作者身份归属鲁棒性的起点

在这里插入图片描述

  • token属性:标志符命名方法、临时变量名的使用、非临时标识符名的使用、全局声明的使用、数组/指针元素的索引方式
  • 语句级属性:定义局部变量的位置、初始化局部变量的位置、变量赋值、加减操作、用户定义的数据类型、宏、包含的头文件或导入的类、返回语句的使用、命名空间的使用、流重定向、库函数调用、内存分配malloc还是[n]
  • 基本块级属性:循环结构、条件结构、复合if语句
  • 函数级属性:嵌套符合语句最大层数,或函数的代码行数

属性的域分为穷尽的(比如循环结构的种类)以及非穷尽的(比如变量名的种类)

两个新的攻击

A = { A 1 , ⋯   , A δ } \mathcal A=\{A_1,\cdots,A_\delta\} A={A1,,Aδ}是一个作者的有穷集合,M是一个代码p到作者A的映射函数,由于攻击是黑盒攻击,对攻击者:

  • 任意查询程序p和对应的作者M§
  • M对攻击者是未知的

攻击者的目标是在保持语义的前提下将p做一些修改,其中 A s = M ( p ) A_s=M(p) As=M(p)变成 p ′ ≠ p p'\ne p p=p,且

  • 目标攻击:攻击目标是 A t , t ≠ s A_t,t\ne s At,t=s,使得 M ( p ′ ) = A t M(p')=A_t M(p)=At,即尝试模仿Alice写的代码在语义不变的条件下修改,使得模型能够将其归类于Bob写的
  • 无目标攻击: M ( p ′ ) = A u ,   A u ∈ A − { A s } M(p')=A_u,\ A_u\in\mathcal A-\{A_s\} M(p)=Au, AuA{As}

自动编码风格模仿攻击

攻击者为 A s A_s As,输入作者集合 A \mathcal A A,作者 A s A_s As写的一个程序p,和目标作者 A t A_t At的程序集合R。

在这里插入图片描述

步骤一:从R中的所有代码提取出代码风格属性。

步骤二:将R中所有的代码风格合成一个总的代码风格属性,从而获得目标作者 A t A_t At的编码风格,其中数值类型取平均值,非数值类型按照频率降序排序

步骤三:提取出p的代码风格属性,用于代码的模仿

步骤四:进行代码转换以模仿目标作者 A t A_t At

自动编码风格隐藏攻击

步骤一:从p中提取代码风格属性

步骤二:获得每一个作者 A d ∈ A − { A s } A_d\in \mathcal A-\{A_s\} AdA{As}的编码风格IA,把 A d A_d Ad当成目标作者

步骤三:为每一个 A d A_d Ad识别p中代码风格属性。

步骤四:选择作者 A u A_u Au进行代码转换,选择一个混淆概率最高的作者作为目标 A u A_u Au

RoPGen框架

在作者归属的DL框架中,输入的训练样本是一组带有标签的程序集合 η \eta η,用 P = { p k , q k } k = 1 η P=\{p_k,q_k\}_{k=1}^\eta P={pk,qk}k=1η表示,其中 p k p_k pk是程序, q k q_k qk是作者标签,输出是一个DL模型M,给定一个有限作者集合 A = { A 1 , ⋯   , A δ } \mathcal A=\{A_1,\cdots,A_\delta\} A={A1,,Aδ}和作者 A s A_s As写的程序 p k p_k pk P r ( M , p k , A s ) Pr(M,p_k,A_s) Pr(M,pk,As)表示M预测 p k p_k pk是作者 A s A_s As写的代码的概率,攻击者将修改 p k p_k pk p k ′ p'_k pk,当 P r ( M , p k ′ , A t ) = m a x 1 ≤ z ≤ δ P r ( M , p k ′ , A z ) Pr(M,p'_k,A_t)=max_{1\le z\le \delta}Pr(M,p'_k,A_z) Pr(M,pk,At)=max1zδPr(M,pk,Az)时攻击成功,一个隐藏式的攻击当 P r ( M , p k ′ , A s ) ≠ m a x 1 ≤ z ≤ δ P r ( M , p k ′ , A z ) Pr(M,p'_k,A_s) \ne max_{1\le z\le \delta}Pr(M,p'_k,A_z) Pr(M,pk,As)=max1zδPr(M,pk,Az)时成功。

下图强调了一个RoPGen框架的训练阶段,将训练原有模型 M M M成一个加强版 M + M^+ M+,输入包括:1. 训练样本 η \eta η和其对应的标签,2. 作者的一个子集 T ⊆ A T \subseteq \mathcal A TA,3. 模型M的对抗样本集合E。

  • 步骤一:通过模仿编码风格扩展训练集

    对每一个程序 ( p k , q k ) (p_k,q_k) (pk,qk),我们将 p k p_k pk按照其他 δ − 1 \delta-1 δ1个作者 A − { q k } \mathcal A-\{q_k\} A{qk}的代码风格进行修改,不修改 p k p_k pk的标签,将这些集合与原来的集合P并起来形成新的扩展训练集U,作为第三步的输入。

  • 步骤二:通过代码风格扰乱生成被操纵的程序

    首先,我们生成M的对抗样本E,对于每一个E中的对抗样本 e r e_r er,我们得到一个转化成 e r e_r er的序列 T r T_r Tr,对于P中的每一个程序 p k p_k pk,我们利用序列 T r T_r Tr生成一个被操作的程序 p k , r p_{k,r} pk,r,这回生成 ∣ U ′ ∣ = ∣ E ∣ × ∣ P ∣ |U'|=|E|\times|P| U=E×P的被操作程序。

    其次,如果不容易生成对抗样本,我们可以通过扰动程序,即改变程序 p k p_k pk中的z个属性值来生成新程序 p k 1 , ⋯   , p k z p_k^1,\cdots,p_k^z pk1,,pkz,这会生成 ∣ U ′ ∣ = z × ∣ P ∣ |U'|=z\times |P| U=z×P的程序。

  • 步骤三:训练鲁棒DL模型M+

    这一步通过在每次迭代中采样多个子网络进行梯度增强,并对模型的梯度产生扰动,来训练模型的鲁棒性,其中RoPGen用扩展训练集U作为输入,子网络利用被操纵的程序U’作为输入。 N \mathcal N N表示深度学习网络, θ \theta θ是其参数,每一次训练迭代含有以下五个步骤:

    • 前向传播和损失函数计算

      对于每一个程序和其标签 ( u , v ) (u,v) (u,v),一次前向传播的预测值为 N ( θ , u ) \mathcal N(\theta,u) N(θ,u),损失函数为 L s t d = l ( N ( θ , u ) , v ) L_{std}=l(\mathcal N(\theta,u),v) Lstd=l(N(θ,u),v),其中l表示交叉熵损失函数。

    • 采样n个子网络

      子网络 N j \mathcal N_j Nj将用于被操作的程序学习不同的表示,增强模型的鲁棒性

    • 计算子网络的前向传播和损失函数

      输入是产生小扰动的 U ′ U' U θ ω j \theta_{\omega j} θωj是子网络 N j \mathcal N_j Nj的参数,对U‘中的被一个样本和其标签 ( u ′ , v ′ ) (u',v') (u,v),计算前向传播的预测值 N ( θ ω j , u ′ ) \mathcal N(\theta_{\omega j},u') N(θωj,u),n个子网络的损失函数为
      L s u b n e t = ∑ j = 1 n l ( N ( θ ω j , u ′ ) , v ′ ) L_{subnet}=\sum_{j=1}^nl(\mathcal N(\theta_{\omega j},u'),v') Lsubnet=j=1nl(N(θωj,u),v)

    • 计算整体损失: L R o P G e n = L s t d + L s u b n e t L_{RoPGen}=L_{std}+L_{subnet} LRoPGen=Lstd+Lsubnet

    • 更新模型权重

      对于整个模型的梯度为 g s t d = ∂ l ( N ( θ , u ) , v ) ∂ θ g_{std}=\dfrac {\partial l(\mathcal N(\theta,u),v)}{\partial\theta} gstd=θl(N(θ,u),v)

      对于n个子网的梯度为 g s u b n e t = ∑ j = 1 n ∂ l ( N ( θ ω j , u ′ ) , v ′ ) ∂ θ ω j g_{subnet}=\sum\limits_{j=1}^n\dfrac {\partial l(\mathcal N(\theta_{\omega j},u'),v')}{\partial\theta_{\omega j}} gsubnet=j=1nθωjl(N(θωj,u),v)

      RoPGen的梯度为 g R o P G e n = g s t d + g s u b n e t g_{RoPGen}=g_{std}+g_{subnet} gRoPGen=gstd+gsubnet

与NLP不同之处,编码识别和生成并不像seq2seq模型,原因是NLP的数据修改了单词可能会导致语义的变化,而对于代码,修改程序常量名,或者利用等价的语句换用其他表达,所表示的语义也不会发生变化。

其次,编码识别是需要加上一些限定条件的,如果不加以限定,那么模型鲁棒性可能会很差,在语义语法都不变的条件下,可能稍加修改就可能让模型训练结果是错误的。

NLP以单词或者字母为最小单位,而代码不仅有单词层面,还有语句、语法、代码块、函数等层面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值