章节内容
一. Android Hook 系列教程(一) Xposed Hook 原理分析
二. Android Hook 系列教程(二) 自己写APK实现Hook Java层函数
三. Android Hook 系列教程(三) Cydia Hook Native 原理分析
四. Android Hook 系列教程(四) 自己写APK实现Hook Native层函数
五. Android Hook 系列教程(五) 更多Hook方法
六. Andoird Hook 系列教程(六) Hook的总结
Java层:https://github.com/rovo89/XposedBridge
Native层:https://github.com/rovo89/Xposed
一个好的开始等于成功了一半.
为了分析Xposed Hook是怎么实现的,选取了findAndHookMethod函数作为研究对象.
在分析过程中,我们应该做到有详有略,我将以关键代码(略过对分析目的无关紧要的部分,留下必须部分).
当然在关键代码后面会贴出全部代码以供参照.
Jave层分析
findAndHookMethod
从源代码:XposedHelpers.java处找到findAndHookMethod
findAndHookMethod关键代码
public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, ``Object``... parameterTypesAndCallback) {
...
XC_MethodHook callback ``=
(XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length``-``1``];``/``/``获取回调函数
Method m ``=
findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));``/``/``获取Method
return
XposedBridge.hookMethod(m, callback);
}
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));``/``/``获取Method
return
XposedBridge.hookMethod(m, callback);
}
上面的代码都很容易懂,简单说一下几个函数
getParameterClasses
功能:把函数所有参数转换为Class<?>数组返回.
比如参数列表为
String.``class``,``int``.``class``,``"java.util.Map"``,回调函数
返回结果相当于
Class<?> []``=``{
String.``class``,
int``.``class``,
Map``.``class
};
findMethodExact
功能:根据方法名和方法参数类获取Method
关键代码:
public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
...
Method method ``=
clazz.getDeclaredMethod(methodName, parameterTypes);``/``/``获取方法声明
method.setAccessible(true);
methodCache.put(fullMethodName, method);
return
method;
}
完整代码:
public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
String fullMethodName ``=
clazz.getName() ``+
'#'
+
methodName ``+
getParametersString(parameterTypes) ``+
"#exact"``;
if
(methodCache.containsKey(fullMethodName)) {
Method method ``=
methodCache.get(fullMethodName);
if
(method ``=``=
null)
throw new NoSuchMethodError(fullMethodName);
return
method;
}
try
{
Method method ``=
clazz.getDeclaredMethod(methodName, parameterTypes);``/``/``获取方法声明
method.setAccessible(true);
methodCache.put(fullMethodName, method);
return
method;
} catch (NoSuchMethodException e) {
methodCache.put(fullMethodName, null);
throw new NoSuchMethodError(fullMethodName);
}
}
让我们继续跟进hookMethod
hookMethod
功能:把方法的参数类型,返回类型,回调函数记录到AdditionalHookInfo类里
并通过hookMethodNative方法进入Native层进行Hook.
在这个方法里面差不多都是关键代码,也就不省略,全部贴出.
完整代码:
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
/``*``检测是否是支持的Method类型``*``/
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());
}
boolean newMethod ``=
false;
CopyOnWriteSortedSet<XC_MethodHook> callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks ``=
sHookedMethodCallbacks.get(hookMethod);
if
(callbacks ``=``=
null) {``/``/``如果为null则函数没有被Hook,否则已经Hook
callbacks ``=
new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(hookMethod, callbacks);
newMethod ``=
true;
}
}
callbacks.add(callback);``/``/``记录回调函数
if
(newMethod) {``/``/``建立新Hook
Class<?> declaringClass ``=
hookMethod.getDeclaringClass();``/``/``获取类声明
int
slot;
Class<?>[] parameterTypes;
Class<?> returnType;
if
(runtime ``=``=
RUNTIME_ART) {``/``/``判断是否是ART模式
slot ``=
0``;
parameterTypes ``=
null;
returnType ``=
null;
} ``else
if
(hookMethod instanceof Method) {``/``/``实例方法
slot ``=
getIntField(hookMethod, ``"slot"``);``/``/``获取slot
parameterTypes ``=
((Method) hookMethod).getParameterTypes();``/``/``获取参数类型Class<?>数组 和前面相同
returnType ``=
((Method) hookMethod).getReturnType();``/``/``获取返回类型
} ``else
{``/``/``构造函数
slot ``=
getIntField(hookMethod, ``"slot"``);``/``/``获取slot
parameterTypes ``=
((Constructor<?>) hookMethod).getParameterTypes();``/``/``获取参数类型Class<?>数组 和前面相同
returnType ``=
null;``/``/``构造参数返回类型为null
}
/``/``把回调函数,参数类型,返回类型 记录在一个AdditionalHookInfo类里
AdditionalHookInfo additionalInfo ``=
new AdditionalHookInfo(callbacks, parameterTypes, returnType);
/``/``Native方法
hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
}
return
callback.new Unhook(hookMethod);
}
看一下AdditionalHookInfo构造函数
private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks, Class<?>[] parameterTypes, Class<?> returnType) {
this.callbacks ``=
callbacks;
this.parameterTypes ``=
parameterTypes;
this.returnType ``=
returnType;
}
Native层分析
hookMethodNative
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 250, 250); text-decoration-style: initial; text-decoration-color: initial;">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
ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
Method* method = dvmSlotToMethod(declaredClass, slot);
if (method == NULL) {
dvmThrowNoSuchMethodError("Could not get internal representation for method");
return;
}
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
SET_METHOD_FLAG(method, ACC_NATIVE);
method->nativeFunc = &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);
}
}
}
</pre>
详细分析:
-
得到Native层的Method
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41);"> ClassObject* declaredClass = (ClassObject) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
Method method = dvmSlotToMethod(declaredClass, slot);
</pre>从Android源代码的Dalvik虚拟机代码里找到dvmSlotToMethod实现如下
实现非常简单,所以slot相等于记录Method的下标
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41);">Method* dvmSlotToMethod(ClassObject* clazz, int slot)
{
if (slot < 0) {
slot = -(slot+1);
assert(slot < clazz->directMethodCount);
return &clazz->directMethods[slot];
} else {
assert(slot < clazz->virtualMethodCount);
return &clazz->virtualMethods[slot];
}
}
</pre>
Method结构
struct Method {
/``*
the ``class
we are a part of ``*``/
ClassObject``*
clazz;
/``*
access flags; low ``16
bits are defined by spec (could be u2?) ``*``/
u4 accessFlags;
/``*
*
For concrete virtual methods, this ``is
the offset of the method
*
in
"vtable"``.
*
*
For abstract methods ``in
an interface ``class``, this ``is
the offset
*
of the method ``in
"iftable[n]->methodIndexArray"``.
*``/
u2 methodIndex;
/``*
*
Method bounds; ``not
needed ``for
an abstract method.
*
*
For a native method, we compute the size of the argument ``list``, ``and
*
set
"insSize"
and
"registerSize"
equal to it.
*``/
u2 registersSize; ``/``*
ins ``+
locals
*``/
u2 outsSize;
u2 insSize;
/``*
method name, e.g. ``"<init>"
or
"eatLunch"
*``/
const char``*
name;
/``*
*
Method prototype descriptor string (``return
and
argument types).
*
*
TODO: This currently must specify the DexFile as well as the proto_ids
*
index, because generated Proxy classes don't have a DexFile. We can
*
remove the DexFile``*
and
reduce
the size of this struct ``if
we generate
*
a DEX ``for
proxies.
*``/
DexProto prototype;
/``*
short``-``form method descriptor string ``*``/
const char``*
shorty;
/``*
*
The remaining items are ``not
used ``for
abstract ``or
native methods.
*
(JNI ``is
currently hijacking ``"insns"
as a function pointer, ``set
*
after the first call. For internal``-``native this stays null.)
*``/
/``*
the actual code ``*``/
const u2``*
insns; ``/``*
instructions, ``in
memory``-``mapped .dex ``*``/
/``*
JNI: cached argument ``and
return``-``type
hints ``*``/
int
jniArgInfo;
/``*
*
JNI: native method ptr; could be actual function ``or
a JNI bridge. We
*
don't currently discriminate between DalvikBridgeFunc ``and
*
DalvikNativeFunc; the former takes an argument superset (i.e. two
*
extra args) which will be ignored. If necessary we can use
*
insns``=``=``NULL to detect JNI bridge vs. internal native.
*``/
DalvikBridgeFunc nativeFunc;
/``*
*
JNI: true ``if
this static non``-``synchronized native method (that has no
*
reference arguments) needs a JNIEnv``*
and
jclass``/``jobject. Libcore
*
uses this.
*``/
bool
fastJni;
/``*
*
JNI: true ``if
this method has no reference arguments. This lets the JNI
*
bridge avoid scanning the shorty ``for
direct pointers that need to be
*
converted to local references.
*
*
TODO: replace this with a ``list
of indexes of the reference arguments.
*``/
bool
noRef;
/``*
*
JNI: true ``if
we should log entry ``and
exit. This ``is
the only way
*
developers can log the local references that are passed into their code.
*
Used ``for
debugging JNI problems ``in
third``-``party code.
*``/
bool
shouldTrace;
/``*
*
Register ``map
data, ``if
available. This will point into the DEX ``file
*
if
the data was computed during pre``-``verification, ``or
into the
*
linear alloc area ``if
not``.
*``/
const RegisterMap``*
registerMap;
/``*
set
if
method was called during method profiling ``*``/
bool
inProfile;
};
-
保存一些Hook信息
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41);">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));
</pre> -
替换方法(最关键的一步)
SET_METHOD_FLAG(method, ACC_NATIVE);
method``-``>nativeFunc ``=
&hookedMethodCallback;``/``/``替换方法为这个
method``-``>insns ``=
(const u2``*``) hookInfo;
method``-``>registersSize ``=
method``-``>insSize;
method``-``>outsSize ``=
0``;
到这里我们已经分析完了,就是保存一些信息,并把方法替换为hookedMethodCallback.
hookedMethodCallback
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 250, 250); text-decoration-style: initial; text-decoration-color: initial;">void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
if (!isMethodHooked(method)) {
dvmThrowNoSuchMethodError("Could not find Xposed original method - how did you even get here?");
return;
}
XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns;
Method* original = (Method*) hookInfo;
Object* originalReflected = hookInfo->reflectedMethod;
Object* additionalInfo = hookInfo->additionalInfo;
// convert/box arguments
const char* desc = &method->shorty[1]; // [0] is the return type.
Object* thisObject = NULL;
size_t srcIndex = 0;
size_t dstIndex = 0;
// for non-static methods determine the "this" pointer
if (!dvmIsStaticMethod(original)) {
thisObject = (Object*) args[0];
srcIndex++;
}
ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT);
if (argsArray == NULL) {
return;
}
//循环获取参数
while (*desc != '\0') {
char descChar = *(desc++);
JValue value;
Object* obj;
switch (descChar) {
case 'Z':
case 'C':
case 'F':
case 'B':
case 'S':
case 'I':
value.i = args[srcIndex++];
obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
dvmReleaseTrackedAlloc(obj, self);
break;
case 'D':
case 'J':
value.j = dvmGetArgLong(args, srcIndex);
srcIndex += 2;
obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
dvmReleaseTrackedAlloc(obj, self);
break;
case '[':
case 'L':
obj = (Object*) args[srcIndex++];
break;
default:
ALOGE("Unknown method signature description character: %c", descChar);
obj = NULL;
srcIndex++;
}
setObjectArrayElement(argsArray, dstIndex++, obj);//把获取的参数加入数组
}
// call the Java handler function
JValue result;
dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,
originalReflected, (int) original, additionalInfo, thisObject, argsArray);
dvmReleaseTrackedAlloc(argsArray, self);
// exceptions are thrown to the caller
if (dvmCheckException(self)) {
return;
}
// return result with proper type
ClassObject* returnType = dvmGetBoxedReturnType(method);
if (returnType->primitiveType == PRIM_VOID) {
// ignored
} else if (result.l == NULL) {
if (dvmIsPrimitiveClass(returnType)) {
dvmThrowNullPointerException("null result when primitive expected");
}
pResult->l = NULL;
} else {
if (!dvmUnboxPrimitive(result.l, returnType, pResult)) {
dvmThrowClassCastException(result.l->clazz, 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
ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
Method* method = dvmSlotToMethod(declaredClass, slot);
if (method == NULL) {
dvmThrowNoSuchMethodError("Could not get internal representation for method");
return;
}
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
SET_METHOD_FLAG(method, ACC_NATIVE);
method->nativeFunc = &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);
}
}
}
</pre>
相信到了这里读者已经可以根据注释自己看懂了,我在把流程梳理一下
获取原先保存的Hook信息
获取参数
-
调用Java层函数
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41);">dvmCallMethod(self, (Method*) methodXposedBridgeHandleHookedMethod, NULL, &result,
originalReflected, (int) original, additionalInfo, thisObject, argsArray);
dvmReleaseTrackedAlloc(argsArray, self);
</pre>看一下参数methodXposedBridgeHandleHookedMethod是什么,可以看到是上层的Java
<pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 13.475px; margin-top: 0px; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41);">#define CLASS_XPOSED_BRIDGE "de/robv/android/xposed/XposedBridge"
classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
jmethodID methodXposedBridgeHandleHookedMethod = NULL;
methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
"(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
</pre>可以看到调用Java层的函数为XposedBridge.handleHookedMethod
Java层函数handleHookedMethod
功能:
调用Hook该方法的所有beforeHookedMethod
调用原始方法
调用Hook该方法的所有afterHookedMethod
关键代码
private static ``Object
handleHookedMethod(Member method, ``int
originalMethodId, ``Object
additionalInfoObj,
Object
thisObject, ``Object``[] args) throws Throwable {
AdditionalHookInfo additionalInfo ``=
(AdditionalHookInfo) additionalInfoObj;
MethodHookParam param ``=
new MethodHookParam();
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 < callbacksLength);
/``/
call original method ``if
not
requested otherwise
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);
else
param.setThrowable(lastThrowable);
}
} ``while
(``-``-``afterIdx >``=
0``);
/``/
return
if
(param.hasThrowable())
throw param.getThrowable();
else
return
param.getResult();
}
完整代码
private static ``Object
handleHookedMethod(Member method, ``int
originalMethodId, ``Object
additionalInfoObj,
Object
thisObject, ``Object``[] args) throws Throwable {
AdditionalHookInfo additionalInfo ``=
(AdditionalHookInfo) additionalInfoObj;
if
(disableHooks) {
try
{
return
invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,
additionalInfo.returnType, thisObject, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
Object``[] callbacksSnapshot ``=
additionalInfo.callbacks.getSnapshot();
final ``int
callbacksLength ``=
callbacksSnapshot.length;
if
(callbacksLength ``=``=
0``) {
try
{
return
invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,
additionalInfo.returnType, thisObject, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
MethodHookParam param ``=
new MethodHookParam();
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 < callbacksLength);
/``/
call original method ``if
not
requested otherwise
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);
else
param.setThrowable(lastThrowable);
}
} ``while
(``-``-``afterIdx >``=
0``);
/``/
return
if
(param.hasThrowable())
throw param.getThrowable();
else
return
param.getResult();
}
好了,到这里我们已经分析完成.
简单总结一下:
-
Java层:
- 记录需要Hook方法的信息包括:参数数组,返回值类型,回调函数.并且记录在AdditionalHookInfo类里面
- 获取slot,声明方法的类
- 把这些信息通过Native函数:hookMethodNative进行Hook
-
Native层
替换Native层Method的字段nativeFunc为函数 hookedMethodCallback完成Hook
hookedMethodCallback里回调Java层函数XposedBridge.handleHookedMethod.
handleHookedMethod里分别调用beforeHookedMethod,原始方法,afterHookedMethod