以前寫的 「裝X指南之Xposed安裝與配置」,有人反饋手機 root 風險較大,並且操做成本高,有沒有什麼方法是不須要 root 就可以實現 hook 的或者不須要 Xposed 也能玩起插件的?因而就有了這篇文章,離開 Xposed ,帶你免 root 實現 hook!java
VA目前被普遍應用於插件化開發、無感知熱更新、自動化、多開等技術領域,但它決不只限於此,Android自己就是一個極其開放的平臺,免安裝運行APK這一Feature打開了無限可能-----這都取決於您的想象力。android
感謝 asLody 開源,聽說他寫這個項目才高二,佩服佩服~git
VirtualApp
僞造了一套 framework
代碼,實現全部在其進程啓動的應用,都運行在一個虛擬空間(注:我的理解,若有錯誤,還請指出)。github
Github 上的代碼,做者已經沒有繼續開源更新了,能夠看到後續的全部修改,都在做者的商業版上操做,因此有可能在使用上會出現一些 bug
。api
其實能夠看到「商業版」,無論穩定性與兼容性,都作了很大的修復和改動,最重要的是,支持 Dalvik 和 Art 的 Java Hook( API 同 Xposed ),惋惜在做者沒有公開源碼的狀況下,咱們我的不可能爲了學習去購買「商業版」~數組
特別說明:做者明確指出,若是項目須要投入商業使用,請購買「商業版」。咱們這裏僅作技術學習使用bash
上文說到咱們沒法使用「商業版」的 VirtualApp
,來進行 Hook ,準確來講是做者沒把 Hook 的 Api 公開。微信
下面我要介紹另外一個基於 VirtualApp
改造的項目 —— VirtualHook(區分:VirtualApp
與 VirtualHook
的區別,不要搞混了,後文使用 VirtualHook 來實踐),感謝 rk700 開源 VirtualHook 與 YAHFAapp
VirtualHook is a tool for hooking application without root permission. It is based on two projects:框架
- VirtualApp. It's a plugin framework which allows running applications in its virtual space.
- YAHFA . It's a hook framework for ART which allows hooking Java method of the application.
VirtualHook
修改 VirtualApp
的核心代碼,提供 Hook 注入代碼的窗口VirtualApp
裏面 VClienImpl
類注入的關鍵代碼DexClassLoader dexClassLoader = new DexClassLoader(apkPath,
VEnvironment.getDalvikCacheDirectory().getAbsolutePath(),
libPath,
appClassLoader);
// YAHFA do hook
HookMain.doHookDefault(dexClassLoader, appClassLoader);
複製代碼
public void findAndBackupAndHook(Class targetClass, String methodName,
String methodSig, Method hook, Method backup);
複製代碼
YAHFA(Yet Another Hook Framework for ART)
是基於 ART 的 Hook 框架,支持 Android 5.0 ~ 9.0
版本的 Java 方法的 Hook 與替代 。而 VirtualHook 則是靠 YAHFA 實現的免 Root Hook。
我是看不太懂裏面的原理,可是仍是把別人的分析過程,貼出來給你們,但願看懂的朋友,不吝分享:
解釋一下相關變量與方法:
- className:指定要 hook 的類名
- methodName:指定要 hook 的方法
- methodSig:指定要 hook 的方法簽名
- hook():該方法是你 hook 方法須要處理的邏輯,這裏執行 hook 相關操做
- backup():是原方法的調用,通常不須要重寫什麼
如 Log.e()
方法。代碼以下:
public class Hook_Log_e {
public static String className = "android.util.Log";
public static String methodName = "e";
public static String methodSig = "(Ljava/lang/String;Ljava/lang/String;)I";
public static int hook(String tag, String msg) {
Log.w("YAHFA", "in Log.e(): "+tag+", "+msg);
return backup(tag, msg);
}
public static int backup(String tag, String msg) {
Log.w("YAHFA", "Log.e() should not be here");
return 1;
}
}
複製代碼
靜態方法和靜態差很少,區別就是,靜態的方法在hook和origin的參數中,少一個 Object 的參數。如 URI.create()
方法。代碼以下:
public class Hook_url {
public static String className = "java.net.URI";
public static String methodName = "create";
public static String methodSig = "(Ljava/lang/String;)Ljava/net/URI;";
public static Object hook(String url)
{
// 改變 url 的值
url = "http://www.baidu.com";
return origin(url);
}
public static Object origin(String url)
{
Log.w("YAHFA", "String.startsWith() should not be here");
return url;
}
}
複製代碼
內部類只是編譯時的概念,一旦編譯成功,就會出現兩個不一樣的類,例如,類outClass
中有個intClass
,那麼編譯後就出現一個名爲outClass.class
和一個outClass$intClass.class
的類。因此className
中就要指定類路徑爲a.b.c.outClass$intClass
如 Log.e() 裏面的方法:
public static int e(String tag, String msg)
對應
(Ljava/lang/String;Ljava/lang/String;)I
複製代碼
File Desciptor | Java Language Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
V | void |
[ | array |
L + 類型描符 + ; | 引用類型 |
[Ljava/lang/String;
對應 String[]
, [[Ljava/lang/Object;
對應 Object[][]
Lcom/tencent/wcdb/Cursor;
Ljava/lang/String;
1. 查看 Java 類的方式 javap -s java.awt.Label
2. 查看 Android 類的方式 javap -s -bootclasspath "D:\Program Files\Android\android-sdk\platforms\android-25\android.jar" -classpath bin/classes android.app.Activity
3. 查看第三方 Jar 的類的方式 javap -s -classpath "D:\AMap_Location.jar" com.amap.api.location.AMapLocation
複製代碼
咱們這裏是使用 VirtualHook
來實踐 。整體步驟以下:
git clone VirtualHook
工程或者下載源碼module
並配置爲插件module
打包成 apk,並放到手機裏面VirtualHook
裏面,克隆目標 App 和加載插件 apk項目目錄結構以下:
app
和 lib
是 VirtualApp
相關代碼YAHFA
是 Hook
框架代碼demoHookPlugin
是插件 module
配置插件 module
的 AndroidManifest.xml
的 meta-data
的值,設置 value
爲 true
<application
android:label="@string/app_name">
<meta-data
android:name="yahfa.hook.plugin"
android:value="true"
/>
</application>
複製代碼
假如咱們須要 Hook 處理 Log.e()
方法,新建一個 Hook_Log_e
類,並在 lab.galaxy.yahfa.HookInfo
配置(不配置的話,hook 不生效),代碼以下:
public class HookInfo {
public static String[] hookItemNames = {
"lab.galaxy.yahfa.demoPlugin.Hook_Log_e",
};
}
複製代碼
注意:HookInfo
類的包名,若是須要改的話,要同時改 HookMain.doHookDefault()
方法裏面的包名。
public static void doHookDefault(ClassLoader patchClassLoader, ClassLoader originClassLoader) {
try {
Class<?> hookInfoClass = Class.forName("lab.galaxy.yahfa.HookInfo", true, patchClassLoader);
String[] hookItemNames = (String[])hookInfoClass.getField("hookItemNames").get(null);
for(String hookItemName : hookItemNames) {
doHookItemDefault(patchClassLoader, hookItemName, originClassLoader);
}
hookInfoClasses.add(hookInfoClass);
}
catch (Exception e) {
e.printStackTrace();
}
}
複製代碼
public class Hook_Wx_Launcher {
public static String className = "com.tencent.mm.ui.LauncherUI";
public static String methodName = "onCreate";
public static String methodSig = "(Landroid/os/Bundle;)V";
public static Activity LauncherUi;
public static void hook(Object thiz, Bundle b) {
Log.w("czc", "LauncherUI oncreate");
return "";
}
public static void backup(Object thiz, Bundle b) {
Log.w("YAHFA", "LauncherUI backup");
return;
}
}
複製代碼
VirtualHook
裏面更多技術分享,請加微信公衆號——碼農茅草屋: