说明:仅供学习使用,请勿用于非法用途,若有侵权,请联系博主删除
作者: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);