Skip to content

Commit 376d98d

Browse files
authored
add Feature/debugger detection (#12)
* add DebuggerDetection * add DebuggerDetection to example app * simplify checks, remove unused code - threatDetected() condition fixed and simplified. - private isTracerPidSet() simplified as a single-expression function. - private isPortInUse() simplified as a single-expression function. - removed unnecessary string casting from val adbPorts list. - removed val adbConsolePorts (not yet used).
1 parent 78eb360 commit 376d98d

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

example/app/src/main/java/com/exxeta/mobilesecuritytoolkitexample/ThreatStatusList.kt

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ fun ThreatStatusList() {
5959
"Running the application in an Emulator",
6060
reportedThreats.contains(ThreatDetectionCenter.Threat.SIMULATOR),
6161
),
62+
ThreatStatus(
63+
"Debugger",
64+
"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",
65+
reportedThreats.contains(ThreatDetectionCenter.Threat.DEBUGGER),
66+
),
6267
ThreatStatus(
6368
"Passcode",
6469
"Indicates if current device is unprotected with a passcode. Biometric protection requires a passcode to be set up",

securitytoolkit/src/main/java/com/exxeta/securitytoolkit/ThreatDetectionCenter.kt

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.exxeta.securitytoolkit
22

33
import android.content.Context
44
import com.exxeta.securitytoolkit.internal.AppSignatureDetection
5+
import com.exxeta.securitytoolkit.internal.DebuggerDetection
56
import com.exxeta.securitytoolkit.internal.DevicePasscodeDetection
67
import com.exxeta.securitytoolkit.internal.EmulatorDetector
78
import com.exxeta.securitytoolkit.internal.HardwareSecurityDetection
@@ -17,6 +18,7 @@ import kotlinx.coroutines.flow.flow
1718
* - [areHooksDetected]: to check for injection (hooking) tweaks
1819
* - [isSimulatorDetected]: to check if the app is running in a simulated
1920
* environment
21+
* - [isDebuggerDetected]: to check if the app is being traced by a debugger
2022
* - [isDeviceWithoutPasscodeDetected]: to check if device is protected with a
2123
* passcode
2224
* - [isHardwareProtectionUnavailable]: to check if device can use
@@ -52,6 +54,9 @@ class ThreatDetectionCenter(private val context: Context) {
5254
val isSimulatorDetected: Boolean
5355
get() = EmulatorDetector.threatDetected()
5456

57+
val isDebuggerDetected: Boolean
58+
get() = DebuggerDetection.threatDetected()
59+
5560
/**
5661
* Performs check for Device Passcode presence
5762
* Returns `false`, when device is **unprotected**
@@ -102,6 +107,9 @@ class ThreatDetectionCenter(private val context: Context) {
102107
if (EmulatorDetector.threatDetected()) {
103108
emit(Threat.SIMULATOR)
104109
}
110+
if (DebuggerDetection.threatDetected()) {
111+
emit(Threat.DEBUGGER)
112+
}
105113
if (DevicePasscodeDetection.threatDetected(context)) {
106114
emit(Threat.DEVICE_WITHOUT_PASSCODE)
107115
}
@@ -122,6 +130,7 @@ class ThreatDetectionCenter(private val context: Context) {
122130
ROOT_PRIVILEGES,
123131
HOOKS,
124132
SIMULATOR,
133+
DEBUGGER,
125134
DEVICE_WITHOUT_PASSCODE,
126135
HARDWARE_PROTECTION_UNAVAILABLE,
127136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.exxeta.securitytoolkit.internal
2+
3+
import android.os.Debug
4+
import java.io.FileReader
5+
import java.net.InetSocketAddress
6+
import java.net.Socket
7+
8+
9+
/**
10+
* A Detector object for debugger
11+
*/
12+
internal object DebuggerDetection {
13+
14+
private val adbPorts = (5555..5585 step 2)
15+
16+
/**
17+
* Exposes public API to detect debugger
18+
*
19+
* @return
20+
* - `true` if debugger is detected.
21+
* - `false` if no debugger is detected.
22+
*/
23+
fun threatDetected(): Boolean {
24+
return Debug.isDebuggerConnected() || Debug.waitingForDebugger() || isAdbPortListening() || isTracerPidSet()
25+
}
26+
27+
private fun isTracerPidSet(): Boolean = try {
28+
FileReader("/proc/self/status").buffered().use { br ->
29+
br.lines().anyMatch {
30+
it.startsWith("TracerPid:") && it.split(":")[1].trim() != "0"
31+
}
32+
}
33+
} catch (e: Throwable) {
34+
false
35+
}
36+
37+
private fun isAdbPortListening(): Boolean {
38+
return adbPorts.any { isPortInUse(it) }
39+
}
40+
41+
private fun isPortInUse(port: Int): Boolean = try {
42+
Socket().use { socket ->
43+
socket.connect(InetSocketAddress("127.0.0.1", port), 200)
44+
}
45+
true
46+
} catch (e: Throwable) {
47+
false
48+
}
49+
50+
}

0 commit comments

Comments
 (0)