Frida遍历启动App所有Activity/Service

说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除

作者:zhu6201976

一、需求

在一些大型App中,往往注册了大量的Activity和Service,这在App的AndroidManifest.xml文件可以清晰呈现。那么,如果给定任意App,在不反编译的前提下,如何获取并自动化批量遍历启动App所有Activity或Service?

二、在不反编译的前提下,如何获取启动App所有Activity或Service?

Android自带adb命令可以轻松获取Activity或Service相关信息,具体命令参考如下:

获取App四大组件相关信息命令:adb shell am

# 启动Android系统中任意App Activity
am start-activity -n com.android.settings/.Settings$NetworkDashboardActivity

# 启动Android系统中任意App Service
am start-service --user 0 com.android.internal.widget.ILockSettings

# 停止Android系统中任意App Service
am stop-service --user 0 com.android.internal.widget.ILockSettings

# 发送Android系统中任意App broadcast
am broadcast --user 0 android.intent.action.BATTERY_CHANGED
am broadcast android.provider.Telephony.SMS_RECEIVED

获取App服务相关信息命令:adb shell service/servicemanager

# 过滤查看包含settings关键词的系统service
service list | grep settings

# 过滤查看包含mosheng关键词的非系统service
dumpsys activity services | grep mosheng

三、如何获取并自动化批量遍历启动App所有Activity或Service?

根据上述描述可知,在Android系统中,adb相关命令可以轻松获取并管理Activity或Service相关信息,功能十分强大,但无法做到自动化批量遍历启动App所有Activity或Service。当然你可以写相关程序执行调用adb命令实现上述功能。

而基于Frida实现的objection功能非常强大,不仅可以枚举App中所有的四大组件相关信息,而且提供了单个组件启动服务,但仍然未实现批量自动化hook。自动化批量遍历启动App所有Activity或Service这种需求在某些特定场景,比如逆向、测试等,为开发者检查App页面及可能存在的页面跳转漏洞提供了一些便利,因此具有某些实际意义。GitHub - sensepost/objection: 📱 objection - runtime mobile exploration

四、Frida遍历启动App所有Activity/Service

基于上述需求,本人站在开发和借鉴objection的基础上,通过Frida主动调用实现了该功能,并在多个App中测试通过,项目已开源,github地址:https://github.com/zhu6201976/frida_intent.git 欢迎star、交流。

参考代码如下:

/**
 * @Time : 2023/6/28 22:00
 * @Author : Tesla
 * @Csdn : https://blog.csdn.net/zhu6201976
 *
 * Frida遍历启动App所有Activity/Service
 * attach模式:
 *     frida -UF -l hook_intent.js
 * spawn模式:
 *     frida -U -l hook_intent.js -f com.mosheng
 */

var context = null;
var packageName = null;
var delay = 1500;

function getContextPackageNameV1() {
    Java.choose('android.app.ActivityThread', {
        onMatch(instance) {
            const currentApplication = instance.currentApplication();
            context = currentApplication.getApplicationContext();
            packageName = context.getPackageName();
            console.log('getContextPackageNameV1', 'context', context, 'packageName', packageName);
        }, onComplete() {
        }
    });
}

function getContextPackageNameV2() {
    const ActivityThread = Java.use("android.app.ActivityThread");
    const currentApplication = ActivityThread.currentApplication();
    context = currentApplication.getApplicationContext();
    packageName = context.getPackageName();
    console.log('getContextPackageNameV2', 'context', context, 'packageName', packageName);
}

function sleep(delay) {
    const start = (new Date()).getTime();
    while ((new Date()).getTime() - start < delay) {
    }
}

/*
public void getAllActivity() {
    PackageManager packageManager = getPackageManager();
    PackageInfo packageInfo = null;
    try {
        packageInfo = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
        ActivityInfo[] activities = packageInfo.activities;
        for (ActivityInfo activity :activities ) {
            Class<?> aClass = Class.forName(activity.name);
        }
    } catch (PackageManager.NameNotFoundException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}
 */
function getActivities() {
    const packageManager = Java.use("android.content.pm.PackageManager");
    const GET_ACTIVITIES = packageManager.GET_ACTIVITIES.value;
    return Array.prototype.concat(context.getPackageManager()
        .getPackageInfo(context.getPackageName(), GET_ACTIVITIES).activities.value.map((activityInfo) => {
            const activity = activityInfo.name.value;
            // return activity;  // 返回所有Activity
            if (activity.indexOf(packageName) !== -1) {  // 返回app自定义Activity
                return activity;
            }
        }));
}

/*
Intent intent = new Intent(MyActivity.this, MyOtherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
 */
function startActivities() {
    const activities = getActivities();
    const set = new Set();
    activities.forEach(function (activity) {
        set.add(activity);
    });
    console.log('getActivities Found ' + set.size + ' activities');

    set.forEach(function (activity) {
        console.log('getActivities start_activity', activity);
        try {
            const Clazz = Java.use(activity);
            const Intent = Java.use('android.content.Intent');
            const intent = Intent.$new(context, Clazz.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK.value);
            context.startActivity(intent);
        } catch (e) {
            console.log('getActivities', e);
        }

        sleep(delay);
    });
}

function getServices() {
    const activityThread = Java.use("android.app.ActivityThread");
    const arrayMap = Java.use("android.util.ArrayMap");
    const packageManager = Java.use("android.content.pm.PackageManager");
    const GET_SERVICES = packageManager.GET_SERVICES.value;
    const currentApplication = activityThread.currentApplication();
    let services = [];
    currentApplication.mLoadedApk.value.mServices.value.values().toArray().map((potentialServices) => {
        Java.cast(potentialServices, arrayMap).keySet().toArray().map((service) => {
            // services.push(service.$className);
            if (service.$className.indexOf(packageName) !== -1) {
                // console.log('getServices 1', service);
                services.push(service.$className);
            }
        });
    });
    services = services.concat(context.getPackageManager()
        .getPackageInfo(context.getPackageName(), GET_SERVICES).services.value.map((activityInfo) => {
            const service = activityInfo.name.value;
            if (service.indexOf(packageName) !== -1) {
                // console.log('getServices 2', service);
                return service;
            }
        }));
    return services;
}

/*
Intent intent = new Intent(this, TestService.class);
startService(intent);
stopService(intent);
 */
function startServices() {
    const services = getServices();
    const set = new Set();
    services.forEach(function (service) {
        set.add(service);
    });
    console.log('startServices Found ' + set.size + ' services');

    set.forEach(function (service) {
        console.log('startServices start_service', service);
        try {
            const Clazz = Java.use(service);
            const Intent = Java.use('android.content.Intent');
            const intent = Intent.$new(context, Clazz.class);
            // context.stopService(intent);
            context.startService(intent);
        } catch (e) {
            console.log('startServices', e);
        }

        sleep(delay);
    });
}

function main() {
    Java.perform(function () {
        try {
            getContextPackageNameV1();
        } catch (e) {
            getContextPackageNameV2();
        }
        startActivities();
        startServices();
    });
}

setTimeout(main, 1500);

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值