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:
parent
961676d772
commit
a1a9cb4d1b
|
|
@ -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
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2020 EdXposed Contributors
|
||||
* Copyright (C) 2021 LSPosed Contributors
|
||||
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include "logging.h"
|
||||
#include "config.h"
|
||||
#include <cassert>
|
||||
#include "ConfigBridge.h"
|
||||
|
||||
#define _uintval(p) reinterpret_cast<uintptr_t>(p)
|
||||
#define _ptr(p) reinterpret_cast<void *>(p)
|
||||
|
|
@ -47,13 +48,13 @@ namespace lspd {
|
|||
|
||||
[[gnu::always_inline]]
|
||||
inline bool RegisterNativeMethodsInternal(JNIEnv *env,
|
||||
const char *class_name,
|
||||
std::string_view class_name,
|
||||
const JNINativeMethod *methods,
|
||||
jint method_count) {
|
||||
|
||||
auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name);
|
||||
auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name.data());
|
||||
if (clazz.get() == nullptr) {
|
||||
LOGF("Couldn't find class: {}", class_name);
|
||||
LOGF("Couldn't find class: {}", class_name.data());
|
||||
return false;
|
||||
}
|
||||
return JNI_RegisterNatives(env, clazz, methods, method_count);
|
||||
|
|
@ -83,7 +84,7 @@ inline bool RegisterNativeMethodsInternal(JNIEnv *env,
|
|||
#endif
|
||||
|
||||
#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) {
|
||||
_make_rwx(original, _page_size);
|
||||
|
|
@ -108,6 +109,12 @@ inline int UnhookFunction(void *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
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
|
|
|||
|
|
@ -43,6 +43,11 @@ namespace lspd {
|
|||
}();
|
||||
return api_level;
|
||||
}
|
||||
|
||||
inline std::string JavaNameToSignature(std::string s) {
|
||||
std::replace(s.begin(), s.end(), '.', '/');
|
||||
return "L" + s;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2020 EdXposed Contributors
|
||||
* Copyright (C) 2021 LSPosed Contributors
|
||||
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2020 EdXposed Contributors
|
||||
* Copyright (C) 2021 LSPosed Contributors
|
||||
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
|
|
@ -24,12 +24,11 @@
|
|||
#include "elf_util.h"
|
||||
#include "native_util.h"
|
||||
#include "resources_hook.h"
|
||||
#include "ConfigBridge.h"
|
||||
|
||||
using namespace lsplant;
|
||||
|
||||
namespace lspd {
|
||||
static constexpr const char *kXResourcesClassName = "android/content/res/XResources";
|
||||
|
||||
using TYPE_GET_ATTR_NAME_ID = int32_t (*)(void *, int);
|
||||
|
||||
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_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() {
|
||||
SandHook::ElfImg fw(kLibFwName);
|
||||
if (!fw.isValid()) {
|
||||
|
|
@ -72,22 +83,23 @@ namespace lspd {
|
|||
}
|
||||
|
||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
|
||||
const auto x_resources_class_name = GetXResourcesClassName();
|
||||
if (auto classXResources_ = Context::GetInstance()->FindClassFromCurrentLoader(env,
|
||||
kXResourcesClassName)) {
|
||||
x_resources_class_name)) {
|
||||
classXResources = JNI_NewGlobalRef(env, classXResources_);
|
||||
} else {
|
||||
LOGE("Error while loading XResources class '{}':", kXResourcesClassName);
|
||||
LOGE("Error while loading XResources class '{}':", x_resources_class_name);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
methodXResourcesTranslateResId = JNI_GetStaticMethodID(
|
||||
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) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
methodXResourcesTranslateAttrId = JNI_GetStaticMethodID(
|
||||
env, classXResources, "translateAttrId",
|
||||
"(Ljava/lang/String;Landroid/content/res/XResources;)I");
|
||||
fmt::format("(Ljava/lang/String;L{};)I", x_resources_class_name));
|
||||
if (!methodXResourcesTranslateAttrId) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
|
@ -210,6 +222,9 @@ namespace lspd {
|
|||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2020 EdXposed Contributors
|
||||
* Copyright (C) 2021 LSPosed Contributors
|
||||
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -23,9 +23,6 @@
|
|||
#include "jni.h"
|
||||
|
||||
namespace lspd {
|
||||
|
||||
static constexpr uint32_t kAccFinal = 0x0010;
|
||||
|
||||
void RegisterResourcesHook(JNIEnv *);
|
||||
|
||||
} // namespace lspd
|
||||
|
|
|
|||
|
|
@ -375,12 +375,14 @@ public class ConfigFileManager {
|
|||
if (moduleClassNames.isEmpty()) return null;
|
||||
|
||||
if (obfuscate) {
|
||||
var signatures = ObfuscationManager.getSignatures();
|
||||
for (int i = 0; i < moduleClassNames.size(); i++) {
|
||||
var s = moduleClassNames.get(i);
|
||||
var original = ObfuscationManager.getOriginalSignature();
|
||||
if (s.startsWith(original)) {
|
||||
var obfuscated = ObfuscationManager.getObfuscatedSignature();
|
||||
moduleClassNames.add(i, s.replace(original, obfuscated));
|
||||
for (String key : signatures.keySet()) {
|
||||
if (s.startsWith(key)) {
|
||||
// value should not be null, but idk how to ignore this warning
|
||||
moduleClassNames.add(i, s.replace(key, signatures.get(key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
public class LSPApplicationService extends ILSPApplicationService.Stub {
|
||||
final static int DEX_TRANSACTION_CODE = 1310096052;
|
||||
final static int OBFUSCATION_MAP_TRANSACTION_CODE = 724533732;
|
||||
// key: <uid, pid>
|
||||
private final static Map<Pair<Integer, Integer>, ProcessInfo> processes = new ConcurrentHashMap<>();
|
||||
|
||||
|
|
@ -81,13 +82,24 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
|
|||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
Log.d(TAG, "LSPApplicationService.onTransact: code=" + code);
|
||||
if (code == DEX_TRANSACTION_CODE) {
|
||||
var shm = ConfigManager.getInstance().getPreloadDex();
|
||||
if (shm == null) return false;
|
||||
// assume that write only a fd
|
||||
shm.writeToParcel(reply, 0);
|
||||
reply.writeLong(shm.getSize());
|
||||
return true;
|
||||
switch (code) {
|
||||
case DEX_TRANSACTION_CODE: {
|
||||
var shm = ConfigManager.getInstance().getPreloadDex();
|
||||
if (shm == null) return false;
|
||||
// assume that write only a fd
|
||||
shm.writeToParcel(reply, 0);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public class LSPSystemServerService extends ILSPSystemServerService.Stub impleme
|
|||
Log.d(TAG, "LSPSystemServerService.onTransact requestApplicationService rejected");
|
||||
return false;
|
||||
}
|
||||
case LSPApplicationService.OBFUSCATION_MAP_TRANSACTION_CODE:
|
||||
case LSPApplicationService.DEX_TRANSACTION_CODE:
|
||||
// Proxy LSP dex transaction to Application Binder
|
||||
return ServiceManager.getApplicationService().onTransact(code, data, reply, flags);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package org.lsposed.lspd.service;
|
|||
|
||||
import android.os.SharedMemory;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ObfuscationManager {
|
||||
// For module dexes
|
||||
static native SharedMemory obfuscateDex(SharedMemory memory);
|
||||
|
||||
// generates signature
|
||||
static native String getObfuscatedSignature();
|
||||
|
||||
static native String getOriginalSignature();
|
||||
static native HashMap<String, String> getSignatures();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,14 @@
|
|||
using namespace lsplant;
|
||||
namespace {
|
||||
std::mutex init_lock{};
|
||||
std::string obfuscated_signature;
|
||||
const std::string original_signature = "Lde/robv/android/xposed/";
|
||||
std::map<const std::string, std::string> signatures = {
|
||||
{"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;
|
||||
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");
|
||||
|
||||
auto regen = []() {
|
||||
auto regen = [](std::string_view original_signature) {
|
||||
static auto& chrs = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
|
|
@ -101,40 +107,72 @@ void maybeInit(JNIEnv *env) {
|
|||
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");
|
||||
inited = true;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) {
|
||||
maybeInit(env);
|
||||
static std::string obfuscated_signature_java = to_java(obfuscated_signature);
|
||||
return env->NewStringUTF(obfuscated_signature_java.c_str());
|
||||
// https://stackoverflow.com/questions/4844022/jni-create-hashmap with modifications
|
||||
jobject stringMapToJavaHashMap(JNIEnv *env, const decltype(signatures)& map) {
|
||||
jclass mapClass = env->FindClass("java/util/HashMap");
|
||||
if(mapClass == nullptr)
|
||||
return nullptr;
|
||||
|
||||
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"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_lsposed_lspd_service_ObfuscationManager_getOriginalSignature(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) {
|
||||
static std::string original_signature_java = to_java(original_signature);
|
||||
return env->NewStringUTF(original_signature_java.c_str());
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_org_lsposed_lspd_service_ObfuscationManager_getSignatures(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) {
|
||||
maybeInit(env);
|
||||
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) {
|
||||
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};
|
||||
|
||||
reader.CreateFullIr();
|
||||
auto ir = reader.GetIr();
|
||||
for (auto &i: ir->strings) {
|
||||
const char *s = i->c_str();
|
||||
char* p = const_cast<char *>(strstr(s, original_signature.c_str()));
|
||||
if (p) {
|
||||
// NOLINTNEXTLINE bugprone-not-null-terminated-result
|
||||
memcpy(p, new_sig, strlen(new_sig));
|
||||
for (const auto &signature: signatures) {
|
||||
char* p = const_cast<char *>(strstr(s, signature.first.c_str()));
|
||||
if (p) {
|
||||
auto new_sig = signature.second.c_str();
|
||||
// NOLINTNEXTLINE bugprone-not-null-terminated-result
|
||||
memcpy(p, new_sig, strlen(new_sig));
|
||||
}
|
||||
}
|
||||
}
|
||||
dex::Writer writer(ir);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <array>
|
||||
#include "logging.h"
|
||||
#include "loader.h"
|
||||
#include "ConfigImpl.h"
|
||||
#include "magisk_loader.h"
|
||||
#include "symbol_cache.h"
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ namespace lspd {
|
|||
LOGI("onModuleLoaded: version v{} ({})", versionName, versionCode);
|
||||
InitSymbolCache(nullptr);
|
||||
MagiskLoader::Init();
|
||||
ConfigImpl::Init();
|
||||
}
|
||||
|
||||
void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "zygisk.h"
|
||||
#include "logging.h"
|
||||
#include "loader.h"
|
||||
#include "ConfigImpl.h"
|
||||
#include "magisk_loader.h"
|
||||
#include "symbol_cache.h"
|
||||
|
||||
|
|
@ -282,6 +283,7 @@ namespace lspd {
|
|||
env_ = env;
|
||||
api_ = api;
|
||||
MagiskLoader::Init();
|
||||
ConfigImpl::Init();
|
||||
|
||||
auto companion = api->connectCompanion();
|
||||
if (companion == -1) {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@
|
|||
#include "config.h"
|
||||
|
||||
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 char* const moduleName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "ConfigImpl.h"
|
||||
#include "elf_util.h"
|
||||
#include "loader.h"
|
||||
#include "magisk_loader.h"
|
||||
|
|
@ -73,9 +74,15 @@ namespace lspd {
|
|||
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) {
|
||||
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
|
||||
kEntryClassName)) {
|
||||
GetEntryClassName())) {
|
||||
entry_class_ = JNI_NewGlobalRef(env, entry_class);
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +111,10 @@ namespace lspd {
|
|||
|
||||
// Call application_binder directly if application binder is available,
|
||||
// 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));
|
||||
close(dex_fd);
|
||||
instance->HookBridge(*this, env);
|
||||
|
|
@ -196,6 +206,8 @@ namespace lspd {
|
|||
};
|
||||
InstallInlineHooks(env, initInfo);
|
||||
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));
|
||||
close(dex_fd);
|
||||
InitHooks(env, initInfo);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "context.h"
|
||||
#include "utils/jni_helper.hpp"
|
||||
#include "symbol_cache.h"
|
||||
#include "ConfigBridge.h"
|
||||
|
||||
using namespace lsplant;
|
||||
|
||||
|
|
@ -108,6 +109,7 @@ namespace lspd {
|
|||
if (auto parcel_class = JNI_FindClass(env, "android/os/Parcel")) {
|
||||
parcel_class_ = JNI_NewGlobalRef(env, parcel_class);
|
||||
} else return;
|
||||
data_size_method_ = JNI_GetMethodID(env, parcel_class_, "dataSize","()I");
|
||||
obtain_method_ = JNI_GetStaticMethodID(env, parcel_class_, "obtain",
|
||||
"()Landroid/os/Parcel;");
|
||||
recycleMethod_ = JNI_GetMethodID(env, parcel_class_, "recycle", "()V");
|
||||
|
|
@ -119,6 +121,7 @@ namespace lspd {
|
|||
write_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "writeStrongBinder",
|
||||
"(Landroid/os/IBinder;)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_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "readStrongBinder",
|
||||
"()Landroid/os/IBinder;");
|
||||
|
|
@ -144,6 +147,12 @@ namespace lspd {
|
|||
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) {
|
||||
static bool kHooked = false;
|
||||
// This should only be ran once, so unlikely
|
||||
|
|
@ -151,7 +160,7 @@ namespace lspd {
|
|||
if (!initialized_) [[unlikely]] return;
|
||||
kHooked = true;
|
||||
if (auto bridge_service_class = context.FindClassFromCurrentLoader(env,
|
||||
kBridgeServiceClassName))
|
||||
GetBridgeServiceName()))
|
||||
bridge_service_class_ = JNI_NewGlobalRef(env, bridge_service_class);
|
||||
else {
|
||||
LOGE("server class not found");
|
||||
|
|
@ -284,26 +293,20 @@ namespace lspd {
|
|||
|
||||
ScopedLocalRef<jobject> Service::RequestApplicationBinderFromSystemServer(JNIEnv *env, const ScopedLocalRef<jobject> &system_server_binder) {
|
||||
auto heart_beat_binder = JNI_NewObject(env, binder_class_, binder_ctor_);
|
||||
auto data = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_);
|
||||
auto reply = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_);
|
||||
Wrapper wrapper{env, this};
|
||||
|
||||
JNI_CallVoidMethod(env, data, write_int_method_, getuid());
|
||||
JNI_CallVoidMethod(env, data, write_int_method_, getpid());
|
||||
JNI_CallVoidMethod(env, 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_int_method_, getuid());
|
||||
JNI_CallVoidMethod(env, wrapper.data, write_int_method_, getpid());
|
||||
JNI_CallVoidMethod(env, wrapper.data, write_string_method_, JNI_NewStringUTF(env, "android"));
|
||||
JNI_CallVoidMethod(env, wrapper.data, write_strong_binder_method_, heart_beat_binder);
|
||||
|
||||
auto res = JNI_CallBooleanMethod(env, system_server_binder, transact_method_,
|
||||
BRIDGE_TRANSACTION_CODE,
|
||||
data,
|
||||
reply, 0);
|
||||
auto res = wrapper.transact(system_server_binder, BRIDGE_TRANSACTION_CODE);
|
||||
|
||||
ScopedLocalRef<jobject> app_binder = {env, nullptr};
|
||||
if (res) {
|
||||
JNI_CallVoidMethod(env, reply, read_exception_method_);
|
||||
app_binder = JNI_CallObjectMethod(env, reply, read_strong_binder_method_);
|
||||
JNI_CallVoidMethod(env, wrapper.reply, read_exception_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) {
|
||||
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) {
|
||||
auto data = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_);
|
||||
auto reply = JNI_CallStaticObjectMethod(env, parcel_class_, obtain_method_);
|
||||
auto res = JNI_CallBooleanMethod(env, binder, transact_method_,
|
||||
DEX_TRANSACTION_CODE,
|
||||
data,
|
||||
reply, 0);
|
||||
Wrapper wrapper{env, this};
|
||||
bool res = wrapper.transact(binder, DEX_TRANSACTION_CODE);
|
||||
if (!res) {
|
||||
LOGE("Service::RequestLSPDex: transaction failed?");
|
||||
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_);
|
||||
auto size = static_cast<size_t>(JNI_CallLongMethod(env, reply, read_long_method_));
|
||||
JNI_CallVoidMethod(env, data, recycleMethod_);
|
||||
JNI_CallVoidMethod(env, reply, recycleMethod_);
|
||||
|
||||
LOGD("Service::RequestLSPDex fd={}, size={}", fd, size);
|
||||
auto size = static_cast<size_t>(JNI_CallLongMethod(env, wrapper.reply, read_long_method_));
|
||||
LOGD("fd={}, size={}", 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
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#ifndef LSPOSED_SERVICE_H
|
||||
#define LSPOSED_SERVICE_H
|
||||
|
||||
#include <map>
|
||||
#include <jni.h>
|
||||
#include "context.h"
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ using namespace std::literals::string_view_literals;
|
|||
namespace lspd {
|
||||
class Service {
|
||||
constexpr static jint DEX_TRANSACTION_CODE = 1310096052;
|
||||
constexpr static jint OBFUSCATION_MAP_TRANSACTION_CODE = 724533732;
|
||||
constexpr static jint BRIDGE_TRANSACTION_CODE = 1598837584;
|
||||
constexpr static auto BRIDGE_SERVICE_DESCRIPTOR = "LSPosed"sv;
|
||||
constexpr static auto BRIDGE_SERVICE_NAME = "activity"sv;
|
||||
|
|
@ -39,6 +41,31 @@ namespace lspd {
|
|||
constexpr static jint BRIDGE_ACTION_GET_BINDER = 2;
|
||||
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:
|
||||
inline static Service* instance() {
|
||||
return instance_.get();
|
||||
|
|
@ -59,6 +86,8 @@ namespace lspd {
|
|||
|
||||
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:
|
||||
inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>();
|
||||
bool initialized_ = false;
|
||||
|
|
@ -90,6 +119,7 @@ namespace lspd {
|
|||
jmethodID transact_method_ = nullptr;
|
||||
|
||||
jclass parcel_class_ = nullptr;
|
||||
jmethodID data_size_method_ = nullptr;
|
||||
jmethodID obtain_method_ = nullptr;
|
||||
jmethodID recycleMethod_ = nullptr;
|
||||
jmethodID write_interface_token_method_ = nullptr;
|
||||
|
|
@ -99,6 +129,7 @@ namespace lspd {
|
|||
jmethodID read_strong_binder_method_ = nullptr;
|
||||
jmethodID write_strong_binder_method_ = nullptr;
|
||||
jmethodID read_file_descriptor_method_ = nullptr;
|
||||
jmethodID read_int_method_ = nullptr;
|
||||
jmethodID read_long_method_ = nullptr;
|
||||
jmethodID read_string_method_ = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e183a459a23d8825f495caff30b81d88e56fc65b
|
||||
Subproject commit ac9744ab71bc899c7954239fadbad605521c4b0c
|
||||
Loading…
Reference in New Issue