Parallize module cache and preload (#1681)
This commit is contained in:
parent
26f414ff03
commit
36d1f8dba7
|
|
@ -35,8 +35,23 @@
|
||||||
#include "slicer/writer.h"
|
#include "slicer/writer.h"
|
||||||
#include "obfuscation.h"
|
#include "obfuscation.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::mutex init_lock{};
|
||||||
|
std::string obfuscated_signature;
|
||||||
|
const std::string old_signature = "Lde/robv/android/xposed";
|
||||||
|
|
||||||
|
jclass class_file_descriptor;
|
||||||
|
jmethodID method_file_descriptor_ctor;
|
||||||
|
|
||||||
|
jclass class_shared_memory;
|
||||||
|
jmethodID method_shared_memory_ctor;
|
||||||
|
|
||||||
|
bool inited = false;
|
||||||
|
}
|
||||||
|
|
||||||
void maybeInit(JNIEnv *env) {
|
void maybeInit(JNIEnv *env) {
|
||||||
if (inited.test_and_set(std::memory_order_acq_rel)) [[likely]] return;
|
if (inited) [[likely]] return;
|
||||||
|
std::lock_guard l(init_lock);
|
||||||
LOGD("ObfuscationManager.init");
|
LOGD("ObfuscationManager.init");
|
||||||
if (auto file_descriptor = JNI_FindClass(env, "java/io/FileDescriptor")) {
|
if (auto file_descriptor = JNI_FindClass(env, "java/io/FileDescriptor")) {
|
||||||
class_file_descriptor = JNI_NewGlobalRef(env, file_descriptor);
|
class_file_descriptor = JNI_NewGlobalRef(env, file_descriptor);
|
||||||
|
|
@ -96,6 +111,7 @@ void maybeInit(JNIEnv *env) {
|
||||||
|
|
||||||
LOGD("ObfuscationManager.getObfuscatedSignature: %s", obfuscated_signature.c_str());
|
LOGD("ObfuscationManager.getObfuscatedSignature: %s", obfuscated_signature.c_str());
|
||||||
LOGD("ObfuscationManager init successfully");
|
LOGD("ObfuscationManager init successfully");
|
||||||
|
inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "jni_helper.h"
|
#include "jni_helper.h"
|
||||||
|
|
||||||
static std::string obfuscated_signature;
|
|
||||||
static const std::string old_signature = "Lde/robv/android/xposed";
|
|
||||||
|
|
||||||
static std::atomic_flag inited;
|
|
||||||
|
|
||||||
static jclass class_file_descriptor;
|
|
||||||
static jmethodID method_file_descriptor_ctor;
|
|
||||||
|
|
||||||
static jclass class_shared_memory;
|
|
||||||
static jmethodID method_shared_memory_ctor;
|
|
||||||
|
|
||||||
class WA: public dex::Writer::Allocator {
|
class WA: public dex::Writer::Allocator {
|
||||||
// addr: {size, fd}
|
// addr: {size, fd}
|
||||||
std::unordered_map<void*, std::pair<size_t, int>> allocated_;
|
std::unordered_map<void*, std::pair<size_t, int>> allocated_;
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
@ -196,25 +197,23 @@ public class ConfigManager {
|
||||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||||
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
var path = cursor.getString(apkPathIdx);
|
|
||||||
var packageName = cursor.getString(pkgNameIdx);
|
|
||||||
var m = cachedModule.computeIfAbsent(packageName, p -> {
|
|
||||||
var module = new Module();
|
var module = new Module();
|
||||||
var file = ConfigFileManager.loadModule(path, dexObfuscate);
|
module.apkPath = cursor.getString(apkPathIdx);
|
||||||
if (file == null) {
|
|
||||||
Log.w(TAG, "Can not load " + path + ", skip!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
module.packageName = cursor.getString(pkgNameIdx);
|
module.packageName = cursor.getString(pkgNameIdx);
|
||||||
module.apkPath = path;
|
|
||||||
module.file = file;
|
|
||||||
module.appId = -1;
|
module.appId = -1;
|
||||||
return module;
|
modules.add(module);
|
||||||
});
|
|
||||||
if (m != null) modules.add(m);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modules;
|
return modules.parallelStream().filter(m -> {
|
||||||
|
var file = ConfigFileManager.loadModule(m.apkPath, dexObfuscate);
|
||||||
|
if (file == null) {
|
||||||
|
Log.w(TAG, "Can not load " + m.apkPath + ", skip!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m.file = file;
|
||||||
|
cachedModule.put(m.packageName, m);
|
||||||
|
return true;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateConfig() {
|
private synchronized void updateConfig() {
|
||||||
|
|
@ -455,9 +454,9 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
int pkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||||
Set<String> obsoleteModules = new HashSet<>();
|
Set<String> obsoleteModules = ConcurrentHashMap.newKeySet();
|
||||||
// packageName, apkPath
|
// packageName, apkPath
|
||||||
Map<String, String> obsoletePaths = new HashMap<>();
|
Map<String, String> obsoletePaths = new ConcurrentHashMap<>();
|
||||||
cachedModule.values().removeIf(m -> {
|
cachedModule.values().removeIf(m -> {
|
||||||
if (m.apkPath == null || !existsInGlobalNamespace(m.apkPath)) {
|
if (m.apkPath == null || !existsInGlobalNamespace(m.apkPath)) {
|
||||||
toClose.addAll(m.file.preLoadedDexes);
|
toClose.addAll(m.file.preLoadedDexes);
|
||||||
|
|
@ -465,52 +464,60 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
List<Module> modules = new ArrayList<>();
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
String packageName = cursor.getString(pkgNameIdx);
|
String packageName = cursor.getString(pkgNameIdx);
|
||||||
String apkPath = cursor.getString(apkPathIdx);
|
String apkPath = cursor.getString(apkPathIdx);
|
||||||
if (packageName.equals("lspd")) continue;
|
if (packageName.equals("lspd")) continue;
|
||||||
// if still present after removeIf, this package did not change.
|
var module = new Module();
|
||||||
var oldModule = cachedModule.get(packageName);
|
module.packageName = packageName;
|
||||||
|
module.apkPath = apkPath;
|
||||||
|
modules.add(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
modules.stream().parallel().filter(m -> {
|
||||||
|
var oldModule = cachedModule.get(m.packageName);
|
||||||
PackageInfo pkgInfo = null;
|
PackageInfo pkgInfo = null;
|
||||||
try {
|
try {
|
||||||
pkgInfo = PackageService.getPackageInfoFromAllUsers(packageName, MATCH_ALL_FLAGS).values().stream().findFirst().orElse(null);
|
pkgInfo = PackageService.getPackageInfoFromAllUsers(m.packageName, MATCH_ALL_FLAGS).values().stream().findFirst().orElse(null);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.w(TAG, "get package info of " + packageName, e);
|
Log.w(TAG, "get package info of " + m.packageName, e);
|
||||||
}
|
}
|
||||||
if (pkgInfo == null || pkgInfo.applicationInfo == null) {
|
if (pkgInfo == null || pkgInfo.applicationInfo == null) {
|
||||||
obsoleteModules.add(packageName);
|
obsoleteModules.add(m.packageName);
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldModule != null &&
|
if (oldModule != null &&
|
||||||
pkgInfo.applicationInfo.sourceDir != null &&
|
pkgInfo.applicationInfo.sourceDir != null &&
|
||||||
apkPath != null && oldModule.apkPath != null &&
|
m.apkPath != null && oldModule.apkPath != null &&
|
||||||
existsInGlobalNamespace(apkPath) &&
|
existsInGlobalNamespace(m.apkPath) &&
|
||||||
Objects.equals(apkPath, oldModule.apkPath) &&
|
Objects.equals(m.apkPath, oldModule.apkPath) &&
|
||||||
Objects.equals(new File(pkgInfo.applicationInfo.sourceDir).getParent(), new File(apkPath).getParent())) {
|
Objects.equals(new File(pkgInfo.applicationInfo.sourceDir).getParent(), new File(m.apkPath).getParent())) {
|
||||||
if (oldModule.appId != -1) {
|
if (oldModule.appId != -1) {
|
||||||
Log.d(TAG, packageName + " did not change, skip caching it");
|
Log.d(TAG, m.packageName + " did not change, skip caching it");
|
||||||
} else {
|
} else {
|
||||||
// cache from system server, keep it and set only the appId
|
// cache from system server, keep it and set only the appId
|
||||||
oldModule.appId = pkgInfo.applicationInfo.uid;
|
oldModule.appId = pkgInfo.applicationInfo.uid;
|
||||||
}
|
}
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
apkPath = getModuleApkPath(pkgInfo.applicationInfo);
|
m.apkPath = getModuleApkPath(pkgInfo.applicationInfo);
|
||||||
if (apkPath == null) obsoleteModules.add(packageName);
|
if (m.apkPath == null) obsoleteModules.add(m.packageName);
|
||||||
else obsoletePaths.put(packageName, apkPath);
|
else obsoletePaths.put(m.packageName, m.apkPath);
|
||||||
var file = ConfigFileManager.loadModule(apkPath, dexObfuscate);
|
m.appId = pkgInfo.applicationInfo.uid;
|
||||||
|
return true;
|
||||||
|
}).forEach(m -> {
|
||||||
|
var file = ConfigFileManager.loadModule(m.apkPath, dexObfuscate);
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
Log.w(TAG, "failed to load module " + packageName);
|
Log.w(TAG, "failed to load module " + m.packageName);
|
||||||
obsoleteModules.add(packageName);
|
obsoleteModules.add(m.packageName);
|
||||||
continue;
|
return;
|
||||||
}
|
|
||||||
var module = new Module();
|
|
||||||
module.apkPath = apkPath;
|
|
||||||
module.packageName = packageName;
|
|
||||||
module.file = file;
|
|
||||||
module.appId = pkgInfo.applicationInfo.uid;
|
|
||||||
cachedModule.put(packageName, module);
|
|
||||||
}
|
}
|
||||||
|
m.file = file;
|
||||||
|
cachedModule.put(m.packageName, m);
|
||||||
|
});
|
||||||
|
|
||||||
if (PackageService.isAlive()) {
|
if (PackageService.isAlive()) {
|
||||||
obsoleteModules.forEach(this::removeModuleWithoutCache);
|
obsoleteModules.forEach(this::removeModuleWithoutCache);
|
||||||
obsoletePaths.forEach((packageName, path) -> updateModuleApkPath(packageName, path, true));
|
obsoletePaths.forEach((packageName, path) -> updateModuleApkPath(packageName, path, true));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue