如何使用FastHook免root hook微信
一、概述
本文介绍如何通过FastHook + VirtualApp实现免root hook。由于VirtualApp已经不更新了,所以本文只作为一个教程,并不主要解决一些兼容和稳定性问题。
项目地址:VirtualFastHook:https://github.com/turing-technician/VirtualFastHook
二、实现原理
要实现应用hook,可以简单的分为下面三个步骤:
1. 识别Hook插件
2. 保存Hook插件信息
3. 获取Hook插件信息进行Hook
2.1 识别Hook插件
规定Hook插件须在AndroidManifest.xml里定义三个<meta-data>:
<meta-data
android:name="fasthook.hook.plugin"
android:value="true"/>
<meta-data
android:name="fasthook.hook.process"
android:value="XXX"/>
<meta-data
android:name="fasthook.hook.info"
android:value="XXX"/>
1. fasthook.hook.plugin:表示这是一个Hook插件
2. fasthook.hook.process:表示要Hook的进程
3. fasthook.hook.info:Hook信息类名
可以在VirtualApp解析Apk时加上判断是否为Hook插件的代码,例如在AppRepository.java
private List<AppInfo> convertPackageInfoToAppData(Context context, List<PackageInfo> pkgList, boolean fastOpen) {
PackageManager pm = context.getPackageManager();
List<AppInfo> list = new ArrayList<>(pkgList.size());
String hostPkg = VirtualCore.get().getHostPkg();
for (PackageInfo pkg : pkgList) {
// ignore the host package
if (hostPkg.equals(pkg.packageName)) {
continue;
}
// ignore the System package
if (isSystemApplication(pkg)) {
continue;
}
boolean isHookPlugin = false;
//start 判断是否是Hook插件
ApplicationInfo ai = null;
try {
ai = context.getPackageManager().getApplicationInfo(pkg.packageName,PackageManager.GET_META_DATA);
if(ai.metaData != null) {
boolean enable = ai.metaData.getBoolean("fasthook.hook.plugin", false);
if(enable) {
String hookProcess = ai.metaData.getString("fasthook.hook.process","");
String hookInfo = ai.metaData.getString("fasthook.hook.info","");
if(!hookProcess.isEmpty() || !hookInfo.isEmpty()) {
isHookPlugin = true;
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
//end 判断是否是Hook插件
String path = ai.publicSourceDir != null ? ai.publicSourceDir : ai.sourceDir;
if (path == null) {
continue;
}
AppInfo info = new AppInfo();
info.packageName = pkg.packageName;
info.fastOpen = fastOpen;
info.path = path;
info.icon = ai.loadIcon(pm);
info.name = ai.loadLabel(pm);
info.isHook = isHookPlugin;
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(pkg.packageName, 0);
if (installedAppInfo != null) {
info.cloneCount = installedAppInfo.getInstalledUsers().length;
}
list.add(info);
}
return list;
}
2.2 如何获取Hook插件
在apk安装时,可以把Hook插件保存起来,例如在VAppManagerService.java。
public synchronized InstallResult installPackage(String path, int flags, boolean notify) {
//无关代码
boolean isHook = (flags & InstallStrategy.IS_HOOK) != 0;
//无关代码
if (res.isUpdate) {
FileUtils.deleteDir(libDir);
VEnvironment.getOdexFile(pkg.packageName).delete();
if(isHook) {
VActivityManagerService.get().killAllApps();
}
else {
VActivityManagerService.get().killAppByPkg(pkg.packageName, VUserHandle.USER_ALL);
}
}
//无关代码
PackageSetting ps;
if (existSetting != null) {
ps = existSetting;
} else {
ps = new PackageSetting();
}
ps.isHook = isHook;
ps.dependSystem = dependSystem;
ps.apkPath = packageFile.getPath();
ps.libPath = libDir.getPath();
ps.packageName = pkg.packageName;
ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
if (res.isUpdate) {
ps.lastUpdateTime = installTime;
} else {
ps.firstInstallTime = installTime;
ps.lastUpdateTime = installTime;
for (int userId : VUserManagerService.get().getUserIds()) {
boolean installed = userId == 0;
ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
}
}
//无关代码
//保存Hook插件信息
if(isHook) {
HookCacheManager.HookCacheInfo info = new HookCacheManager.HookCacheInfo(ps.packageName,(String)(pkg.mAppMetaData.get(HookCacheManager.HOOK_PROCESS)),(String)(pkg.mAppMetaData.get(HookCacheManager.HOOK_INFO)));
HookCacheManager.put((String)(pkg.mAppMetaData.get(HookCacheManager.HOOK_PROCESS)),info);
}
else if (notify) {
notifyAppInstalled(ps, -1);
}
res.isSuccess = true;
return res;
}
2.3 如何Hook
实际可以在任意地方Hook,但为了更好的Hook,这里在应用apk加载之后,attachBaseContext方法调用之前进行Hook,这样便可以Hook所有应用的方法了。例如在VClientImpl.java。
private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) {
//无关代码
NativeEngine.launchEngine();
Object mainThread = VirtualCore.mainThread();
NativeEngine.startDexOverride();
Context context = createPackageContext(data.appInfo.packageName);
System.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath());
//无关代码
Object boundApp = fixBoundApp(mBoundApplication);
mBoundApplication.info = ContextImpl.mPackageInfo.get(context);
mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info);
VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion);
//进行Hook
try {
tryHook(processName,context.getClassLoader());
}catch (Exception e) {
e.printStackTrace();
}
//无关代码
VirtualCore.get().getComponentDelegate().beforeApplicationCreate(mInitialApplication);
try {
mInstrumentation.callApplicationOnCreate(mInitialApplication);
InvocationStubManager.getInstance().checkEnv(HCallbackStub.class);
if (conflict) {
InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);
}
Application createdApp = ActivityThread.mInitialApplication.get(mainThread);
if (createdApp != null) {
mInitialApplication = createdApp;
}
} catch (Exception e) {
if (!mInstrumentation.onException(mInitialApplication, e)) {
throw new RuntimeException(
"Unable to create application " + mInitialApplication.getClass().getName()
+ ": " + e.toString(), e);
}
}
VActivityManager.get().appDoneExecuting();
VirtualCore.get().getComponentDelegate().afterApplicationCreate(mInitialApplication);
}
//根据进程名获取Hook插件并Hook
private void tryHook(String process, ClassLoader apkClassLoader) {
String[] infos = VPackageManager.get().getInstalledHookPlugins(process);
if(infos != null) {
for(String info : infos) {
int size = info.charAt(0);
String pluginName = info.substring(1,1 + size);
String hookInfoName = info.substring(1 + size);
DexClassLoader hookClassLoader = new DexClassLoader(VEnvironment.getPackageResourcePath(pluginName).getAbsolutePath(),
VEnvironment.getDalvikCacheDirectory().getAbsolutePath(),
VEnvironment.getPackageLibPath(pluginName).getAbsolutePath(),
apkClassLoader);
FastHookManager.doHook(hookInfoName,hookClassLoader,apkClassLoader,hookClassLoader,hookClassLoader,false);
}
}
}
三、Hook微信
3.1 准备一个Hook插件
根据FastHook框架要求,提供一下信息:
1. HookMethodInfo.java(Hook方法、Forward方法具体实现)
public class HookMethodInfo {
public static void hook(Object thiz, Context context) {
Log.d("FastHookManager","hook attachBaseContext2");
forward(thiz,context);
Toast toast = Toast.makeText(context,"hook attachBaseContext2",Toast.LENGTH_LONG);
toast.show();
}
public native static void forward(Object thiz, Context context);
}
可以看到Hook方法的逻辑很简单,只是弹出一个Toast,内容为“hook attachBaseContext2”。
2. HookInfo.java(根据FastHook框架规定提供HOOK_ITEMS信息)
public class HookInfo {
public static String[][] HOOK_ITEMS = {
{"1",
"com.tencent.tinker.loader.app.TinkerApplication","attachBaseContext","Landroid/content/Context;",
"com.example.fasthookplugin.HookMethodInfo","hook","Ljava/lang/Object;Landroid/content/Context;",
"com.example.fasthookplugin.HookMethodInfo","forward","Ljava/lang/Object;Landroid/content/Context;"}
};
}
使用的是Inline模式,Hook的是attachBaseContext方法,这是应用被系统调用的第一个方法。
- 配置AndroidManifest.xml(配置Hook插件信息)
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="fasthook.hook.plugin"
android:value="true"/>
<meta-data
android:name="fasthook.hook.process"
android:value="com.tencent.mm"/>
<meta-data
android:name="fasthook.hook.info"
android:value="com.example.fasthookplugin.HookInfo"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
配置apk为Hook插件,Hook的目标进程为com.tencent.mm,即微信主进程,具体Hook信息位于HookInfo类HOOK_ITEMS数组。
三、实际效果
VirtualFastHook主界面.png安装上VirtualFastHook和Hook插件后,运行看看实际效果
安装Hook插件.png
运行微信.png
Hook成功.png
所有的Hook插件左上角都有一个红色小图标,代表该应用为Hook插件
四、结语
接受消息.png上述只是一个基本Hook操作,实际还可以做出更多有用的功能,下面这个是我随手做的7.0.3版本微信消息防撤回。
阻止对方撤回消息.png
对方显示消息已撤回.png
五、参考
欢迎大家关注图灵技师公众号
FastHook:https://github.com/turing-technician/FastHook
VirtualApp:https://github.com/asLody/VirtualApp