The ADBI framework and Libinject, which are analyzed earlier, are implemented using so injection, which enables the loading of the specified code into the target process with several features:
1. Is dynamic and requires that the target process has started
2. Cannot affect the global, For example, inject a process hook inside libc.so open function, at this time, the B process uses the libc.so open function or the old function, the Linux system through the cow mechanism, when you inject a process and execute the hook to open, copy the new page, put into the new function. If you want to affect the global, it should be injected into a process like Zygote, and should be injected immediately after the Zygote process starts, so that subsequent Zygote processes can use the hooks after the child process is generated
3. Need to rely on ptrace mechanism, in some cases, the target process can not be executed ptrace, this way will be invalidated
The other way we analyze this is the way the famous xposed framework is used, without the need for dynamic injection, instead of simply replacing an executable program of the Android system.
One, the Android application layer process starts the most initial steps
After the Linux system loads and initializes each subsystem, executes the first application-level program init, the Android Init program is customized, and unlike other Linux distributions, it also parses and executes the init.rc configuration file. One of the steps below is to invoke the App_process program to start the zygote process, and Xposed replaces the/system/bin/app_process program
System/core/rootdir/init.rc
Service Zygote/system/bin/app_process-xzygote/system/bin--zygote--start-system-Server class main 660 root system /sys/android_power/request_state wake / sys/power/state on onrestart Restart media onrestart restart netd
Android\frameworks\base\cmds\app_process\app_main.cpp:main function
if(zygote) {Runtime.start ("Com.android.internal.os.ZygoteInit", Startsystemserver?"Start-system-server":""); } Else if(className) {//remainder of args get passed to startup class main ()Runtime.mclassname =ClassName; RUNTIME.MARGC= ARGC-i; RUNTIME.MARGV= argv +i; Runtime.start ("Com.android.internal.os.RuntimeInit", Application?"Application":"Tool"); } Else{
App_process is the portal of the native world into the Java world, which initializes the execution-time environment of the virtual machine, and calls Com.android.internal.os.ZygoteInit, depending on the parameters, or Com.android.internal.os.RuntimeInit The main function of the two Java classes, if the former, enters the zygote world.
Xposed\app_main.cpp:main function
if(zygote) {Runtime.start (keeploadingxposed? Xposed_class_dots:"Com.android.internal.os.ZygoteInit", Startsystemserver?"Start-system-server":""); } Else if(className) {//remainder of args get passed to startup class main ()Runtime.mclassname =ClassName; RUNTIME.MARGC= ARGC-i; RUNTIME.MARGV= argv +i; Runtime.start (keeploadingxposed? Xposed_class_dots:"Com.android.internal.os.RuntimeInit", Application?"Application":"Tool"); } Else{
#define Xposed_class_dots "De.robv.android.xposed.XposedBridge"
Unlike the standard process, if the Android version is detected to support xposed and the xposed is already installed, Runtime.start starts with the main function of De.robv.android.xposed.XposedBridge. Into the xposed world.
Xposedbridge.java
Private Static voidMain (string[] args) {//The class The VM has been created for or null for the Zygote processString Startclassname =Getstartclassname (); //Initialize the Xposed framework and modules Try { //Initialize log file Try{logFile=NewFile (Base_dir +"Log/error.log"); if(Startclassname = =NULL&& logfile.length () >max_logfile_size_soft) Logfile.renameto (NewFile (Base_dir +"Log/error.log.old")); LogWriter=NewPrintWriter (NewFileWriter (LogFile,true)); Logfile.setreadable (true,false); Logfile.setwritable (true,false); } Catch(IOException ignored) {} String date= Dateformat.getdatetimeinstance (). Format (NewDate ()); Determinexposedversion (); Log ("-----------------\ n"+ Date +"utc\n"+"Loading Xposed v"+xposed_bridge_version+"( for"+ (Startclassname = =NULL?"Zygote": startclassname) +")..."); if(Startclassname = =NULL) { //ZygoteLog"Running ROM '"+ Build.display +"' with fingerprint '"+ Build.fingerprint +"'"); } if(Initnative ()) {if(Startclassname = =NULL) { //initializations for ZygoteInitxbridgezygote (); } loadmodules (Startclassname); } Else{log ("Errors during native Xposed initialization"); } } Catch(Throwable t) {log ("Errors during Xposed initialization"); Log (t); Disablehooks=true; } //Call the original startup code if(Startclassname = =NULL) Zygoteinit.main (args); ElseRuntimeinit.main (args); }
The Xposedbridge class initializes the environment required by the xposed and then loads the Xposed module that is registered with the xposed framework, after which the xposed hooks to the virtual machine have been completed, and the Mian function finally executes Zygoteinit.main Or runtimeinit.main, into the normal process.
As can be seen here, Xposed's injection of virtual machines uses a more elegant approach than dynamic injection, with several features:
1. Since the replacement of App_process, the replacement of App_process must first start xposed and then enter the zygote, and other apps are zygote created, so xposed hook is a certain global, all apps will be affected
2. Only need to install xposed when the root authority to replace the system's app_process, and then no longer need root privileges, and the previous use of so dynamic injection, each time to hook need to inject, each injection zygote need root authority
3. No need to rely on mechanisms such as Ptrace
Android Hook framework xposed how to implement injection