Android plug-in, Android Development Manual

Posted by cpace1983 on Wed, 15 Dec 2021 02:06:03 +0100

Now we need to implement the function of starting Main3Activity in FixInstrumentation, but Main3Activity is a plug-in activity loaded in and is not registered in the program registration list. If it is started normally, an exception will be thrown. Here, we want to use Hook Instrumentation object to replace the startup object with a registered activity, so as to avoid system inspection, This is also the Android plug-in technology solution. Let's replace it in the execStartActivity() method

public ActivityResult execStartActivity(

      Context who, IBinder contextThread, IBinder token, Activity target,

      Intent intent, int requestCode, Bundle options) {



   ComponentName componentName = intent.getComponent();

   String packageName = componentName.getPackageName();

   String classname = componentName.getClassName();

   if (classname.equals("com.alex.kotlin.plugin.Main3Activity")) { //Determine whether it is Main3Activity

      intent.setClassName(who, ProxyActivity.class.getCanonicalName()); // Replace with registered ProxyActivity startup

   }

   intent.putExtra(ACTIVITY_RAW, classname); // Save the original Main3Activity at the same time

   try {

      @SuppressLint("PrivateApi")

      Method method = instrumentation.getClass().getDeclaredMethod("execStartActivity",

            Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);

      if (!Modifier.isPublic(method.getModifiers())) {

         method.setAccessible(true);

      }

      return (ActivityResult) method.invoke(instrumentation, who, contextThread, token, target, intent, requestCode, options);

} 

  • Create a truly started Activity

The above replacement operation evades the registration check of the system. When the program is executed to the activity created by newActivity(), we should go back to the target activity actually started, otherwise everything will be busy in vain. Next, complete the replacement in newActivity(), and execute the original logic after the replacement is completed;

public Activity newActivity(ClassLoader cl, String className,

                     Intent intent) throws InstantiationException,

      IllegalAccessException {

   String classnameIntent = intent.getStringExtra(ACTIVITY_RAW);

   String packageName = intent.getComponent().getPackageName(); // Get the real Activity package name and class name saved in Intent

   if (className.equals(ProxyActivity.class.getCanonicalName())) {

      ComponentName componentName = new ComponentName(packageName, classnameIntent); // Replace the package name and class name of the real Activity

      intent.setComponent(componentName);

      className = classnameIntent;

   }

   Log.d("FixInstrumentation == ", "set activity is  original" + className);

   try {

      @SuppressLint("PrivateApi")

      Method method = instrumentation.getClass().getDeclaredMethod("newActivity",

            ClassLoader.class, String.class, Intent.class);

      if (!Modifier.isPublic(method.getModifiers())) {

         method.setAccessible(true);

      }

      return (Activity) method.invoke(instrumentation, cl, className, intent); // Execute the original creation method

   }

} 

Hook Instrumentation implementation Activity plug-in startup summary:

  1. The Instrumentation object of the Hook system to set the created proxy class

  2. Modify the Intent of the starting Activity in the proxy class and replace the starting target Activity with a placeholder Activity to avoid checking the registration list

  3. Override newActivity () in the proxy class to switch the started activity back to the real target, and then continue to execute the original logic

3. Binder Hook (Hook system service)

The above activity startup process is modified through Hook technology, which belongs to the Hook of application program. Next, try Hook Android system service and modify the system function. Before Hook, first understand the acquisition process of system service and try to find Hook points;

3.1 principle of system access service
  • ContextImpl.getSystemService(String name)
 @Override

public Object getSystemService(String name) {

     return SystemServiceRegistry.getSystemService(this, name);

}

public static Object getSystemService(ContextImpl ctx, String name) {

     //1. From registered system_ SERVICE_ Get ServiceFetcher by name in fetchers

     ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); 

     return fetcher != null ? fetcher.getService(ctx) : null; //2. Create service in ServiceFetcher

} 

When using the system service, it calls the getSystemService () of Context directly, and finally calls the method in ContextImpl and calls SystemServiceRegistry.getSystemService in ContextImpl. () for a brief introduction to systemserviceregistry, the system will register a series of services with systemserviceregistry at startup, and obtain services directly according to the service name during use;

  • SYSTEM_ SERVICE_ Register services in readers (take JOB_SCHEDULER_SERVICE as an example)
//Registration service

registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, new StaticServiceFetcher<JobScheduler>() {

           @Override

          public JobScheduler createService() {

             IBinder b = ServiceManager.getService(Context.JOB_SCHEDULER_SERVICE); //Get Binder from ServiceManager

             return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); //Gets the proxy object for Binder

         }});

private static <T> void registerService(String serviceName, Class<T> serviceClass,ServiceFetcher<T> serviceFetcher) {

    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);

    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); //Save the service name, StaticServiceFetcher instance as a key value pair

} 

From the above registration process, the system first encapsulates the creation process of each service in the corresponding ServiceFetcher object, and then registers the ServiceFetcher object in the system with the service name_ SERVICE_ In sensors, this is why the service name is passed in when obtaining the service;

  • ServiceManager.getService (): get the Binder object corresponding to the corresponding service in the system
 public JobScheduler createService() throws ServiceNotFoundException {

               IBinder b = ServiceManager.getServiceOrThrow(Context.JOB_SCHEDULER_SERVICE);

               return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));

           } 

In the process of service acquisition, the createService () method of ServiceFetcher will be called. In create (), the Binder object saved in the system will be obtained first, and then asInterface () will be called to find the proxy class according to the Binder object. asInterface () will first check whether the Binder object exists in the process. If it does not exist, a proxy object will be created;

  • Summarize the service acquisition process:
  1. At the beginning of the system, the system will look like SYSTEM_SERVICE_FETCHERS registers the ServiceFetcher instance that encapsulates the service

  2. When a program calls to obtain a service, it is obtained from system according to the service name_ SERVICE_ Fetchers finds and returns the corresponding ServiceFetcher instance

  3. When calling get() of the instance to obtain the service, first obtain the Binder of the service saved in the system from the ServerManager

  4. Call the asInterface () method of IxxInterface to find and return the proxy class of Binder

3.2. Look for Hook points
  1. Through the above analysis, we know that obj is the place that can be operated queryLocalInterface(), if we Hook the incoming Binder object, modify its queryLocalInterface to return the proxy object of the substitute object, and then the proxy can be implemented;

  2. To achieve goal 1, we must ensure that the Binder specified by us can be returned in the search of ServerManager. Fortunately, we can get it from the system Map cache in ServerManager. We just put the agent's Binder in the cached Map, and then return the specified Binder during the search;

3.3. Actual combat -- Taking the cut version service as an example
  • Create a dynamic proxy class for the service
public class FixBinder implements InvocationHandler {

    private static final String TAG = "BinderHookHandler";

    // Original Service object (IInterface)

    Object base;

    public FixBinder(IBinder base, Class<?> stubClass) {

        try {

         Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class);//Gets the asInterface of the original interface

         this.base = asInterfaceMethod.invoke(null, base); //Use the original Binder reflection to get the proxy class of the original service

        } catch (Exception e) {

            throw new RuntimeException("hooked failed!");

        }

    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // Deceive the system into thinking that there is always content on the cutout

        if ("hasPrimaryClip".equals(method.getName())) {

            return true;

        }

        return method.invoke(base, args); //The remaining methods are executed using the original Binder proxy reflection

    }

} 

  1. This is different from the previous direct saving of system objects, because the Binder of the system is obtained first when searching for services. Only by using the Binder, the proxy class will be returned

  2. Pass in the Binder object found in the system in the constructor, and then call asasInterface() to obtain and save the proxy class of the service of the system service itself

  3. Intercept the clipping method. The intercepting hasPrimaryClip () method returns true, so that the system always thinks that there is content on the clipboard

  • Create Binder object
public class ProxyBinder implements InvocationHandler {

    IBinder base;

    Class<?> stub;

    Class<?> iinterface;

    public ProxyBinder(IBinder base) {

        this.base = base; //(1)

        try {

            this.stub = Class.forName("android.content.IClipboard$Stub"); //(2)

            this.iinterface = Class.forName("android.content.IClipboard");

        } catch (ClassNotFoundException e) {

            e.printStackTrace();

        }

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("queryLocalInterface".equals(method.getName())) { //(3) 

            return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),//(4)

                    // asInterface will detect whether it is a specific type of interface, and then cast it

                    // Therefore, the type of type information generated by the dynamic agent here must be correct, that is, it must be the following three interface instances

                    new Class[] { IBinder.class, IInterface.class, this.iinterface },

                    new FixBinder(base, stub));

        }

        return method.invoke(base, args);

    }

} 

First, the proxy class of the system service is created. From the previous analysis, it is known that the use of the proxy class is queried by Binder. Therefore, the next step is to create a Binder class and internally intercept the queryLocalInterface () method of the query, so that this method returns to the proxy class of the first step. The creation steps are as follows:

  1. Like a normal agent, the original real Binder in ServerManager is saved inside the agent

  2. Use reflection to obtain IClipboard$Stub class, which is used to find proxy class

  3. Hook the queryLocalInterface method

  4. After the invoke () intercepts the method, use the dynamic proxy to create and return the proxy Binder of IClipboard

  • Hook replaces Binder in ServerManager
final String CLIPBOARD_SERVICE = "clipboard";

// The meaning of the following paragraph is actually: Service Manager getService("clipboard");

Class<?> serviceManager = Class.forName("android.os.ServiceManager");

Method getService = serviceManager.getDeclaredMethod("getService", String.class);

// (1) The original Clipboard Binder object managed in ServiceManager

IBinder rawBinder = (IBinder) getService.invoke(null, CLIPBOARD_SERVICE);

//(2) Hook drops the queryLocalInterface method of the Binder proxy object

IBinder hookedBinder = (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),

        new Class<?>[] { IBinder.class },

        new BinderProxyHookHandler(rawBinder));

// (3) Put the hook Binder proxy object into the cache of ServiceManager

Field cacheField = serviceManager.getDeclaredField("sCache");

cacheField.setAccessible(true);

Map<String, IBinder> cache = (Map) cacheField.get(null);

cache.put(CLIPBOARD_SERVICE, hookedBinder); 

All the proxy binders to be created have been implemented in the first two parts. The rest is to put the ProxyBinder into the cache of the system ServiceManager, so that the Binder will be returned according to our requirements during query, and the following routine can be executed. See the code Notes for the specific Hook process;

4. Hook system service AMS (Android 9.0)

The above two examples have clearly introduced the use of Hook. Next, use Hook technology to intercept the AMS of the system and change the Service startup of the system, which is also the principle of plug-in startup Service. Here, start the unregistered MyService. For the Service startup process, click the links of the four components above, Because AMS also communicates through Binder, the first step of Hook is to implement Binder's dynamic agent

  • Create AMS agent to realize the startup process of function interception service
public class HookProxyBinder implements InvocationHandler {

   public static final String HookProxyBinder = "HookProxyBinder";

   Object binder;

   public HookProxyBinder(Object binder) {

      this.binder = binder;

   }

   @Override

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

      Log.e("HookProxyBinder==", method.getName());

      if ("startService".equals(method.getName())) { //Intercept start service

         int i = 0;

         Intent intent = null;

         for (int index = 0; index < args.length; index++) {

            if (args[index] instanceof Intent) {

               i = index;

#### Is there anything you must master in addition to flutter for Android development?

Believe that most people are engaged in Android Development friends are finding it more and more difficult to find a job and the requirements of interview are higher and higher

In addition to solid foundation java Knowledge, data structures, algorithms, design patterns, and the underlying source code, NDK Technology, performance tuning, and some small programs and cross platforms, for example flutter,It is shown in the following figure in the form of mind map;

**[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**

![](https://img-blog.csdnimg.cn/img_convert/6e9a3582d093c74d79a2fdc22f836ef8.png)

= null;

         for (int index = 0; index < args.length; index++) {

            if (args[index] instanceof Intent) {

               i = index;

#### Is there anything you must master in addition to flutter for Android development?

Believe that most people are engaged in Android Development friends are finding it more and more difficult to find a job and the requirements of interview are higher and higher

In addition to solid foundation java Knowledge, data structures, algorithms, design patterns, and the underlying source code, NDK Technology, performance tuning, and some small programs and cross platforms, for example flutter,It is shown in the following figure in the form of mind map;

**[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**

[External chain picture transfer...(img-6tXSPAnX-1630833493010)]

Topics: Android SQLite Design Pattern