The quiter you become,the more you are able to hear!

Xposed demo分析

Author: geneblue

Blog: https://geneblue.github.io/

代码下载地址:这里


MainActivity类

下面从主函数MainActivity开始入手分析:

MainActivity

MainActivity主要是处理页面上的一些控件。modifyMac()方法是关键,主要是通过调用PrivacyManager类中的setValue()方法,设置mac值。

PrivacyManager类没有做任何实际处理,只是调用了PrivacyService中的getSetting()和setValue()方法。 #### XParam类 XParam类是非常基础的参数类,这个类里主要是通过Xposed提供的XC_MethodHook类的内部类MethodHookParam获取hook方法参数,如方法method,对象thisObject,方法调用的参数args,返回的对象result,异常抛出情况throwable等。

Xparam

从图可以看出,XParam类使用的字段和方法。fromXposed()方法就是将MethodHookParam类中关于被hook的方法信息(上述)赋值给XParam的字段,并返回一个XParam对象。doesReturn()方法判断是否有返回的hook的方法,doesThrow()方法判断hook的方法是否抛出了异常。

XGene类

以下是程序在Xposed中的入口类XGene

XGene

XGene类中主要实现了定义服务(AIDL)的注册和方法的hook操作。从上图可以看出,XGene类实现了两个接口。XposedHookedLoadPackage接口定义了加载应用程序包的时候执行怎样的用户操作,具体实现是XGene中的handleLoadPackage()方法,在这里,这个方法什么都没做;IXposedHookZygoteInit接口主要定义了hook Zygote初始化的过程,具体的实现在XGene类的initZygote()方法中。initZygote()方法实现了定义的服务(AIDL)的注册工作,将自定义的服务注册到系统SystemServer类中作为系统服务。在应用程序调用SystemServer中的方法时会首先调用定义的方法,这样就可以达到返回虚假值得目的。这是程序的一个关键点,以下是它的实现代码:

public void initZygote(StartupParam startupParam) throws Throwable {
    try {
        Class<?> cSystemServer = Class
                .forName("com.android.server.SystemServer");
                Method mMain = cSystemServer.getDeclaredMethod("main",
                String[].class); 
        // 使用Xposed来hook
        XposedBridge.hookMethod(mMain, new XC_MethodHook() {
            protected void beforeHookedMethod(MethodHookParam param)
                    throws Throwable {
                PrivacyService.register(); // 服务注册
                
            }
        });
    } catch (Throwable e) {
           }
    hookAll(XContextImpl.getInstances(), "1");  //XContextImpl类必须存
}

StrartupParam类是接口IXposedHookZygoteInit的一个内部类,只定义了一个String类型的字段modulePath,在这里并未使用到。代码开始通过Class类的静态方法forName()获取了SystemServer类,然后使用java反射机制获取到SystemServer对象的main方法,这也是Zygote创建System_Server进程的入口点函数,之后调用XposedBridge中的静态方法hookMethod对main方法进行hook,具体的操作是在调用main方法之前就注册我们定义的服务(AIDL)。接下来hook XContextImpl类,XContextImpl中的getInstances()方法返回一个含有XContextImpl对象的list,然后对该list中的每一项都进行hook操作。具体的hook方法最终都转到hook(final XHook hook,ClassLoader classLoader,String secret)方法中。接下来具体分析这个方法。注意这个时候Zygote还未创建System_Server进程,所以这个时候注册的服务就可以作为系统的服务。

hook(final XHook hook,ClassLoader classLoader,String secret)方法共传递进来三个参数,分别是不能再扩展的XHook类,类加载器和一个String的值,这个String类型的值在这里没有用到。该方法的具体实现代码如下:

private static void hook(final XHook hook, ClassLoader classLoader,
        String secret) {
    try {
               //1
        XC_MethodHook methodHook = new XC_MethodHook() {
            protected void beforeHookedMethod(MethodHookParam param)
                    throws Throwable {
                try {
                    if (Process.myUid() <= 0) 
                        return;
                    XParam xParam = XParam.fromXposed(param);
                    hook.before(xParam);
                    if (xParam.hasResult())
                        param.setResult(xParam.getResult());
                    if (xParam.hasThrowable())
                        param.setThrowable(xParam.getThrowable());
                    param.setObjectExtra("xextra", xParam.getExtras());
                } catch (Throwable ex) {
                }
            }

            protected void afterHookedMethod(MethodHookParam param)
                    throws Throwable {
                if (!param.hasThrowable()) {
                    try {
                        if (Process.myUid() <= 0)
                            return;
                        XParam xParam = XParam.fromXposed(param);
                        xParam.setExtras(param.getObjectExtra("xextra"));

                        hook.after(xParam);
                        if (xParam.hasResult())
                            param.setResult(xParam.getResult());
                        if (xParam.hasThrowable())
                            param.setThrowable(xParam.getThrowable());
                    } catch (Throwable ex) {
                    }
                }
            }
        };

                              //2
        // 使用findClass方法找到要hook的类
        Class<?> hookClass = null;
        try {
            hookClass = findClass(hook.getClassName(), classLoader);
        } catch (Throwable ex) {
        }

                             //3
        List<Member> listMember = new ArrayList<Member>();
        Class<?> clazz = hookClass;
        while (clazz != null) {

            if (hook.getMethodName() == null) {
                // 当hook类的构造方法修饰符是public时,就将该构造方法放到listMember中
                for (Constructor<?> constructor : clazz
                        .getDeclaredConstructors()) {
                    if (Modifier.isPublic(constructor.getModifiers()) ? hook
                            .isVisible() : !hook.isVisible())
                        listMember.add(constructor);
                }
                break;
            } else {
                // 当hook类clazz的方法就是要hook的方法,并且该方法不是抽象方法时,就将该方法放到listMember中
                for (Method method : clazz.getDeclaredMethods())
                    if (method.getName().equals(hook.getMethodName())
                            && !Modifier.isAbstract(method.getModifiers()))
                        listMember.add(method);
            }
            clazz = clazz.getSuperclass();
        }

                              //4
        // Hook members
        for (Member member : listMember)
            try {
                // 方法是抽象方法就略过
                if (Modifier.isAbstract(member.getModifiers()))
                    ;
                else
                    // 当不是抽象方法时就hook
                    XposedBridge.hookMethod(member, methodHook);
            } catch (NoSuchFieldError ex) {
            } catch (Throwable ex) {
            }

        // 以下代码好像没用
        if (listMember.isEmpty()
                && !hook.getClassName()
                        .startsWith("com.google.android.gms"))
            if (!hook.isOptional())
                ;
    } catch (Throwable ex) {
    }

}

在标号1处实现的代码主要是在hook方法的前后设定好参数信息,标号2处寻找到要hook的类名,标号3处寻找将要hook类的构造方法和非抽象方法。在标号4处就将获取到的方法进行hook操作,而且这里用到了标号1处的代码。

XHook类

上述中经常使用XHook类,那这个类是干什么用的还需要解释:

XHook

XHook类是一个抽象类,则其他的类如XContextImpl,XTelephonyManager,XWifiManager都继承了这个抽象类,并实现了该抽象类中的抽象方法。可以重左图中看到,该类有两个构造方法,这两个构造方法都是对变量的赋值。Optional()方法的作用未知,isVisible()方法判断要hook的方法是否是可见的属性为public就可见。getClassName(),before(),after()方法都是抽象方法,都在子类中实现。

在分析过程中结合实验验证(将PrivacyService.java中的有关虚假信息文件读写操作的代码屏蔽掉,并另外实现一个程序通过系统api获取mac值,结果无法获取mac值),猜想此module注册成系统服务后,ServiceManager中提供的服务如WifiManager,TelephonyManager等如果要调用方法,都会被我们注册的服务拦截到,并将存储的值作为调用方法的请求值。接下来就看看PrivacyService.java中都做了哪些操作:

PrivacyService类

PrivacyService

这个类主要是AIDL接口的实现,并将设定的虚假值写到文件中,同时做了读取虚假值的操作。rigister()方法首先在本地创建了一个文件夹,这个文件夹里存放的就是以后设定虚假值的文件,然后该方法使用java反射机制调用android.os.ServiceManager类中的addService方法将定义的服务注册到系统中,作为系统级服务。getClient()方法通过反射调用android.os.ServiceManager类中的getService()方法来获取定义服务实体的代理接口。getSetting()方法通过AIDL接口实现类mPrivacyService获取存储在本地的虚假值。createInfoFile()和getInfoFile()负责本地虚假值文件的读和写。接口实现类mPrivacyService就是通过上述两个方法的文件读写操作来设定虚假值。setValue()方法通过接口实现类来设定虚假值。

最后还剩下三个文件XContextImpl.java,XWifiManager.java和XTelephonyManager.java还有待分析,这三个类都是XHook抽象类的子类: Context的理解和ContextImpl是什么见文档,XContextImpl类主要在after()方法中调用XGene类中的handleGetSystemService()方法。XWifiManager类和XTelephonyManager类大致相同。