一种面向安卓系统隐私泄漏检测的动态特性信息提取方法与流程

文档序号:16070349发布日期:2018-11-24 13:08阅读:141来源:国知局

本发明属于代码中的动态特性信息提取领域,具体涉及一种面向安卓系统隐私泄漏检测的动态特性信息提取方法。

背景技术

隐私信息泄露检测中,通常基于数据流进行污点分析,然而代码中的动态特性会形成数据流的断点,阻碍污点传播分析。为了弥补数据流断点,通常以动态测试获取断点信息,弥补数据流分析中动态特性断点。但是目前的测试方法大多采用动态随机测试,没有对动态特性触发因素进行深入分析,覆盖不全,导致动态特性信息的遗漏,使隐私信息泄露分析不完整。因此,对动态特性信息针对性的有效获取,是隐私信息泄露检测的重要基础之一,具有非常现实的意义。



技术实现要素:

本发明针对现有技术中的不足,提供一种面向安卓系统隐私泄漏检测的动态特性信息提取方法,目的是为提高隐私信息泄露分析的覆盖率和精准度,提供关键技术支持。

为实现上述目的,本发明采用以下技术方案:

一种面向安卓系统隐私泄漏检测的动态特性信息提取方法,其特征在于,包括以下步骤:

步骤一、构建程序调用图:把待检测程序app作为输入,提取其程序调用图;

步骤二、提取动态特性调用子图:基于程序调用图,面向动态特性调用反向提取所依赖的动态特性调用子图;

步骤三、区分动态特性路径:根据目标参数不同的定义-使用关系,对调用子图划分动态特性调用路径;

步骤四、提取路径信息约束:基于每条调用路径的切片信息进行分析,提取调用路径的约束信息;

步骤五、生成测试案例:根据动态特性调用路径和提取的约束信息,生成测试案例;

步骤六、提取动态特性信息:分析输出的日志,得到待检测程序的动态特性信息。

为优化上述技术方案,采取的具体措施还包括:

首先,分析动态特性在代码中使用的特点,作为静态分析识别动态特性目标变量的依据;

步骤一中,解析组件函数中定义的通信intent参数,进行属性匹配构造组件间的调用关系;组件内调用图借助flowdroid工具构造,然后把组件内调用图加入到组件间的调用关系中,形成程序调用图;

步骤二中,基于程序调用图,根据动态特性目标变量数据依赖性提取数据依赖子图,然后根据控制依赖性提取事件处理方法作为入口,形成从动态特性调用点到事件处理方法入口的所有依赖调用子图;

步骤三中,以每个动态特性目标变量为起点,反向分析变量的数据依赖性,遇到不同的定义就划分不同的调用路径;

步骤四中,对提取的每条调用路径,利用切片方法对动态特性进行后向切片,获得动态特性调用路径的切片信息;基于每条路径的切片信息,分析语句和谓词,收集路径上需要的事件以及需要输入的参数,并确定事件输入的顺序;

步骤五中,生成对应的模拟事件,加入数据,确定模拟事件输入顺序,形成需要的模拟事件链,得到测试案例;

步骤六中,利用测试案例指导程序运行,根据程序的uid识别测试程序的输出日志,提取动态加载和反射调用信息,把动态加载文件和反射调用信息保存到动态特性信息文件中。

所述步骤一具体包括:

步骤11、选取一个注册的组件,分析组件函数中定义的通信intent参数;

步骤12、判断intent参数的目标component组件名是否为空,若不是,转到步骤16,构建组件和被调用组件的关系,若是,转到步骤13;

步骤13、判断组件intent-filter的action动作属性和intent的action动作属性是否匹配,若是,转到步骤16,构建组件和被调用组件的关系,若不是,转到步骤14;

步骤14、判断在组件intent-filter中category类别属性和intent的category类别属性是否匹配,若是,转到步骤16,构建组件和被调用组件的关系,若不是,转到步骤15;

步骤15、判断在组件intent-filter中data.scheme数据属性和intent的data.scheme数据属性是否匹配,若是,转到步骤16,构建组件和被调用组件的关系;

步骤16、构建组件和被调用组件的关系;

步骤17、把组件输入到flowdroid工具,flowdroid根据android生命周期和回调函数构建组件内调用图;

步骤18、去除组件内调用图的虚拟main函数,把组件内调用图替换对应组件间的调用图中的对应节点形成程序调用图。

所述步骤二具体包括:

步骤21、从代码中选取一个动态特性目标变量a;

步骤22、判断变量a是否有数据依赖变量b,若是,转到步骤23,若不是,转到步骤26;

步骤23、判断变量a和b是否同属于一个方法,若是,转到步骤25,进入下一轮的数据依赖查找,若不是,转到步骤24;

步骤24、把变量a和b所在的方法调用关系加入子图中;

步骤25、把变量b作为变量a进入下轮的数据依赖查找,返回步骤22;

步骤26、判断变量a所属的方法是否是事件处理方法,若是,这一轮的调用子图提取结束,若不是,转到步骤27,开始提取事件处理方法;

步骤27、以变量的初始定义方法为起点,开始反向的查找下一个调用方法;

步骤28、把遍历的方法加入调用子图,形成方法调用序列;

步骤29、判断查找的方法是否是事件处理方法,若是,这轮提取结束,若不是,转到步骤27,继续反向查找下一个调用方法。

所述步骤三具体包括:

步骤31、设置保存路径执行语句的栈,初始状态为空;

步骤32、从代码中选取一个动态特性目标变量a;

步骤33、判断变量a是否是常量字符串,若是,不需要区分路径,转到步骤32,选取下一个动态特性目标变量,若不是,转到步骤34;

步骤34、判断变量a是否有数据依赖的变量b,若不是,转到步骤37,变量没有数据依赖,一条执行路径提取结束,若是,转到步骤35;

步骤35、把包含变量b的执行语句加入到栈中;

步骤36、把变量b作为变量a,然后进入下一轮的数据依赖查找,返回步骤34;

步骤37、一条执行路径提取结束,栈中的执行语句出栈顺序就是一条调用路径,把该路径加入到路径集合中;

步骤38、判断是否还有没有区分路径的动态目标,若是,转到步骤31,把栈设置为空,开始新一轮的路径区分,若不是,转到结束状态。

所述步骤四具体包括:

步骤41、选取一条路径的切片信息;

步骤42、分析切片信息,根据回调方法提取相应的输入事件;

步骤43、判断是否需要数据的输入,若不是,转到步骤4c,保存输入事件,若是,转到步骤44;

步骤44、判断输入数据是否与动态特性参数和控制相关,若不是,转到步骤4a,直接设定随机的数据,不影响动态特性的触发,若是,转到步骤45;

步骤45、判断数据是否与控制相关,若是,转到步骤4b,设定数据的值为控制跳转值或互斥值,若不是,转到步骤46;

步骤46、判断数据是否与动态特性参数依赖相关,若不是,转到步骤4c,保存输入事件,若是,转到步骤47;

步骤47、设定数据为同一个父类的其他子类的值;

步骤48、设定数据为数据相关的判断语句的值;

步骤49、设定数据为空值;

步骤4c、把提取的事件和输入数据保存到path_infor链表中,保存路径约束信息;

步骤4d、表示这条路径切片信息是否还有下一个回调方法,若是,转到步骤43开始新的一轮事件和数据的提取,若不是,转到结束状态。

所述步骤五具体包括:

步骤51、设置事件栈,初始化状态为空栈;

步骤52、从约束信息path_infor链表获取一个节点;

步骤53、判断获取的节点是否为空,若是,转到步骤59,表示这条约束信息path_infor链表被遍历完,把事件栈中的模拟事件链放入到cases测试案例集合中,若不是,转到步骤54;

步骤54、判断事件是否包含数据的输入,若不是,转到步骤56,直接生成相应的模拟事件,若是,转到步骤55;

步骤55、把不同数据值以组合方式添加到事件中;

步骤56、根据事件和数据生成对应的模拟事件;

步骤57、把生成的模拟事件放入到事件栈中,保存模拟事件信息;

步骤58、获取约束信息path_infor链表的下一个节点,进入下一个模拟事件的生成操作,返回步骤53;

步骤59、把事件栈中的模拟事件链放入到cases测试案例集合中。

所述步骤六具体包括:

步骤61、根据测试程序的uid读取log日志;

步骤62、判断该日志信息是否是关于动态加载信息,若是,转到步骤63,分析加载的文件的位置,把加载的文件进行复制,若不是,转到步骤64;

步骤64、判断该日志信息是否是关于反射调用信息,若是,转到步骤65,提取反射目标方法信息,若不是,转到步骤69;

步骤66、分析方法调用堆栈输出的信息,获得源方法信息;

步骤67、把源方法信息和目标方法信息形成信息<source,target>;

步骤68、把动态加载文件和反射调用信息保存到动态特性信息文件中;

步骤69、判断是否结束提取动态特性信息,若不是,转到步骤61,开始下一轮的日志分析,若是,转到结束状态。

本发明的有益效果是:动态测试以静态分析为指导,保证了路径敏感和上下文敏感,提高了动态特性信息获取的覆盖率和精准度,既避免了动态特性信息获取的随机性,又提高了动态特性信息的覆盖面和获取效率。

附图说明

图1是面向安卓系统隐私泄漏检测的动态特性信息提取过程示意图。

图2是构建程序调用图流程图。

图3是提取动态特性调用子图流程图。

图4是区分动态特性路径流程图。

图5是提取路径信息约束流程图。

图6是生成测试案例流程图。

图7是提取动态特性信息流程图。

具体实施方式

现在结合附图对本发明作进一步详细的说明。

如图1所示的面向安卓系统隐私泄漏检测的动态特性信息提取方法,通过提取触发路径约束信息来指导动态测试获取动态特性信息。主要操作过程包括构建程序调用图、提取动态特性调用子图、区分动态特性路径、提取路径信息约束、生成测试案例以及提取动态特性信息等六个阶段,本方法的关键操作如下:

一、构建程序调用图:处理程序之间有隐式方法调用,建立组件间和组件内的完整函数调用图。

二、提取动态特性调用子图:提取调用子图时,先考虑动态特性数据依赖关系,找到动态特性目标所依赖的数据最初定义位置,然后根据控制依赖性提取事件处理方法作为入口。

三、区分动态特性路径:对给定的动态特性根据参数依赖性反向寻找参数的定义,遇到不同的定义时,把找到的路径复制成与不同定义数量相同的路径,然后对每条路径重复的反向查找。

四、提取路径信息约束:对于调用路径的切片信息,分析语句和谓词,收集路径中事件处理方法需要输入的事件和输入的数据。

五、生成测试案例:基于动态特性调用路径的约束信息,生成对应的模拟事件,增加数据组合,形成需要的模拟事件链,得到测试案例。

六、提取动态特性信息:根据被测试程序的uid,循环从日志中读取相关信息,对动态加载和反射调用分别处理,提取相关动态特性信息。

构建程序调用图阶段,把待检测程序app作为输入,提取其程序调用图。提取动态特性调用子图阶段,基于程序调用图,面向动态特性调用反向提取所依赖的动态特性调用子图,把分析限制在较小的程序代码上。区分动态特性路径阶段,根据目标参数不同的定义-使用关系,对调用子图划分动态特性调用路径。提取路径信息约束阶段,基于每条调用路径的切片信息进行分析,提取调用路径的约束信息。生成测试案例阶段,根据动态特性调用路径和提取的约束信息,生成测试案例。提取动态特性信息阶段,分析输出的日志,提取动态特性信息。得到待检测程序的动态特性信息,可供弥补静态分析的信息流动态断点之用。

本方法在整体上分为两部分,第一部分通过静态分析提取触发动态特性约束信息,第二部分动态测试依赖约束信息执行,获取动态特性信息。大致的操作流程如下:分析动态特性在代码中使用的特点,作为静态分析识别动态特性目标变量的依据。解析组件函数中定义的通信intent参数,进行属性匹配构造组件间的调用关系。组件内调用图借助flowdroid工具构造,然后把组件内调用图加入到组件间的调用关系中,形成程序的调用图。基于程序调用图,首先根据动态特性目标变量数据依赖性提取数据依赖子图,然后根据控制依赖性提取事件处理方法作为入口,形成从动态特性调用点到事件处理方法入口的所有依赖调用子图。以每个动态特性目标变量为起点,反向分析变量的数据依赖性,遇到不同的定义就划分不同的调用路径。对提取的每条调用路径,利用传统的切片方法对动态特性进行后向切片,获得动态特性调用路径的切片信息。基于每条路径的切片信息,分析语句和谓词,收集路径上需要的事件以及需要输入的参数,并确定事件输入的顺序。然后进入到动态测试部分,生成对应的模拟事件,加入数据,确定模拟事件输入顺序,形成需要的模拟事件链,得到测试案例。利用测试案例指导程序运行,根据程序的uid识别测试程序的输出日志,提取动态加载和反射调用信息,把动态加载文件和反射调用方法信息保存到动态特性信息文件中。

图2为构建程序调用图流程图。在一个android应用程序里,所用到的组件都需要在androidmanifest.xml文件中注册,可从这个文件中获取所有的组件信息。因为android组件之间的通信借助于intent机制,可以通过对intent解析,找出各组件之间的调用关系。每个组件所能处理的intent参数属性也注册在androidmanifest.xml文件中。根据组件函数中定义的intent参数action、category、data,在组件的intent-filter中进行属性匹配,找到对应的目标组件。组件内调用图需要正确处理隐式调用,隐式调用主要存在于回调方法、异步调用、生命周期方法。利用flowdroid工具构建组件内的调用图,首先提取与android生命周期相关的入口函数,根据android生命周期构建调用图;然后,把回调函数加入这个函数调用图;最后,将调用图上所有的执行入口连接到一个虚假的main函数上。组件间和组件内调用图构建完成后,采用组件内调用图替换对应组件间的调用图中的对应节点的方式获取程序调用图。组件内的调用图生成时包含一个虚拟节点,所以加入之前必须去除。

本过程输入为待检测程序app,输出为待检测程序app的调用图。具体的流程如下:步骤10是初始动作;步骤11表示选取一个注册的组件,分析组件函数中定义的通信intent参数;步骤12表示intent参数的目标component组件名是否为空,若不是,转到步骤16直接构建组件和被调用组件的关系,若是,转到步骤13;步骤13表示组件intent-filter的action动作属性和intent的action动作属性是否匹配,若是,转到步骤16构建组件和被调用组件的关系,若不是,转到步骤14;步骤14表示在组件intent-filter中category类别属性和intent的category类别属性是否匹配,若是,转到步骤16构建组件和被调用组件的关系,若不是,转到步骤15;步骤15表示在组件intent-filter中data.scheme数据属性和intent的data.scheme数据属性是否匹配,若是,转到步骤16构建组件和被调用组件的关系;步骤17表示把组件输入到flowdroid工具,flowdroid根据android生命周期和回调函数构建组件内调用图。步骤18表示去除组件内调用图的虚拟main函数,把组件内调用图替换对应组件间的调用图中的对应节点形成程序调用图。步骤19表示为结束状态。

图3为提取动态特性调用子图流程图。构建完成应用程序调用图后,可以对动态特性无关调用进行删减,把分析限制在一个动态特性相关的、较小的调用子图上。因android程序基于事件驱动的,被提取的路径能包含以事件处理方法为入口点到动态特性调用的所有调用方法序列。在调用图提取调用子图时,先考虑动态特性数据依赖关系,找到动态特性目标所依赖的数据最初定义位置,然后分析该位置所在方法是否为事件处理方法。若不是,按照控制依赖性进行反向查找事件处理方法,直到提取到事件处理方法。考虑动态特性数据依赖关系时,对于方法内部的数据依赖性不需要单独分析,因为方法包括了数据在内部的传递关系,主要分析方法之间的数据传递关系。

本过程输入为待检测程序app的调用图和动态特性目标变量,输出为动态特性调用子图。具体的流程如下:步骤20是初始动作;步骤21表示从代码中选取一个动态特性目标变量a;步骤22表示判断变量a是否有数据依赖变量b,若是,转到步骤23,若不是,转到步骤26;步骤23表示判断变量a和b是否同属于一个方法,若是,转到步骤25进入下一轮的数据依赖查找,若不是,转到步骤24;步骤24表示把变量a和b所在的方法调用关系加入子图中;步骤25表示把变量b作为变量a进入下轮的数据依赖查找;步骤26表示判断变量a所属的方法是否是事件处理方法,若是,转到步骤2a,这一轮的调用子图提取结束,若不是,转到步骤27开始提取事件处理方法;步骤27表示以变量的初始定义方法为起点,开始反向的查找下一个调用方法;步骤28表示把遍历的方法加入调用子图,形成方法调用序列;步骤29表示判断查找的方法是否是事件处理方法,若是,转到步骤2a,这轮提取结束,若不是,转到步骤27继续反向查找下一个调用方法;步骤2a表示结束状态。

图4为区分动态特性路径流程图。对于动态特性,其不同的路径可以触发不同的动态特性,因此需要区分到达动态特性不同的路径。一条方法调用链中动态特性依赖的参数可能包含多个定义,而每个参数定义可以导致触发的目标不同。将动态特性依赖的参数定义-使用关系划分为参数不同定义的数据传递路径,每个路径保证动态特性依赖的每个参数恰好只有一次定义。对动态特性目标是常量字符串,静态分析可以解决不需考虑。对给定的动态特性目标,根据参数依赖性反向寻找参数的定义,遇到不同的定义时,开始依次遍历每个不同的定义,对每个定义进行深度分析,递归寻找该定义的参数依赖关系,直到参数没有依赖,这时对定义的深度分析的参数依赖关系逆序就是一条参数定义是唯一的调用路径。这里借助栈保存反向查找的包含依赖变量的执行语句,最后找到依赖变量初始定义时,栈里的执行语句出站顺序就是一条执行路径。

本过程输入为动态特性调用子图和动态特性目标变量,输出为动态特性调用路径。具体的流程如下:步骤30是初始动作;步骤31表示设置保存路径执行语句的栈,初始状态为空;步骤32表示从代码中选取一个动态特性目标变量a;步骤33表示判断变量a是否是常量字符串,若是,不需要区分路径,转到步骤32选取下一个动态特性目标变量,若不是,转到步骤34;步骤34表示判断变量a是否有数据依赖的变量b,若不是,转到步骤37,变量没有数据依赖,一条执行路径提取结束,若是,转到步骤35;步骤35表示把包含变量b的执行语句加入到栈中;步骤36表示把变量b作为变量a,然后进入下一轮的数据依赖查找;步骤37表示一条执行路径提取结束,栈中的执行语句出栈顺序就是一条调用路径,把该路径加入到路径集合中;步骤38表示判断是否还有没有区分路径的动态目标,若是,转到步骤31,把栈设置为空,开始新一轮的路径区分,若不是,转到步骤39;步骤39表示结束状态。

图5为提取路径信息约束流程图。对提取的每条调用路径,利用传统的切片方法对动态特性进行后向切片,获得动态特性调用路径的切片信息。为了实际触发动态特性调用路径,需要提取控制路径执行的所有约束条件,这里需要分析路径中事件处理方法需要输入的事件和输入的数据。对于调用路径的切片信息展现出一条单一执行的路径信息,遍历这条路径信息从入口点到动态特性调用,分析语句和谓词收集控制路径执行的所有约束。一般android系统提供回调方法来处理输入的事件,可以根据回调方法提取相应的事件。在一条调用路径上可能不止一个接收事件的回调方法,所以要按照路径执行的顺序提取事件,保证正确的事件顺序。路径的执行伴随着数据的输入;如果输入的数据与动态特性参数和控制跳转不相关,可以直接输入随机数值;如果输入的数据与控制跳转有关,分析对应的语句和谓词,把控制跳转的值或互斥分值作为数据输入的值;如果输入的数据和动态特性参数依赖相关,可以从父类的所有其他子类,数据相关的判断语句和空值等方面设定数据。我们将这些路径约束信息保存在path_infor链表中,供后续工作使用。

本过程输入动态特性调用路径的切片信息,输出为动态特性调用路径约束信息path_infor链表。具体的流程如下:步骤40是初始动作;步骤41选取一条路径的切片信息;步骤42表示分析切片信息,根据回调方法提取相应的输入事件;步骤43表示判断是否需要数据的输入,若不是,转到步骤4c保存输入事件,若是,转到步骤44;步骤44表示判断输入数据是否与动态特性参数和控制相关,若不是,转到步骤4a直接设定随机的数据,不影响动态特性的触发,若是,转到步骤45;步骤45表示判断数据是否与控制相关,若是,转到步骤4b设定数据的值为控制跳转值或互斥值,若不是,转到步骤46;步骤46表示判断数据是否与动态特性参数依赖相关,若不是,转到步骤4c保存输入事件,若是,转到步骤47;步骤47表示设定数据为同一个父类的其他子类的值;步骤48表示设定数据为数据相关的判断语句的值;步骤49表示默认的操作,设定数据为空值;步骤4c表示把提取的事件和输入数据保存到path_infor链表中,保存路径约束信息;步骤4d表示这条路径切片信息是否还有下一个回调方法,若是,转到步骤43开始新的一轮事件和数据的提取,若不是,转到步骤4e;步骤4e表示结束状态。

图6为生成测试案例流程图。为收集动态特性信息,需要构建测试案例,把测试案例输入到目标程序,使程序按照特定的触发路径执行,输出动态特性信息。图5得到了动态特性调用路径的约束信息path_infor链表,遍历链表中的节点,按事件处理方法的信息生成对应的模拟事件,如果模拟事件包含数据输入,就把数据封装到模拟事件中。数据可能有多个不同的值,为了测试不同的值对动态特性的影响,需要把不同数据值封装到模拟事件中,形成不同的模拟事件链,进而对同一动态特性调用构成不同的测试案例。cases表示触发动态特性的测试案例,测试案例包含触发动态特性调用的特定顺序模拟事件链。这里借用栈来保存生成的模拟事件链。

本过程输入为调用路径的约束信息path_infor链表,输出为动态特性测试案例cases。具体的流程如下:步骤50是初始动作;步骤51表示设置事件栈,初始化状态为空栈;步骤52表示从约束信息path_infor链表获取一个节点;步骤53表示判断获取的节点是否为空,若是,转到步骤59,表示这条约束信息path_infor链表被遍历完,把事件栈中的模拟事件链放入到cases测试案例集合中,若不是,转到步骤54;步骤54表示判断事件是否包含数据的输入,若不是,转到步骤56,直接生成相应的模拟事件,若是,转到步骤55;步骤55表示把不同数据值以组合方式添加到事件中;步骤56表示根据事件和数据生成对应的模拟事件;步骤57表示把生成的模拟事件放入到事件栈中,保存模拟事件信息;步骤58表示获取约束信息path_infor链表的下一个节点,进入下一个模拟事件的生成操作;步骤59,把事件栈中的模拟事件链放入到cases测试案例集合中;步骤5a表示结束状态。

图7为提取动态特性信息流程图。这里主要关注动态加载和反射调用信息,通过对android系统源码相应的修改定制,当程序运行时需要在日志中能够输出两种类型的相关信息。日志中会存在其他一些无关系统信息,利用android为每一个应用程序分配的uid,可以唯一标识被测试的应用程序,区分其他程序产生日志信息。当程序运行时,根据被测试程序的uid,循环从日志中读取相关信息。对动态加载,解析日志中的信息,找到文件加载的位置,把加载的文件进行复制,保存到动态特性信息的文件夹中。对反射调用,从日志中获取到反射目标的类名,方法名,参数等target信息,然后,分析方法调用堆栈输出的信息,获得堆栈中源类名,源方法名,参数等source信息,最后,把源方法信息和目标方法信息形成<source,target>信息对,把信息对储存到动态信息文件中。

本过程输入为测试程序的日志,输出为动态特性的信息。具体的流程如下:步骤60是初始动作;步骤61表示根据测试程序的uid读取log日志;步骤62表示判断该日志信息是否是关于动态加载信息,若是,转到步骤63分析加载的文件的位置,把加载的文件进行复制,若不是,转到步骤64;步骤64表示判断该日志信息是否是关于反射调用信息,若是,转到步骤65提取反射目标方法信息,若不是,转到步骤69;步骤66表示分析方法调用堆栈输出的信息,获得源方法信息;步骤67表示把源方法信息和目标方法信息形成信息<source,target>;步骤68表示把动态加载文件和反射调用信息保存到动态特性信息文件中;步骤69表示是否结束提取动态特性信息,若不是,转到步骤61开始下一轮的日志分析,若是,转到步骤6a;步骤6a表示结束状态。

以上仅是本发明的优选实施方式,本发明的保护范围并不仅局限于上述实施例,凡属于本发明思路下的技术方案均属于本发明的保护范围。应当指出,对于本技术领域的普通技术人员来说,在不脱离本发明原理前提下的若干改进和润饰,应视为本发明的保护范围。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1