Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app/src/main/cpp/detector/debug_detector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(&currentTime);

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
}
7 changes: 7 additions & 0 deletions app/src/main/cpp/detector/debug_detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <string>
#include <vector>
#include <ctime>

struct MultiLayerResult;

Expand All @@ -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
89 changes: 89 additions & 0 deletions app/src/main/cpp/detector/hook_detector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 6 additions & 0 deletions app/src/main/cpp/detector/hook_detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
48 changes: 48 additions & 0 deletions app/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
35 changes: 35 additions & 0 deletions app/src/main/java/com/xff/launch/detector/NativeDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down