Skip to content

Feature/debugger detection #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 17, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ fun ThreatStatusList() {
"Running the application in an Emulator",
reportedThreats.contains(ThreatDetectionCenter.Threat.SIMULATOR),
),
ThreatStatus(
"Debugger",
"A tool that allows developers to inspect and modify the execution of a program in real-time, potentially exposing sensitive data or allowing unauthorized control",
reportedThreats.contains(ThreatDetectionCenter.Threat.DEBUGGER),
),
ThreatStatus(
"Passcode",
"Indicates if current device is unprotected with a passcode. Biometric protection requires a passcode to be set up",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.exxeta.securitytoolkit

import android.content.Context
import com.exxeta.securitytoolkit.internal.AppSignatureDetection
import com.exxeta.securitytoolkit.internal.DebuggerDetection
import com.exxeta.securitytoolkit.internal.DevicePasscodeDetection
import com.exxeta.securitytoolkit.internal.EmulatorDetector
import com.exxeta.securitytoolkit.internal.HardwareSecurityDetection
Expand All @@ -17,6 +18,7 @@ import kotlinx.coroutines.flow.flow
* - [areHooksDetected]: to check for injection (hooking) tweaks
* - [isSimulatorDetected]: to check if the app is running in a simulated
* environment
* - [isDebuggerDetected]: to check if the app is being traced by a debugger
* - [isDeviceWithoutPasscodeDetected]: to check if device is protected with a
* passcode
* - [isHardwareProtectionUnavailable]: to check if device can use
Expand Down Expand Up @@ -52,6 +54,9 @@ class ThreatDetectionCenter(private val context: Context) {
val isSimulatorDetected: Boolean
get() = EmulatorDetector.threatDetected()

val isDebuggerDetected: Boolean
get() = DebuggerDetection.threatDetected()

/**
* Performs check for Device Passcode presence
* Returns `false`, when device is **unprotected**
Expand Down Expand Up @@ -102,6 +107,9 @@ class ThreatDetectionCenter(private val context: Context) {
if (EmulatorDetector.threatDetected()) {
emit(Threat.SIMULATOR)
}
if (DebuggerDetection.threatDetected()) {
emit(Threat.DEBUGGER)
}
if (DevicePasscodeDetection.threatDetected(context)) {
emit(Threat.DEVICE_WITHOUT_PASSCODE)
}
Expand All @@ -122,6 +130,7 @@ class ThreatDetectionCenter(private val context: Context) {
ROOT_PRIVILEGES,
HOOKS,
SIMULATOR,
DEBUGGER,
DEVICE_WITHOUT_PASSCODE,
HARDWARE_PROTECTION_UNAVAILABLE,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.exxeta.securitytoolkit.internal

import android.os.Debug
import java.io.FileReader
import java.net.InetSocketAddress
import java.net.Socket


/**
* A Detector object for debugger
*/
internal object DebuggerDetection {

private val adbPorts = (5555..5585 step 2)

/**
* Exposes public API to detect debugger
*
* @return
* - `true` if debugger is detected.
* - `false` if no debugger is detected.
*/
fun threatDetected(): Boolean {
return Debug.isDebuggerConnected() || Debug.waitingForDebugger() || isAdbPortListening() || isTracerPidSet()
}

private fun isTracerPidSet(): Boolean = try {
FileReader("/proc/self/status").buffered().use { br ->
br.lines().anyMatch {
it.startsWith("TracerPid:") && it.split(":")[1].trim() != "0"
}
}
} catch (e: Throwable) {
false
}

private fun isAdbPortListening(): Boolean {
return adbPorts.any { isPortInUse(it) }
}

private fun isPortInUse(port: Int): Boolean = try {
Socket().use { socket ->
socket.connect(InetSocketAddress("127.0.0.1", port), 200)
}
true
} catch (e: Throwable) {
false
}

}