小研究 - Android 字节码动态分析分布式框架(五)

news2024/9/29 13:15:40

安卓平台是个多进程同时运行的系统,它还缺少合适的动态分析接口。因此,在安卓平台上进行全面的动态分析具有高难度和挑战性。已有的研究大多是针对一些安全问题的分析方法或者框架,无法为实现更加灵活、通用的动态分析工具的开发提供支持。此外,很多研究只是针对单进程的分析,在安卓平台多个应用进程协作完成事务的情境下,则无法进行很好的分析。

目录

5  实例分析

5.1 覆盖率程序分析实例

5.1.1 注入与分析实现

5.1.2 Grinderbench 测试

5.1.3 安卓应用情景测试

 5.2 权限使用检测实例

5.2.1 注入与分析实现

5.2.2 结果以及分析

 5.3 本章小结


5  实例分析

5.1 覆盖率程序分析实例

测试程序的覆盖率,是一种评价测试活动覆盖产品代码比例的指标。代码覆盖率能够直接反映一个程序执行路径(比如某测试用例)的有效性。在 JVM 上已经有不少工具可以测试代码的覆盖率,比如 Emma和 Jacoco。JaCoCo 如今也能支持在安卓平台测试代码覆盖率,然而 JaCoCo 测试需要与安卓的部署系统Gradle 相结合,以在部署应用的时候预注入代码覆盖的相关逻辑和依赖的 JaCoCo类库。由于 JaCoCo 的功能已经固定在它复杂的代码体系里,要扩展并不容易。

5.1.1 注入与分析实现

由于方法属于类,分支属于方法,因此找到分支的覆盖情况的过程中也能获取类以及方法覆盖的情况。为此如何获取分支覆盖的数据是本实例重要的一点。在 Java 语言中,分支的产生只有两种情况,一种是条件分支语句,一种是 Switch分支语句(并不包含异常处理语句),这与 JaCoCo 的实现原理相同。为了识别某一次方法调用中分支覆盖的情况,可以静态得从遍历该方法内的所有字节码指令,获取方法的所有分支,在运行时记录每一个遍历到的分支。

下图表示了本实例在方法进入和方法退出时候进行的注入。本文利用了DiSL 的@SyntheticLocal 变量。这个变量会被 DiSL 加到目标程序的方法里,变为一个方法局部变量,以供分析使用。本案例中,创建了两个该种类的变量:encounterBranch 和 branches,分别表示是否在分支语句内,以及方法内分支覆盖率的统计信息(采取 boolean 数组的形式,分别对应一个方法内 b local 个分支)。

@SyntheticLocal
public static boolean encounterBranch = false;
@SyntheticLocal
public static boolean [] branches;
@Before (marker = BodyMarker.class, order = 2)
public static void onMethodEntry (CodeCoverageContext c) {
    branches = new boolean [c.getMethodBranchCount ()];
}
@After (marker = BodyMarker.class, order = 2)
public static void onMethodExit (CodeCoverageContext c) {
    CodeCoverageAnalysisStub.commitBranch (
        c.thisClassName(), c.thisMethodName(), branches);
}

通过两个实验来验证本实例分支覆盖率分析的正确性。第一个实验,通过与已有的 JaCoCo 分支覆盖率测试工具进行比较以及对 Grinderbench这个轻量级 Java 测试套件实验来验证本案例分析结果的正确性;第二个实验,通过对一个安卓应用程序的四个操作情景进行测试,统计并验证四种情境下几个核心方法的覆盖率情况与代码情况是否吻合。

@AfterReturning (marker = BranchInsnMarker.class, order = 1)
public static void afterBranchInstr (CodeCoverageContext c)
{
    if (encounterBranch) {
        branches [c.getIndex ()] = true;
        encounterBranch = false;
    }
}
@Before (marker = SwitchInsnMarker.class, order = 1)
public static void beforeSwitch () {
     encounterBranch = true;
}
@AfterReturning (marker = BranchLabelMarker.class, order = 1)
public static void afterBranchLabel (CodeCoverageContext c)
{
    if (encounterBranch) {
        branches [c.getIndex ()] = true;
        encounterBranch = false;
    }
}
5.1.2 Grinderbench 测试

Grinderbench 是一个 JVM 上的简单的测试套件,其包括 Parallel、kXML、PNGdecoding、Chess 和 Crypto 五个测试,涉及多线程、CPU 密集、I/O 密集等测试类型。在不添加任何参数的情况下,该测试会依次进行这五个测试。本案例就是测量这种模式下的程序覆盖率。为了能够在安卓平台上运行 Grinderbench,本文利用 dx 工具,将 Grinderbench 的 jar 文件转换成 DVM 下可以执行的 jar 文件,并通过 adb 工具将该 jar 包发送到安卓系统上。这样就可以在安卓系统上通过 dalvikvm来新建一个 DVM 进程从而进行测试。

在 JVM 上,首先利用 JaCoCo 工具,来测试上述的运行模式下程序的覆盖率测试结果;紧接着,在本框架下于安卓 Dalvik 虚拟机中执行分支覆盖率测试。由于这部分分析没有安卓独有的事件,因而可以与原 JVM 上的 ShadowVM 模型兼容。可以直接在 JVM 上利用相同的分析代码执行覆盖率测试。

实验结果如表 5-1 所示。第一段数据是 JaCoCo 分别在 JVM 和 DVM 上对GrinderBench 进行测试的结果汇总情况,二者完全一致。第二段是本文开发的覆盖率分析分别在JVM和DVM上的结果,可以发现无论是在JVM上还是DVM上,覆盖到的分支、方法和类的数目均与 JaCoCo 一致。然而在总数上与 JaCoCo 并不完全相同。

因此可以看出基于本文框架,不仅能够方便的开发动态分析,还可以兼有跨平台的可移植性,而分析结果也能正确的反映程序的实际执行。

5.1.3 安卓应用情景测试

DroidBench测试集合中的 ImplicitFlow4 应用来检测本分析在安卓应用中的测试情况。ImplicitFlow4 应用是一个简易的安卓程序,它含有一个动组件,组件中包含了用户名输入、密码输入的文本框和登陆的按钮。用户输入用户名密码以后点击按钮会触发程序的判断操作。该应用的具体代码参考DroidBench 的 github 源代码库。

本案例分析的目标是目标应用中的两个重要方法checkUsernamePassword 以及 lookup,用户输入错误的用户名或者密码都会触发不同的程序运行路径从而造成不同的分支覆盖结果。本案例采取了四组用户数据作为输入:

1-用户名错误;

2-用户名正确、密码错误;

3-用户名正确,密码正确;

4-用户依次按照前三个情况输入。

经过试验,这四种情况下的覆盖情况如下所示。通过直接阅读源代码,可以验证该结果的正确性。

 5.2 权限使用检测实例

安卓系统采取权限控制的方式控制应用的访问能力。应用在安装时会提示用户需要的权限,用户并没有办法了解这些赋予的权限会被如何使用。加上安卓存在很多利用 Intent 的跨进程消息,通过静态分析很难完全识别出所有的敏感函数调用。

5.2.1 注入与分析实现

先介绍本案例用户定义的分析事件,即注入部分需要插入的事件。为此首先需要监控 ActivityManagerService 类的 checkPermission 方法。

public class IPCAnalysis extends RemoteIPCAnalysis {
    /* Analysis Event Handler Starts */
    public void permissionUsed (
        Context ctx, int tid, ShadowString permissionName) {
        List<ThreadState> callers = ThreadState.getCallers (ctx,tid);
        for(ThreadState caller:callers){
            caller.addPermission(permissionName.toString ());
        }
}
public void boundaryStart (
    Context ctx, int tid, ShadowString boundaryName) {
    ThreadState state = ThreadState.get (ctx, tid);
    state.pushBoundary (boundaryName.toString ());
}
public void boundaryEnd (
    Context ctx, int tid, ShadowString boundaryName) {
    ThreadState state = ThreadState.get (ctx, tid);
    state.popBoundary (boundaryName.toString ());
}
/* Analysis Event Handler Ends */

ActivityManagerService.checkPermission 方 法 接 受 三 个 参 数 : (Stringpermission, int pid, int uid),其中 permission 参数是一个字符串,用来表示不同的权 限 的 名 称 , pid 与 uid 则 分 别 表 示 调 用 进 程 的 进 程 号 与 用 户 id 。ActivityManagerService类的checkPermission方法一般在System Server进程中被调用,而且一般都是响应来自别的进程的 API 调用,因此它处在 IPC Binder 调用的第二与第三个事件之间。本案例利用该方法来捕获System Server所有的权限验证。其次,为了在分析过程检测到权限使用时能够还原出目标应用进程的对应线程的栈情况,需要在分析端为每个分析进程的每个线程维护一个栈。这个实现原理如下,只要记录下每次进入一个方法以及离开一个方法,就能知道任意时刻的线程的栈信息。在注入端,可以通过监控目标应用中的所有类的方法入口以及方法出口,产生相应分析事件,这样在分析服务器就可以动态得掌握当前分析事件处在什么样的运行时栈当中。注入部分的代码此处略去,可以参考图 5-3 中的分析响应代码。

由于在分析中并不是所有的分析都需要完整的还原 IPC 事件的顺序,因此框架并不直接提供事件同步功能。而本案例需要严格的控制事件顺序,也展示了框架的灵活性和可扩展性。

@Override
public void onRequestSent (
    TransactionInfo info, NativeThread client, Context ctx) {
    ThreadState clientState = ThreadState.get (client);
    clientState.recordRequestSent(client,info);
}
@Override
public void onRequestReceived (
    TransactionInfo info, NativeThread client,
    NativeThread server, Context ctx) {
    ThreadState clientState = ThreadState.get (client);
    clientState.waitForRequestSent (info);
    ThreadState serverState = ThreadState.get (server);
    serverState.recordRequestReceived(client, server, info);
}
@Override
    public void onResponseSent (
        TransactionInfo info, NativeThread client, NativeThread
    server, Context ctx) {
        ThreadState serverState = ThreadState.get (server);
        serverState.recordResponseSent(client, server, info);
}
@Override
public void onResponseReceived (
    TransactionInfo info, NativeThread client,
    NativeThread server, Context ctx) {
        ThreadState serverState = ThreadState.get (server);
        serverState.waitForResponseSent (info, client);
        ThreadState clientState = ThreadState.get (client);
        if(clientState.getPermissionCount()>0){
            IPCLogger.reportPermissionUsage (clientState);
            clientState.clearPermissions ();
        }
        clientState.recordResponseReceived(client, server, info);
    }
}
5.2.2 结果以及分析

为了验证分析的有效性,本文通过一个简单的实例,来展示本实例的功能。本案例选取 DroidBench 测试程序组中的 Reflection_Reflection4 程序作为测试程序。该程序只包含了一个活动组件,在启动应用后活动组件 onCreate 会自动被调用。

应用程序 => Phone进程 => System Server 进程 => Phone进程 => 应用程序。

通过本文的框架,首先 System Server 进程中当调用 ActivityManagerService的 checkPermission 方法检测 READ_PHONE_STATE 权限时,分析中会识别所有的上层调用方(Phone 进程的某个线程以及应用进程的某个线程),并告知它们在这段 IPC 调用中这个权限被使用了。那么最后应用进程执行完 IPC 方法后,分析端收到 onResponseReceived 事件,即收到 getDeviceId 的 IPC 调用返回值的时候,它就知道了该 IPC 调用造成了 READ_PHONE_STATE 权限的使用。

分析端得到的分析结果如图 5-7 所示,第一段信息表明了在程序执行到ConcreteClass 的 foo 方 法 里 , 调 用 TelephoneManager.getDeviceId 的 时 候READ_PHONE_STATE 权限被使用了。类似的,第二段信息则表明在 Concrete 类的 bar 方法中,对 SmsManager.sendTextMessage 的调用引起了 SEND_SMS 以及WAKE_LOCK 权限的使用。从结果中也可以发现,一个 API 调用可能引起多个权限的使用,用户可以进一步在分析中筛选自己关注的那些权限。

 5.3 本章小结

首先介绍了基于本框架实现的一个覆盖测试分析,通过运行 GrinderBench 以及在执行安卓应用这两种情景,验证了覆盖率分析的正确性,并说明了一些分析可以跨平台复用。接着通过一个检测安卓权限使用的案例,介绍了用户如何将分析与Binder 事件联合使用,以进行多进程分析。通过这两个例子充分展示了本框架强大的功能性,以及用户友好的分析编写方式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目---日志系统

目录 项目系统开发环境核心技术日志系统介绍为什么需要日志系统? 日志系统框架设计日志系统模块划分代码实现通用工具实现日志等级模块实现日志消息模块实现格式化模块实现落地模块实现日志器模块同步日志器异步日志器缓冲区实现异步工作器实现 回归异步日志器模块建造者模式日…

用大白话来讲讲多线程的知识架构

感觉多线程的知识又多又杂&#xff0c;自从接触java&#xff0c;就在一遍一遍捋脉络和深入学习。现在将这次的学习成果展示如下。 什么是多线程&#xff1f; 操作系统运行一个程序&#xff0c;就是一个线程。同时运行多个程序&#xff0c;就是多线程。即在同一时间&#xff0…

C语言练习4(巩固提升)

C语言练习4 选择题 前言 面对复杂变化的世界&#xff0c;人类社会向何处去&#xff1f;亚洲前途在哪里&#xff1f;我认为&#xff0c;回答这些时代之问&#xff0c;我们要不畏浮云遮望眼&#xff0c;善于拨云见日&#xff0c;把握历史规律&#xff0c;认清世界大势。 选择题 …

设计模式--适配器模式(Adapter Pattern)

一、什么是适配器模式&#xff08;Adapter Pattern&#xff09; 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式主要用于解决不兼容接口之间的问题&#xff0c;使得原本…

小研究 - Java虚拟机性能及关键技术分析

利用specJVM98和Java Grande Forum Benchmark suite Benchmark集合对SJVM、IntelORP,Kaffe3种Java虚拟机进行系统测试。在对测试结果进行系统分析的基础上&#xff0c;比较了不同JVM实现对性能的影响和JVM中关键模块对JVM性能的影响&#xff0c;并提出了提高JVM性能的一些展望。…

[Go版]算法通关村第十四关白银——堆高效解决的经典问题(在数组找第K大的元素、堆排序、合并K个排序链表)

目录 题目&#xff1a;在数组中找第K大的元素解法1&#xff1a;维护长度为k的最小堆&#xff0c;遍历n-k个元素&#xff0c;逐一和堆顶值对比后&#xff0c;和堆顶交换&#xff0c;最后返回堆顶复杂度&#xff1a;时间复杂度 O ( k ( n − k ) l o g k ) O(k(n-k)logk) O(k(n−…

大数据治理运营整体解决方案[39页PPT]

导读&#xff1a;原文《大数据治理运营整体解决方案[39页PPT]》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 数据治理总体方案 数据治理平台解决方案 数据治理运…

nginx生成自定义证书

1、创建key文件夹 [rootlocalhost centos]# mkdir key 进入key文件夹 [rootlocalhost centos]# cd key/ 2、生成私钥文件 [rootlocalhost key]# openssl genrsa -des3 -out ssl.key 4096 输入这个key文件的密码。不推荐输入&#xff0c;因为以后要给nginx使用。每次reload ngin…

yolov8热力图可视化

安装pytorch_grad_cam pip install grad-cam自动化生成不同层的bash脚本 # 循环10次&#xff0c;将i的值从0到9 for i in $(seq 0 13) doecho "Running iteration $i";python yolov8_heatmap.py $i; done热力图生成python代码 import warnings warnings.filterwarn…

如何延长周末体验感

美好的周末永远都是从周五开始 为了享受周末的美好时光一定要在周五下班前把工作中应该处理的事情处理好&#xff0c;避免突发事件影响后续的计划。 此外过周五晚上开始做让自己感到开心的事情&#xff0c;以此让自己感觉到周末已经开始了。包括单不限于 享受美食 周五晚上是一…

【业务功能篇84】微服务SpringCloud-ElasticSearch-Kibanan-电商实例应用

一、商品上架功能 ElasticSearch实现商城系统中全文检索的流程。 1.商品ES模型 商品的映射关系 PUT product {"mappings": {"properties": {"skuId": {"type": "long"},"spuId": {"type": "ke…

mall:redis项目源码解析

文章目录 一、mall开源项目1.1 来源1.2 项目转移1.3 项目克隆 二、Redis 非关系型数据库2.1 Redis简介2.2 分布式后端项目的使用流程2.3 分布式后端项目的使用场景2.4 常见的缓存问题 三、源码解析3.1 集成与配置3.1.1 导入依赖3.1.2 添加配置3.1.3 全局跨域配置 3.2 Redis测试…

DataFrame.set_index()方法--Pandas

1.函数功能 为DataFrame重新设置索引&#xff08;行标签&#xff09; 2. 函数语法 DataFrame.set_index(keys, *, dropTrue, appendFalse, inplaceFalse, verify_integrityFalse)3. 函数参数 参数含义keys作为行标签的列名&#xff0c;可以DataFrame中的是单个列或者多列组…

【实例分割】(一)Mask R-CNN详细介绍带python代码

目录 1.&#x1f340;&#x1f340;实例分割定义 2.&#x1f340;&#x1f340;Mask R-CNN 3.&#x1f340;&#x1f340;经典的实例分割算法 4.&#x1f340;&#x1f340;Mask R-CNN python代码 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01;…

【FreeRTOS】【应用篇】任务管理相关函数

文章目录 前言一、函数解析1. 任务挂起 vTaskSuspend()① 使用场景② 设计思路③ 代码 2. 任务恢复 vTaskResume()① 作用② 设计思路③ 代码 3. 挂起任务调度器 vTaskSuspendAll()① 作用② 代码 4. 恢复任务调度器 xTaskResumeAll()① 设计思路② 代码 5. 任务删除函数 vTask…

牡丹宣言:七种皮肤类型|教你如何区分和保姆级护肤大法

经常听到有人说&#xff0c;我的皮肤T区油&#xff0c;脸颊干&#xff0c;应该是混合型皮肤吧 正常的皮肤根据皮脂腺分泌油脂量的多少可分为&#xff1a;中性&#xff0c;干性&#xff0c;油性&#xff0c;混合性。 接下来小编就帮大家细化整理了七种不同的皮肤类型&#xff0c…

Nginx详解 第一部分:编译安装Nginx+Nginx模块

Part 1 一 、HTTP 和 Nginx1.1 套接字Socket1.2 URL1.2.1 定义1.2.1 URL和URN的区别1.2.3 URL组成 1.3 请求访问完整过程详解 二、I/O模型 处理高并发的时候用2.1 I/O模型简介2.2 多路复用I/O型2.3 异步I/O模型2.4 事件模型 select poll epoll 三、NGINX概述3.1 简介3.2 NGINX和…

【Java并发】聊聊对象内存布局和syn锁升级过程

对象存储解析&#xff1a;一个空Object对象到底占据多少内存&#xff1f; 对象内存布局 Mark Word占用8字节&#xff0c;类型指针占用8个字节&#xff0c;对象头占用16个字节。 好了&#xff0c;我们来看一下一个Object对占用多少空间&#xff0c; 因为java默认是开启压缩…

帆软只是一个BI厂商?答案是“No”!

大数据产业创新服务媒体 ——聚焦数据 改变商业 2023年&#xff0c;8月17-19日&#xff0c;帆软智数大会落子花城广州&#xff0c;邀请了1200海内外CIO和数字化专家&#xff0c;共同探讨数字化转型新机遇。 值得关注的是&#xff0c;这也是帆软首次以BI和零代码双赛道第一的身…

django开发流程

设计model django采用ORM映射&#xff0c;可以在代码中描述数据库的布局 只需要导入from django.db import models 并使类继承models.Model&#xff0c;models中的一个类对应数据库中的一个表&#xff0c;类的变量对应表字段。 创建数据库 $ python manage.py makemigration…