xposed的实现

一、xposed 的使用:
hook一个方法,

需要首先实现一个XC_MethodHook 用于,定义调用 方法之前与之后执行的操作。
找到要hook的类,然后调用hookAllMethods 去hook 对应方法,代码为:

     XC_MethodHook callback = new XC_MethodHook() {
         //调用原方法之前调用
         @Override
         protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
             .......
         }
         //调用原方法之后调用
         @Override
         protected void afterHookedMethod(MethodHookParam param) throws Throwable {
             ......
         }
     };
     Class<?> clazz = findClass("class", null);
     Method method = clazz.getDeclaredMethod("method", parameterTypes);
     method.setAccessible(true);
     XposedBridge.hookMethod(m, callback);
二、hook的实现为:
    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
        ..........
        AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
        hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
        ..........
        return callback.new Unhook(hookMethod);
    }

hookMethodNative 在xposed 框架初始化时在 onVmCreated 方法内 已经注册到系统中,在 libxposed_art.cpp 为其natvie 实现:

        void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
                jobject, jint, jobject javaAdditionalInfo) {
            ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);
            // hook 指定方法。
            artMethod->EnableXposedHook(soa, javaAdditionalInfo);
        }

在Android 中,系统在native层 将java的类方法的描述为ArtMethod,调用其EnableXposedHook,对当前方法进行hook 操作
开始hook 指定方法

     void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) {
         //创建原方法的备份,backup_method
         auto* cl = Runtime::Current()->GetClassLinker();
         ArtMethod* backup_method = cl->AllocArtMethodArray(soa.Self(), 1);
         backup_method->CopyFrom(this, cl->GetImagePointerSize());
         //添加设置 backup_method的flags 为kAccXposedOriginalMethod,表示其为hook方法的原方法
         backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod);
         mirror::AbstractMethod* reflect_method;
         if (IsConstructor()) {
             reflect_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method);
         } else {
             reflect_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method);
         }
         reflect_method->SetAccessible<false>(true);
         //创建hookinfo,用于保存,hook的原方法,与其 before && after 方法
         XposedHookInfo* hookInfo = reinterpret_cast<XposedHookInfo*>(calloc(1, sizeof(XposedHookInfo)));
         hookInfo->reflectedMethod = soa.Vm()->AddGlobalRef(soa.Self(), reflect_method);
         //添加hook的方法(before && after 方法)
         hookInfo->additionalInfo = soa.Env()->NewGlobalRef(additional_info);
         //添加 backup_method(原方法)
         hookInfo->originalMethod = backup_method;
         //通过setEntryPointFromJni 将hookinfo 的写入当前 函数
         SetEntryPointFromJni(reinterpret_cast<uint8_t*>(hookInfo));
         // 重新设置 EntryPointFromQuickCompiledCode 值为GetQuickProxyInvokeHandler 的返回值art_quick_proxy_invoke_handler
         //以此达到hook的目的
         SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
         //设置当前方法以机器指令执行。
         SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
         // TODO将此方法设置为 非native(~kAccNative) 方法,且为xposed hook 的方法(kAccXposedHookedMethod)
         SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod);
     }
     //GetQuickProxyInvokeHandler 用于获取xposed 框架下,函数以本地机器码执行时的首地址(即java层函数的执行从这个C 函数还是的)
     static inline const void* GetQuickProxyInvokeHandler() {
         return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
     }
EnableXposedHook,会创建一个hookInfo 将original、before、after 方法存入,然后通过 SetEntryPointFromJni 方法将hookinfo 保存到ArtMethod 中。
在之后 通过SetEntryPointFromInterpreter 方法强制设置 java 方法为,本地机器码执行
通过SetEntryPointFromQuickCompiledCode 改变机器码执行时的函数入口为 art_quick_proxy_invoke_handler
到此函数的整个hook 流程就已经完成。
三、在方法调用时,hook的实现为:
在调用到hook 方法时,他的大概逻辑为:
    art_method.Invoke()-->   art_quick_invoke_stub-->   EntryPointFromQuickCompiledCode(art_quick_proxy_invoke_handler) --> artQuickProxyInvokeHandler--->InvokeXposedHandleHookedMethod-->soa.Env()->CallStaticObjectMethodA-->XposedBridge.handleHookedMethod
        -->开始处理hook 逻辑

详细为:
art在执行java 方法是通过调用 art_method.Invoke() 来进行执行的:

        void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) {
            //如果是本地机器码 执行,会走:
            if (!IsStatic()) {
                (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
            } else {
                (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
            }
        }

在art_method.Invoke() 中会 判断方法类型进行区别调用,假设其为非静态方法,那么会调用到汇编中的 art_quick_invoke_stub方法:

     ENTRY art_quick_invoke_stub
         .........
         INVOKE_STUB_CALL_AND_RETURN
     END art_quick_invoke_stub
     在此汇编指令中会调用 INVOKE_STUB_CALL_AND_RETURN 宏方法,实现为:
     .macro INVOKE_STUB_CALL_AND_RETURN
         // load method-> METHOD_QUICK_CODE_
         ldr x9, [x0, #ART_METHOD_QUICK_CODE
         // Branch to method.
         blr x9
     .endm

通过着两端汇编可知,系统在调用 art_quick_invoke_stub ,会找到ArtMethod的ART_METHOD_QUICK_CODE(即:entry_point_from_quick_compiled_code_)字段,这个就是EntryPointFromQuickCompiledCode,从而进入OAT函数执行。

        #define ART_METHOD_QUICK_CODE_OFFSET_64 48
            ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_64,
                art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())

而EntryPointFromQuickCompiledCode 我们在之前hook时,已经通过SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()) 方法强制性改为自己的 art_quick_proxy_invoke_handler 方法了。
那么可知,在调用art_quick_invoke_stub 时,会自动调用到我们设置的 art_quick_proxy_invoke_handler 函数中去。

        ENTRY art_quick_proxy_invoke_handler
            bl      artQuickProxyInvokeHandler  // (Method* proxy method, receiver, Thread*, SP)
        END art_quick_proxy_invoke_handler

从art_quick_proxy_invoke_handler 的汇编中,可以看出他会直接调用的到artQuickProxyInvokeHandler 方法,

        extern "C" uint64_t artQuickProxyInvokeHandler(
            ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
            const bool is_xposed = proxy_method->IsXposedHookedMethod();//判断 GetAccessFlags() 的kAccXposedHookedMethod 字段
            ......
            if (is_xposed) {
                jmethodID proxy_methodid = soa.EncodeMethod(proxy_method);
                self->EndAssertNoThreadSuspension(old_cause);
                JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args);
                local_ref_visitor.FixupReferences();
                return result.GetJ();
            }
            ......
        }

在artQuickProxyInvokeHandler方法中,系统会通过的 kAccXposedHookedMethod 字段判断当前 ArtMethod 是否被hook,如被hook那么会调用 InvokeXposedHandleHookedMethod 走他的hook调用逻辑。
InvokeXposedHandleHookedMethod 方法会从 ArtMethod 中获取 之前设置的 hookinfo,然后调用到 xposed 的java 层XposedBridge.java 去处理他的 调用逻辑

        JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, jobject rcvr_jobj, jmethodID method, std::vector<jvalue>& args) {
            //获取ArtMethod 的hookinfo 信息
            const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo();
            //将hookinfo 转为一个数组,以便和java 层进行通信调用
            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;
            //通过CallStaticObjectMethodA 调用 xposed_callback_method 类里面 xposed_callback_method 的方法
            //xposed_callback_method: XposedBridge.java
            //xposed_callback_method: handleHookedMethod 方法
            //ArtMethod 的这两个值,在系统开机时 在 onVmCreated 进行赋值的
            jobject result = soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class,
                                       ArtMethod::xposed_callback_method,
                                       invocation_args);
        }

到此会调用到 java层 XposedBridge.java 类的handleHookedMethod 方法,真正去处理 before、Original、after 这三个方法的调用关系了:

     private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable {
         AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
         //如果xposed 时关闭状态,或者callbacksSnapshot 长度为0, 那么将会走原逻辑,不会hook
         ......
         // 从callbacksSnapshot 中取出“before method“ 并调用。
         int beforeIdx = 0;
         //使用循环以防有多个before 方法。
         do {
             ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
             if (param.returnEarly) {
                 // skip remaining "before" callbacks and corresponding "after" callbacks
                 beforeIdx++;
                 break;
             }
         } while (++beforeIdx < callbacksLength);
         //调用正常的逻辑
         if (!param.returnEarly) {
             try {
                 param.setResult(invokeOriginalMethodNative(method, originalMethodId,
                     additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
             } catch (InvocationTargetException e) {
             param.setThrowable(e.getCause());
             }
         }
         // 同理调用  "after method" 的方法。
     }
四、应用的启动,与hook

app 进程的启动见:
https://blog.csdn.net/xiaolli/article/details/72869106
app的进程启动,首先走的是

core/java/android/app/ActivityThread.java----->main方法
 public static void main(String[] args) {
 			thread.attach(false, startSeq);
 }
 private void attach(boolean system, long startSeq) {
        final IActivityManager mgr = ActivityManager.getService();
        try {
     		//通过attachApplication 将自身进程attach到 AMS, 建立app和AMS 的连接
     		mgr.attachApplication(mAppThread, startSeq);                        
 		} catch (RemoteException ex) {
    		 throw ex.rethrowFromSystemServer();
 		}
}

AMS的attachApplication 方法会调用thread.bindApplication, 在走回ActivityThread 的 bindApplication方法
而bindApplication方法, 在zygote进程中xposed框架初始化时已经被hook了见:
https://blog.csdn.net/xiaolli/article/details/107506138

XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java
/*package*/ static void initForZygote() throws Throwable {
		findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
			@Override
			protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
               ......
				XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.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);
		});
		....
}

在app进程执行handleBindApplication 之前会执行 beforeHookedMethod 方法,此方法内,会加载相关类, 并hook 他们的方法

a. hook 信息的加载:XposedBridge.sLoadedPackageCallbacks
hook 信息的加载也是在zygote进程内完成:

    XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedBridge.java ---->main方法
    	XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java--->loadModules方法
    			XposedBridge-art/app/src/main/java/de/robv/android/xposed/XposedInit.java---->loadModule 方法
private static void loadModule(String apk, ClassLoader topClassLoader) {
		ZipFile zipFile = null;
		InputStream is;
		zipFile = new ZipFile(apk);
		ZipEntry zipEntry = zipFile.getEntry("assets/xposed_init");
		ClassLoader mcl = new PathClassLoader(apk, XposedBridge.BOOTCLASSLOADER);
		BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is));
		try {
			String moduleClassName;
			while ((moduleClassName = moduleClassesReader.readLine()) != null) {
				moduleClassName = moduleClassName.trim();
				try {
					Class<?> moduleClass = mcl.loadClass(moduleClassName);
					final Object moduleInstance = moduleClass.newInstance();
					if (XposedBridge.isZygote) {
						if (moduleInstance instanceof IXposedHookLoadPackage)
							XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
						if (moduleInstance instanceof IXposedHookInitPackageResources)
							XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
					} 
			}
		} catch (IOException e) {
			Log.e(TAG, "  Failed to load module from " + apk, e);
		} finally {
			closeSilently(is);
			closeSilently(zipFile);
		}
	}

XposedBridge.hookLoadPackage 方法会 将此callback 添加进sLoadedPackageCallbacks中

public static void hookLoadPackage(XC_LoadPackage callback) {    
    synchronized (sLoadedPackageCallbacks) {
        sLoadedPackageCallbacks.add(callback);
    }
}

跳回initForZygote 方法他会将sLoadedPackageCallbacks 封装到XC_LoadPackage.LoadPackageParam中的callback中之后调用XC_LoadPackage 的callAll函数

XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.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);

XposedBridge-art/app/src/main/java/de/robv/android/xposed/callbacks/XCallback.java

public static void callAll(Param param) {                                                     
    if (param.callbacks == null)
        throw new IllegalStateException("This object was not created for use with callAll");

    for (int i = 0; i < param.callbacks.length; i++) {
        try {
            ((XCallback) param.callbacks[i]).call(param);
        } catch (Throwable t) { XposedBridge.log(t); }
    }
}

XposedBridge-art/app/src/main/java/de/robv/android/xposed/callbacks/XC_LoadPackage.java

protected void call(Param param) throws Throwable {
    	if (param instanceof LoadPackageParam)
			handleLoadPackage((LoadPackageParam) param);
}

loadModule 方法中创建的为Wrapper 对象,那么会走到
XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

final class Wrapper extends XC_LoadPackage {
    private final IXposedHookLoadPackage instance;
    public Wrapper(IXposedHookLoadPackage instance) {
        this.instance = instance;
    }
    @Override
    public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {     
        instance.handleLoadPackage(lpparam);
    }
}

然后就会走到 开发者实现的handleLoadPackage 方法中取hook方法

http://www.infocool.net/kb/OtherMobile/201611/208851.html
http://www.cnblogs.com/lanrenxinxin/p/5207174.html

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值