Skip to content

Commit

Permalink
[enhances] Redirect entry update to backup method
Browse files Browse the repository at this point in the history
  • Loading branch information
canyie committed May 16, 2022
1 parent c95af52 commit 0e072b5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 18 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/top/canyie/pine/Pine.java
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ private static native void init0(int androidVersion, boolean debug, boolean debu

private static native void enableFastNative();

private static native long getArtMethod(Member method);
public static native long getArtMethod(Member method);

private static native Method hook0(long thread, Class<?> declaring, Member target, Method bridge,
boolean isInlineHook, boolean jni, boolean proxy);
Expand Down
66 changes: 54 additions & 12 deletions enhances/src/main/cpp/enhances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,32 @@
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

// Special flag, means "delaying, not yet actually hooked"
#define DELAYING (nullptr)
// Special flag, means "redirects farther entry update, but backup is not available yet".
#define REDIRECT_ENTRY_UPDATE (reinterpret_cast<void* const>(0x1))

static JavaVM* jvm_;
static jclass PineEnhances_;
static jmethodID onClassInit_;
static void*(*GetClassDef)(void* cls) = nullptr;
static size_t page_size_ = static_cast<const size_t>(sysconf(_SC_PAGESIZE));
static std::unordered_set<void*> cared_classes_;
static std::unordered_map<void*, bool> hooked_methods_;
static std::unordered_map<void*, void*> hooked_methods_;
static std::unordered_map<void*, const void*> pending_entries_;
static std::mutex cared_classes_mutex_;
static std::shared_mutex hooked_methods_mutex_;
static std::mutex pending_entries_mutex_;
static bool care_no_class_def_ = false;

static void* instrumentation_ = nullptr;

static void* (*FindElfSymbol)(void*, const char*, bool);

class ScopedLock {
public:
inline ScopedLock(std::mutex& mutex) : mLock(mutex) { mLock.lock(); }
inline ScopedLock(std::mutex* mutex) : mLock(*mutex) { mLock.lock(); }
inline explicit ScopedLock(std::mutex& mutex) : mLock(mutex) { mLock.lock(); }
inline explicit ScopedLock(std::mutex* mutex) : mLock(*mutex) { mLock.lock(); }
inline ~ScopedLock() { mLock.unlock(); }
private:
std::mutex& mLock;
Expand Down Expand Up @@ -68,7 +77,7 @@ static bool Unprotect(void* addr) {
return true;
}

bool IsClassCared(void* ptr) {
bool AcquireClassCareFlag(void* ptr) {
if (ptr == nullptr) return false;
void* class_def = GetClassDef(ptr);
if (class_def == nullptr) {
Expand All @@ -78,16 +87,24 @@ bool IsClassCared(void* ptr) {
return cared_classes_.erase(class_def) != 0;
}

bool IsMethodHooked(void* method, bool exact) {
bool IsMethodHooked(void* method, bool redirect_entry_update) {
std::shared_lock<std::shared_mutex> lk(hooked_methods_mutex_);
auto i = hooked_methods_.find(method);
if (i == hooked_methods_.end()) return false;
if (exact) return i->second;
if (redirect_entry_update) return i->second != DELAYING;
return true;
}

void* GetMethodBackup(void* method) {
std::shared_lock<std::shared_mutex> lk(hooked_methods_mutex_);
auto i = hooked_methods_.find(method);
if (i == hooked_methods_.end()) return nullptr;
auto second = i->second;
return second == REDIRECT_ENTRY_UPDATE ? nullptr : second;
}

void MaybeCallClassInitMonitor(void* ptr) {
if (!IsClassCared(ptr)) return;
if (!AcquireClassCareFlag(ptr)) return;
JNIEnv* env = CurrentEnv();
env->CallStaticVoidMethod(PineEnhances_, onClassInit_, reinterpret_cast<jlong>(ptr));
if (env->ExceptionCheck()) {
Expand Down Expand Up @@ -139,7 +156,18 @@ HOOK_ENTRY(MarkClassInitialized, void*, void* thiz, void* self, uint32_t* cls_pt
}

HOOK_ENTRY(UpdateMethodsCode, void, void* thiz, void* method, const void* quick_code) {
if (IsMethodHooked(method, true)) return;
instrumentation_ = thiz;
if (IsMethodHooked(method, true)) {
auto backup = GetMethodBackup(method);
if (backup) {
// Redirect entry update to backup
method = backup;
} else {
ScopedLock lk(pending_entries_mutex_);
pending_entries_[method] = quick_code;
return;
}
}
backup_UpdateMethodsCode(thiz, method, quick_code);
}

Expand All @@ -157,9 +185,23 @@ void PineEnhances_careClassInit(JNIEnv*, jclass, jlong address) {
cared_classes_.insert(class_def);
}

void PineEnhances_recordMethodHooked(JNIEnv*, jclass, jlong method, jboolean exact) {
std::unique_lock<std::shared_mutex> lk(hooked_methods_mutex_);
hooked_methods_[reinterpret_cast<void*>(method)] = exact == JNI_TRUE;
void PineEnhances_recordMethodHooked(JNIEnv*, jclass, jlong method, jlong backup) {
auto o = reinterpret_cast<void*>(method);
auto b = reinterpret_cast<void*>(backup);
{
std::unique_lock<std::shared_mutex> lk(hooked_methods_mutex_);
hooked_methods_[o] = b;
}
if (!(instrumentation_ && backup_UpdateMethodsCode)) return;
const void* saved_entry;
{
ScopedLock lk(pending_entries_mutex_);
auto i = pending_entries_.find(o);
if (i == pending_entries_.end()) return;
saved_entry = i->second;
pending_entries_.erase(i);
}
backup_UpdateMethodsCode(instrumentation_, b, saved_entry);
}

jboolean PineEnhances_initClassInitMonitor(JNIEnv* env, jclass PineEnhances, jint sdk_level,
Expand Down Expand Up @@ -238,7 +280,7 @@ jboolean PineEnhances_initClassInitMonitor(JNIEnv* env, jclass PineEnhances, jin
JNINativeMethod JNI_METHODS[] = {
{"initClassInitMonitor", "(IJJJ)Z", (void*) PineEnhances_initClassInitMonitor},
{"careClassInit", "(J)V", (void*) PineEnhances_careClassInit},
{"recordMethodHooked", "(JZ)V", (void*) PineEnhances_recordMethodHooked}
{"recordMethodHooked", "(JJ)V", (void*) PineEnhances_recordMethodHooked}
};

EXPORT bool init_PineEnhances(JavaVM* jvm, JNIEnv* env, jclass cls) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
*/
@SuppressLint("SoonBlockedPrivateApi")
public class PendingHookHandler implements Pine.HookHandler, ClassInitMonitor.Callback {
// Special flag, means "delaying, not yet actually hooked"
private static final long DELAYING = 0x0;
// Special flag, means "prevents farther entry update, but backup is not available yet".
private static final long PREVENT_ENTRY_UPDATE = 0x1;
private static volatile PendingHookHandler instance;
private static Field status;
private Pine.HookHandler realHandler;
Expand Down Expand Up @@ -114,7 +118,7 @@ public MethodHook.Unhook handleHook(Pine.HookRecord hookRecord, MethodHook hook,
Member target = hookRecord.target;
if (hook != null && shouldDelay(target, newMethod, modifiers)) {
PineEnhances.logD("Delay hook method %s", target);
recordMethodHooked(hookRecord.artMethod, false);
recordMethodHooked(hookRecord.artMethod, DELAYING);
Class<?> declaring = hookRecord.target.getDeclaringClass();
synchronized (pendingMap) {
Set<Pine.HookRecord> pendingHooks = pendingMap.get(declaring);
Expand All @@ -129,8 +133,10 @@ public MethodHook.Unhook handleHook(Pine.HookRecord hookRecord, MethodHook hook,
return hook.new Unhook(hookRecord);
}
PineEnhances.logD("Not delay method %s", target);
if (newMethod) recordMethodHooked(hookRecord.artMethod, true);
return realHandler.handleHook(hookRecord, hook, modifiers, newMethod, canInitDeclaringClass);
if (newMethod) recordMethodHooked(hookRecord.artMethod, PREVENT_ENTRY_UPDATE);
MethodHook.Unhook u = realHandler.handleHook(hookRecord, hook, modifiers, newMethod, canInitDeclaringClass);
if (newMethod) recordMethodHooked(hookRecord.artMethod, Pine.getArtMethod(hookRecord.backup));
return u;
}

@Override public void handleUnhook(Pine.HookRecord hookRecord, MethodHook hook) {
Expand All @@ -147,8 +153,10 @@ public MethodHook.Unhook handleHook(Pine.HookRecord hookRecord, MethodHook hook,
for (Pine.HookRecord hookRecord : pendingHooks) {
Member target = hookRecord.target;
PineEnhances.logD("Flushing pending hooks for method %s", target);
recordMethodHooked(hookRecord.artMethod, true);
// Place 0x1 to prevent method entry updating
recordMethodHooked(hookRecord.artMethod, PREVENT_ENTRY_UPDATE);
realHandler.handleHook(hookRecord, null, target.getModifiers(), true, false);
recordMethodHooked(hookRecord.artMethod, Pine.getArtMethod(hookRecord.backup));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,5 @@ private static void onClassInit(long ptr) {

static native boolean initClassInitMonitor(int sdkLevel, long openElf, long findElfSymbol, long closeElf);
static native void careClassInit(long ptr);
public static native void recordMethodHooked(long artMethod, boolean exact);
public static native void recordMethodHooked(long origin, long backup);
}

0 comments on commit 0e072b5

Please sign in to comment.