之前写的 「装X指南之Xposed安装与配置」,有人反馈手机 root 风险较大,而且操作成本高,有没有什么方法是不需要 root 就能够实现 hook 的或者不需要 Xposed 也能玩起插件的?于是就有了这篇文章,离开 Xposed ,带你免 root 实现 hook!
VA目前被广泛应用于插件化开发、无感知热更新、自动化、多开等技术领域,但它决不仅限于此,Android本身就是一个极其开放的平台,免安装运行APK这一Feature打开了无限可能-----这都取决于您的想象力。
感谢 asLody 开源,据说他写这个项目才高二,佩服佩服~
- https://github.com/asLody/VirtualApp
VirtualApp
伪造了一套 framework
代码,实现所有在其进程启动的应用,都运行在一个虚拟空间(注:个人理解,如有错误,还请指出)。
- https://blog.csdn.net/leif_/article/details/72420934
- https://blog.csdn.net/ganyao939543405/article/details/76146760
Github 上的代码,作者已经没有继续开源更新了,可以看到后续的所有修改,都在作者的商业版上操作,所以有可能在使用上会出现一些 bug
。
其实可以看到「商业版」,不管稳定性与兼容性,都做了很大的修复和改动,最重要的是,支持 Dalvik 和 Art 的 Java Hook( API 同 Xposed ),可惜在作者没有公开源码的情况下,我们个人不可能为了学习去购买「商业版」~
特别说明:作者明确指出,如果项目需要投入商业使用,请购买「商业版」。我们这里仅做技术学习使用
上文说到我们无法使用「商业版」的 VirtualApp
,来进行 Hook ,准确来说是作者没把 Hook 的 Api 公开。
下面我要介绍另一个基于 VirtualApp
改造的项目 —— VirtualHook(区分:VirtualApp
与 VirtualHook
的区别,不要搞混了,后文使用 VirtualHook 来实践),感谢 rk700 开源 VirtualHook 与 YAHFA
- https://github.com/rk700/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.
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。
- https://bbs.pediy.com/thread-216786.htm
- https://github.com/rk700/YAHFA
我是看不太懂里面的原理,但是还是把别人的分析过程,贴出来给大家,希望看懂的朋友,不吝分享:
http://rk700.github.io/2017/03/30/YAHFA-introduction/
https://blog.csdn.net/zhu929033262/article/details/74457324
解释一下相关变量与方法:
- 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
假如我们需要 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
里面更多技术分享,请加微信公众号——码农茅草屋: