fix: 正確設定共享偏好設定鍵並提升加載穩定性

- 修正了 SharedPreferences 鍵配置錯誤,確保模組緩存能正確加載。
- 優化 檔案注入器: 減少 ZipFile 重複 I/O,提升啟動效率。
- 優化 profile 禁用: 採用冪等性檢查和 Truncate 替代 Delete/Create 流程,增強對系統監控的穩定性,避免用戶配置意外清除。
- 代碼精簡: 改進了變數命名和程式碼結構。

fix: 正確設定共享偏好設定鍵並提升加載穩定性

- 修正了 SharedPreferences 鍵配置錯誤,確保模組緩存能正確加載。
- 優化 檔案注入器: 減少 ZipFile 重複 I/O,提升啟動效率。
- 優化 profile 禁用: 採用冪等性檢查和 Truncate 替代 Delete/Create 流程,增強對系統監控的穩定性,避免用戶配置意外清除。
- 改進 manager 構建檔案確保所有庫檔案不被壓縮。
- 代碼精簡: 改進了變數命名和程式碼結構。
This commit is contained in:
NkBe 2025-12-07 22:05:46 +08:00
parent 799bc1d022
commit 5a90ed436e
No known key found for this signature in database
GPG Key ID: 525137026FF031DF
3 changed files with 43 additions and 58 deletions

View File

@ -8,6 +8,11 @@
-assumenosideeffects public class kotlin.coroutines.jvm.internal.DebugMetadataKt { -assumenosideeffects public class kotlin.coroutines.jvm.internal.DebugMetadataKt {
private static ** getDebugMetadataAnnotation(...) return null; private static ** getDebugMetadataAnnotation(...) return null;
} }
-keep class com.beust.jcommander.** { *; }
-keep interface com.beust.jcommander.** { *; }
-keepclassmembers class org.lsposed.patch.NPatch {
@com.beust.jcommander.Parameter *;
}
-keep class com.beust.jcommander.** { *; } -keep class com.beust.jcommander.** { *; }
-keep class org.lsposed.npatch.database.** { *; } -keep class org.lsposed.npatch.database.** { *; }

View File

@ -24,22 +24,12 @@ class LSPApplication : Application() {
var targetApkFiles: ArrayList<File>? = null var targetApkFiles: ArrayList<File>? = null
val globalScope = CoroutineScope(Dispatchers.Default) val globalScope = CoroutineScope(Dispatchers.Default)
companion object {
init {
try {
System.loadLibrary("verify")
} catch (e: UnsatisfiedLinkError) {
e.printStackTrace()
}
}
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
verifySignature() verifySignature()
try { try {
nativeVerify()
} catch (e: UnsatisfiedLinkError) { } catch (e: UnsatisfiedLinkError) {
e.printStackTrace() e.printStackTrace()
} catch (e: Exception) { } catch (e: Exception) {
@ -75,4 +65,8 @@ class LSPApplication : Application() {
killApp() killApp()
} }
} }
private fun killApp() {
Process.killProcess(Process.myPid())
}
} }

View File

@ -19,15 +19,15 @@ import com.google.gson.Gson;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.lsposed.lspd.core.Startup;
import org.lsposed.lspd.models.Module;
import org.lsposed.lspd.service.ILSPApplicationService;
import org.lsposed.npatch.loader.util.FileUtils; import org.lsposed.npatch.loader.util.FileUtils;
import org.lsposed.npatch.loader.util.XLog; import org.lsposed.npatch.loader.util.XLog;
import org.lsposed.npatch.service.IntegrApplicationService; import org.lsposed.npatch.service.IntegrApplicationService;
import org.lsposed.npatch.service.NeoLocalApplicationService; import org.lsposed.npatch.service.NeoLocalApplicationService;
import org.lsposed.npatch.service.RemoteApplicationService; import org.lsposed.npatch.service.RemoteApplicationService;
import org.lsposed.npatch.share.PatchConfig; import org.lsposed.npatch.share.PatchConfig;
import org.lsposed.lspd.core.Startup;
import org.lsposed.lspd.models.Module;
import org.lsposed.lspd.service.ILSPApplicationService;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -47,6 +47,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import dalvik.system.DexFile; import dalvik.system.DexFile;
@ -56,6 +57,7 @@ import hidden.HiddenApiBridge;
/** /**
* Created by Windysha * Created by Windysha
* Updated by NkBe
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class LSPApplication { public class LSPApplication {
@ -109,8 +111,8 @@ public class LSPApplication {
moduleObj.put("packageName", module.packageName); moduleObj.put("packageName", module.packageName);
moduleArr.put(moduleObj); moduleArr.put(moduleObj);
} }
SharedPreferences shared = context.getSharedPreferences("opatch", Context.MODE_PRIVATE); SharedPreferences shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE);
shared.edit().putString("modules",moduleArr.toString()).commit(); shared.edit().putString("modules", moduleArr.toString()).commit();
Log.e(TAG, "Success update module scope"); Log.e(TAG, "Success update module scope");
}catch (Exception e){ }catch (Exception e){
Log.e(TAG, "Failed to connect to manager, fallback to fixed local service"); Log.e(TAG, "Failed to connect to manager, fallback to fixed local service");
@ -161,11 +163,13 @@ public class LSPApplication {
Log.i(TAG, "Signature bypass level: " + config.sigBypassLevel); Log.i(TAG, "Signature bypass level: " + config.sigBypassLevel);
Path originPath = Paths.get(appInfo.dataDir, "cache/npatch/origin/"); Path originPath = Paths.get(appInfo.dataDir, "cache/npatch/origin/");
Path cacheApkPath; String originalSourceDir = appInfo.sourceDir;
try (ZipFile sourceFile = new ZipFile(appInfo.sourceDir)) {
cacheApkPath = originPath.resolve(sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc() + ".apk"); long sourceCrc;
try (ZipFile sourceFile = new ZipFile(originalSourceDir)) {
sourceCrc = sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc();
} }
String sourceFileaa = appInfo.sourceDir; Path cacheApkPath = originPath.resolve(sourceCrc + ".apk");
appInfo.sourceDir = cacheApkPath.toString(); appInfo.sourceDir = cacheApkPath.toString();
appInfo.publicSourceDir = cacheApkPath.toString(); appInfo.publicSourceDir = cacheApkPath.toString();
@ -181,17 +185,16 @@ public class LSPApplication {
} }
Path providerPath = null; Path providerPath = null;
if (config.injectProvider) { if (config.injectProvider) {
try (ZipFile sourceFile = new ZipFile(sourceFileaa)) { providerPath = originPath.resolve("p_" + sourceCrc + ".dex");
providerPath = Paths.get(appInfo.dataDir, "cache/npatch/origin/p_" + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc() + ".dex"); try {
Files.deleteIfExists(providerPath); Files.deleteIfExists(providerPath);
try (InputStream is = baseClassLoader.getResourceAsStream(PROVIDER_DEX_ASSET_PATH)) { try (InputStream is = baseClassLoader.getResourceAsStream(PROVIDER_DEX_ASSET_PATH)) {
Files.copy(is, providerPath); Files.copy(is, providerPath);
} }
if (providerPath != null) { providerPath.toFile().setWritable(false);
providerPath.toFile().setWritable(false);
}
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Failed to inject provider:" + Log.getStackTraceString(e)); Log.e(TAG, "Failed to inject provider:" + Log.getStackTraceString(e));
providerPath = null;
} }
} }
@ -260,48 +263,31 @@ public class LSPApplication {
} }
public static void disableProfile(Context context) { public static void disableProfile(Context context) {
final ArrayList<String> codePaths = new ArrayList<>();
var appInfo = context.getApplicationInfo(); var appInfo = context.getApplicationInfo();
var pkgName = context.getPackageName();
if (appInfo == null) return; if (appInfo == null) return;
if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
codePaths.add(appInfo.sourceDir);
}
if (appInfo.splitSourceDirs != null) {
Collections.addAll(codePaths, appInfo.splitSourceDirs);
}
if (codePaths.isEmpty()) { var codePaths = new ArrayList<String>();
// If there are no code paths there's no need to setup a profile file and register with if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) codePaths.add(appInfo.sourceDir);
// the runtime, if (appInfo.splitSourceDirs != null) Collections.addAll(codePaths, appInfo.splitSourceDirs);
return; if (codePaths.isEmpty()) return;
}
var profileDir = HiddenApiBridge.Environment_getDataProfilesDePackageDirectory(appInfo.uid / PER_USER_RANGE, pkgName); var profileDir = HiddenApiBridge.Environment_getDataProfilesDePackageDirectory(appInfo.uid / PER_USER_RANGE, context.getPackageName());
var attrs = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("r--------"));
for (int i = codePaths.size() - 1; i >= 0; i--) { for (int i = codePaths.size() - 1; i >= 0; i--) {
String splitName = i == 0 ? null : appInfo.splitNames[i - 1]; String splitName = i == 0 ? null : appInfo.splitNames[i - 1];
File curProfileFile = new File(profileDir, splitName == null ? "primary.prof" : splitName + ".split.prof").getAbsoluteFile(); File profile = new File(profileDir, splitName == null ? "primary.prof" : splitName + ".split.prof");
Log.d(TAG, "Processing " + curProfileFile.getAbsolutePath());
try { try {
if (!curProfileFile.canWrite() && Files.size(curProfileFile.toPath()) == 0) { // 如果已是 0 字節且唯讀直接跳過
Log.d(TAG, "Skip profile " + curProfileFile.getAbsolutePath()); if (profile.exists() && profile.length() == 0 && !profile.canWrite()) continue;
continue; // 自動將已存在的檔案內容清空或建立新檔
} try (var ignored = new FileOutputStream(profile)) {
if (curProfileFile.exists() && !curProfileFile.delete()) {
try (var writer = new FileOutputStream(curProfileFile)) {
Log.d(TAG, "Failed to delete, try to clear content " + curProfileFile.getAbsolutePath());
} catch (Throwable e) {
Log.e(TAG, "Failed to delete and clear profile file " + curProfileFile.getAbsolutePath(), e);
}
Os.chmod(curProfileFile.getAbsolutePath(), 00400);
} else {
Files.createFile(curProfileFile.toPath(), attrs);
} }
// 設定檔案只讀
Os.chmod(profile.getAbsolutePath(), 00444);
} catch (Throwable e) { } catch (Throwable e) {
Log.e(TAG, "Failed to disable profile file " + curProfileFile.getAbsolutePath(), e); Log.e(TAG, "Failed to disable profile: " + profile.getName(), e);
} }
} }
} }