Alibaba的AndFix热修复:
Alibaba-AndFix Bug热修复框架的使用
Alibaba-AndFix Bug热修复框架原理及源码解析
上一篇中已经介绍了Alibaba-Dexposed框架在线热补丁修复的使用 ,这篇主要是了解框架的原理和源码解析。
原理:
在Dalvik虚拟机下,主要是通过改变一个方法对象方法在Dalvik虚拟机中的定义来实现,具体做法就是将该方法的类型改变为Native并且将这个方法的实现链接到一个通用的Native Dispatch方法上。这个 Dispatch方法通过JNI回调到Java端的一个统一处理方法,最后在统一处理方法中调用before, after函数来实现AOP。在Art虚拟机上目前也是通过改变一个 ArtMethod的入口函数来实现。
在宿主项目的Application需要调用以下方法来判断手机是否支持Dexposed框架:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">DexposedBridge.canDexposed(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">this</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
canDexposed方法源码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">canDexposed</span>(Context context) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> !DeviceCheck.isDeviceSupport(context)?<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">false</span>:loadDexposedLib(context);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
可以看到,第一判断了机型是否支持,如果支持就加载lib文件。
DeviceCheck.isDeviceSupport()源码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">isDeviceSupport</span>(Context context) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">boolean</span> var2;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(!isCheckedDeviceSupport) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(isDalvikMode() && isSupportSDKVersion() && !isX86CPU() && !isYunOS()) {
isDeviceSupportable = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">true</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> isDeviceSupportable;
}
isDeviceSupportable = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">false</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> isDeviceSupportable;
}
var2 = isDeviceSupportable;
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">finally</span> {
Log.d(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"hotpatch"</span>, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"device support is "</span> + isDeviceSupportable + <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"checked"</span> + isCheckedDeviceSupport);
isCheckedDeviceSupport = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">true</span>;
}
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> var2;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li></ul>
判断机型,主要判断的有是否是Dalvik虚拟机、sdk版本、是否是x86cpu架构、是否是YunOS系统。
loadDexposedLib加载lib的源码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">loadDexposedLib</span>(Context context) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(VERSION.SDK_INT != <span class="hljs-number" style="color:#06666;box-sizing: border-box;">10</span> && VERSION.SDK_INT != <span class="hljs-number" style="color:#06666;box-sizing: border-box;">9</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(VERSION.SDK_INT > <span class="hljs-number" style="color:#06666;box-sizing: border-box;">19</span>) {
System.loadLibrary(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed_l"</span>);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
System.loadLibrary(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed"</span>);
}
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
System.loadLibrary(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed2.3"</span>);
}
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">true</span>;
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">catch</span> (Throwable var2) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">false</span>;
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>
根据sdk的不同版本加载不同的so文件。
以上仅是判断当然机型是否支持Dexposed框架的运行环境。
接下,就是对Dexposed的使用原理进行源码分析:
在上一篇提到,当加载补丁文件时,会扫描补丁文件中实现IPatch接口的所有的类。
IPatch定义如下:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">interface</span> IPatch {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> handlePatch(PatchParam var1) throws Throwable;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
就是说,修复bug的处理只能在handlePatch方法中实现。
官网也只提供了2种实现方式:
第一:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Target class, method with parameter types, followed by the hook callback (XC_MethodHook).</span>
DexposedBridge.findAndHookMethod(Activity.class, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"onCreate"</span>, Bundle.class, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> XC_MethodHook() {
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// To be invoked before Activity.onCreate().</span>
<span class="hljs-annotation" style="color:#9b859d;box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">beforeHookedMethod</span>(MethodHookParam param) <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throws</span> Throwable {
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// "thisObject" keeps the reference to the instance of target class.</span>
Activity instance = (Activity) param.thisObject;
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// The array args include all the parameters.</span>
Bundle bundle = (Bundle) param.args[<span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>];
Intent intent = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> Intent();
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// XposedHelpers provide useful utility methods.</span>
XposedHelpers.setObjectField(param.thisObject, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"mIntent"</span>, intent);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Calling setResult() will bypass the original method body use the result as method return value directly.</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (bundle.containsKey(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"return"</span>))
param.setResult(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>);
}
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// To be invoked after Activity.onCreate()</span>
<span class="hljs-annotation" style="color:#9b859d;box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">afterHookedMethod</span>(MethodHookParam param) <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throws</span> Throwable {
XposedHelpers.callMethod(param.thisObject, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"sampleMethod"</span>, <span class="hljs-number" style="color:#06666;box-sizing: border-box;">2</span>);
}
});</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li></ul>
第二:
<code class="hljs r has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> DexposedBridge.findAndHookMethod(Activity.class, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"onCreate"</span>, Bundle.class, new XC_MethodReplacement() {
@Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
// Re-writing the method logic outside the original method context is a bit tricky but still viable.
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">...</span>
}
});</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>
调用的接口是相同的,只不过传递的回调接口不同。
第一种是在方法前后执行做一些处理,第二种就是直接把方法进行替换。
在这里,我们就重点看findAndHookMethod方法,跟着此方法追踪源码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> Unhook <span class="hljs-title" style="box-sizing: border-box;">findAndHookMethod</span>(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(parameterTypesAndCallback.length != <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span> && parameterTypesAndCallback[parameterTypesAndCallback.length - <span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>] <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> XC_MethodHook) {
XC_MethodHook callback = (XC_MethodHook)parameterTypesAndCallback[parameterTypesAndCallback.length - <span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>];
Method m = XposedHelpers.findMethodExact(clazz, methodName, parameterTypesAndCallback);<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//根据Java的反射机制获取到Method对象</span>
Unhook unhook = hookMethod(m, callback);<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//见下方代码分析</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(!(callback <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> XC_MethodKeepHook) && !(callback <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> XC_MethodKeepReplacement)) {
ArrayList var6 = allUnhookCallbacks;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">synchronized</span>(allUnhookCallbacks) {
allUnhookCallbacks.add(unhook);
}
}
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> unhook;
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> IllegalArgumentException(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"no callback defined"</span>);
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>
hookMethod方法源码:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> Unhook <span class="hljs-title" style="box-sizing: border-box;">hookMethod</span>(Member hookMethod, XC_MethodHook callback) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(!(hookMethod <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> Method) && !(hookMethod <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> Constructor)) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> IllegalArgumentException(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"only methods and constructors can be hooked"</span>);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">boolean</span> newMethod = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">false</span>;
Map declaringClass = hookedMethodCallbacks;
DexposedBridge.CopyOnWriteSortedSet callbacks;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">synchronized</span>(hookedMethodCallbacks) {
callbacks = (DexposedBridge.CopyOnWriteSortedSet)hookedMethodCallbacks.get(hookMethod);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//如果没有修复此方法,就创建一个回调接口的集合</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(callbacks == <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>) {
callbacks = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> DexposedBridge.CopyOnWriteSortedSet();
hookedMethodCallbacks.put(hookMethod, callbacks);
newMethod = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">true</span>;
}
}
callbacks.add(callback);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(newMethod) {<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//如果是新方法,获取方法的参数列表和返回值</span>
Class declaringClass1 = hookMethod.getDeclaringClass();
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> slot = runtime == <span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>?XposedHelpers.getIntField(hookMethod, <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"slot"</span>):<span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>;
Class[] parameterTypes;
Class returnType;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span>(hookMethod <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">instanceof</span> Method) {
parameterTypes = ((Method)hookMethod).getParameterTypes();
returnType = ((Method)hookMethod).getReturnType();
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
parameterTypes = ((Constructor)hookMethod).getParameterTypes();
returnType = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>;
}
DexposedBridge.AdditionalHookInfo additionalInfo = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> DexposedBridge.AdditionalHookInfo(callbacks, parameterTypes, returnType, (DexposedBridge.AdditionalHookInfo)<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//调用Native方法,接口在下方</span>
hookMethodNative(hookMethod, declaringClass1, slot, additionalInfo);
}
callback.getClass();
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> Unhook(callback, hookMethod);<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//返回一个Unhook实例对象</span>
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul>
hookMethodNative Native方法生命:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">synchronized</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">native</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">hookMethodNative</span>(Member var0, Class<?> var1, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> var2, Object var3);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
Dalvik虚拟机的Native方法实现:
hookMethodNative Native层的代码实现:
<code class="hljs oxygene has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> void com_taobao_android_dexposed_DexposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">{s
// Usage errors?
if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {
dvmThrowIllegalArgumentException("method and declaredClass must not be null");
return;
}</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Find the internal representation of the method</span>
ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">Method</span>* <span class="hljs-title" style="box-sizing: border-box;">method</span> = <span class="hljs-title" style="box-sizing: border-box;">dvmSlotToMethod</span><span class="hljs-params" style="color:#66066;box-sizing: border-box;">(declaredClass, slot)</span>;</span><span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//把Java的Method映射为Native Method</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span> == <span class="hljs-title" style="box-sizing: border-box;">NULL</span>) <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">{
dvmThrowNoSuchMethodError("could not get internal representation for method");
return;
}</span>
<span class="hljs-title" style="box-sizing: border-box;">if</span> <span class="hljs-params" style="color:#66066;box-sizing: border-box;">(dexposedIsHooked(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>)</span>) <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">{//判断此方法是否已经被hook(钩)
// already hooked
return;
}</span>
// <span class="hljs-title" style="box-sizing: border-box;">Save</span> <span class="hljs-title" style="box-sizing: border-box;">a</span> <span class="hljs-title" style="box-sizing: border-box;">copy</span> <span class="hljs-title" style="box-sizing: border-box;">of</span> <span class="hljs-title" style="box-sizing: border-box;">the</span> <span class="hljs-title" style="box-sizing: border-box;">original</span> <span class="hljs-title" style="box-sizing: border-box;">method</span> <span class="hljs-title" style="box-sizing: border-box;">and</span> <span class="hljs-title" style="box-sizing: border-box;">other</span> <span class="hljs-title" style="box-sizing: border-box;">hook</span> <span class="hljs-title" style="box-sizing: border-box;">info</span>
<span class="hljs-title" style="box-sizing: border-box;">DexposedHookInfo</span>* <span class="hljs-title" style="box-sizing: border-box;">hookInfo</span> = <span class="hljs-params" style="color:#66066;box-sizing: border-box;">(DexposedHookInfo*)</span> <span class="hljs-title" style="box-sizing: border-box;">calloc</span><span class="hljs-params" style="color:#66066;box-sizing: border-box;">(1, sizeof(DexposedHookInfo)</span>);</span><span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//新申请一块内存</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//备份method对象到hookInfo中</span>
memcpy(hookInfo, <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>, <span class="hljs-title" style="box-sizing: border-box;">sizeof</span><span class="hljs-params" style="color:#66066;box-sizing: border-box;">(hookInfo->originalMethodStruct)</span>);</span>
hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//把方法的实现指向native方法的实现,指针替换</span>
hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Replace method with our own code</span>
SET_METHOD_FLAG(<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>, <span class="hljs-title" style="box-sizing: border-box;">ACC_NATIVE</span>);</span><span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//把method对象方法属性设置成native方法</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>-><span class="hljs-title" style="box-sizing: border-box;">insns</span> = <span class="hljs-params" style="color:#66066;box-sizing: border-box;">(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">const</span> u2*)</span> <span class="hljs-title" style="box-sizing: border-box;">hookInfo</span>;</span><span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//把备份的method数据挂在这里传递数据</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>-><span class="hljs-title" style="box-sizing: border-box;">registersSize</span> = <span class="hljs-title" style="box-sizing: border-box;">method</span>-><span class="hljs-title" style="box-sizing: border-box;">insSize</span>;</span>
<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>-><span class="hljs-title" style="box-sizing: border-box;">outsSize</span> = 0;</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (PTR_gDvmJit != NULL) <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">{
// reset JIT cache
MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;
}</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul>
<code class="hljs oxygene has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">method</span>-><span class="hljs-title" style="box-sizing: border-box;">nativeFunc</span> = &<span class="hljs-title" style="box-sizing: border-box;">dexposedCallHandler</span>;</span><span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//链接到Native方法的实现</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>
当虚拟机调用到这个存在bug的方法时就会调用这个Native方法:
dexposedCallHandler方法源码:
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> dexposedCallHandler(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">const</span> u4* args, JValue* pResult, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">const</span> Method* method, ::Thread* <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (!dexposedIsHooked(method)) {
dvmThrowNoSuchMethodError(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"could not find Dexposed original method - how did you even get here?"</span>);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span>;
}
DexposedHookInfo* hookInfo = (DexposedHookInfo*) method->insns;
Method* original = (Method*) hookInfo;
Object* originalReflected = hookInfo->reflectedMethod;
Object* additionalInfo = hookInfo->additionalInfo;
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// convert/box arguments</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">char</span>* desc = &method->shorty[<span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>]; <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// [0] is the return type.</span>
Object* thisObject = <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>;
size_t srcIndex = <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>;
size_t dstIndex = <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>;
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// for non-static methods determine the "this" pointer</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (!dvmIsStaticMethod(original)) {
thisObject = (Object*) args[<span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>];
srcIndex++;
}
ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - <span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>, ALLOC_DEFAULT);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (argsArray == <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span>;
}
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">while</span> (*desc != <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'\0'</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">char</span> descChar = *(desc++);
JValue value;
Object* obj;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">switch</span> (descChar) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'Z'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'C'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'F'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'B'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'S'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'I'</span>:
value<span class="hljs-variable" style="color:#66066;box-sizing: border-box;">.i</span> = args[srcIndex++];
obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
dvmReleaseTrackedAlloc(obj, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">break</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'D'</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'J'</span>:
value<span class="hljs-variable" style="color:#66066;box-sizing: border-box;">.j</span> = dvmGetArgLong(args, srcIndex);
srcIndex += <span class="hljs-number" style="color:#06666;box-sizing: border-box;">2</span>;
obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
dvmReleaseTrackedAlloc(obj, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">break</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'['</span>:
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">case</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">'L'</span>:
obj = (Object*) args[srcIndex++];
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">break</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">default</span>:
ALOGE(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"Unknown method signature description character: %c\n"</span>, descChar);
obj = <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>;
srcIndex++;
}
dexposedSetObjectArrayElement(argsArray, dstIndex++, obj);
}
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// call the Java handler function</span>
JValue result;
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">//调用了Java层的方法</span>
dvmCallMethod(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>, dexposedHandleHookedMethod, <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>, &result,
originalReflected, (<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span>) original, additionalInfo, thisObject, argsArray);
dvmReleaseTrackedAlloc((Object *)argsArray, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// exceptions are thrown to the caller</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (dvmCheckException(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">self</span>)) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span>;
}
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// return result with proper type</span>
ClassObject* returnType = dvmGetBoxedReturnType(method);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (returnType->primitiveType == PRIM_VOID) {
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// ignored</span>
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (result<span class="hljs-variable" style="color:#66066;box-sizing: border-box;">.l</span> == <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (dvmIsPrimitiveClass(returnType)) {
dvmThrowNullPointerException(<span class="hljs-string" style="color:#0880;box-sizing: border-box;">"null result when primitive expected"</span>);
}
pResult->l = <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">NULL</span>;
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (!dvmUnboxPrimitive((Object *)result<span class="hljs-variable" style="color:#66066;box-sizing: border-box;">.l</span>, returnType, pResult)) {
dvmThrowClassCastException(((Object *)result<span class="hljs-variable" style="color:#66066;box-sizing: border-box;">.l</span>)->clazz, returnType);
}
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li><li style="box-sizing: border-box; padding: 0px 5px;">84</li><li style="box-sizing: border-box; padding: 0px 5px;">85</li><li style="box-sizing: border-box; padding: 0px 5px;">86</li><li style="box-sizing: border-box; padding: 0px 5px;">87</li><li style="box-sizing: border-box; padding: 0px 5px;">88</li><li style="box-sizing: border-box; padding: 0px 5px;">89</li><li style="box-sizing: border-box; padding: 0px 5px;">90</li><li style="box-sizing: border-box; padding: 0px 5px;">91</li><li style="box-sizing: border-box; padding: 0px 5px;">92</li><li style="box-sizing: border-box; padding: 0px 5px;">93</li></ul>
这个方法主要就是调用Java的方法,实现调度。
调用的Java的方法是:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> Object <span class="hljs-title" style="box-sizing: border-box;">handleHookedMethod</span>(Member method, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> originalMethodId, Object additionalInfoObj,
Object thisObject, Object[] args) throws Throwable {
AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
final <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> callbacksLength = callbacksSnapshot.length;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (callbacksLength == <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,
additionalInfo.returnType, thisObject, args);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">catch</span> (InvocationTargetException e) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throw</span> e.getCause();
}
}
MethodHookParam param = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">new</span> MethodHookParam();
param.method = method;
param.thisObject = thisObject;
param.args = args;
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// call "before method" callbacks</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> beforeIdx = <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">do</span> {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">catch</span> (Throwable t) {
log(t);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// reset result (ignoring what the unexpectedly exiting callback did)</span>
param.setResult(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>);
param.returnEarly = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">false</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">continue</span>;
}
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (param.returnEarly) {
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// skip remaining "before" callbacks and corresponding "after" callbacks</span>
beforeIdx++;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">break</span>;
}
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">while</span> (++beforeIdx < callbacksLength);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// call original method if not requested otherwise</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (!param.returnEarly) {
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
param.setResult(invokeOriginalMethodNative(method, originalMethodId,
additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">catch</span> (InvocationTargetException e) {
param.setThrowable(e.getCause());
}
}
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// call "after method" callbacks</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">int</span> afterIdx = beforeIdx - <span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">do</span> {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">try</span> {
((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">catch</span> (Throwable t) {
DexposedBridge.log(t);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// reset to last result (ignoring what the unexpectedly exiting callback did)</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (lastThrowable == <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">null</span>)
param.setResult(lastResult);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span>
param.setThrowable(lastThrowable);
}
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">while</span> (--afterIdx >= <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// return</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (param.hasThrowable())
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">throw</span> param.getThrowable();
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span> param.getResult();
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li></ul>
这个方法里面了实现了调度机制,调用回调接口的方法和备份的Java方法。
本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc,
registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 dexposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找dexposed模块及dexposed框架调用 findAndHookMethod 注册的 before,after
函数,如果有,就执行,再通过invokeOriginalMethodNative 执行挂钩前函数。
MethodHookParam.thisObject:这个类的一个实例
MethodHookParam.args:用于传递被注入函数的所有参数
MethodHookParam.setResult:用于修改原函数调用的结果,如果在beforeHookedMethod回调函数中调用setResult,可以阻止对原函数的调用。但是如果有返回值的话仍然需要通过hook处理器进行return操作。
Art虚拟机的Native方法实现
<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">static <span class="hljs-literal" style="color:#06666;box-sizing: border-box;">void</span> com_taobao_android_dexposed_DexposedBridge_hookMethodNative(
JNIEnv<span class="hljs-subst" style="color:#000000;box-sizing: border-box;">*</span> env, jclass, jobject java_method, jobject, jint,
jobject additional_info) {
ScopedObjectAccess soa(env);
art<span class="hljs-tag" style="color:#06666;box-sizing: border-box;">::Thread</span><span class="hljs-subst" style="color:#000000;box-sizing: border-box;">*</span> <span class="hljs-built_in" style="color:#66066;box-sizing: border-box;">self</span> <span class="hljs-subst" style="color:#000000;box-sizing: border-box;">=</span> art<span class="hljs-tag" style="color:#06666;box-sizing: border-box;">::Thread</span><span class="hljs-tag" style="color:#06666;box-sizing: border-box;">::Current</span>();
jobject javaArtMethod <span class="hljs-subst" style="color:#000000;box-sizing: border-box;">=</span> env<span class="hljs-subst" style="color:#000000;box-sizing: border-box;">-></span>GetObjectField(java_method,
WellKnownClasses<span class="hljs-tag" style="color:#06666;box-sizing: border-box;">::java_lang_reflect_AbstractMethod_artMethod</span>);
ArtMethod<span class="hljs-subst" style="color:#000000;box-sizing: border-box;">*</span> method <span class="hljs-subst" style="color:#000000;box-sizing: border-box;">=</span> soa<span class="hljs-built_in" style="color:#66066;box-sizing: border-box;">.</span>Decode<span class="hljs-subst" style="color:#000000;box-sizing: border-box;"><</span>mirror<span class="hljs-tag" style="color:#06666;box-sizing: border-box;">::ArtMethod</span><span class="hljs-subst" style="color:#000000;box-sizing: border-box;">*></span>(javaArtMethod);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">LOG</span>(INFO) <span class="hljs-subst" style="color:#000000;box-sizing: border-box;"><<</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed: >>> hookMethodNative "</span> <span class="hljs-subst" style="color:#000000;box-sizing: border-box;"><<</span> method <span class="hljs-subst" style="color:#000000;box-sizing: border-box;"><<</span> <span class="hljs-string" style="color:#0880;box-sizing: border-box;">" "</span> <span class="hljs-subst" style="color:#000000;box-sizing: border-box;"><<</span> PrettyMethod(method);
EnableXposedHook(env, method, additional_info);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li></ul>
EnableXposedHook:
<code class="hljs cpp has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> EnableXposedHook(JNIEnv* env, ArtMethod* art_method, jobject additional_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
LOG(INFO) << <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed: >>> EnableXposedHook"</span> << art_method << <span class="hljs-string" style="color:#0880;box-sizing: border-box;">" "</span> << PrettyMethod(art_method);
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (dexposedIsHooked(art_method)) {
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Already hooked</span>
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">return</span>;
}
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// else if (UNLIKELY(art_method->IsXposedOriginalMethod())) {</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// // This should never happen</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// ThrowIllegalArgumentException(nullptr, StringPrintf("Cannot hook the method backup: %s", PrettyMethod(art_method).c_str()).c_str());</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// return;</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// }</span>
ScopedObjectAccess soa(env);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Create a backup of the ArtMethod object</span>
ArtMethod* backup_method = down_cast<ArtMethod*>(art_method->Clone(soa.Self()));
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Set private flag to avoid virtual table lookups during invocation</span>
backup_method->SetAccessFlags(backup_method->GetAccessFlags() <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">/*| kAccXposedOriginalMethod*/</span>);
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Create a Method/Constructor object for the backup ArtMethod object</span>
jobject reflect_method;
<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">if</span> (art_method->IsConstructor()) {
reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor);
} <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">else</span> {
reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method);
}
env->SetObjectField(reflect_method, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod,
env->NewGlobalRef(soa.AddLocalReference<jobject>(backup_method)));
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Save extra information in a separate structure, stored instead of the native method</span>
DexposedHookInfo* hookInfo = <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">reinterpret_cast</span><DexposedHookInfo*>(<span class="hljs-built_in" style="color:#66066;box-sizing: border-box;">calloc</span>(<span class="hljs-number" style="color:#06666;box-sizing: border-box;">1</span>, <span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">sizeof</span>(DexposedHookInfo)));
hookInfo->reflectedMethod = env->NewGlobalRef(reflect_method);
hookInfo->additionalInfo = env->NewGlobalRef(additional_info);
hookInfo->originalMethod = backup_method;
jstring shorty = (jstring)env->GetObjectField(additional_info,additionalhookinfo_shorty_field);
hookInfo->shorty = env->GetStringUTFChars(shorty, <span class="hljs-number" style="color:#06666;box-sizing: border-box;">0</span>);
LOG(INFO) << <span class="hljs-string" style="color:#0880;box-sizing: border-box;">"dexposed: >>> EnableXposedHook shorty:"</span> << hookInfo->shorty;
<span class="hljs-preprocessor" style="color:#444444;box-sizing: border-box;">#if PLATFORM_SDK_VERSION < 22</span>
art_method->SetNativeMethod(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">reinterpret_cast</span><uint8_t *>(hookInfo));
<span class="hljs-preprocessor" style="color:#444444;box-sizing: border-box;">#else</span>
art_method->SetEntryPointFromJni(<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">reinterpret_cast</span><<span class="hljs-keyword" style="color:#0088;box-sizing: border-box;">void</span> *>(hookInfo));
<span class="hljs-preprocessor" style="color:#444444;box-sizing: border-box;">#endif</span>
art_method->SetEntryPointFromQuickCompiledCode(GetQuickDexposedInvokeHandler());
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// art_method->SetEntryPointFromInterpreter(art::artInterpreterToCompiledCodeBridge);</span>
<span class="hljs-comment" style="color:#8800;box-sizing: border-box;">// Adjust access flags</span>
art_method->SetAccessFlags((art_method->GetAccessFlags() & ~kAccNative) <span class="hljs-comment" style="color:#8800;box-sizing: border-box;">/*| kAccXposedHookedMethod*/</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li></ul>
通过:
art_method->SetAccessFlags((art_method->GetAccessFlags()&~kAccNative)/| kAccXposedHookedMethod/);
art_method->SetEntryPointFromQuickCompiledCode(GetQuickDexposedInvokeHandler());
同样也是把把实现指向native方法实现调度机制来达到目的。
原文地址: http://blog.youkuaiyun.com/qxs965266509/article/details/50117137
本文详细介绍了阿里巴巴AndFix热修复框架的工作原理及其源码分析。主要讲述了如何通过改变方法对象实现在Dalvik和Art虚拟机上的热修复,并探讨了Dexposed框架的使用方法。
1万+

被折叠的 条评论
为什么被折叠?



