Android开发经验谈Android开发Android安全-逆向

Xposed系列之Demo上手指南及源码解析(一)

2020-07-08  本文已影响0人  小不点CC3

先附上Demo代码地址:https://github.com/xbdcc/CXposed/tree/master/demo

Xposed简介

百度百科介绍:

Xposed框架(Xposed Framework)是一套开源的、在Android高权限模式下运行的框架服务,可以在不修改APK文件的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。`

Xposed相关工具

三个工具首页分别长这样:

Xposed Installer VirtualXposed 太极
image image image

Xposed Demo

新建项目配置Xposed环境

    compileOnly 'de.robv.android.xposed:api:82'
       <meta-data
                android:name="xposedmodule"
                android:value="true" />
        <meta-data
                android:name="xposeddescription"
                android:value="A Xposed demo." />
        <meta-data
                android:name="xposedminversion"
                android:value="89" />

编写Hook代码

class DemoClass {

    private val name = "carlos"

    fun printlnName() {
        log("name is:$name")
    }

    companion object {

        @JvmStatic
        fun printlnHelloWorld() {
            log("hello world!")
        }

    }

}
class MainHook : IXposedHookLoadPackage {

    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {
    }

}

    private fun hookDemoClass(lpparam: XC_LoadPackage.LoadPackageParam) {
        // 通过类加载器加载DemoClass类
        val hookClass = lpparam.classLoader.loadClass("com.carlos.cxposed.demo.DemoClass") ?: return
        // 通过XposedHelpers调用静态方法printlnHelloWorld
        XposedHelpers.callStaticMethod(hookClass, "printlnHelloWorld")
        // 获取DemoClass的类对象
        val demoClass = hookClass.newInstance()
        // 获取私有字段name
        val field = hookClass.getDeclaredField("name")
        // 私有字段name访问属性改为公有
        field.isAccessible = true
        // 给字段name赋值为"xbd"
        field.set(demoClass, "xbd")
        // 通过XposedHelpers调用非静态方法printlnName
        XposedHelpers.callMethod(hookClass.newInstance(), "printlnName")
    }
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        log(getLog())
    }

    private fun getLog() : String {
        return "hello world"
    }

    fun click(view: View) {
        log("click")
    }

}
    private fun hookMainActivity(lpparam: XC_LoadPackage.LoadPackageParam) {
        val hookClass = lpparam.classLoader.loadClass("com.carlos.cxposed.demo.MainActivity")

        XposedHelpers.findAndHookMethod(hookClass, "getLog", object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam?) {
                xlog("hook before getLog")
                // 修改方法返回值
                param?.result = "this is a hook message."
            }

            override fun afterHookedMethod(param: MethodHookParam?) {
                xlog("hook after getLog")
            }
        })

        XposedHelpers.findAndHookMethod(hookClass, "click", View::class.java,
            object : XC_MethodReplacement() {
                override fun replaceHookedMethod(param: MethodHookParam?): Any {
                    xlog("hook replace click")
                    val thisObject = param?.thisObject ?: return ""
                    // 修改textView的显示内容
                    val activity = thisObject as Activity
                    val textView = activity.findViewById<TextView>(R.id.textview)
                    textView.text = "carlos"
                    return ""
                }
            })

    }
com.carlos.cxposed.demo.MainHook
07-06 22:28:43.470 3965-3965/? I/Xposed: MainHook->MainHook->hook an app start:com.carlos.cxposed.demo
07-06 22:28:43.472 3965-3965/? D/MainHook->: hello world!
07-06 22:28:43.472 3965-3965/? D/MainHook->: name is:xbd
07-06 22:28:45.352 3965-3965/com.carlos.cxposed.demo I/Xposed: MainHook->hook before getLog
07-06 22:28:45.352 3965-3965/com.carlos.cxposed.demo I/Xposed: MainHook->hook after getLog
07-06 22:28:45.352 3965-3965/com.carlos.cxposed.demo D/MainHook->: this is a hook message.
07-06 22:28:51.193 3965-3965/com.carlos.cxposed.demo I/Xposed: MainHook->hook replace click

Xposed原理

Xposed还有C库,我们这里简单分析下我们引用的他的Java层de.robv.android.xposed:api:82,看下我们用到的两个类XposedHelpersXposedBridge的源码

XposedBridge解析

    hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
    public static void hookLoadPackage(XC_LoadPackage callback) {
        synchronized (sLoadedPackageCallbacks) {
            sLoadedPackageCallbacks.add(callback);
        }
    }
    /**
     * Load a module from an APK by calling the init(String) method for all classes defined
     * in <code>assets/xposed_init</code>.
     */
    private static void loadModule(String apk) {
        log("Loading modules from " + apk);

        if (!new File(apk).exists()) {
            log("  File does not exist");
            return;
        }

        ClassLoader mcl = new PathClassLoader(apk, BOOTCLASSLOADER);
        InputStream is = mcl.getResourceAsStream("assets/xposed_init");
        if (is == null) {
            log("assets/xposed_init not found in the APK");
            return;
        }

        BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is));
        try {
            String moduleClassName;
            while ((moduleClassName = moduleClassesReader.readLine()) != null) {
                moduleClassName = moduleClassName.trim();
                if (moduleClassName.isEmpty() || moduleClassName.startsWith("#"))
                    continue;

                try {
                    log ("  Loading class " + moduleClassName);
                    Class<?> moduleClass = mcl.loadClass(moduleClassName);

                    if (!IXposedMod.class.isAssignableFrom(moduleClass)) {
                        log ("    This class doesn't implement any sub-interface of IXposedMod, skipping it");
                        continue;
                    } else if (disableResources && IXposedHookInitPackageResources.class.isAssignableFrom(moduleClass)) {
                        log ("    This class requires resource-related hooks (which are disabled), skipping it.");
                        continue;
                    }

                    final Object moduleInstance = moduleClass.newInstance();
                    if (isZygote) {
                        if (moduleInstance instanceof IXposedHookZygoteInit) {
                            IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
                            param.modulePath = apk;
                            param.startsSystemServer = startsSystemServer;
                            ((IXposedHookZygoteInit) moduleInstance).initZygote(param);
                        }

                        if (moduleInstance instanceof IXposedHookLoadPackage)
                            hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

                        if (moduleInstance instanceof IXposedHookInitPackageResources)
                            hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
                    } else {
                        if (moduleInstance instanceof IXposedHookCmdInit) {
                            IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
                            param.modulePath = apk;
                            param.startClassName = startClassName;
                            ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
                        }
                    }
                } catch (Throwable t) {
                    log(t);
                }
            }
        } catch (IOException e) {
            log(e);
        } finally {
            try {
                is.close();
            } catch (IOException ignored) {}
        }
    }
        InputStream is = mcl.getResourceAsStream("assets/xposed_init");
        if (is == null) {
            log("assets/xposed_init not found in the APK");
            return;
        }
    protected static void main(String[] args) {
        // Initialize the Xposed framework and modules
        try {
            SELinuxHelper.initOnce();
            SELinuxHelper.initForProcess(null);

            runtime = getRuntime();
            if (initNative()) {
                XPOSED_BRIDGE_VERSION = getXposedVersion();
                if (isZygote) {
                    startsSystemServer = startsSystemServer();
                    initForZygote();
                }

                loadModules();
            } else {
                log("Errors during native Xposed initialization");
            }
        } catch (Throwable t) {
            log("Errors during Xposed initialization");
            log(t);
            disableHooks = true;
        }

        // Call the original startup code
        if (isZygote)
            ZygoteInit.main(args);
        else
            RuntimeInit.main(args);
    }
    public static Object callMethod(Object obj, String methodName, Object... args) {
        try {
            return findMethodBestMatch(obj.getClass(), methodName, args).invoke(obj, args);
        } catch (IllegalAccessException e) {
            // should not happen
            XposedBridge.log(e);
            throw new IllegalAccessError(e.getMessage());
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (InvocationTargetException e) {
            throw new InvocationTargetError(e.getCause());
        }
    }
    public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
        if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_MethodHook))
            throw new IllegalArgumentException("no callback defined");

        XC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];
        Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));

        return XposedBridge.hookMethod(m, callback);
    }
    // normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
        findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                ActivityThread activityThread = (ActivityThread) param.thisObject;
                ApplicationInfo appInfo = (ApplicationInfo) getObjectField(param.args[0], "appInfo");
                String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
                SELinuxHelper.initForProcess(reportedPackageName);
                ComponentName instrumentationName = (ComponentName) getObjectField(param.args[0], "instrumentationName");
                if (instrumentationName != null) {
                    XposedBridge.log("Instrumentation detected, disabling framework for " + reportedPackageName);
                    disableHooks = true;
                    return;
                }
                CompatibilityInfo compatInfo = (CompatibilityInfo) getObjectField(param.args[0], "compatInfo");
                if (appInfo.sourceDir == null)
                    return;

                setObjectField(activityThread, "mBoundApplication", param.args[0]);
                loadedPackagesInProcess.add(reportedPackageName);
                LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
                XResources.setPackageNameForResDir(appInfo.packageName, loadedApk.getResDir());

                LoadPackageParam lpparam = new LoadPackageParam(sLoadedPackageCallbacks);
                lpparam.packageName = reportedPackageName;
                lpparam.processName = (String) getObjectField(param.args[0], "processName");
                lpparam.classLoader = loadedApk.getClassLoader();
                lpparam.appInfo = appInfo;
                lpparam.isFirstApplication = true;
                XC_LoadPackage.callAll(lpparam);

                if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME))
                    hookXposedInstaller(lpparam.classLoader);
            }
        });
上一篇 下一篇

猜你喜欢

热点阅读