behindeye 发表于 2019-1-20 12:01

通过 VirtualApp 实现免 Root 权限 Hook

本帖最后由 behindeye 于 2020-1-20 14:12 编辑

![装X指南之通过 VirtualApp 实现免 Root 权限 Hook](https://i.loli.net/2019/01/20/5c43f09eec806.jpg)

## 一、前言
之前写的 [「装X指南之Xposed安装与配置」](https://www.jianshu.com/p/aa2ada7bd637),有人反馈手机 root 风险较大,而且操作成本高,有没有什么方法是不需要 root 就能够实现 hook 的或者不需要 Xposed 也能玩起插件的?**于是就有了这篇文章,离开 Xposed ,带你免 root 实现 hook!**

## 二、VirtualApp

#### 1、关于 VirtualApp 的介绍
>VA目前被广泛应用于插件化开发、无感知热更新、自动化、多开等技术领域,但它决不仅限于此,Android本身就是一个极其开放的平台,免安装运行APK这一Feature打开了无限可能-----这都取决于您的想象力。

**感谢 asLody 开源,据说他写这个项目才高二,佩服佩服~**

- VirtualApp 项目地址:
> 1. https://github.com/asLody/VirtualApp

#### 2、VirtualApp 的原理

`VirtualApp` 伪造了一套 `framework` 代码,实现所有在其进程启动的应用,都运行在一个虚拟空间(注:个人理解,如有错误,还请指出)。

- VirtualApp 源码学习与原理分析
> 1. https://blog.csdn.net/leif_/article/details/72420934
> 2. https://blog.csdn.net/ganyao939543405/article/details/76146760

#### 3、VirtualApp 使用问题

Github 上的代码,作者已经没有继续开源更新了,可以看到后续的所有修改,都在作者的商业版上操作,所以有可能在使用上会出现一些 `bug`。

其实可以看到「商业版」,不管稳定性与兼容性,都做了很大的修复和改动,最重要的是,**支持 Dalvik 和 Art 的 Java Hook( API 同 Xposed )**,可惜在作者没有公开源码的情况下,我们个人不可能为了学习去购买「商业版」~

**特别说明:作者明确指出,如果项目需要投入商业使用,请购买「商业版」**。我们这里仅做技术学习使用


![商业版特性](https://upload-images.jianshu.io/upload_images/2578395-c63da5780d65a0a8.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/661/format/webp)

#### 4、VirtualHook 介绍
上文说到我们无法使用「商业版」的 `VirtualApp` ,来进行 Hook ,准确来说是作者没把 Hook 的 Api 公开。

下面我要介绍另一个基于 `VirtualApp` 改造的项目 —— VirtualHook(**区分:`VirtualApp` 与 `VirtualHook` 的区别,不要搞混了,后文使用 VirtualHook 来实践**),**感谢 rk700 开源 VirtualHook 与 YAHFA**

#####1)VirtualHook 项目地址:
> 1. https://github.com/rk700/VirtualHook

#####2)VirtualHook 构成:

> VirtualHook is a tool for hooking application without root permission. It is based on two projects:
> - (https://github.com/asLody/VirtualApp). It's a plugin framework which allows running applications in its virtual space.
> - (https://github.com/rk700/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. https://bbs.pediy.com/thread-216786.htm

- YAHFA 项目地址:
> 1. https://github.com/rk700/YAHFA

#### 2、YAHFA 原理

我是看不太懂里面的原理,但是还是把别人的分析过程,贴出来给大家,希望看懂的朋友,不吝分享:

- 写文的时候,好像作者的博客挂了,不过还是写上吧
> http://rk700.github.io/2017/03/30/YAHFA-introduction/

- csdn 一位博主分享的原理分析
> https://blog.csdn.net/zhu929033262/article/details/74457324

#### 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. 数组用 [ 表示,二维数组 [[ 表示。如: ``,
`[[]`
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



项目目录结构如下:

![项目目录结构](https://upload-images.jianshu.io/upload_images/2578395-c552262fa88b8ee3.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/355/format/webp)

- `app` 和 `lib` 是 `VirtualApp` 相关代码
- `YAHFA` 是 `Hook` 框架代码
- `demoHookPlugin` 是插件 `module`

#### 1、配置插件 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>
```

#### 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` 里面

![插件](https://upload-images.jianshu.io/upload_images/2578395-d6b62d0fa262092d.png?imageMogr2/auto-orient/strip|imageView2/2/w/274/format/webp)
![安装](https://upload-images.jianshu.io/upload_images/2578395-301e805c1631871d.png?imageMogr2/auto-orient/strip|imageView2/2/w/273/format/webp)

- hook 成功打印出 log
!(https://upload-images.jianshu.io/upload_images/2578395-d9cdc18c56c6624e.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp)

behindeye 发表于 2019-1-20 13:51

相关阅读:
用Xposed把支付宝资产改成100w

如有写的不对的地方,还请指出改正,感谢支持~

中财内参早知道 发表于 2019-1-21 22:38

heavyd 发表于 2019-1-20 14:02
直接吧我看懵了。。。。有没有成品啊。。我现在这个荣耀8X。不能root 。。愁死了。。XPOSED。平行空间   都 ...

VirtualXposed和无极都可以简单实现NO root安装各种多开,抢红包插件。。。

behindeye 发表于 2019-1-20 14:26

behindeye 发表于 2019-1-20 13:51
相关阅读:
用Xposed把支付宝资产改成100w



原理跟这差不多 https://www.52pojie.cn/forum.php?mod=viewthread&tid=792701&ctid=1083

xiawan 发表于 2019-1-20 12:45

看的懵懂。。。。

behindeye 发表于 2019-1-20 12:52

xiawan 发表于 2019-1-20 12:45
看的懵懂。。。。

额,慢慢看,不懂可以问我

冷若冰淡如水 发表于 2019-1-20 13:29

容我好好学习一波

edwinchang 发表于 2019-1-20 13:41

{:1_925:} 好东西降低风险~但是需要长时间研究。。比较Xpos 比较方便。 希望能弄个简介版本

heavyd 发表于 2019-1-20 14:02

直接吧我看懵了。。。。有没有成品啊。。我现在这个荣耀8X。不能root 。。愁死了。。XPOSED。平行空间   都用过了。还是不行。也不知道是不是我不会用。

御河 发表于 2019-1-20 14:20

额,慢慢看,不懂可以问我

foreverhsu 发表于 2019-1-20 14:44

root一下也不会有什么损失吧,学习这个的目的是为啥
页: [1] 2 3 4 5 6 7
查看完整版本: 通过 VirtualApp 实现免 Root 权限 Hook