各位读者你们好啊,又是打工的一天!
今天我们主要学习一下 objection 的一些功能实现,更好的熟悉 frida 语法。
列出四大组件
Activity
export const getActivities = (): Promise<string[]> => {
return wrapJavaPerform(() => {
const packageManager: PackageManager = Java.use("android.content.pm.PackageManager");
const GET_ACTIVITIES = packageManager.GET_ACTIVITIES.value;
const context = getApplicationContext();
return Array.prototype.concat(context.getPackageManager()
.getPackageInfo(context.getPackageName(), GET_ACTIVITIES).activities.value.map((activityInfo) => {
return activityInfo.name.value;
}),
);
});
};
这里面有些是 typescript 的语法,有些是 android 的api,刚开始看起来可能比较蛋疼。
我们先看 android 里面是如何获取一个 app 里面的所有 activity 的:
public void getAllActivity() {
PackageManager packageManager = getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
//所有的Activity
ActivityInfo[] activities = packageInfo.activities;
}
对比可以看出,objection 就是直接使用了 Android 提供的 api 来实现获取所有 activity 的。然后用 typescript 的 api 将结果处理了一下。
Service
service 的话要比较麻烦,因为并没有直接提供方法。android apk 在安装的时候,清单文件已经被解析过了,所以肯定是有这些信息,只不过存放的位置比较隐蔽:
export const getServices = (): Promise<string[]> => {
return wrapJavaPerform(() => {
const activityThread: ActivityThread = Java.use("android.app.ActivityThread");
const arrayMap: ArrayMap = Java.use("android.util.ArrayMap");
const packageManager: PackageManager = Java.use("android.content.pm.PackageManager");
const GET_SERVICES = packageManager.GET_SERVICES.value;
const currentApplication = activityThread.currentApplication();
// not using the helper as we need other variables too
const context = currentApplication.getApplicationContext();
let services = [];
currentApplication.mLoadedApk.value.mServices.value.values().toArray().map((potentialServices) => {
Java.cast(potentialServices, arrayMap).keySet().toArray().map((service) => {
services.push(service.$className);
});
});
services = services.concat(context.getPackageManager()
.getPackageInfo(context.getPackageName(), GET_SERVICES).services.value.map((activityInfo) => {
return activityInfo.name.value;
}),
);
return services;
});
};
可以看出,objection 是从 LoadedApk 里面拿了些信息:
150 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
151 private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
152 = new ArrayMap<>();
这个是获取了 Context 对象,这个 context 实际上是一个 service 对象,在创建 service 的时候赋值:
context.setOuterContext(service);
有点不明白,app 里面的 service 应该都在清单里面声明了,应该直接获取就行,为啥要加这一步呢???这个api还包括已经建立连接的外部service???
然后再加上从 getPackageManager 获取的自身的 service 信息即可。
其他的广播,ContentProvider 也是类似的,就不说了,贴一个源码链接:
启动Activity
// starts an Android activity
// This method does not yet allow for 'extra' data to be send along
// with the intent.
export const startActivity = (activityClass: string): Promise<void> => {
// -- Sample Java
//
// Intent intent = new Intent(this, DisplayMessageActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//
// startActivity(intent);
return wrapJavaPerform(() => {
const context = getApplicationContext();
// Setup a new Intent
const androidIntent: Intent = Java.use("android.content.Intent");
// Get the Activity class's .class
const newActivity: Java.Wrapper = Java.use(activityClass).class;
send(`Starting activity ${c.green(activityClass)}...`);
// Init and launch the intent
const newIntent: Intent = androidIntent.$new(context, newActivity);
newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(newIntent);
send(c.blackBright(`Activity successfully asked to start.`));
});
};
就是调用了 Context的 startActivity方法,没有传递参数,如果目标界面有校验,就会失效。
Hook所有重载方法
export const setReturnValue = (fqClazz: string, filterOverload: string | null, newRet: boolean): Promise<void> => {
...
return wrapJavaPerform(() => {
...
const targetClazz: JavaClass = Java.use(clazz);
targetClazz[method].overloads.forEach((m: any) => {
// get the argument types for this method
const calleeArgTypes: string[] = m.argumentTypes.map((arg) => arg.className);
...
// tslint:disable-next-line:only-arrow-functions
m.implementation = function () {
let retVal = m.apply(this, arguments);
...
return retVal;
};
});
jobs.add(job);
});
};
使用 overloads 获取了所有的重载方法。
而且,targetClazz直接是使用了 [] 语法来指定方法名,这样如果有些不可打印的方法名,也可以用这种方式来 hook。
替换方法实现,直接使用 implementation,获取方法参数使用 arguments 即可。
打印堆栈
let t = Java.use("java.lang.Throwable").$new()
let logClazz = Java.use("android.util.Log")
let stackString = logClazz.getStackTraceString(t)
console.log(stackString)
显然,这里其实也是用了 Android 的 Log 类的一个 API:
public static String getStackTraceString(@Nullable Throwable tr) {
...
}
总结
如果调用静态方法,可以直接调用:
var a = Java.use(xxx);
a.staticMethod(xxx);
如果是成员方法,先找到对象再调用:
var bclazz = Java.use('这里写类名');
var b = bclazz.$new();
b.memberMethod(xxxx)
字段也是一样。
最后,需要熟悉 Android 的 API。
二手的程序员
欢迎关注二手的程序员,这里主要分享逆向相关的知识。专注于完整系列,让知识不再碎片化。不定时更新,也欢迎关注我的博客:lyldalek.top
**
公众号