Xposed魔改笔记

同时在工作上,需要大量的修改aosp源码,每次 “ 修改-刷机-测试” 的成本过于昂贵。所以Xposed或frida是个非常不错的选择。

frida:适用于开发环境

Xposed:适用于生产环境

由于Xposed每次都需要重启,过于麻烦,这边使用lsposed.

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相关的包.

意义不大, 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相关的内容。

检测方案

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,当发现XposedZygote有错误输出时,修改输出信息,例如将输出置空来绕过错误信息检测。

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内核中,这个文件存储了进程映射了的内存区域和访问权限,因此遍历自身加载的库,就可以拿到当前上下文的sojar列表,通过查找Xposed相关文件来做检测

解决方案

因为读取的时候会调用BufferedReader进行读取命令的内容,我们只需要Hook BufferedReader过滤掉XposedBridge.jar等相关内容就可以完成绕过。

 

微信公众号:囧友JIONG
版权声明:
作者:小囧
链接:https://www.jyou9.com/333
来源:囧友资源网-免费资源分享平台
文章版权归作者所有,未经允许请勿转载。
THE END
分享
二维码
< <上一篇
下一篇>>
文章目录
关闭
目 录