Hook技術之Hook Activity

一、Hook技術概述


Hook技術的核心實際上是動態分析技術,動態分析是指在程序運行時對程序進行調試的技術。眾所周知,Android系統的代碼和回調是按照一定的順序執行的,這里舉一個簡單的例子,如圖所示。

對象A調用類對象B,對象B處理后將數據回調給對象A。接下來看看采用Hook的調用流程,如下圖:

上圖中的Hook可以是一個方法或者一個對象,它就想一個鉤子一樣,始終連著AB,在AB之間互傳信息的時候,hook會在中間做一些處理,比如修改方法的參數和返回值等,就這樣hook起到了欺上瞞下的作用,我們把hook的這種行為稱之為劫持。同理,大家知道,系統進程和應該進程之間是相互獨立的,應用進程要想直接去修改系統進程,這個是很難實現的,有了hook技術,就可以在進程之間進行行為更改了。如圖所示:

可見,hook將自己融入到它所劫持的對象B所在的進程中,成為系統進程的一部分,這樣我們就可以通過hook來更改對象B的行為了,對象B就稱為hook點。

二、Hook Instrumentation


上面講了Hook可以劫持對象,被劫持的對象叫hook點,用代理對象來替代這個Hook點,這樣我們就可以在代理上實現自己想做的操作。這里我們用Hook startActivity來舉例。Activity的插件化中需要解決的一個問題就是啟動一個沒有在AndroidManifest中注冊的Activity,如果按照正常的啟動流程是會報crash的。這里先簡要介紹一下Activity的啟動,具體的啟動方式講解還需移步專門的文獻。

2.1 Activity的Hook點

啟動Activity時應用進程會發消息給AMS,請求AMS創建Activity,AMS在SystemServer系統進程中,其與應用進程是隔離的,AMS管理所有APP的啟動,所以我們無法在系統進程下做hook操作,應該在應用進程中。為了繞過AMS的驗證,我們需要添加一個在Manifest中注冊過的Activity,這個Activity稱為占坑,這樣可以達到欺上瞞下的效果,當AMS驗證通過后再用插件Activity替換占坑去實現相應的功能。 核心功能兩點:

  • 替換插件Activity為占坑Activity
  • 繞過AMS驗證后需要還原插件Activity

啟動Activity的時候會調用Activity的startActivity()如下:

   @Overridepublic void startActivity(Intent intent) {this.startActivity(intent, null);}
復制代碼

接著又調用了startActivity()

    @Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}}
復制代碼

查看startActivityForResult方法

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {// If this start is requesting a result, we can avoid making// the activity visible until the result is received.  Setting// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the// activity hidden during this time, to avoid flickering.// This can only be done when a result is requested because// that guarantees we will get information back when the// activity is finished, no matter what happens to it.mStartedActivity = true;}cancelInputsAndStartExitTransition(options);// TODO Consider clearing/flushing other event sources and events for child windows.} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {// Note we want to go through this method for compatibility with// existing applications that may have overridden it.mParent.startActivityFromChild(this, intent, requestCode);}}}
復制代碼

上述方法中調用mInstrumentation的execStartActivity方法來啟動Activity,這個mInstrumentation是Activity的成員變量,我們就選擇Instrumentation為Hook點,用代理的Instrumentation去替換原始的Instrumentation來完成Hook,如下是代理類:

public class InstrumentationProxy extends Instrumentation {private Instrumentation mInstrumentation;private PackageManager mPackageManager;public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {this.mInstrumentation = instrumentation;this.mPackageManager = packageManager;}public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {List<ResolveInfo> resolveInfo = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);//判斷啟動的插件Activity是否在AndroidManifest.xml中注冊過if (null == resolveInfo || resolveInfo.size() == 0) {//保存目標插件intent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME, intent.getComponent().getClassName());//設置為占坑Activityintent.setClassName(who, "replugin.StubActivity");}try {Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity",Context.class, IBinder.class, IBinder.class, Activity.class,Intent.class, int.class, Bundle.class);return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException,IllegalAccessException, ClassNotFoundException {String intentName = intent.getStringExtra(HookHelper.REQUEST_TARGET_INTENT_NAME);if (!TextUtils.isEmpty(intentName)) {return super.newActivity(cl, intentName, intent);}return super.newActivity(cl, className, intent);}}
復制代碼

InstrumentationProxy類繼承類Instrumentation,實現了類execStartActivity方法,接著通過反射去用原始Instrumentation的execStartActivity方法,這就是替換為占坑Activity的過程。Activity的創建是在ActivityThread中,里面有個performLaunchActivity方法;

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}}...activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback);...
}
復制代碼

這里的newActivity就是創建Activity的過程,我們同樣的在代理類中去實現這個方法,這就是還原插件Activity 的過程。

接下來我們看個例子: 占位坑Activity:

public class StubActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_stub;}@Overridepublic void initViews() {}@Overridepublic void onClick(View v) {}
}
復制代碼

這個Activity一定是需要在AndroidManifest中去注冊。 再寫一個插件Activity

public class TargetActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_target;}@Overridepublic void initViews() {}@Overridepublic void onClick(View v) {}
}
復制代碼

都是很簡單的Activity,TargetActivity并沒有注冊,現在我們需要啟動這個Activity。代理類上面代碼已經貼出來了。接下來就是替換代理類,達到Hook的目的,我們在Application中做這個事情:

public class MyApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);hookActivityThreadInstrumentation();}private void hookActivityThreadInstrumentation() {try {Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");Field activityThreadField=activityThreadClass.getDeclaredField("sCurrentActivityThread");activityThreadField.setAccessible(true);//獲取ActivityThread對象sCurrentActivityThreadObject activityThread=activityThreadField.get(null);Field instrumentationField=activityThreadClass.getDeclaredField("mInstrumentation");instrumentationField.setAccessible(true);//從sCurrentActivityThread中獲取成員變量mInstrumentationInstrumentation instrumentation= (Instrumentation) instrumentationField.get(activityThread);//創建代理對象InstrumentationProxyInstrumentationProxy proxy=new InstrumentationProxy(instrumentation,getPackageManager());//將sCurrentActivityThread中成員變量mInstrumentation替換成代理類InstrumentationProxyinstrumentationField.set(activityThread,proxy);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
復制代碼

這樣就把原始的Instrumentation替換為代理的了,具體的操作我們在InstrumentationProxy中去做實現。接下來我們就是從主界面跳轉插件Activity了:

public class PluginActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_stub;}@Overridepublic void initViews() {Log.d("", "initViews: ");findViewById(R.id.btn_start_replugin).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(PluginActivity.this, TargetActivity.class));}});}@Overridepublic void onClick(View v) {}public static void startActivity(Context context) {Intent i = new Intent(context, PluginActivity.class);context.startActivity(i);}}
復制代碼

轉載于:https://juejin.im/post/5c67da6251882562547b99ab

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388269.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388269.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388269.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(第三周)周報

此作業要求https://edu.cnblogs.com/campus/nenu/2018fall/homework/2143 1.本周PSP 總計&#xff1a;1422 min 2.本周進度條 (1)代碼累積折線圖 (2)博文字數累積折線圖 4.PSP餅狀圖 轉載于:https://www.cnblogs.com/gongylx/p/9761852.html

功能測試代碼python_如何使您的Python代碼更具功能性

功能測試代碼pythonFunctional programming has been getting more and more popular in recent years. Not only is it perfectly suited for tasks like data analysis and machine learning. It’s also a powerful way to make code easier to test and maintain.近年來&am…

layou split 屬性

layou split&#xff1a;true - 顯示側分欄 轉載于:https://www.cnblogs.com/jasonlai2016/p/9764450.html

BZOJ4503:兩個串(bitset)

Description 兔子們在玩兩個串的游戲。給定兩個字符串S和T&#xff0c;兔子們想知道T在S中出現了幾次&#xff0c;分別在哪些位置出現。注意T中可能有“?”字符&#xff0c;這個字符可以匹配任何字符。Input 兩行兩個字符串&#xff0c;分別代表S和TOutput 第一行一個正整數k&…

C#Word轉Html的類

C#Word轉Html的類/**//******************************************************************** created: 2007/11/02 created: 2:11:2007 23:13 filename: D:C#程序練習WordToChmWordToHtml.cs file path: D:C#程序練習WordToChm file bas…

分庫分表的幾種常見形式以及可能遇到的難題

前言 在談論數據庫架構和數據庫優化的時候&#xff0c;我們經常會聽到“分庫分表”、“分片”、“Sharding”…這樣的關鍵詞。讓人感到高興的是&#xff0c;這些朋友所服務的公司業務量正在&#xff08;或者即將面臨&#xff09;高速增長&#xff0c;技術方面也面臨著一些挑戰。…

iOS 鑰匙串的基本使用

級別&#xff1a; ★☆☆☆☆ 標簽&#xff1a;「鑰匙串」「keychain」「iOS」 作者&#xff1a; WYW 審校&#xff1a; QiShare團隊 前言 &#xff1a; 項目中有時會需要存儲敏感信息&#xff08;如密碼、密鑰等&#xff09;&#xff0c;蘋果官方提供了一種存儲機制--鑰匙串&a…

線性回歸和將線擬合到數據

Linear Regression is the Supervised Machine Learning Algorithm that predicts continuous value outputs. In Linear Regression we generally follow three steps to predict the output.線性回歸是一種監督機器學習算法&#xff0c;可預測連續值輸出。 在線性回歸中&…

Spring Boot MyBatis配置多種數據庫

mybatis-config.xml是支持配置多種數據庫的&#xff0c;本文將介紹在Spring Boot中使用配置類來配置。 1. 配置application.yml # mybatis配置 mybatis:check-config-location: falsetype-aliases-package: ${base.package}.modelconfiguration:map-underscore-to-camel-case: …

小米盒子4 拆解圖解_我希望當我開始學習R時會得到的盒子圖解指南

小米盒子4 拆解圖解Customizing a graph to transform it into a beautiful figure in R isn’t alchemy. Nonetheless, it took me a lot of time (and frustration) to figure out how to make these plots informative and publication-quality. Rather than hoarding this …

組態王仿真隨機數

1、新建IO設備&#xff0c;選擇PLC---亞控---仿真PLC&#xff0c;一直“下一步”。 2、在“數據詞典”中新建變量“Tag1”&#xff0c;雙擊Tag1&#xff0c;變量類型選&#xff1a;I/O實數&#xff1b;初始值設為&#xff1a;0.6&#xff1b;最小值設為&#xff1a;0.5&#xf…

藍牙一段一段_不用擔心,它在那里存在了一段時間

藍牙一段一段You’re sitting in a classroom. You look around and see your friends writing something down. It seems they are taking the exam, and they know all the answers (even Johnny who, how to say it… wasn’t the brilliant one). You realize that your ex…

Linux基礎命令---ifup、ifdown

ifupifup指令用來啟動網絡接口設備&#xff0c;設備必須是定義在“/etc/sysconfig/network-scripts/ifcfg-ethX”或者“/etc/sysconfig/network”的文件。這些腳本通常使用一個參數&#xff1a;配置的名稱(例如eth0)。在引導序列中&#xff0c;使用“boot”的第二個參數調用它們…

OllyDBG 入門之四--破解常用斷點設

OllyDBG 入門之四--破解常用斷點&#xff08;轉&#xff09; 軟件漢化2010-07-08 16:25:23 閱讀76評論0 字號&#xff1a;大中小 訂閱 bpx hmemcpy 破解萬能斷點&#xff0c;攔截內存拷貝動作 bpx Lockmytask 當你用其它斷點都無效時可以試一下&#xff0c;這個斷點攔截…

POJ1204 Word Puzzles

傳送門 這題果然是AC自動機的大好題&#xff01; 題目的大意是&#xff0c;給定一個l*c的大網格&#xff0c;每個格子里有一個字符&#xff0c;每個格子可以向八個方向形成字符串&#xff0c;問給定的字符串在哪里能被匹配以及在網格中出現的方向&#xff08;A代表北&#xff0…

普通話測試系統_普通話

普通話測試系統Traduzido/adaptado do original por Vincius Barqueiro a partir do texto original “Writing Alt Text for Data Visualization”, escrito por Amy Cesal e publicado no blog Nightingale.Traduzido / adaptado由 VinciusBarqueiro 提供原始 文本“為數據可…

Mac OS 被XCode搞到無法正常開機怎么辦?

Mac OS 被XCode搞到無法正常開機怎么辦&#xff1f; 第一天拿到這臺air的時候&#xff0c;迫不及待地把從別處搜集來的XCode 3.2.5iOS SDK 4.1的dmg安裝了上來&#xff0c;結果系統直接崩潰&#xff0c;再開機就不能正常開機&#xff0c;總是碰到kernel panic。這不坑爹嗎…… …

美國隊長3:內戰_隱藏的寶石:尋找美國最好的秘密線索

美國隊長3:內戰There are plenty of reasons why one would want to find solitude in the wilderness, from the therapeutic effects of being immersed in nature, to not wanting to contribute to trail degradation and soil erosion on busier trails.人們有很多理由想要…

Java入門第三季——Java中的集合框架(中):MapHashMap

1 package com.imooc.collection;2 3 import java.util.HashSet;4 import java.util.Set;5 6 /**7 * 學生類8 * author Administrator9 * 10 */ 11 public class Student { 12 13 public String id; 14 15 public String name; 16 17 public Set<…

【譯】 WebSocket 協議第八章——錯誤處理(Error Handling)

概述 本文為 WebSocket 協議的第八章&#xff0c;本文翻譯的主要內容為 WebSocket 錯誤處理相關內容。 錯誤處理&#xff08;協議正文&#xff09; 8.1 處理 UTF-8 數據錯誤 當終端按照 UTF-8 的格式來解析一個字節流&#xff0c;但是發現這個字節流不是 UTF-8 編碼&#xff0c…