Obfuscate all classes (#1850)

* Obfuscate more classes

WIP DO NOT USE THIS BRANCH

* Fix resources hook

NOT TESTED

* done

* remode debug code

* use fmt

* obfuscate core & nativebridge

* BridgeService

* update proguard rules

Co-authored-by: 南宫雪珊 <vvb2060@gmail.com>
Co-authored-by: LoveSy <shana@zju.edu.cn>
This commit is contained in:
双草酸酯 2022-04-17 15:51:27 +08:00 committed by GitHub
parent 961676d772
commit a1a9cb4d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 323 additions and 84 deletions

View File

@ -0,0 +1,46 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Kotori0 on 2022/4/14.
//
#ifndef LSPOSED_CONFIGBRIDGE_H
#define LSPOSED_CONFIGBRIDGE_H
#include <map>
using obfuscation_map_t = std::map<std::string, std::string>;
class ConfigBridge {
public:
inline static ConfigBridge *GetInstance() {
return instance_.get();
}
inline static std::unique_ptr<ConfigBridge> ReleaseInstance() {
return std::move(instance_);
}
virtual obfuscation_map_t& obfuscation_map() = 0;
virtual void obfuscation_map(obfuscation_map_t) = 0;
protected:
inline static std::unique_ptr<ConfigBridge> instance_ = nullptr;
};
#endif //LSPOSED_CONFIGBRIDGE_H

View File

@ -15,7 +15,7 @@
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>. * along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
* *
* Copyright (C) 2020 EdXposed Contributors * Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors * Copyright (C) 2021 - 2022 LSPosed Contributors
*/ */
#include <dlfcn.h> #include <dlfcn.h>
@ -31,6 +31,7 @@
#include "logging.h" #include "logging.h"
#include "config.h" #include "config.h"
#include <cassert> #include <cassert>
#include "ConfigBridge.h"
#define _uintval(p) reinterpret_cast<uintptr_t>(p) #define _uintval(p) reinterpret_cast<uintptr_t>(p)
#define _ptr(p) reinterpret_cast<void *>(p) #define _ptr(p) reinterpret_cast<void *>(p)
@ -47,13 +48,13 @@ namespace lspd {
[[gnu::always_inline]] [[gnu::always_inline]]
inline bool RegisterNativeMethodsInternal(JNIEnv *env, inline bool RegisterNativeMethodsInternal(JNIEnv *env,
const char *class_name, std::string_view class_name,
const JNINativeMethod *methods, const JNINativeMethod *methods,
jint method_count) { jint method_count) {
auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name); auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name.data());
if (clazz.get() == nullptr) { if (clazz.get() == nullptr) {
LOGF("Couldn't find class: {}", class_name); LOGF("Couldn't find class: {}", class_name.data());
return false; return false;
} }
return JNI_RegisterNatives(env, clazz, methods, method_count); return JNI_RegisterNatives(env, clazz, methods, method_count);
@ -83,7 +84,7 @@ inline bool RegisterNativeMethodsInternal(JNIEnv *env,
#endif #endif
#define REGISTER_LSP_NATIVE_METHODS(class_name) \ #define REGISTER_LSP_NATIVE_METHODS(class_name) \
RegisterNativeMethodsInternal(env, "org.lsposed.lspd.nativebridge." #class_name, gMethods, arraysize(gMethods)) RegisterNativeMethodsInternal(env, GetNativeBridgeSignature() + #class_name, gMethods, arraysize(gMethods))
inline int HookFunction(void *original, void *replace, void **backup) { inline int HookFunction(void *original, void *replace, void **backup) {
_make_rwx(original, _page_size); _make_rwx(original, _page_size);
@ -108,6 +109,12 @@ inline int UnhookFunction(void *original) {
return DobbyDestroy(original); return DobbyDestroy(original);
} }
static std::string GetNativeBridgeSignature() {
const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();
static auto signature = obfs_map.at("org.lsposed.lspd.nativebridge.");
return signature;
}
} // namespace lspd } // namespace lspd
#pragma clang diagnostic pop #pragma clang diagnostic pop

View File

@ -43,6 +43,11 @@ namespace lspd {
}(); }();
return api_level; return api_level;
} }
inline std::string JavaNameToSignature(std::string s) {
std::replace(s.begin(), s.end(), '.', '/');
return "L" + s;
}
} }
#pragma clang diagnostic pop #pragma clang diagnostic pop

View File

@ -15,7 +15,7 @@
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>. * along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
* *
* Copyright (C) 2020 EdXposed Contributors * Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors * Copyright (C) 2021 - 2022 LSPosed Contributors
*/ */
// //

View File

@ -15,7 +15,7 @@
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>. * along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
* *
* Copyright (C) 2020 EdXposed Contributors * Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors * Copyright (C) 2021 - 2022 LSPosed Contributors
*/ */
#include <jni.h> #include <jni.h>
@ -24,12 +24,11 @@
#include "elf_util.h" #include "elf_util.h"
#include "native_util.h" #include "native_util.h"
#include "resources_hook.h" #include "resources_hook.h"
#include "ConfigBridge.h"
using namespace lsplant; using namespace lsplant;
namespace lspd { namespace lspd {
static constexpr const char *kXResourcesClassName = "android/content/res/XResources";
using TYPE_GET_ATTR_NAME_ID = int32_t (*)(void *, int); using TYPE_GET_ATTR_NAME_ID = int32_t (*)(void *, int);
using TYPE_STRING_AT = char16_t *(*)(const void *, int32_t, size_t *); using TYPE_STRING_AT = char16_t *(*)(const void *, int32_t, size_t *);
@ -46,6 +45,18 @@ namespace lspd {
static TYPE_RESTART ResXMLParser_restart = nullptr; static TYPE_RESTART ResXMLParser_restart = nullptr;
static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr; static TYPE_GET_ATTR_NAME_ID ResXMLParser_getAttributeNameID = nullptr;
static std::string GetXResourcesClassName() {
auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();
if (obfs_map.empty()) {
LOGW("GetXResourcesClassName: obfuscation_map empty?????");
}
static auto name = lspd::JavaNameToSignature(
obfs_map.at("android.content.res.X")) // TODO: kill this hardcoded name
.substr(1) + "Resources";
LOGD("{}", name.c_str());
return name;
}
static bool PrepareSymbols() { static bool PrepareSymbols() {
SandHook::ElfImg fw(kLibFwName); SandHook::ElfImg fw(kLibFwName);
if (!fw.isValid()) { if (!fw.isValid()) {
@ -72,22 +83,23 @@ namespace lspd {
} }
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) { LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
const auto x_resources_class_name = GetXResourcesClassName();
if (auto classXResources_ = Context::GetInstance()->FindClassFromCurrentLoader(env, if (auto classXResources_ = Context::GetInstance()->FindClassFromCurrentLoader(env,
kXResourcesClassName)) { x_resources_class_name)) {
classXResources = JNI_NewGlobalRef(env, classXResources_); classXResources = JNI_NewGlobalRef(env, classXResources_);
} else { } else {
LOGE("Error while loading XResources class '{}':", kXResourcesClassName); LOGE("Error while loading XResources class '{}':", x_resources_class_name);
return JNI_FALSE; return JNI_FALSE;
} }
methodXResourcesTranslateResId = JNI_GetStaticMethodID( methodXResourcesTranslateResId = JNI_GetStaticMethodID(
env, classXResources, "translateResId", env, classXResources, "translateResId",
"(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I"); fmt::format("(IL{};Landroid/content/res/Resources;)I", x_resources_class_name));
if (!methodXResourcesTranslateResId) { if (!methodXResourcesTranslateResId) {
return JNI_FALSE; return JNI_FALSE;
} }
methodXResourcesTranslateAttrId = JNI_GetStaticMethodID( methodXResourcesTranslateAttrId = JNI_GetStaticMethodID(
env, classXResources, "translateAttrId", env, classXResources, "translateAttrId",
"(Ljava/lang/String;Landroid/content/res/XResources;)I"); fmt::format("(Ljava/lang/String;L{};)I", x_resources_class_name));
if (!methodXResourcesTranslateAttrId) { if (!methodXResourcesTranslateAttrId) {
return JNI_FALSE; return JNI_FALSE;
} }
@ -210,6 +222,9 @@ namespace lspd {
}; };
void RegisterResourcesHook(JNIEnv *env) { void RegisterResourcesHook(JNIEnv *env) {
auto sign = fmt::format("(JL{};Landroid/content/res/Resources;)V", GetXResourcesClassName());
gMethods[3].signature = sign.c_str();
REGISTER_LSP_NATIVE_METHODS(ResourcesHook); REGISTER_LSP_NATIVE_METHODS(ResourcesHook);
} }
} }

View File

@ -15,7 +15,7 @@
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>. * along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
* *
* Copyright (C) 2020 EdXposed Contributors * Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors * Copyright (C) 2021 - 2022 LSPosed Contributors
*/ */
#pragma once #pragma once
@ -23,9 +23,6 @@
#include "jni.h" #include "jni.h"
namespace lspd { namespace lspd {
static constexpr uint32_t kAccFinal = 0x0010;
void RegisterResourcesHook(JNIEnv *); void RegisterResourcesHook(JNIEnv *);
} // namespace lspd } // namespace lspd

View File

@ -375,12 +375,14 @@ public class ConfigFileManager {
if (moduleClassNames.isEmpty()) return null; if (moduleClassNames.isEmpty()) return null;
if (obfuscate) { if (obfuscate) {
var signatures = ObfuscationManager.getSignatures();
for (int i = 0; i < moduleClassNames.size(); i++) { for (int i = 0; i < moduleClassNames.size(); i++) {
var s = moduleClassNames.get(i); var s = moduleClassNames.get(i);
var original = ObfuscationManager.getOriginalSignature(); for (String key : signatures.keySet()) {
if (s.startsWith(original)) { if (s.startsWith(key)) {
var obfuscated = ObfuscationManager.getObfuscatedSignature(); // value should not be null, but idk how to ignore this warning
moduleClassNames.add(i, s.replace(original, obfuscated)); moduleClassNames.add(i, s.replace(key, signatures.get(key)));
}
} }
} }
} }

View File

@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class LSPApplicationService extends ILSPApplicationService.Stub { public class LSPApplicationService extends ILSPApplicationService.Stub {
final static int DEX_TRANSACTION_CODE = 1310096052; final static int DEX_TRANSACTION_CODE = 1310096052;
final static int OBFUSCATION_MAP_TRANSACTION_CODE = 724533732;
// key: <uid, pid> // key: <uid, pid>
private final static Map<Pair<Integer, Integer>, ProcessInfo> processes = new ConcurrentHashMap<>(); private final static Map<Pair<Integer, Integer>, ProcessInfo> processes = new ConcurrentHashMap<>();
@ -81,13 +82,24 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
@Override @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Log.d(TAG, "LSPApplicationService.onTransact: code=" + code); Log.d(TAG, "LSPApplicationService.onTransact: code=" + code);
if (code == DEX_TRANSACTION_CODE) { switch (code) {
var shm = ConfigManager.getInstance().getPreloadDex(); case DEX_TRANSACTION_CODE: {
if (shm == null) return false; var shm = ConfigManager.getInstance().getPreloadDex();
// assume that write only a fd if (shm == null) return false;
shm.writeToParcel(reply, 0); // assume that write only a fd
reply.writeLong(shm.getSize()); shm.writeToParcel(reply, 0);
return true; reply.writeLong(shm.getSize());
return true;
}
case OBFUSCATION_MAP_TRANSACTION_CODE: {
var signatures = ObfuscationManager.getSignatures();
reply.writeInt(signatures.size() * 2);
for(Map.Entry<String,String> entry : signatures.entrySet()){
reply.writeString(entry.getKey());
reply.writeString(entry.getValue());
}
return true;
}
} }
return super.onTransact(code, data, reply, flags); return super.onTransact(code, data, reply, flags);
} }

View File

@ -109,6 +109,7 @@ public class LSPSystemServerService extends ILSPSystemServerService.Stub impleme
Log.d(TAG, "LSPSystemServerService.onTransact requestApplicationService rejected"); Log.d(TAG, "LSPSystemServerService.onTransact requestApplicationService rejected");
return false; return false;
} }
case LSPApplicationService.OBFUSCATION_MAP_TRANSACTION_CODE:
case LSPApplicationService.DEX_TRANSACTION_CODE: case LSPApplicationService.DEX_TRANSACTION_CODE:
// Proxy LSP dex transaction to Application Binder // Proxy LSP dex transaction to Application Binder
return ServiceManager.getApplicationService().onTransact(code, data, reply, flags); return ServiceManager.getApplicationService().onTransact(code, data, reply, flags);

View File

@ -2,12 +2,12 @@ package org.lsposed.lspd.service;
import android.os.SharedMemory; import android.os.SharedMemory;
import java.util.HashMap;
public class ObfuscationManager { public class ObfuscationManager {
// For module dexes // For module dexes
static native SharedMemory obfuscateDex(SharedMemory memory); static native SharedMemory obfuscateDex(SharedMemory memory);
// generates signature // generates signature
static native String getObfuscatedSignature(); static native HashMap<String, String> getSignatures();
static native String getOriginalSignature();
} }

View File

@ -39,8 +39,14 @@
using namespace lsplant; using namespace lsplant;
namespace { namespace {
std::mutex init_lock{}; std::mutex init_lock{};
std::string obfuscated_signature; std::map<const std::string, std::string> signatures = {
const std::string original_signature = "Lde/robv/android/xposed/"; {"Lde/robv/android/xposed/", ""},
{ "Landroid/app/AndroidApp", ""},
{ "Landroid/content/res/X", ""},
{ "Lorg/lsposed/lspd/core/", ""},
{ "Lorg/lsposed/lspd/nativebridge/", ""},
{ "Lorg/lsposed/lspd/service/", ""},
};
jclass class_file_descriptor; jclass class_file_descriptor;
jmethodID method_file_descriptor_ctor; jmethodID method_file_descriptor_ctor;
@ -73,7 +79,7 @@ void maybeInit(JNIEnv *env) {
method_shared_memory_ctor = JNI_GetMethodID(env, class_shared_memory, "<init>", "(Ljava/io/FileDescriptor;)V"); method_shared_memory_ctor = JNI_GetMethodID(env, class_shared_memory, "<init>", "(Ljava/io/FileDescriptor;)V");
auto regen = []() { auto regen = [](std::string_view original_signature) {
static auto& chrs = "abcdefghijklmnopqrstuvwxyz" static auto& chrs = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -101,40 +107,72 @@ void maybeInit(JNIEnv *env) {
return out; return out;
}; };
obfuscated_signature = regen(); for (auto &i: signatures) {
i.second = regen(i.first);
LOGD("%s => %s", i.first.c_str(), i.second.c_str());
}
LOGD("ObfuscationManager.getObfuscatedSignature: %s", obfuscated_signature.c_str());
LOGD("ObfuscationManager init successfully"); LOGD("ObfuscationManager init successfully");
inited = true; inited = true;
} }
extern "C" // https://stackoverflow.com/questions/4844022/jni-create-hashmap with modifications
JNIEXPORT jstring JNICALL jobject stringMapToJavaHashMap(JNIEnv *env, const decltype(signatures)& map) {
Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) { jclass mapClass = env->FindClass("java/util/HashMap");
maybeInit(env); if(mapClass == nullptr)
static std::string obfuscated_signature_java = to_java(obfuscated_signature); return nullptr;
return env->NewStringUTF(obfuscated_signature_java.c_str());
jmethodID init = env->GetMethodID(mapClass, "<init>", "()V");
jobject hashMap = env->NewObject(mapClass, init);
jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
auto citr = map.begin();
for( ; citr != map.end(); ++citr) {
jstring keyJava = env->NewStringUTF(citr->first.c_str());
jstring valueJava = env->NewStringUTF(citr->second.c_str());
env->CallObjectMethod(hashMap, put, keyJava, valueJava);
env->DeleteLocalRef(keyJava);
env->DeleteLocalRef(valueJava);
}
auto hashMapGobal = static_cast<jobject>(env->NewGlobalRef(hashMap));
env->DeleteLocalRef(hashMap);
env->DeleteLocalRef(mapClass);
return hashMapGobal;
} }
extern "C" extern "C"
JNIEXPORT jstring JNICALL JNIEXPORT jobject JNICALL
Java_org_lsposed_lspd_service_ObfuscationManager_getOriginalSignature(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) { Java_org_lsposed_lspd_service_ObfuscationManager_getSignatures(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) {
static std::string original_signature_java = to_java(original_signature); maybeInit(env);
return env->NewStringUTF(original_signature_java.c_str()); static jobject signatures_jni = nullptr;
if (signatures_jni) return signatures_jni;
decltype(signatures) signatures_java;
for (const auto &i: signatures) {
signatures_java[to_java(i.first)] = to_java(i.second);
}
signatures_jni = stringMapToJavaHashMap(env, signatures_java);
return signatures_jni;
} }
static int obfuscateDex(const void *dex, size_t size) { static int obfuscateDex(const void *dex, size_t size) {
const char* new_sig = obfuscated_signature.c_str(); // const char* new_sig = obfuscated_signature.c_str();
dex::Reader reader{reinterpret_cast<const dex::u1*>(dex), size}; dex::Reader reader{reinterpret_cast<const dex::u1*>(dex), size};
reader.CreateFullIr(); reader.CreateFullIr();
auto ir = reader.GetIr(); auto ir = reader.GetIr();
for (auto &i: ir->strings) { for (auto &i: ir->strings) {
const char *s = i->c_str(); const char *s = i->c_str();
char* p = const_cast<char *>(strstr(s, original_signature.c_str())); for (const auto &signature: signatures) {
if (p) { char* p = const_cast<char *>(strstr(s, signature.first.c_str()));
// NOLINTNEXTLINE bugprone-not-null-terminated-result if (p) {
memcpy(p, new_sig, strlen(new_sig)); auto new_sig = signature.second.c_str();
// NOLINTNEXTLINE bugprone-not-null-terminated-result
memcpy(p, new_sig, strlen(new_sig));
}
} }
} }
dex::Writer writer(ir); dex::Writer writer(ir);

View File

@ -24,6 +24,7 @@
#include <array> #include <array>
#include "logging.h" #include "logging.h"
#include "loader.h" #include "loader.h"
#include "ConfigImpl.h"
#include "magisk_loader.h" #include "magisk_loader.h"
#include "symbol_cache.h" #include "symbol_cache.h"
@ -42,6 +43,7 @@ namespace lspd {
LOGI("onModuleLoaded: version v{} ({})", versionName, versionCode); LOGI("onModuleLoaded: version v{} ({})", versionName, versionCode);
InitSymbolCache(nullptr); InitSymbolCache(nullptr);
MagiskLoader::Init(); MagiskLoader::Init();
ConfigImpl::Init();
} }
void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *, void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *,

View File

@ -25,6 +25,7 @@
#include "zygisk.h" #include "zygisk.h"
#include "logging.h" #include "logging.h"
#include "loader.h" #include "loader.h"
#include "ConfigImpl.h"
#include "magisk_loader.h" #include "magisk_loader.h"
#include "symbol_cache.h" #include "symbol_cache.h"
@ -282,6 +283,7 @@ namespace lspd {
env_ = env; env_ = env;
api_ = api; api_ = api;
MagiskLoader::Init(); MagiskLoader::Init();
ConfigImpl::Init();
auto companion = api->connectCompanion(); auto companion = api->connectCompanion();
if (companion == -1) { if (companion == -1) {

View File

@ -26,9 +26,6 @@
#include "config.h" #include "config.h"
namespace lspd { namespace lspd {
inline static constexpr auto kEntryClassName = "org.lsposed.lspd.core.Main"_tstr;
inline static constexpr auto kBridgeServiceClassName = "org.lsposed.lspd.service.BridgeService"_tstr;
extern const int apiVersion; extern const int apiVersion;
extern const char* const moduleName; extern const char* const moduleName;
} }

View File

@ -0,0 +1,43 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Kotori0 on 2022/4/14.
//
#ifndef LSPOSED_CONFIGIMPL_H
#define LSPOSED_CONFIGIMPL_H
#include "ConfigBridge.h"
#include "service.h"
class ConfigImpl: public ConfigBridge {
public:
inline static void Init() {
instance_ = std::make_unique<ConfigImpl>();
}
virtual obfuscation_map_t& obfuscation_map() override { return obfuscation_map_; }
virtual void obfuscation_map(obfuscation_map_t m) override { obfuscation_map_ = std::move(m); }
private:
inline static std::map<std::string, std::string> obfuscation_map_;
};
#endif //LSPOSED_CONFIGIMPL_H

View File

@ -22,6 +22,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "ConfigImpl.h"
#include "elf_util.h" #include "elf_util.h"
#include "loader.h" #include "loader.h"
#include "magisk_loader.h" #include "magisk_loader.h"
@ -73,9 +74,15 @@ namespace lspd {
env->DeleteLocalRef(dex_buffer); env->DeleteLocalRef(dex_buffer);
} }
std::string GetEntryClassName() {
const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();
static auto signature = obfs_map.at("org.lsposed.lspd.core.") + "Main";
return signature;
}
void MagiskLoader::SetupEntryClass(JNIEnv *env) { void MagiskLoader::SetupEntryClass(JNIEnv *env) {
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
kEntryClassName)) { GetEntryClassName())) {
entry_class_ = JNI_NewGlobalRef(env, entry_class); entry_class_ = JNI_NewGlobalRef(env, entry_class);
} }
} }
@ -104,7 +111,10 @@ namespace lspd {
// Call application_binder directly if application binder is available, // Call application_binder directly if application binder is available,
// or we proxy the request from system server binder // or we proxy the request from system server binder
auto [dex_fd, size]= instance->RequestLSPDex(env, application_binder ? application_binder : system_server_binder); const auto next_binder = std::move(application_binder ? application_binder : system_server_binder);
const auto [dex_fd, size] = instance->RequestLSPDex(env, next_binder);
auto obfs_map = instance->RequestObfuscationMap(env, next_binder);
ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));
LoadDex(env, PreloadedDex(dex_fd, size)); LoadDex(env, PreloadedDex(dex_fd, size));
close(dex_fd); close(dex_fd);
instance->HookBridge(*this, env); instance->HookBridge(*this, env);
@ -196,6 +206,8 @@ namespace lspd {
}; };
InstallInlineHooks(env, initInfo); InstallInlineHooks(env, initInfo);
auto [dex_fd, size] = instance->RequestLSPDex(env, binder); auto [dex_fd, size] = instance->RequestLSPDex(env, binder);
auto obfs_map = instance->RequestObfuscationMap(env, binder);
ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map));
LoadDex(env, PreloadedDex(dex_fd, size)); LoadDex(env, PreloadedDex(dex_fd, size));
close(dex_fd); close(dex_fd);
InitHooks(env, initInfo); InitHooks(env, initInfo);

View File

@ -28,6 +28,7 @@
#include "context.h" #include "context.h"
#include "utils/jni_helper.hpp" #include "utils/jni_helper.hpp"
#include "symbol_cache.h" #include "symbol_cache.h"
#include "ConfigBridge.h"
using namespace lsplant; using namespace lsplant;
@ -108,6 +109,7 @@ namespace lspd {
if (auto parcel_class = JNI_FindClass(env, "android/os/Parcel")) { if (auto parcel_class = JNI_FindClass(env, "android/os/Parcel")) {
parcel_class_ = JNI_NewGlobalRef(env, parcel_class); parcel_class_ = JNI_NewGlobalRef(env, parcel_class);
} else return; } else return;
data_size_method_ = JNI_GetMethodID(env, parcel_class_, "dataSize","()I");
obtain_method_ = JNI_GetStaticMethodID(env, parcel_class_, "obtain", obtain_method_ = JNI_GetStaticMethodID(env, parcel_class_, "obtain",
"()Landroid/os/Parcel;"); "()Landroid/os/Parcel;");
recycleMethod_ = JNI_GetMethodID(env, parcel_class_, "recycle", "()V"); recycleMethod_ = JNI_GetMethodID(env, parcel_class_, "recycle", "()V");
@ -119,6 +121,7 @@ namespace lspd {
write_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "writeStrongBinder", write_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "writeStrongBinder",
"(Landroid/os/IBinder;)V"); "(Landroid/os/IBinder;)V");
read_exception_method_ = JNI_GetMethodID(env, parcel_class_, "readException", "()V"); read_exception_method_ = JNI_GetMethodID(env, parcel_class_, "readException", "()V");
read_int_method_ = JNI_GetMethodID(env, parcel_class_, "readInt", "()I");
read_long_method_ = JNI_GetMethodID(env, parcel_class_, "readLong", "()J"); read_long_method_ = JNI_GetMethodID(env, parcel_class_, "readLong", "()J");
read_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "readStrongBinder", read_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "readStrongBinder",
"()Landroid/os/IBinder;"); "()Landroid/os/IBinder;");
@ -144,6 +147,12 @@ namespace lspd {
initialized_ = true; initialized_ = true;
} }
std::string GetBridgeServiceName() {
const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map();
static auto signature = obfs_map.at("org.lsposed.lspd.service.") + "BridgeService";
return signature;
}
void Service::HookBridge(const Context &context, JNIEnv *env) { void Service::HookBridge(const Context &context, JNIEnv *env) {
static bool kHooked = false; static bool kHooked = false;
// This should only be ran once, so unlikely // This should only be ran once, so unlikely
@ -151,7 +160,7 @@ namespace lspd {
if (!initialized_) [[unlikely]] return; if (!initialized_) [[unlikely]] return;
kHooked = true; kHooked = true;
if (auto bridge_service_class = context.FindClassFromCurrentLoader(env, if (auto bridge_service_class = context.FindClassFromCurrentLoader(env,
kBridgeServiceClassName)) GetBridgeServiceName()))
bridge_service_class_ = JNI_NewGlobalRef(env, bridge_service_class); bridge_service_class_ = JNI_NewGlobalRef(env, bridge_service_class);
else { else {
LOGE("server class not found"); LOGE("server class not found");
@ -284,26 +293,20 @@ namespace lspd {
ScopedLocalRef<jobject> Service::RequestApplicationBinderFromSystemServer(JNIEnv *env, const ScopedLocalRef<jobject> &system_server_binder) { ScopedLocalRef<jobject> Service::RequestApplicationBinderFromSystemServer(JNIEnv *env, const ScopedLocalRef<jobject> &system_server_binder) {
auto heart_beat_binder = JNI_NewObject(env, binder_class_, binder_ctor_); auto heart_beat_binder = JNI_NewObject(env, binder_class_, binder_ctor_);
auto data = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_); Wrapper wrapper{env, this};
auto reply = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_);
JNI_CallVoidMethod(env, data, write_int_method_, getuid()); JNI_CallVoidMethod(env, wrapper.data, write_int_method_, getuid());
JNI_CallVoidMethod(env, data, write_int_method_, getpid()); JNI_CallVoidMethod(env, wrapper.data, write_int_method_, getpid());
JNI_CallVoidMethod(env, data, write_string_method_, JNI_NewStringUTF(env, "android")); JNI_CallVoidMethod(env, wrapper.data, write_string_method_, JNI_NewStringUTF(env, "android"));
JNI_CallVoidMethod(env, data, write_strong_binder_method_, heart_beat_binder); JNI_CallVoidMethod(env, wrapper.data, write_strong_binder_method_, heart_beat_binder);
auto res = JNI_CallBooleanMethod(env, system_server_binder, transact_method_, auto res = wrapper.transact(system_server_binder, BRIDGE_TRANSACTION_CODE);
BRIDGE_TRANSACTION_CODE,
data,
reply, 0);
ScopedLocalRef<jobject> app_binder = {env, nullptr}; ScopedLocalRef<jobject> app_binder = {env, nullptr};
if (res) { if (res) {
JNI_CallVoidMethod(env, reply, read_exception_method_); JNI_CallVoidMethod(env, wrapper.reply, read_exception_method_);
app_binder = JNI_CallObjectMethod(env, reply, read_strong_binder_method_); app_binder = JNI_CallObjectMethod(env, wrapper.reply, read_strong_binder_method_);
} }
JNI_CallVoidMethod(env, data, recycleMethod_);
JNI_CallVoidMethod(env, reply, recycleMethod_);
if (app_binder) { if (app_binder) {
JNI_NewGlobalRef(env, heart_beat_binder); JNI_NewGlobalRef(env, heart_beat_binder);
} }
@ -312,23 +315,49 @@ namespace lspd {
} }
std::tuple<int, size_t> Service::RequestLSPDex(JNIEnv *env, const ScopedLocalRef<jobject> &binder) { std::tuple<int, size_t> Service::RequestLSPDex(JNIEnv *env, const ScopedLocalRef<jobject> &binder) {
auto data = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_); Wrapper wrapper{env, this};
auto reply = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_); bool res = wrapper.transact(binder, DEX_TRANSACTION_CODE);
auto res = JNI_CallBooleanMethod(env, binder, transact_method_,
DEX_TRANSACTION_CODE,
data,
reply, 0);
if (!res) { if (!res) {
LOGE("Service::RequestLSPDex: transaction failed?"); LOGE("Service::RequestLSPDex: transaction failed?");
return {-1, 0}; return {-1, 0};
} }
auto parcel_fd = JNI_CallObjectMethod(env, reply, read_file_descriptor_method_); auto parcel_fd = JNI_CallObjectMethod(env, wrapper.reply, read_file_descriptor_method_);
int fd = JNI_CallIntMethod(env, parcel_fd, detach_fd_method_); int fd = JNI_CallIntMethod(env, parcel_fd, detach_fd_method_);
auto size = static_cast<size_t>(JNI_CallLongMethod(env, reply, read_long_method_)); auto size = static_cast<size_t>(JNI_CallLongMethod(env, wrapper.reply, read_long_method_));
JNI_CallVoidMethod(env, data, recycleMethod_); LOGD("fd={}, size={}", fd, size);
JNI_CallVoidMethod(env, reply, recycleMethod_);
LOGD("Service::RequestLSPDex fd={}, size={}", fd, size);
return {fd, size}; return {fd, size};
} }
std::map<std::string, std::string>
Service::RequestObfuscationMap(JNIEnv *env, const ScopedLocalRef<jobject> &binder) {
std::map<std::string, std::string> ret;
Wrapper wrapper{env, this};
bool res = wrapper.transact(binder, OBFUSCATION_MAP_TRANSACTION_CODE);
if (!res) {
LOGE("Service::RequestObfuscationMap: transaction failed?");
return ret;
}
auto size = JNI_CallIntMethod(env, wrapper.reply, read_int_method_);
if (!size || (size & 1) == 1) {
LOGW("Service::RequestObfuscationMap: invalid parcel size");
}
auto get_string = [this, &wrapper, &env]() -> std::string {
auto s = JNI_Cast<jstring>(JNI_CallObjectMethod(env, wrapper.reply, read_string_method_));
return JUTFString(s);
};
for (auto i = 0; i < size / 2; i++) {
// DO NOT TOUCH, or value evaluates before key.
auto &&key = get_string();
ret[key] = get_string();
}
#ifndef NDEBUG
for (const auto &i: ret) {
LOGD("{} => {}", i.first, i.second);
}
#endif
return ret;
}
} // namespace lspd } // namespace lspd

View File

@ -24,6 +24,7 @@
#ifndef LSPOSED_SERVICE_H #ifndef LSPOSED_SERVICE_H
#define LSPOSED_SERVICE_H #define LSPOSED_SERVICE_H
#include <map>
#include <jni.h> #include <jni.h>
#include "context.h" #include "context.h"
@ -32,6 +33,7 @@ using namespace std::literals::string_view_literals;
namespace lspd { namespace lspd {
class Service { class Service {
constexpr static jint DEX_TRANSACTION_CODE = 1310096052; constexpr static jint DEX_TRANSACTION_CODE = 1310096052;
constexpr static jint OBFUSCATION_MAP_TRANSACTION_CODE = 724533732;
constexpr static jint BRIDGE_TRANSACTION_CODE = 1598837584; constexpr static jint BRIDGE_TRANSACTION_CODE = 1598837584;
constexpr static auto BRIDGE_SERVICE_DESCRIPTOR = "LSPosed"sv; constexpr static auto BRIDGE_SERVICE_DESCRIPTOR = "LSPosed"sv;
constexpr static auto BRIDGE_SERVICE_NAME = "activity"sv; constexpr static auto BRIDGE_SERVICE_NAME = "activity"sv;
@ -39,6 +41,31 @@ namespace lspd {
constexpr static jint BRIDGE_ACTION_GET_BINDER = 2; constexpr static jint BRIDGE_ACTION_GET_BINDER = 2;
inline static jint SET_ACTIVITY_CONTROLLER_CODE = -1; inline static jint SET_ACTIVITY_CONTROLLER_CODE = -1;
class Wrapper {
public:
Service* service_;
JNIEnv *env_;
lsplant::ScopedLocalRef<jobject> data;
lsplant::ScopedLocalRef<jobject> reply;
Wrapper(JNIEnv *env, Service* service) :
service_(service),
env_(env),
data(lsplant::JNI_CallStaticObjectMethod(env, service->parcel_class_, service->obtain_method_)),
reply(lsplant::JNI_CallStaticObjectMethod(env, service->parcel_class_, service->obtain_method_))
{}
inline bool transact(const lsplant::ScopedLocalRef<jobject> &binder, jint transaction_code) {
return JNI_CallBooleanMethod(env_, binder, service_->transact_method_,transaction_code,
data, reply, 0);
}
inline ~Wrapper() {
JNI_CallVoidMethod(env_, data, service_->recycleMethod_);
JNI_CallVoidMethod(env_, reply, service_->recycleMethod_);
}
};
public: public:
inline static Service* instance() { inline static Service* instance() {
return instance_.get(); return instance_.get();
@ -59,6 +86,8 @@ namespace lspd {
std::tuple<int, size_t> RequestLSPDex(JNIEnv *env, const lsplant::ScopedLocalRef<jobject> &binder); std::tuple<int, size_t> RequestLSPDex(JNIEnv *env, const lsplant::ScopedLocalRef<jobject> &binder);
std::map<std::string, std::string> RequestObfuscationMap(JNIEnv *env, const lsplant::ScopedLocalRef<jobject> &binder);
private: private:
inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>(); inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>();
bool initialized_ = false; bool initialized_ = false;
@ -90,6 +119,7 @@ namespace lspd {
jmethodID transact_method_ = nullptr; jmethodID transact_method_ = nullptr;
jclass parcel_class_ = nullptr; jclass parcel_class_ = nullptr;
jmethodID data_size_method_ = nullptr;
jmethodID obtain_method_ = nullptr; jmethodID obtain_method_ = nullptr;
jmethodID recycleMethod_ = nullptr; jmethodID recycleMethod_ = nullptr;
jmethodID write_interface_token_method_ = nullptr; jmethodID write_interface_token_method_ = nullptr;
@ -99,6 +129,7 @@ namespace lspd {
jmethodID read_strong_binder_method_ = nullptr; jmethodID read_strong_binder_method_ = nullptr;
jmethodID write_strong_binder_method_ = nullptr; jmethodID write_strong_binder_method_ = nullptr;
jmethodID read_file_descriptor_method_ = nullptr; jmethodID read_file_descriptor_method_ = nullptr;
jmethodID read_int_method_ = nullptr;
jmethodID read_long_method_ = nullptr; jmethodID read_long_method_ = nullptr;
jmethodID read_string_method_ = nullptr; jmethodID read_string_method_ = nullptr;

@ -1 +1 @@
Subproject commit e183a459a23d8825f495caff30b81d88e56fc65b Subproject commit ac9744ab71bc899c7954239fadbad605521c4b0c