1.檢測(cè)是否調(diào)試APP
這個(gè)原理就是APP的AndroidManifest.xml文件中application是否配置了android:debuggable="true",設(shè)置true支持動(dòng)態(tài)調(diào)試
public static boolean isAppDebuggable(Context context) { return (context.getApplicationInfo().flags & 2) != 0; }
檢測(cè)當(dāng)前APP是否被動(dòng)態(tài)調(diào)試中
public static boolean isDebuggerAttached() { return Debug.isDebuggerConnected() || Debug.waitingForDebugger(); }
2.檢測(cè)是否模擬器
這里獲取了Android id,如果Android id是null,就是模擬器?如果包含GOLDFISH字符串也屬于模擬器
public static boolean isEmulator(Context context) {
return Build.PRODUCT.contains(SDK) || Build.HARDWARE.contains(GOLDFISH) || Build.HARDWARE.contains(RANCHU) || Settings.Secure.getString(context.getContentResolver(), "android_id") == null;
}
3.Root檢測(cè)
檢測(cè)系統(tǒng)的tags是不是test-keys。
檢測(cè)是不是安裝了supersu的APP,檢測(cè)su文件是否存在。
public static boolean isRooted(Context context) {
boolean isEmulator = isEmulator(context);
String str = Build.TAGS;
if ((isEmulator || str == null || !str.contains("test-keys")) && !new File("/system/app/Superuser.apk").exists()) {
return !isEmulator && new File("/system/xbin/su").exists();
}
return true;
}
判斷一些root的APP是否安裝
private final boolean a(Listlist) { PackageManager packageManager = this.b.getPackageManager(); boolean z = false; for (String str : list) { try { packageManager.getPackageInfo(str, 0); C0339Io.h.e(str + " ROOT management app detected!"); z = true; } catch (PackageManager.NameNotFoundException unused) { } } return z; }
通過which su尋找su文件
public final boolean f() {
/*
r5 = this;
r0 = 0
r1 = 0
java.lang.Runtime r2 = java.lang.Runtime.getRuntime() // Catch: java.lang.Throwable -> L2f
java.lang.String r3 = "which"
java.lang.String r4 = "su"
java.lang.String[] r3 = new java.lang.String[]{r3, r4} // Catch: java.lang.Throwable -> L2f
java.lang.Process r1 = r2.exec(r3) // Catch: java.lang.Throwable -> L2f
java.io.BufferedReader r2 = new java.io.BufferedReader // Catch: java.lang.Throwable -> L2f
java.io.InputStreamReader r3 = new java.io.InputStreamReader // Catch: java.lang.Throwable -> L2f
java.io.InputStream r4 = r1.getInputStream() // Catch: java.lang.Throwable -> L2f
r3.(r4) // Catch: java.lang.Throwable -> L2f
java.io.Reader r3 = (java.io.Reader) r3 // Catch: java.lang.Throwable -> L2f
r2.(r3) // Catch: java.lang.Throwable -> L2f
java.lang.String r2 = r2.readLine() // Catch: java.lang.Throwable -> L2f
if (r2 == 0) goto L29
r0 = 1
L29:
if (r1 == 0) goto L32
L2b:
r1.destroy()
goto L32
L2f:
if (r1 == 0) goto L32
goto L2b
L32:
return r0
*/
throw new UnsupportedOperationException("Method not decompiled: jmgldvb.C6107cme.f():boolean");
}
4.分區(qū)讀寫檢測(cè)
public final boolean k() {
String str;
String str2;
String[] strArr;
int i;
boolean z;
String str3;
String[] e = e();
int i2 = 0;
if (e == null) {
return false;
}
int i3 = Build.VERSION.SDK_INT;
int length = e.length;
int i4 = 0;
boolean z2 = false;
while (i4 < length) {
String str4 = e[i4];
Object[] array = new Regex(StringUtils.SPACE).split(str4, i2).toArray(new String[i2]);
Intrinsics.checkNotNull(array, "null cannot be cast to non-null type kotlin.Array");
String[] strArr2 = (String[]) array;
int i5 = 23;
if ((i3 > 23 || strArr2.length >= 4) && (i3 <= 23 || strArr2.length >= 6)) {
boolean z3 = true;
if (i3 > 23) {
str = strArr2[2];
str2 = strArr2[5];
} else {
str = strArr2[1];
str2 = strArr2[3];
}
String[] strArr3 = C4631btu.g;
int length2 = strArr3.length;
String str5 = str2;
int i6 = i2;
while (i6 < length2) {
String str6 = strArr3[i6];
if (StringsKt.equals(str, str6, z3)) {
if (Build.VERSION.SDK_INT > i5) {
str3 = str6;
str5 = StringsKt.replace$default(StringsKt.replace$default(str5, "(", "", false, 4, (Object) null), ")", "", false, 4, (Object) null);
} else {
str3 = str6;
}
strArr = e;
Object[] array2 = new Regex(",").split(str5, i2).toArray(new String[i2]);
Intrinsics.checkNotNull(array2, "null cannot be cast to non-null type kotlin.Array");
String[] strArr4 = (String[]) array2;
int length3 = strArr4.length;
int i7 = i2;
while (true) {
if (i7 >= length3) {
i = i3;
z = true;
break;
}
String[] strArr5 = strArr4;
i = i3;
z = true;
if (StringsKt.equals(strArr4[i7], "rw", true)) {
C0339Io.b(str3 + " path is mounted with rw permissions! " + str4);
z2 = true;
break;
}
i7++;
strArr4 = strArr5;
i3 = i;
}
} else {
strArr = e;
i = i3;
z = z3;
}
i6++;
z3 = z;
e = strArr;
i3 = i;
i2 = 0;
i5 = 23;
}
} else {
C0339Io.h.e("Error formatting mount line: " + str4);
}
i4++;
e = e;
i3 = i3;
i2 = 0;
}
return z2;
}
5.面具檢測(cè)
通過which命令查找面具可執(zhí)行文件。
public final boolean m() {
return a("magisk");
}
6.APP安裝時(shí)間檢測(cè)
通過日志發(fā)現(xiàn)每次啟動(dòng)都會(huì)檢測(cè)APP的安裝,更新時(shí)間。
PackageInfoUtils_generatePackageInfo_1 com.CTION, firstInstallTime: 1701134370160, lastUpdateTime: 1701134626468
7.ROM檢測(cè)
如果是開發(fā)版的ROM,通常tags都是:test-keys
public final boolean q() {
String str = Build.TAGS;
return str != null && StringsKt.contains$default((CharSequence) str, (CharSequence) "test-keys", false, 2, (Object) null);
}
8.掛載信息獲取
這個(gè)mount會(huì)把掛載信息返回,可以檢測(cè)掛載信息中是否有magisk或者zygisk字符串等。
private final String[] e() {
try {
InputStream inputStream = Runtime.getRuntime().exec("mount").getInputStream();
if (inputStream == null) {
return null;
}
String propVal = new Scanner(inputStream).useDelimiter("\A").next();
Intrinsics.checkNotNullExpressionValue(propVal, "propVal");
Object[] array = new Regex(StringUtils.LF).split(propVal, 0).toArray(new String[0]);
Intrinsics.checkNotNull(array, "null cannot be cast to non-null type kotlin.Array");
return (String[]) array;
} catch (IOException e) {
C0339Io.a((Exception) e);
return null;
} catch (NoSuchElementException e2) {
C0339Io.a((Exception) e2);
return null;
}
}
9.讀取系統(tǒng)屬性
讀取了系統(tǒng)的全部屬性,應(yīng)該手機(jī)手機(jī)型號(hào),廠商等等信息。
private final String[] g() {
try {
InputStream inputStream = Runtime.getRuntime().exec("getprop").getInputStream();
if (inputStream == null) {
return null;
}
String propVal = new Scanner(inputStream).useDelimiter("\A").next();
Intrinsics.checkNotNullExpressionValue(propVal, "propVal");
Object[] array = new Regex(StringUtils.LF).split(propVal, 0).toArray(new String[0]);
Intrinsics.checkNotNull(array, "null cannot be cast to non-null type kotlin.Array");
return (String[]) array;
} catch (IOException e) {
C0339Io.a((Exception) e);
return null;
} catch (NoSuchElementException e2) {
C0339Io.a((Exception) e2);
return null;
}
}
判斷是不是模擬器:ro.kernel.qemu
獲取手機(jī)廠商ro.product.manufacturer
品牌:ro.build.product
指紋:ro.build.fingerprint
10.開發(fā)者模式adb檢測(cè)
Settings中獲取adb狀態(tài)。
Settings.Secure
Global getInt : adb_enabled
Secure getInt : adb_wifi_enabled
11.可疑的二進(jìn)制文件
這里就是可以傳遞su,magisk等等參數(shù)
public final boolean a(String filename) {
String[] a;
Intrinsics.checkNotNullParameter(filename, "filename");
boolean z = false;
for (String str : C4631btu.d.a()) {
String str2 = str + filename;
if (new File(str, filename).exists()) {
C0339Io.b(str2 + " binary detected!");
z = true;
}
}
return z;
}
12.ro.debuggable檢測(cè)
ro.debuggable這個(gè)系統(tǒng)屬性,在user版本的系統(tǒng)中是0,如果發(fā)現(xiàn)是1可以判斷當(dāng)前系統(tǒng)是可調(diào)試狀態(tài)和root狀態(tài)。
public final boolean b() {
HashMap hashMap = new HashMap();
hashMap.put("ro.debuggable", "1");
hashMap.put("ro.secure", "0");
String[] g = g();
if (g == null) {
return false;
}
boolean z = false;
for (String str : g) {
for (String str2 : hashMap.keySet()) {
String str3 = str;
if (StringsKt.contains$default((CharSequence) str3, (CharSequence) str2, false, 2, (Object) null)) {
String str4 = "[" + ((String) hashMap.get(str2)) + ']';
if (StringsKt.contains$default((CharSequence) str3, (CharSequence) str4, false, 2, (Object) null)) {
C0339Io.b(str2 + " = " + str4 + " detected!");
z = true;
}
}
}
}
return z;
}
13.無障礙檢測(cè)
getEnabledAccessibilityServiceList
獲取當(dāng)前無障礙服務(wù)的激活的APP信息,這里拿到了激活了無障礙的APP,只有發(fā)現(xiàn)有,APP直接崩潰。
private final ListgetPackageAccessibilityServiceEnabledList(Context context) { PackageInfo packageInfo; List enabledAccessibilityServiceList = getAccessibilityService(context).getEnabledAccessibilityServiceList(-1); Intrinsics.checkNotNullExpressionValue(enabledAccessibilityServiceList, "accessibilityManager.get…ceInfo.FEEDBACK_ALL_MASK)"); List list = enabledAccessibilityServiceList; ArrayList arrayList = new ArrayList(CollectionsKt.collectionSizeOrDefault(list, 10)); for (AccessibilityServiceInfo accessibilityServiceInfo : list) { ServiceInfo serviceInfo = accessibilityServiceInfo.getResolveInfo().serviceInfo; try { packageInfo = context.getPackageManager().getPackageInfo(serviceInfo.packageName, 0); } catch (PackageManager.NameNotFoundException unused) { PackageInfo packageInfo2 = new PackageInfo(); packageInfo2.packageName = serviceInfo.packageName; packageInfo2.applicationInfo = serviceInfo.applicationInfo; packageInfo = packageInfo2; } arrayList.add(packageInfo); } return arrayList; }
14.獲取APP安裝列表
獲取了用戶安裝的所有APP,應(yīng)該是收集上傳,判斷是否裝了一些root有關(guān)的APP。
public ListgetInstalledApps(PackageManager packageManager) { Intrinsics.checkNotNullParameter(packageManager, "packageManager"); List installedPackages = packageManager.getInstalledPackages(0); Intrinsics.checkNotNullExpressionValue(installedPackages, "packageManager.getInstalledPackages(0)"); return filterNotSystemApp(installedPackages); }
?
?現(xiàn)在的APP檢測(cè)環(huán)境都挺全
審核編輯:湯梓紅
-
Android
+關(guān)注
關(guān)注
12文章
3980瀏覽量
132739 -
APP
+關(guān)注
關(guān)注
33文章
1588瀏覽量
75464 -
調(diào)試
+關(guān)注
關(guān)注
7文章
618瀏覽量
35294 -
環(huán)境檢測(cè)
+關(guān)注
關(guān)注
0文章
21瀏覽量
7791
原文標(biāo)題:Android App環(huán)境檢測(cè)分析
文章出處:【微信號(hào):哆啦安全,微信公眾號(hào):哆啦安全】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Android APP如何進(jìn)行訪問硬件驅(qū)動(dòng)
基于物聯(lián)網(wǎng)的戶外環(huán)境檢測(cè)裝置(STM32、APP、WIFI)
#Android開發(fā) Android開發(fā)環(huán)境搭建_1.9 案例1:我的第一個(gè)App(HiAndroid)
[資料分享]+《Android軟件安全與逆向分析》
如何使用BlueSTSDK Android App檢測(cè)加速事件?
分享一個(gè)不錯(cuò)的單片機(jī)與Android App的通信方案
怎么實(shí)現(xiàn)Android APP與STM32無線環(huán)境控制系統(tǒng)的設(shè)計(jì)?
Android內(nèi)核鉤子檢測(cè)技術(shù)
Android開發(fā)APP應(yīng)該如何省電
Java代碼加密支持Android App Bundle動(dòng)態(tài)化框架
Android 12“App Pairs”功能:改造分屏多任務(wù)能力
基于Android的果蠅識(shí)別APP
Android使用Wireshark抓包
Android App開發(fā)新選擇:使用Chaquopy輕松結(jié)合Python

Android App環(huán)境檢測(cè)分析
評(píng)論