diff --git a/app/src/main/cpp/detector/debug_detector.cpp b/app/src/main/cpp/detector/debug_detector.cpp index c53f245..b62857e 100644 --- a/app/src/main/cpp/detector/debug_detector.cpp +++ b/app/src/main/cpp/detector/debug_detector.cpp @@ -98,3 +98,25 @@ MultiLayerResult DebugDetector::detectPtrace() { result.syscallResult = false; // Can't check ptrace via syscall reliably return result; } + +// Anti-timing attack detection +// Detects debugger breakpoints by measuring initialization elapsed time. +// If a debugger inserts breakpoints during security initialization, +// execution will take abnormally long (>= 2 seconds). +// The caller should capture time_t at the start of initialization, then +// call this method to check if too much time has elapsed. +bool DebugDetector::checkInitTimingAttack(time_t initStartTime) { + time_t currentTime; + time(¤tTime); + + time_t elapsed = currentTime - initStartTime; + + if (elapsed >= 2) { + LOGD("🚨 Anti-timing attack: init took %ld seconds (>= 2s threshold), " + "debugger breakpoints suspected!", (long)elapsed); + return true; // Timing attack detected + } + + LOGD("Anti-timing: init completed in %ld seconds (normal)", (long)elapsed); + return false; // Normal execution time +} diff --git a/app/src/main/cpp/detector/debug_detector.h b/app/src/main/cpp/detector/debug_detector.h index 0fcae8c..dab15de 100644 --- a/app/src/main/cpp/detector/debug_detector.h +++ b/app/src/main/cpp/detector/debug_detector.h @@ -3,6 +3,7 @@ #include #include +#include struct MultiLayerResult; @@ -22,6 +23,12 @@ class DebugDetector { // Get TracerPid value static int getTracerPid(); + + // Anti-timing attack detection + // Measures initialization elapsed time, if >= 2s, debugger breakpoints detected + // @param initStartTime The time_t value captured at init start + // @return true if timing attack detected (init took >= 2 seconds) + static bool checkInitTimingAttack(time_t initStartTime); }; #endif // LAUNCH_DEBUG_DETECTOR_H diff --git a/app/src/main/cpp/detector/hook_detector.cpp b/app/src/main/cpp/detector/hook_detector.cpp index 69777d0..c0ab4c7 100644 --- a/app/src/main/cpp/detector/hook_detector.cpp +++ b/app/src/main/cpp/detector/hook_detector.cpp @@ -1282,3 +1282,92 @@ MultiLayerResult HookDetector::detectMemoryHooks() { result.syscallResult = checkMapsForHooksSyscall() || checkFridaMemorySyscall(); return result; } + +// ===================== DumpArtMethod Hook Detection ===================== +// +// The dumpArtMethod symbol is used by ART method dumping/hooking tools: +// - FDex2: Dumps DEX files from memory +// - DexDump: ART method dumping +// - Various ART hooking frameworks that patch ArtMethod structures +// +// Detection: Read /proc/self/maps and search for "dumpArtMethod" symbol +// If found, it means a hooking tool has injected a library that exports +// (or references) the dumpArtMethod function. + +bool HookDetector::checkDumpArtMethodHookNative() { + LOGD("=== checkDumpArtMethodHookNative() START ==="); + + // Read /proc/self/maps using libc + std::ifstream maps("/proc/self/maps"); + if (!maps.is_open()) { + LOGD("Failed to open /proc/self/maps (native)"); + return false; + } + + std::string line; + while (std::getline(maps, line)) { + // Search for dumpArtMethod symbol in memory maps + if (line.find("dumpArtMethod") != std::string::npos) { + LOGD("🚨 [DETECTED] dumpArtMethod found in maps (native): %s", line.c_str()); + maps.close(); + return true; + } + + // Also check for related dumping tool signatures + if (line.find("libdexdump") != std::string::npos || + line.find("libFDex2") != std::string::npos || + line.find("fdex2") != std::string::npos || + line.find("dexdump") != std::string::npos || + line.find("libdexhunter") != std::string::npos || + line.find("dexhunter") != std::string::npos || + line.find("libunpacker") != std::string::npos) { + LOGD("🚨 [DETECTED] ART dumping tool found in maps (native): %s", line.c_str()); + maps.close(); + return true; + } + } + + maps.close(); + LOGD("=== checkDumpArtMethodHookNative() END - Clean ==="); + return false; +} + +bool HookDetector::checkDumpArtMethodHookSyscall() { + LOGD("=== checkDumpArtMethodHookSyscall() START ==="); + + // Read /proc/self/maps using direct syscall (bypass libc hooks) + std::string maps = syscall_read_file("/proc/self/maps", 131072); // 128KB + if (maps.empty()) { + LOGD("Failed to read /proc/self/maps (syscall)"); + return false; + } + + // Search for dumpArtMethod symbol + if (maps.find("dumpArtMethod") != std::string::npos) { + LOGD("🚨 [DETECTED] dumpArtMethod found in maps (syscall)"); + return true; + } + + // Also check for related dumping tool signatures + if (maps.find("libdexdump") != std::string::npos || + maps.find("libFDex2") != std::string::npos || + maps.find("fdex2") != std::string::npos || + maps.find("dexdump") != std::string::npos || + maps.find("libdexhunter") != std::string::npos || + maps.find("dexhunter") != std::string::npos || + maps.find("libunpacker") != std::string::npos) { + LOGD("🚨 [DETECTED] ART dumping tool found in maps (syscall)"); + return true; + } + + LOGD("=== checkDumpArtMethodHookSyscall() END - Clean ==="); + return false; +} + +MultiLayerResult HookDetector::detectDumpArtMethodHook() { + MultiLayerResult result; + result.javaResult = false; + result.nativeResult = checkDumpArtMethodHookNative(); + result.syscallResult = checkDumpArtMethodHookSyscall(); + return result; +} diff --git a/app/src/main/cpp/detector/hook_detector.h b/app/src/main/cpp/detector/hook_detector.h index ae6b5fe..1567b6c 100644 --- a/app/src/main/cpp/detector/hook_detector.h +++ b/app/src/main/cpp/detector/hook_detector.h @@ -66,6 +66,12 @@ class HookDetector { static MultiLayerResult detectZygisk(); // 改为通用 Zygisk 检测 static MultiLayerResult detectSmapsHook(); // SMAPS 内存取证检测 static MultiLayerResult detectMemoryHooks(); + static MultiLayerResult detectDumpArtMethodHook(); // DumpArtMethod Hook 检测 + + // DumpArtMethod Hook Detection + // Scans /proc/self/maps for dumpArtMethod symbol - indicates ART method dumping tools + static bool checkDumpArtMethodHookNative(); + static bool checkDumpArtMethodHookSyscall(); }; #endif // LAUNCH_HOOK_DETECTOR_H diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 7e227b2..972a3fe 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -1617,4 +1617,52 @@ Java_com_xff_launch_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) { return env->NewStringUTF("Launch - 设备环境检测"); } +// ===================== Anti-Timing Attack Detection ===================== + +/** + * Capture current time for anti-timing attack measurement + * Call this at the START of security initialization + * @return Current time as seconds since epoch (to pass to checkInitTimingAttack) + */ +JNIEXPORT jlong JNICALL +Java_com_xff_launch_detector_NativeDetector_captureInitStartTime(JNIEnv *env, jobject thiz) { + time_t timer; + time(&timer); + return (jlong)timer; +} + +/** + * Check if security initialization took too long (>= 2 seconds) + * This indicates debugger breakpoints were inserted during initialization + * @param initStartTime The value returned by captureInitStartTime() + * @return true if timing attack detected (init took >= 2 seconds) + */ +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkInitTimingAttack(JNIEnv *env, jobject thiz, + jlong initStartTime) { + return DebugDetector::checkInitTimingAttack((time_t)initStartTime); +} + +// ===================== DumpArtMethod Hook Detection ===================== + +/** + * Check for dumpArtMethod hook via native (libc) + * Scans /proc/self/maps for dumpArtMethod symbol and related dumping tools + * @return true if dumpArtMethod hook detected + */ +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkDumpArtMethodHookNative(JNIEnv *env, jobject thiz) { + return HookDetector::checkDumpArtMethodHookNative(); +} + +/** + * Check for dumpArtMethod hook via direct syscall + * Same check but using direct syscall to bypass libc hooks + * @return true if dumpArtMethod hook detected + */ +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkDumpArtMethodHookSyscall(JNIEnv *env, jobject thiz) { + return HookDetector::checkDumpArtMethodHookSyscall(); +} + } // extern "C" diff --git a/app/src/main/java/com/xff/launch/detector/NativeDetector.java b/app/src/main/java/com/xff/launch/detector/NativeDetector.java index 922c12c..c4114af 100644 --- a/app/src/main/java/com/xff/launch/detector/NativeDetector.java +++ b/app/src/main/java/com/xff/launch/detector/NativeDetector.java @@ -305,6 +305,41 @@ public class NativeDetector { */ public native boolean detectTimingAnomaly(long syscallTime, long libcTime, float threshold); + // ===================== Anti-Timing Attack Detection ===================== + + /** + * Capture current time for anti-timing attack measurement. + * Call this at the START of security initialization. + * @return Current time as seconds since epoch + */ + public native long captureInitStartTime(); + + /** + * Check if security initialization took too long (>= 2 seconds). + * If initialization is suspiciously slow, it indicates a debugger has + * inserted breakpoints during the process. + * @param initStartTime The value returned by captureInitStartTime() + * @return true if timing attack detected (init took >= 2 seconds) + */ + public native boolean checkInitTimingAttack(long initStartTime); + + // ===================== DumpArtMethod Hook Detection ===================== + + /** + * Check for dumpArtMethod hook via native (libc). + * Scans /proc/self/maps for dumpArtMethod symbol and related + * ART method dumping tools (FDex2, DexDump, DexHunter, etc.) + * @return true if dumpArtMethod hook detected + */ + public native boolean checkDumpArtMethodHookNative(); + + /** + * Check for dumpArtMethod hook via direct syscall. + * Same check but using direct syscall to bypass libc hooks. + * @return true if dumpArtMethod hook detected + */ + public native boolean checkDumpArtMethodHookSyscall(); + // Singleton instance private static NativeDetector instance;