[core] Runtime dex obfuscation implementation
This commit is contained in:
parent
9130d13576
commit
30d1c1b551
|
|
@ -5,9 +5,9 @@ endef
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := lspd
|
LOCAL_MODULE := lspd
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src $(LOCAL_PATH)/../shared/
|
||||||
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/src))
|
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/src))
|
||||||
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) api/config.cpp
|
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) api/config.cpp ../shared/Obfuscation.cpp
|
||||||
LOCAL_STATIC_LIBRARIES := cxx riru yahfa dobby dex_builder
|
LOCAL_STATIC_LIBRARIES := cxx riru yahfa dobby dex_builder
|
||||||
ifeq ($(API), riru)
|
ifeq ($(API), riru)
|
||||||
LOCAL_SRC_FILES += api/riru_main.cpp
|
LOCAL_SRC_FILES += api/riru_main.cpp
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@
|
||||||
#include "jni/native_api.h"
|
#include "jni/native_api.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "symbol_cache.h"
|
#include "symbol_cache.h"
|
||||||
|
#include "slicer/reader.h"
|
||||||
|
#include "slicer/writer.h"
|
||||||
|
#include "slicer/dex_utf8.h"
|
||||||
|
#include "Obfuscation.h"
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
@ -64,20 +68,40 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) {
|
Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) {
|
||||||
if (auto addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); addr) {
|
auto *old = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
auto *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
|
||||||
|
if (old != MAP_FAILED && addr != MAP_FAILED) {
|
||||||
|
memmove(addr, old, size);
|
||||||
addr_ = addr;
|
addr_ = addr;
|
||||||
size_ = size;
|
size_ = size;
|
||||||
} else {
|
} else {
|
||||||
LOGE("Read dex failed");
|
if (old == MAP_FAILED) LOGE("Old failed");
|
||||||
|
if (addr == MAP_FAILED) LOGE("addr failed");
|
||||||
|
LOGE("Read dex failed: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
munmap(old, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::PreloadedDex::~PreloadedDex() {
|
Context::PreloadedDex::~PreloadedDex() {
|
||||||
if (*this) munmap(addr_, size_);
|
if (*this) munmap(addr_, size_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::ObfuscateDex() {
|
||||||
|
if (!dex_) [[unlikely]] return;
|
||||||
|
|
||||||
|
auto dex = Obfuscation::obfuscateDex(dex_.data(), dex_.size());
|
||||||
|
// TODO: multiple memory copy prevention
|
||||||
|
auto *mem = mmap(nullptr, dex.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
memmove(mem, dex.data(), dex.size());
|
||||||
|
PreloadedDex new_dex(mem, dex.size());
|
||||||
|
std::swap(dex_, new_dex);
|
||||||
|
LOGD("Context::ObfuscateDex: %p, size=%zu", reinterpret_cast<dex::u1*>(dex_.data()), dex.size());
|
||||||
|
}
|
||||||
|
|
||||||
void Context::PreLoadDex(int fd, std::size_t size) {
|
void Context::PreLoadDex(int fd, std::size_t size) {
|
||||||
dex_ = PreloadedDex{fd, size};
|
dex_ = PreloadedDex{fd, size};
|
||||||
|
ObfuscateDex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::PreLoadDex(std::string_view dex_path) {
|
void Context::PreLoadDex(std::string_view dex_path) {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,9 @@ namespace lspd {
|
||||||
other.size_ = 0;
|
other.size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use with caution!
|
||||||
|
PreloadedDex(void* addr, size_t size) : addr_(addr), size_(size) {};
|
||||||
|
|
||||||
operator bool() const { return addr_ && size_; }
|
operator bool() const { return addr_ && size_; }
|
||||||
|
|
||||||
auto size() const { return size_; }
|
auto size() const { return size_; }
|
||||||
|
|
@ -112,6 +115,8 @@ namespace lspd {
|
||||||
|
|
||||||
PreloadedDex dex_{};
|
PreloadedDex dex_{};
|
||||||
|
|
||||||
|
void ObfuscateDex();
|
||||||
|
|
||||||
Context() {}
|
Context() {}
|
||||||
|
|
||||||
void LoadDex(JNIEnv *env);
|
void LoadDex(JNIEnv *env);
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ namespace lspd {
|
||||||
cbuilder.set_source_file("LSP");
|
cbuilder.set_source_file("LSP");
|
||||||
|
|
||||||
auto hooker_type =
|
auto hooker_type =
|
||||||
TypeDescriptor::FromClassname("de.robv.android.xposed.LspHooker");
|
TypeDescriptor::FromClassname("ac.ksmm.notioss.lspdaa.LspHooker");
|
||||||
|
|
||||||
auto *hooker_field = cbuilder.CreateField("hooker", hooker_type)
|
auto *hooker_field = cbuilder.CreateField("hooker", hooker_type)
|
||||||
.access_flags(dex::kAccStatic)
|
.access_flags(dex::kAccStatic)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// Created by Kotori0 on 2021/12/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "slicer/reader.h"
|
||||||
|
#include "slicer/writer.h"
|
||||||
|
#include "Obfuscation.h"
|
||||||
|
|
||||||
|
class WA: public dex::Writer::Allocator {
|
||||||
|
std::unordered_map<void*, size_t> allocated_;
|
||||||
|
public:
|
||||||
|
void* Allocate(size_t size) override {
|
||||||
|
auto *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
allocated_[mem] = size;
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
void Free(void* ptr) override {
|
||||||
|
munmap(ptr, allocated_[ptr]);
|
||||||
|
allocated_.erase(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ustring Obfuscation::obfuscateDex(void *dex, size_t size) {
|
||||||
|
const char* new_sig = "Lac/ksmm/notioss/lspdaa";
|
||||||
|
dex::Reader reader{reinterpret_cast<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, "Lde/robv/android/xposed"));
|
||||||
|
if (p) {
|
||||||
|
memcpy(p, new_sig, strlen(new_sig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dex::Writer writer(ir);
|
||||||
|
|
||||||
|
size_t new_size;
|
||||||
|
WA allocator;
|
||||||
|
auto *p_dex = writer.CreateImage(&allocator, &new_size);
|
||||||
|
ustring new_dex(p_dex, new_size);
|
||||||
|
allocator.Free(p_dex);
|
||||||
|
return new_dex;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// Created by Kotori0 on 2021/12/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LSPOSED_OBFUSCATION_H
|
||||||
|
#define LSPOSED_OBFUSCATION_H
|
||||||
|
#include "slicer/writer.h"
|
||||||
|
|
||||||
|
using ustring = std::basic_string<unsigned char>;
|
||||||
|
|
||||||
|
class Obfuscation {
|
||||||
|
public:
|
||||||
|
static ustring obfuscateDex(void* dex, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //LSPOSED_OBFUSCATION_H
|
||||||
|
|
@ -2,10 +2,12 @@ LOCAL_PATH := $(call my-dir)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := daemon
|
LOCAL_MODULE := daemon
|
||||||
LOCAL_SRC_FILES := logcat.cpp
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../core/src/main/cpp/shared/
|
||||||
LOCAL_STATIC_LIBRARIES := cxx
|
LOCAL_SRC_FILES := logcat.cpp obfuscation.cpp ../../../../core/src/main/cpp/shared/Obfuscation.cpp
|
||||||
|
LOCAL_STATIC_LIBRARIES := cxx dex_builder
|
||||||
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
|
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
|
||||||
LOCAL_LDLIBS := -llog
|
LOCAL_LDLIBS := -llog -landroid
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
include ../core/src/main/cpp/external/DexBuilder/Android.mk
|
||||||
$(call import-module,prefab/cxx)
|
$(call import-module,prefab/cxx)
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// Created by Kotori2 on 2021/12/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <android/sharedmem.h>
|
||||||
|
#include <android/sharedmem_jni.h>
|
||||||
|
#include <slicer/dex_utf8.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "slicer/reader.h"
|
||||||
|
#include "slicer/writer.h"
|
||||||
|
#include "Obfuscation.h"
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobject
|
||||||
|
Java_org_lsposed_lspd_service_ObfuscationService_obfuscateDex(JNIEnv *env, jclass /*clazz*/,
|
||||||
|
jobject memory) {
|
||||||
|
int fd = ASharedMemory_dupFromJava(env, memory);
|
||||||
|
auto size = ASharedMemory_getSize(fd);
|
||||||
|
ustring mem_wrapper;
|
||||||
|
mem_wrapper.resize(size);
|
||||||
|
read(fd, mem_wrapper.data(), size);
|
||||||
|
|
||||||
|
void *mem = mem_wrapper.data();
|
||||||
|
|
||||||
|
auto new_dex = Obfuscation::obfuscateDex(mem, size);
|
||||||
|
|
||||||
|
// create new SharedMem since it cannot be resized
|
||||||
|
jclass clazz = env->FindClass("android/os/SharedMemory");
|
||||||
|
auto *ref = env->NewGlobalRef(clazz);
|
||||||
|
jmethodID mid = env->GetStaticMethodID(clazz, "create", "(Ljava/lang/String;I)Landroid/os/SharedMemory;");
|
||||||
|
jstring empty_str = env->NewStringUTF("");
|
||||||
|
jobject new_mem = env->CallStaticObjectMethod(clazz, mid, empty_str, static_cast<jint>(new_dex.size()));
|
||||||
|
int new_fd = ASharedMemory_dupFromJava(env, new_mem);
|
||||||
|
|
||||||
|
env->DeleteGlobalRef(ref);
|
||||||
|
env->DeleteLocalRef(empty_str);
|
||||||
|
|
||||||
|
mem = mmap(nullptr, new_dex.size(), PROT_READ | PROT_WRITE, MAP_SHARED, new_fd, 0);
|
||||||
|
if (mem == MAP_FAILED) {
|
||||||
|
// LOGE("Failed to map new dex to memory?");
|
||||||
|
}
|
||||||
|
memcpy(mem, new_dex.data(), new_dex.size());
|
||||||
|
ASharedMemory_setProt(fd, PROT_READ);
|
||||||
|
return new_mem;
|
||||||
|
}
|
||||||
|
|
@ -296,6 +296,7 @@ public class ConfigFileManager {
|
||||||
var byteBuffer = memory.mapReadWrite();
|
var byteBuffer = memory.mapReadWrite();
|
||||||
Channels.newChannel(in).read(byteBuffer);
|
Channels.newChannel(in).read(byteBuffer);
|
||||||
SharedMemory.unmap(byteBuffer);
|
SharedMemory.unmap(byteBuffer);
|
||||||
|
memory = ObfuscationService.obfuscateDex(memory);
|
||||||
memory.setProtect(OsConstants.PROT_READ);
|
memory.setProtect(OsConstants.PROT_READ);
|
||||||
preLoadedDexes.add(memory);
|
preLoadedDexes.add(memory);
|
||||||
} catch (IOException | ErrnoException e) {
|
} catch (IOException | ErrnoException e) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.lsposed.lspd.service;
|
||||||
|
|
||||||
|
import android.os.SharedMemory;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class ObfuscationService {
|
||||||
|
static native SharedMemory obfuscateDex(SharedMemory memory);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue