日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

hook(2)Activity启动流程,2021年大厂Android岗面试必问

發布時間:2023/12/15 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 hook(2)Activity启动流程,2021年大厂Android岗面试必问 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

}

效果:跳轉依然正常,并且logcat中可以發現下面的日志.

#####ok,插入自己的邏輯,成功


##三. 第二種啟動方式的hook方案
創建ApplicationContextHookHelper.java,然后 同樣是三步走:

1.確定hook的對象和該對象的持有者
鎖定 ActivityThread的mInstrumentation成員.

//1.主線程ActivityThread內部的mInstrumentation對象,先把他拿出來
Class<?> ActivityThreadClz = Class.forName(“android.app.ActivityThread”);
//再拿到sCurrentActivityThread
Field sCurrentActivityThreadField = ActivityThreadClz.getDeclaredField(“sCurrentActivityThread”);
sCurrentActivityThreadField.setAccessible(true);
Object activityThreadObj = sCurrentActivityThreadField.get(null);//靜態變量的屬性get不需要參數,傳null即可.
//再去拿它的mInstrumentation
Field mInstrumentationField = ActivityThreadClz.getDeclaredField(“mInstrumentation”);
mInstrumentationField.setAccessible(true);
Instrumentation base = (Instrumentation) mInstrumentationField.get(activityThreadObj);// OK,拿到

2.創建代理對象 和上面的代理類一模一樣,就不重復貼代碼了

//2.構建自己的代理對象,這里Instrumentation是一個class,而不是接口,所以只能用創建內部類的方式來做
ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);

3.替換掉原對象

//3.偷梁換柱
mInstrumentationField.set(activityThreadObj, proxyInstrumentation);

如何使用: 在Main4Activity的onCreate中加入一行ApplicationContextHookHelper.hook();

public class Main4Activity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);

ApplicationContextHookHelper.hook();
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivityByApplicationContext();
}
});
}

private void startActivityByApplicationContext() {
Intent i = new Intent(Main4Activity.this, Main5Activity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(i);
}
}

效果

####OK,第二種啟動方式,我們也可以加入自己的邏輯了.hook成功!


##四. 目前方案弊端分析
啟動方式1的hook: 只是在針對單個Activity類,來進行hook,多個Activity則需要寫多次,或者寫在BaseActivity里面.
啟動方式2的hook:可以針對全局進行hook,無論多少個Activity,只需要調用一次ApplicationContextHookHelper.hook();函數即可,但是,它只能針對 getApplicationContext().start![](https://www.hualigs.cn/image/61dba891ed8ee.jpg) Activity(i); 普通的Activity.startActivity則不能起作用.

那么有沒有一種完全體的解決方案:能夠在全局起作用,并且可以在兩種啟動方式下都能hook.
回顧之前的兩張代碼索引結論圖,會發現,兩種啟動Activity的方式,最終都被執行到了 AMS內部,
下一步,嘗試hook AMS.


##五. 最終解決方案

代碼索引: 基于SDK 28 ~ android9.0

下方紅框標記的部分,就是取得AMS(ActivityManagerService實例)的代碼.

如果可以在系統接收到AMS實例之前,把他截了,是不是就可以達到我們的目的?
進去看看getService的代碼:

真正的AMS實例來自一個Singleton單例輔助類的create()方法,并且這個Singleton單例類,提供get方法,獲得真正的實例.

那么,我們從這個單例中,就可以獲得系統當前的 AMS實例,將它取出來,然后保存.
OK,確認:
hook對象: ActivityManager的IActivityManagerSingleton成員 變量內的 單例 mInstance.
hook對象的持有者:ActivityManager的IActivityManagerSingleton成員變量

那么,動手:

  • 找到hook對象,并且存起來
  • //1.把hook的對象取出來保存
    //矮油,靜態的耶,開心.
    Class<?> ActivityManagerClz = Class.forName(“android.app.ActivityManager”);
    Method getServiceMethod = ActivityManagerClz.getDeclaredMethod(“getService”);
    final Object IActivityManagerObj = getServiceMethod.invoke(null);//OK,已經取得這個系統自己的AMS實例

  • 創建自己的代理類對象,IActivityManager 是一個AIDL生成的動態接口類,所以在編譯時,androidStudio會找不到這個類,所以,先反射,然后用Proxy進行創建代理。
  • //2.現在創建我們的AMS實例
    //由于IActivityManager是一個接口,那么我們可以使用Proxy類來進行代理對象的創建
    // 結果被擺了一道,IActivityManager這玩意居然還是個AIDL,動態生成的類,編譯器還不認識這個類,怎么辦?反射咯
    Class<?> IActivityManagerClz = Class.forName(“android.app.IActivityManager”);
    Object proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
    new Class[]{IActivityManagerClz}, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //proxy是創建出來的代理類,method是接口中的方法,args是接口執行時的實參
    if (method.getName().equals(“startActivity”)) {
    Log.d(“GlobalActivityHook”, “全局hook 到了 startActivity”);
    }
    return method.invoke(IActivityManagerObj, args);
    }
    });

  • 偷梁換柱:這次有點復雜, 不再是簡單的field.set,因為這次的hook對象被包裹在了一個Singleton里。
  • //3.偷梁換柱,這里有點糾結,這個實例居然被藏在了一個單例輔助類里面
    Field IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(“IActivityManagerSingleton”);
    IActivityManagerSingletonField.setAccessible(true);
    Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);
    //反射創建一個Singleton的class
    Class<?> SingletonClz = Class.forName(“android.util.Singleton”);
    Field mInstanceField = SingletonClz.getDeclaredField(“mInstance”);
    mInstanceField.setAccessible(true);
    mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);

    使用方法:老樣子,在你自己的Activity onCreate里面加入GlobalActivityHookHelper.hook();
    運行起來,預期結果應該是:能夠在logcat中看到日志 :
    GlobalActivityHook - 全局hook 到了 startActivity;
    但是,你運行起來可能看不到這一行。

    如果你看不到這個日志,那么原因就是:

    程序報錯了,


    沒有這樣的方法,怎么回事?
    debug找原因:

    為什么會沒有getService這個方法!?
    查看了我當前設備的系統版本號
    居然是23版本,6.0.
    所以,恍然大悟,我們寫的hook代碼并沒有兼容性,遇到低版本的設備,就失靈了.

    解決方案:

    1.找到SDK 23的源碼
    (注意,前方有坑,androidStudio,你如果直接把combileSDK改成23.會出現很多位置問題,所以不建議這么做. 但是我們一定要看SDK 23的源碼,怎么辦?

    • .在線查看源碼 - https://www.androidos.net.cn/sourcecode ;
    • 從谷歌官網下載SDK 23的源碼,然后用SourceInsight查看)

    2.查看getService方法不存在的原因,兩個版本28 和 23,在這一塊代碼上有什么不同.
    3.改造 GlobalActivityHookHelper.java ,判定當前設備的系統版本號,讓它可以兼容所有版本.

    按照上面的步驟:
    我發現SDK 23里面:
    Instrumentation類的 execStartActivitiesAsUser(Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId)方法里,獲取AMS實例的方式完全不同.


    它是使用 ActivityManagerNative.getDefault()來獲得的,繼續往下找,看看有沒有什么不同。
    進去ActivityManagerNative 找找看:

    OK,找到了區別,確定結論:SDK 28和23在這塊代碼上的區別就是:
    獲得AMS實例的類名和方法名都不同.另外,查了度娘之后發現,這個變化是在SDK 26版本修改的,所以26和26以后,ActivityManager.getService()來獲取,26以前,用ActivityManagerNative.getDefault()來獲得
    調整當前的hook方法,修改為下面這樣:

    public class GlobalActivityHookHelper {

    //設備系統版本是不是大于等于26
    private static boolean ifSdkOverIncluding26() {
    int SDK_INT = Build.VERSION.SDK_INT;
    if (SDK_INT > 26 || SDK_INT == 26) {
    return true;
    } else {
    return false;
    }
    }

    public static void hook() {

    try {
    Class<?> ActivityManagerClz;
    final Object IActivityManagerObj;
    if (ifSdkOverIncluding26()) {
    ActivityManagerClz = Class.forName(“android.app.ActivityManager”);
    Method getServiceMethod = ActivityManagerClz.getDeclaredMethod(“getService”);
    IActivityManagerObj = getServiceMethod.invoke(null);//OK,已經取得這個系統自己的AMS實例
    } else {
    ActivityManagerClz = Class.forName(“android.app.ActivityManagerNative”);
    Method getServiceMethod = ActivityManagerClz.getDeclaredMethod(“getDefault”);
    IActivityManagerObj = getServiceMethod.invoke(null);//OK,已經取得這個系統自己的AMS實例
    }

    //2.現在創建我們的AMS實例
    //由于IActivityManager是一個接口,那么其實我們可以使用Proxy類來進行代理對象的創建
    // 結果被擺了一道,IActivityManager這玩意居然還是個AIDL,動態生成的類,編譯器還不認識這個類,怎么辦?反射咯
    Class<?> IActivityManagerClz = Class.forName(“android.app.IActivityManager”);
    Object proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IActivityManagerClz}, new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //proxy是創建出來的代理類,method是接口中的方法,args是接口執行時的實參
    if (method.getName().equals(“startActivity”)) {
    Log.d(“GlobalActivityHook”, “全局hook 到了 startActivity”);
    }
    return method.invoke(IActivityManagerObj, args);
    }
    });

    //3.偷梁換柱,這里有點糾結,這個實例居然被藏在了一個單例輔助類里面
    Field IActivityManagerSingletonField;
    if (ifSdkOverIncluding26()) {
    IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(“IActivityManagerSingleton”);
    } else {
    IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(“gDefault”);
    }

    IActivityManagerSingletonField.setAccessible(true);
    Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);
    Class<?> SingletonClz = Class.forName(“android.util.Singleton”);//反射創建一個Singleton的class
    Field mInstanceField = SingletonClz.getDeclaredField(“mInstance”);
    mInstanceField.setAccessible(true);
    mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);

    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    再次嘗試:

    成功,實現了全局范圍內的startActivity動作的hook.


    ##六. Hook開發可能的坑
    1. androidStudio閱讀源碼很多類無法索引,這是因為有一些類是@hide的,無法Ctrl點進去,
    解決方案:Ctrl+shift+R 輸入類名,手動進入.

    2.androidStudio閱讀源碼直接報紅 :或者一些是AIDL動態生成的接口,無法直接查看,比IActivityManager. ,
    解決方案:這種接口不用管它,如果非要用到它,那就使用本類的包名+IActivityManager作為全限定名,去反射創建它.

    3. hook開發,是學習源碼思想,改變源碼執行流程,所以,在多個版本的設備上運行,很容易發生不兼容的情況.
    解決方案:找到不兼容的設備版本,根據報的異常,參照源碼的版本變遷做出相應的兼容性改動.


    #結語
    歷時3天,忙里偷閑,總算是寫完了.
    喜歡的客官幫忙點個贊哦,你們的鼓勵是我最大的動力,以后還會更新更多干貨.
    最后~本文的代碼Demo.

    它,如果非要用到它,那就使用本類的包名+IActivityManager作為全限定名,去反射創建它.**

    3. hook開發,是學習源碼思想,改變源碼執行流程,所以,在多個版本的設備上運行,很容易發生不兼容的情況.
    解決方案:找到不兼容的設備版本,根據報的異常,參照源碼的版本變遷做出相應的兼容性改動.


    #結語
    歷時3天,忙里偷閑,總算是寫完了.
    喜歡的客官幫忙點個贊哦,你們的鼓勵是我最大的動力,以后還會更新更多干貨.
    最后~本文的代碼Demo.

    總結

    以上是生活随笔為你收集整理的hook(2)Activity启动流程,2021年大厂Android岗面试必问的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。