Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android——插件化學習筆記(一)

Android——插件化學習筆記(一)

編輯:關於Android編程

寫了一個月應用層代碼,感覺寫嘔了,最近在研究插件化動態加載方面的東西。

本文需要解決的作業:在Activity自身的跳轉中進行Hook。

先簡要說下遇到的幾個坑以及後面的學習整理:

Activity 啟動流程和Context類詳解:

比較重要的點是要理清Context的繼承關系

\

由圖可一目了然Activity的繼承關系,由於Activity是繼承自Context的包裝類ContextWrapper的,在我們的Activity得到Context對象時,會通過重寫的attachBaseContext方法得到Context實例。

這也就是在第一個例子中為什麼在attachBaseContext中執行hook方法的原因:

\

然而這裡要特別注意一點,作者的tainn的例子中使用的是ApplicationContext,然而作業使用到的並不是ContextImpl的mInstrumentation而是自己的mInstrumentation,我們來看看Activity中調用attachBaseContext調用的地方:

\

可以看到attachBaseContext是在Activity的成員變量mInstrumentation初始化之前進行調用的,所以不能像作者一樣在attachBaseContext中執行hook方法,這裡我選擇了在執行startActivity之前調用以保證能hook到。

 

作業流程:

1.首先修改MainActivity中的點擊事件和hook觸發方法的:

 

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // TODO: 16/1/28 支持Activity直接跳轉請在這裡Hook
        // 家庭作業,留給讀者完成.

        LinearLayout linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);
        LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(300, 100);
        btnParams.setMargins(10, 10, 10, 10);

//        Button tv = new Button(this);
//        tv.setLayoutParams(btnParams);
//        tv.setText("測試1");
//        linearLayout.addView(tv);

        Button tv2 = new Button(this);
        tv2.setLayoutParams(btnParams);
        tv2.setText("家庭作業");
        linearLayout.addView(tv2);

        setContentView(linearLayout);

//        tv.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                Intent intent = new Intent(Intent.ACTION_VIEW);
//                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//                intent.setData(Uri.parse("http://www.baidu.com"));
//                // 注意這裡使用的ApplicationContext 啟動的Activity
//                // 因為Activity對象的startActivity使用的並不是ContextImpl的mInstrumentation
//                // 而是自己的mInstrumentation, 如果你需要這樣, 可以自己Hook
//                // 比較簡單, 直接替換這個Activity的此字段即可.
//                getApplicationContext().startActivity(intent);
//            }
//        });

        tv2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(MainActivity.this,OtherActivity.class);
                // 在這裡進行Hook
                Log.d("xiaonangua","開始hook");

                try {
                    MyHookHelper.attachContext(MainActivity.this);
                } catch (Exception e) {
                    Log.d("xiaonangua",e.getMessage().toString());

                    e.printStackTrace();
                }
                startActivity(i);
            }
        });
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
          //  HookHelper.attachContext();
        } catch (Exception e) {
            Log.d("xiaonangua",e.getMessage().toString());
            e.printStackTrace();
        }
    }
}

 

2.與tainn作者分析Context的調用鏈不同,我們先來看看Activity.startActivity()源碼:

 

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
			.....
接下來要做的就是把mInstrumentation換成我們修改過的代理對象,通過反射得到Activity的Instrumentation字段然後創建代理對象並偷梁換柱QAQ:

 

 

public class MyHookHelper {
    public static void attachContext(Activity mActivity) throws Exception{
        // 先獲取到當前的ActivityThread對象
        Class activityClass = Class.forName("android.app.Activity");

        // 拿到原始的 mInstrumentation字段
        Field mInstrumentationField = activityClass.getDeclaredField("mInstrumentation");//獲取屬性
        //打破封裝
        mInstrumentationField.setAccessible(true);

        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(mActivity);

        // 創建代理對象
        Instrumentation myEvilInstrumentation = new MyEvillnstrumentation(mInstrumentation);

        // 偷梁換柱
        mInstrumentationField.set(mActivity, myEvilInstrumentation);
    }
}
代理類基本和作者的一樣,加了一個小字段而已:

 

 

public class MyEvillnstrumentation  extends Instrumentation {

    private static final String TAG = "xiaonangua";

    // ActivityThread中原始的對象, 保存起來
    Instrumentation mBase;

    public MyEvillnstrumentation(Instrumentation base) {
        mBase = base;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        // Hook之前, XXX到此一游!
        Log.d(TAG, "\n南瓜執行了startActivity, 參數如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");

        // 開始調用原始的方法, 調不調用隨你,但是不調用的話, 所有的startActivity都失效了.
        // 由於這個方法是隱藏的,因此需要使用反射調用;首先找到這個方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);

            execStartActivity.setAccessible(true);
            Log.d("xiaonangua",execStartActivity.toString());
            return (ActivityResult) execStartActivity.invoke(mBase,who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            Log.d("xiaonangua",e.getMessage().toString());
            return null;
            // 某該死的rom修改了  需要手動適配
            //throw new RuntimeException("do not support!!! pls adapt it");
        }
    }
}


然後啟動app,運行,打印log如下:

 

 

10-28 16:51:02.520 26242-26242/com.weishu.upf.dynamic_proxy_hook.app2 D/xiaonangua: 開始hook
10-28 16:51:02.530 26242-26242/com.weishu.upf.dynamic_proxy_hook.app2 D/xiaonangua: 南瓜執行了startActivity, 參數如下: 
                                                                                    who = [com.weishu.upf.dynamic_proxy_hook.app2.MainActivity@3ec0c1af], 
                                                                                    contextThread = [android.app.ActivityThread$ApplicationThread@2f6ff9bc], 
                                                                                    token = [android.os.BinderProxy@2222360], 
                                                                                    target = [com.weishu.upf.dynamic_proxy_hook.app2.MainActivity@3ec0c1af], 
                                                                                    intent = [Intent { cmp=com.weishu.upf.dynamic_proxy_hook.app2/.OtherActivity }], 
                                                                                    requestCode = [-1], 
                                                                                    options = [null]

Success~成功地完成了第一篇家庭作業哈哈哈。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved