安卓环境检查调试
1. 问题描述
手机刷入 Magisk 等 Root 工具后,某些应用会因为检测到环境异常而无法运行。虽然一些环境隐藏方案可以解决大部分问题,但是仍有某些难以被隐藏的环境问题,比如手机刷入了第三方 ROM LineageOS等,能检测出 LineageOS 的手段有很多难以防止。所以这就需要针对特定的 app 去查看它到底检测了哪些环境问题,从而进行针对性的解决。
项目地址: https://github.com/lxmghct/xposed-env-logger
2. 实现思路
我打算仅针对自己的特殊需求来开发这样一个工具,而并不是覆盖所有的环境检测场景。我目前 Hook 了下面的几个 API 来检测环境问题:
android.os.SystemProperties.get(String key):获取系统属性java.lang.System.getProperty(String key):获取系统属性java.io.File的三个构造函数:创建文件对象java.io.File.exists():检查文件是否存在java.io.FileInputStream(File file):打开文件输入流java.io.FileReader(String path):打开文件读取器
3. xposed 开发环境搭建
- 在 settings.gradle 中添加 xposed 仓库
dependencyResolutionManagement { repositories { maven { url 'https://api.xposed.info/' } } } - 在 app/build.gradle 中添加 xposed 依赖
dependencies { compileOnly 'de.robv.android.xposed:api:82' } - 在 AndroidManifest.xml 中添加 xposed 标识
<meta-data android:name="xposedmodule" android:value="true"/> <meta-data android:name="xposeddescription" android:value="@string/xposed_description"/> <meta-data android:name="xposedminversion" android:value="54"/> <meta-data android:name="xposedscope" android:resource="@array/xposedscope"/> - 在 assets 目录下创建 xposed_init 文件,内容为包名+类名
com.example.envlogger.MainHook
4. 代码实现
package com.example.envlogger;
import java.io.File;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class MainHook implements IXposedHookLoadPackage {
private void writeLog(String msg) {
XposedBridge.log("【EnvLogger】 " + msg);
}
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Hooking " + lpparam.packageName);
// Hook SystemProperties.get(String)
XposedHelpers.findAndHookMethod("android.os.SystemProperties", lpparam.classLoader,
"get", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String key = (String) param.args[0];
String value = (String) param.getResult();
writeLog("[SystemProperties.get] " + key + " = " + value);
}
});
// Hook System.getProperty(String)
XposedHelpers.findAndHookMethod("java.lang.System", lpparam.classLoader,
"getProperty", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String key = (String) param.args[0];
String value = (String) param.getResult();
writeLog("[System.getProperty] " + key + " = " + value);
}
});
// Hook File(String)
XposedHelpers.findAndHookConstructor("java.io.File", lpparam.classLoader,
String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String path = (String) param.args[0];
writeLog("[File] new File: " + path);
}
});
// File(String parent, String child)
XposedHelpers.findAndHookConstructor("java.io.File", lpparam.classLoader,
String.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String parent = (String) param.args[0];
String child = (String) param.args[1];
writeLog("[File] new File: parent=" + parent + " child=" + child);
}
});
// File(File parent, String child)
XposedHelpers.findAndHookConstructor("java.io.File", lpparam.classLoader,
java.io.File.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
File parent = (File) param.args[0];
String child = (String) param.args[1];
writeLog("[File] new File: parent=" + parent.getAbsolutePath() + " child=" + child);
}
});
// Hook File.exists()
XposedHelpers.findAndHookMethod("java.io.File", lpparam.classLoader,
"exists", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
File f = (File) param.thisObject;
boolean result = (boolean) param.getResult();
writeLog("[File.exists] " + f.getAbsolutePath() + " = " + result);
}
});
// Hook FileInputStream(File)
XposedHelpers.findAndHookConstructor("java.io.FileInputStream", lpparam.classLoader,
java.io.File.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
File file = (File) param.args[0];
writeLog("[FileInputStream] " + file.getAbsolutePath());
}
});
XposedHelpers.findAndHookConstructor("java.io.FileReader", lpparam.classLoader,
String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String path = (String) param.args[0];
writeLog("[FileReader] " + path);
}
});
}
}
5. 后续改进
- 可以考虑增加更多的 Hook 点,覆盖更多的环境检测手段。
- 输出日志到文件,方便后续分析。