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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java基础知识点面试题,安卓程序员必备hook技术之进阶篇

發布時間:2023/12/15 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java基础知识点面试题,安卓程序员必备hook技术之进阶篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

方式1:使用Activity自帶的startActivity

示例代碼

private void startActivityByActivity() {Intent i = new Intent(MainActivity.this, Main2Activity.class);startActivity(i);}

程序執行走向圖.

代碼追蹤:


這里有個if(mParent==null)判定,先看true分支:

發現一個坑,mInstrumentation.execStartActivity 這里居然不能繼續往下索引了?很奇怪,不過不重要,我們直接進入Instrumentation.java去找這個方法:


在這個execStartActivity中,可以找到關鍵代碼:

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);

通過這種方式啟動Activity,最終的執行權被交給了 ActivityManager.getService()(即AMS),它的作用是 啟動一個Activity并且返回result,然后checkStartActivityResult(result, intent);這句話,對當前的跳轉意圖intent進行檢測;

have you declared this activity in your AndroidManifest.xml 這句異常應該很熟悉了吧?啟動一個沒有注冊的Activity的報錯.

再看個if(mParent==null)的false分支:



控制權依然是交給了mInstrumentation.execStartActivity(),剩余的代碼索引和上面的一樣.

所以,代碼索引的結論,按照一張圖來表示就是:

方式2:使用applictonContext的startActivity

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

方式1 中已經展示了源碼索引的方式,所以這里不再贅述貼圖.直接給出代碼索引結論圖:

兩張圖對比,我們很容易得出一個結論:
啟動Activity的最終執行權,都被交給了 Instrumentation.java 類,
方式1:Activity.startActivity的最終執行者是 它的mInstrumentation成員,mInstrumentation的持有者是 Activity自身.
方式2:getApplicationContext().startActivity(i); 的最終執行者是:ActivityThread的 mInstrumentation成員,持有者是ActivityThread 主線程.
兩種方式都可以把mInstrumentation當作hook切入點,將它從它的持有者中"偷梁換柱".

下面開始動手嘗試:

##二. 第一種啟動方式的hook方案

創建一個HookActivityHelper.java ,然后三步走:

  • 找到hook點,以及hook對象的持有者,上文中已經說明:hook點是Activity的mInstrumentation成員,持有者就是Activity
  • Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");mInstrumentationField.setAccessible(true);Instrumentation base = (Instrumentation) mInstrumentationField.get(activity);

    base是系統原來的執行邏輯,存起來后面用得著.

  • 創建Instrumentation代理類, 繼承Instrumentation然后,重寫execStartActivity方法,加入自己的邏輯,然后再執行系統的邏輯.
  • private static class ProxyInstrumentation extends Instrumentation {public ProxyInstrumentation(Instrumentation base) {this.base = base;}Instrumentation base;public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {Log.d("ProxyInstrumentation", "我們自己的邏輯");//這里還要執行系統的原本邏輯,但是突然發現,這個execStartActivity居然是hide的,只能反射咯try {Class<?> InstrumentationClz = Class.forName("android.app.Instrumentation");Method execStartActivity = InstrumentationClz.getDeclaredMethod("execStartActivity",Context.class, IBinder.class, IBinder.class, Activity.class,Intent.class, int.class, Bundle.class);return (ActivityResult) execStartActivity.invoke(base, who, contextThread, token, target, intent, requestCode, options);} catch (Exception e) {e.printStackTrace();}return null;}}
  • 用代理類對象替換 hook對象.
  • ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);mInstrumentationField.set(activity, proxyInstrumentation);

    如何使用: 在MainActivity的onCreate中加入一行ActivityHookHelper.hook(this)

    public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ActivityHookHelper.hook(this);findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivityByActivity();}});}private void startActivityByActivity() {Intent i = new Intent(MainActivity.this, Main2Activity.class);startActivity(i);}}

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

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

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

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

    //1.主線程ActivityThread內部的mInstrumentation對象,先把他拿出來Class<?> ActivityThreadClz = Class.forName("android.app.ActivityThread");//再拿到sCurrentActivityThreadField sCurrentActivityThreadField = ActivityThreadClz.getDeclaredField("sCurrentActivityThread");sCurrentActivityThreadField.setAccessible(true);Object activityThreadObj = sCurrentActivityThreadField.get(null);//靜態變量的屬性get不需要參數,傳null即可.//再去拿它的mInstrumentationField 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 {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main4);ApplicationContextHookHelper.hook();findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {@Overridepublic 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().startActivity(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() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//proxy是創建出來的代理類,method是接口中的方法,args是接口執行時的實參if (method.getName().equals("startActivity")) {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//proxy是創建出來的代理類,method是接口中的方法,args是接口執行時的實參if (method.getName().equals("startActivity")) {

    總結

    以上是生活随笔為你收集整理的Java基础知识点面试题,安卓程序员必备hook技术之进阶篇的全部內容,希望文章能夠幫你解決所遇到的問題。

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