Xposed魔改笔记【未完待续】
项目合作联系微信 cjh-18888
近期对于xposed的需求不断的增加,有必要好好学一下Xposed。
同时在工作上,需要大量的修改aosp源码,每次 “ 修改-刷机-测试” 的成本过于昂贵。所以Xposed或frida是个非常不错的选择。
frida:适用于开发环境
Xposed:适用于生产环境
由于Xposed每次都需要重启,过于麻烦,这边使用lsposed.
[toc]
0x0. Xposed原理
Xposed框架是由rovo89开发的一款针对Android平台的动态劫持项目,是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,通过替换/system/bin/app_process 程序控制 zygote 进程,从而使 app_process 在启动过程中加载XposedBridge.jar 这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持
Zygote进程在启动过程中,除了创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,同时还会注册一些Android核心类的JNI方法到前面创建的Dalvik虚拟机实例中去。 一个应用程序被孵化出来的时候,其不仅会获得Zygote进程中的Dalvik虚拟机实例,还会与Zygote一起共享Java运行时库,这也是可以将XposedBridge.jar这个jar包加载到每一个Android应用中的原因。
XposedBridge有一个私有的Native方法:hookMethodNative,这个方法也在app_process中使用。该函数提供一个方法对象利用Java的反射机制来对内置的方法覆写。
0x1. 为什么要魔改?
魔改主要是因为目前有许多应用都在检测Xposed模块,所以修改掉Xposed特征是有必要的。
0x2. 有什么检测方式?
Q:为什么要研究检测方式?
A:咱不知道它的检测方式,怎么对抗?怎么魔改?
1. 安装列表检测
通过PM管理器来遍历APP安装列表,判别是否有安装Xposed installer相关的包.
与Xposed有关的包名可以到我的gitlab中下载.
http://gitlab.rpc.zhuoyue360.com/qiled/xposedchecker
意义不大, Google Play要求2011年11月后更新的应用必须适配Android11,应用将无法获取应用列表。检测安装应用已无实际意义 From: Momo
private boolean check_package() throws IOException {
// Todo:通过Pm管理器来获取应用已安装的程序(包括系统应用),和sXposedModules进行比对.
sXposedModules = readInputStream(getAssets().open("xposed_apps.list")).split("\n");
List<PackageInfo> installedPackages = getPackageManager().getInstalledPackages(0);
for (PackageInfo info:installedPackages) {
for (String pkg : sXposedModules) {
if (pkg.equals(info.packageName)) {
SendMsg("检测到Xposed相关应用[" + info.packageName + "]");
return true;
}
}
}
SendMsg("未发现");
return false;
}
2. 检测堆栈
原理
在正常的android系统启动过程中,init进程
会去解析init.rc
文件启动的一系列服务,其中就包含有app_process
进程,在app_process
执行过程中,会设置自身进程名为Zygote
,启动com.android.internal.os.ZygoteInit.Main
方法。而Xposed修改了app_process
进程,会先启动de.robv.android.xposed.XposedBridge.Main
方法,再由它去启动com.android.internal.os.ZygoteInit.Main
方法,因此堆栈信息中会多出一些内容。简单说就是Xposed
先于了Zygote
进程,因此在系统堆栈信息中会多出Xposed
相关的内容。
检测方案
在lsposed下,没生效。 有没有大佬指点指点.
private boolean check_stack(){
//Todo: 通过堆栈来检测Xposed
try{
throw new Exception();
} catch (Exception e) {
StackTraceElement[] arrayOfStackTraceElement = e.getStackTrace();
for(StackTraceElement s : arrayOfStackTraceElement){
String line = s.toString();
Log.d(TAG, "check_stack: " + line);
if (line.contains("posedBridbg")) {
Log.d(TAG, "check_stack: Check Success - " + line);
return true;
}
}
}
return false;
}
解决方案
通过Hook堆栈类StackTraceElement
,当发现Xposed
和Zygote
有错误输出时,修改输出信息,例如将输出置空来绕过错误信息检测。
XposedHelpers.findAndHookMethod(StackTraceElement.class, "getClassName", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String result = (String) param.getResult();
if (result != null){
if (result.contains("de.robv.android.xposed.")) {
param.setResult("");
// Log.i(tag, "替换了,字符串名称 " + result);
}else if(result.contains("com.android.internal.os.ZygoteInit")){
param.setResult("");
}
}
super.afterHookedMethod(param);
}
});
3. 通过反射指定类进行检测.
特么的也不行.擦. 写不下去了
尝试载入 de.robv.android.xposed.XposedHelpers
private boolean tryloadClass(String className){
try {
Class.forName(className);
ClassLoader.getSystemClassLoader().loadClass(className);
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return false;
}
4. maps扫描
原理
通过读取proc/self/maps
文件,在linux内核中,这个文件存储了进程映射了的内存区域和访问权限,因此遍历自身加载的库,就可以拿到当前上下文的so
和jar
列表,通过查找Xposed
相关文件来做检测
解决方案
因为读取的时候会调用BufferedReader
进行读取命令的内容,我们只需要Hook BufferedReader
过滤掉XposedBridge.jar
等相关内容就可以完成绕过。