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
88 changes: 88 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,91 @@ MultiLayerResult DebugDetector::detectPtrace() {
result.syscallResult = false; // Can't check ptrace via syscall reliably
return result;
}

// ===================== Suspicious Tool Path Detection =====================
// Iterates over suspicious tool path array, using access() to check file existence

// Suspicious tool path entries with descriptions
struct ToolPathEntry {
const char* path;
const char* description;
};

static const ToolPathEntry SUSPICIOUS_TOOL_PATHS[] = {
{"/data/local/tmp/android_server", "IDA debugger (32-bit)"},
{"/data/local/tmp/android_server64", "IDA debugger (64-bit)"},
{"/data/local/tmp/gdbserver", "GDB debugger"},
{"/data/local/tmp/inject", "Injection tool"},
{"/data/local/tmp/libhello.so", "Frida gadget"},
{"/sdcard/xxxx/", "Unpacker (FART etc.)"},
{"/sdcard/ooxx/", "Unpacker"},
{"/sdcard/fart/", "FART unpacker"},
{"/sdcard/Download/dexDump/", "DEX dump tool"},
{"/sdcard/Download/top.niunaijun.blackdexa32_logcat.txt", "BlackDex log (32-bit)"},
{"/sdcard/Download/top.niunaijun.blackdexa64_logcat.txt", "BlackDex log (64-bit)"},
{"/data/data/top.niunaijun.blackdexa32", "BlackDex app (32-bit)"},
{"/data/data/top.niunaijun.blackdexa64", "BlackDex app (64-bit)"},
};
static const int SUSPICIOUS_TOOL_PATHS_COUNT = sizeof(SUSPICIOUS_TOOL_PATHS) / sizeof(SUSPICIOUS_TOOL_PATHS[0]);

bool DebugDetector::checkSuspiciousToolPathsNative() {
for (int i = 0; i < SUSPICIOUS_TOOL_PATHS_COUNT; i++) {
if (access(SUSPICIOUS_TOOL_PATHS[i].path, F_OK) == 0) {
LOGD("Suspicious tool path found (native): %s [%s]",
SUSPICIOUS_TOOL_PATHS[i].path, SUSPICIOUS_TOOL_PATHS[i].description);
return true;
}
}
return false;
}

bool DebugDetector::checkSuspiciousToolPathsSyscall() {
for (int i = 0; i < SUSPICIOUS_TOOL_PATHS_COUNT; i++) {
if (syscall_file_exists(SUSPICIOUS_TOOL_PATHS[i].path)) {
LOGD("Suspicious tool path found (syscall): %s [%s]",
SUSPICIOUS_TOOL_PATHS[i].path, SUSPICIOUS_TOOL_PATHS[i].description);
return true;
}
}
return false;
}

std::string DebugDetector::getDetectedSuspiciousToolPaths() {
std::string result = "[";
bool first = true;

for (int i = 0; i < SUSPICIOUS_TOOL_PATHS_COUNT; i++) {
bool nativeExists = (access(SUSPICIOUS_TOOL_PATHS[i].path, F_OK) == 0);
bool syscallExists = syscall_file_exists(SUSPICIOUS_TOOL_PATHS[i].path);

if (nativeExists || syscallExists) {
if (!first) result += ",";
first = false;

result += "{\"path\":\"";
result += SUSPICIOUS_TOOL_PATHS[i].path;
result += "\",\"desc\":\"";
result += SUSPICIOUS_TOOL_PATHS[i].description;
result += "\",\"native\":";
result += nativeExists ? "true" : "false";
result += ",\"syscall\":";
result += syscallExists ? "true" : "false";
result += "}";

LOGD("Detected suspicious tool: %s [%s] (native:%d, syscall:%d)",
SUSPICIOUS_TOOL_PATHS[i].path, SUSPICIOUS_TOOL_PATHS[i].description,
nativeExists, syscallExists);
}
}

result += "]";
return result;
}

MultiLayerResult DebugDetector::detectSuspiciousToolPaths() {
MultiLayerResult result;
result.javaResult = false; // Not checked at Java layer
result.nativeResult = checkSuspiciousToolPathsNative();
result.syscallResult = checkSuspiciousToolPathsSyscall();
return result;
}
9 changes: 9 additions & 0 deletions app/src/main/cpp/detector/debug_detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ class DebugDetector {

// Get TracerPid value
static int getTracerPid();

// Suspicious tool path detection
// Detects debuggers, injection tools, Frida gadgets, unpackers, etc.
static bool checkSuspiciousToolPathsNative();
static bool checkSuspiciousToolPathsSyscall();
static std::string getDetectedSuspiciousToolPaths();

// Combined detection for suspicious tool paths
static MultiLayerResult detectSuspiciousToolPaths();
};

#endif // LAUNCH_DEBUG_DETECTOR_H
17 changes: 17 additions & 0 deletions app/src/main/cpp/native-lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,23 @@ Java_com_xff_launch_detector_NativeDetector_getTracerPid(JNIEnv *env, jobject th
return DebugDetector::getTracerPid();
}

// Suspicious tool path detection
JNIEXPORT jboolean JNICALL
Java_com_xff_launch_detector_NativeDetector_checkSuspiciousToolPathsNative(JNIEnv *env, jobject thiz) {
return DebugDetector::checkSuspiciousToolPathsNative();
}

JNIEXPORT jboolean JNICALL
Java_com_xff_launch_detector_NativeDetector_checkSuspiciousToolPathsSyscall(JNIEnv *env, jobject thiz) {
return DebugDetector::checkSuspiciousToolPathsSyscall();
}

JNIEXPORT jstring JNICALL
Java_com_xff_launch_detector_NativeDetector_getDetectedSuspiciousToolPaths(JNIEnv *env, jobject thiz) {
std::string details = DebugDetector::getDetectedSuspiciousToolPaths();
return env->NewStringUTF(details.c_str());
}

// ===================== File Operations via Syscall =====================

JNIEXPORT jboolean JNICALL
Expand Down
162 changes: 162 additions & 0 deletions app/src/main/java/com/xff/launch/detector/DebugDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,44 @@ public DetectionItem detectPtraceSelfProtection() {
return item;
}

/**
* Detect suspicious tool paths
* Checks for debuggers (IDA/GDB), injection tools, Frida gadgets,
* unpackers (FART/BlackDex/DEX Dump), etc.
*/
public DetectionItem detectSuspiciousToolPaths() {
DetectionItem item = new DetectionItem("可疑工具路径", "检测调试器/脱壳工具/注入工具等文件路径");

// Java layer - check via File.exists()
boolean javaResult = checkSuspiciousToolPathsJava();
item.setLayerResult(DetectionLayer.JAVA, javaResult);

// Native layer - check via libc access()
boolean nativeResult = nativeDetector.checkSuspiciousToolPathsNative();
item.setLayerResult(DetectionLayer.NATIVE, nativeResult);

// Syscall layer - check via direct syscall
boolean syscallResult = nativeDetector.checkSuspiciousToolPathsSyscall();
item.setLayerResult(DetectionLayer.SYSCALL, syscallResult);

if (item.getMostTrustworthyResult()) {
item.setStatus(DetectionStatus.RISK);
item.setDetail("检测到可疑工具");

// Collect detailed detection info
collectSuspiciousToolPathDetails(item);
} else {
item.setStatus(DetectionStatus.SAFE);
item.setDetail("未检测到");
}

if (item.hasInconsistentResults()) {
item.setDetail(item.getDetail() + " (检测层不一致)");
}

return item;
}

/**
* Get all debug detection items
*/
Expand All @@ -253,6 +291,7 @@ public List<DetectionItem> getAllDetections() {
items.add(detectJdwp());
items.add(detectPtrace());
items.add(detectPtraceSelfProtection());
items.add(detectSuspiciousToolPaths());
return items;
}

Expand Down Expand Up @@ -381,6 +420,38 @@ private String checkAllThreadsTracerPid() {
return result.toString();
}

/**
* Java layer check for suspicious tool paths using File.exists()
* Checks suspicious tool paths via Java File API
*/
private boolean checkSuspiciousToolPathsJava() {
String[] toolPaths = {
"/data/local/tmp/android_server",
"/data/local/tmp/android_server64",
"/data/local/tmp/gdbserver",
"/data/local/tmp/inject",
"/data/local/tmp/libhello.so",
"/sdcard/xxxx/",
"/sdcard/ooxx/",
"/sdcard/fart/",
"/sdcard/Download/dexDump/",
"/sdcard/Download/top.niunaijun.blackdexa32_logcat.txt",
"/sdcard/Download/top.niunaijun.blackdexa64_logcat.txt",
"/data/data/top.niunaijun.blackdexa32",
"/data/data/top.niunaijun.blackdexa64"
};

for (String path : toolPaths) {
try {
if (new java.io.File(path).exists()) {
return true;
}
} catch (Exception ignored) {
}
}
return false;
}

// ===================== Detail Collection Methods =====================

/**
Expand Down Expand Up @@ -639,4 +710,95 @@ private void collectPtraceDetails(DetectionItem item, int javaTracerPid,
"PID: " + myPid,
DetectionLayer.JAVA, "🆔");
}

/**
* Collect suspicious tool path detection details
*/
private void collectSuspiciousToolPathDetails(DetectionItem item) {
// Get detected path details from native layer (JSON format)
String detectedJson = null;
try {
detectedJson = nativeDetector.getDetectedSuspiciousToolPaths();
} catch (Exception ignored) {
}

// Define all paths with descriptions and categories (consistent with native layer)
String[][] toolPaths = {
// {path, description, icon, category}
{"/data/local/tmp/android_server", "IDA 调试器 (32位)", "🔴", "调试器"},
{"/data/local/tmp/android_server64", "IDA 调试器 (64位)", "🔴", "调试器"},
{"/data/local/tmp/gdbserver", "GDB 调试器", "🔴", "调试器"},
{"/data/local/tmp/inject", "注入工具", "🟠", "注入/Frida"},
{"/data/local/tmp/libhello.so", "Frida gadget", "🟠", "注入/Frida"},
{"/sdcard/xxxx/", "脱壳工具", "🟡", "脱壳工具"},
{"/sdcard/ooxx/", "脱壳工具", "🟡", "脱壳工具"},
{"/sdcard/fart/", "FART 脱壳工具", "🟡", "脱壳工具"},
{"/sdcard/Download/dexDump/", "DEX Dump 工具", "🟡", "脱壳工具"},
{"/sdcard/Download/top.niunaijun.blackdexa32_logcat.txt", "BlackDex 日志 (32位)", "🟡", "脱壳工具"},
{"/sdcard/Download/top.niunaijun.blackdexa64_logcat.txt", "BlackDex 日志 (64位)", "🟡", "脱壳工具"},
{"/data/data/top.niunaijun.blackdexa32", "BlackDex 应用 (32位)", "🟡", "脱壳工具"},
{"/data/data/top.niunaijun.blackdexa64", "BlackDex 应用 (64位)", "🟡", "脱壳工具"},
};

int detectedCount = 0;
StringBuilder detectedNames = new StringBuilder();

for (String[] entry : toolPaths) {
String path = entry[0];
String desc = entry[1];
String icon = entry[2];
String category = entry[3];

// Check via Java layer
boolean javaExists = false;
try {
javaExists = new java.io.File(path).exists();
} catch (Exception ignored) {
}

// Check via native layer (fileExistsNative)
boolean nativeExists = false;
try {
nativeExists = nativeDetector.fileExistsNative(path);
} catch (Exception ignored) {
}

// Check via syscall layer
boolean syscallExists = false;
try {
syscallExists = nativeDetector.fileExistsSyscall(path);
} catch (Exception ignored) {
}

if (javaExists || nativeExists || syscallExists) {
detectedCount++;

StringBuilder detail = new StringBuilder();
detail.append("路径: ").append(path);
detail.append("\n检测层: ");
if (javaExists) detail.append("Java ✓ ");
if (nativeExists) detail.append("Native ✓ ");
if (syscallExists) detail.append("Syscall ✓ ");

// Determine which detection layer to report
DetectionLayer reportLayer = syscallExists ? DetectionLayer.SYSCALL :
nativeExists ? DetectionLayer.NATIVE :
DetectionLayer.JAVA;

item.addDetectionDetail(icon + " " + category, desc,
detail.toString(), reportLayer, icon);

if (detectedNames.length() > 0) detectedNames.append(", ");
detectedNames.append(desc);
}
}

// Add detection summary
if (detectedCount > 0) {
item.addDetectionDetail("📊 检测统计", "发现可疑路径",
"数量: " + detectedCount + "/" + toolPaths.length + "\n" +
"发现: " + detectedNames.toString(),
DetectionLayer.SYSCALL, "📈");
}
}
}
8 changes: 8 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 @@ -83,6 +83,14 @@ public class NativeDetector {

public native int getTracerPid();

// Suspicious tool path detection
/** Check suspicious tool paths - Native layer (access) */
public native boolean checkSuspiciousToolPathsNative();
/** Check suspicious tool paths - Syscall layer */
public native boolean checkSuspiciousToolPathsSyscall();
/** Get detected suspicious tool path details (JSON format) */
public native String getDetectedSuspiciousToolPaths();

// ===================== File Operations =====================

public native boolean fileExistsNative(String path);
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
<string name="detect_debugger">调试器连接</string>
<string name="detect_jdwp">JDWP 检测</string>
<string name="detect_ptrace">Ptrace 检测</string>
<string name="detect_suspicious_tool_paths">可疑工具路径</string>

<!-- Fingerprint -->
<string name="fingerprint_title">设备指纹信息</string>
Expand Down