AndroidHook機(jī)制之實(shí)戰(zhàn)模擬

簡(jiǎn)介

什么是 Hook

Hook 又叫“鉤子”,它可以在事件傳送的過(guò)程中截獲并監(jiān)控事件的傳輸,將自身的代碼與系統(tǒng)方法進(jìn)行融入。這樣當(dāng)這些方法被調(diào)用時(shí),也就可以執(zhí)行我們自己的代碼,這也是面向切面編程的思想(AOP)。

創(chuàng)新互聯(lián)公司專(zhuān)注于企業(yè)成都營(yíng)銷(xiāo)網(wǎng)站建設(shè)、網(wǎng)站重做改版、蔡家坡網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、html5、成都做商城網(wǎng)站、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性?xún)r(jià)比高,為蔡家坡等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。

Hook 分類(lèi)

1.根據(jù)Android開(kāi)發(fā)模式,Native模式(C/C++)和Java模式(Java)區(qū)分,在Android平臺(tái)上

  • Java層級(jí)的Hook;

  • Native層級(jí)的Hook;

2.根 Hook 對(duì)象與 Hook 后處理事件方式不同,Hook還分為:

  • 消息Hook;

  • API Hook;

3.針對(duì)Hook的不同進(jìn)程上來(lái)說(shuō),還可以分為:

  • 全局Hook;

  • 單個(gè)進(jìn)程Hook;

常見(jiàn) Hook 框架

在Android開(kāi)發(fā)中,有以下常見(jiàn)的一些Hook框架:

  • Xposed

    通過(guò)替換 /system/bin/app_process 程序控制 Zygote 進(jìn)程,使得 app_process 在啟動(dòng)過(guò)程中會(huì)加載 XposedBridge.jar 這個(gè) Jar 包,從而完成對(duì) Zygote 進(jìn)程及其創(chuàng)建的 Dalvik 虛擬機(jī)的劫持。
    Xposed 在開(kāi)機(jī)的時(shí)候完成對(duì)所有的 Hook Function 的劫持,在原 Function 執(zhí)行的前后加上自定義代碼。

  • Cydia Substrate

    Cydia Substrate 框架為蘋(píng)果用戶(hù)提供了越獄相關(guān)的服務(wù)框架,當(dāng)然也推出了 Android 版 。Cydia Substrate 是一個(gè)代碼修改平臺(tái),它可以修改任何進(jìn)程的代碼。不管是用 Java 還是 C/C++(native代碼)編寫(xiě)的,而 Xposed 只支持 Hook app_process 中的 Java 函數(shù)。

  • Legend

    Legend 是 Android 免 Root 環(huán)境下的一個(gè) Apk Hook 框架,該框架代碼設(shè)計(jì)簡(jiǎn)潔,通用性高,適合逆向工程時(shí)一些 Hook 場(chǎng)景。大部分的功能都放到了 Java 層,這樣的兼容性就非常好。
    原理是這樣的,直接構(gòu)造出新舊方法對(duì)應(yīng)的虛擬機(jī)數(shù)據(jù)結(jié)構(gòu),然后替換信息寫(xiě)到內(nèi)存中即可。

Hook 必須掌握的知識(shí)

  • 反射

如果你對(duì)反射還不是很熟悉的話(huà),建議你先復(fù)習(xí)一下 java 反射的相關(guān)知識(shí)。

動(dòng)態(tài)代理是指在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi),不需要我們像靜態(tài)代理那個(gè)去手動(dòng)寫(xiě)一個(gè)個(gè)的代理類(lèi)。在 java 中,我們可以使用 InvocationHandler 實(shí)現(xiàn)動(dòng)態(tài)代理。

本文的主要內(nèi)容是講解單個(gè)進(jìn)程的 Hook,以及怎樣 Hook。


Hook 使用實(shí)例

Hook 選擇的關(guān)鍵點(diǎn)

  • Hook 的選擇點(diǎn):盡量靜態(tài)變量和單例,因?yàn)橐坏﹦?chuàng)建對(duì)象,它們不容易變化,非常容易定位。

  • Hook 過(guò)程:

    • 尋找 Hook 點(diǎn),原則是盡量靜態(tài)變量或者單例對(duì)象,盡量 Hook public 的對(duì)象和方法。

    • 選擇合適的代理方式,如果是接口可以用動(dòng)態(tài)代理。

    • 偷梁換柱——用代理對(duì)象替換原始對(duì)象。

  • Android 的 API 版本比較多,方法和類(lèi)可能不一樣,所以要做好 API 的兼容工作。

簡(jiǎn)單案例一: 使用 Hook 修改 View.OnClickListener 事件

首先,我們先分析 View.setOnClickListener 源碼,找出合適的 Hook 點(diǎn)??梢钥吹?OnClickListener 對(duì)象被保存在了一個(gè)叫做 ListenerInfo 的內(nèi)部類(lèi)里,其中 mListenerInfo 是 View 的成員變量。ListeneInfo 里面保存了 View 的各種監(jiān)聽(tīng)事件。因此,我們可以想辦法 hook ListenerInfo 的 mOnClickListener 。

public?void?setOnClickListener(@Nullable?OnClickListener?l)?{????if?(!isClickable())?{
????????setClickable(true);
????}
????getListenerInfo().mOnClickListener?=?l;
}static?class?ListenerInfo?{

?????---????ListenerInfo?getListenerInfo()?{????????if?(mListenerInfo?!=?null)?{????????????return?mListenerInfo;
????????}
????????mListenerInfo?=?new?ListenerInfo();????????return?mListenerInfo;
????}
????
????---
}

接下來(lái),讓我們一起來(lái)看一下怎樣 Hook View.OnClickListener 事件?

大概分為三步:

  • 第一步:獲取 ListenerInfo 對(duì)象

從 View 的源代碼,我們可以知道我們可以通過(guò) getListenerInfo 方法獲取,于是,我們利用反射得到 ListenerInfo 對(duì)象

  • 第二步:獲取原始的 OnClickListener事件方法

從上面的分析,我們知道 OnClickListener 事件被保存在 ListenerInfo 里面,同理我們利用反射獲取

  • 第三步:偷梁換柱,用 Hook代理類(lèi) 替換原始的 OnClickListener

public?static?void?hookOnClickListener(View?view)?throws?Exception?{????//?第一步:反射得到?ListenerInfo?對(duì)象
????Method?getListenerInfo?=?View.class.getDeclaredMethod("getListenerInfo");
????getListenerInfo.setAccessible(true);
????Object?listenerInfo?=?getListenerInfo.invoke(view);????//?第二步:得到原始的?OnClickListener事件方法
????Class<?>?listenerInfoClz?=?Class.forName("android.view.View$ListenerInfo");
????Field?mOnClickListener?=?listenerInfoClz.getDeclaredField("mOnClickListener");
????mOnClickListener.setAccessible(true);
????View.OnClickListener?originOnClickListener?=?(View.OnClickListener)?mOnClickListener.get(listenerInfo);????//?第三步:用?Hook代理類(lèi)?替換原始的?OnClickListener
????View.OnClickListener?hookedOnClickListener?=?new?HookedClickListenerProxy(originOnClickListener);
????mOnClickListener.set(listenerInfo,?hookedOnClickListener);
}
public?class?HookedClickListenerProxy?implements?View.OnClickListener?{????private?View.OnClickListener?origin;????public?HookedClickListenerProxy(View.OnClickListener?origin)?{????????this.origin?=?origin;
????}????@Override
????public?void?onClick(View?v)?{
????????Toast.makeText(v.getContext(),?"Hook?Click?Listener",?Toast.LENGTH_SHORT).show();????????if?(origin?!=?null)?{
????????????origin.onClick(v);
????????}
????}
????
}

執(zhí)行以下代碼,將會(huì)看到當(dāng)我們點(diǎn)擊該按鈕的時(shí)候,會(huì)彈出 toast “Hook Click Listener”

mBtn1?=?(Button)?findViewById(R.id.btn_1);
mBtn1.setOnClickListener(this);try?{
????HookHelper.hookOnClickListener(mBtn1);
}?catch?(Exception?e)?{
????e.printStackTrace();
}

簡(jiǎn)單案例二: HooK Notification

發(fā)送消息到通知欄的核心代碼如下:

NotificationManager?notificationManager?=?(NotificationManager)?context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(id,?builder.build());

跟蹤 notify 方法發(fā)現(xiàn)最終會(huì)調(diào)用到 notifyAsUser 方法

public?void?notify(String?tag,?int?id,?Notification?notification){
????notifyAsUser(tag,?id,?notification,?new?UserHandle(UserHandle.myUserId()));
}

而在 notifyAsUser 方法中,我們驚喜地發(fā)現(xiàn) service 是一個(gè)單例,因此,我們可以想方法 hook 住這個(gè) service,而 notifyAsUser 最終會(huì)調(diào)用到 service 的 enqueueNotificationWithTag 方法。因此 hook 住 service 的 enqueueNotificationWithTag 方法即可

public?void?notifyAsUser(String?tag,?int?id,?Notification?notification,?UserHandle?user){????//?
????INotificationManager?service?=?getService();
????String?pkg?=?mContext.getPackageName();????//?Fix?the?notification?as?best?we?can.
????Notification.addFieldsFromContext(mContext,?notification);????if?(notification.sound?!=?null)?{
????????notification.sound?=?notification.sound.getCanonicalUri();????????if?(StrictMode.vmFileUriExposureEnabled())?{
????????????notification.sound.checkFileUriExposed("Notification.sound");
????????}
????}
????fixLegacySmallIcon(notification,?pkg);????if?(mContext.getApplicationInfo().targetSdkVersion?>?Build.VERSION_CODES.LOLLIPOP_MR1)?{????????if?(notification.getSmallIcon()?==?null)?{????????????throw?new?IllegalArgumentException("Invalid?notification?(no?valid?small?icon):?"
????????????????????+?notification);
????????}
????}????if?(localLOGV)?Log.v(TAG,?pkg?+?":?notify("?+?id?+?",?"?+?notification?+?")");????final?Notification?copy?=?Builder.maybeCloneStrippedForDelivery(notification);????try?{
????????service.enqueueNotificationWithTag(pkg,?mContext.getOpPackageName(),?tag,?id,
????????????????copy,?user.getIdentifier());
????}?catch?(RemoteException?e)?{????????throw?e.rethrowFromSystemServer();
????}
}private?static?INotificationManager?sService;static?public?INotificationManager?getService(){????if?(sService?!=?null)?{????????return?sService;
????}
????IBinder?b?=?ServiceManager.getService("notification");
????sService?=?INotificationManager.Stub.asInterface(b);????return?sService;
}

綜上,要 Hook ?Notification,大概需要三步:

  • 第一步:得到 NotificationManager 的 sService

  • 第二步:因?yàn)?sService 是接口,所以我們可以使用動(dòng)態(tài)代理,獲取動(dòng)態(tài)代理對(duì)象

  • 第三步:偷梁換柱,使用動(dòng)態(tài)代理對(duì)象 proxyNotiMng 替換系統(tǒng)的 sService

于是,我們可以寫(xiě)出如下的代碼

public?static?void?hookNotificationManager(final?Context?context)?throws?Exception?{
????NotificationManager?notificationManager?=?(NotificationManager)?context.getSystemService(Context.NOTIFICATION_SERVICE);

????Method?getService?=?NotificationManager.class.getDeclaredMethod("getService");
????getService.setAccessible(true);????//?第一步:得到系統(tǒng)的?sService
????final?Object?sOriginService?=?getService.invoke(notificationManager);

????Class?iNotiMngClz?=?Class.forName("android.app.INotificationManager");????//?第二步:得到我們的動(dòng)態(tài)代理對(duì)象
????Object?proxyNotiMng?=?Proxy.newProxyInstance(context.getClass().getClassLoader(),?new
????????????Class[]{iNotiMngClz},?new?InvocationHandler()?{????????@Override
????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????????Log.d(TAG,?"invoke().?method:"?+?method);
????????????String?name?=?method.getName();
????????????Log.d(TAG,?"invoke:?name="?+?name);????????????if?(args?!=?null?&&?args.length?>?0)?{????????????????for?(Object?arg?:?args)?{
????????????????????Log.d(TAG,?"invoke:?arg="?+?arg);
????????????????}
????????????}
????????????Toast.makeText(context.getApplicationContext(),?"檢測(cè)到有人發(fā)通知了",?Toast.LENGTH_SHORT).show();????????????//?操作交由?sOriginService?處理,不攔截通知
????????????return?method.invoke(sOriginService,?args);????????????//?攔截通知,什么也不做
????????????//????????????????????return?null;
????????????//?或者是根據(jù)通知的?Tag?和?ID?進(jìn)行篩選
????????}
????});????//?第三步:偷梁換柱,使用?proxyNotiMng?替換系統(tǒng)的?sService
????Field?sServiceField?=?NotificationManager.class.getDeclaredField("sService");
????sServiceField.setAccessible(true);
????sServiceField.set(notificationManager,?proxyNotiMng);

}

Hook 使用進(jìn)階

Hook ClipboardManager

第一種方法

從上面的 hook NotificationManager 例子中,我們可以得知 NotificationManager 中有一個(gè)靜態(tài)變量 sService,這個(gè)變量是遠(yuǎn)端的 service。因此,我們嘗試查找 ClipboardManager 中是不是也存在相同的類(lèi)似靜態(tài)變量。

查看它的源碼發(fā)現(xiàn)它存在 mService 變量,該變量是在 ClipboardManager 構(gòu)造函數(shù)中初始化的,而 ClipboardManager 的構(gòu)造方法用 @hide 標(biāo)記,表明該方法對(duì)調(diào)用者不可見(jiàn)。

而我們知道 ClipboardManager,NotificationManager 其實(shí)這些都是單例的,即系統(tǒng)只會(huì)創(chuàng)建一次。因此我們也可以認(rèn)為
ClipboardManager 的 mService 是單例的。因此 mService 應(yīng)該是可以考慮 hook 的一個(gè)點(diǎn)。

public?class?ClipboardManager?extends?android.text.ClipboardManager?{????private?final?Context?mContext;????private?final?IClipboard?mService;????/**?{@hide}?*/
????public?ClipboardManager(Context?context,?Handler?handler)?throws?ServiceNotFoundException?{
????????mContext?=?context;
????????mService?=?IClipboard.Stub.asInterface(
????????????????ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
????}
}

接下來(lái),我們?cè)賮?lái)一個(gè)看一下 ClipboardManager 的相關(guān)方法 setPrimaryClip , getPrimaryClip

public?void?setPrimaryClip(ClipData?clip)?{????try?{????????if?(clip?!=?null)?{
????????????clip.prepareToLeaveProcess(true);
????????}
????????mService.setPrimaryClip(clip,?mContext.getOpPackageName());
????}?catch?(RemoteException?e)?{????????throw?e.rethrowFromSystemServer();
????}
}/**
?*?Returns?the?current?primary?clip?on?the?clipboard.
?*/public?ClipData?getPrimaryClip()?{????try?{????????return?mService.getPrimaryClip(mContext.getOpPackageName());
????}?catch?(RemoteException?e)?{????????throw?e.rethrowFromSystemServer();
????}
}

可以發(fā)現(xiàn)這些方法最終都會(huì)調(diào)用到 mService 的相關(guān)方法。因此,ClipboardManager 的 mService 確實(shí)是一個(gè)可以 hook 的一個(gè)點(diǎn)。

hook ClipboardManager.mService ?的實(shí)現(xiàn)

大概需要三個(gè)步驟

  • 第一步:得到 ClipboardManager 的 mService

  • 第二步:初始化動(dòng)態(tài)代理對(duì)象

  • 第三步:偷梁換柱,使用 proxyNotiMng 替換系統(tǒng)的 mService

public?static?void?hookClipboardService(final?Context?context)?throws?Exception?{
????ClipboardManager?clipboardManager?=?(ClipboardManager)?context.getSystemService(Context.CLIPBOARD_SERVICE);
????Field?mServiceFiled?=?ClipboardManager.class.getDeclaredField("mService");
????mServiceFiled.setAccessible(true);????//?第一步:得到系統(tǒng)的?mService
????final?Object?mService?=?mServiceFiled.get(clipboardManager);????
????//?第二步:初始化動(dòng)態(tài)代理對(duì)象
????Class?aClass?=?Class.forName("android.content.IClipboard");
????Object?proxyInstance?=?Proxy.newProxyInstance(context.getClass().getClassLoader(),?new
????????????Class[]{aClass},?new?InvocationHandler()?{????????@Override
????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????????Log.d(TAG,?"invoke().?method:"?+?method);
????????????String?name?=?method.getName();????????????if?(args?!=?null?&&?args.length?>?0)?{????????????????for?(Object?arg?:?args)?{
????????????????????Log.d(TAG,?"invoke:?arg="?+?arg);
????????????????}
????????????}????????????if?("setPrimaryClip".equals(name))?{
????????????????Object?arg?=?args[0];????????????????if?(arg?instanceof?ClipData)?{
????????????????????ClipData?clipData?=?(ClipData)?arg;????????????????????int?itemCount?=?clipData.getItemCount();????????????????????for?(int?i?=?0;?i?<?itemCount;?i++)?{
????????????????????????ClipData.Item?item?=?clipData.getItemAt(i);
????????????????????????Log.i(TAG,?"invoke:?item="?+?item);
????????????????????}
????????????????}
????????????????Toast.makeText(context,?"檢測(cè)到有人設(shè)置粘貼板內(nèi)容",?Toast.LENGTH_SHORT).show();
????????????}?else?if?("getPrimaryClip".equals(name))?{
????????????????Toast.makeText(context,?"檢測(cè)到有人要獲取粘貼板的內(nèi)容",?Toast.LENGTH_SHORT).show();
????????????}????????????//?操作交由?sOriginService?處理,不攔截通知
????????????return?method.invoke(mService,?args);

????????}
????});????//?第三步:偷梁換柱,使用?proxyNotiMng?替換系統(tǒng)的?mService
????Field?sServiceField?=?ClipboardManager.class.getDeclaredField("mService");
????sServiceField.setAccessible(true);
????sServiceField.set(clipboardManager,?proxyInstance);

}

Android Hook 機(jī)制之實(shí)戰(zhàn)模擬

image

第二種方法

對(duì) Android 源碼有基本了解的人都知道,Android 中的各種 Manager 都是通過(guò) ServiceManager 獲取的。因此,我們可以通過(guò) ServiceManager hook 所有系統(tǒng) Manager,ClipboardManager 當(dāng)然也不例外。

public?final?class?ServiceManager?{????/**
?????*?Returns?a?reference?to?a?service?with?the?given?name.
?????*?
?????*?@param?name?the?name?of?the?service?to?get
?????*?@return?a?reference?to?the?service,?or?<code>null</code>?if?the?service?doesn't?exist
?????*/
????public?static?IBinder?getService(String?name)?{????????try?{
????????????IBinder?service?=?sCache.get(name);????????????if?(service?!=?null)?{????????????????return?service;
????????????}?else?{????????????????return?getIServiceManager().getService(name);
????????????}
????????}?catch?(RemoteException?e)?{
????????????Log.e(TAG,?"error?in?getService",?e);
????????}????????return?null;
????}
}

老套路

  • 第一步:通過(guò)反射獲取剪切板服務(wù)的遠(yuǎn)程Binder對(duì)象,這里我們可以通過(guò) ServiceManager getService 方法獲得

  • 第二步:創(chuàng)建我們的動(dòng)態(tài)代理對(duì)象,動(dòng)態(tài)代理原來(lái)的Binder對(duì)象

  • 第三步:偷梁換柱,把我們的動(dòng)態(tài)代理對(duì)象設(shè)置進(jìn)去

public?static?void?hookClipboardService()?throws?Exception?{????//通過(guò)反射獲取剪切板服務(wù)的遠(yuǎn)程Binder對(duì)象
????Class?serviceManager?=?Class.forName("android.os.ServiceManager");
????Method?getServiceMethod?=?serviceManager.getMethod("getService",?String.class);
????IBinder?remoteBinder?=?(IBinder)?getServiceMethod.invoke(null,?Context.CLIPBOARD_SERVICE);????//新建一個(gè)我們需要的Binder,動(dòng)態(tài)代理原來(lái)的Binder對(duì)象
????IBinder?hookBinder?=?(IBinder)?Proxy.newProxyInstance(serviceManager.getClassLoader(),????????????new?Class[]{IBinder.class},?new?ClipboardHookRemoteBinderHandler(remoteBinder));????//通過(guò)反射獲取ServiceManger存儲(chǔ)Binder對(duì)象的緩存集合,把我們新建的代理Binder放進(jìn)緩存
????Field?sCacheField?=?serviceManager.getDeclaredField("sCache");
????sCacheField.setAccessible(true);????Map<String,?IBinder>?sCache?=?(Map<String,?IBinder>)?sCacheField.get(null);
????sCache.put(Context.CLIPBOARD_SERVICE,?hookBinder);

}
public?class?ClipboardHookRemoteBinderHandler?implements?InvocationHandler?{????private?IBinder?remoteBinder;????private?Class?iInterface;????private?Class?stubClass;????public?ClipboardHookRemoteBinderHandler(IBinder?remoteBinder)?{????????this.remoteBinder?=?remoteBinder;????????try?{????????????this.iInterface?=?Class.forName("android.content.IClipboard");????????????this.stubClass?=?Class.forName("android.content.IClipboard$Stub");
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????}
????}????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????Log.d("RemoteBinderHandler",?method.getName()?+?"()?is?invoked");????????if?("queryLocalInterface".equals(method.getName()))?{????????????//這里不能攔截具體的服務(wù)的方法,因?yàn)檫@是一個(gè)遠(yuǎn)程的Binder,還沒(méi)有轉(zhuǎn)化為本地Binder對(duì)象
????????????//所以先攔截我們所知的queryLocalInterface方法,返回一個(gè)本地Binder對(duì)象的代理
????????????return?Proxy.newProxyInstance(remoteBinder.getClass().getClassLoader(),????????????????????new?Class[]{this.iInterface},????????????????????new?ClipboardHookLocalBinderHandler(remoteBinder,?stubClass));
????????}????????return?method.invoke(remoteBinder,?args);
????}
}

標(biāo)題名稱(chēng):AndroidHook機(jī)制之實(shí)戰(zhàn)模擬
瀏覽地址:http://muchs.cn/article20/gphejo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站虛擬主機(jī)、網(wǎng)站設(shè)計(jì)公司、網(wǎng)站收錄建站公司、搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)