发布: 更新时间:2024-04-30 11:52:47
在某些虚拟化、免安装、打点、环境检测、拦截器等场景中,针对Android系统服务接口的拦截是一种常用的技术方案。通常,这种拦截只针对正向的接口调用,而涉及被动的服务回调拦截则实现起来会有些麻烦。
由于容器产品的特性,需要对超过100个系统服务的通信进行拦截、过滤、修正和还原接口通信的数据,以实现系统和应用无感知运行,从而实现免安装运行的效果。
整个方案主要集中在服务模块主动调用的拦截上,而系统回调的拦截涉及较少。然而,随着功能的深入,对服务回调的接口(例如Binder for Oneway)进行拦截的需求也越来越大。在这里,我们将分享整个通用的拦截方案和实现过程,希望对大家有所帮助。
Binder的Oneway回调机制指的是应用进程向系统服务注册回调(通常注册和取消注册成对出现),当服务端有相应事件时,可以直接回调给该Binder对象实例。
例如,常见的AMS服务接口:
// source code: /frameworks/base/core/java/android/app/IActivityManager.aidl
interface IActivityManager {
// ...
Intent registerReceiver(IApplicationThread caller,
String callerPackage,
IIntentReceiver receiver,
IntentFilter filter,
String requiredPermission,
int userId,
int flags
);
void unregisterReceiver(in IIntentReceiver receiver);
// ...
}
我们的目标是:
由于通常系统接口类(例如IActivityManager.aidl、IPackageManager.aidl等)均为隐藏类,因此很自然的想法是将系统的aidl源文件导入到工程中。
配置好目录:
sourceSets {
main {
aidl.srcDirs = ['src/main/aidl']
}
}
编译后我们就可以连接该类,并进行继承扩展了,如:
public class StubIntentReceiver extends IIntentReceiver.Stub {
Object mOrigin;
protected StubIntentReceiver(Object org) {
this.mOrigin = org;
}
private static Method sMethod_performReceive;
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// TODO something here ...
if (null == sMethod_performReceive) {
sMethod_performReceive = ReflectUtils.getDeclaredMethod(
mOrigin, "performReceive",
Intent.class, int.class, String.class, Bundle.class, boolean.class, boolean.class, int.class
);
}
sMethod_performReceive.invoke(mOrigin, intent, resultCode, data, extras, ordered, sticky, sendingUser);
}
}
对于IIntentReceiver.aidl的回调接口来说,这样就可以解决了,因为它满足了几个特性:
然而更多的接口并非如此,接口类函数不仅仅是多个,而且不同版本类方法各异,同函数参数也都不相同,这才是常态。因此,我们自然的解决方案就是:flavor。
既然每个版本可能不一致,那就编译多版本就可以解决了,如:
这样确实能解决多版本系统接口变化的问题,但同时带来了新的问题:
我们对于复杂的方案生来恐惧,越复杂越做不稳定,所以我们的目标:
于是我们通过编译后的源码我们目标锁定在Binder的onTransact函数,如:
public interface IIntentReceiver extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.content.IIntentReceiver
{
private static final java.lang.String DESCRIPTOR = "android.content.IIntentReceiver";
/** Construct the stub at attach it to the interface. */
@Override
public boolean onTransact(int code,
android.os.Parcel data,
android.os.Parcel reply,
int flags
) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case TRANSACTION_performReceive:
{
data.enforceInterface(DESCRIPTOR);
Intent _arg0;
if (0 != data.readInt()) {
_arg0 = Intent.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
int _arg1 = data.readInt();
String _arg2 = data.readString();
Bundle _arg3;
if (0 != data.readInt()) {
_arg3 = (Bundle)Bundle.CREATOR.createFromParcel(data);
} else {
_arg3 = null;
}
boolean _arg4 = 0 != data.readInt();
boolean _arg5 = 0 != data.readInt();
int _arg6 = data.readInt();
// call function here !!!
this.performReceive(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
}
于是我们的方案:
于是我们扩展实现类为:
import android.content.IIntentReceiver;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.text.TextUtils;
public class OnewayIIntentReceiver extends IIntentReceiver.Stub {
private final Object mArgument;
private static int TRANSACTION_performReceive = -1;
public OnewayIIntentReceiver(Object org) {
mArgument = org;
if (TRANSACTION_performReceive < 0) {
TRANSACTION_performReceive = ReflectUtils.getMethodCode(org, "TRANSACTION_performReceive");
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException {
if (TRANSACTION_performReceive == code) {
data.enforceInterface(getInterfaceDescriptor());
Intent _arg0;
if (0 != data.readInt()) {
_arg0 = Intent.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
int _arg1 = data.readInt();
String _arg2 = data.readString();
Bundle _arg3;
if (0 != data.readInt()) {
_arg3 = (Bundle)Bundle.CREATOR.createFromParcel(data);
} else {
_arg3 = null;
}
boolean _arg4 = 0 != data.readInt();
boolean _arg5 = 0 != data.readInt();
int _arg6 = data.readInt();
// do call origin
Method method = ReflectUtils.getDeclaredMethod(
mArgument.mOrigin, "performReceive",
Intent.class, int.class, String.class, Bundle.class, boolean.class, boolean.class, int.class
);
method.invoke(mOrigin, _arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6);
reply.writeNoException();
return true;
}
return doTransact(code, data, reply, flags);
}
public boolean doTransact(int code, Parcel data, Parcel reply, int flags) {
Method method = ReflectUtils.getDeclaredMethod(
mOrigin, "onTransact",
int.class, Parcel.class, Parcel.class, int.class
);
}
try {
return (Boolean) method.invoke(mOrigin, code, data, reply, flags);
} catch (Throwable e) {
Logger.e(e);
}
return false;
}
}
至此,我们找到了相对简单、兼容性好的系统接口回调的拦截方案。
如果该服务为Native实现,则需要参考我们的另一篇文章☞ 深入Binder拦截 ☜来解决。