在上一篇我们学习了如何在AS中创建Xposed模块,本篇来分析下官方教程中redClock的实现原理。本系列文章基于version-51

public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if(!lpparam.packageName.equals("com.android.systemui")) return;
XposedBridge.log("we are in systemui !"); findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
} @Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
TextView tv = (TextView) param.thisObject;
String text = tv.getText().toString();
tv.setText(text + ")");
tv.setTextColor(Color.RED);
}
});
}

上面的代码可以将原先在状态栏的时钟文本颜色变成红色,且在后面加")"。看下图:

主要的实现代码在findAndHookMethod函数中,查看函数定义:

findAndHookMethod:

——>findMethodExact(clazz,methodName,paramterClasses);

——>XposedBridge.hookMethod(method,callback);

先看findMethodExact,

代码很简单就是要得到methodName在android中对应的函数对象,根据findAndHookMethod的参数得到字符串sb(格式参考注释行),用sb在methodCache这个hashMap查找有没有对应的method;若没有则根据methodName和parameterTypes利用getDeclaredMethod得到对应的method。

public static Method findMethodExact(Class<?> clazz, String methodName, Class... parameterTypes) {
StringBuilder sb = new StringBuilder(clazz.getName());
sb.append('#');
sb.append(methodName);
sb.append(getParametersString(parameterTypes));
sb.append("#exact");
// sb = com.android.systemui.statusbar.policy.Clock#updateClock(参数1,参数2)#exact String fullMethodName = sb.toString();
Method e;
//methodCache键值对存放fullMethodName,method对象
if(methodCache.containsKey(fullMethodName)) {
e = (Method)methodCache.get(fullMethodName);
if(e == null) {
throw new NoSuchMethodError(fullMethodName);
} else {
return e;
}
} else {
try {
e = clazz.getDeclaredMethod(methodName, parameterTypes);
e.setAccessible(true);
methodCache.put(fullMethodName, e);
return e;
} catch (NoSuchMethodException var6) {
methodCache.put(fullMethodName, (Object)null);
throw new NoSuchMethodError(fullMethodName);
}
}
}

  看来重点在XposedBridge.hookMethod:

public static Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
if(!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
} else if(hookMethod.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
} else if(Modifier.isAbstract(hookMethod.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
} else {
//上面代码分步检查hookMethod的类型
//else中的代码得到hookMethod对应的键值对
boolean newMethod = false;
Map declaringClass = sHookedMethodCallbacks;
XposedBridge.CopyOnWriteSortedSet callbacks;
synchronized(sHookedMethodCallbacks) {
callbacks = (XposedBridge.CopyOnWriteSortedSet)sHookedMethodCallbacks.get(hookMethod);
if(callbacks == null) {
callbacks = new XposedBridge.CopyOnWriteSortedSet();
sHookedMethodCallbacks.put(hookMethod, callbacks);
newMethod = true;
}
} callbacks.add(callback);
//替换hookMehod的callbacks为callback,其实callback是存放的是hookMethod所有的callback,看定义:</span></span>
final XposedBridge.CopyOnWriteSortedSet<XC_MethodHook> callbacks;
//以上代码就是为hookMethod建立对应的callback list
//sHookedMethodCallbacks存放hookMethod和callback if(newMethod) {
Class declaringClass1 = hookMethod.getDeclaringClass();
int slot = XposedHelpers.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;
}
//以上代码得到method的参数和返回值,在AdditionalHookInfo下使用
//把callback、method参数、method返回值汇总在AdditionalHookInfo类下
XposedBridge.AdditionalHookInfo additionalInfo = new XposedBridge.AdditionalHookInfo(callbacks, parameterTypes, returnType, (XposedBridge.AdditionalHookInfo)null);
//本地函数在libxposed_dalvik.cpp
       hookMethodNative(hookMethod, declaringClass1, slot, additionalInfo);
} callback.getClass();
return new Unhook(callback, hookMethod);
//为callback绑定hookMthod
}
}

上面乱七八糟的走了这么多,登记了2个hashmap{(fullMethodName,method对象),(hookmethod,callback)}。看来java层只是管理这些结构并没有实质性的操作,进入native代码----Xposed.cpp:

//参数:reflectedMethodIndirect==>hookmethod,declaredClassIndirect==>hookmethod所在的类
// slot==>slot,additionalInfoIndirect==>结构体包含callback、parameterTypes、returnType
void 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
//获得dalvik中的classObject对象
ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
//获得dalvik中的Method,被dalvik执行的函数体不同于java层的Method,位于Object.h
Method* method = dvmSlotToMethod(declaredClass, slot);
if (method == NULL) {
dvmThrowNoSuchMethodError("Could not get internal representation for method");
return;
}
//若method已被hook则直接返回
if (isMethodHooked(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 将method替换成我们自己的代码
//设置method->accessFlags = ACC_NATIVE;表示method为native代码
//下面几行代码都是为这行代码作补充
//For a native method, we compute the size of the argument list,
//and set "insSize" and "registerSize" equal to it.
SET_METHOD_FLAG(method, ACC_NATIVE);
//给method添加callback函数表示已被hooked
method->nativeFunc = &hookedMethodCallback;
//原本的insns中存放的是dex指令,现变为hookinfo为hookedMethodCallback准备
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);

代码主要的就是红色标注的,它将Method标为native code。dalvik虚拟机在执行Method时,则会直接调用其成员变量hookedMethodCallback执行。注意,这个时候已经改变了原本的Method的执行步骤了(Xposed在此刻觉醒啦啦啦)。看下面dalvik代码,/dalvik/vm/interp/Stack.c

void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
bool fromJni, JValue* pResult, va_list args)
{
...... if (dvmIsNativeMethod(method)) {
TRACE_METHOD_ENTER(self, method);
/*
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
(*method->nativeFunc)(self->curFrame, pResult, method, self);
TRACE_METHOD_EXIT(self, method);
} else {</span>
//这里是在Inter.cpp中直接解析method
dvmInterpret(self, method, pResult);
} ......
}

   这一路走来感觉有点偏离主线类,回看下主题,在执行完 findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader,"updateClock", new XC_MethodHook() );后。updateClock(hookmethod)的accessflag = ACC_NATIVE,dalvik在执行updateClock方法时发现其为native code,则执行nativeFunc 函数体即hookedMethodCallback。ok,找到"元凶"了,继续看代码:

/* This is called when a hooked method is executed. */
void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
......
//call the Java handler function</span>
JValue result; dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
originalReflected, (int) original, additionalInfo, thisObject, argsArray);
......
}
  hookedMethodCallback函数中主要是调用dvmCallMethod去执行xposedHandleHookedMethod,而xposedHandleHookedMethod是classXposedBridge里的handleHookedMethod方法。ok,重头戏来了
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable {
XposedBridge.AdditionalHookInfo additionalInfo = (XposedBridge.AdditionalHookInfo)additionalInfoObj;
if(disableHooks) {
try {
//hook不使能,执行原method
return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);
......
}
} else {
/得到hookmethod的callback,在之前的XposedBridge.hookMethod中为callbacks添加了callback
Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
int callbacksLength = callbacksSnapshot.length;
if(callbacksLength == 0) {
//hookmethod的callback为空,hooked无意义,执行原method
try {
return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);
......
}
} else {
......
do {
label65: {
try {
((XC_MethodHook)callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
} catch (Throwable var18) {
log(var18);
param.setResult((Object)null);
param.returnEarly = false;
break label65;
} if(param.returnEarly) {
++beforeIdx;
break;
}
} ++beforeIdx;
//hookmethod有几个callback就循环几次
} while(beforeIdx < callbacksLength); if(!param.returnEarly) {
try {
//在beforeHookedMethod后执行原method
param.setResult(invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
} catch (InvocationTargetException var16) {
param.setThrowable(var16.getCause());
}
} int afterIdx = beforeIdx - 1; do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable(); try {
((XC_MethodHook)callbacksSnapshot[afterIdx]).afterHookedMethod(param);
} catch (Throwable var17) {
log(var17);
if(lastThrowable == null) {
param.setResult(lastResult);
} else {
param.setThrowable(lastThrowable);
}
} --afterIdx;
} while(afterIdx >= 0);
......
}
}
}

别看handleHookedMethod代码老长了,其实它很单纯。

第一步:是否需要执行callback,否则直接执行原method,gameover;

第二步:执行callbacks里的beforeHookedMethod方法,有几个callback执行几次beforeHookedMethod;

第三步:执行原method;

第四步:执行callbacks里的afterHookedMethod方法,类同beforeHookedMethod。

需要注意的是如果method有多个callback,其beforeHookedMethod和afterHookedMethod执行顺序:

A1.before->A2.before->原method->A2.after->A1.after,也是蛮符合客观规律的嘛。

好,关于findAndHookMethod()函数也算是从上倒下看了个遍,但你上面添这么多代码是算怎么回事呢?下面就简单总结下罗:

好了,本篇就先这样吧,太长了也不好看。下篇再分析下其余枝节:

1 handleLoadPackage 怎么生效

2 dalvik层如何回到java层(数据类型如何回传)

3 XC_MethodHook中会执行原来的java函数体,如何执行;

其他想到再分析啰,大家也可以对上述执行流程提问,我们一起探讨。

参考资料:

1 Xposed框架Java部分

2 Dalvik虚拟机的运行过程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。

Xposed学习二:实现机制的更多相关文章

  1. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  2. Struts2框架学习(二) Action

    Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...

  3. Java开发学习(二十八)----拦截器(Interceptor)详细解析

    一.拦截器概念 讲解拦截器的概念之前,我们先看一张图: (1)浏览器发送一个请求会先到Tomcat的web服务器 (2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源 (3)如 ...

  4. emberjs学习二(ember-data和localstorage_adapter)

    emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...

  5. TweenMax动画库学习(二)

    目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            Tw ...

  6. java学习之反射机制

    java语言区别于C,C++等准静态语言的最大特点就是java的反射机制.静态语言的最直接定义就是不能在运行时改变程序结构或变量的类型.按照这样的定义,python,ruby是动态语言,C,C++,J ...

  7. Hbase深入学习(二) 安装hbase

    Hbase深入学习(二) 安装hbase This guidedescribes setup of a standalone hbase instance that uses the local fi ...

  8. Python学习二:词典基础详解

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7862377.html 邮箱:moyi@moyib ...

  9. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

随机推荐

  1. LeetCode-二叉树的深度

    二叉树的深度 二叉树的深度 使用递归求解二叉树的深度. 需要注意使用的临界条件. /** * 任意一个二叉树的最大深度 **/ #include<iostream> #include< ...

  2. Intellij IDEA设置默认字符编码

    file---settings--editor--file encoding里面设置

  3. Comet OJ - Contest #9 & X Round 3 【XR-3】核心城市 【树的理解】

    一.题目 [XR-3]核心城市 二.分析 题意就是在树中确定$K$个点,满足剩下的$N-K$个点中到这$K$个点的最大距离尽可能小. 理解上肯定是确定一个根,这个根是这个图的中心. 可以通过根据结点的 ...

  4. Codeforces Round #533 C. Ayoub and Lost Array

    题面: 传送门 题目描述: 题意很简单:1.数组中的所有整数都在区间[l, r],2.所有元素之和能被3整除.现在知道这个数组的大小,l和r,问:按照题目的要求组成的数组一共有多少种可能.   题目分 ...

  5. POJ2635(数论+欧拉筛+大数除法)

    题目链接:https://vjudge.net/problem/POJ-2635 题意:给定一个由两个质数积的大数M和一个数L,问大数M的其中较小的质数是否小于L. 题解:因为大数M已经超过long ...

  6. 【linux】驱动-2-内核模块

    目录 前言 2. 内核模块 2.1 内核模块概念 2.1.1 内核 2.1.2 内核模块机制的引入 2.2 内核模块 2.2.1 内核模块参考例程 2.2.2 内核模块命令 2.2.3 系统自动加载模 ...

  7. 菜刀jsp小马

    逛google收获小马一枚,收藏一下 <%@page import="java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*&q ...

  8. JVM虚拟机知识问答总结(简单复习,快速回忆!)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  9. 提高Python的性能

    01 使用哈希表的数据结构   如果在程序中遇到大量搜索操作时,并且数据中没有重复项,则可以使用查找而不是循环.举例如下: items = ['a', 'b',..,'100m'] #1000s of ...

  10. csss3属性 — will-change

    1. CPU和GPU CPU即中央处理器,它的功能主要是解释计算机指令以及处理计算机软件中的数据,也被称为主板. GPU即图形处理器,是与处理和绘制图形相关的硬件.GPU是专为执行复杂的数学和几何计算 ...