之前提到当安装Xposed framework
后,Xposed framework
会替换一个新的app_process
至/system/bin/
中,app_process
就是Android
中的Zygote
进程,这期以app_process
的实现为主线来详细分析Xposed framework
的实现。
首先从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
系统调用创建子进程,并在子进程中调用systemService
和appService
来添加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
的动态库,这个动态库分art
和dalvik
两个版本,然后调用这个动态库的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
,这个接口会进一步调用initXposedBridge
和initZygoteService
来初始化环境,先往下延伸,看到在onVmCreatedCommon
最后又调用了一个onVmCreated
,这个onVmCreated
的实现在如上代码的最后一部分,就是给xposed_callback_class
和xposed_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_SERVICE
和CLASS_FILE_RESULT
。
至此完成了框架的初始化工作。我们来回顾一下这个过程,首先注册了两个服务,然后创建了几个重要的类,并注册了它们的native方法,另外还给ArtMethod
的xposed_callback_class
和xposed_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);
...
}
可以结合上一期的分析一起看,此处借助ArtMethod
的xposed_callback_class
和xposed_callback_method
调用handleHookedMethod
方法。
Xposed
的安装方式官方都有教程,Android5.0以上需要刷flash。如果你有一台运行Android系统的开发板,并且支持从SD卡启动,那么有一种手动安装Xposed
的方法可供选择。步骤如下:
# ln -sf /media/user/system /system
# unzip xposed-v84-sdk22-arm64.zip
cp META-INF/com/google/android/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
# sh flash-script.sh
成功后,会看到如下:
Here's what it spit out for me
******************************
Xposed framework installer zip
******************************
- Checking environment
Xposed version: 84
- Placing files
- Done