Understand Android Hook technology and simple combat

1. What is Hook

The English translation of Hook means "hook", so when do we use this "hook"? In the Android operating system, the system maintains its own set of event distribution mechanisms. The application, including application trigger events and background logic processing, is also executed step by step according to the event flow. The meaning of "hook" is to intercept and monitor the transmission of the event before the event is transmitted to the end point, just like a hook hooking the event, and can handle some of its own specific events when the event is hooked.

Hook schematic

This ability of Hook enables it to "integrate" its own code into the process of the program that is hooked (Hook) and become a part of the target process. API Hook technology is a technology used to change the execution result of an API, which can redirect the execution of the system's API functions. The sandbox mechanism is used in the Android system, and the process space of ordinary user programs is independent, and the programs run without interfering with each other. This makes the idea that we hope to change some behavior of other programs through one program cannot be directly realized, but the emergence of Hook has opened up a way for us to solve such problems. Of course, Hooks are also divided into different types, such as message Hooks, API Hooks, etc.

2. Common Hook Framework

  1. Regarding the Hook mechanism in Android, there are roughly two ways:
  • To root permissions, direct Hook system, you can kill all the App.
  • No root permission, but only Hook itself, can do nothing to other apps in the system.
  1. Several Hook schemes:

By replacing the /system/bin/app_process program to control the Zygote process, the app_process will load the XposedBridge.jar Jar package during the startup process, thereby completing the hijacking of the Zygote process and the Dalvik virtual machine it creates.
Xposed completes the hijacking of all Hook Functions at boot time, and adds custom code before and after the execution of the original Function.

  • Cydia Substrate

    The Cydia Substrate framework provides a jailbreak-related service framework for Apple users, and of course an Android version is also available. Cydia Substrate is a code modification platform that can modify the code of any process. Whether written in Java or C/C++ (native code), Xposed only supports Java functions in Hook app_process.

  • Legend

    Legend is an Apk Hook framework in the Android free root environment. The framework code design is simple, high versatility, suitable for some Hook scenarios during reverse engineering. Most of the functions are placed in the Java layer, so the compatibility is very good.
    The principle is this, directly construct the virtual machine data structure corresponding to the old and new methods, and then write the replacement information into the memory.

3. Implement API Hook using Java reflection

By injecting the virtual machine into the Android platform and reflecting on Java, we can change the way Android virtual machine calls functions (ClassLoader), so as to achieve the purpose of Java function redirection. Here we call this type of operation Java API Hook.

The use of Hook is described below through the OnClickListener of Hook View.

First enter the setOnClickListener method of View, we see that the OnClickListener object is saved in an inner class called ListenerInfo, where mListenerInfo is a member variable of View. ListenInfo stores various listening events of View, such as OnClickListener, OnLongClickListener, OnKeyListener and so on.

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

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

Our goal is to Hook OnClickListener, so we need to replace the OnClickListener object and inject custom operations after setting the listener event for the View.

    private void hookOnClickListener(View view) {
        try {
            // 得到 View 的 ListenerInfo 对象
            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);
            // 用自定义的 OnClickListener 替换原始的 OnClickListener
            View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
            mOnClickListener.set(listenerInfo, hookedOnClickListener);
        } catch (Exception e) {
            log.warn("hook clickListener failed!", e);
        }
    }

    class HookedOnClickListener implements View.OnClickListener {
        private View.OnClickListener origin;

        HookedOnClickListener(View.OnClickListener origin) {
            this.origin = origin;
        }

        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
            log.info("Before click, do what you want to to.");
            if (origin != null) {
                origin.onClick(v);
            }
            log.info("After click, do what you want to to.");
        }
    }

At this point, we have successfully hooked the OnClickListener, which can perform certain operations before and after the click, which achieves our purpose. The following is the part of the call, after setting the OnClickListener to the Button, the Hook operation is performed. After clicking the button, the print result of the log is: Before click → onClick → After click.

        Button btnSend = (Button) findViewById(R.id.btn_send);
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                log.info("onClick");
            }
        });
        hookOnClickListener(btnSend);

4. Use Hooks to Block In-App Notifications

When many SDKs are connected to the application, the SDK will use the system service NotificationManager to send notifications, which makes it difficult to manage and control notifications. Now we use Hook technology to intercept some notifications and limit the notification sending operation in the application.

The notification method of NotificationManager is used to send notifications. Let's follow the API to take a look. It will use the object of type INotificationManager and call its enqueueNotificationWithTag method to complete the sending of the notification.

    public void notify(String tag, int id, Notification notification)
    {
        INotificationManager service = getService();
        …… // 省略部分代码
        try {
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    stripped, idOut, UserHandle.myUserId());
            if (id != idOut[0]) {
                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
            }
        } catch (RemoteException e) {
        }
    }
    private static INotificationManager sService;

    /** @hide */
    static public INotificationManager getService()
    {
        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService("notification");
        sService = INotificationManager.Stub.asInterface(b);
        return sService;
    }

INotificationManager is a Binder class for cross-process communication, and sService is the proxy of NMS (NotificationManagerService) on the client side. Sending notifications should be delegated to sService, which will be passed to NMS. The specific principles will not be discussed in detail here. Application communication process.

We found that sService is a static member variable and will only be initialized once. Just replace sService with a custom one, it's true. The following uses a lot of Java reflection and dynamic proxy, pay special attention to the writing of the code.

    private void hookNotificationManager(Context context) {
        try {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            // 得到系统的 sService
            Method getService = NotificationManager.class.getDeclaredMethod("getService");
            getService.setAccessible(true);
            final Object sService = getService.invoke(notificationManager);

            Class iNotiMngClz = Class.forName("android.app.INotificationManager");
            // 动态代理 INotificationManager
            Object proxyNotiMng = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {
                
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    log.debug("invoke(). method:{}", method);
                    if (args != null && args.length > 0) {
                        for (Object arg : args) {
                            log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
                        }
                    }
                    // 操作交由 sService 处理,不拦截通知
                    // return method.invoke(sService, args);
                    // 拦截通知,什么也不做
                    return null;
                    // 或者是根据通知的 Tag 和 ID 进行筛选
                }
            });
            // 替换 sService
            Field sServiceField = NotificationManager.class.getDeclaredField("sService");
            sServiceField.setAccessible(true);
            sServiceField.set(notificationManager, proxyNotiMng);
        } catch (Exception e) {
            log.warn("Hook NotificationManager failed!", e);
        }
    }

The timing of Hook is as early as possible, we operate in attachBaseContext.

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        hookNotificationManager(newBase);
    }

In this way, we have completed the interception of notifications. It can be seen that the Hook technology is really very powerful, and many plug-in principles are based on Hook.

in conclusion:

  1. Choice points for hooks: static variables and singletons, because once an object is created, they are not easy to change and are very easy to locate.
  2. Hook process:
  • Looking for hook points, the principle is static variables or singleton objects, and try to hook public objects and methods.
  • Select the appropriate proxy method, if it is an interface, you can use dynamic proxy.
  • Replacing Columns - Replaces the original object with a proxy object.
  1. There are many versions of Android API, and the methods and classes may be different, so it is necessary to do a good job of API compatibility.

Reference article:



Author: Luoying Cuilu
Link: https://www.jianshu.com/p/4f6d20076922
Source: Jianshu
The copyright belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325340875&siteId=291194637