反编译器级漏洞扫描器
一、漏洞扫描引擎简介
GDA漏洞扫描引擎由漏洞检测引擎和规则解释引擎构成:
漏洞检测引擎采用两级HASH TABLE来存储method,以实现method的快速定位,同时结合GDA独有高速反编译内核、HIRA(高级中间表示分析器)、API链检测器等底层分析模块构建而成的一个高效漏洞检测引擎。
规则解释引擎采用基于堆栈状态机的规则判定法,同时采用了一种以动态执行的方式进行规则解释(动态规则解释器)。其中规则表达式通过词法分析器和语法分析器将规则表达式存入堆栈,规则解释器是基于堆栈做规则表达式的解释执行。这里之所以是动态的,体现在两个方面:1、词法分析器和语法分析器从左到右扫描规则,一旦检查出一条完整表达式后或者检测到算符优先级下降时,就会调用解释器做堆栈执行,这里不会将整条规则解析到堆栈后再调用解释器执行;2、动态解释执行后的结果是一种布尔状态的值(结果中常常还会携带有更复杂的参数),同样GDA的规则解释引擎也不会等到所有表达式执行完后再做规则判定,而是采用效率更高的动态判定方法。
二、快速上手
GDA的漏洞扫描是一个规则驱动的静态扫描器,因此我们只要学会正确的定义规则便可以使用。
如下是一条典型的规则:(更多规则案例)
这是一条典型的GDA漏洞规则,其中id是规则的唯一标识(可选配),rule是规则表达式(定义规则),type代表该规则的类型(如加密型安全漏洞),vname为该、漏洞名称(自定义),vlevel为该规则等级(自定义),description为漏洞描述,suggestion为漏洞建议,returnType分为callors(返回最后匹配结果的位置)、methods(返回方法匹配结果)和strings(返回匹配的字符串结果).
该条规则的规则表达式由内置函数和内置变量组合而成,该规则用于检测一个app中是否使用了不安全加密选项。内置函数用于从当前分析的APP中匹配相关的api函数,然后将匹配的函数存储于api对象中,第二个表达式使用api对象的callors属性,指示GDA去收集匹配结果的调用者,refString属性收集调用者函数所引用的字符串,最后通过正则包含的方式匹配其后的正则表达式。
在GDA中通过菜单Tools->Vul Tester启动漏洞规则测试窗口。
然后点击Load Rules按钮加载你自定义的规则文件(请事先将所有规则存储在.rule文件中),然后所有规则会显示在左侧的列表中,点击每个列表可以查看具体的规则内容,你可以对规则进行编译运行,删除和修改。
1.启动规则测试器;2.加载规则文件;3.点击规则查看;4.编译执行规则。
此外在GDA中,你还可以通过菜单->文件->Execute Vul Rule直接加载、编译、执行自定义的规则文件。
如下是其中一个规则扫描的案例:
在扫描结果中,我们还可以双击[]之间的方法或者类直接定位到存在漏洞风险出现的地方。
三、完整的内置函数,内置变量和操作符介绍
1、内置函数
·内置函数是可以在规则中直接使用的函数。
函数 |
说明 |
api.match(name,package,argn) |
主要用于定位api函数的位置,函数执行后会将执行结果存储在内存中,其后可以跟随一个api相关的内置变量来对该结果进行过滤。其中参数name表示方法名,package是该方法的包名,argn代表该方法的参数个数,其中name和package支持同时匹配多个方法和包,用’|’隔开)。使用形式([]可选)为: api.match(“mname[|mname1|mname2|....]”)//只匹配api函数名; api.match(“mname[|mname1|mname2|....]”,”package[|package1|package2|....]”)//匹配函数名和包名; api.match(“mname[|mname1|mname2|....]”,”package[|package1|package2|....]”,”2”)//匹配函数名和包名,指定参数个数为2; api.match(“mname[|mname1|mname2|....]”,”package[|package1|package2|....]”,”2-5”)//匹配函数名和包名,指定参数个数范围,参数在2-5之间(包含2,5); api.match(“mname[|mname1|mname2|....]”,”1”)//匹配函数名,指定参数个数为1; api.match(“mname[|mname1|mname2|....]”,”2-4”)//匹配函数名,指定参数个数范围,参数在2-4之间(包含2,4) |
method.match(name,package,argn) |
同上 |
apk.exposed(typename) |
该函数检查apk是否存在组件暴露问题。Typename只能取如下值: "activity","receive","service"分别检查三种组件是否存在暴露的问题。 |
2、内置变量
内置变量包含4大类,分别为api内置变量,method内置变量,apk内置变量,mainfest内置变量。其中api和method内置变量为函数型内置变量,其代表内存中所有的api或者method,其都包含调用者相关属性;apk内置变量表示apk文件,其包含有最小版本,apk字符串等,mainfest变量代表androidmainfest.xml相关信息。具体说明如下:
内置变量 |
执行效率 |
说明 |
api.callors.body.len method.callors.body.len |
高 |
必须存在与match内置函数之后,从函数执行的结果中过滤出符合该内置变量指定条件的结果。该变量会指引反编译器去反编译结果函数的调用者,然后对调用者的函数体长度做整型匹配(适合操作符>,<,>=,<=,!=)。 |
api.callors.body1 method.callors.body1 |
高 |
该变量会指引反编译器去反编译结果函数的调用者,然后对调用者的函数体做字符串匹配(适合操作符^,!^,^~)。 |
api.callors.body2 method.callors.body2 |
高 |
该变量会指引反编译器提取调用者使用的函数列表,然后对该函数列表做函数匹配(适合操作符^,!^,^~) |
api.callors.refString method.callors.refString |
高 |
该变量会指引反编译器提取调用者的引用字符串,并做字符串匹配(适合操作符^,!^,^~) |
method.body.len |
高 |
该变量会指引反编译器反编译匹配结果中的method,然后对该method函数体长度做整型匹配(适合操作符>,<,>=,<=,!=) |
method.body1 |
高 |
该变量会指引反编译器反编译匹配结果中的method,然后对调用者的函数体做字符串匹配(适合操作符^,!^,^~)。 |
method.body2 |
高 |
该变量会指引反编译器提取匹配method使用的函数列表,然后对该函数列表做函数匹配(适合操作符^,!^,^~) |
apk.minsdk |
高 |
该变量指示apk兼容的最小sdk版本(适合操作符>,<,>=,<=,!=) |
apk.refstrings |
低 |
该变量指示apk中所有DEX引用的字符串(适合操作符^,!^,^~) |
apk.allstring |
低 |
该变量指示apk中所有DEX的字符串(适合操作符^,!^,^~) |
apk.allstring.line |
低 |
该变量指示apk中所有DEX的字符串,按行匹配(适合操作符^,!^,^~) |
mainfest.application |
高 |
该变量指示AndroidManifest中的application的所有字符串(适合操作符^,!^,^~) |
manifest.body |
高 |
该变量指示AndroidManifest的所有内容(适合操作符^,!^,^~) |
mainfest.permission |
高 |
该变量指示AndroidManifest的所有permission(适合操作符^,!^,^~) |
3、操作符
操作符 |
优先级别 |
说明 |
|| |
0 |
语句或关系 |
&& |
0 |
语句与关系 |
^ |
1 |
字符串包含 |
!^ |
1 |
字符串不包含 |
^~ |
1 |
字符串正则包含 |
== |
1 |
字符串/整数相等 |
!= |
1 |
字符串/整数不相等 |
> |
1 |
整数大于 |
< |
1 |
整数小于 |
>= |
1 |
整数大于等于 |
<= |
1 |
整数小于等于 |
! |
2 |
非 |
() |
3 |
括弧 |