写个代码扫描插件,再也不怕 log4j 等问题

引言

关于静态代码扫描,大家想必都非常熟悉了,比如 lintdetekt 等,这些也都是常用的扫描工具。但随着隐私合规在国内越来越趋于常态,我们经常需要考虑某些危险api的调用排查等等,此时上述的工具往往不容易实现现有的需求,以及后续扩展。而在这个背景下,ASM 就是解决方式的最佳手段之一。

故此,本篇我们将通过写一个代码扫描插件,从而简单玩转并入门 ASM :)

Github:Bee-AnalysisPlugin

背景

记得在前司(下厨房)的时候,我们 App 曾被报出存在漏洞问题,具体原因是:

项目中使用了log4j等api,导致存在安全漏洞。

其实当听到这个问题的时候,总感觉略有点离谱,客户端怎么会存在这个问题?

在我的印象中,log4j 似乎是21年时的一个广泛问题,当然主要影响是后端同学,团队内部也还排查过。但因为客户端和这系列库离的相对就比较远了,所以对于客户端的我们没有在意(为后面埋了伏笔)。

所以当真正收到相关部门邮件时,我们先是不相信,然后和另一个同学(化名z)开始着手排查:

结果还真是狠狠打脸了,项目历史代码中存在使用 HttpURLConnection 导致,而 HttpURLConnection 内部又引入了 Log4j 系列库,从而导致相关问题,于是就立即开始分工处理:

  • z负责写代码扫描插件,全量扫项目,从而确保已经完全移除相关api;
  • 我负责对代码层进行处理,对涉及到相关的 HttpURLConnection 逻辑进行移除与逻辑调整;

最终在收到问题的当天晚上就提了PR流程,总耗时大概3小时,也算是比较迅速。


事后来看, 虽然问题解决了,但同时也暴漏出了一些问题,比如 客户端代码 没有相关 危险代码扫描机制 ,导致这部分隐患一直处于黑盒状态。而从技术角度来思考,实现这个check也非常简单。

如下所示:

  • 定义一份线上的漏洞表(定期更新),每次 CI 时拉取最新的;
  • 定义一个代码扫描插件,每次 PR commit 时进行自动触发,并拉取最新的漏洞表,如果项目中存在相关漏洞,则中断本次打包并通知;

聊聊需求

通过上面的背景,我们大概也能知道本篇的缘由以及一些应用场景,所以如果要从练习角度入手,做一个代码扫描插件,其目的是静态扫描出相关方法的调用次数以及具体调用者,从而便于我们进行排查,应该怎么做?

此时可能会有同学抢答,我直接使用 Android Studio 全局搜索也行啊,为什么还需要专门写个插件扫描呢? 🤔

直接使用AS也能实现类似的需求,但是如果我们需要找出所有相关的调用处,这并不是一件易事,特别是对于复杂的项目而言(当然你要是没事愿意一个一个🔍,那另说了😑)。

而如果使用 ASM ,上述的需求实现起来就比较简单,而且后续的扩展也会相对成本较低,甚至我们还可以做一个调用替换等等,当然这些都是后话。

基础入门

为避免部分同学不太理解 ASM ,故这里选择先简单聊聊 ASM 基础背景,也算科普了(逃跑~)。

什么是ASM?

Java ASM(Java Bytecode Assembler)是一个用于 生成 和 修改 Java字节码的库。ASM 提供了一种灵活而强大的方式来分析、转换和生成Java类文件。使用 ASM ,我们可以在 不改变源代码 的情况下,通过操纵字节码来实现对代码的定制化需求。这种能力在许多领域中都有应用,包括 编译器 、代码优化字节码工具AOP(面向切面编程)框架等。

ASM与AGP关系

回到 Android 中,我们知道 Android虚拟机 是基于 Dalvik(5.0是ART),而 Dalvik 也是属于 JVM虚拟机 的一种。所以Android的开发语言是 Java (Kotlin会由编译器转为Java),而我们 Java 代码编译后的 class 文件为了便于 Dalvik 识别,故最终还需要转为dex 文件。

整个过程如下所示:

java -> class -> dex

常用的 AGP(Android Gradle Plugin) 插件,就是在 class -> dex 前,为开发者提供了一个时机,允许我们进行二次修改 Class ,从而实现自定义的需求,这也即是 ASM 在 AGP 中的作用由来。

ASM常见API
  • ClassReader

    负责对 Class 进行读写,最终调用 accpet 加载 class,由 ClassVisitor 开始进行处理;

  • ClassVisitor

    负责对读取到的 Class 进行操作,比如对 class 中某一部分信息(方法、属性等)进行修改;

ASM基础操作

总结起来通常就是三步:

  • 读取class,创建 ClassReader
  • 进行修改,创建 ClassVisitor(通常是ClassWriter+其他);
  • 保存结果,ClassWriter.toByteArray() ;

伪代码如下:

val cr = ClassReader(classStream)
val cw = ClassWriter(cr, 0)
val cv = xxxClassVisitor(cw)
cr.accept(cv, ClassReader.EXPAND_FRAMES)
FileOutputStream(outClassPath).use {
     it.write(cw.toByteArray())
}

ClassVisitor 提供了很多方法,比如当方法被调用时(visitMethod),开发者可以根据需求重写相应的方法,从而在 class 访问过程中,实现 class 修改。当然这些都只是最基础的操作,实际使用时我们还会使用其他更多的一些 Api ,由于本文并不是全面介绍相关 Api 的文章,故这部分就留给读者自行探索了:)

具体思路

要扫描代码,肯定是要先写一个 Plugin ,然后注册一个 Transform ,并在其中其中读取所有 class 与 jar ,从而对其进行处理。具体过程中,如果存在我们指定的方法调用,我们就将当前调用类的位置或者方法保存,最后当 ASM 处理结束后,我们再对结果进行处理。

不过需要注意的是 Transform 在 AGP7.0 已经被标注了 废弃AGP8.0 也已经正式 移除 ,所以我们要实现上述的逻辑,还是需要做一些改动。

故我们选用的是 AndroidComponentsExtension 来进行实现,这个 API 是Android团队专门针对 ASM 做的一个 hook 时机。不过需要注意的是,其并不像 Transform,我们可以 拿到所有class以及jar直接进行处理,而是当某个 class 被处理时,我们可以有时机进行拦截并处理。故如果我们想确保收集完所有信息,就必须在相应的 Task 之后再进行汇总处理,比如在 transformxxClassesWithAsm 之后。

实现效果

我们以检测业务中 PrintStream 类的调用为例,最终实现效果如下所示:

image.png

如上图所示,业务中一共有三处使用 PrintStream 类,分别调用的都是其 print() 以及 println() 方法。

当然对于结果的处理,无论是以文件形式保存还是其他方式,都是由我们自行处理,这里只是将其打印出来。

具体流程

示例Github: Bee-AnalysisPlugin

插件配置

作为开始,我们需要定义一个自己的插件类,需要继承自 Plugin 类,具体代码如下所示:

image.png

上述的流程我们分为3步:

  1. 创建我们的扩展实例(用于传递配置参数);
  2. 注册 AsmClassVisitor ,用于访问字节码;
  3. 当字节码处理完成后,统计处理结果;

具体的配置扩展类: RuleExtension

open class RuleExtension {
 var classPackages: Array<String> = emptyArray()
 var enableLog: Boolean = false
}

注意:这里需要增加open,否则编译失败;

ASM配置

在 AGP 7.0 之后,我们自定义的 ASM 访问器,需要继承自 AsmClassVisitorFactory ,并需要传入一个 InstrumentationParameters 泛型,用于确定是否需要实例化参数,因为我们需要对每个变体进行处理,所以这里传入 buildType 作为分类。当然如果并不需要传参的话,这里的工厂泛型可以直接传入 InstrumentationParameters.None ;

image.png

 

image.png

上述的流程如下:

我们定义了一个 字节码工厂访问器,并规定只处理非 Androidx 以及 R. 相关的 class,这样当字节码在处理时,如果当前class满足条件,就会触发 createClassVisitor() 方法,从而我们就可以创建自己的 字节码访问类,并使用这个处理类对当前字节码进行修改。

当我们在读取 class 时,内部会对相关的方法、构造函数、属性等等都进行一次遍历或者调用,同时也会触发相关的回调方法,在这些回调方法里,也有对应的访问器进行处理,整体类似一个树形结构。

比如当访问 class 中的方法时,此时会调用 visitMethod() 方法,而我们本篇是希望遍历所有方法,所以需要重写该方法,并返回我们自己的方法访问器(MethodVisitor);

相应的,在具体的 MethodVisitor 里,当这个方法内部去访问其他方法时,或者访问其他对象时等,也都会再次回调相关方法。故此,我们只需要在其访问其他方法时,将其保存到我们自己池子中,从而就可以得到如下信息:

当前类、当前方法、被访问的类、被访问的方法等

而根据这些信息,我们就可以清晰的得知我们自己需要拦截的方法被谁调用了,调用了多少次,调用位置等等。

检测逻辑

具体的检测逻辑就比较简单了,我们只需要定义一个静态处理类,其内部持有一个 Map 结构的结果集(key 为变体名、value为结果集),而具体的判断规则可以存在一个Set或者List中。比如我们示例中只需要判断是否存在指定包或者类的调用,那么只需要传入 packages 即可,如果有更多的规则,比如方法等等,则可以根据逻辑进行更改。

image.png

具体逻辑如上,其中 filterAndAddMethod() 是每次当访问到相关方法时调用,如果满足条件,则将其信息缓存起来;当ASM处理完成后,也就是 transformXXXClassesWithAsm 之后,我们再调用 end() 去统计,从而按照当前 buildType 输出结果。

当然,当拿到结果后,怎么处理那都是题外话题了,比如可以直接打印,或者存储到文件里,也可以抛出异常等等,这些就留给大家自行决断吧。

使用方式

具体的使用方式,比较简单,我们直接在 application 所在的 build.gradle 添加下面的配置语句即可。

//示例
analysis {
    classPackages = ["java.io.PrintStream"]
}

示例Github: Bee-AnalysisPlugin

总结

本篇到这里就结束了,严格而言,本篇其实算不上什么ASM高深技巧,只能算的上是基础操作。更多是希望,通过本篇,能使得新手同学对于 ASM 基础使用有一个了解,特别是在 AGP7.0 之后的打开方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/704571.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python内存管理与垃圾回收机制

目录 一、引言 二、Python内存管理概述 三、引用计数机制 四、垃圾回收机制 标记-清除&#xff08;Mark-and-Sweep&#xff09; 分代收集&#xff08;Generational Collection&#xff09; 五、内存泄漏与优化 六、总结 一、引言 Python作为一门高级编程语言&#xff…

MySQL查询优化最佳实践15条(建议收藏)

目录 1 优化方法&#xff08;15条&#xff09; 2 总结 MySQL的数据库常规查询的过程中性能的优化非常重要&#xff0c;其中很多点是和开发习惯有关&#xff0c;能熟练掌握不只能提高工作的效率&#xff0c;同时也能提高个人的技能。有一些优化的技巧同样也适合于其他的数据库…

2024中国通信技术产业博览会:JUNO光缆与WaveLogic 6技术,海洋深处的数字脉搏

在数字化时代&#xff0c;通信技术是连接世界的桥梁。NTT DATA主导的JUNO海底光缆计划&#xff0c;通过部署Ciena的WaveLogic 6技术&#xff0c;标志着全球通信网络容量的一次重大飞跃。这一进步不仅加强了亚洲与北美之间的数据连接&#xff0c;更为即将到来的“2024中国军民两…

华为云EI生态

1、人工智能技术趋势 2、华为AI发展思路 3、华为云EI&#xff1a;让企业更智能 4、华为云服务全景图 5、基础平台类服务 6、MLS:解决特性到模型应用的完整过程 7.DLS 8.GES超大规模一体化图分析与查询 9、EI视觉认知 10、EI语音语义 11、OCR&#xff1a;提供高精度光学文字自动…

OpenCV特征匹配

1、OpenCV Brute-Force匹配器 Brute-Force匹配器的匹配方法非常简单&#xff0c;输入两张图像所分别对应的特征&#xff08;特征点坐标与特征点域对应的描述子&#xff09;&#xff0c;循环遍历两幅图像中的特征&#xff0c;计算第一幅图像与第二幅图像之间每个特征点之间的距…

49.Python-web框架-Django解决多语言redirect时把post改为get的问题

目录 1.背景 2.思路 3.寻找 Find and Replace 4.再次运行程序&#xff0c;POST来了 5.小结 1.背景 昨天在练习一个Django功能时&#xff0c;把form的method设置为POST&#xff0c;但是实际提交时&#xff0c;一直是GET方法。最后发现这是与多语言相关&#xff0c;django前面…

Linux - 复盘一次句柄数引发的故障

文章目录 Pre&#xff08;内核、用户、进程&#xff09;句柄数设置问题 shell修复 Pre Linux - 深入理解/proc虚拟文件系统&#xff1a;从基础到高级 &#xff08;内核、用户、进程&#xff09;句柄数设置 在Linux系统中&#xff0c;进程打开的最大句柄数可以通过多种方式配置…

计算机组成原理 期末复习笔记整理(上)(个人复习笔记/侵删/有不足之处欢迎斧正)

零、计算机的发展 冯.诺依曼计算机的特点: 1.计算机由五大部件组成 2.指令和数据以同等地位存于存储器&#xff0c;可按地址寻访 3.指令和数据用二进制表示 4.指令由操作码和地址码组成 5.存储程序&#xff08;首次提出存储结构&#xff09; 6.以运算器为中心&#xff08;现代…

大众点评全国美食POI采集780万家-2024年5月底

大众点评全国美食POI采集780万家-2024年5月底 店铺POI点位示例&#xff1a; 店铺id H8kTSRz3kLUQ2WtU 店铺名称 幸福西饼生日蛋糕(布心店) 十分制服务评分 8.2 十分制环境评分 8.4 十分制划算评分 8.3 人均价格 75 评价数量 119033 店铺地址 金稻田路1068号边防布心住…

Python文件操作与IO:从基础到高级技巧的完整指南

目录 一、引言 二、基础文件操作 打开文件 写入文件 读取和写入二进制文件 四、文件路径处理 五、文件操作的高级技巧 上下文管理器&#xff08;with 语句&#xff09; 文件锁 文件编码 使用内置模块处理文件 文件压缩与解压缩 六、案例&#xff1a;处理日志文件 …

支付卡产业最新发布PCI DSS v4.0.1

自2022年3月PCI DSS v4.0发布以来&#xff0c;受到全球支付产业高度关注&#xff0c;为了解决来自所有产业相关者的反馈和问题&#xff0c;PCI安全标准委员会&#xff08;PCI SSC&#xff09;发布了该标准的修订版PCI DSS v4.0.1。其中包括对格式和印刷错误的更正&#xff0c;也…

顶顶通呼叫中心中间件-限制最大通话时间(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-限制最大通话时间(mod_cti基于FreeSWITCH) 一、最大通话时间 1、配置拨号方案 1、点击拨号方案 ->2、在框中输入通话最大时长->3、点击添加->4、根据图中配置->5、勾选continue。修改拨号方案需要等待一分钟即可生效 action"sched…

基于Pytorch实现AI写藏头诗

网上你找了一圈发现开源的代码不是付费订阅就是代码有问题,基于Pytorch实现AI写藏头诗看我这篇就够了。 用到的工具:华为云ModelArts平台的notebook/Pycharm/Vscode都行。 镜像:pytorch1.8-cuda10.2-cudnn7-ubuntu18.04,有GPU优先使用GPU资源。 实验背景 在短时测试使用场…

变电站SF6-O2在线监控报警系统在电力行业的创新应用

一、六氟化硫是什么&#xff1f; 六氟化硫又称为SF6&#xff0c;这种气体在常温常压下为无色无臭无毒的气体。不燃烧。对热稳定&#xff0c;没有腐蚀性&#xff0c;可以作为通用材料。电绝缘性能和消弧性能好&#xff0c;绝缘性能为空气的2&#xff5e;3倍&#xff0c;而且气体…

外网如何访问公司内网服务器?

在现代商业环境中&#xff0c;随着信息技术的快速发展&#xff0c;越来越多的公司有需求让远程用户在外网环境下访问公司内网服务器。这在很大程度上提高了远程办公的灵活性和效率。由于安全和网络限制等问题&#xff0c;实现这一目标并不是一件容易的事情。 在处理这个问题时…

【Qt 学习笔记】Qt窗口 | 对话框 | 创建自定义对话框

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 对话框 | 创建自定义对话框 文章编号&#xff1a;Qt 学习笔记…

[人工智能]啥是大模型?一篇文章看懂火遍全网的“AI大模型”

美国商业科技界正在升起两位“新神”。 一位是“钢铁侠”埃隆马斯克&#xff0c;“带领人类走向火星”&#xff1b; 一位是“奥特曼”山姆阿尔特曼&#xff0c;“带领AI走向人类”。 大多数人对马斯克很熟悉了&#xff0c;特斯拉汽车在中国道路上疾驰&#xff0c;朋友圈也经…

B端系统的颜值问题:成也框架,败也框架!

B端UI框架和前端框架的出现&#xff0c;让系统的搭建就像堆积木一样&#xff0c;十分的容易了。这也一下子把程序员的设计和审美水平拔高到了UI框架能够达到的高度。伴随而来的则是系统的堆砌、同质化、糟糕的体验&#xff0c;以及各种违和的组件被生搬硬套的绑定在一块&#x…

LabVIEW故障预测

在LabVIEW故障预测中&#xff0c;振动信号特征提取的关键技术主要包括以下几个方面&#xff1a; 时域特征提取&#xff1a;时域特征是直接从振动信号的时间序列中提取的特征。常见的时域特征包括振动信号的均值、方差、峰值、峰-峰值、均方根、脉冲指数等。这些特征能够反映振动…

电信数字员工“上岗”!重庆电信携手实在智能加快“数智化”转型

中国电信股份有限公司重庆分公司&#xff08;以下简称重庆电信&#xff09;与实在智能已达成合作&#xff0c;由实在智能为其建设RPA数字员工&#xff0c;首批已覆盖数十个业务场景&#xff0c;大幅提升相应业务自动化和智能化水平。深度适配电信业务运转的RPA数字员工&#xf…