论文阅读笔记——Rethinking Pointer Reasoning in Symbolic Execution


前言

  此博客为Rethinking Pointer Reasoning in Symbolic Execution论文的阅读笔记,本篇论文提出了一种新的符号内存处理方法,以减少符号爆炸和符号丢失的问题。本文将会以原论文的行文结构来分析本篇论文,并且对其基本情况进行了概述。以下就是本文的全部内容。


Rethinking Pointer Reasoning in Symbolic Execution

12.1、基本情况概述

  2017 32nd IEEE/ACM International Conference on Automated Software Engineering (ASE——B类会议)

12.2、摘要

  本文介绍了MEMSIGHT,这是一种新的符号内存处理方法,旨在减少对具体化的需求,从而提供更广泛的状态探索和更精确的指针推理机会。与之前的工具不同,MEMSIGHT不是将地址实例映射到数据,而是将符号地址表达式映射到数据,以紧凑、隐式的形式维护由符号地址引用的内存所导致的可能的替代状态。通过对DARPA Cyber Grand Challenge的知名基准进行初步实验调查,结果表明MEMSIGHT能够探索到先前技术无法达到的状态。

12.3、引言

  在介绍本文所解决的问题和目标之前,我们先来了解一下符号执行技术。符号执行是一种主要用于软件测试和安全领域的程序属性验证技术。通过采用符号输入而非具体输入值,可以同时探索多个执行路径,其中每个路径描述程序是对一类明确定义的输入的行为。然而,要探索的路径数量可能非常庞大,例如,在存在无限循环或要解引用的指针由符号表达式表示时。我们基于下图的简单运行示例进行讨论。
在这里插入图片描述
  该函数以数组 a a a和两个索引 i i i j j j作为输入。我们假设 a a a可以指向一个大的内存区域,可能是整个内存,并且我们对 i i i j j j没有任何约束。我们感兴趣的是消除 b o m b bomb bomb的输入,即不触发断言语句的输入(即断言成功,从而 b o m b bomb bomb的值应为 0 0 0)。先前的研究和最先进的工具通常将内存建模为具体地址和具体值或符号值表达式之间的映射。在这种情境下,处理引用内存时的符号地址有不同的可能性。
  符号执行器可以通过在当前路径约束下使用一个有效的符号表达式模型来具体化地址。例如,这种策略自然会出现在动态测试生成中,其中同时维护来自具体执行的值。然而,先前的研究指出,具体化可能无法触发程序的分支和路径。另一方面,将内存视为完全符号化将允许执行器推理所有可能的地址,方法是分叉执行以解释与表达式匹配的每个具体地址,或者通过嵌套的 i t e ite ite(即 i f − t h e n − e l s e if-then-else ifthenelse)表达式捕获地址的不确定性。
  不幸的是,如上所述的完全符号化的内存在实践中很难扩展。因此,现代执行器通常通过实现部分内存模型来在性能和健壮性之间取得平衡。在这种模型中,写操作总是具体化的,而读操作则仅在面对可管理数量的可能地址值(例如,最多 1024 1024 1024个地址值)时被建模为完全符号化的内存,否则会被具体化。
  为了简化我们示例的讨论,让我们假设涉及的内存最初被零填充,以避免从预先存在的存储中消除 b o m b bomb bomb。对 & a [ i ] \&a[i] &a[i] & a [ j ] \&a[j] &a[j]的完全具体化可能导致断言失败,除非 i i i j j j使用相同的值。部分内存模型也会对符号读取进行具体化,因为 & a [ j ] \&a[j] &a[j]跨越的内存很大。即使调用上下文信息产生了可管理大小的间隔,符号读取 a [ j ] a[j] a[j]仍会逐个考虑每个 a + j a+j a+j地址实例,导致执行器仅找到一个消除 b o m b bomb bomb的输入,即具体地址 a + j a+j a+j等于写具体化策略选择的 a + i a+i a+i值的输入。相反,完全符号化的方法将显示所有满足 i = = j i==j i==j条件的输入都可以消除 b o m b bomb bomb的属性。
  基于以上,我们明确了,本文要解决的问题为:

  1. 传统的符号执行技术对内存进行符号执行时,由于具体化参数,可能会导致符号丢失的问题
  2. 如果对内存进行符号执行时使用完全符号化的方法,又将导致符号执行要探索的状态数量可能随指数级增长,那么符号执行器可能很快耗尽空间

  为了解决以上问题,本文实现了以下几个目标:

  1. 讨论了符号指针推理设计的不同视角,即展示了如何紧凑地将值与符号地址表达式关联起来,而不是与具体地址
  2. 研究了使用分页区间树实现高效完全符号化内存的方法。此种方法被称为MEMSIGHT,此方法本能地考虑了状态合并,这是现代执行器中的主流性能增强方式,并已集成到ANGR框架中
  3. MEMSIGHT通过在著名的基准测试中进行更广泛的状态探索,揭示了以前的技术可能会忽略或使用过多资源来识别的行为

12.4、方法

12.4.1、基本版本

  作者将符号内存化 M M M建模为一组元组 ( e , v , t , δ ) (e,v,t,δ) evtδ,其中 e e e是表示地址的表达式, v v v是表示地址 e e e处的值的表达式。属性 t t t是创建元组的逻辑时间,加载操作使用它来确定在给定地址写入的最新值。为了支持内存的合并,作者考虑了反映元组有效的特定条件的谓词δ:δ通常由执行器根据要合并的状态之间的发散路径约束来计算。
  作者的符号化内存数据结构的基本版本如 A l g o r i t h m 1 Algorithm \quad 1 Algorithm1所示。要解释它是如何工作的,请再次考虑刚刚介绍的上图中的示例。为了确定是否有任何消除 b o m b bomb bomb输入,作者设置了一个符号执行器,将指针 a a a与符号值 α a α_a αa相关联,并将索引 i i i j j j分别与符号值 α i α_i αi α j α_j αj相关联。
在这里插入图片描述
  该程序对符号状态的影响如下图所示。为了跟踪逻辑时间,该状态包括一个从零开始的计时器 t t t。最初,内存 M M M包括由参数传递产生的地址值映射。为了紧凑起见,作者将元组 ( e , v , t , δ ) (e,v,t,δ) evtδ表示为 e ⟼ v ∣ t = ⋯ δ = ⋯ \left.e \longmapsto v\right|_{t=\cdots} ^{\delta=\cdots} evt=δ=,如果 t = 0 t=0 t=0,则省略 t t t,如果 δ = t r u e δ=true δ=true,则省略 δ δ δ
在这里插入图片描述

12.4.1.1、内存加载和存储

  为了执行 a [ i ] = 23 a[i] = 23 a[i]=23操作,程序首先加载变量 a a a i i i的值。 l o a d ( e ) load(e) load(e)操作( A l g o r i t h m 1 Algorithm \quad 1 Algorithm1)构建了一个 i t e ite ite表达式,试图将 e e e与先前在 M M M中分配的所有地址进行匹配,优先考虑最近的元组。最内层 i t e ite ite “ e l s e ” “else” else部分考虑了未初始化的内存位置,为了简化起见,作者在这个基本版本中将其默认设置为零。在作者的例子中, l o a d ( & a ) load(\&a) load(&a)产生的结果是 i t e ( & a = & a , α a , i t e ( & a = & i , α i , i t e ( & a = & j , α j , 0 ) ) ) ite(\&a = \&a,α_a ,ite(\&a = \&i,α_i,ite(\&a = \&j,α_j ,0))) ite(&a=&a,αa,ite(&a=&i,αi,ite(&a=&j,αj,0))),可以简化为 α a α_a αa。同样, l o a d ( & i ) load(\&i) load(&i)产生的结果是 α i α_i αi。赋值是通过 s t o r e ( α a + α i , 23 ) store(α_a+α_i,23) store(αa+αi,23)操作完成的,该操作在更新 t t t后将 ( α a + α i , 23 , 1 , t r u e ) (α_a+α_i,23,1,true) (αa+αi,23,1,true)添加到 M M M中,即 α a + α i ↦ 23 ∣ t = 1 \alpha_{a}+\left.\alpha_{i} \mapsto 23\right|_{t=1} αa+αi23t=1
  测试 i f ( a [ j ] = = 23 ) if (a[j] == 23) if(a[j]==23)执行了一个 l o a d ( α a + α j ) load(α_a+α_j) load(αa+αj)操作,该操作构建了一个 i t e ite ite表达式 v v v,选择了地址 α a + α j α_a+α_j αa+αj处的适当值。这是通过首先将址 α a + α j α_a+α_j αa+αj与最近写入的符号地址(即 α a + α i α_a+α_i αa+αi)进行匹配,然后考虑参数 & a \&a &a & i \&i &i & j \&j &j来完成的。然后,执行 “ t h e n ” “then” then分支创建一个新状态 S o t h e r = M o t h e r , t o t h e r S_{other} = {M_{other}, t_{other}} Sother=Mother,tother,其中 M o t h e r M_{other} Mother M M M的克隆, t o t h e r = t t_{other}=t tother=t。仅当 v = 23 v = 23 v=23时,才会选择此分支。

12.4.1.2、状态合并

  由于 b o o m boom boom变量的值取决于执行的分支,合并操作( A l g o r i t h m 1 Algorithm \quad 1 Algorithm1)通过将 M M M M o t h e r M_{other} Mother合并成 M M M来协调这些状态。该操作有四个参数: δ = ( v ≠ 23 ) δ = (v ≠ 23) δ=(v=23) “ e l s e ” “else” else分支的路径条件,该分支一直在处理 M M M S o t h e r S_{other} Sother “ t h e n ” “then” then分支产生的状态,而 δ o t h e r = ( v = 23 ) δ_{other} = (v = 23) δother=(v=23)是其路径条件。最后, t a = 1 t_a = 1 ta=1是两个分支的最小共同祖先的时间戳。合并通过使用 “ e l s e ” “else” else分支条件 δ δ δ更新自分支点在时间 t a t_a ta之后添加到 M M M中的所有元组(第2-3行),然后通过使用 “ t h e n ” “then” then分支条件 δ o t h e r δ_{other} δother添加自 M o t h e r M_{other} Mother t a t_a ta之后添加的所有元组到 M M M中(第4-6行)。在上面的示例中,合并状态后, M M M中存在 & b o o m ↦ 1 ∣ t = 2 δ = ( v ≠ 23 ) \&\mathrm{boom}\mapsto1|_{t=2}^{\delta=(v\neq23)} &boom1t=2δ=(v=23) & b o o m ↦ 0 ∣ t = 2 δ = ( v = 23 ) \&\mathrm{boom}\mapsto0|_{t=2}^{\delta=(v=23)} &boom0t=2δ=(v=23)的元组。
  最终,程序加载并返回变量 b o o m boom boom的值,构建了(简化的)表达式 v ′ = i t e ( v = 23 , 0 , 1 ) v^{'} = ite(v = 23,0,1) v=ite(v=23,0,1)。因此,符号执行可以得出结论,任何使得 v ′ = 0 v^{'}=0 v=0的模型,例如 α i = α j α_i=α_j αi=αj,都将解除 b o o m boom boom

12.4.2、改进

  在其最初的朴素形式中,提出的方案存在一些通用性和性能问题。故作者讨论了一些改进措施,从而设计了 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2
在这里插入图片描述

12.4.2.1、地址范围选择

   A l g o r i t h m 1 Algorithm \quad 1 Algorithm1的一个主要缺点是 l o a d load load m e r g e merge merge操作需要扫描整个内存,这可能会消耗大量的时间和空间。作者注意到,符号地址通常受到某个特定区间的限制。因此,一个更有效的方法是为每个元组 ( e , v , t , δ ) (e,v,t,δ) evtδ索引最小的范围 [ a , b ] [a,b] [a,b],这个范围包括了变量 e e e可能到达的所有值(见 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2 s t o r e store store函数的第6行)。该范围可以通过 S M T SMT SMT求解器计算得到(见 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2中的第2-3行)。 l o a d ( e ) load(e) load(e)操作可以仅扫描那些与 e e e的最小值和最大值相交的元组(见 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2的第2-3行和第8行)。

12.4.2.2、内存清理

   A l g o r i t h m 1 Algorithm \quad 1 Algorithm1在每次 s t o r e store store操作时都简单地添加一个元组。一个有用的改进是清除不再需要的旧元组:一种方法是,如果一个元组的地址与正在写入的地址“等效”( A l g o r i t h m 2 Algorithm \quad 2 Algorithm2 s t o r e store store函数的第5行),即它们对于任何可能的符号值的具体化都导致相同的具体地址,则移除该元组。

12.4.2.3、符号化的未初始化内存

  识别程序访问未初始化内存区域时可能的行为对于测试和漏洞利用至关重要。在作者的基本版本中,假设未初始化的单元值为零,这限制了分析的精度。 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2中的 l o a d ( e , v ) load(e,v) load(e,v)操作通过执行隐式存储来支持符号化的未初始化内存,如果地址 e e e不完全“覆盖”已经存在于 M M M中的地址表达式,则为地址 e e e分配一个新的符号( A l g o r i t h m 2 Algorithm \quad 2 Algorithm2的第4-6行)。
  一个微妙的问题是如何确保访问未初始化的内存地址始终产生相同的符号值。更准确地说,对于任意的两个 l o a d ( e ) = v load(e)=v load(e)=v l o a d ( e ′ ) = v ′ load(e^{'})=v^{'} load(e)=v操作,如果 γ γ γ是一个符号值的赋值,使得 γ ∣ = e = e ′ = x γ |=e=e^{'}=x γ=e=e=x,并且地址 x x x是未初始化的,则 γ ∣ = v = v ′ γ |=v=v^{'} γ=v=v。为了实现这个属性,作者使用了一种基于负时间戳的元组打破策略,该策略用于隐式存储创建的元组。需要注意的是,作者对未初始化内存的处理与约束求解器处理未解释函数的方式有相似之处。

12.4.2.4、多字节加载和存储

  本节提出的解决方案适用于 1 1 1字节内存对象。可以通过针对各个字节进行单独的 s t o r e store store l o a d load load操作,然后将结果组合来支持多字节操作。例如,可以通过连接 l o a d ( e ) load(e) load(e) l o a d ( e + 1 ) load(e+1) load(e+1) l o a d ( e + 2 ) load(e+2) load(e+2) l o a d ( e + 3 ) load(e+3) load(e+3)的结果来获得 l o a d ( e , s i z e o f ( i n t ) ) load(e,sizeof(int)) load(e,sizeof(int))。这种策略在几个符号执行器中被采用(例如,KLEE)。

12.4.3、讨论

  先前的研究暗示了某些错误只有在对写入进行符号化建模时才能揭示出来。然而,传统方法可能无法扩展。例如,Driller: Augmenting fuzzing through selective symbolic execution指出“使用相同的符号索引进行重复读取和写入将导致符号约束的二次增长或[…]存储的符号表达式的复杂性”。
  作者的解决方案提供了一种更紧凑的编码,无需显式枚举有效地址。这对于求解器来说可能是一个复杂的任务,因为需要为分叉状态或构建 i t e ite ite表达式枚举有效地址。

12.5、实现

12.5.1、ANGR

  ANGR是一个开源的用于二进制分析的框架。它提供了一个强大的与平台无关的符号执行引擎,用于分析 V E X VEX VEX字节码,并依赖于 Z 3 Z3 Z3作为 S M T SMT SMT求解器。
  ANGR采用了部分内存模型,对于符号读取,如果跨越的范围不太大,则会构造 i t e ite ite表达式。它查询 Z 3 Z3 Z3获取地址可以假设的最大和最小值,如果它们之间的差异最多为 1024 1024 1024,则ANGR将要求 Z 3 Z3 Z3枚举所有解并相应地构造一个 i t e ite ite,否则地址将被具体化。写入地址也可以选择被视为符号化,其中范围大小的默认阈值为 128 128 128
  为了减轻求解器的重复查询负担并提高效率, C L A R I P Y CLARIPY CLARIPY约束求解包实现了一些优化。为了提高可扩展性,ANGR实现了扩展的 v e r i t e s t i n g veritesting veritesting合并策略,通过分析控制流图来确定在哪些地方将代码块的效果收缩成 i t e ite ite约束是有利的。

12.5.2、MEMSIGHT原型

  作者设计了MEMSIGHT作为一个插件,实现了ANGR的SIMUVEX符号引擎所需的内存抽象,因此可以轻松地与用于部分内存建模的默认插件进行替换。由于该抽象明确考虑了合并原语,因此诸如 v e r i t e s t i n g veritesting veritesting等策略可以在MEMSIGHT上运行,无需额外的工作。
  首先要克服的实际挑战是有效地支持 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2中的范围操作。一种可能的方法是维护一个区间树,以便能够高效地检索与给定区间重叠的所有存储区间。然而应该记住,当遇到分支时,执行器通常会克隆状态以及关联的内存。为了更好地利用空间,作者因此提出了一种基于内存分页的区间树。作者将地址空间划分为页面:在页面索引之上构建的主区间树保存指向包含 M M M中元组的次级区间树的指针。每个元组都包含在正好一个次级树中。页面大小经验地确定为最小化数据结构中最大树大小。主区间树和次级区间树都使用写时复制策略进行维护,以最小化克隆需求,并促进不同状态之间的内存共享。
  另一个需要考虑的关键方面是,在符号探索中,大多数内存访问通常发生在具体地址上。将具体存储捕获到区间树中将导致维护关于许多大小为 1 1 1的范围的信息。因此,作者通过具体内存对象扩展了其表示,即将具体地址与表示值的表达式关联起来。每个表达式都带有时间戳,因此在加载操作期间,它可能与映射到符号地址的值组合。出于效率考虑,具体内存实现为一个分页哈希映射,对于页面采用写时复制克隆,类似于ANGR的默认内存实现方式。

12.6、评估

12.6.1、实验设置和方法

  作者的实验基于来自Cyber Grand Challenge(CGC)的Cromulence(CROMU)DARPA执行者团队的基准测试。实验是在一台配备有Intel Xeon CPU E5-2630 v3 @ 2.40GHz、16核和64GB内存的服务器上进行的,运行着Linux CentOS 6.7操作系统。作者将MEMSIGHT与三种不同的ANGR内存具体化策略进行比较:

  1. ANGR-CONC:它具体化所有访问的符号地址
  2. ANGR-PART(ANGR的默认策略):它具体化所有跨越大于 1024 1024 1024的读取地址和所有写入地址
  3. ANGR-FULL:它通过将读取和写入的阈值提高到无穷大来不进行地址具体化

  作者在CROMU基准测试上使用了2小时和32GB RAM,对MEMSIGHT、ANGR-CONC、ANGR-PART和ANGR-FULL进行了符号执行。为了描述内存模型的广度探索,作者测量了所探索路径的数量。为了在不同的内存模型之间进行有意义的比较,在作者的实验中,符号执行使用先进先出策略按轮次进行状态探索:只有在上一轮中的所有状态都被探索完毕后,新的一轮才会开始。因此,在任何一轮中,某些具体化策略下探索的路径集合始终是执行较少具体化操作时将要探索的路径的子集。

12.6.2、案例分析

  我们现在讨论一个实际的代码示例,展示了MEMSIGHT如何在一个情境中保持完全符号化的地址,而先前的技术则被迫将它们具体化。CROMU_00006是DARPA CGC套件中包含的一个服务,它产生随机数并为数值数据生成图表,包括条形图和火花线图。该基准测试解引用了跨越大范围地址空间的指针。例如,在下图中显示的read_data函数中,它填充了一个具有符号大小datum_cnt的缓冲区data,其中的值从输入中读取。
在这里插入图片描述
  对x86二进制代码的检查显示,动态堆栈分配uint32 data[datum_cnt](第2行)使得堆栈指针寄存器 e s p esp esp成为符号。代码稍后(第6行)在堆栈上对函数read的参数传递导致对符号esp的存储。由于程序对最大堆栈大小的先前约束,此时 e s p esp esp可以假定的可能地址范围高达 262128 262128 262128个。这触发了在ANGR-PART中的具体化,迫使符号执行在一个固定大小的缓冲区上进行推理。由于 e s p esp esp的符号范围非常大,ANGR-FULL由于资源消耗过大而无法产生结果。相比之下,MEMSIGHT保持 e s p esp esp符号化,考虑到数据缓冲区的所有可能大小进行分析。正如作者的实验所证实的,考虑到一个可变大小的缓冲区的能力影响了探索的广度,使得MEMSIGHT能够将符号执行推进到ANGR-PART无法发现的状态。

12.6.3、实验

  首先要解决的问题是:CROMU基准测试中执行的符号地址解引用有多广泛?为此,作者测量了整个分析过程中符号加载和存储的范围。这是所考虑的基准测试的结构属性,与所选择的内存模型无关。下表的左半部分报告了对具有大于1的范围大小的符号地址进行的内存访问总数( # C O N C R \# \quad CONCR #CONCR),以及贯穿执行过程中由加载和存储操作访问的符号地址的范围的最大大小。请注意,对于某些基准测试,范围远远大于在部分内存模型中实际可承受的阈值。
在这里插入图片描述
  第二个问题是:具体化在多大程度上限制了状态探索?上表的右侧部分比较了作者考虑的内存模型所探索的不同控制流路径数量。为了进行直接比较,作者在所有内存版本的相同探索深度 K K K下,对同一基准测试的路径数进行了快照。首先观察到完全具体化(ANGR-CONC)可能会限制可探索路径的数量,这印证了Precise pointer reasoning for dynamic test generation、Unleashing Mayhem on binary code中报道的结果。此外还注意到,对于一些展示出符号地址范围较大的基准测试,部分内存模型(ANGR-PART)未能捕获所有可探索的路径。由于资源需求过大,显式完全符号化内存(ANGR-FULL)无法完成。相反,MEMSIGHT支持全面的探索,它可以访问执行状态空间的更大部分。

12.7、相关工作

  许多项目已经解决了模拟符号指针的问题。EXE的内存模型允许通过将指针模拟为数组对象的偏移引用来进行符号读取,对于多次指针解引用,使用具体化,而对于符号写入,则没有详细讨论。KLEE实现了类似的策略,但是在指针可以引用多个对象时克隆执行状态,将指针限制在克隆中的单个对象内。
  SAGE利用动态测试生成的具体值来支持符号指针,将它们限制在相应具体值所在的内存区域内。该工作还讨论了在测试中多次指针解引用和符号写入的相关性。
  MAYHEM引入了部分内存建模,并提出了一些巧妙的优化方法,比如值集分析和细粒度的查询缓存,以减轻 S M T SMT SMT求解器在评估范围大小时的负担。
  作者的工作与Symbolic Memory with Pointers中提出的分段偏移平面模型存在几个类似之处,该模型根据数据的类型将数据存储在不同的平面中。每个平面都保存一个写记录列表,并且对于每个读操作都调用一个求解器来检查存储的表达式是否与给定的(类型化的)符号地址发生冲突。作者认为其方法更加通用,因为它不受语言类型安全性的影响,它提供了对于可扩展性非常重要的状态合并支持,并且明确考虑了未初始化内存。
  Specification of concretization and symbolization policies in symbolic execution中提出的框架用于描述符号值和地址的具体化策略,为一个有趣的研究问题提供了启示,为系统地研究具体化策略和策略调整铺平了道路。作者还相信,指针具体化策略可能会从Symbolic execution with mixed concrete-symbolic solving中提出的处理非线性约束的未解释函数的延迟具体化技术中受益。

12.8、结论

  作者相信,将符号内存泛化,使其将符号地址表达式(而不仅仅是具体地址)映射到值表达式的关键概念,可以引发进一步的有趣发展。
  在 A l g o r i t h m 2 Algorithm \quad 2 Algorithm2中引入的改进以及应用于作者原型实现的优化可以显著影响方法的基本版本的性能。然而,优化中要探索的设计空间很大,留下了很大的改进空间。
  首先,可以使用诸如值集分析之类的静态分析技术来精细化范围,从而简化约束求解。此外,加载操作返回的表达式可能适合简化,因为最近的符号写操作产生的表达式可能会一起取代执行中较早存储的其他表达式。类似地,分页区间树可以定期重建,或者以延迟方式进行修改,以修剪“过时”的值。
  执行器可能还可以在后续阶段为了保证性能而选择将某些符号地址表达式具体化,或者使用推测性启发式方法限制它们跨越的范围。探讨延迟指针具体化在符号执行中的益处以及可能的策略仍然是一个有趣的未解之谜。

12.9、致谢

  这项工作部分得到意大利总统府部长会议的资助,并由CINI网络安全国家实验室在CISCO Systems Inc.和Leonardo SpA资助的FilieraSicura项目中提供支持。


总结

  以上就是本篇论文阅读笔记的全部内容了,读完这篇论文后,相信各位读者朋友也会认为本篇论文写的相当不错,当然,有些地方笔者可能也理解不深,欢迎各位读者朋友与我积极讨论。后面如果有时间,我会继续分享阅读其它论文的心得体会!

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IronmanJay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值