一种具有数据流混淆的虚拟化软件保护方法

文档序号:10725454阅读:212来源:国知局
一种具有数据流混淆的虚拟化软件保护方法
【专利摘要】本发明公开了一种具有数据流混淆的虚拟化软件保护方法,步骤包括:步骤1,PE文件检测;步骤2,定位关键代码段;步骤3,将本地x86指令转化为虚拟指令;步骤4,对虚拟指令进行编码生成对应的字节码指令;步骤5,对虚拟机的调度结构进行双进程设计;步骤6,对虚拟机中的Handler进行数据流混淆;步骤7隐藏程序中原本的谓词信息,并添加新的谓词信息构成假的执行流分支;步骤8,目标文件重构;该方法利用计算机系统,对windows系统下的可执行二进制代码文件进行虚拟化保护,保护强度高、易于扩展。
【专利说明】
一种具有数据流混淆的虚拟化软件保护方法
技术领域
[0001] 本发明涉及计算机安全技术领域,具体涉及一种具有数据流混淆的虚拟化软件保 护方法。
【背景技术】
[0002] 随着信息科学技术的发展,如今计算机和移动终端的各种软件已经成为人们日常 生活中不可或缺的一部分。软件一般总是指在计算机上运行的程序,它是软件开发人员智 慧的结晶,它的研发过程会消耗大量的人力及财力。然而软件一旦发布之后就会受到各种 各样的威胁,攻击者通过代码逆向、篡改等手段,窃取软件中的核心算法、机密信息,进而在 产品的竞争中获取不正当的优势,这给企业和软件开发人员带来了严重的损失,严重危害 软件产业的健康发展。
[0003] 目前常见的软件保护方法1.软件加壳,通过加壳使软件进行压缩或加密;2.垃圾 代码插入,通过向代码中加入垃圾代码使得反汇编出现错误;3.代码混淆,如变量替换、等 价指令替换、控制流变换等,这些保护方法在一定程度上阻止了攻击者的逆向分析难度,但 是它们仍然存在自身的局限性,如软件加壳,在软件执行的时候会首先脱壳(解压或解密), 攻击者就可以看到原始指令;垃圾代码插入只能阻止静态分析不能阻止基于动态执行的软 件分析;代码混淆是基于语义等价的,都是通过简单的变换操作,对逆向分析的作用是有限 的。
[0004] 虚拟机软件保护是近年来新兴的一种软件保护方法,能够为软件提供强大的保 护。相比于传统的保护方法,虚拟机保护的优势在于它极大地增加了攻击者逆向分析的代 价。
[0005] 传统的攻击虚拟机保护后的程序的方法首先逆向出虚拟解释器,然后根据虚拟解 释器逐条读取字节码并调用Handler执行的过程得到每条字节码指令的功能,最后通过对 字节码序列的功能进行简化和分析从而还原出原始程序的功能。这种传统的攻击方法需要 熟悉虚拟机保护的结构,可以被称作是基于虚拟机结构的攻击方法。以上对虚拟机保护的 各种加固方法都能够增加虚拟机的安全性,提高对上述传统的基于虚拟机结构的攻击方法 的抵御能力。
[0006] 除了传统的基于虚拟机结构的攻击方法,近几年研究人员又提出了基于语义的攻 击方法。Coogan等人提出了一种用于识别和系统调用相关的指令的方法,能够提取出一个 近似于原始未混淆程序的指令序列。这种方法对于分析虚拟机保护后的恶意程序的效果比 较好,因为恶意程序如果要实现某个有意义的恶意操作,就不可避免地会与系统调用发生 交互,而那些用于和系统调用进行交互的指令将被识别出来用来分析程序的关键行为。 Sharif等人通过动态执行程序,并使用动态数据流分析和污点分析去识别字节码程序的内 容并提取字节码程序的语义,进而重构出原始程序的执行路径和效果。Yadegari等人通过 收集程序的一条执行路径,然后使用动态污点分析和符号执行技术对路径可达性进行分 析,进而简化出原始程序的控制流图和程序内部的逻辑结构。由于这些攻击方法不依赖于 虚拟机保护的结构,所以通用性比较好。上述的各种加固方法无法很好地抵御基于语义的 逆向分析。

【发明内容】

[0007] 针对上述现有技术中存在的问题,本发明的目的在于,提供一种具有数据流混淆 的虚拟化软件保护方法,阻止基于语义的数据流分析技术对虚拟化保护后的软件进行攻 击。
[0008] 为了实现上述任务,本发明采用以下技术方案:
[0009] -种具有数据流混淆的虚拟化软件保护方法,包括以下步骤:
[0010] 步骤一,验证载入的待保护文件是否PE文件,如果是则进行下一步;
[0011]步骤二,在待保护的文件的源代码段上指定需要被保护的关键代码段,对关键代 码段进行反汇编操作,得到关键代码段的X86指令集合;
[0012]步骤三,将步骤二得到的χ86指令集合转换成虚拟指令;
[0013]步骤四,将虚拟指令映射成相应的Handler序列HAS,然后把映射的HAS进行加密处 理,得到字节码指令;
[0014]步骤五,对虚拟机的调度结构进行双进程设计,双进程指用于执行正常程序功能 的子进程以及用于调试子进程的父进程,当Handler执行完成的时候,产生一个异常,通过 父进程捕获到所述的异常,在父进程的中断地址表中查找异常的地址和异常跳向的目的地 址,当能够查询到异常的地址和异常跳向的目的地址时,父进程将子进程的EIP指向目的地 址并恢复子进程继续执行,若查询不到,则终止程序的执行;
[0015] 步骤六,对虚拟机中的Handler进行数据流混淆,使程序中的数据流复杂化;
[0016] 步骤七,隐藏程序中原本的谓词信息,并添加新的谓词信息构成假的执行流分支; [00?7] 步骤八,对原PE文件添加一个新节,将混淆后的Handler、字节码指令和虚拟机其 他组成部分嵌入到新节中,用垃圾指令填充关键代码段,并将关键代码段的起始地址处指 令修改为一条跳向虚拟机入口的跳转指令,重新生成一个保护后的可执行文件。
[00?8]进一步地,所述的步骤六中对Handler进行数据混淆的过程包括:
[0019] 污点漂白:选取待混淆的Handler,分析Handler中污点传播的路径,在路径上选取 一个寄存器al,并增加一个新的寄存器ebx;程序执行到寄存器al时,令ebx不断执行+1操 作,当ebx中的值与al中的值相等时,将ebx中的数值继续向后面的程序传播。
[0020] 进一步地,所述的步骤六中对Handler进行数据流混淆的过程还包括:
[0021] 过度标记:选取待混淆的Handler,分析Handler中污点传播的路径,在路径上选取 需要使用的寄存器,将该寄存器压栈,并将污点数据使用的数据传输或算数运算指令传播 到一个其他的寄存器中,然后将需要使用的寄存器弹出栈以恢复栈环境。
[0022] 进一步地,所述的步骤七的具体过程包括:
[0023]利用随机函数对虚拟机中的部分Handler后的跳转指令进行隐藏,增加程序执行 流的复杂性,同时在Handler中随机加入一些谓词信息以构成假的分支。
[0024]进一步地,所述的Handler后的跳转指令进行隐藏的具体方法包括:
[0025] 构造异常指令库,异常指令库中存储有不同的异常指令;
[0026] 建立中断地址信息表,该表由异常指令库中的异常指令的当前地址和异常指令所 在基本块的目的地址组成;
[0027] 利用随机函数将虚拟机中部分Handler后的跳转指令修改为一个异常指令使其产 生一个异常中断,当异常中断被捕获后,查找中断地址信息表中异常指令的当前地址以及 异常指令所在基本块的目的地址,然后进行跳转从而达到原来的跳转目的。
[0028] 本发明与现有技术相比具有以下技术特点:
[0029] 1.针对二进制代码进行保护,与使用的编程语言无关,适用性广;
[0030] 2.传统的虚拟机软件保护系统无法很好地抵御基于语义的反混淆攻击,基于语义 的反混淆的基础是数据流分析和优化,因此,通过采用有效的数据流混淆方法能够提高虚 拟机保护系统对该类攻击的抵御效果。为了达到较好的数据流混淆的效果,采用并实现了 一种基于具有双进程的虚拟机保护系统,重新设计了虚拟机结构,使虚拟机保护后软件的 执行过程更加的复杂多样化。
[0031] 3.相比于单一进程的VM结构,在设计时引入双进程结构,使得虚拟机结构具有不 确定的结构,表现的程序执行流和数据流更加的复杂多样化,增加攻击者语义分析难度,双 进程之间是交互通信相互协作共同完成程序的执行过程,具有一定的进程监视和反调试作 用。
[0032] 4.利用数据流变形引擎对虚拟解释器Handler进行变换,使得Handler能够阻止基 于数据流的分析。
【附图说明】
[0033]图1为本发明的流程框架图;
[0034] 图2为本发明方法数据流混淆的虚拟机保护后程序执行图;
[0035] 图3为使用本发明前虚拟机保护后软件执行过程;
[0036] 图4为本发明一个简单的隐藏谓词示例;
【具体实施方式】
[0037] 本发明提出了一种具有数据流混淆的虚拟化软件保护方法,包括以下步骤:
[0038]步骤一,验证载入的待保护文件是否PE文件,如果是则进行下一步;在本发明中的 PE文件是指windows平台上主流的可执行文件格式,如:.exe文件.dl 1文件等。
[0039] 步骤二,在待保护的文件的源代码段上指定需要被保护的关键代码段,对关键代 码段进行反汇编操作,得到关键代码段的x86指令集合;
[0040] 关键代码段是指用户在源代码上指定的需要被保护的关键代码,在关键代码段开 始处嵌入SDK起始标记,结束处嵌入SDK结束标记.SDK是DF〇-VMP(A Virtual Software Protection Method with Data Flow Obfuscation)定义了一组起始和结束标志。生成PE 文件后,只要寻找SDK标记就能得到关键代码段的起始和结束地址,从而定位到关键代码 段,再进行反汇编,得到关键代码段的x86指令集合。
[0041] 步骤三,在保证语义等价的前提下,根据X86指令和虚拟指令的对应关系,将步骤 二得到的x86指令集合转换成虚拟指令;
[0042]在保护的过程中,将本地指令转变为虚拟指令,主要有三步操作:
[0043] (1) "load"虚拟指令。将本地指令的操作数压入栈中;
[0044] (2)目标操作指令。执行本地指令的目标操作,该虚拟指令不需要考虑操作数的类 型,直接从栈顶获取相关操作数,但需要考虑操作数的大小;
[0045] (3) "store"虚拟指令。将操作执行的结果存入虚拟环境中。
[0046] 数据传输指令虚拟化过程主要使用"load"、"store"指令,如"mov"、"push"和 "pop"指令;算数和逻辑运算指令的虚拟化过程严格按照上面的三步操作来实现;控制跳转 指令虚拟化过程通过"1 oad"指令和"jmp"指令组合实现。表1给出了一些本地指令虚拟化的 示例。
[0047] 有些本地指令有复杂的寻址方式,在虚拟化的过程中会反复用到上述虚拟指令, 例如表1中的"move eax,dword[esi + 32]"指令;其中,表1中的"42a583h"是在地址 "4020a8h"中存储的本地指令相对应的字节码指令的地址。
[0048] 表1:本地指令和对应虚拟指令的示例
[0050]步骤四,将虚拟指令映射成相应的Handler序列HAS,然后把映射的HAS进行加密处 理,得到字节码指令;
[0051 ]虚拟指令最终是以字节码的形式存放在保护后的程序中的,虚拟指令的虚拟操作 码、寻址方式和操作数不同,对应的Handler序列也不相同,其中一条虚拟指令是由一个或 者多个Handler解释的,将VI映射为相应的Handler序列HAS(Handler Sequence),而HAS由 Handler序号和Handler参数组成,然后把映射的HAS进行加密处理,得到字节码指令 VMdata〇
[0052]虚拟指令和本地指令是一种简单的对应关系,本方案采取一种简单的编码规则, 即将虚拟指令中描述操作以及描述操作对象的字节分隔开。在实现中,给每一个虚拟指令 指定不同的ID,这些ID取值范围是0~255,一个字节可以充分的编码所有的ID,这些ID也可 以成为操作码。如表2所示:
[0053]表2:虚拟指令对应的字节码指令
[0055] 步骤五,虚拟机调度结构的重新设计
[0056]虚拟机中传统集中式调度结构的过程为:进入虚拟机后,执行Dispatcher取 VMdata,解密得到Handler序号并执行相应的Handler,执行完继续返回Dispatcher,重复前 面的过程,直到完成关键代码段的功能。
[0057]本步骤对传统的虚拟机的调度结构进行双进程设计,双进程指用于执行正常程序 功能的子进程以及用于调试子进程的父进程,当Handler执行完成的时候,产生一个异常, 通过父进程捕获到所述的异常;在父进程中设置有中断地址表,表中记录有异常的地址以 及异常的跳向目的地址;
[0058]在父进程的中断地址表中查找异常的地址和异常跳向的目的地址,当能够查询到 异常的地址和异常跳向的目的地址时,父进程将子进程的EIP指向目的地址并恢复子进程 继续执行,若查询不到,则终止程序的执行。
[0059] 当程序执行时,首先父进程执行一段汇编程序用于创建一个子进程,创建的子进 程用于执行正常程序的功能,父进程用做调试子进程,创建子进程的汇编代码的一个示例 如表3所示:
[0060] 表3:创建子进程汇编代码
[0061]
[0062]将传统虚拟机结构进行重新设计,将虚拟机结构分布到两个不同的进程中,这种 结构具有以下的特点:1.是增加程序执行时控制流和执行流的复杂度,使得程序的结构更 加的复杂。2.进程之间的相互通信机制能够使得程序的执行被监视,不被攻击者篡改程序 的执行流。3.同时起到反调试的目的,这是因为在Windows环境中调试器只能对单一进程进 行调试追踪。综合这些特点使得这种结构能够很好的起到保护作用。
[0063] 步骤六,对虚拟机中的Handler进行数据流混淆,使程序中的数据流复杂化;
[0064] 对虚拟机中引入数据流的混淆目的是增加程序执行时数据流的复杂度,阻止攻击 者利用数据流的分析技术对其进行分析逆向,达到抵抗语义分析目的,在引入数据流混淆 时,可以采用多种的数据流混淆机制,主要包括两个方面的内容,1.是污点漂白,即在程序 的数据流中加入污点的漂白作用。2.是过度污染,即尽可能多的增加程序中的污染数据,这 种机制使得攻击者在进行污点分析时,无法从大量的污点数据中分离出有用的数据信息, 从而不能够精确对指令分析。下边分别对两个方面的内容举例说明。
[0065] 1.污点漂白
[0066] 表4未经过混淆的原子Handler
[0068] 如表4中所示是虚拟机中的一个未经过数据流混淆的Handler,这个原子Handler 完成的操作是从VMdata中读取(指令3完成读取)一个字节码然后对字节码解密(指令4-7完 成解密),根据解密出的字节码计算出一个虚拟寄存器环境的地址(指令8-9完成计算),并 将这个地址压入栈。其中寄存器esi指向的是VMdata的起始地址,寄存器edi指向VMcontext 的首地址。
[0069]对这个原子Handler分析可以发现,当攻击者分析程序时,首先给定一个程序的输 入值并标记为污点数据(如:在VMdata中的字节码数据进行污点标记),当程序执行到这个 Handler时,执行指令lods byte ptr ds: [esi]时,污点会被传播到寄存器al,在随后的计 算虚拟上下文环境地址时,污点会一直被传播,这样攻击者就能够利用数据流准确的分析 出虚拟上下文环境中寄存器地址的布局情况,进而再进行后续的分析工作。
[0070] 虚拟机中的字节码最终是由虚拟机设计的Handler解释执行的,因此对Handler进 行数据流混淆是至关重要的,这种混淆能够有效阻止攻击者使用污点传播技术对其分析。 [0071] 表5经过混淆后的原子Handler
[0074] 如表5所示是经过数据流混淆后的原子Handler,从表中可以看出经过混淆后的 Handler中通过增加了一个新的寄存器ebx来阻断污染的传播路径。当指令lods byte ptr ds : [esi ]读取到一个字节码后存放在寄存器al中,然后利用寄存器ebx进行自加运算,当 ebx的值和读取到的al值相等时,将ebx的值传递给eax,这样寄存器eax就不会被污染,随后 在计算内存地址时也不会被污染传播。这种方式的原理是利用cmp比较运算来进行数据更 新,利用这个原理可以变形出多种形式,为了减少循环比较的次数,如利用取整和取余运算 减少比较次数。利用这个方法可以有效的阻止污染的传播,阻止攻击者的进一步分析。 [00 75]污点漂白的具体过程为:选取待混淆的Handler,分析Handler中污点传播的路径, 在路径上选取一个寄存器al,并增加一个新的寄存器ebx;程序执行到寄存器al时,令ebx不 断执行+1操作,当ebx中的值与al中的值相等时,将ebx中的数值继续向后面的程序传播。 [0076] 2.过度标记
[0077]同样使用表4中原子handler进行说明,在原子handler中加入一些数据流混淆的 指令,这些指令的作用是尽可能的是污点的传播扩大,使得攻击者无法区分需要标记的指 令,不能收集到准确的执行信息。
[0078] 表6未经过混淆的原子Handler
[0080]如表6所示在原子handle中加入污点的过度传播指令,当攻击者分析程序时给定 一个污点标记al,这个污点标记将传播到寄存器bl,ebx,ecx,edx,同时收集到的指令数量 也将比原本没有进行数据流混淆时增加很多。需要说明的是,原始程序是由多个handler去 共同执行完成原始程序的功能,这样最终的污点的传播将变得非常的庞大,能够非常有效 的阻止攻击者的分析。
[0081 ]过度标记的具体过程为:选取待混淆的Handler,分析Handler中污点传播的路径, 在路径上选取需要使用的寄存器,为了保持栈的平衡,需要将该寄存器压栈,并将污点数据 使用的数据传输或算数运算指令传播到一个其他的寄存器中,如:污点数据是存储在al中, 使用mov ebx,al,add ecx,al,move dx,al等操作将污点比价的数据al传播到其他的寄存 器中,然后将需要使用的寄存器弹出栈以恢复栈环境。
[0082] 步骤七,隐藏程序中原本的谓词信息,并添加新的谓词信息构成假的执行流分支;
[0083] 抵抗符号执行分析的关键是隐藏程序中的谓词信息,因为攻击者在利用符号执行 去分析程序时首先是要定位到程序中的谓词信息,然后将指令进行符号化表达,进而再进 行可达性推理,最终构建出程序的控制流图信息。
[0084]在虚拟机的设计中,每一个H a n d 1 e r执行完都会有一个跳转指令跳回到 Dispatcher进行取下一个字节码然后解密执行,循环执行取码-译码-执行,直到所有的字 节码执行完成,然后结束循环。基于此我们将虚拟机中的Handler进行变换,利用随机函数 使部分Hand 1 er后的跳转指令进行隐藏,增加程序执行流的复杂性,同时在Hand 1 er中随机 加入一些谓词信息,构成假的分支,进一步迷惑攻击者,当攻击者进行分析时,所构造出的 控制流图是不完整的或者是存在假的分支结构信息。
[0085]跳转指令进行隐藏的具体方法包括:
[0086]构造异常指令库,异常指令库中存储有不同的异常指令;异常指令的设计采用的 是常用的x86指令,这样做具有一定的隐蔽作用,使攻击者在分析程序指令时不能够轻易的 发现异常代码。在设计时首先构造异常指令库,进行替换时在异常指令库中随机选择异常 指令进行替换,如表3所示异常代码是x86的一些指令,如除零异常,内存访问异常,中断异 常等。
[0087]表7异常指令类型和举例说明 「00881
[0089] 建立中断地址信息表,该表由异常指令库中的异常指令的当前地址和异常指令所 在基本块的目的地址组成;中断地址信息表的结构如表8所示:
[0090] 表8中断信息表
[0092]利用异常机制的方法将程序中的谓词信息进行隐藏,如图4所示,利用随机函数将 虚拟机中部分Handler后的跳转指令修改为一个异常指令使其产生一个异常中断,当异常 中断被捕获后,查找中断地址信息表中异常指令的当前地址以及异常指令所在基本块的目 的地址,然后进行跳转从而达到原来的跳转目的。
[0093] 步骤八,对原PE文件添加一个新节,将步骤六混淆后的Handler、最终生成的字节 码指令和虚拟机其他组成部分(VMcontext、VMinit dispatcher、Handlers、VMexit)嵌入到 新节中,用垃圾指令填充关键代码段,并将关键代码段的起始地址处指令修改为一条跳向 虚拟机入口的跳转指令,重新生成一个保护后的可执行文件。
[0094]将上述虚拟机的各个组成部分和字节码序列的组合作为PE文件的一个新节附加 在原PE文件之后,并根据添加了新区段之后的PE文件的大小修改新PE文件的大小和区段数 目。关键代码段在PE文件中起始位置处用一条无条件跳转语句填充,该跳转语句用于指向 新节中虚拟机初始化入口(VMinit)的代码段起始地址处;然后将关键代码段的剩余部分用 一些随机的垃圾数据填充。在执行时,这段代码不会被执行,不会影响程序功能,并且还能 起到迷惑攻击者的作用。
【主权项】
1. 一种具有数据流混淆的虚拟化软件保护方法,其特征在于,包括以下步骤: 步骤一,验证载入的待保护文件是否PE文件,如果是则进行下一步; 步骤二,在待保护的文件的源代码段上指定需要被保护的关键代码段,对关键代码段 进行反汇编操作,得到关键代码段的χ86指令集合; 步骤三,将步骤二得到的x86指令集合转换成虚拟指令; 步骤四,将虚拟指令映射成相应的Handler序列HAS,然后把映射的HAS进行加密处理, 得到字节码指令; 步骤五,对虚拟机的调度结构进行双进程设计,双进程指用于执行正常程序功能的子 进程以及用于调试子进程的父进程,当Handler执行完成的时候,产生一个异常,通过父进 程捕获到所述的异常,在父进程的中断地址表中查找异常的地址和异常跳向的目的地址, 当能够查询到异常的地址和异常跳向的目的地址时,父进程将子进程的EIP指向目的地址 并恢复子进程继续执行,若查询不到,则终止程序的执行; 步骤六,对虚拟机中的Handl er进行数据流混淆,使程序中的数据流复杂化; 步骤七,隐藏程序中原本的谓词信息,并添加新的谓词信息构成假的执行流分支; 步骤八,对原PE文件添加一个新节,将混淆后的Handler、字节码指令和虚拟机其他组 成部分嵌入到新节中,用垃圾指令填充关键代码段,并将关键代码段的起始地址处指令修 改为一条跳向虚拟机入口的跳转指令,重新生成一个保护后的可执行文件。2. 如权利要求1所述的具有数据流混淆的虚拟化软件保护方法,其特征在于,所述的步 骤六中对Handler进行数据混淆的过程包括: 污点漂白:选取待混淆的Handler,分析Handler中污点传播的路径,在路径上选取一个 寄存器al,并增加一个新的寄存器ebx;程序执行到寄存器al时,令ebx不断执行+1操作,当 ebx中的值与al中的值相等时,将ebx中的数值继续向后面的程序传播。3. 如权利要求2所述的具有数据流混淆的虚拟化软件保护方法,其特征在于,所述的步 骤六中对Handler进行数据流混淆的过程还包括: 过度标记:选取待混淆的Handler,分析Handler中污点传播的路径,在路径上选取需要 使用的寄存器,将该寄存器压栈,并将污点数据使用的数据传输或算数运算指令传播到一 个其他的寄存器中,然后将需要使用的寄存器弹出栈以恢复栈环境。4. 如权利要求1所述的具有数据流混淆的虚拟化软件保护方法,其特征在于,所述的步 骤七的具体过程包括: 利用随机函数对虚拟机中的部分Handler后的跳转指令进行隐藏,增加程序执行流的 复杂性,同时在Handler中随机加入一些谓词信息以构成假的分支。5. 如权利要求4所述的具有数据流混淆的虚拟化软件保护方法,其特征在于,所述的 Handler后的跳转指令进行隐藏的具体方法包括: 构造异常指令库,异常指令库中存储有不同的异常指令; 建立中断地址信息表,该表由异常指令库中的异常指令的当前地址和异常指令所在基 本块的目的地址组成; 利用随机函数将虚拟机中部分Handler后的跳转指令修改为一个异常指令使其产生一 个异常中断,当异常中断被捕获后,查找中断地址信息表中异常指令的当前地址以及异常 指令所在基本块的目的地址,然后进行跳转从而达到原来的跳转目的。
【文档编号】G06F21/14GK106096338SQ201610399231
【公开日】2016年11月9日
【申请日】2016年6月7日
【发明人】房鼎益, 张恒, 汤战勇, 周祥, 陈晓江, 龚晓庆, 刘方圆, 张洁, 叶贵鑫, 陈锋
【申请人】西北大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1