查看原文
其他

如何用lint扫出不安全代码

xvale vivo千镜 2022-11-05

点击上方蓝字关注我们噢~

每个开发者都有一个梦想,希望写出来的代码没有Bug。虽然我们知道这是不可能的,退而求其次,那我们能否尽可能在编码阶段发现更多的潜在问题呢?这个是有可能的,这就是今天要介绍的主题lint。

01

Lint是什么


Lint是google提供的一款静态代码扫描工具,目的是帮助开发者发现代码的质量问题。


相对于其他的工具,Lint有如下优势:

1)功能强大,支持java文件、class文件、资源文件、Gradle等文件的检测。

2)与android studio天然集成,可一键完成lint检测。

3)Lint专为android设计,规则丰富,原生已提供几百个实用规则。

4)扩展性强,支持自定义lint规则

......



02

Lint的原理


Lint功能如此强大,具体Lint是如何做到的?下图是Lint扫描的工作流。
lint工具的代码扫描工作流
App源文件:包含组成Android项目的文件,如java、Kotlin、xml等Lint.xml: lint工具的配置文件,可根据业务场景调整问题的严重级别,或忽略某项检测项。
通过上图,可推断大致流程,lint Tool加载App Source文件及配置文件,然后根据内置规则及定制规则解析文件,最终生成lint out的报告。
根据推测,我们去分析Lint的源码,结果基本符合我们的猜想。整个lint工具包含三个核心库文件:lint.jar,lint-api.jar,lint-checks.jar。

Lint.jar是整个lint的核心及入口,并在lint.jar中调用lint-api.jar及lint-checks.jar。


Lint-api.jar主要是对api进行一些封装,而lint-checks主要是内置规则的一些实现,下文将简单介绍整个lint的加载过程。



主入口是,Lint.jar的main函数,具体如下:

Main中run方法包含lint工具的处理流程。Run函数主要流程如下:
通过BuiltinIssueRegistry将内置规则和自定义规则进行加载,具体如下:
当规则准备好以后,client开始run,进入实际的分析流程。


Client的run方法实际调用driver的analyze()方法,driver实际为lint-api.jar中LintDriver。analyze()方法调用checkProject进行项目的检查。

CheckProject方法又继续调用runFileDetectors进行分析。



以xml文件为例,在runFileDetectors会调用XmlVisitor.visitFile方法,最终调用相应scanner的具体方法。


至此,整个规则加载及检测流程基本清晰。

03

Lint的重要概念


Lint提供一些API,我们可以根据API来编写自定义的规则,但其中涉及几个重要的概念,理解清楚有助于我们编写规则。具体的概念如下:
Issue:表示一条规则。
IssueRegistry:用于注册Issue。自定义的Lint最终会生成jar包,jar包的Manifest须指向IssueRegistry类。
Detector:检测单元。每个Issue对应一个Detector。
Scope:声明Detector作用域,即扫描代码的范围,如java源文件、xml资源文件、Gradle文件等。
Scanner:扫描单元。扫描并发现代码中的Issue。每个Detector可以实现一个或多个Scanner。自定义Lint主要工作就是实现Scanner。



04

自定义lint


原理介绍差不多了,大家一定迫不及待想看看到底如何自定义lint吧。下面将介绍如何创建自定义lint。



创建Java Library

Android Studio中选择新建Module,选择Java Library

具体的创建过程不详细叙述,这里特别说明几处重要的注意项:

Gradle Plugin版本,请选择2.3.3,Gradle 版本选择3.3,否则一些包会找不到



项目目录下build.gradle修改,重点是依赖的版本号及maven目录



根目录下build.gradle文件


编写自定义lint


Registry注册Issue,如下我们注册一个名为VSecureRandomDetector的ISSUE

Scanner编写,通过getApplicableMethodNames定义关注的方法,这里我们只关心setSeed的方法。
然后在visitMethod中实现具体的逻辑,如当检查到setSeed方法时,我们需要判断方法的Class信息、参数信息等。如果不符合预期,则打印报告。详情如下:



创建aar库


由于最终需要将jar集成到android工程中,所以我们需要将jar打包成aar文件,方便集成到android工程中。


创建android Library

修改aar的build.gradle文件,建立和jar之间关系,最终将jar打包到aar中。详情如下:

最终执行build,生成libaar-debug.aar



自定义Lint集成


将libarr-debug.aar包放到项目目录的libs目录下,如果没有可以新建一个libs目录。然后在build.gradle中引入aar,具体可以参考下图:



接下来编写测试代码,看看自定义lint规则是否生效。测试代码如下:

测试代码中,当我们直接使用setSeed时,Android Studio显示红色波浪线,提示错误。报错原因是不符合VSecureRandomForTest规则的要求,而VSecureRandomForTest正是我们上文自定义的lint规则,证明规则生效了。
当然,当我们build工程的时候,android studio也会自动执行lint检查。当检测到error时,会停止build的执行,并给出代码行及问题的原因,所以排查起来也是相对方便。具体如下图:



Lint调试


编写规则过程,不可避免会出现错误,那我们能否去调试规则代码?当然可以。调试时,我们需要通过remote远程到项目上,执行调试。具体如下:
在规则项目中,建立remote,选择自己的代码模块,其他默认参数


在测试项目中,build项添加VM选项,VM选项可以从第一步的remote中拷贝,然后把suspend改为yes,具体如下图所示


在测试项目build.gradle中添加lint配置如下:


以debug方式运行测试项目,然后将规则项目attach到测试项目中,就可以调试了,如下所示


总结


自定义lint集成的过程中,踩了很多坑,大部分是环境的问题,如gradle的版本、android studio的版本等。


整个环境打通后,后续的工作就是研究规则编写。这部分内容,一方面需要理解lint的重要概念、加载原理,内容上文已介绍;另一方面需要贴合自身的业务,这部分不详细阐述。


本文主要起抛砖引玉的作用,欢迎拍砖。


参考:1.https://developer.android.com/studio/write/lint?hl=zh-cn2.https://tech.meituan.com/2018/04/13/waimai-android-lint.html




更多精彩阅读
如何用OLLVM来保护你的关键代码
一文读懂 | 内置安全成熟度模型BSIMM
像攻击者一样思考:论威胁分析中的建模模型




长按关注  更多安全技术干货等你发现 


好文!在看吗?点一下鸭!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存