IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android插件化开发指南——Hook技术(三)版本适配问题 -> 正文阅读

[移动开发]Android插件化开发指南——Hook技术(三)版本适配问题

1. 前言

Android插件化开发指南——Hook技术(一)【长文】AMS进行Hook的时候,我重新创建了一个低版本的项目,并创建了对应的模拟器来运行程序,以得到AMS对象。当时所使用的API版本为30,所以其实可以知道25其实和30AMSHook会略有不同。实际上两个分水岭为2326。前面几篇博客中所给出的Hook得到AMS对象的为2326之间的版本。对于大于等于26的版本需要重新做适配。

再次查看Android版本和API级别的对应关系:代号、标记和细分版本号
我们知道从OreoAndroid版本为8.0.0开始,API 级别就为 26。且http://androidxref.com/也支持查看对应的高版本系统的源码,所以这里就尝试以源码网站中的Oreo - 8.1.0_r33为例开始。

2. API版本大于26后的AMS

对于前面的流程从startActivity->startActivityForResult->Instrumentation->ams.startActivity的这个流程这里不再继续看源码进行追踪。这里直接找到Instrumentation这个类,对应的execStartActivity方法。简略版如下:

// API 26 ( >= 26)
// Instrumentation
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

不妨将之前博客中写的API 25版本也放置在这里:

// API 25 ( >= 23)
// Instrumentation
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

很明显这里得到AMS对象不再是使用ActivityManagerNative.getDefault(),而是ActivityManager.getService()方法。所以这里需要继续追踪一下ActivityManager这个类,ActivityManager.java

// ActivityManager.java (API 26)
/**
 * @hide
 */
public static IActivityManager getService(){
	return IActivityManagerSingleton.get();
}

这里按照语义还是使用了一个单例对象来进行存储,接着继续看这个单例对象IActivityManagerSingleton的源码。

// ActivityManager.java (API 26)
private static final Singleton<IActivityManager> IActivityManagerSingleton = 
	new Singleton<IActivityManager>() {
		@Override
		portected IActivityManager create() {
			final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
			final IActivityManager am = IActivityManager.Stub.asInterface(b);
			return am;
		}
	};

对于这个单例属性只需要执行它的create方法就可以创建对应的AMS。所以我们需要得到这个单例对象。类似的,由于其包装在Singleton类中,所以还是需要继续查看Singleton的源码:

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

这个类和前面的没有什么区别。所以这里还是首先得到这个单例类对象,然后通过create方法来获取到AMS对象。即:

Object iActivityManagerSingletonValue = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // 
    Field iActivityManagerSingletonField = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
    iActivityManagerSingletonField.setAccessible(true);
    iActivityManagerSingletonValue = iActivityManagerSingletonField.get(null);
}else{
    Class<?> aClass = Class.forName("android.app.ActivityManagerNative");
    Field getDefault = aClass.getDeclaredField("gDefault");
    getDefault.setAccessible(true);
    // 获取静态的gDefault对象
    iActivityManagerSingletonValue = getDefault.get(null);
}

// 而实际上AMS在单例Singleton中
Class<?> singletonClazz = Class.forName("android.util.Singleton");
Field mInstance = singletonClazz.getDeclaredField("mInstance");
mInstance.setAccessible(true);
Object amsObj = mInstance.get(iActivityManagerSingletonValue); // AMS

3. API版本大于28后的ActivityThread

需要注意的是在API版本大于28后,ActivityThread中对消息的处理也不一样。注意到API级别28对应的Android版本为9。刚好在http://androidxref.com/中提供了Pie - 9.0.0_r3系统的源码。但是这个网站用来查看代码实在是太卡了,所以这里还是修改grdle文件,然后直接在本地查看:

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.6"

    defaultConfig {
        applicationId "com.weizu.myapplication"
        minSdkVersion 28
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
     ...

在我本地其实是下载了对应的SDK,如下图:
在这里插入图片描述
如果需要下载对应版本的SDK,直接打开Seetings->Android SDK然后选择下载即可。这里我选择28

找到ActivityThread的消息处理对象mH,可以发现消息标识的编码已经没有了之前的100,也就是没有了LAUNCH_ACTIVITY。这里对应的修改为了EXECUTE_TRANSACTION至于怎么来的,这里就不追踪源码了。现在的为:

// ActivityThread.java  (API 28)
case EXECUTE_TRANSACTION: // 159
    final ClientTransaction transaction = (ClientTransaction) msg.obj;
    mTransactionExecutor.execute(transaction);
    if (isSystem()) {
        // Client transactions inside system process are recycled on the client side
        // instead of ClientLifecycleManager to avoid being cleared before this
        // message is handled.
        transaction.recycle();
    }
    // TODO(lifecycler): Recycle locally scheduled transactions.
    break;

之前的为:

// ActivityThread.java  (API 25)
case LAUNCH_ACTIVITY: {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

    r.packageInfo = getPackageInfoNoCheck(
            r.activityInfo.applicationInfo, r.compatInfo);
    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;

且在之前的代码中我们做Hanlder.Callback的时候,反射的是ActivityClientRecord这个对象,然后从r对象中得到其属性intent,然后将这个intent进行替换。

这里类似的首先需要追踪一下TransactionExecutor这个类,然后看下execute这个方法会执行什么?

// TransactionExecutor.java
public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

    executeCallbacks(transaction);

    executeLifecycleState(transaction);
    mPendingActions.clear();
    log("End resolving transaction");
}

继续查看executeCallbacks方法,但是在这个方法也没有找到和Intent相关的东西。

// TransactionExecutor.java
public void executeCallbacks(ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        if (callbacks == null) {
            // No callbacks to execute, return early.
            return;
        }
       	...
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            ...

但是在executeCallbacks中进行了List<ClientTransactionItem> callbacks的一个遍历。所以可以看看ClientTransactionItem中存放的什么内容。很遗憾只是一个抽闲的类,没有具体的实现。所以我们可通过ClientTransaction,找找addCallback方法。

// ClientTransaction.java
public void addCallback(ClientTransactionItem activityCallback) {
    if (mActivityCallbacks == null) {
        mActivityCallbacks = new ArrayList<>();
    }
    mActivityCallbacks.add(activityCallback);
}

所以我们只要找到调用addCallback的地方就可以知道ClientTransactionItem中存放的是什么东西。很不幸不太了解AMS的启动,所以这里还是直接给出答案:ActivityStackSupervisor

// ActivityStackSupervisor.java
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
        System.identityHashCode(r), r.info,
        // TODO: Have this take the merged configuration instead of separate global
        // and override configs.
        mergedConfiguration.getGlobalConfiguration(),
        mergedConfiguration.getOverrideConfiguration(), r.compat,
        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
        profilerInfo));

对应的LaunchActivityItem的类中就有一个mIntent属性:

public class LaunchActivityItem extends ClientTransactionItem {

    private Intent mIntent;
    ...
}

所以流程为:

  • 获取到mActivityCallbacks 集合;
  • 遍历mActivityCallbacks ,得到LaunchActivityItem
  • 替换LaunchActivityItem 中的Intent

因为mActivityCallbacks 属于ClientTransaction,首先是拿到ClientTransaction 实例对象。在ActivityThreadcase EXECUTE_TRANSACTION:处理中给了从消息中得到ClientTransaction对象的方法,即:

final ClientTransaction transaction = (ClientTransaction) msg.obj;

最终的代码如下:

private void handleLaunchActivity28(Message message) {
    try {
        Object clientTransactionObj = message.obj; // ClientTransaction
        Field mActivityCallbacksField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
        mActivityCallbacksField.setAccessible(true);
        List mActivityCallbacksValue = (List) mActivityCallbacksField.get(clientTransactionObj);

        // 遍历mActivityCallbacks
        for (Object o : mActivityCallbacksValue) {
            if(o.getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")){
                Field mIntentField = o.getClass().getDeclaredField("mIntent");
                mIntentField.setAccessible(true);
                Intent newIntent = (Intent) mIntentField.get(o);
                // 从这个newIntent得到真正的意图
                Intent oldIntent = newIntent.getParcelableExtra(ORIGIN_INTENT);
                if(oldIntent != null){
                    // 设置r中的intent为当前的这个oldIntent
                    mIntentField.set(o, oldIntent);
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述
最后测试效果也成功进入到了插件的Activity

代码地址:https://github.com/baiyazi/pluginLearn/tree/main/demo2


References

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-28 11:24:56  更:2021-11-28 11:25:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:08:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码