@@ -65,6 +65,9 @@ public class ExposedBridge {
6565 ? "/data/user_de/0/de.robv.android.xposed.installer/" : BASE_DIR_LEGACY ;
6666
6767 private static final String VERSION_KEY = "version" ;
68+ private static boolean SYSTEM_CLASSLOADER_INJECT = true ;
69+
70+ private static final String WECHAT_PACKAGE = "com.tencent.mm" ;
6871
6972 private static Pair <String , Set <String >> lastModuleList = Pair .create (null , null );
7073 private static Map <ClassLoader , ClassLoader > exposedClassLoaderMap = new HashMap <>();
@@ -76,7 +79,7 @@ public class ExposedBridge {
7679 private static ModuleLoadListener sModuleLoadListener = new ModuleLoadListener () {
7780 @ Override
7881 public void onLoadingModule (String moduleClassName , ApplicationInfo applicationInfo , ClassLoader appClassLoader ) {
79- if ("com.tencent.mm" .equalsIgnoreCase (applicationInfo .packageName )) {
82+ if (WECHAT_PACKAGE .equalsIgnoreCase (applicationInfo .packageName )) {
8083 isWeChat = true ;
8184 }
8285 }
@@ -100,6 +103,8 @@ enum ModuleLoadResult {
100103 }
101104
102105 public static void initOnce (Context context , ApplicationInfo applicationInfo , ClassLoader appClassLoader ) {
106+ SYSTEM_CLASSLOADER_INJECT = patchSystemClassLoader ();
107+
103108 appContext = context ;
104109 ReLinker .loadLibrary (context , "epic" );
105110 ExposedHelper .initSeLinux (applicationInfo .processName );
@@ -109,15 +114,31 @@ public static void initOnce(Context context, ApplicationInfo applicationInfo, Cl
109114 initForXposedInstaller (context , applicationInfo , appClassLoader );
110115 }
111116
112- public static void patchAppClassLoader (Context baseContext ) {
113- final ClassLoader originClassLoader = baseContext .getClassLoader ();
114- Object mPackageInfo = XposedHelpers .getObjectField (baseContext , "mPackageInfo" );
115- ClassLoader appClassLoaderWithXposed = getAppClassLoaderWithXposed (originClassLoader );
116- XposedHelpers .setObjectField (mPackageInfo , "mClassLoader" , appClassLoaderWithXposed );
117- Thread .currentThread ().setContextClassLoader (appClassLoaderWithXposed );
117+ private static boolean patchSystemClassLoader () {
118+ // 1. first create XposedClassLoader -> BootstrapClassLoader
119+ ClassLoader xposedClassLoader = new XposedClassLoader (ExposedBridge .class .getClassLoader ());
120+
121+ // 2. replace the systemclassloader's parent.
122+ ClassLoader systemClassLoader = ClassLoader .getSystemClassLoader ();
123+
124+ try {
125+ Field parent = ClassLoader .class .getDeclaredField ("parent" );
126+ parent .setAccessible (true );
127+ parent .set (systemClassLoader , xposedClassLoader );
128+
129+ // SystemClassLoader -> XposedClassLoader -> BootstrapClassLoader
130+ return systemClassLoader .getParent () == xposedClassLoader ;
131+ } catch (NoSuchFieldException e ) {
132+ // todo no such field ? use unsafe.
133+ log (e );
134+ return false ;
135+ } catch (IllegalAccessException e ) {
136+ log (e );
137+ return false ;
138+ }
118139 }
119140
120- public static synchronized ClassLoader getAppClassLoaderWithXposed (ClassLoader appClassLoader ) {
141+ private static synchronized ClassLoader getAppClassLoaderWithXposed (ClassLoader appClassLoader ) {
121142 if (exposedClassLoaderMap .containsKey (appClassLoader )) {
122143 return exposedClassLoaderMap .get (appClassLoader );
123144 } else {
@@ -151,18 +172,26 @@ public static ModuleLoadResult loadModule(final String moduleApkPath, String mod
151172 return ModuleLoadResult .DISABLED ;
152173 }
153174
154- log ("Loading modules from " + moduleApkPath + " for process: " + currentApplicationInfo .processName );
175+ log ("Loading modules from " + moduleApkPath + " for process: " + currentApplicationInfo .processName + " i s c: " + SYSTEM_CLASSLOADER_INJECT );
155176
156177 if (!new File (moduleApkPath ).exists ()) {
157178 log (moduleApkPath + " does not exist" );
158179 return ModuleLoadResult .NOT_EXIST ;
159180 }
160181
161- ClassLoader hostClassLoader = ExposedBridge .class .getClassLoader ();
162- ClassLoader appClassLoaderWithXposed = getAppClassLoaderWithXposed (appClassLoader );
163-
164- ClassLoader mcl = new DexClassLoader (moduleApkPath , moduleOdexDir , moduleLibPath , getXposedClassLoader (hostClassLoader ));
165- // ClassLoader mcl = new DexClassLoader(moduleApkPath, moduleOdexDir, moduleLibPath, hostClassLoader);
182+ ClassLoader appClassLoaderWithXposed ;
183+ ClassLoader mcl ;
184+ if (SYSTEM_CLASSLOADER_INJECT ) {
185+ // we replace the systemclassloader's parent success, go with xposed's way
186+ appClassLoaderWithXposed = appClassLoader ;
187+ mcl = new DexClassLoader (moduleApkPath , moduleOdexDir , moduleLibPath , XposedBridge .BOOTCLASSLOADER );
188+ } else {
189+ // replace failed, just wrap.
190+ ClassLoader hostClassLoader = ExposedBridge .class .getClassLoader ();
191+ appClassLoaderWithXposed = getAppClassLoaderWithXposed (appClassLoader );
192+ mcl = new DexClassLoader (moduleApkPath , moduleOdexDir , moduleLibPath , getXposedClassLoader (hostClassLoader ));
193+ // ClassLoader mcl = new DexClassLoader(moduleApkPath, moduleOdexDir, moduleLibPath, hostClassLoader);
194+ }
166195
167196 InputStream is = mcl .getResourceAsStream ("assets/xposed_init" );
168197 if (is == null ) {
@@ -226,10 +255,7 @@ public static ModuleLoadResult loadModule(final String moduleApkPath, String mod
226255 } catch (IOException e ) {
227256 log (e );
228257 } finally {
229- try {
230- is .close ();
231- } catch (IOException ignored ) {
232- }
258+ closeSliently (is );
233259 }
234260 return ModuleLoadResult .FAILED ;
235261 }
@@ -284,8 +310,6 @@ private static void initForXposedInstaller(Context context, ApplicationInfo appl
284310 return ;
285311 }
286312
287- Log .i (TAG , "initForXposedInstaller" );
288-
289313 // XposedInstaller
290314 final int fakeXposedVersion = 91 ;
291315 final String fakeVersionString = String .valueOf (fakeXposedVersion );
0 commit comments