Xposed 源码剖析(二)

0x00 背景

之前提到当安装Xposed framework后,Xposed framework会替换一个新的app_process/system/bin/中,app_process 就是Android中的Zygote进程,这期以app_process的实现为主线来详细分析Xposed framework的实现。

0x01 框架分析

首先从main函数开始:

main() {
    if (zygote) {
        isXposedLoaded = xposed::initialize(true, startSystemServer, NULL, argc, argv);
        runtimeStart(runtime, "de.robv.android.xposed.XposedBridge", args, zygote);
   }
}

在修改的main函数中,首先调用initialize函数初始化Xposed的运行时环境,然后再调用runtimeStart启动运行时环境。

initialize函数的内部实现如下:

namespace xposed {
XposedShared* xposed = new XposedShared;

/** Initialize Xposed (unless it is disabled). */
bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {
    ...
    xposed->zygote = zygote;
    xposed->startSystemServer = startSystemServer;
    xposed->startClassName = className;
    xposed->xposedVersionInt = xposedVersionInt;

    ...
    if (startSystemServer) {
        if (!xposed::service::startAll()) {
            return false;
        }
        xposed::logcat::start();
    }
    ...

    return addJarToClasspath();
}

其内部主要做了三件事情,首先给XposedShared对象内部的成员赋值,然后调用startAll()接口启动所有服务,最后调用addJarToClasspath()XposedBridge.jar添加至系统目录,之后会将它加入到Zygote进程地址空间中。

这里最重要的就是startAll(),进一步来看它的代码实现:

bool startAll() {
    bool useSingleProcess = !xposed->isSELinuxEnabled;
    if (xposed->isSELinuxEnabled && !membased::init()) {
        return false;
    }

    if (!useSingleProcess) {
      pid = fork();
      if (pid == 0) {//child
          systemService();
      }
    }

    pid = fork();
    if (pid == 0) {//child
        appService(useSingleProcess);
    }
}

代码有所删减,关于SELinux的部分不详细展开,只要知道如果开启SELinux时,需要初始化一段供Xposed使用的内存,然后通过fork系统调用创建子进程,并在子进程中调用systemServiceappService来添加Xposed需要的服务。

来看appService的代码实现:

#define XPOSED_BINDER_SYSTEM_SERVICE_NAME "user.xposed.system"
#define XPOSED_BINDER_APP_SERVICE_NAME    "user.xposed.app"

static void appService(bool useSingleProcess) {
    xposed::setProcessName(useSingleProcess ? "xposed_service" : "xposed_service_app");
    xposed::dropCapabilities();

    sp<IServiceManager> sm(defaultServiceManager());
    status_t err;
    if (useSingleProcess) {
        // Initialize the system service here as this is the only service process
        err = sm->addService(String16(XPOSED_BINDER_SYSTEM_SERVICE_NAME), new binder::XposedService(true), true);
        // The app service can be registered directly
        err = sm->addService(String16(XPOSED_BINDER_APP_SERVICE_NAME), new binder::XposedService(false), true);

    } 
    ...
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();

    IPCThreadState::self()->joinThreadPool();
}

首先通过defaultServiceManager()获取Server Manager的远程接口,即一个BpServiceManger类实例,然后通过addService接口注册新的服务。服务的定义如下所示:

class XposedService : public BnXposedService {
    public:
        XposedService(bool system);
        virtual int test() const;
        virtual status_t addService(const String16& name,
                                    const sp& service,
                                    bool allowIsolated = false) const;
        virtual status_t accessFile(const String16& filename16,
                                    int32_t mode) const;
        virtual status_t statFile(const String16& filename,
                                  int64_t* size,
                                  int64_t* mtime) const;
        virtual status_t readFile(const String16& filename16,
                                  int32_t offset,
                                  int32_t length,
                                  int64_t* size,
                                  int64_t* mtime,
                                  uint8_t** buffer,
                                  int32_t* bytesRead,
                                  String16* errormsg) const;
    private:
        bool isSystem;
};

这里不详细分析这些接口,只要知道这个服务是用来进行IPC的即可。我们来继续看Android启动虚拟机的逻辑:

AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector& options)
{
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env); 
}

Xposed实现了自己的Runtime,实现代码如下所示:

class AppRuntime : public AndroidRuntime
{
public:
    ...
    virtual void onVmCreated(JNIEnv* env)
    {
        if (isXposedLoaded)
            xposed::onVmCreated(env);
    }
}

namespace xposed {
    /** Load the libxposed_*.so(art or dalvik) library for the currently active runtime. */
    void onVmCreated(JNIEnv* env) {

        // Load the suitable libxposed_*.so for it
        dlopen("libxposed_*.so");

        // Initialize the library
        *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib");

        if (xposedInitLib(xposed)) {
            xposed->onVmCreated(env);
        }
    }
}

onVmCreated的回调进入到xposed::onVmCreated中,在它的代码实现中,首先通过dlopen打开Xposed的动态库,这个动态库分artdalvik两个版本,然后调用这个动态库的xposedInitLib接口,并且又一次调用了onVmCreated方法,几个onVmCreated只是名字一样而已。分析到这里,其实看不出什么,我们需要进一步借助libxposed_art.so的源码来分析。

实现源码在libxposed_art.cpp文件中,代码实现如下:

/** Called by Xposed's app_process replacement. */
bool xposedInitLib(XposedShared* shared) {
    xposed = shared;
    xposed->onVmCreated = &onVmCreatedCommon;
    return true;
}

void onVmCreatedCommon(JNIEnv* env) {
    if (!initXposedBridge(env) || !initZygoteService(env)) {
        return;
    }
    jclass classXTypedArray = env->FindClass(CLASS_XTYPED_ARRAY);
    ...
    prepareSubclassReplacement(env, classXTypedArray);
    if (!onVmCreated(env)) {
        return;
    }
    xposedLoadedSuccessfully = true;
    return;
}

/** Called very early during VM startup. */
bool onVmCreated(JNIEnv*) {
    // TODO: Handle CLASS_MIUI_RESOURCES?
    ArtMethod::xposed_callback_class = classXposedBridge;
    ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod;
    return true;
}

xposedInitLib接口中把onVmCreated函数指针设为onVmCreatedCommon,也就是说,在之前的xposed->onVmCreated(env);实际调用的是onVmCreatedCommon,这个接口会进一步调用initXposedBridgeinitZygoteService来初始化环境,先往下延伸,看到在onVmCreatedCommon最后又调用了一个onVmCreated,这个onVmCreated的实现在如上代码的最后一部分,就是给xposed_callback_classxposed_callback_method两个变量赋值,这两个值是通过initXposedBridge函数获取到的,来看一下它的实现:

bool initXposedBridge(JNIEnv* env) {
    classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
    ...
    classXposedBridge = reinterpret_cast(env->NewGlobalRef(classXposedBridge));

    ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);
    if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {
       ...
    }

    methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
        "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
    return true;
}

首先通过FindClass接口去找到CLASS_XPOSED_BRIDGE这个类,并创建一个该类的全局引用。然后调用register_natives_XposedBridge注册这个类的native方法。最后把methodXposedBridgeHandleHookedMethod设置成handleHookedMethod的方法ID。

回到onVmCreatedCommon函数中,在调用了initXposedBridge函数之后,还调用了initZygoteService函数,这个函数的内部实现类似于initXposedBridge,只不过初始化的类是CLASS_ZYGOTE_SERVICECLASS_FILE_RESULT

至此完成了框架的初始化工作。我们来回顾一下这个过程,首先注册了两个服务,然后创建了几个重要的类,并注册了它们的native方法,另外还给ArtMethodxposed_callback_classxposed_callback_method两个变量赋了值。那么这两个值到底有什么作用呢?

在上一期我们分析了被Hook方法的执行过程,在执行到InvokeXposedHandleHookedMethod方法时:

JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty,
                                    jobject rcvr_jobj, jmethodID method,
                                    std::vector& args) {
   ...
  const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo();

  // Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
  //                                      Object thisObject, Object[] args)
  jvalue invocation_args[5];
  invocation_args[0].l = hookInfo->reflectedMethod;
  invocation_args[1].i = 1;
  invocation_args[2].l = hookInfo->additionalInfo;
  invocation_args[3].l = rcvr_jobj;
  invocation_args[4].l = args_jobj;
  jobject result =
      soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class,
                                         ArtMethod::xposed_callback_method,
                                         invocation_args);
  ...
}

可以结合上一期的分析一起看,此处借助ArtMethodxposed_callback_classxposed_callback_method调用handleHookedMethod方法。

0x02 写在最后

Xposed的安装方式官方都有教程,Android5.0以上需要刷flash。如果你有一台运行Android系统的开发板,并且支持从SD卡启动,那么有一种手动安装Xposed的方法可供选择。步骤如下:

  1. 把烧有Android系统的SD卡插入Linux中
  2. # ln -sf /media/user/system /system
  3. 下载最新的安装包
    # unzip xposed-v84-sdk22-arm64.zip
  4. cp META-INF/com/google/android/flash-script.sh .
  5. modify flash-script.sh
and remove these lines about halfway through:

echo "- Mounting /system and /vendor read-write"
mount /system >/dev/null 2>&1
mount /vendor >/dev/null 2>&1
mount -o remount,rw /system
mount -o remount,rw /vendor >/dev/null 2>&1

and these lines near the bottom:

if [ "$API" -ge "22" ]; then
  find /system /vendor -type f -name '*.odex.gz' 2>/dev/null | while read f; do mv "$f" "$f.xposed"; done
fi
  1. Install
# sh flash-script.sh

成功后,会看到如下:

Here's what it spit out for me

******************************
Xposed framework installer zip
******************************
- Checking environment
  Xposed version: 84
- Placing files
- Done

你可能感兴趣的:(Android安全)