react-native-device-defense 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/android/build.gradle +90 -0
- package/android/proguard-rules.pro +28 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/cpp/CMakeLists.txt +45 -0
- package/android/src/main/cpp/device-security.cpp +314 -0
- package/android/src/main/java/vn/osp/security/DebugDetection.kt +131 -0
- package/android/src/main/java/vn/osp/security/DeviceSecurityModule.kt +277 -0
- package/android/src/main/java/vn/osp/security/DeviceSecurityPackage.kt +58 -0
- package/android/src/main/java/vn/osp/security/EmulatorDetection.kt +204 -0
- package/android/src/main/java/vn/osp/security/HookDetection.kt +270 -0
- package/android/src/main/java/vn/osp/security/NativeSecurityCheck.kt +66 -0
- package/android/src/main/java/vn/osp/security/RootDetection.kt +349 -0
- package/lib/commonjs/NativeDeviceSecurity.js +9 -0
- package/lib/commonjs/NativeDeviceSecurity.js.map +1 -0
- package/lib/commonjs/api.js +213 -0
- package/lib/commonjs/api.js.map +1 -0
- package/lib/commonjs/components/SecurityBlockedScreen.js +177 -0
- package/lib/commonjs/components/SecurityBlockedScreen.js.map +1 -0
- package/lib/commonjs/components/index.js +13 -0
- package/lib/commonjs/components/index.js.map +1 -0
- package/lib/commonjs/hooks/index.js +13 -0
- package/lib/commonjs/hooks/index.js.map +1 -0
- package/lib/commonjs/hooks/useDeviceSecurity.js +81 -0
- package/lib/commonjs/hooks/useDeviceSecurity.js.map +1 -0
- package/lib/commonjs/index.js +48 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/NativeDeviceSecurity.js +3 -0
- package/lib/module/NativeDeviceSecurity.js.map +1 -0
- package/lib/module/api.js +206 -0
- package/lib/module/api.js.map +1 -0
- package/lib/module/components/SecurityBlockedScreen.js +169 -0
- package/lib/module/components/SecurityBlockedScreen.js.map +1 -0
- package/lib/module/components/index.js +2 -0
- package/lib/module/components/index.js.map +1 -0
- package/lib/module/hooks/index.js +2 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useDeviceSecurity.js +73 -0
- package/lib/module/hooks/useDeviceSecurity.js.map +1 -0
- package/lib/module/index.js +21 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/NativeDeviceSecurity.d.ts +16 -0
- package/lib/typescript/NativeDeviceSecurity.d.ts.map +1 -0
- package/lib/typescript/api.d.ts +55 -0
- package/lib/typescript/api.d.ts.map +1 -0
- package/lib/typescript/components/SecurityBlockedScreen.d.ts +23 -0
- package/lib/typescript/components/SecurityBlockedScreen.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +2 -0
- package/lib/typescript/components/index.d.ts.map +1 -0
- package/lib/typescript/hooks/index.d.ts +3 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useDeviceSecurity.d.ts +7 -0
- package/lib/typescript/hooks/useDeviceSecurity.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +12 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +81 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/package.json +72 -0
- package/react-native-device-security.podspec +18 -0
- package/src/NativeDeviceSecurity.ts +33 -0
- package/src/api.ts +225 -0
- package/src/components/SecurityBlockedScreen.tsx +204 -0
- package/src/components/index.ts +1 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useDeviceSecurity.ts +91 -0
- package/src/index.ts +27 -0
- package/src/types.ts +95 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
package vn.osp.security
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.pm.PackageManager
|
|
5
|
+
import java.io.BufferedReader
|
|
6
|
+
import java.io.File
|
|
7
|
+
import java.io.InputStreamReader
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detection for hooking frameworks (Frida, Xposed, Magisk, etc.)
|
|
11
|
+
*/
|
|
12
|
+
class HookDetection(private val context: Context) {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if Frida framework is present
|
|
16
|
+
*/
|
|
17
|
+
fun hasFrida(): Boolean {
|
|
18
|
+
return checkFridaPorts() ||
|
|
19
|
+
checkFridaLibraries() ||
|
|
20
|
+
checkFridaProcesses() ||
|
|
21
|
+
checkFridaFiles()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if Xposed framework is present
|
|
26
|
+
*/
|
|
27
|
+
fun hasXposed(): Boolean {
|
|
28
|
+
return checkXposedApp() ||
|
|
29
|
+
checkXposedInClassLoader() ||
|
|
30
|
+
checkXposedFiles()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if Magisk is present
|
|
35
|
+
*/
|
|
36
|
+
fun hasMagisk(): Boolean {
|
|
37
|
+
return checkMagiskApp() ||
|
|
38
|
+
checkMagiskFiles() ||
|
|
39
|
+
checkMagiskModules()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check for Frida ports (default: 27042, 27043)
|
|
44
|
+
*/
|
|
45
|
+
private fun checkFridaPorts(): Boolean {
|
|
46
|
+
val fridaPorts = listOf(27042, 27043, 27044, 27045)
|
|
47
|
+
|
|
48
|
+
// Read /proc/net/tcp to check for open ports
|
|
49
|
+
try {
|
|
50
|
+
val bufferedReader = BufferedReader(InputStreamReader(
|
|
51
|
+
File("/proc/net/tcp").inputStream()
|
|
52
|
+
))
|
|
53
|
+
|
|
54
|
+
bufferedReader.use { reader ->
|
|
55
|
+
var line: String?
|
|
56
|
+
while (reader.readLine().also { line = it } != null) {
|
|
57
|
+
// Format: sl local_address rem_address st ...
|
|
58
|
+
// Skip header
|
|
59
|
+
if (line?.startsWith(" sl") == true) continue
|
|
60
|
+
|
|
61
|
+
val parts = line?.trim()?.split("\\s+".toRegex()) ?: continue
|
|
62
|
+
if (parts.size >= 2) {
|
|
63
|
+
try {
|
|
64
|
+
// local_address format: hex_ip:hex_port
|
|
65
|
+
val localAddress = parts[1]
|
|
66
|
+
val addressParts = localAddress.split(":")
|
|
67
|
+
if (addressParts.size == 2) {
|
|
68
|
+
val portHex = addressParts[1]
|
|
69
|
+
val port = portHex.toInt(16)
|
|
70
|
+
if (port in fridaPorts) {
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
// Ignore parsing errors
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (e: Exception) {
|
|
81
|
+
// Ignore errors
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check for Frida libraries in memory
|
|
89
|
+
*/
|
|
90
|
+
private fun checkFridaLibraries(): Boolean {
|
|
91
|
+
// Check /proc/self/maps for Frida libraries
|
|
92
|
+
try {
|
|
93
|
+
val bufferedReader = BufferedReader(InputStreamReader(
|
|
94
|
+
File("/proc/self/maps").inputStream()
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
bufferedReader.use { reader ->
|
|
98
|
+
var line: String?
|
|
99
|
+
while (reader.readLine().also { line = it } != null) {
|
|
100
|
+
if (line?.contains("frida") == true ||
|
|
101
|
+
line?.contains("frida-agent") == true ||
|
|
102
|
+
line?.contains("libfrida") == true) {
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e: Exception) {
|
|
108
|
+
// Ignore errors
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check for Frida processes
|
|
116
|
+
*/
|
|
117
|
+
private fun checkFridaProcesses(): Boolean {
|
|
118
|
+
try {
|
|
119
|
+
val process = Runtime.getRuntime().exec("ps")
|
|
120
|
+
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
|
121
|
+
|
|
122
|
+
reader.use {
|
|
123
|
+
var line: String?
|
|
124
|
+
while (it.readLine().also { line = it } != null) {
|
|
125
|
+
if (line?.contains("frida") == true ||
|
|
126
|
+
line?.contains("frida-server") == true) {
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
// Ignore errors
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Check for Frida-related files
|
|
140
|
+
*/
|
|
141
|
+
private fun checkFridaFiles(): Boolean {
|
|
142
|
+
val fridaPaths = listOf(
|
|
143
|
+
"/data/local/tmp/frida",
|
|
144
|
+
"/data/local/tmp/frida-server",
|
|
145
|
+
"/data/local/tmp/frida-agent.so",
|
|
146
|
+
"/data/local/tmp/frida-agent-*.so",
|
|
147
|
+
"/system/lib/libfrida.so",
|
|
148
|
+
"/system/lib64/libfrida.so"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return fridaPaths.any { path ->
|
|
152
|
+
try {
|
|
153
|
+
// Handle wildcards
|
|
154
|
+
if (path.contains("*")) {
|
|
155
|
+
val dir = File(path.substringBeforeLast("/"))
|
|
156
|
+
val prefix = path.substringAfterLast("/").replace("*", "")
|
|
157
|
+
dir.list()?.any { it.startsWith(prefix) } == true
|
|
158
|
+
} else {
|
|
159
|
+
File(path).exists()
|
|
160
|
+
}
|
|
161
|
+
} catch (e: Exception) {
|
|
162
|
+
false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if Xposed installer app is present
|
|
169
|
+
*/
|
|
170
|
+
private fun checkXposedApp(): Boolean {
|
|
171
|
+
val xposedPackages = listOf(
|
|
172
|
+
"de.robv.android.xposed.installer",
|
|
173
|
+
"de.robv.android.xposed.installer",
|
|
174
|
+
"org.meowcat.edxposed.manager",
|
|
175
|
+
"io.github.huskydg.xposed",
|
|
176
|
+
"com.solohsu.android.edxp.manager"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return xposedPackages.any { isPackageInstalled(it) }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check for Xposed in class loader
|
|
184
|
+
*/
|
|
185
|
+
private fun checkXposedInClassLoader(): Boolean {
|
|
186
|
+
return try {
|
|
187
|
+
// Check for XposedBridge class
|
|
188
|
+
Class.forName("de.robv.android.xposed.XposedBridge")
|
|
189
|
+
true
|
|
190
|
+
} catch (e: ClassNotFoundException) {
|
|
191
|
+
false
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Check for Xposed-related files
|
|
197
|
+
*/
|
|
198
|
+
private fun checkXposedFiles(): Boolean {
|
|
199
|
+
val xposedPaths = listOf(
|
|
200
|
+
"/system/framework/XposedBridge.jar",
|
|
201
|
+
"/system/bin/app_process32_xposed",
|
|
202
|
+
"/system/bin/app_process64_xposed",
|
|
203
|
+
"/system/xposed.prop",
|
|
204
|
+
"/cache/XposedBridge.jar"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return xposedPaths.any { File(it).exists() }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Check if Magisk app is present
|
|
212
|
+
*/
|
|
213
|
+
private fun checkMagiskApp(): Boolean {
|
|
214
|
+
val magiskPackages = listOf(
|
|
215
|
+
"com.topjohnwu.magisk",
|
|
216
|
+
"com.topjohnwu.magisk.ui"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return magiskPackages.any { isPackageInstalled(it) }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check for Magisk-related files
|
|
224
|
+
*/
|
|
225
|
+
private fun checkMagiskFiles(): Boolean {
|
|
226
|
+
val magiskPaths = listOf(
|
|
227
|
+
"/sbin/.magisk",
|
|
228
|
+
"/sbin/.core/mirror",
|
|
229
|
+
"/sbin/.core/img",
|
|
230
|
+
"/sbin/.core/db-0/magisk.db",
|
|
231
|
+
"/cache/magisk.log",
|
|
232
|
+
"/dev/com.topjohnwu.magisk",
|
|
233
|
+
"/data/adb/magisk",
|
|
234
|
+
"/data/adb/magisk.img",
|
|
235
|
+
"/data/adb/magisk.db",
|
|
236
|
+
"/data/adb/magisk_simple",
|
|
237
|
+
"/system/addon.d/99-magisk.sh"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return magiskPaths.any { File(it).exists() }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check for Magisk modules
|
|
245
|
+
*/
|
|
246
|
+
private fun checkMagiskModules(): Boolean {
|
|
247
|
+
val magiskModuleDir = "/data/adb/modules"
|
|
248
|
+
val moduleDir = File(magiskModuleDir)
|
|
249
|
+
|
|
250
|
+
if (moduleDir.exists() && moduleDir.isDirectory) {
|
|
251
|
+
// Check if any modules are enabled
|
|
252
|
+
val modules = moduleDir.listFiles()
|
|
253
|
+
return modules?.isNotEmpty() == true
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return false
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check if package is installed
|
|
261
|
+
*/
|
|
262
|
+
private fun isPackageInstalled(packageName: String): Boolean {
|
|
263
|
+
return try {
|
|
264
|
+
context.packageManager.getPackageInfo(packageName, 0)
|
|
265
|
+
true
|
|
266
|
+
} catch (e: PackageManager.NameNotFoundException) {
|
|
267
|
+
false
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
package vn.osp.security
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Native (JNI) security checks
|
|
5
|
+
* This class provides native methods that are harder to bypass with JavaScript hooks
|
|
6
|
+
*/
|
|
7
|
+
object NativeSecurityCheck {
|
|
8
|
+
|
|
9
|
+
init {
|
|
10
|
+
try {
|
|
11
|
+
System.loadLibrary("device-security")
|
|
12
|
+
} catch (e: UnsatisfiedLinkError) {
|
|
13
|
+
// Native library not loaded, fallback to Java/Kotlin checks
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Native root detection
|
|
19
|
+
* Uses C++ code to check for root indicators that are harder to bypass
|
|
20
|
+
* @return true if device is rooted
|
|
21
|
+
*/
|
|
22
|
+
external fun isRooted(): Boolean
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Native check for dangerous binaries
|
|
26
|
+
* @return true if dangerous binaries found
|
|
27
|
+
*/
|
|
28
|
+
external fun hasDangerousBinaries(): Boolean
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Native check for system properties
|
|
32
|
+
* @return true if suspicious system properties found
|
|
33
|
+
*/
|
|
34
|
+
external fun hasSuspiciousSystemProperties(): Boolean
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Native check for hook frameworks
|
|
38
|
+
* @return true if hooking framework detected
|
|
39
|
+
*/
|
|
40
|
+
external fun hasHookFramework(): Boolean
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Native check for debugger
|
|
44
|
+
* @return true if debugger detected
|
|
45
|
+
*/
|
|
46
|
+
external fun isDebuggerAttached(): Boolean
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get native security status as JSON string
|
|
50
|
+
* @return JSON string with security status
|
|
51
|
+
*/
|
|
52
|
+
external fun getSecurityStatus(): String
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if native library is loaded
|
|
56
|
+
*/
|
|
57
|
+
fun isNativeLibraryLoaded(): Boolean {
|
|
58
|
+
return try {
|
|
59
|
+
// Try to call a native method
|
|
60
|
+
isRooted()
|
|
61
|
+
true
|
|
62
|
+
} catch (e: UnsatisfiedLinkError) {
|
|
63
|
+
false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
package vn.osp.security
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.pm.PackageManager
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import com.scottyab.rootbeer.RootBeer
|
|
7
|
+
import java.io.BufferedReader
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.InputStreamReader
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Multi-layer root detection using various techniques
|
|
13
|
+
*/
|
|
14
|
+
class RootDetection(private val context: Context) {
|
|
15
|
+
|
|
16
|
+
data class RootDetectionResult(
|
|
17
|
+
val isRooted: Boolean,
|
|
18
|
+
val hasRootBeerDetected: Boolean,
|
|
19
|
+
val hasNativeRootDetected: Boolean,
|
|
20
|
+
val hasDangerousBins: Boolean,
|
|
21
|
+
val hasRootApps: Boolean,
|
|
22
|
+
val hasSystemPropsModified: Boolean,
|
|
23
|
+
val details: Map<String, Boolean>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Perform comprehensive root detection
|
|
28
|
+
*/
|
|
29
|
+
fun performDetection(): RootDetectionResult {
|
|
30
|
+
val rootBeer = RootBeer(context)
|
|
31
|
+
val hasRootBeerDetected = rootBeer.isRooted
|
|
32
|
+
|
|
33
|
+
// Native detection (JNI)
|
|
34
|
+
val hasNativeRootDetected = NativeSecurityCheck.isRooted()
|
|
35
|
+
|
|
36
|
+
// Check for dangerous binaries
|
|
37
|
+
val hasDangerousBins = checkDangerousBinaries()
|
|
38
|
+
|
|
39
|
+
// Check for root management apps
|
|
40
|
+
val hasRootApps = checkRootApps()
|
|
41
|
+
|
|
42
|
+
// Check system properties
|
|
43
|
+
val hasSystemPropsModified = checkSystemProperties()
|
|
44
|
+
|
|
45
|
+
val details = mapOf(
|
|
46
|
+
"root_beer" to hasRootBeerDetected,
|
|
47
|
+
"native_detection" to hasNativeRootDetected,
|
|
48
|
+
"dangerous_bins" to hasDangerousBins,
|
|
49
|
+
"root_apps" to hasRootApps,
|
|
50
|
+
"system_props" to hasSystemPropsModified,
|
|
51
|
+
"su_binary" to checkForSuBinary(),
|
|
52
|
+
"busybox_binary" to checkForBusybox(),
|
|
53
|
+
"rw_system" to checkSystemRemounted(),
|
|
54
|
+
"dangerous_props" to checkDangerousSystemProperties()
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
val isRooted = hasRootBeerDetected ||
|
|
58
|
+
hasNativeRootDetected ||
|
|
59
|
+
hasDangerousBins ||
|
|
60
|
+
hasRootApps ||
|
|
61
|
+
hasSystemPropsModified
|
|
62
|
+
|
|
63
|
+
return RootDetectionResult(
|
|
64
|
+
isRooted = isRooted,
|
|
65
|
+
hasRootBeerDetected = hasRootBeerDetected,
|
|
66
|
+
hasNativeRootDetected = hasNativeRootDetected,
|
|
67
|
+
hasDangerousBins = hasDangerousBins,
|
|
68
|
+
hasRootApps = hasRootApps,
|
|
69
|
+
hasSystemPropsModified = hasSystemPropsModified,
|
|
70
|
+
details = details
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check for dangerous binaries commonly found on rooted devices
|
|
76
|
+
*/
|
|
77
|
+
private fun checkDangerousBinaries(): Boolean {
|
|
78
|
+
val dangerousBins = listOf(
|
|
79
|
+
"su",
|
|
80
|
+
"busybox",
|
|
81
|
+
"magisk",
|
|
82
|
+
"supolicy",
|
|
83
|
+
"supol",
|
|
84
|
+
"daemonsu",
|
|
85
|
+
"ksu", // KernelSU
|
|
86
|
+
"apd" // Apex (another root solution)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
val paths = listOf(
|
|
90
|
+
"/system/app/Superuser.apk",
|
|
91
|
+
"/sbin/su",
|
|
92
|
+
"/system/bin/su",
|
|
93
|
+
"/system/xbin/su",
|
|
94
|
+
"/data/local/xbin/su",
|
|
95
|
+
"/data/local/bin/su",
|
|
96
|
+
"/system/sd/xbin/su",
|
|
97
|
+
"/system/bin/failsafe/su",
|
|
98
|
+
"/data/local/su",
|
|
99
|
+
"/su/bin/su",
|
|
100
|
+
"/magisk/.core/bin/su",
|
|
101
|
+
"/system/usr/we-need-root/su",
|
|
102
|
+
"/system/app/SuperSU",
|
|
103
|
+
"/system/app/SuperSU.apk",
|
|
104
|
+
"/system/app/Superuser",
|
|
105
|
+
"/system/app/Superuser.apk",
|
|
106
|
+
"/data/data/com.noshufou.android.su",
|
|
107
|
+
"/data/data/com.thirdparty.superuser",
|
|
108
|
+
"/data/data/eu.chainfire.supersu",
|
|
109
|
+
"/system/xbin/busybox",
|
|
110
|
+
"/system/bin/busybox",
|
|
111
|
+
"/data/data/com.topjohnwu.magisk",
|
|
112
|
+
"/cache/magisk.log",
|
|
113
|
+
"/dev/com.topjohnwu.magisk",
|
|
114
|
+
"/system/app/MagiskManager"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
// Check for dangerous binaries in PATH
|
|
118
|
+
val pathEnv = System.getenv("PATH") ?: ""
|
|
119
|
+
val pathDirs = pathEnv.split(":")
|
|
120
|
+
|
|
121
|
+
for (bin in dangerousBins) {
|
|
122
|
+
// Check in common locations
|
|
123
|
+
for (path in paths) {
|
|
124
|
+
if (File(path).exists()) {
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check in PATH directories
|
|
130
|
+
for (dir in pathDirs) {
|
|
131
|
+
if (File(dir, bin).exists()) {
|
|
132
|
+
return true
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check for su binary specifically
|
|
142
|
+
*/
|
|
143
|
+
private fun checkForSuBinary(): Boolean {
|
|
144
|
+
val suPaths = listOf(
|
|
145
|
+
"/system/bin/su",
|
|
146
|
+
"/system/xbin/su",
|
|
147
|
+
"/sbin/su",
|
|
148
|
+
"/data/local/xbin/su",
|
|
149
|
+
"/data/local/bin/su",
|
|
150
|
+
"/system/sd/xbin/su",
|
|
151
|
+
"/system/bin/failsafe/su",
|
|
152
|
+
"/data/local/su",
|
|
153
|
+
"/su/bin/su",
|
|
154
|
+
"/magisk/.core/bin/su"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return suPaths.any { File(it).exists() && File(it).canExecute() }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check for busybox binary
|
|
162
|
+
*/
|
|
163
|
+
private fun checkForBusybox(): Boolean {
|
|
164
|
+
val busyboxPaths = listOf(
|
|
165
|
+
"/system/xbin/busybox",
|
|
166
|
+
"/system/bin/busybox",
|
|
167
|
+
"/sbin/busybox",
|
|
168
|
+
"/data/local/xbin/busybox",
|
|
169
|
+
"/data/local/bin/busybox"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return busyboxPaths.any { File(it).exists() }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check for root management apps
|
|
177
|
+
*/
|
|
178
|
+
private fun checkRootApps(): Boolean {
|
|
179
|
+
val rootApps = listOf(
|
|
180
|
+
"com.noshufou.android.su",
|
|
181
|
+
"com.thirdparty.superuser",
|
|
182
|
+
"eu.chainfire.supersu",
|
|
183
|
+
"com.koushikdutta.superuser",
|
|
184
|
+
"com.topjohnwu.magisk",
|
|
185
|
+
"com.topjohnwu.magisk.ui",
|
|
186
|
+
"com.kingroot.kinguser",
|
|
187
|
+
"com.kingo.root",
|
|
188
|
+
"com.smedialink.oneclickroot",
|
|
189
|
+
"com.zhiqupk.root",
|
|
190
|
+
"com.alephzain.framaroot",
|
|
191
|
+
"com.ramdroid.appquarantine",
|
|
192
|
+
"com.ramdroid.appquarantinepro",
|
|
193
|
+
"com.devadvance.rootcloak",
|
|
194
|
+
"com.devadvance.rootcloakplus",
|
|
195
|
+
"de.robv.android.xposed.installer",
|
|
196
|
+
"com.saurik.substrate",
|
|
197
|
+
"com.zachspong.temprootremovejb",
|
|
198
|
+
"com.amphoras.hidemyroot",
|
|
199
|
+
"com.amphoras.hidemyrootadfree",
|
|
200
|
+
"com.formyhm.hiderootPremium",
|
|
201
|
+
"com.formyhm.hideroot",
|
|
202
|
+
"me.phh.superuser",
|
|
203
|
+
"eu.chainfire.firefdsuid",
|
|
204
|
+
"com.koushikdutta.rommanager",
|
|
205
|
+
"com.koushikdutta.rommanager.license",
|
|
206
|
+
"com.dimonvideo.luckypatcher",
|
|
207
|
+
"com.chelpus.luckypatcher",
|
|
208
|
+
"com.android.vending.billing.InAppBillingService.COIN",
|
|
209
|
+
"com.android.vending.billing.InAppBillingService.LUCK",
|
|
210
|
+
"com.topjohnwu.magisk.ua"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return rootApps.any { isPackageInstalled(it) }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if package is installed
|
|
218
|
+
*/
|
|
219
|
+
private fun isPackageInstalled(packageName: String): Boolean {
|
|
220
|
+
return try {
|
|
221
|
+
context.packageManager.getPackageInfo(packageName, 0)
|
|
222
|
+
true
|
|
223
|
+
} catch (e: PackageManager.NameNotFoundException) {
|
|
224
|
+
false
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Check system properties for root indicators
|
|
230
|
+
*/
|
|
231
|
+
private fun checkSystemProperties(): Boolean {
|
|
232
|
+
return checkDangerousSystemProperties() ||
|
|
233
|
+
checkSystemRemounted() ||
|
|
234
|
+
checkForDangerousBuildProps()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Check for dangerous system properties
|
|
239
|
+
*/
|
|
240
|
+
private fun checkDangerousSystemProperties(): Boolean {
|
|
241
|
+
val dangerousProps = listOf(
|
|
242
|
+
"ro.debuggable",
|
|
243
|
+
"ro.secure",
|
|
244
|
+
"service.adb.root",
|
|
245
|
+
"ro.build.selinux"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
for (prop in dangerousProps) {
|
|
249
|
+
val value = getSystemProperty(prop)
|
|
250
|
+
when (prop) {
|
|
251
|
+
"ro.debuggable" -> {
|
|
252
|
+
// 1 means debuggable (should be 0 on production)
|
|
253
|
+
if (value == "1") return true
|
|
254
|
+
}
|
|
255
|
+
"ro.secure" -> {
|
|
256
|
+
// 0 means insecure (should be 1 on production)
|
|
257
|
+
if (value == "0") return true
|
|
258
|
+
}
|
|
259
|
+
"service.adb.root" -> {
|
|
260
|
+
// 1 means adb root is enabled
|
|
261
|
+
if (value == "1") return true
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return false
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Check if system is remounted as RW (indicates root)
|
|
271
|
+
*/
|
|
272
|
+
private fun checkSystemRemounted(): Boolean {
|
|
273
|
+
val systemMounts = listOf(
|
|
274
|
+
"/system",
|
|
275
|
+
"/vendor",
|
|
276
|
+
"/product",
|
|
277
|
+
"/system_ext"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
// Read mount info
|
|
281
|
+
try {
|
|
282
|
+
val bufferedReader = BufferedReader(InputStreamReader(
|
|
283
|
+
File("/proc/mounts").inputStream()
|
|
284
|
+
))
|
|
285
|
+
|
|
286
|
+
bufferedReader.use { reader ->
|
|
287
|
+
var line: String?
|
|
288
|
+
while (reader.readLine().also { line = it } != null) {
|
|
289
|
+
for (mount in systemMounts) {
|
|
290
|
+
if (line?.contains(mount) == true) {
|
|
291
|
+
// Check if mounted RW (read-write)
|
|
292
|
+
if (line?.contains("rw,") == true ||
|
|
293
|
+
line?.contains(" rw ") == true) {
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} catch (e: Exception) {
|
|
301
|
+
// Ignore errors
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return false
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Check for dangerous build.prop modifications
|
|
309
|
+
*/
|
|
310
|
+
private fun checkForDangerousBuildProps(): Boolean {
|
|
311
|
+
try {
|
|
312
|
+
val buildProps = listOf(
|
|
313
|
+
"/system/build.prop",
|
|
314
|
+
"/vendor/build.prop",
|
|
315
|
+
"/product/build.prop",
|
|
316
|
+
"/system_ext/build.prop"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
for (propFile in buildProps) {
|
|
320
|
+
val file = File(propFile)
|
|
321
|
+
if (file.exists()) {
|
|
322
|
+
// Check if file is writable (should not be on production)
|
|
323
|
+
if (file.canWrite()) {
|
|
324
|
+
return true
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} catch (e: Exception) {
|
|
329
|
+
// Ignore errors
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return false
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get system property value
|
|
337
|
+
*/
|
|
338
|
+
private fun getSystemProperty(prop: String): String? {
|
|
339
|
+
return try {
|
|
340
|
+
val process = Runtime.getRuntime().exec("getprop $prop")
|
|
341
|
+
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
|
342
|
+
val value = reader.readLine()
|
|
343
|
+
reader.close()
|
|
344
|
+
value
|
|
345
|
+
} catch (e: Exception) {
|
|
346
|
+
null
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _default = exports.default = _reactNative.TurboModuleRegistry.getEnforcing('DeviceSecurity');
|
|
9
|
+
//# sourceMappingURL=NativeDeviceSecurity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeDeviceSecurity.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAmD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GA+BpCC,gCAAmB,CAACC,YAAY,CAAO,gBAAgB,CAAC","ignoreList":[]}
|