36

Android Hook入门

 6 years ago
source link: https://www.tuicool.com/articles/QnURVbN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Android Hook入门

Hook 英文意思是钩子,可以把一段执行着的代码钩下来,然后加入我们自己的逻辑,最后在放回去。比如我们可以Hook住一段系统代码,在执行系统代码之前加入我们自己的逻辑。

Hook技术主要用到java反射和java动态代理两个知识点,下面来个简单的例子,我们来Hook一个按钮的点击事件

Button button = findViewById(R.id.btn_click);
       button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Toast.makeText(getApplicationContext(),((Button) v).getText(),Toast.LENGTH_SHORT).show();
           }
       });

点击按钮的时候,我们如何能在不改变上面代码的前提下,在Toast弹出之前执行一些别的逻辑呢?比如现在Toast弹出的是按钮上的字,我们动态的把子给它改了

思路就是我们通过反射拿到系统中的OnClickListener的对象,通过动态代理,创建一个该对象的代理对象,这个代理对象中就可以写一些别的逻辑啦,最后把这个代理对象通过反射设置回系统中来个偷梁换柱就可以啦。

OK下面开始按照步骤来

第一步

反射拿到设置的OnClickListener对象,上面的代码中,我们进入setOnClickListener方法可以看到

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

这里把我们传进来的回调对象设置给了getListenerInfo().mOnClickListener。

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

getListenerInfo()方法返回一个ListenerInfo对象,它内部就保存了我们传过来的mOnClickListener回调对象。我们功过反射执行getListenerInfo方法就能拿到ListenerInfo对象了如下

Class<?> viewClass = Class.forName("android.view.View");
//需要拿到setOnClickListener方法set过去的对象
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true);
//本质是ListenerInfo对象
Object listenerInfo = getListenerInfoMethod.invoke(view);

拿到了ListenerInfo对象,在通过它拿到它的成员变量mOnClickListener

Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
final Object onClickListener = onClickListenerField.get(listenerInfo);

第二步

使用动态代理定义一个代理的onClickListener,在回调函数中添加一些别的逻辑

Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
                 //需要将监听的方法
                 new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                     @Override
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                         Button button = new Button(MainActivity.this);
                         button.setText("我就点你");
                         return method.invoke(onClickListener, button);
                     }
                 });

在InvocationHandler方法中就可以添加别的逻辑了,这里添加了Toast用来测试,最后返回一个代理的OnClickListener对象。

第三步

把代理的OnClickListener对象设置回系统中,在第一步中已经拿到了mOnClickListener对象,最后把它给改了就好了。

onClickListenerField.set(listenerInfo,proxyClickListener);

OK Hook完成,效果

fA3En2J.gif

完整代码:

/**
     * hook
     * @param view 需要hook的view
     */
    private void hook(View view) {
        try {
            Class<?> viewClass = Class.forName("android.view.View");
            //需要拿到setOnClickListener方法set过去的对象
            Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
            getListenerInfoMethod.setAccessible(true);
            Object listenerInfo = getListenerInfoMethod.invoke(view);

            Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
            Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
            final Object onClickListener = onClickListenerField.get(listenerInfo);

            Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
                    //需要将监听的方法
                    new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Button button = new Button(MainActivity.this);
                            button.setText("我就点你");
                            return method.invoke(onClickListener, button);
                        }
                    });
            onClickListenerField.set(listenerInfo,proxyClickListener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK