最近在做大模型相关的工作,发现一款比较好用的第三方APP,模型推理的首字时间非常快,于是打算看看它的逻辑以及Prompt提示词,于是想到了一个方案:反编译加Xposed框架hook关键方法,顺着这个思路开整。
工具
Apktool
Apktool是一款逆向工程Android Apk文件的工具
Apktool官网
官网中讲解了Windows、Linux、Mac以及Brew平台的安装方式,我们按照步骤执行即可。-
Dex2Jar
Dex2Jar是将 dex 文件转换为包含 class 文件的 jar 文件的工具
Dex2Jar官网
安装:按照下图中指示下载最新的Releases版本即可
Dex2Jar使用说明.png
我们选择最新版本的dex-tools-v2.4.zip即可
Dex2Jar Release版本下载.png JD-GUI
JD-GUI是个独立的图形实用程序,用于显示".class"文件相应的Java源代码
JD-GUI官网
打开该应用后导入Dex2Jar转换后的Jar文件即可查看相应的Java源代码
如果觉得上面的方法繁琐,可以使用下面的简便方法
更简便工具
Jadx-GUI
- 主要功能
可从APK, dex, aar或zip文件反编译Dalvik字节码为Java源代码
解码AndroidManifest.xml文件以及resources.arsc中的其它的资源文件 - 软件下载
Jadx官网
打开官网按照下图指示下载即可使用
Jadx 下载.png -
使用方式
下载with-jre的版本,解压后双击"jadx-gui-1.5.1"打开该软件,点击【文件】选择要反编译的APK文件稍等片刻就完成了所有的反编译功能,包括源代码,资源文件以及AndroidManifest.xml文件
Jadx使用界面.png - 混淆方法处理
很多三方App都会混淆,如上图的类名、方法名、变量名等,对于这种代码我们一般拿不到mapping文件,本人水平有限暂时没有深入研究更好的方法处理(有经验的大神欢迎分享)
自己的处理逻辑主要还是根据一些不可变的内容结合App界面上显示内容进行逻辑分析,找到要Hook的方法
不可变内容:
-- Activity的名称
-- res/values目录中的内容
-- native方法名
使用XPosed框架Hook应用的特定方法
框架选择
- Xposed框架: 官方版本支持到Android 8.0/8.1 beta3版本
- Edxposed框架:支持Android 8.0到10.0,能够完全实现Xposed框架的相关功能,并且和rove89的官方Xposed一样,是不需要虚拟机直接安装在系统中的
- LSPosed框架:支持Android 8.1到15,是目前比较热门的Xposed框架,需要搭配Magisk使用
根据支持的版本,我们选择使用LSPosed框架
Magisk
Magisk官网
Magisk中文文档
Magisk App下载地址
前置知识
- 用户需了解adb及fastboot
- 用户设备需解锁引导加载程序(Bootloader)
Magisk使用步骤
- 解锁 Boot loader
注意:解锁 Bootloader 会清除数据,有重要数据请备份再操作!!!
注意:解锁 Bootloader 会清除数据,有重要数据请备份再操作!!!
注意:解锁 Bootloader 会清除数据,有重要数据请备份再操作!!!
-- 首先手机或平板要处于开发者模式
-- 在开发者选项中解锁引导加载程序,把【OEM解锁】功能打开即可,每款设备打开方式略有不同,用户可根据自己手中产品百度一下哈。
- 解锁 Boot loader
- 获取 boot.img 或者 init_boot.img(若有安装包,直接拷贝即可)
如果没有boot.img文件需要用户提取该文件,参考下面连接进行操作:
payload-dumper-go 提取 boot.img
- 获取 boot.img 或者 init_boot.img(若有安装包,直接拷贝即可)
我是在Lenovo的平板上操作的,使用boot.img文件试几次都不成功(Magisk不出现当前版本号信息,见下图),改用init_boot.img文件一次成功!用户操作时注意哈。
参考官网文档发现有三个文件,boot.img, init_boot.img, recovery.img

- 安卓设备连接电脑,开启文件传输模式,复制电脑中的.img文件到安卓设备
- 安卓设备Magisk App安装
Github安装连接
打开Magisk App下载地址,选择最新版本"Magisk-V28.1.apk"(这是写文档时最新版本,使用你看到的最新版本即可)下载安装即可
Magisk 下载.png
- 安卓设备Magisk App安装
-
修补.img镜像文件
修补生成的文件 magisk_patched-xxx.img 在 Download 目录
Patching Image.png
-
- adb-fastboot命令安装:直接使用SDK目录中platform-tools中的即可
- 设备连接电脑,开启文件传输模式,复制设备中修补后的magisk_patched-xxx.img文件到电脑
- 手机进入Bootloader模式
手机关机,同时按下电源键 + 音量下键
- 手机进入Bootloader模式
- 打开CMD命令行,执行下面的刷入命令
magisk.img 每次修补生成的名字都不一样,使用的时候请输入生成的名字
fastboot flash boot magisk_patched-xxx.img
- 打开CMD命令行,执行下面的刷入命令
-
显示OKAY 就是成功刷入了,使用指令 fastboot reboot 重启手机即可
image.png
-
LSPosed
下载LSPosed模块(Zygisk版)
-
打开Magisk设置中开启Zygisk
Zygisk开启.jpg -
打开Magisk - 模块 - 从本地安装
image.png -
重启设备,通知栏点开通知打开管理器
通知栏消息.jpg -
创建LSPosed快捷方式
LSPosed快捷方式创建.jpg -
LSPosed显示“已激活”则表示成功刷入
已激活.jpg
Hook应用
- Android Studio 中新建一个项目
- setting.gradle.kts文件设置
在setting文件中的dependencyResolutionManagement中添加xposed仓库地址:maven { url = uri("https://api.xposed.info/") } // 添加Xposed的库
- setting.gradle.kts文件设置
- build.gradle设置
在app模块的dependencies中添加xposed依赖
compileOnly("de.robv.android.xposed:api:82")
注意:此处只能使用compileOnly,否则会报错:
Cannot load module: com.example.xxx(自定义的包名)
The Xposed API classes are compiled into the module's APK.
- build.gradle设置
- AndroidManifest.xml设置
添加permission
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
在application标签中添加xposed的meta信息
<meta-data
android:name="xposedmodule"
android:value="true"/>
<meta-data
android:name="xposeddescription"
android:value="这是一个用于hook第三方APP方法的模块"/>
<meta-data
android:name="xposedminversion"
android:value="54"/>
<meta-data
android:name="xposedscope"
android:resource="@array/xposed_scope"/>
- AndroidManifest.xml设置
- 目标APP包名设置
在res\values目录下新建arrays.xml文件,在文件中输入如下内容:
<resources>
<string-array name="xposed_scope">
<item>com.example.d</item>
</string-array>
</resources>
- 目标APP包名设置
- assets目录创建
右键点击[src]-[new]-[Directory],选择main\assets,创建assets目录,在该目录中创建文件:xposed_init,并在文件中输入:com.example.lsposed.MainHook,该类是你要创建执行的Hook的类全路径名。
- assets目录创建
- Hook方法
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
public class HookTest {
public static void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam.packageName.equals("目标APP的包名")) { // 替换为目标APP的包名
XposedBridge.log("开始hook目标APP的方法");
Class<?> targetClass = lpparam.classLoader.loadClass("目标类的完整路径"); // 替换为目标类的完整路径
XposedHelpers.findAndHookMethod(targetClass, "目标方法名", new XC_MethodHook() { // 替换为目标方法名
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("方法执行前的逻辑");
super.beforeHookedMethod(param);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("方法执行后的逻辑");
super.afterHookedMethod(param);
}
});
}
}
}
- java.lang.NoSuchMethodError处理
因为混淆后的代码运行也是以混淆方式执行的,因此Hook的时候使用混淆后的类名、方法名称即可
如果类名、方法名都对了还报这种错误一般是方法路径不正确,比如:
- java.lang.NoSuchMethodError处理
8.1 Companion类的完整路径
在 Kotlin 中,Companion 类的完整路径通常是 ClassName$Companion,例如,如果目标类是 com.example.MyClass,那么 Companion 类的完整路径就是 com.example.MyClass$Companion-
带有参数的方法正确的Hook方式
-- 通用类型参数,直接使用该类型即可
-- 第三方App自定义的接口类型,需使用相关的classLoader.loadClass()方法加载该三方类获取类型,如下图所示
获取三方App自定义类型.png
以下是完整的示例,供参考
-
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class MyHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("目标APP的包名")) { // 替换为目标APP的包名
return;
}
Class<?> targetClass = lpparam.classLoader.loadClass("com.example.MyClass");
XposedHelpers.findAndHookMethod(
targetClass,
"myMethod",
String.class, // 参数类型1
lpparam.classLoader.loadClass("com.example.ResponseCallback"), // 参数类型2,第三方自定义回调接口
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
android.util.Log.i("MyHook", "Hooked myMethod before: param1 = " + param.args[0]);
// 处理回调接口
Object callback = param.args[1];
if (callback != null) {
// 在回调接口中添加日志输出
XposedHelpers.findAndHookMethod(
callback.getClass(),
"onResponse",
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
android.util.Log.i("MyHook", "onResponse called");
}
}
);
}
}
@Override
protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
android.util.Log.i("MyHook", "Hooked myMethod after: param1 = " + param.args[0]);
}
}
);
}
}
以上就是自己在处理第三方APP的过程,如有偏颇之处欢迎批评指正。












