第十九节 Android APP 安全机制
(第1章 安卓逆向概论)
学习目标
学完本节,希望你能够:搞清 Android 安全机制(沙盒、SELinux、签名、Root 检测);掌握代码保护(混淆、加固、反调试);知道存储安全、别明文泄露;会动态分析、绕过 Root 检测/反调试/加固(仅学习);能分析并绕过 APP 安全防护。
一、应用沙盒是啥?
用一句话说清楚
- 每个 Android APP 运行在 独立的 Linux 进程 中,具有 独立的 UID/GID。
- 不同应用的文件、数据、进程默认不能互相访问。
查看某应用的 UID
bash
adb shell dumpsys package com.example.app | grep userId=
示例输出:
userId=10123
👉 每个 APP 都有唯一的 UID,防止数据泄露。
尝试访问其他 APP 目录
bash
adb shell ls /data/data/com.other.app/
如果未 Root,访问会被拒绝!
二、SELinux 是啥?
SELinux(Security-Enhanced Linux)用于限制 进程间通信、访问系统资源。
查看 SELinux 状态
bash
adb shell getenforce
示例输出:
Enforcing # 说明 SELinux 已启用
禁用 SELinux(Root 权限)
bash
adb shell setenforce 0
查看 SELinux 拒绝的访问
bash
adb shell dmesg | grep avc
三、Android 签名验证是咋回事?
Android APP 在安装时需要 数字签名 以确保应用完整性。
检查 APK 签名
bash
apksigner verify --print-certs app.apk
示例输出:
Signer #1 certificate DN: CN=Developer, O=Example Corp, C=US
绕过签名校验
在 AndroidManifest.xml 添加:
xml
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
然后重新签名:
bash
zipalign -v 4 modded.apk aligned.apk
apksigner sign --ks my.keystore --out signed.apk aligned.apk
adb install signed.apk
四、Root 检测是啥、咋绕过?
许多应用 检测设备是否 Root 以防止被逆向分析。
常见 Root 检测方法
- 检查 su
java
File f = new File("/system/bin/su");
if (f.exists()) {
Log.d("RootCheck", "Device is Rooted!");
}
- 检测 Root 进程
bash
adb shell ps -A | grep magiskd
示例输出:
magiskd 1023 567 123456K fg 00000000 S magiskd
绕过 Root 检测
使用 Frida Hook File.exists()
js
Java.perform(function() {
var File = Java.use("java.io.File");
File.exists.implementation = function() {
return false;
};
});
执行 Frida
bash
frida -U -n com.example.app -e "..."
五、代码混淆和加固是啥?
1. ProGuard 混淆
proguard
-dontobfuscate
-keep class com.example.** { *; }
反混淆 DEX
bash
jadx -d output/ app.apk
2. 加固
加固(如 360加固, 腾讯加固)用于 隐藏代码逻辑,防止逆向。
检查 APK 是否加固
bash
unzip -l app.apk | grep classes
如果 classes.dex 被拆分或替换,则 APK 可能被加固。
绕过加固(Frida Dump DEX)
bash
frida -U -n com.example.app -e "Java.perform(function() {
Java.use('dalvik.system.BaseDexClassLoader').loadClass.implementation = function(name) {
console.log('Load Class: ' + name);
};
});"
六、存储安全咋做?
1. 避免明文存储
❌ 不安全
java
SharedPreferences prefs = getSharedPreferences("settings", MODE_PRIVATE);
prefs.edit().putString("password", "123456").apply();
查看存储数据
bash
adb shell cat /data/data/com.example.app/shared_prefs/settings.xml
解决方案:使用加密存储
java
EncryptedSharedPreferences encryptedPrefs = EncryptedSharedPreferences.create(
"settings",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
encryptedPrefs.edit().putString("password", "123456").apply();
七、逆向里咋绕过这些机制?(仅学习)
1. 绕过 Root 检测
js
Java.perform(function() {
var File = Java.use("java.io.File");
File.exists.implementation = function() {
return false;
};
});
2. 绕过反调试
js
Java.perform(function() {
var Debug = Java.use("android.os.Debug");
Debug.isDebuggerConnected.implementation = function() {
return false;
};
});
3. Dump DEX
js
Java.perform(function() {
var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
DexClassLoader.loadClass.implementation = function(name) {
console.log("Hooked Dex Load: " + name);
return this.loadClass(name);
};
});
动手练一练
- 检查应用的 UID
bash
adb shell dumpsys package com.example.app | grep userId=
- 禁用 SELinux
bash
adb shell setenforce 0
- Hook File.exists() 绕过 Root 检测
js
Java.perform(function() {
var File = Java.use("java.io.File");
File.exists.implementation = function() {
return false;
};
});
- Dump DEX
js
Java.perform(function() {
var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
DexClassLoader.loadClass.implementation = function(name) {
console.log("Hooked Dex Load: " + name);
return this.loadClass(name);
};
});
本节小结
你只要记住这几条就行:每个 APP 独立 UID 沙盒,SELinux 再加强制访问控制;签名保证完整性,改包要重签;Root 检测、反调试、加固都可被 Hook 或 dump 绕过(仅限学习);敏感数据别明文放 SharedPreferences,用加密存储。
本节思考与练习
- 概念:沙盒、SELinux、签名各在干啥?Root 检测一般查啥?
- 动手:用 Frida Hook
File.exists()绕过某 APP 的 Root 检测(仅学习环境)。 - 动手:用 Frida Hook
Debug.isDebuggerConnected绕过反调试。
下一节预告 :下一节讲 CTF 逆向挑战(初级)(第二十节),用 ELF/APK/JNI 小题目练手拿 flag。