欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中的HOOK技術(shù)是什么

 更新時(shí)間:2023年02月17日 09:17:09   作者:守住Android最后的光  
最近一段時(shí)間在研究Android加殼和脫殼技術(shù),其中涉及到了一些hook技術(shù),于是將自己學(xué)習(xí)的一些hook技術(shù)進(jìn)行了一下梳理,以便后面回顧和大家學(xué)習(xí)

1. 什么是 Hook

Hook 英文翻譯過(guò)來(lái)就是「鉤子」的意思,那我們?cè)谑裁磿r(shí)候使用這個(gè)「鉤子」呢?在 Android 操作系統(tǒng)中系統(tǒng)維護(hù)著自己的一套事件分發(fā)機(jī)制。應(yīng)用程序,包括應(yīng)用觸發(fā)事件和后臺(tái)邏輯處理,也是根據(jù)事件流程一步步地向下執(zhí)行。而「鉤子」的意思,就是在事件傳送到終點(diǎn)前截獲并監(jiān)控事件的傳輸,像個(gè)鉤子鉤上事件一樣,并且能夠在鉤上事件時(shí),處理一些自己特定的事件。

Hook 的這個(gè)本領(lǐng),使它能夠?qū)⒆陨淼拇a「融入」被勾?。℉ook)的程序的進(jìn)程中,成為目標(biāo)進(jìn)程的一個(gè)部分。API Hook 技術(shù)是一種用于改變 API 執(zhí)行結(jié)果的技術(shù),能夠?qū)⑾到y(tǒng)的 API 函數(shù)執(zhí)行重定向。在 Android 系統(tǒng)中使用了沙箱機(jī)制,普通用戶程序的進(jìn)程空間都是獨(dú)立的,程序的運(yùn)行互不干擾。這就使我們希望通過(guò)一個(gè)程序改變其他程序的某些行為的想法不能直接實(shí)現(xiàn),但是 Hook 的出現(xiàn)給我們開(kāi)拓了解決此類問(wèn)題的道路。當(dāng)然,根據(jù) Hook 對(duì)象與 Hook 后處理的事件方式不同,Hook 還分為不同的種類,比如消息 Hook、API Hook 等。

2. Hook的應(yīng)用場(chǎng)景

Hook的應(yīng)用非常廣泛,不僅開(kāi)發(fā)人員會(huì)用到,攻擊者也會(huì)用到。

開(kāi)發(fā)有:對(duì)程序的執(zhí)行記錄日志、防止應(yīng)用重復(fù)啟動(dòng)等。

攻擊有:使用hook攔截用戶輸入信息,獲取鍵盤(pán)數(shù)據(jù)等。

3. Hook的技術(shù)方式或框架

  • inline hook方式:目標(biāo)函數(shù)執(zhí)行指令中插入Jump跳轉(zhuǎn)指令實(shí)現(xiàn)重定向
  • 動(dòng)態(tài)代理方式:思路應(yīng)該是類似于設(shè)計(jì)模式中的代理模式,代理原本的函數(shù)的執(zhí)行
  • Method Swizzle方式:動(dòng)態(tài)改變SEL(方法編號(hào))與IMP(方法實(shí)現(xiàn))的對(duì)應(yīng)關(guān)系
  • Cydia Substrate方式:適用于iOS和andriod,定義了一系列的函數(shù)和宏,底層調(diào)用了objc的runtime和fishHook來(lái)替代目標(biāo)函數(shù)或者系統(tǒng)方法
  • fishHook方式:是Facebook提供一種動(dòng)態(tài)修改鏈接Mach-O文件的工具。此利用Mach-O文件加載原理,通過(guò)修改非懶加載和懶加載兩個(gè)表的指針達(dá)到C函數(shù)的Hook的目的
  • Xposed框架:目標(biāo)函數(shù)為native,利用JNI hook重定向表中的函數(shù)指針
  • 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)存中即可

4. Hook的一般步驟和技巧

  • 尋找 Hook 點(diǎn)。原則是盡可能是靜態(tài)變量或者單例對(duì)象,因?yàn)樗鼈內(nèi)菀锥ㄎ?,其次是盡量 Hook public 的對(duì)象和方法。
  • 選擇適當(dāng)?shù)膆ook方式或框架。
  • 將hook代碼注入到目標(biāo)程序的運(yùn)行內(nèi)存中。

實(shí)戰(zhàn)

我們自己的代碼里面,給一個(gè)view設(shè)置了點(diǎn)擊事件,現(xiàn)在要求在不改動(dòng)這個(gè)點(diǎn)擊事件的情況下,添加額外的點(diǎn)擊事件邏輯.

View v = findViewById(R.id.tv);
        v.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "別點(diǎn)啦,再點(diǎn)我咬你了...", Toast.LENGTH_SHORT).show();
            }
        });

這是view的點(diǎn)擊事件,toast了一段話,現(xiàn)在要求,不允許改動(dòng)這個(gè)OnClickListener,要在toast之前添加日志打印 Log.d(…).

按照上面的思路來(lái):

第一步:根據(jù)需求 確定要hook的對(duì)象;

我們的目的是在OnClickListener中,插入自己的邏輯.所以要hook的是v.setOnClickListener()方法的實(shí)參。

第二步:尋找要hook的對(duì)象的持有者,拿到要hook的對(duì)象

進(jìn)入v.setOnClickListener源碼:發(fā)現(xiàn)我們創(chuàng)建的OnClickListener對(duì)象被賦值給了getListenerInfo().mOnClickListener

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

繼續(xù)索引:getListenerInfo() 是個(gè)什么玩意?繼續(xù)追查:

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

結(jié)果發(fā)現(xiàn)這個(gè)其實(shí)是一個(gè)偽單例,一個(gè)View對(duì)象中只存在一個(gè)ListenerInfo對(duì)象. 進(jìn)入ListenerInfo內(nèi)部:發(fā)現(xiàn)OnClickListener對(duì)象 被ListenerInfo所持有.

static class ListenerInfo {
 ...
 public OnClickListener mOnClickListener;
 ...
}

到這里為止,完成第二步,找到了點(diǎn)擊事件的實(shí)際持有者:ListenerInfo .

第三步:定義“要hook的對(duì)象”的代理類,并且創(chuàng)建該類的對(duì)象

我們要hook的是View.OnClickListener對(duì)象,所以,創(chuàng)建一個(gè)類 實(shí)現(xiàn)View.OnClickListener接口.

static class ProxyOnClickListener implements View.OnClickListener {
       View.OnClickListener oriLis;
       public ProxyOnClickListener(View.OnClickListener oriLis) {
           this.oriLis = oriLis;
      }
       @Override
      public void onClick(View v) {
          Log.d("HookSetOnClickListener", "點(diǎn)擊事件被hook到了");
         if (oriLis != null) {
             oriLis.onClick(v);
         }
     }
   }

然后,創(chuàng)建出一個(gè)代理對(duì)象

ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);

可以看到,這里傳入了一個(gè)View.OnClickListener對(duì)象,它存在的目的,是讓我們可以有選擇地使用到原先的點(diǎn)擊事件邏輯。一般hook,都會(huì)保留原有的源碼邏輯.

另外提一句:當(dāng)我們要?jiǎng)?chuàng)建的代理類,是被接口所約束的時(shí)候,比如現(xiàn)在,我們創(chuàng)建的ProxyOnClickListener implements View.OnClickListener,只實(shí)現(xiàn)了一個(gè)接口,則可以使用JDK提供的Proxy類來(lái)創(chuàng)建代理對(duì)象

Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), 
            new Class[]>>{View.OnClickListener.class}, new InvocationHandler() {
               @Override
               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  Log.d("HookSetOnClickListener", "點(diǎn)擊事件被hook到了");//加入自己的邏輯
                  return method.invoke(onClickListenerInstance, args);//執(zhí)行被代理的對(duì)象的邏輯
             }
         });

到這里為止,第三步:定義“要hook的對(duì)象”的代理類,并且創(chuàng)建該類的對(duì)象 完成。

第四步:使用上一步創(chuàng)建出來(lái)的對(duì)象,替換掉要hook的對(duì)象,達(dá)成 偷梁換柱的最終目的. 利用反射,將我們創(chuàng)建的代理點(diǎn)擊事件對(duì)象,傳給這個(gè)view field.set(mListenerInfo, proxyOnClickListener);

這里,貼出最終代碼:

輔助類

/**
* hook的輔助類
* hook的動(dòng)作放在這里
*/
public class HookSetOnClickListenerHelper {
   /**
    * hook的核心代碼
    * 這個(gè)方法的唯一目的:用自己的點(diǎn)擊事件,替換掉 View原來(lái)的點(diǎn)擊事件
      *
    * @param v hook的范圍僅限于這個(gè)view
      */
   public static void hook(Context context, final View v) {//
       try {
           // 反射執(zhí)行View類的getListenerInfo()方法,拿到v的mListenerInfo對(duì)象,這個(gè)對(duì)象就是點(diǎn)擊事件的持有者
           Method method = View.class.getDeclaredMethod("getListenerInfo");
           method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加這個(gè)代碼來(lái)保證訪問(wèn)權(quán)限
           Object mListenerInfo = method.invoke(v);//這里拿到的就是mListenerInfo對(duì)象,也就是點(diǎn)擊事件的持有者
           //要從這里面拿到當(dāng)前的點(diǎn)擊事件對(duì)象
           Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 這是內(nèi)部類的表示方法
           Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
           final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真實(shí)的mOnClickListener對(duì)象
           //2. 創(chuàng)建我們自己的點(diǎn)擊事件代理類
           //   方式1:自己創(chuàng)建代理類
           //   ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);
           //   方式2:由于View.OnClickListener是一個(gè)接口,所以可以直接用動(dòng)態(tài)代理模式
           // Proxy.newProxyInstance的3個(gè)參數(shù)依次分別是:
           // 本地的類加載器;
           // 代理類的對(duì)象所繼承的接口(用Class數(shù)組表示,支持多個(gè)接口)
           // 代理類的實(shí)際邏輯,封裝在new出來(lái)的InvocationHandler內(nèi)
           Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
               @Override
               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                   Log.d("HookSetOnClickListener", "點(diǎn)擊事件被hook到了");//加入自己的邏輯
                   return method.invoke(onClickListenerInstance, args);//執(zhí)行被代理的對(duì)象的邏輯
               }
           });
           //3. 用我們自己的點(diǎn)擊事件代理類,設(shè)置到"持有者"中
           field.set(mListenerInfo, proxyOnClickListener);
           //完成
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
   // 還真是這樣,自定義代理類
   static class ProxyOnClickListener implements View.OnClickListener {
       View.OnClickListener oriLis;
       public ProxyOnClickListener(View.OnClickListener oriLis) {
           this.oriLis = oriLis;
       }
       @Override
       public void onClick(View v) {
           Log.d("HookSetOnClickListener", "點(diǎn)擊事件被hook到了");
           if (oriLis != null) {
               oriLis.onClick(v);
           }
       }
   }
}

具體調(diào)用

v.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "別點(diǎn)啦,再點(diǎn)我咬你了...", Toast.LENGTH_SHORT).show();
            }
        });
 HookSetOnClickListenerHelper.hook(this, v);//這個(gè)hook的作用,是 用我們自己創(chuàng)建的點(diǎn)擊事件代理對(duì)象,替換掉之前的點(diǎn)擊事件。

ok,目的達(dá)成v.setOnClickListener已經(jīng)被hook.

文末

關(guān)于 Android 中的 Hook 機(jī)制,大致有兩個(gè)方式:

  • 要 root 權(quán)限,直接 Hook 系統(tǒng),可以干掉所有的 App。
  • 免 root 權(quán)限,但是只能 Hook 自身,對(duì)系統(tǒng)其它 App 無(wú)能為力。

到此這篇關(guān)于Android中的HOOK技術(shù)是什么的文章就介紹到這了,更多相關(guān)Android HOOK內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論