編輯:關於android開發
本文由嵌入式企鵝圈原創團隊成員、阿裡資深工程師Hao分享。
上篇文章《Android無線開發的幾種常用技術》我們介紹了幾種android移動應用開發中的常用技術,其中的熱補丁正在被越來越多的開發團隊所使用,它涉及到dalvik虛擬機和android的一些核心技術,現在就來介紹下它的一些原理。
本篇先介紹dexposed方案:https://github.com/alibaba/dexposed,它是手機淘寶團隊使用的熱補丁方案,後來開源到github上,取的名字dexposed表明了自己是基於大名鼎鼎的xposed hook方案,有飲水思源、回饋開源項目的意思。與xposed不同的是,dexposed是自己hook自己的應用,因此不需要root權限。
它的關鍵點是:在native層中先找到要修復的Java函數對應的Method對象,修改它變為native方法,把它的nativeFunc指向hookedMethodCallback。這樣對這個java函數的調用就轉為調用hookedMethodCallback這個native函數了,然後再用這個native函數回調java層自己實現的統一接口來處理。這個統一接口是XC_MethodReplacement類,它主要有beforeHookedMethod、afterHookedMethod和replaceHookedMethod等幾個方法,前兩個在執行原java函數前後做一些事,replaceHookedMethod則是替換原java方法。下面來詳細分析下這個hook的過程。
一、DexposedBridge.findAndHookMethod
findAndHookMethod是hook原java方法的入口,它傳入的參數是類Class和方法名,最後一個可變參數parameterTypesAndCallback,是用戶實現的用於替換原方法的XC_MethodReplacement的實例。
先調用XposedHelpers.findMethodExact找到要hook的java方法,再用hookMethod進行真正的hook。
1.findMethodExact根據類名和方法名,用反射找到Method,並把它的屬性改為可訪問。
2.hookMethod先把hook成功後的callback、要hook的方法的參數和返回值類型保存到AdditionalHookInfo中,把它作為參數傳給hookMethodNative。hookMethodNative是一個native方法,它的第3個參數slot表示該Method在class的方法表中所處的位置,在native的實現中會用到這個slot。
hookMethodNative的實現環境分dalvik和art,因為dexposed對art的支持不完善,同時art本身的原理和機制也是一個難點,所以本篇只介紹dalvik下的實現,art的有關內容以後有機會再作介紹。
二、hookMethodNative
每一個java的類在虛擬機的實現中都對應著一個C++的ClassObject。dvmDecodeIndirectRef是libdvm中的方法,它可以從java對象的間接引用獲得ClassObject對象,再根據slot,用dvmSlotToMethod找到Method對象。這裡的ClassObject和Method都是虛擬機內部用來表示class和Method的數據結構。
然後把原來的Method結構先備份到XposedHookInfo中,
XposedHookInfo的結構如下:
可見,它用originalMethod保存原來java方法的Method,用reflectedMethod保存原java方法在native的引用,注意這跟originalMethod中保存的Method對象不同。originalMethod中保存的Method可以理解為執行字節碼的地址,而reflectedMethod中保存的是用來描述原java方法的一個ClassObject對象。它們兩個在第五部分重新調用原java方法時會用到。
additionalInfo用來保存附加信息AdditionalHookInfo。接著使用SET_METHOD_FLAG宏把該方法設為native,讓nativeFunc指向hookedMethodCallback,這樣對該java方法的調用就會轉為對hookedMethodCallback這個native方法的調用了。Insns指向這個方法的字節碼,在這裡把它改為指向hookInfo,實際上也就是originalMethod的字節碼的地址。
三、hookedMethodCallback
hookedMethodCallback會回調java層的方法handleHookedMethod,最終會調用到前面說過的,在findAndHookMethod中傳入的XC_MethodReplacement裡的before、after方法。
這裡首先把在hookInfo中保存的信息作為傳給java層的handleHookedMethod的參數,然後用dvmCallMethod這個dalvik的函數調用xposedHandleHookedMethod這個java的方法。xposedHandleHookedMethod在初始化時已經被設置好了。
GetStaticMethodId是dvm中用來獲取靜態方法地址的函數,可見在初始化時,已經把java的靜態方法handleHookedMethod的地址賦給了xposedHandleHookedMethod了。這裡需要注意兩點,一是這個時候已經不能像沒hook之前那樣,通過jni從native調java的函數,或者從java調native的函數,因為原來java方法的上下文已經被改變了(已經被保存在hookInfo中),所以後面只能通過libdvm中的方法,手動修改函數的指向。二是dvmCallMethod的第5和第6個參數originalReflected和original就是第二部分中保存的方法的引用和方法的字節碼地址(original被直接轉成了int型),後面第五部分中這兩項還會被重新傳回native層用來找到原java函數的入口。
四、handleHookedMethod
前面說到從native中調回java的方法handleHookedMethod,handleHookedMethod會根據需要,選擇是否還調用原來的java方法,或者只調用XC_MethodReplacement裡自己實現的before、after方法。
其中beforeHookedMethod方法默認會調用replaceHookedMethod,我們只要實現它即可替代對原方法的調用。
如果param.returnEarly為false才會調invokeOriginalMethodNative執行原來的方法。
默認的beforeHookedMethod中會調setParam,把param.returnEarly的值設為為true,這樣就不會再調用原來的java方法了。
最後還要把返回值返回。
五、invokeOriginalMethodNative
如果在java層需要重新調用原java函數,那麼在第二部分中把原java函數的信息備份到hookInfo中就能起到作用了。Java層的invokeOriginalMethod方法會調一個native的方法invokeOriginalMethodNative來實現這個過程。
這個native函數同樣在初始化時就被設置好了:
要調用的invokeOriginalMethodNative在虛擬機中Method是dexposedInvokeOriginalMethod,這裡傳入了第二部分中備份的原java方法的對象引用reflectedMethod和字節碼地址int型的original。
dvmSetNativeFunc的第2個參數是DalvikBridgeFunc類型的指針,這個函數會把dexposedInvokeOriginalMethod的nativeFunc指向xxx_invokeOriginalMethodNative。再次注意此時不能像平常的jni調用那樣,java層的invokeOriginalMethodNative經過jni注冊後能直接調到com_taobao_android_dexposed_DexposedBridge_invokeOriginalMethodNative了。
dvmInvokeMethod跟dvmCallMethod一樣,都是dalvik中用來直接調Method的函數,這樣就完成了對原java方法的調用。
最後一句話概括這種hook方法,就是通過把原java方法的類型改為native來把對java函數的調用轉到native層,在native層用dvm的各種函數來操作Method的指針和對象來控制函數流程。
基於鴻洋博客自定於View實現的android音量調節控件,android音量調節 1.在values建立attrs.xml,寫出你需要的屬性:  
javascript基礎【一】2015/11/13 16:10:04javascript自誕生之初就處於爭論之中,但是這依舊不影響其成為今天WEB編程的主流語言。最初的j
安卓開發GitHub的入門使用流程---》適合小學生 使用AndroidStudio項目發布到GitHub 在AndroidStudio中新建一個項目 設
Android 配置文件(activity)元素 語法: . . . 被包含在: 可以包含: 描述: 聲明實現了應用程序可視化