Combat simulation Android Hook Mechanism

Brief introduction

What is the Hook

Hook called "hook", it can intercept and monitor the transmission of events in the course of events in the transmission of the code itself and system integration methods. So that when the method is called, you can execute our own code, which is oriented programming section of thought (AOP).

Hook Category

1. The development model Android, Native mode (C / C ++) and Java mode (Java) distinction, the Android platform

  • Java level Hook;

  • Native level Hook;

2. Hook root objects with different ways to handle events after the Hook, Hook is also divided into:

  • Message Hook;

  • API Hook;

3. Hook for the different processes of it, can be divided into:

  • Global Hook;

  • A single process Hook;

Hook common framework

In Android development, following some common framework Hook:

  • Xposed

    By replacing the / system / bin / app_process Zygote process control program, so that app_process during the boot process will load XposedBridge.jar this Jar package, thus completing the hijacking of the Dalvik virtual machine Zygote process and created.
    Xposed complete hijacking of all Hook Function at boot time, before and after the execution of the original Function add custom code.

  • Cydia Substrate

    Cydia Substrate framework provides related services framework jailbreak for Apple users, of course, also launched the Android version. Cydia Substrate is a code modification platform, it can modify the code in any process. Either in Java or C / C ++ (native code) written, but Xposed Hook app_process only supports the Java function.

  • Legend

    Legend is a free Android Apk Hook framework of Root environment, the framework code simple design, high versatility, while for reverse engineering some of the Hook scene. Most of the functions are put on Java layer, so compatibility is very good.
    Such a principle is directly constructed virtual machine data structures corresponding to the old and new methods, and then replace the information is written to memory.

Hook must master knowledge

  • reflection

If you are not very familiar with the reflection, I suggest you brush up on knowledge of java reflection.

Dynamic proxy is a proxy class generated dynamically at run time, like we do not need to manually write the static agent that one of the proxy class. In java, we can use dynamic InvocationHandler agent.

The main content of this article is to explain the individual processes Hook, and how Hook.


Hook Use Case

Hook selection of key points

  • Hook selection point: try to static variables and single cases, because once an object is created, they are not easy to change, very easy to locate.

  • Hook process:

    • Looking Hook point, a static variable or principle is to singleton object, try the public Hook objects and methods.

    • Select the appropriate proxy mode, if the agent is an interface with dynamic.

    • Substitution - replacing the original object with the proxy object.

  • Android API version is more, methods and classes may be different, so do the work of compatible API.

A simple case: Use Hook modify View.OnClickListener event

First, let's analyze View.setOnClickListener source, Hook find the right point. You can see OnClickListener objects are stored in an internal class called the ListenerInfo, which mListenerInfo is a member variable of View. ListeneInfo which holds a variety of events View of listening. Therefore, we can think of ways to hook ListenerInfo of 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;
    }
    
    ---
}

Next, let us look at how Hook View.OnClickListener event?

Roughly divided into three steps:

  • The first step: Get ListenerInfo objects

View from the source code, we know that we can obtain by getListenerInfo methods, so we obtained by reflection objects ListenerInfo

  • Step Two: Get original OnClickListener event method

From the above analysis, we know OnClickListener events are saved in ListenerInfo inside, the same way we use reflection to obtain

  • The third step: substitution, replacing the original proxy class OnClickListener with Hook

public static void hookOnClickListener (View view) throws Exception {// Step: reflecting objects obtained ListenerInfo 
    Method, getListenerInfo = View.class.getDeclaredMethod ( "getListenerInfo"); 
    getListenerInfo.setAccessible (to true); 
    Object listenerInfo = getListenerInfo.invoke (View ); // Step two: get the original OnClickListener event method 
    Class listenerInfoClz = Class.forName ( "android.view.View $ ListenerInfo"); <?> 
    Field, mOnClickListener = listenerInfoClz.getDeclaredField ( "mOnClickListener"); 
    mOnClickListener.setAccessible (to true); 
    View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get (listenerInfo); // third step: replacing the original proxy class OnClickListener with Hook
    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);
        }
    }
    
}

Execute the following code, you'll see when we click the button, will pop up the toast "Hook Click Listener"

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

Simple Case II: HooK Notification

Core code sends a message to the notification column:

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

Tracking notify method calls finally found a method to notifyAsUser

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

In notifyAsUser method, we were pleasantly surprised to find that service is a singleton, so we can think of ways to hook live this service, but eventually will be called to enqueueNotificationWithTag notifyAsUser method of service. Therefore enqueueNotificationWithTag method of service to live hook

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;
}

In summary, to Hook Notification, it takes about three steps:

  • The first step: get the sService NotificationManager

  • Step Two: Because sService is the interface, so we can use the dynamic agent, to obtain a dynamic proxy object

  • The third step: substitution using a dynamic proxy object proxyNotiMng alternative system sService

So, we can write the following code

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);    // 第一步:得到系统的 sService
    final Object sOriginService = getService.invoke(notificationManager);

    Class iNotiMngClz = Class.forName("android.app.INotificationManager");    // 第二步:得到我们的动态代理对象
    Object proxyNotiMng = Proxy.newProxyInstance(context.getClass().getClassLoader(), new
            Class[]{iNotiMngClz}, new InvocationHandler() {        @Override
        Invoke Object public (Object Proxy, Method, Method, Object [] args) {throws the Throwable 
            Log.d (the TAG,. "Invoke () Method:" + Method); 
            String name = method.getName (); 
            Log.d (the TAG , "Invoke: name =" name +); IF (args = null && args.length> 0) {for (Arg Object: args) {! 
                    Log.d (the TAG, "Invoke: Arg =" + Arg); 
                } 
            } 
            Toast.makeText (context.getApplicationContext (), "was issued a notice of detected", Toast.LENGTH_SHORT) .show (); // referred sOriginService processing operation, does not intercept notification 
            return method.invoke (sOriginService, args); // intercept notice and do nothing 
            // return null;
            // or notifications according Tag ID and screening 
        } 
    }); // third step: substitution, the alternative system using proxyNotiMng Sservice 
    Field, sServiceField = NotificationManager.class.getDeclaredField ( "Sservice"); 
    sServiceField.setAccessible (to true) ; 
    sServiceField.set (the NotificationManager, proxyNotiMng); 

}

Hook the use of advanced

Hook ClipboardManager

the first method

NotificationManager hook from the above example, we can see that there is a static variable NotificationManager sService, this variable is a remote service. Therefore, we try to find ClipboardManager is not there the same similar static variables.

View its source found to exist mService variable, the variable is initialized in the constructor ClipboardManager, and ClipboardManager construction method with @hide mark, indicating that the method is not visible to the caller.

And we know ClipboardManager, NotificationManager fact, these are single cases, ie, the system creates only once. Therefore, we can also think
ClipboardManager of mService are singletons. So mService should be able to consider a point of the hook.

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));
    }
}

Next, let us look at a related method ClipboardManager of 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();
    }
}

You can find these methods will eventually call the relevant methods of mService. Therefore, ClipboardManager of mService indeed be a hook of a point.

hook ClipboardManager.mService implementation

It takes about three steps

  • The first step: get the mService ClipboardManager

  • Step two: a dynamic proxy object initialization

  • The third step: substitution, the alternative system using proxyNotiMng 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);    // 第一步:得到系统的 mService
    final Object mService = mServiceFiled.get(clipboardManager);    
    // 第二步:初始化动态代理对象
    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 (the TAG, "Invoke: Item =" Item +); 
                    } 
                } 
                Toast.makeText (context, "detects that a person is provided clipboard contents", Toast.LENGTH_SHORT) the .Show (); 
            } the else IF ( "getPrimaryClip" .equals (name)) { 
                Toast.makeText (context, "was detected to obtain the content of the clipboard", Toast.LENGTH_SHORT) the .Show (); 
            } // referred sOriginService processing operation, does not intercept notification 
            return method.invoke (MSERVICE, args); 

        } 
    }); // third step: substitution, the alternative system using proxyNotiMng MSERVICE 
    Field, sServiceField = ClipboardManager.class.getDeclaredField ( "MSERVICE"); 
    sServiceField.setAccessible (to true); 
    sServiceField.set (clipboardManager , proxyInstance);

}

webp

image

第二种方法

对 Android 源码有基本了解的人都知道,Android 中的各种 Manager 都是通过 ServiceManager 获取的。因此,我们可以通过 ServiceManager hook 所有系统 Manager,ClipboardManager 当然也不例外。

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;
    }
}

老套路

  • 第一步:通过反射获取剪切板服务的远程Binder对象,这里我们可以通过 ServiceManager getService 方法获得

  • 第二步:创建我们的动态代理对象,动态代理原来的Binder对象

  • 第三步:偷梁换柱,把我们的动态代理对象设置进去

public static void hookClipboardService() throws Exception {    //通过反射获取剪切板服务的远程Binder对象
    Class serviceManager = Class.forName("android.os.ServiceManager");
    Method getServiceMethod = serviceManager.getMethod("getService", String.class);
    IBinder remoteBinder = (IBinder) getServiceMethod.invoke(null, Context.CLIPBOARD_SERVICE);    //新建一个我们需要的Binder,动态代理原来的Binder对象
    IBinder hookBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),            new Class[]{IBinder.class}, new ClipboardHookRemoteBinderHandler(remoteBinder));    //通过反射获取ServiceManger存储Binder对象的缓存集合,把我们新建的代理Binder放进缓存
    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 ())) {// Here specific services not intercept method, as this is a remote Binder, not yet translated into local Binder object 
            // so we know first interception queryLocalInterface method returns a local proxy object Binder 
            return Proxy.newProxyInstance (remoteBinder.getClass (). getClassLoader (), new Class [] {} this.iInterface, new new ClipboardHookLocalBinderHandler (remoteBinder, stubClass)); 
        } return Method.invoke (remoteBinder, args); 
    } 
}


Guess you like

Origin blog.51cto.com/13673213/2422517