adroid xpose 修改java方法实例_android hook 框架 xposed 如何实现挂钩

前面知道,安装xposed框架后,系统启动,执行init, init 再调用 app_process 程序,由于这时候 app_process 已经被换了,所以app_process 启动后先进入的是 xposedbridge.class 的 main 函数, 这个函数最后才进入标准的 zygoteInit.class 的 main 函数,在进入 zygote 之前,它调用了几个函数,初始化了xposed框架,下面逐个分析。

一. initNative

Xposed.cpp (xposed): {"initNative", "()Z", (void*)de_robv_android_xposed_XposedBridge_initNative},

XposedBridge.java (xposedbridge\src\de\robv\android\xposed):if(initNative()) {

XposedBridge.java (xposedbridge\src\de\robv\android\xposed):private native static boolean initNative();

XposedBridge.class 的 initNative 是一个 native 函数,真正的实现在 Xposed.cpp 里的  de_robv_android_xposed_XposedBridge_initNative

1. de_robv_android_xposed_XposedBridge_initNative(JNIEnv* env, jclass clazz)

xposedHandleHookedMethod = (Method*) env->GetStaticMethodID(xposedClass, "handleHookedMethod","(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

首先,从xposedClass(即XposedBridge.class)类获取函数 handleHookedMethod, 这个函数是java函数,这里获取其对应的 Method 结构体指针,这样就可以在 native 里调用(jni的原理)。那么,这个 handleHookedMethod 是干嘛的呢,这个函数就是最终执行的挂钩函数,后面会分析。

Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(xposedClass, "invokeOriginalMethodNative","(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

dvmSetNativeFunc(xposedInvokeOriginalMethodNative, de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative, NULL);

其次,从xposedClass(即XposedBridge.class)类获取函数  invokeOriginalMethodNative 函数的 Method 结构体指针,然后调用  dvmSetNativeFunc 为这个java函数设置其 jni 实现  de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative , 这样,调用 invokeOriginalMethodNative  函数其实执行的是后者。

objectArrayClass = dvmFindArrayClass("[Ljava/lang/Object;", NULL);

xresourcesClass= env->FindClass(XRESOURCES_CLASS);

xresourcesClass= reinterpret_cast(env->NewGlobalRef(xresourcesClass));if (register_android_content_res_XResources(env) !=JNI_OK) {}

xresourcesTranslateResId= env->GetStaticMethodID(xresourcesClass, "translateResId","(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");

xresourcesTranslateAttrId= env->GetStaticMethodID(xresourcesClass, "translateAttrId","(Ljava/lang/String;Landroid/content/res/XResources;)I");

最后,获取其他一些java类或函数的标识

二, initXbridgeZygote

XposedBridge.class main 函数第二个重要的函数是 initXbridgeZygote

//normal process initialization (for new Activity, Service, BroadcastReceiver etc.)

findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", newXC_MethodHook() {protected voidbeforeHookedMethod(MethodHookParam param) throws Throwable {

。。。

}

首先,挂钩了 ActivityThread 类的  handleBindApplication 函数,这个函数是在android ams 系统创建新进程成功后在新进程内部调用的,挂钩这个函数,可以在新进程创建后做一些事情

这里调用了一个函数,实现了挂钩  findAndHookMethod 。这个函数定义在 XposedHelpers.java

XposedHelpers.class

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));returnXposedBridge.hookMethod(m, callback);

}

这个函数找到类 clazz 的函数 methodName 所对应的 Method结构体,然后调用 XposedBridge 的 hookMethod 函数挂钩它

XposedBridge.class

public staticXC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {

....boolean newMethod= false;

CopyOnWriteSortedSetcallbacks;

synchronized (sHookedMethodCallbacks) {

callbacks= sHookedMethodCallbacks.get(hookMethod);if (callbacks == null) {

callbacks= new CopyOnWriteSortedSet();

sHookedMethodCallbacks.put(hookMethod, callbacks);

newMethod= true;

}

}

callbacks.add(callback); // 先将被挂钩的函数 hookMethod 及挂钩它的函数保存起来if(newMethod) {

Class> declaringClass =hookMethod.getDeclaringClass();int slot = (int) getIntField(hookMethod, "slot");

Class>[] parameterTypes;

Class>returnType;if(hookMethod instanceof Method) {

parameterTypes=((Method) hookMethod).getParameterTypes();

returnType=((Method) hookMethod).getReturnType();

}else{

parameterTypes= ((Constructor>) hookMethod).getParameterTypes();

returnType= null;

}

AdditionalHookInfo additionalInfo= newAdditionalHookInfo(callbacks, parameterTypes, returnType);

hookMethodNative(hookMethod, declaringClass, slot, additionalInfo); // 最终调用hookMethodNative 函数

}return callback.newUnhook(hookMethod); }

这个函数先将被挂钩的函数 hookMethod 及挂钩它的 XC_MethodHook结构体保存起来,然后调用 hookMethodNative 函数

{"hookMethodNative", "(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V", (void*)de_robv_android_xposed_XposedBridge_hookMethodNative},

这个函数定义在 xposed.cpp 里

static void de_robv_android_xposed_XposedBridge_hookMethodNative(JNIEnv*env, jclass clazz, jobject reflectedMethodIndirect,

jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {//Usage errors?

if (declaredClassIndirect == NULL || reflectedMethodIndirect ==NULL) {

dvmThrowIllegalArgumentException("method and declaredClass must not be null");return;

}//Find the internal representation of the method

ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);

Method* method =dvmSlotToMethod(declaredClass, slot);if (method ==NULL) {

dvmThrowNoSuchMethodError("could not get internal representation for method");return;

}if(xposedIsHooked(method)) {//already hooked

return;

}//Save a copy of the original method and other hook info

XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));

memcpy(hookInfo, method,sizeof(hookInfo->originalMethodStruct));

hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));

hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));//Replace method with our own code

SET_METHOD_FLAG(method, ACC_NATIVE);

method->nativeFunc = &xposedCallHandler;

method->insns = (const u2*) hookInfo;

method->registersSize = method->insSize;

method->outsSize = 0;if (PTR_gDvmJit !=NULL) {//reset JIT cache

char currentValue = *((char*)PTR_gDvmJit +MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));if (currentValue == 0 || currentValue == 1) {

MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull)= true;

}else{

ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);

}

}

}

这个过程跟ADBI框架类似,先获取要挂钩的java函数的 Method 结构体指针,然后检查一下是否已经被挂钩了,如果是直接返回,否则,通过对 Method 结构体进行赋值的方式,完成挂钩

method->nativeFunc = &xposedCallHandler;

method->insns = (const u2*) hookInfo;

method->registersSize = method->insSize;

method->outsSize = 0;

这里挂钩函数全部使用 xposedCallHandler 这个函数。挂钩的详细信息保存在 Method结构体的 insns 成员里

xposed.cpp

static void xposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread*self) {

。。。

JValue result;

dvmCallMethod(self, xposedHandleHookedMethod, NULL,&result,

originalReflected, (int) original, additionalInfo, thisObject, argsArray);

。。。

}

可以看到,最终执行xposedHandleHookedMethod这个native函数,这个native函数前面 initNative 执行时,已经获取了它的java实现,真正的实现在

XposedBridge.java

private static Object handleHookedMethod(Member method, intoriginalMethodId, Object additionalInfoObj,

Object thisObject, Object[] args) throws Throwable {

AdditionalHookInfo additionalInfo=(AdditionalHookInfo) additionalInfoObj;if(disableHooks) {try{returninvokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,

additionalInfo.returnType, thisObject, args);

}catch(InvocationTargetException e) {throwe.getCause();

}

}

Object[] callbacksSnapshot=additionalInfo.callbacks.getSnapshot();

finalint callbacksLength =callbacksSnapshot.length;if (callbacksLength == 0) {try{returninvokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,

additionalInfo.returnType, thisObject, args);

}catch(InvocationTargetException e) {throwe.getCause();

}

}

MethodHookParam param= newMethodHookParam();

param.method=method;

param.thisObject=thisObject;

param.args=args;//call "before method" callbacks

int beforeIdx = 0;do{try{

((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);

}catch(Throwable t) {

XposedBridge.log(t);//reset result (ignoring what the unexpectedly exiting callback did)

param.setResult(null);

param.returnEarly= false;continue;

}if(param.returnEarly) {//skip remaining "before" callbacks and corresponding "after" callbacks

beforeIdx++;break;

}

}while (++beforeIdx

if (!param.returnEarly) {try{

param.setResult(invokeOriginalMethodNative(method, originalMethodId,

additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));

}catch(InvocationTargetException e) {

param.setThrowable(e.getCause());

}

}//call "after method" callbacks

int afterIdx = beforeIdx - 1;do{

Object lastResult=param.getResult();

Throwable lastThrowable=param.getThrowable();try{

((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);

}catch(Throwable t) {

XposedBridge.log(t);//reset to last result (ignoring what the unexpectedly exiting callback did)

if (lastThrowable == null)

param.setResult(lastResult);elseparam.setThrowable(lastThrowable);

}

}while (--afterIdx >= 0);//return

if(param.hasThrowable())throwparam.getThrowable();else

returnparam.getResult();

}

这个函数查找被挂钩函数的挂钩 XC_MethodHook 结构体,然后执行里边的 beforeHookedMethod 函数,再通过 invokeOriginalMethodNative 执行挂钩前的原始函数,最后再执行 afterHookedMethod 函数。

findAndHookMethod 的实现就分析完了,本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc, registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 xposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找 xposed模块及xposed框架调用 findAndHookMethod 注册的 before,after 函数,如果有,就执行,再通过 invokeOriginalMethodNative 执行挂钩前函数。

回到 initXbridgeZygote  函数,xposed 框架预先挂钩的函数,除了 handleBindApplication 外,还有

com.android.server.ServerThread 系统thread创建时触发

hookAllConstructors(LoadedApk.class, new XC_MethodHook(){。。。} apk 包加载时触发这个挂钩

findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication", 。。。

3. loadModules

/**

* Try to load all modules defined in BASE_DIR/conf/modules.list*/

private static voidloadModules(String startClassName) throws IOException {

BufferedReader apks= new BufferedReader(new FileReader(BASE_DIR + "conf/modules.list"));

String apk;while ((apk = apks.readLine()) != null) {

loadModule(apk, startClassName);

}

apks.close();

}

xposed框架本身挂钩的函数很少,真正的挂钩由具体的xposed模块实现,xposed模块也是正常的app,安装的时候注册其挂钩信息到xposed框架的 modules.list 等配置里。xposed框架初始化的时候, 加载这些模块,并完成挂钩函数的挂钩

/**

* Load a module from an APK by calling the init(String) method for all classes defined

* in assets/xposed_init.*/@SuppressWarnings("deprecation")private static voidloadModule(String apk, String startClassName) {

。。。。

while ((moduleClassName = moduleClassesReader.readLine()) != null) {//call the init(String) method of the module

final Object moduleInstance =moduleClass.newInstance();if (startClassName == null) {if(moduleInstance instanceof IXposedHookZygoteInit) {

IXposedHookZygoteInit.StartupParam param= newIXposedHookZygoteInit.StartupParam();

param.modulePath=apk;

((IXposedHookZygoteInit) moduleInstance).initZygote(param);

}if(moduleInstance instanceof IXposedHookLoadPackage)

hookLoadPackage(newIXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));if(moduleInstance instanceof IXposedHookInitPackageResources)

hookInitPackageResources(newIXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));

}else{if(moduleInstance instanceof IXposedHookCmdInit) {

IXposedHookCmdInit.StartupParam param= newIXposedHookCmdInit.StartupParam();

param.modulePath=apk;

param.startClassName=startClassName;

((IXposedHookCmdInit) moduleInstance).initCmdApp(param);

}

}

}

}

loadModule 函数从配置文件里读取所有的模块,实例化模块类,xposed 提供了几个接口类供xposed模块继承,不同的接口类对应不同的hook时机

IXposedHookZygoteInit zygote 初始化前就执行挂钩,即loadModule执行时就挂钩了

IXposedHookLoadPackage apk包加载的时候执行挂钩,先将挂钩函数保存起来,等加载apk函数执行后触发callback (这里的callback是xposed框架自己挂钩的函数),再执行模块注册的挂钩函数

IXposedHookInitPackageResources apk资源实例化时执行挂钩,同上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值