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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Hook技术之Hook Activity

發(fā)布時(shí)間:2023/11/29 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Hook技术之Hook Activity 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、Hook技術(shù)概述


Hook技術(shù)的核心實(shí)際上是動(dòng)態(tài)分析技術(shù),動(dòng)態(tài)分析是指在程序運(yùn)行時(shí)對(duì)程序進(jìn)行調(diào)試的技術(shù)。眾所周知,Android系統(tǒng)的代碼和回調(diào)是按照一定的順序執(zhí)行的,這里舉一個(gè)簡(jiǎn)單的例子,如圖所示。

對(duì)象A調(diào)用類(lèi)對(duì)象B,對(duì)象B處理后將數(shù)據(jù)回調(diào)給對(duì)象A。接下來(lái)看看采用Hook的調(diào)用流程,如下圖:

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

可見(jiàn),hook將自己融入到它所劫持的對(duì)象B所在的進(jìn)程中,成為系統(tǒng)進(jìn)程的一部分,這樣我們就可以通過(guò)hook來(lái)更改對(duì)象B的行為了,對(duì)象B就稱(chēng)為hook點(diǎn)。

二、Hook Instrumentation


上面講了Hook可以劫持對(duì)象,被劫持的對(duì)象叫hook點(diǎn),用代理對(duì)象來(lái)替代這個(gè)Hook點(diǎn),這樣我們就可以在代理上實(shí)現(xiàn)自己想做的操作。這里我們用Hook startActivity來(lái)舉例。Activity的插件化中需要解決的一個(gè)問(wèn)題就是啟動(dòng)一個(gè)沒(méi)有在AndroidManifest中注冊(cè)的Activity,如果按照正常的啟動(dòng)流程是會(huì)報(bào)crash的。這里先簡(jiǎn)要介紹一下Activity的啟動(dòng),具體的啟動(dòng)方式講解還需移步專(zhuān)門(mén)的文獻(xiàn)。

2.1 Activity的Hook點(diǎn)

啟動(dòng)Activity時(shí)應(yīng)用進(jìn)程會(huì)發(fā)消息給AMS,請(qǐng)求AMS創(chuàng)建Activity,AMS在SystemServer系統(tǒng)進(jìn)程中,其與應(yīng)用進(jìn)程是隔離的,AMS管理所有APP的啟動(dòng),所以我們無(wú)法在系統(tǒng)進(jìn)程下做hook操作,應(yīng)該在應(yīng)用進(jìn)程中。為了繞過(guò)AMS的驗(yàn)證,我們需要添加一個(gè)在Manifest中注冊(cè)過(guò)的Activity,這個(gè)Activity稱(chēng)為占坑,這樣可以達(dá)到欺上瞞下的效果,當(dāng)AMS驗(yàn)證通過(guò)后再用插件Activity替換占坑去實(shí)現(xiàn)相應(yīng)的功能。 核心功能兩點(diǎn):

  • 替換插件Activity為占坑Activity
  • 繞過(guò)AMS驗(yàn)證后需要還原插件Activity

啟動(dòng)Activity的時(shí)候會(huì)調(diào)用Activity的startActivity()如下:

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

接著又調(diào)用了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);}} 復(fù)制代碼

查看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);}}} 復(fù)制代碼

上述方法中調(diào)用mInstrumentation的execStartActivity方法來(lái)啟動(dòng)Activity,這個(gè)mInstrumentation是Activity的成員變量,我們就選擇Instrumentation為Hook點(diǎn),用代理的Instrumentation去替換原始的Instrumentation來(lái)完成Hook,如下是代理類(lèi):

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);//判斷啟動(dòng)的插件Activity是否在AndroidManifest.xml中注冊(cè)過(guò)if (null == resolveInfo || resolveInfo.size() == 0) {//保存目標(biāo)插件intent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME, intent.getComponent().getClassName());//設(shè)置為占坑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);}} 復(fù)制代碼

InstrumentationProxy類(lèi)繼承類(lèi)Instrumentation,實(shí)現(xiàn)了類(lèi)execStartActivity方法,接著通過(guò)反射去用原始Instrumentation的execStartActivity方法,這就是替換為占坑Activity的過(guò)程。Activity的創(chuàng)建是在ActivityThread中,里面有個(gè)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);... } 復(fù)制代碼

這里的newActivity就是創(chuàng)建Activity的過(guò)程,我們同樣的在代理類(lèi)中去實(shí)現(xiàn)這個(gè)方法,這就是還原插件Activity 的過(guò)程。

接下來(lái)我們看個(gè)例子: 占位坑Activity:

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

這個(gè)Activity一定是需要在AndroidManifest中去注冊(cè)。 再寫(xiě)一個(gè)插件Activity

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

都是很簡(jiǎn)單的Activity,TargetActivity并沒(méi)有注冊(cè),現(xiàn)在我們需要啟動(dòng)這個(gè)Activity。代理類(lèi)上面代碼已經(jīng)貼出來(lái)了。接下來(lái)就是替換代理類(lèi),達(dá)到Hook的目的,我們?cè)贏pplication中做這個(gè)事情:

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對(duì)象sCurrentActivityThreadObject activityThread=activityThreadField.get(null);Field instrumentationField=activityThreadClass.getDeclaredField("mInstrumentation");instrumentationField.setAccessible(true);//從sCurrentActivityThread中獲取成員變量mInstrumentationInstrumentation instrumentation= (Instrumentation) instrumentationField.get(activityThread);//創(chuàng)建代理對(duì)象InstrumentationProxyInstrumentationProxy proxy=new InstrumentationProxy(instrumentation,getPackageManager());//將sCurrentActivityThread中成員變量mInstrumentation替換成代理類(lèi)InstrumentationProxyinstrumentationField.set(activityThread,proxy);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}} } 復(fù)制代碼

這樣就把原始的Instrumentation替換為代理的了,具體的操作我們?cè)贗nstrumentationProxy中去做實(shí)現(xiàn)。接下來(lái)我們就是從主界面跳轉(zhuǎn)插件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);}} 復(fù)制代碼

轉(zhuǎn)載于:https://juejin.im/post/5c67da6251882562547b99ab

總結(jié)

以上是生活随笔為你收集整理的Hook技术之Hook Activity的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。