Kymjs.com/code/2016/0…
Dynamically loading a Service into an application uses the same disguise as an Activity to trick the system into recognizing it.
This article mainly introduces Android plug-in development, how to run not installed apK Service. Unlike the solution I described two years ago (running services that are not installed in APK), the solution implemented this time is completely open-ended and the plug-in APK can be a completely standalone application without special syntax changes.
Service loading process
As with the dynamic loading principle of an Activity, we need to start and load the Service first. The main process is as follows:
Starting a Service is similar to an Activity and eventually calls a method in ActivityManagerService.
- And then the
startServiceLocked()
Safety monitoring; - Once the safety checks are complete,
scheduleCreateService()
Ready to createService
- Call again
scheduleServiceArgs()
Send a message - Will end up in
ActivityThread.Callback
In the processingHandle
Sent message.
After defining the entire process for starting a Service, we find that although the process is very similar to the Activity startup process described in the previous article, we cannot use the same method for activities, because the Instrumentation class is not used at all. And just like in the Activity, there is no way to override the startServiceLocked() method to tamper with the system validation because it runs in another system process, system_server. A final problem is that unlike an Activity, a Service can start multiple instances, and the onCreate() method is never called again after the same Service has been executed.
Replace the system Service creation process
Although there is no way to create a Service through Instrumentation, there are ways to replace the system creation process. In the last activityThread. Callback step, Handle sends a number of message types, including: CREATE_SERVICE, SERVICE_ARGS, BIND_SERVICE, etc… Not only the creation of the service, but also the Activity’s lifecycle methods are invoked in this callback. CREATE_SERVICE calls a method called handleCreateService(CreateServiceData data) with the main code:
As you can see, the Service is also a normal class, where the system comes out new and executes its onCreate() method. So we can do our own Service creation logic by replacing the callback class and modifying its logic if the message is CREATE_SERVICE. If the service being loaded is a plug-in, the service will replace the ClassLoader with the plug-in ClassLoader, and the loaded class will follow the process of the original host service. The attach() and onCreate() methods are all called manually. Instead, reflection is used to find mH in the original ActivityThread class.
I really want to make fun of the Android source code, which is full of various weird names. For example, mH is actually a Handle, but its class name is only one letter, a capital H, so its object is called mH. Then there’s an ActivityInfo variable called aInfo, and then there’s an ApplicationInfo object called aInfo, and every now and then there’s an AI, and you don’t know what it is and you have to go back and find its type. OK, back to the point, after the callback is replaced, creating the Service can be performed by our own methods. The problem is that onCreate is not called more than once, so we also need to change the logic of handleMessage(). If it is SERVICE_ARGS or BIND_SERVICE, we need to check first. If the plug-in service is passed in that it has not been created, then you need to run the handleCreateService() method again to create it.
Step on pit and climb pit
If you implement the whole plugin along the same lines, you’ll notice two huge pitfalls:
- The plug-in service is created, but if more than one plug-in service is started, then all but the last one is created
onCreate()
None of the other lifecycle methods are called. - The plug-in Service will not be called
onDestroy()
Methods.
Let’s start with the first problem, the lifecycle approach. As mentioned earlier, each lifecycle method is actually handled through this Handle as well. Services = mservices.get (data.token); Services = mservices.get (data.token); All created services are added to a map (HashMap before 4.0, ArrayMap after 4.0), from which they are read based on keys or tokens when needed. Life cycle methods are not called. HandleCreateService () : mservices.put (data.token, service); That’s what it was for. It also explains why other services do not call lifecycle methods because map values are overwritten. So simple, this key value token we create and add to it.
The second trap, onDestroy(), was not executed. After repeated testing, it was found that the message with STOP_SERVICE identifier was not sent. The solution is also very simple, since the system did not send, then manually send this message on the line. Find the source of all messages, ActivityManagerService, and it’s easy to replace our focus with a dynamic proxy. Find two methods related to destroy called stopServiceToken() and unbindService(). While both methods are executing, call doServiceDestroy() to manually send the message yourself. And then when the other side receives this message execute the plug-in onDestroy()
Resources are loaded dynamically with so files
In this way, the dynamic loading of activities and services in the uninstalled APK is resolved. As a review, there are only six classes in total, so why eight classes to handle the plug-in? There are two classes for handling the dynamic loading of resources and so files. So file first, in fact, DexClassLoader native support dynamic loading, but why we passed solib did not load, or because of permissions. SD cards on Android phones do not have executable permission, so we must copy the so file to the application package storage area, either getFilesDir() or getCacheDir() are directories with executable permission. When constructing the plug-in DexClassLoader, pass the third parameter to a path with executable permissions. Since we only need to load an APK dynamically, there is no plugin resource conflict involved at all, and only one method is needed:
At the end
No tidbits at the end ~ so simple 8 classes, do you have any questions?