LSPosed / LSPatch

LSPatch: A non-root Xposed framework extending from LSPosed
GNU General Public License v3.0
7.06k stars 751 forks source link

Handling duplicate System.load calls #200

Closed harshitshah4 closed 1 year ago

harshitshah4 commented 1 year ago

We have seen apps to be crashing due to multiple System.load calls. As seen from the log lines:

06-01 16:43:41.615 I/LSPosed-Bridge(16031): Caused by: java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.example.sample-some_random_string/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.sample-some_random_string/base.apk"],nativeLibraryDirectories=[/data/app/com.example.sample-some_random_string/lib/arm64, /data/app/com.example.sample-some_random_string/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ffc869ecc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.example.sample-some_random_string/base.apk"],nativeLibraryDirectories=[/data/app/com.example.sample-some_random_string/lib/arm64, /data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]])

Looks like we are trying to load liblspatch.so file even when it was loaded earlier by the ClassLoader, due to which it is causing the error.

This is similar to this issue: https://stackoverflow.com/questions/54155086/preventing-duplicate-system-loadlibrary-calls-when-dynamically-loading-reloading

Have added try ... catch block around System.load method to handle exceptions / error raised from it, and have added log line to debug the issue in cases of actual errors.

harshitshah4 commented 1 year ago

@yujincheng08 thanks for the quick review, @vvb2060 @Dr-TSNG can we please get your review too.

harshitshah4 commented 1 year ago

@yujincheng08 can we get this PR merged ?

harshitshah4 commented 1 year ago

@yujincheng08 / @vvb2060 why did we revert this ?

yujincheng08 commented 1 year ago

modules should not load lspatch's so.

harshitshah4 commented 1 year ago

@yujincheng08 Module was not loading lspatch.so file, LSPatch was loading it internally. Since we have added System.load method in static block in LSPAppComponentFactory.stub method, sometimes apps gets restarted on its own, and the static block in App Component Factory gets loaded again in to memory due to which System.load method gets called again. Hence only some apps were crashing.

harshitshah4 commented 1 year ago

@yujincheng08 @vvb2060 I feel this is a valid bug in LSPatch, and should be merged in master to fix this.

yujincheng08 commented 1 year ago

so will ur fix works? if app is reloaded, and thus the lspatch classloader is reloaded, will the reloaded lspatch and app work?

harshitshah4 commented 1 year ago

Yes.

yujincheng08 commented 1 year ago

how? since lspatch.so is not loaded successfully?

harshitshah4 commented 1 year ago

lspatch.so has loaded already, and we are trying to reload it hence it is failing.

yujincheng08 commented 1 year ago

I think AppComponentFactory will only be loaded once. Can you show me the code that AppComponentFactory will be loaded the second time by different classloader? Since you don't show the full backtrace, I cannot reference the code.

harshitshah4 commented 1 year ago

@yujincheng08 sharing the complete stacktrace:

06-01 13:09:24.926 W/istmobile.debu(31172): ClassLoaderContext classpath size mismatch. expected=0, found=42 (PCL[]{PCL[/system/framework/org.apache.http.legacy.jar*1038351580]} | PCL[/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk*963391286:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes2.dex*3523290992:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes3.dex*1437002724:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes4.dex*3949827186:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes5.dex*104114143:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes6.dex*1198623303:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes7.dex*30799929:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.
06-01 13:09:24.954 E/System  (31172): Unable to open zip file: /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk
06-01 13:09:24.954 E/System  (31172): java.io.FileNotFoundException: /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk (No such file or directory)
06-01 13:09:24.954 E/System  (31172):   at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:164)
06-01 13:09:24.955 I/LSPatch-MetaLoader(31172): Bootstrap loader from embedment
06-01 13:09:24.968 W/istmobile.debu(31172): Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/org.houstonmethodist.methodistmo
06-01 13:09:24.969 I/LSPosed-Bridge(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:514)
06-01 13:09:24.969 I/LSPosed-Bridge(31172): Caused by: java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]])
06-01 13:09:24.969 I/LSPosed-Bridge(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:464)
06-01 13:09:24.969 E/AndroidRuntime(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:514)
06-01 13:09:24.969 E/AndroidRuntime(31172): Caused by: java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]])
06-01 13:09:24.969 E/AndroidRuntime(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:464)
yujincheng08 commented 1 year ago

hmm, still dont know where LSPAppComponentFactoryStub is loaded.

harshitshah4 commented 1 year ago

You can check these log lines:

06-01 13:08:47.000 I/LSPatch-MetaLoader(31172): Bootstrap loader from embedment
06-01 13:08:47.104 I/LSPatch (31172): Use manager: false
06-01 13:08:47.104 I/LSPatch (31172): Signature bypass level: 0
06-01 13:08:47.107 I/LSPatch (31172): Extract original apk
06-01 13:08:47.511 I/LSPatch (31172): hooked app initialized: android.app.LoadedApk@8c9a549
06-01 13:08:47.519 W/LSPatch (31172): Original AppComponentFactory not found: androidx.core.app.CoreComponentFactory
06-01 13:08:47.519 D/LSPatch (31172): Initialize service client
06-01 13:08:47.523 I/LSPatch (31172): Extract module apk: com.varuns2002.disable_flag_secure
06-01 13:08:47.533 D/LSPatch (31172): Processing /data/misc/profiles/cur/0/com.sample.app.debug/primary.prof
06-01 13:08:47.540 D/LSPatch (31172): Skip profile /data/misc/profiles/cur/0/com.sample.app.debug/primary.prof
06-01 13:08:47.551 I/LSPatch (31172): Bootstrap Xposed
06-01 13:08:47.560 I/LSPosed-Bridge(31172): Loading module com.varuns2002.disable_flag_secure from /data/user/0/com.sample.app.debug/cache/lspatch/com.varuns2002.disable_flag_secure/3335930313.apk
06-01 13:08:47.571 I/LSPatch (31172): Load modules
06-01 13:08:50.620 I/LSPatch (31172): Modules initialized
06-01 13:08:50.621 I/LSPatch (31172): LSPatch bootstrap completed
06-01 13:09:24.926 W/istmobile.debu(31172): ClassLoaderContext classpath size mismatch. expected=0, found=42 (PCL[]{PCL[/system/framework/org.apache.http.legacy.jar*1038351580]} | PCL[/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk*963391286:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes2.dex*3523290992:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes3.dex*1437002724:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes4.dex*3949827186:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes5.dex*104114143:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes6.dex*1198623303:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!classes7.dex*30799929:/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.
06-01 13:09:24.954 E/System  (31172): Unable to open zip file: /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk
06-01 13:09:24.954 E/System  (31172): java.io.FileNotFoundException: /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk (No such file or directory)
06-01 13:09:24.954 E/System  (31172):   at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:164)
06-01 13:09:24.955 I/LSPatch-MetaLoader(31172): Bootstrap loader from embedment
06-01 13:09:24.968 W/istmobile.debu(31172): Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/org.houstonmethodist.methodistmo
06-01 13:09:24.969 I/LSPosed-Bridge(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:514)
06-01 13:09:24.969 I/LSPosed-Bridge(31172): Caused by: java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]])
06-01 13:09:24.969 I/LSPosed-Bridge(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:464)
06-01 13:09:24.969 E/AndroidRuntime(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:514)
06-01 13:09:24.969 E/AndroidRuntime(31172): Caused by: java.lang.UnsatisfiedLinkError: Shared library "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/assets/lspatch/so/arm64-v8a/liblspatch.so" already opened by ClassLoader 0x1c7(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]]); can't open in ClassLoader 0x7ff8d8befc(dalvik.system.PathClassLoader[DexPathList[[zip file "/data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk", zip file "/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/base.apk"],nativeLibraryDirectories=[/data/app/com.sample.app.debug-QEePRTHZDLE9exp1cVuEfA==/lib/arm64, /data/user/0/com.sample.app.debug/cache/lspatch/origin/4198609975.apk!/lib/arm64-v8a, /system/lib64, /product/lib64]]])
06-01 13:09:24.969 E/AndroidRuntime(31172):     at org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub.<clinit>(Unknown Source:464)

If you see at the log line: Bootstrap loader from embedment in above logs, this log line is added in code at this point: https://github.com/LSPosed/LSPatch/blob/e79bf0e004b17171607ab04dcd42f05493d59d9f/meta-loader/src/main/java/org/lsposed/lspatch/metaloader/LSPAppComponentFactoryStub.java#L85, which means LSPAppComponentFactoryStub is loaded, and hence causing the crash.

yujincheng08 commented 1 year ago

I mean, who loaded the LSPAppComponentFactoryStub?

harshitshah4 commented 1 year ago

This whole code is inside static block in LSPAppComponentFactoryStub As in Android/ Java every static block gets loaded automatically as soon as the class is referenced, which generally happens when app is started / re-started

yujincheng08 commented 1 year ago

I know. So where the new classloader is created and who loads LSPAppComponentFactoryStub to trigger the clinit?

harshitshah4 commented 1 year ago

There could be multiple reasons for clinit to getting calling multiple times, it is not guaranteed to be called only once. It could happen in case of class reloading: In some cases, such as during hot swapping or dynamic code reloading, the class may be reloaded, leading to the execution of the static block again.

yujincheng08 commented 1 year ago

it is not guaranteed to be called only once

no. pls learn java better.

the reason is that someone has created a new classloader of the base apk (not the original one) and loaded LSPAppComponentFactoryStub again. we need to find out where and who did this.

harshitshah4 commented 1 year ago

@yujincheng08 even if what you are saying is true, someone can write a code in an "app" to do that. And if the app works without patching, we should try to make it work with patching too.

yujincheng08 commented 1 year ago

so the app writes some anti-lspatch codes, right? intentedly query the pm for the base apk, load it and load LSPAppComponentFactoryStub right?

harshitshah4 commented 1 year ago

The app that I am testing has not written anti-lspatch code. They might query pm of base.apk as a "valid" workflow in their app.

harshitshah4 commented 1 year ago

Even if someone is loading all the class files in base apks using pm, its a valid use case in app, it shouldn't mean that they have written anti-lspatch mechanisms.

harshitshah4 commented 1 year ago

@yujincheng08 We are also seeing this log line:

ClassLoaderContext classpath size mismatch. expected=0, found=42 (PCL[]{PCL[/system/framework/org.apache.http.legacy.jar*1038351580]} | PCL[/data/user/0/com.example.app/cache/lspatch/origin/4198609975.apk*963391286:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes2.dex*3523290992:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes3.dex*1437002724:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes4.dex*3949827186:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes5.dex*104114143:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes6.dex*1198623303:/data/user/0/org.houstonmethodist.methodistmobile.debug/cache/lspatch/origin/4198609975.apk!classes7.dex*30799929:/data/user/0/
Running dexopt (dexoptNeeded=1) on: /data/app/com.example.app-QEePRTHZDLE9exp1cVuEfA==/base.apk

Not sure if it can help you debug.

harshitshah4 commented 1 year ago

Found this reference: https://source.android.com/docs/core/runtime/art-class-loader-context#boot-time-clc-mismatch

Also the app is crashing on Android v10, and working on other versions

ayush5harma commented 1 year ago

someone can write a code in an "app" to do that.

@yujincheng08 @harshitshah4 Is this not a valid usecase for android apps?

harshitshah4 commented 1 year ago

Yes @ayush5harma this is a valid use case in an Android app, and hence its a valid bug in LSPatch. @yujincheng08

yujincheng08 commented 1 year ago
  1. There should only be one AppComponentFactory for a process, which should be loaded by the system framework. LSPatch replaces it with LSPAppComponentFactoryStub and loads lspatch.so.
  2. It's possible that an app creates another classloader to load its base.apk again, but most apps won't do this, because this would cause the app to crash if some of its class loads a so in its clinit (like what LSPAppComponentFactoryStub does).
  3. Though a few apps may load its base.apk again for some reason, it should be able to bypass by enabling the level 2 signature bypass.
  4. Even if the l2 signature bypass is off, your fix is not valid. You only ignored the failure of loading the library, but you didn't forward class loading to the correct classloader. Say, if the app loads its base.apk again, and tries to load a class HelloWorld, what would happen? There's no such class in LSPatch's classloader.
  5. Based on point 4, The app you are hooking is very likely doing so to anti-lspatch.

I think this behavior is expected if the l2 signature bypass is off. Because we cannot forward class loading to the correct classloader perfectly. Imagine if an app loads its base.apk again, and loads some class with strange names that do not exist in LSPatch's classloader and crashes, how can we avoid this?

harshitshah4 commented 1 year ago

@yujincheng08 thanks for the detailed explanation. Since you mentioned that app is trying to load class files in base.apk, due to which LSPAppComponentFactory is getting loaded. Is there a way to redirect the PackageManager to point to the original apk, and not LSPatch apk

yujincheng08 commented 1 year ago

it should be able to bypass by enabling the level 2 signature bypass

harshitshah4 commented 1 year ago

@yujincheng08 The app crashes with same reason even after level 2 signature bypass