通過 VirtualApp 實現免 Root 許可權 Hook

幕後眼光發表於2019-01-22

一、前言

之前寫的 「裝X指南之Xposed安裝與配置」,有人反饋手機 root 風險較大,而且操作成本高,有沒有什麼方法是不需要 root 就能夠實現 hook 的或者不需要 Xposed 也能玩起外掛的?於是就有了這篇文章,離開 Xposed ,帶你免 root 實現 hook!

二、VirtualApp

1.關於 VirtualApp 的介紹

VA目前被廣泛應用於外掛化開發、無感知熱更新、自動化、多開等技術領域,但它決不僅限於此,Android本身就是一個極其開放的平臺,免安裝執行APK這一Feature開啟了無限可能-----這都取決於您的想象力。

感謝 asLody 開源,據說他寫這個專案才高二,佩服佩服~

  • VirtualApp 專案地址:
  1. github.com/asLody/Virt…

2.VirtualApp 的原理

VirtualApp 偽造了一套 framework 程式碼,實現所有在其程式啟動的應用,都執行在一個虛擬空間(注:個人理解,如有錯誤,還請指出)。

  • VirtualApp 原始碼學習與原理分析
  1. blog.csdn.net/leif_/artic…
  2. blog.csdn.net/ganyao93954…

3.VirtualApp 使用問題

Github 上的程式碼,作者已經沒有繼續開源更新了,可以看到後續的所有修改,都在作者的商業版上操作,所以有可能在使用上會出現一些 bug

其實可以看到「商業版」,不管穩定性與相容性,都做了很大的修復和改動,最重要的是,支援 Dalvik 和 Art 的 Java Hook( API 同 Xposed ),可惜在作者沒有公開原始碼的情況下,我們個人不可能為了學習去購買「商業版」~

特別說明:作者明確指出,如果專案需要投入商業使用,請購買「商業版」。我們這裡僅做技術學習使用

商業版特性

4.VirtualHook 介紹

上文說到我們無法使用「商業版」的 VirtualApp ,來進行 Hook ,準確來說是作者沒把 Hook 的 Api 公開。

下面我要介紹另一個基於 VirtualApp 改造的專案 —— VirtualHook(區分:VirtualAppVirtualHook 的區別,不要搞混了,後文使用 VirtualHook 來實踐),感謝 rk700 開源 VirtualHook 與 YAHFA

1)VirtualHook 專案地址:
  1. github.com/rk700/Virtu…
2)VirtualHook 構成:

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.
3)VirtualHook 注入
  • 關鍵的地方,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

1.YAHFA 介紹

YAHFA(Yet Another Hook Framework for ART) 是基於 ART 的 Hook 框架,支援 Android 5.0 ~ 9.0 版本的 Java 方法的 Hook 與替代 。而 VirtualHook 則是靠 YAHFA 實現的免 Root Hook。

  • 來自看雪論壇:
  1. bbs.pediy.com/thread-2167…
  • YAHFA 專案地址:
  1. github.com/rk700/YAHFA

2.YAHFA 原理

我是看不太懂裡面的原理,但是還是把別人的分析過程,貼出來給大家,希望看懂的朋友,不吝分享:

  • 寫文的時候,好像作者的部落格掛了,不過還是寫上吧

rk700.github.io/2017/03/30/…

  • csdn 一位博主分享的原理分析

blog.csdn.net/zhu92903326…

3.YAHFA Hook

解釋一下相關變數與方法:

  • className:指定要 hook 的類名
  • methodName:指定要 hook 的方法
  • methodSig:指定要 hook 的方法簽名
  • hook():該方法是你 hook 方法需要處理的邏輯,這裡執行 hook 相關操作
  • backup():是原方法的呼叫,一般不需要重寫什麼
1)普通方法

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;
    }
}
複製程式碼
2)靜態方法

靜態方法和靜態差不多,區別就是,靜態的方法在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;
    }
}
複製程式碼
3)匿名內部類

內部類只是編譯時的概念,一旦編譯成功,就會出現兩個不同的類,例如,類outClass中有個intClass,那麼編譯後就出現一個名為outClass.class和一個outClass$intClass.class的類。所以className中就要指定類路徑為a.b.c.outClass$intClass

4.獲取方法的簽名描述符

1)方法簽名描述符組成,括號內是引數的簽名,括號外是返回值的簽名:
如 Log.e() 裡面的方法:
    public static int e(String tag, String msg)
對應
    (Ljava/lang/String;Ljava/lang/String;)I
複製程式碼
2)各型別參照表
  • 除了 boolean 和 long 型別分別是 Z 和 J 外,其他的描述符對應的都是 Java 型別名的大寫首字母。另外,void 的描述符為 V
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 + 型別描符 + ; 引用型別
  • 說明:
  1. 陣列用 [ 表示,二維陣列 [[ 表示。如: [Ljava/lang/String; 對應 String[], [[Ljava/lang/Object; 對應 Object[][]
  2. 引用型別注意前面的 L , “/” 分割和 “;” ,不要遺漏了。如:Lcom/tencent/wcdb/Cursor;
  3. String 是物件,所以是:Ljava/lang/String;
  4. 還是不知道怎麼寫的話,可以通過以下 adb 命令找到方法簽名描述符:
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 搭配 YAHFA 使用教程

我們這裡是使用 VirtualHook 來實踐 。總體步驟如下:

  1. git clone VirtualHook 工程或者下載原始碼
  2. 新建 module 並配置為外掛
  3. module 打包成 apk,並放到手機裡面
  4. VirtualHook 裡面,克隆目標 App 和載入外掛 apk

專案目錄結構如下:

專案目錄結構

  • applibVirtualApp 相關程式碼
  • YAHFAHook 框架程式碼
  • demoHookPlugin 是外掛 module

1.配置外掛 module

配置外掛 moduleAndroidManifest.xmlmeta-data 的值,設定 valuetrue

<application
    android:label="@string/app_name">
    <meta-data
        android:name="yahfa.hook.plugin"
        android:value="true"
    />
</application>
複製程式碼

2.配置 Hook 類

假如我們需要 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();
    }
}
複製程式碼

3.驗證結果

  • 這裡我寫了個 hook 微信啟動頁的 onCreate() 方法。
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;
    }
}
複製程式碼
  • 安裝打包好的外掛apk,外掛左上有個小圖示,以作區別,同時克隆 微信 到 VirtualHook 裡面

外掛
安裝

  • hook 成功列印出 log
    Hook_Wx_Launcher

更多技術分享,請加微信公眾號——碼農茅草屋:

碼農茅草屋

相關文章