1. 程式人生 > >android 黑科技 hook技術簡單示例

android 黑科技 hook技術簡單示例

     Hook屬於android 中的黑科技,一般在逆向研究中使用較多,這幾天技術分享會需要分享這個點。趁此機會來try一try。

     先說一下Hook 意思,讓大家有個初步的認識,要不一臉懵逼,Hook 簡單類似網路傳輸中的中間人攔截,我攔截app中的原始方法,自己定義一個方法,替換原始的東西,實現我不可描述的目的,大白話就是這樣,但是實際過程和應用還是比較複雜的。

    常見的使用場景,舉幾個栗子:

      App登入劫持,一般使用者手動點選“登入”按鈕才會將使用者名稱和密碼資訊傳送至伺服器端去驗證賬號與密碼是否正確。這樣就很簡單了,居心叵測的人只需要找到開發者在使用EditText控制元件的getText方法後進行網路驗證的方法,Hook該方法,就能劫持到使用者的賬戶與密碼了。App 首頁注入廣告,app 啟動的時候載入 HomeActivity 肯定要執行onCreate 方法, 劫持首頁的onCreate 方法,在裡面注入彈窗廣告,獲得廣告收入。比如玩一款遊戲App ,修改裡面額金幣數,可以反編譯App ,找到具體的類和方法,插入自己的方法就可以實現。

     聽起來很牛b,但是要實現,需要掌握很多的逆向技術和其它需要技術,可是非常不易的。那我就不吹牛了,簡單介紹下基礎的hook 使用。

      1.Hook 點選事件onClick。

      2.Hook 通知事件NotificationManager。

1.1 Hook的物件一般是靜態物件或者是單例,一般是Hook系統的方法,需要看系統的原始碼,這裡就不分析了。

1.2 首先初始化一個點選的view,設定點選監聽事件:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        tv.setOnClickListener(this);
        try {
            // 1.點選事件攔截
            hookOnClickListener(tv);
            // 2. 通知攔截
//            hookNotificationManager(this);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

                                                                          圖1

1.2  view  tv 的點選監聽替換  方法hookOnClickListener(tv),這個方法實現如下

 

  public static void hookOnClickListener(View view) throws Exception {
        // 1.反射得到 ListenerInfo 物件
        Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
        getListenerInfo.setAccessible(true);
        Object listenerInfo = getListenerInfo.invoke(view);

        //2.得到原始的 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);

        // 3.用 Hook代理類 替換原始的 OnClickListener
        View.OnClickListener hookedOnClickListener = new HookedClickListenerProxy(originOnClickListener);
        mOnClickListener.set(listenerInfo, hookedOnClickListener);
    }

    點選事件的 代理類HookedClickListenerProxy 實現如下:

class HookedClickListenerProxy implements View.OnClickListener {

    private View.OnClickListener origin;
    public HookedClickListenerProxy(View.OnClickListener originOnClickListener) {
        this.origin = originOnClickListener;
//        this=context;
    }

    @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), " 攔截後 after Hook Click Listener", 
        Toast.LENGTH_SHORT).show();
        if (origin != null) {
            origin.onClick(v);
        }
    }
}
 從中可見攔截中自己重寫了onClick方法, 吐司了一下,並且繼續呼叫了原始的點選方法。 原始的onClick方法如下:
  @Override
    public void onClick(View v) {
        Toast.makeText(v.getContext(), "原始的 Click  Listener", Toast.LENGTH_SHORT).show();
    }

1這個時候重新啟動app, 點選後會發現先彈 攔截後的吐司,再彈原始的吐司,攔截成功。

2.1  Hook 通知事件 ,還得看原始碼,分析分析需要攔截那個類和方法。然後套路和上面 類似,首先在初始化時定義通知 類,hookNotificationManager 實現該方法:

 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);
        // 【1】得到系統的 sService
        final Object originService = getService.invoke(notificationManager);

        Class iNotiMagClz = Class.forName("android.app.INotificationManager");
        // 【2】得到我們的動態代理物件
        Object proxyNotiMag = 
        Proxy.newProxyInstance(context.getClass().getClassLoader(),new
                Class[]{iNotiMagClz}, new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();
                if (args != null && args.length > 0) {
                    for (Object arg : args) {
                    }
                }
                Toast.makeText(context.getApplicationContext(), "檢測到有人發通知了", Toast.LENGTH_SHORT).show();
                // 操作交由originService 處理,不攔截通知
                return method.invoke(originService, args);
                // 攔截通知,什麼也不做
                // return null;
                // 或者是根據通知的 Tag 和 ID 進行篩選
            }
        });
        // 【3】偷天換日,使用 proxyNotiMag 替換系統的 sService
        Field sServiceField = NotificationManager.class.getDeclaredField("sService");
        sServiceField.setAccessible(true);
        sServiceField.set(notificationManager, proxyNotiMag);
    }
通過以上步驟就將hookNotificationManager 類成功替換 ,在動態代理物件的回撥方法invoke中實現我能不可描述的目的。技術沒有問題,問題是使用技術的人。

         Hook 技術就簡單介紹這麼點,敘述不全再所難免,還有常用的 一些Hook庫,後面有時間再介紹。