针对区块链智能合约函数签名自动化恢复的方法及系统

文档序号:26101470发布日期:2021-07-30 18:12阅读:161来源:国知局
一种针对区块链智能合约函数签名自动化恢复的方法及系统,用于函数签名自动化恢复,属于区块链智能合约
技术领域
:。
背景技术
::以太坊是目前规模最大的支持智能合约的公链,目前已有超过3000万个智能合约部署在其上。以太坊智能合约由高级语言编写,并编译为以太坊虚拟机字节码,智能合约中的public和external函数能够被使用者调用。目前主流的以太坊智能合约编译器为solidity和vyper。调用智能合约中函数时,使用者需要知道其函数签名,函数签名包含了函数的functionid和函数的参数的类型列表,其中functionid由函数名进行哈希运算并取前4字节得到,调用函数所使用的参数保存在调用数据calldata中。函数签名对于智能合约的恶意攻击检测,模糊测试以及智能合约字节码逆向工程具有重要价值,然而,现有的方法都无法高效地从智能合约字节码中恢复函数签名。函数签名恢复工具gigahorse,eveem和onlinesoliditydecompiler通过查询ethereumfunctionsignaturedatabase等函数签名数据库进行函数签名的恢复。然而此类工具依赖的函数签名数据库并不完整,仅能覆盖链上部署合约全部函数签名的31.7%,且此类函数签名数据库在缺乏持续更新的情况下将很快过期,无法应对持续增加的智能合约数量。abidecompiler通过枚举全部可能的参数的类型组合,以及可能的函数名来尝试恢复函数签名,然而由于庞大的组合空间,该方法仅能覆盖12.3%的函数签名。gigahorse和eveem利用一些启发式规则,分析智能合约字节码的指令来恢复函数签名,但由于它们的启发式规则并不完备,恢复出函数签名的正确率最高仅能达到58.1%。符号执行技术在1970年被提出,并在近年成为研究热点。其采用抽象的符号代替确定的变量值作为程序输入,得出每个路径抽象的输出结果。然而传统符号执行技术无法用于以太坊虚拟机字节码中函数参数的类型推断,因此无法有效恢复函数签名。j.caballero等人在论文《binarycodeextractionandinterfaceidentificationforsecurityapplications》提出了从x86/x64架构二进制程序恢复参数类型的方法,在论文《lightweightheuristicstoretrieveparameterassociationsfrombinaries》中提出了一种使用启发式方法从二进制程序文件中获得参数关系的方法。在论文《scalablevariableanddatatypedetectioninabinaryrewriter》中,提出了在不恢复函数参数类型等情况下识别出函数参数的方法,论文《acompiler-levelintermediaterepresentationbasedbinaryanalysisandrewritingsystem》,《parameterandreturn-valueanalysisofbinaryexecutables》和《analyzingmemoryaccessesinx86executables》也提出了类似的方法。这类方法分析二进制程序文件,并得到函数参数的信息。但只能应用于x86/x64架构的二进制程序,由于以太坊虚拟机和x86/x64架构具有显著差异,上述方法无法直接应用于智能合约字节码来得到合约函数签名。在文献《aprincipledapproachforfunctionrecognitionincotsbinaries》中,sekar等研究者使用基于规则的方法,识别二进制程序中的函数,论文《learningtorecognizefunctionsinbinarycode》也提出了识别二进制程序中函数的同类方法。论文《recognizingfunctionsinbinarieswithneuralnetworks》和《semantics-awaremachinelearningforfunctionrecognitioninbinarycode》提出了使用机器学习技术识别二进制程序中的函数的方法,主要为以下几点:(1)训练一个递归神经网络,该网络将二进制程序的字节序列作为输入,并判断某个位置是否是函数边界。(2)通过符号执行来获得指令的语义信息,并训练一个函数识别模型,通过机器学习方法识别函数边界。这类方法只能应用于x86/x64二进制程序,无法在智能合约字节码文件上使用来获得智能合约函数签名。论文《automaticreverseengineeringofdatastructuresfrombinaryexecution》提出了推断二进制程序中变量类型的方法,该方法使用了x86/x64架构特定的规则来进行变量类型的推断,这些规则涉及系统调用,标准库调用以及一些类型相关的机器指令。在另外三篇论文《simpletypesystemforprogramreengineering》,《tie:principledreverseengineeringoftypesinbinaryprograms》和《polymorphictypeinferenceformachinecode》中也提出了类似的方法,主要为以下几点:(1)利用标准函数库原型来推断变量类型。(2)从访问参数的指令中提取架构特定的规则,并利用这些规则来恢复变量类型。(3)从数组参数,结构体类型参数的访问模式中推断数组和struct类型。然而,由于以太坊虚拟机与x86/x64程序架构上的显著差异,上述用于x86/x64程序的方法都无法用于智能合约函数签名的恢复。torres等研究者在论文《osiris:huntingforintegerbugsinethereumsmartcontracts》中提出使用启发式规则推断有符号和无符号整数类型的方法,但该方法仅能推断出这两种类型,但无法用于完整恢复函数签名。consensys开发的mythril能够将以太坊虚拟机字节码反编译为中间代码,并在中间代码上进行符号执行,论文《ethir:aframeworkforhigh-ievelanalysisofethereumbytecode》提出一种能够将以太坊虚拟机字节码转化为基于规则的表示形式的方法。上述方法能够获得智能合约中以太坊虚拟机指令的语义信息,但无法用于恢复函数签名。综上所述,现有技术要么完全无法恢复函数签名,要么无法完整恢复函数签名,从而造成无法调用智能合约中的public和external函数,不能对智能合约恶意攻击进行高效检测,不能对智能合约进行高效的模糊测试,以及不能产生智能合约字节码逆向工程的高可读性结果的问题。技术实现要素:针对上述研究的问题,本发明的目的在于提供一种针对区块链智能合约函数签名自动化恢复的方法及系统,解决现有要么完全无法恢复函数签名,要么无法完整恢复函数签名,从而造成无法调用智能合约中的public和external函数,不能对智能合约恶意攻击进行高效检测,不能对智能合约进行高效的模糊测试,以及不能产生智能合约字节码逆向工程的高可读性结果的问题。为了达到上述目的,本发明采用如下技术方案:一种恢复函数参数类型的方法,包括如下步骤:a1:自动生成智能合约并将生成的智能合约编译为字节码,其中,智能合约包含一个类型为public函数或external的函数,函数包含一个随机类型的参数和访问该参数的语句;a2:收集各类型的参数的访问模式;a3:基于所有参数的访问模式提取公共访问模式;a4:基于公共访问模式生成参数的符号表达式;a5:基于参数的公共访问模式和参数的符号表达式获得函数的参数的类型规则。进一步,所述步骤a1中使用solidity或vyper编译器支持的语法生成一个智能合约。进一步,所述步骤a2的具体实现为:将编译后的智能合约进行反编译,得到反编译后的以太坊虚拟机指令,得到指令后,对指令进行数据依赖和控制依赖分析,分析后,定位访问参数的指令序列,即将指令序列作为函数的参数的访问模式,其中,定位访问参数的指令序列具体为:从每个public或external函数的入口处开始,检查public函数或external函数的指令序列,即搜索calldataload指令和calldatacopy指令,以及对calldataload指令和calldatacopy指令读取的数据进行运算和存储的指令,其中,calldataload指令和calldatacopy指令为从栈顶获取读取位置,即从调用数据的读取位置读取参数到栈顶的指令。进一步,所述步骤a3的具体实现为:对于基础类型相同,但位宽度不同的同系列类型的参数,将同系列类型的参数的访问模式中都出现的指令序列提取为该系列类型的参数的公共访问模式,其中,基础类型是指uint类型或int类型,根据不同的位宽度,分别有uint<m>类型和int<m>类型,m是8的倍数,包括uint8,uint16,uint32,…,uint128,uint256,以及int8,int16,int32,…,int128,int256;uint8,uint16,uint32,…,uint128,uint256为同系列类型的参数,int8,int16,int32,…,int128,int256为同系列类型的参数。进一步,所述步骤a4的具体实现为:将智能合约中的函数的调用数据视为符号,在获得的参数的访问模式上进行符号执行,执行后,获得函数的调用数据中每个变量的符号表达式,其中,函数的调用数据是指由函数的fucntionid和调用的函数传入的实参按照顺序和结构组织成的数据。进一步,所述步骤a5中基于参数的公共访问模式和参数的符号表达式获得31条参数的类型规则为,步骤a5的具体实现为:r1:规则r1定义为x=calldataload1(loc)∧y=calldataload2(x+4),其中,x=calldataload1(loc)表示使用一条calldataload指令从调用数据的loc位置读取32字节值,并赋值给x,实际上x为public函数或external函数中dynamicarray/bytes/string类型的参数的offset字段,由于参数num字段的位置为offset字段指示的位置再加上4个字节的fucntionid的长度,因此calldataload读取参数的num字段,y=calldataload2(x+4)表示使用一条calldataload指令,从调用数据的x+4位置读取32字节值,并赋值给y,∧是一个逻辑运算符号,表示且,如果规则r1被满足,表明当前的指令序列读取的参数的类型为dynamicarray/bytes/string;r2:规则r2定义为其中,表示calldataload指令要读取调用数据的loc位置需要通过offset字段相加得出,表示loc位置的符号表达式中包含32的乘法,表示依赖于,l表示在执行calldataload(loc)之前,需要先执行n个lt比较指令,calldataload(loc)表示calldataload指令从调用数据的loc位置读取数据,in,numn为lt比较指令的两个操作数,in表示array元素在第n维的下标,numn表示array第n维的长度,lt比较指令是比较in是否小于numn,即做边界检查,查看calldataload读取的位置是否越界,exp(loc)表示将一个loc位置要符合的规则用一个式子表示出来,表示包含,∧是一个逻辑运算符号,表示且,如果规则r2被满足,表明该指令序列读取的参数的类型为external函数的n-维dynamicarray;r3:规则r3定义为其中,表示读取调用数据的loc位置不由offset计算出,表示在执行calldataload(loc)之前,需要先执行n个lt比较指令,表示取反,exp(loc)、ιn、numn、和∧的含义同规则r2中的含义相同,如果规则r3被满足,则表明函数的参数的类型为n-维staticarray;r4:规则r4定义为x=calldataload(loc),在r1、r2和r3都不满足时,将参数看作为uint256类型,r4表示在缺乏更多有效线索的情况下,只能获知x的长度为32字节,因此将x看作uint256类型,后续将使用其他规则将参数的类型进一步细化为特定类型;r5:r5在r1满足后使用,规则r5定义为其中,x在规则r1中定义,规则r5表示calldatacopy指令不在以lt比较指令为嵌套循环条件的循环中,即指calldatacopy指令从调用数据的offsetm位置开始,读取len长度的数据,复制到内存的x+36位置,表示不存在,表示依赖于,如果规则r5被满足,则这条calldatacopy读取的函数的参数的类型为1维dynamicarray/bytes/string,其中,嵌套循环条件就是指lt比较指令的执行结果,执行结果为真或假,结果为假,则退出循环,即calldatacopy指令不在循环中,结果为真,继续循环;r6:规则r6定义为其中,规则r6表示calldatacopy指令从调用数据的offsetm位置开始,读取len长度的数据,复制到内存的ffsetc位置,且calldatacopy指令不在以lt比较指令为嵌套循环条件的循环中,表示不存在,表示依赖于,其中,m和c为位置变量,如果规则r6被满足,表明函数的参数的类型为public函数的1维staticarray;r7:规则r7在规则r5满足的前提下使用,定义为len==32×y,其中,len和y在规则r5和r1中定义,规则r7表示calldatacopy指定的操作数len长度包含y与32的乘法,表明calldatacopy指令从调用数据读取的数据为一个array,且array的每个元素都被扩展为32字节,因此,如果规则r7被满足,该calldatacopy指令读取的参数的类型为1维dynamicarray类型;r8:规则r8在r5被满足后使用,规则r8定义为其中,len和y在规则r5和r1中定义,的表示对y/32进行四舍五入,由于bytes/string类型在存入调用数据时需要扩展为32字节的整数倍,因此,如果规则r8被满足,则函数的参数的类型为public函数的bytes/string类型:r9:规则r9定义为其中,num1,...numn均为常数,in、numn和的含义同规则r2中的含义相同,calldatacopy(offsetm,offsetc,len)表示calldatacopy指令从调用数据的offsetm位置开始,读取len长度的数据,复制到内存的ffsetc位置,规则r9表示calldatacopy指令执行前,执行了n条lt比较指令,即calldatacopy指令位于一个n层嵌套循环条件中,如果规则r9被满足,函数的参数的类型为public函数中的n+1维staticarray;r10:规则r10在规则r1被满足后使用,r10被定义为和calldatacopy(offsetm,offsetc,len)的含义同规则r9中的含义相同,规则r10表示calldatacopy指令执行前,执行了n条lt比较指令,即calldatacopy指令在n层嵌套循环条件中,如果规则r10被满足,则calldatacopy指令读取的函数的参数的类型为public函数的n+1维dynamicarray;r11:规则r11定义为and(op1,op2),且op1的类型为uint256,op2的大端是k个0字节,k的范围在0到32之间,and指令接受从栈顶读取的两个操作数op1,op2,作用是按位进行与操作,每位相等为1,不等为0,如果规则r11被满足,则将op1的参数的类型从uint256细化为uint<256-8×k>:r12:规则r12定义为and(op1,op2),且op1的类型为uint256,op2的小端是k个0字节,k的范围在0到32之间,and指令接受从栈顶读取的两个操作数op1,op2,作用是按位进行与操作,每位相等为1,不等为0,由于bytes类型的参数的小端进行了0填充,读取时使用小端为0的数进行掩码操作,因此,如果规则r12被满足,则op1的参数的类型从uint256细化为bytes:r13:规则r13定义为sigextend(op,m),且op的类型为uint256,0<m<32,op,m分别表示从栈顶读取的操作数,由于sigextend指令的功能为扩展有符号数类型参数的长度,因此,如果规则r13被满足,则将op的类型从uint256细化为int<(m+1)×8>;r14:规则r14定义为x=iszero(op)∧y=iszero(x),且op的类型为uint256,x和y为本规则中定义的变量,x=iszero(op)表示检查op的值是否为0,如果为0,将1赋值给x,如果不为0,将0赋值给x,y=iszero(x)表示检查x的值是否为0,如果为0,将1赋值给y,如果不为0,将0赋值给y,规则r14表示使用两条iszero指令对op进行掩码操作,因此,如果规则r14被满足,将op的类型从uint256细化为bool;r15:规则r15定义为sdiv/smod/slt/sgt(op1,op2),且op1的类型为uint256,由于sdiv/smod/slt/sgt分别表示sdiv指令、smod指令、slt指令和sgt指令,sdiv指令、smod指令、slt指令和sgt指令分别用于有符号数除法、有符号数取模、有符号数小于比较和有符号数大于比较,op1,op2分别表示从栈顶读取的操作数,因此,如果规则r15被满足,则将op1的类型由uint256细化为int256,对op2的类型不做要求;r16:规则r16定义为op1,op1表示从栈顶读取的操作数,未被任何数学运算指令使用,且op1的类型为uint160,由于address类型的长度为160位,且不能使用数学运算指令处理address类型,因此,如果规则r16被满足,则op1的类型从uint160细化位address类型;r17:规则r17定义为byte/mstore(op),且op的类型为bytes/string,op表示从栈顶读取的操作数,byte/mstore指令分别表示byte指令和store指令,都用于读取op的单个字节,由于bytes类型支持单个字节访问而string类型不支持,因此,如果规则r17被满足,则将op的类型细化为bytes;r18:规则r18定义为byte(op),且op的类型为uint256,op表示从栈顶读取的操作数,byte指令用于读取op的单个字节,由于bytes类型支持使用该指令单个字节访问而uint256类型不支持,因此,如果r18被满足,则将op的类型细化为bytes32;r19:规则r19定义为规则r19表示offset1的读取位置是一个常数,而offset2读取位置由offset1计算得出,以此类推,即calldataload指令的读取位置由参数中某个元素的offset字段计算得出,参数中存在着具有动态长度的参数,offset1=calldataload1(loc1),ffset2=calldataload2(loc2),offsetn=calldataloadn(locn)表示使用calldataload指令从调用数据的locn位置读取32字节数据,并赋值给offsetn,表示依赖于,因此,如果规则r19被满足,则函数的参数的类型为nestedarray或struct;r20:规则r20定义为x=calldataload(0)∧mstore(offsetm,x),x为本规则中定义的变量,x=calldataload(0)表示使用calldataload指令从调用数据的开始位置读取数据,并赋值给x,mstore(offsetm,x)表示将×使用的mstore指令存储到内存的offsetm位置,由于vyper编译的智能合约在字节码开始处会使用calldataload指令和mstore指令将调用数据的前4字节,即functionid存储到内存中,而solidity编译的智能合约没有这个特点,因此,如果规则r20被满足,则该字节码为vyper字节码;r21:规则r21在规则r19被满足后使用,r21定义为,i表示要访问的array元素的下标,calldataload(offset1+0x4)表示array的大小,lt(i,calldataload(offset1+0x4))表示lt比较指令检查读取的array元素下标i是否小于array的大小,如果不存在这样的一条lt比较指令,则表明参数的类型不是array,而是struct,calldataload(offset1+0x4+0x32×i)表示calldataload指令从调用数据的offset1+0x4+0x32×i读取一个参数,表示依赖于,表示不存在,规则r21表示offset1的读取位置为一个常数,且在使用calldataload指令之前,不存在对offset1+0x4位置的边界检查指令,0x4是16进制数,由于struct类型参数内没有num字段,即访问struct内部元素前,不经过lt比较指令做边界检查的过程,因此如果规则r21被满足,则calldataload指令读取的参数类型为struct类型;r22:规则r22在规则r19被满足后使用,规则r22定义为且其中,numx可以为常数或变量,且num1,…,numn-1中至少有1个为变量,exp(loc)、in和的含义同规则r2中的含义相同,规则r22表示在执行calldataload之前,使用lt比较指令进行了n次边界检查,因此如果规则r22被满足,则可以推断函数参数的类型为n维nestedarray;r23:规则r23在规则r20被满足后使用,r23定义为×=calldataload(loc)∧calldatacopy(offsetm.offsetc,len),且len为常数,offsetc由x计算出,x=calldataload(loc)表示使用一条calldataload指令从调用数据的ioc位置读取数据,并赋值给x,calldatacopy(offsetm.offsetc,len)表示calldatacopy指定从调用数据的offsetm位置开始,读取len长度的数据,复制到内存的ffsetc位置,∧表示且,由于vyper智能合约中,使用calldatacopy指令将定长bytes或string类型参数的元素个数字段和整个参数值从调用数据复制到内存中,因此,如果规则r23被满足,则函数的参数的类型为定长bytes或string;r24:在规则r20被满足后,规则r24被使用,r24满足的条件与r3相同,如果规则r24被满足,则将函数的参数的类型为定长list;r25:在规则r20被满足后,规则r25被使用,r25的定义为×=calldataload(loc)且规则r23和r24不满足,规则r25表示在缺少有效线索的情况下,将函数的参数的类型暂时看作为uint256;r26:在规则r23满足后,规则r26被使用来区分vyper中的定长bytes和定长string类型,规则r26定义为byte/mstore(op),且op的类型为定长bytes/string,byte/mstore指令分别表示byte指令和mstore指令,用于读取op的单个字节,op表示从栈顶读取的操作数,由于定长bytes类型支持单个字节访问而定长string类型不支持,因此,如果规则r26被满足,则将op的类型细化为定长bytes;r27:在规则r25被满足后,使用规则r27,规则r27定义为lt(op1,op2),且op1的类型为uint256,op2的值为2160,op1,op2分别表示从栈顶读取的操作数,由于vyper使用lt比较指令将address参数与2160进行比较,检查address类型参数是否合法,因此,如果规则r27被满足,则该函数参数的类型为address;r28:在规则r25被满足后,使用规则r28,规则r28定义为slt(op1,op2)∧sgt(op1,op3),且op1的类型为uint256,op1,op2,op3分别表示从栈顶读取的操作数,∧表示且,op2和op3的值分别为2127-1和-2127,规则r28表示使用slt指令和sgt指令检查op1的值是否在2127-1和-2127之间,因此,如果规则r28被满足,则可以将函数参数的类型由uint256细化为int128;r29:在规则r25被满足后,使用规则r29,r29定义为slt(op1,op2)∧sgt(op1,op3),且op1的类型为uint256,op1,op2,op3分别表示从栈顶读取的操作数,∧表示且,op2和op3的值分别为精确度为10的decimal类型值2127-1和-2127,表示使用slt指令和sgt指令检查op1的值是否在2127-1和-2127之间,因此,如果规则r29被满足,将函数的参数的类型由uint256类型细化为decimal类型;r30:在规则r25被满足后,使用规则r30,r30定义为lt(op1,op2),且op1的类型为uint256,op1,op2分别表示从栈顶读取的操作数,op2的值为2,由于bool类型的取值为true或false,对应数字值为1或0,因此,规则r30表示使用lt比较指令进行bool类型参数的合法性检查,如果r30被满足,则可以将函数的参数的类型由uint256细化为bool;r31:在规则r25被满足后,使用规则r31,r31的定义与r18相同,如果规则r31满足,则函数参数的类型被为bytes32;上述calldataload指令和lt比较指令中,若带有下标,下标表示不同的calldataload指令和lt比较指令。一种恢复函数参数类型的类型感知符号执行方法,包括如下步骤:b1:粗粒度类型推断:使用规则r20区分智能合约的字节码属于solidity还是vyper字节码,即solidity智能合约还是vyper智能合约.再使用规则r1-r10和r19-r25对solidity智能合约中参数的struct、array、bytes、string和基本类型或vyper智能合约中参数的定长list、定长bytearray、定长string和基本类型进行识别;b2:确定参数数量和顺序:通过统计步骤b1中使用规则r1、r3、r4、r6、r9、r21、r22、r23、r24和r25的使用次数,得到参数数量,再根据相关实参在调用数据的排列顺序得到函数签名中参数的顺序,其中,相关实参是指使用规则r1、r3、r4、r6、r9、r21、r22、r23、r24和r25识别出的参数;b3:引入参数相关符号:当智能合约读取参数时,使用相同符号标记该参数的所有字节来引入参数相关符号;b4:细粒度类型推断:基于引入参数相关符号,使用规则r11-r18和r26-r31区分solidity和vyper智能合约中参数的基本类型,并识别solidity智能合约中array类型的参数和nestedarray类型的参数中每个元素的类型,类型struct中每个元素的类型,vyper智能合约中定长list类型参数中每个元素的类型,识别后,将步骤b1中得到的solidity和vyper智能合约的参数的基本类型细化为更加具体的类型,细化后,再基于步骤b2中确定的参数数量和顺序,将参数顺序重新排列,得到参数的类型列表,其中,基本类型共有五种,分别是uint<m>,8≤m≤256,m是8的倍数,包括uint8,uint16,…,uint256,int<m>,8≤m≤256,m是8的倍数,包括int8,int16,…,int256,address,bool和bytes。一种针对区块链智能合约函数签名自动化恢复的方法,包括如下步骤:c1:将智能合约的字节码文件进行反汇编,得到反汇编代码文件;c2:扫描反汇编代码文件中的反汇编代码,将反汇编代码分割成代码块;c3:对分割的代码块进行静态分析,识别出public/external函数的代码块,即获得functionid:c4:对识别出的函数的代码块使用类型感知符号执行方法,即基于参数的类型规则采用类型感知符号执行方法,得到每个public/external函数的参数的类型,得到智能合约的函数的参数的类型列表;c5:使用c3和c4步骤获得的functionid和类型列表,得到智能合约的函数签名。进一步,所述步骤c2的具体实现为:扫描反汇编代码,找到所有的jumpdest指令,每个jumpdest指令都是一个代码块的开头,找到所有的jump/jumpi/stop/return/revert/invalid指令,jump和jumpi指令分别是有条件跳转和无条件跳转指令,stop、return、revert和invalid指令会终止智能合约的执行,每个上述指令标志着一个代码块的结束,根据代码块的开始和结束标志,将反汇编代码划分为基本块,即代码块;所述步骤c3的具体实现为:根据反汇编文件从solidity智能合约或vyper智能合约得来,分别进行如下处理:solidity智能合约:如果代码块的字节码中使用lt比较指令比较调用数据的前4字节和一个4字节常数,随后使用了jumpi指令,即可定位fucntionid和函数的第一个块,其4字节常数即为fucntionid,而jumpi指令的跳转地址即为函数的第一个块;vyper智能合约:如果代码块的字节码中调用数据的前4字节与一个4字节整数进行比较,并在随后执行了一条jumpi指令,即可定位functionid和函数的第1个块,其4字节常数即为functionid。一种针对区块链智能合约函数签名自动化恢复的系统,包括:反汇编模块:用于将智能合约的字节码文件进行反汇编,得到反汇编代码文件;代码块识别和函数识别模块:用于扫描反汇编代码文件中的反汇编代码,将反汇编代码分割成代码块,对分割的代码块进行静态分析,识别出public/external函数的代码块,即获得functionid;类型感知符号执行模块:对识别出的函数的代码块使用类型感知符号执行方法,即基于参数的类型规则采用类型感知符号执行方法,得到每个public/external函数的参数的类型,得到智能合约的函数的参数的类型列表;输出模块:functionid和类型列表,得到智能合约的函数签名输出。本发明同现有技术相比,其有益效果表现在:一、本发明中的函数签名恢复的准确率高:使用本发明恢复函数签名的正确率达到98.7%,同类工具gigahorse,eveem最高仅能达到58.1%。二、本发明中的函数签名恢复效率高:使用本发明恢复函数签名平均用时为0.074秒。三、本发明中的函数签名恢复的正确率受编译器版本更新的影响小:使用本发明恢复函数签名,正确率基本不受编译器版本更新的影响,在目前solidity编译器总共155个版本上的准确率都不会低于96%。四、本发明中的恢复函数签名不需要函数签名数据库的支持:本发明不需要查询函数签名数据库即可恢复智能合约函数签名。五、本发明适应智能合约编程语言语法和类型变化的能力强:函数签名恢复使用的启发式规则的生成方法的五个步骤中,前四步都可以自动化进行,当智能合约编程语言出现语法和类型的变化时,仅需少量的人力介入生成新的规则就能继续使用。六、本发明同时支持solidity和vyper两种主流智能合约编译器,对于智能合约函数签名恢复的适用面广。附图说明图1为本发明的总体架构图;图2为本发明中类型感知符号执行规则的层次结构。具体实施方式下面将结合附图及具体实施方式对本发明作进一步的描述。反汇编模块:反汇编模块的输入内容为以太坊智能合约的字节码文件,该字节码文件可由solidity编译器或vyper编译器产生,即得到solidity智能合约或vyper智能合约,反汇编模块使用gethdisassembler对合约字节码文件进行反汇编,产生智能合约的反汇编代码文件。智能合约包含一个类型为public函数或external的函数,函数包含一个随机类型的参数和访问该参数的语句,例如其中,a是生成的智能合约,智能合约a中包含了一个public函数b,函数b有一个uint256参数c,函数b中的访问该参数c的语句为”uint256d=c”,表示访问了参数c并赋值给d。代码块识别和函数识别模块:扫描反汇编代码文件中的反汇编代码,找到所有的jumpdest指令,每个jumpdest指令都是一个代码块的开头,找到所有的jump/jumpi/stop/return/revert/invalid指令,“/”表示或,jump和jumpi分别是有条件跳转和无条件跳转指令,stop、return、revert和invalid指令会终止智能合约的执行,每个上述指令标志着一个代码块的结束。根据代码块的开始和结束标志,将反汇编代码划分为基本块,即代码块。随后,根据反汇编文件从solidity合约或vyper合约得来,分别进行如下处理:(1)solidity合约:如果代码块的字节码中使用比较指令lt比较调用数据的前4字节和一个4字节常数,随后使用了jumpi指令,据此可以定位fucntionid和函数的第一个块,其4字节常数即为fucntionid,而jumpi指令的跳转地址即为函数的第一个块。(2)vyper合约:如果代码块的字节码中调用数据的前4字节与一个4字节整数进行比较,并在随后执行了一条jumpi指令,则据此定位functionid和函数的第1个块。需要指出,4字节常数即为functionid,函数的第1块在jumpi指令之后,这点与solidity不同。类型感知符号执行模块:该模块对代码块识别和函数识别模块识别出的函数的代码块进行类型感知符号执行,探索合约中的路径,如果在执行过程中,发现跳转目标由函数参数或其他输入确定,则停止。以图2所示,该模块的主要执行流程包括:(1)粗粒度类型推断:使用规则r20区分智能合约字节码属于solidity还是vyper字节码,使用规则r1-r10和r19-r25识别solidity智能合约的参数的类型struct、array、bytes、string和基本类型,识别vyper智能合约参数的类型的定长iist、定长bytearray、定长string和基本类型。根据是否满足规则r1-r10,r19-r25,判断函数参数是否为基本类型,但暂不决定该参数具体为基本类型的哪一种,即不决定参数的完整类型,同时不区分bytes和string类型的区别。struct类型是一个结构体,由内部一组成员元素构成,各个元素的类型可以不同,这些元素的类型可以是基本类型或struct。在本步骤中,仅判断参数的类型是否为struct,而不判断struct内部元素的具体类型。array类型是一个数组,由一组相同类型的元素构成。元素的类型可以是基本类型、struct或array。array类型进一步可分为(1)staticarray,其具有固定的长度和维数,(2)dynamicarray,其具有固定的维数,但长度可变,(3)nestedarray,其维数和长度都可变。在本步骤中,仅判断参数的类型是否为array,而不判断array内部的元素的具体类型。本步骤中,各规则的具体使用方式如下:(1.1)使用规则r20,如果满足规则r20,如果满足规则r20,则字节码文件为vyper智能合约,如果不满足规则r20,则字节码文件为solidity智能合约。(1.2)对于solidity智能合约中未知类型的参数,使用规则r1、r3、r6、r9、r4和r19。(1.2.1)如果规则r1成立,推断参数的类型为dynamicarray/bytes/string,使用规则r2、r5和r10。若规则r2、r5和r10均不满足,推断参数的类型为bytes/string,进一步使用规则r17,如果满足规则r17,推断参数的类型为string,否则推断参数的类型为bytes。如果满足规则r2,则推断函数参数的类型为dynamicarray。如果规则r10满足,则推断参数的类型为多维dynamicarray。若规则r5满足,进一步使用规则r7和r8,如果满足r7,推断参数的类型为1维dynamicarray,如果r8满足,推断参数的类型为bytes/string。对bytes/string表示参数类型为bytes或string,其类型为前者或后者等待进一步推断。(1.2.2)如果规则r3成立,推断函数的参数的类型为staticarray,但array元素的类型等待进一步推断。(1.2.3)如果规则r6成立,推断函数的参数的类型为1维staticarray,但array元素的类型等待进一步推断。(1.2.4)如果规则r9成立,推断函数的参数的类型为n维staticarray,但staticarray元素的类型等待进一步推断。(1.2.5)如果规则r4满足,暂时推断函数的参数的类型为uint256,该参数完整类型等待进一步细化为int8,int16,...,int128,int256,uint8,uint16,...,uint128,uint256,bool,address类型。(1.2.6)如果规则r19满足,推断函数的参数的类型为struct/nestedarray。(1.3)对于vyper智能合约中未知类型的参数,使用规则r23、r24和r25。(1.3.1)如果规则r23满足,推断参数的类型为定长bytearray/string。定长bytearray/string表示参数类型为定长bytearray或string,其具体为前者或后者等待进一步推断。(1.3.2)如果规则r24满足,推断函数的参数的类型为定长list,但list元素的类型等待进一步推断。(1.3.3)如果规则r25满足,暂时推断参数的类型为uint256,该参数完整类型等待进一步细化为int128,int256,uint128,uint256,bool,address类型。(2)确定参数数量和顺序,该步骤中通过统计步骤b1中使用规则r1、r3、r4、r6、r9、r21、r22、r23、r24和r25的使用次数,推断参数数目。通过计算r1规则的使用次数,可以得到solidity智能合约dynamicarray/bytes/string类型的参数的数量并记为n1。通过计算规则r3、r6和r9的使用次数,可以获得solidity智能合约staticarray类型的参数的数量并记为n2。由于r4将所有的基本类型看作uint256,因此可通过计算规则r4的使用次数来获得solidity智能合约基本类型的参数数量并记为n3。通过计算规则r21和r22的使用次数,获得solidity智能合约nestedarray和struct类型的参数的数量并记为n4。通过计算规则r23和r24的使用次数,获得vyper合约定长list,定长bytearray和定长string的参数数量并记为n5。n1+n2+n3+n4+n5+n6即为所有函数的参数的总数。随后,根据相关实参在调用数据的排列顺序推断函数签名中参数的顺序,其中,相关实参是指使用规则r1、r3、r4、r6、r9、r21、r22、r23、r24和r25识别出的参数。(3)引入参数相关符号:当智能合约读取参数时,使用相同符号标记该参数的所有字节来引入参数相关符号。当智能合约执行calldataload和calldatacopy指令后,将对应的栈上元素和内存区域也用相同符号进行标记,当智能合约执行mload指令后,将mload指令读取的内存区域的符号复制到栈顶,目的是将相同的参数用同样的符号标记,例如,内存中区域a的标记为参数1,使用mload指令把区域a存储的参数读取到栈顶,那么栈顶的这个参数也被标记成参数1。通过引入从调用数据读取参数时的参数相关符号,判断各指令是否作用于某个函数的参数,以及具体作用于哪一个参数。其中,调用数据是指由函数的fucntionid和给被调用函数传入的参数按照顺序和结构构成的数据。调用数据为智能合约的通用术语,例如一个函数add(uint8a,uint8b)。这个函数接收两个参数,分别是uint8类型的参数a和uint8类型的参数b。调用这个函数的时候,就需要把参数a和b传给这个函数。调用数据就是把函数的fucntionid和参数按照函数声明的顺序排列好的数据。调用数据中,参数需要被扩展为32字节的整数倍,因此a和b都被扩展为32字节。假设传入的参数a=1,b=2,则调用数据的前4字节为函数add的functionid,第4到36字节为uint8类型参数a,即1,第36到第68字节为uint8类型参数b,即2。把这个调用数据传给函数,函数中有相应的calldatacopy指令和calldataload指令,把1和2(也就是a,b)读取出来。如果参数的类型是array等更复杂的不定长结构,调用数据中还会添加offset字段指示数据的开始位置,添加num字段指示数据的长度。(4)细粒度类型推断:使用规则r11-r18和r26-r31区分solidity智能合约和vyper智能合约的基本类型,通过推断array、nestedarray和struct参数中单个元素的类型,以及推断定长list中单个元素的类型,基于粗粒度类型推断后,进行细粒度类型推断的主要流程如下:(4.1)对于solidity智能合约中的参数,若步骤(1.2.1)的类型推断结果为bytes/string,进一步使用规则r17,如果满足规则r17,推断参数的类型为string,否则推断参数的类型为bytes。若步骤(1.2.1)的类型推断结果为dynamicarray,进一步推断dynamicarray元素的类型,推断元素的类型的方法为:使用规则r11、r12、r13、r14、r15和r18。如果满足规则r11,则dynamicarray的元素的类型为uint<m>,并继续使用规则r16,如果满足r16,则dynamicarray的元素的类型为推断为address。如果满足规则r12,则dynamicarray的元素的类型为bytes<m>。如果满足规则r13,则dynamicarray的元素的类型为int<m>。如果满足规则r14,则dynamicarray的元素的类型为bool。如果满足规则r15,则dynamicarray的元素的类型为int256。如果满足规则r18,则dynamicarray的元素的类型为bytes32。(4.2)对于步骤(1.2.2)、(1.2.3)、(1.2.4)和(1.2.6)推断出的staticarray,1维staticarray,n维staticarray,struct/nestedarray,进一步推断其内部元素的类型。推断此类参数内元素的类型的方法为:使用规则r11、r12、r13、r14、r15和r18。如果满足规则r11,则元素的类型为uint<m>,并继续使用规则r16,如果满足r16,则元素的类型为推断为address。如果满足规则r12,则元素的类型为的类型为bytes<m>。如果满足规则r13,则元素的类型为int<m>。如果满足规则r14,则元素的类型为bool。如果满足规则r15,则元素的类型为int256。如果满足规则r18,则元素的类型为bytes32。(4.3)对于步骤(1.2.5)推断出的uint256类型,进一步使用规则r11、r12、r13、r14、r15和r18。如果满足规则r11,将参数的类型细化为uint<m>,并继续使用规则r16,如果满足r16,将参数的类型推断为address。如果满足规则r12,推断参数的类型为bytes<m>。如果满足规则r13,推断参数的类型为int。如果满足规则r14,推断参数的类型为bool。如果满足规则r15,推断参数的类型为int256。如果满足规则r18,推断参数的类型为bytes32。(4.4)对于vyper智能合约中的函数参数,步骤(1.3.1)推断出的定长bytearray/string类型,进一步使用规则r26。如果规则r26满足,则函数的参数的类型推断为定长string,若规则r26不满足,函数的参数的类型推断为定长bytearray。(4.5)对步骤(1.3.2)推断出的定长list类型,进一步推断list元素的类型,推断list元素的类型的方法为:使用规则r27-r31。如果满足规则r27,将list元素的类型推断为address。如果满足规则r28,则list元素的类型为int128。如果满足规则r29,则list元素的类型为decimal。如果满足规则r30,则list元素的类型为bool。如果满足规则r31,则list元素的类型为bytes32。(4.6)对步骤(1.3.3)推断出的uint256类型,进一步使用规则r27-r31。如果满足规则r27,将参数的类型细化为address。如果满足规则r28,将参数的类型细化为int128。如果满足规则r29,将参数的类型细化为decimal。如果满足规则r30,将参数的类型细化为bool。如果满足规则r31,将参数的类型细化为bytes32。(4.7)上述具体化的参数再基于步骤b2中确定的参数数量和顺序,将参数顺序重新排列。如:函数func(uint8a,uint16b)。b2步骤分析出函数的参数总共有2个,且顺序为uint8型的a在前,uint16型的b在后,在引入参数相关符号之后,与a相关的标记为参数1,与b相关的标记为参数2。那么细粒度类型推断得到参数2是uint16,参数1是uint8后,按照b2步骤得到的顺序调整为(uint8,uint16)。(5)输出模块:根据获得的functionid和步骤(4)中获得的函数的参数的类型列表,该系统输出恢复的函数签名。以上仅是本发明众多具体应用范围中的代表性实施例,对本发明的保护范围不构成任何限制。凡采用变换或是等效替换而形成的技术方案,均落在本发明权利保护范围之内。当前第1页12当前第1页12
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1