Xposd模块编写,API详解
Xposd模块编写,API详解
1.什么是Xposed?
Xposed是一款可以在不修改APK的情况下影响程序运行的框架,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。在这个框架下,我们可以编写并加载自己编写的插件APP,实现对目标apk的注入拦截等。
- 核心功能
- 动态Hook:无需修改APK即可拦截和修改目标方法的执行逻辑,例如改变返回值、插入代码等38。
- 模块化扩展:开发者可通过编写模块(Module)实现功能扩展,模块间相互独立且可同时运行46。
- 系统级控制:能够干预系统核心进程(如Zygote),影响所有应用的运行环境
2.Xposed原理
用自己实现的app_process替换掉了系统原本提供的app_process,加载一个额外的jar包,入口从原来的: com.android.internal.osZygoteInit.main()被替换成了: de.robv.android.xposed.XposedBridge.main(),
创建的Zygote进程就变成Hook的Zygote进程了,从而完成对zygote进程及其创建的Dalvik/ART虚拟机的劫持(zytoge注入)
文字解释
(1) 劫持Zygote进程
- Zygote的作用:Android系统中所有应用进程均由Zygote进程孵化而来。Xposed通过替换系统文件
/system/bin/app_process
,在Zygote启动时加载自定义的XposedBridge.jar
,从而接管Zygote的初始化流程。 - 注入逻辑:修改后的
app_process
会优先执行XposedBridge.main()
,而非原生的ZygoteInit.main()
。此时,Xposed框架完成Hook机制的初始化,并加载所有启用的模块。
(2) Hook机制实现
- 方法替换:Xposed将目标Java方法标记为Native方法,并将其实现指向自定义的
xposedCallHandler
函数。当方法被调用时,控制权会转移到Xposed框架,执行模块中定义的beforeHookedMethod
和afterHookedMethod
回调89。 - 回调链管理:若多个模块Hook同一方法,Xposed会根据优先级排序执行回调,顺序为:
A.before → B.before → 原方法 → B.after → A.after
。
(3) 模块加载与执行
- 模块入口:每个Xposed模块需在
AndroidManifest.xml
中声明xposedmodule
元数据,并通过xposed_init
文件指定入口类。框架在Zygote启动时加载这些模块,并在目标应用启动时触发Hook逻辑34。 - API支持:通过
XposedHelpers
和XposedBridge
等工具类,开发者可便捷地定位目标类与方法,例如使用findAndHookMethod
实现方法拦截。
- Zygote的作用:Android系统中所有应用进程均由Zygote进程孵化而来。Xposed通过替换系统文件
3.Xposed的发展及免root框架
名称 | 地址 | 支持版本 | 是否免root |
---|---|---|---|
xposed | https://github.com/rovo89/Xposed | 2.3-8.1 | 否 |
EDXposed | https://github.com/ElderDrivers/EdXposed | 8.0-10 | 否 |
LSPosed | https://github.com/LSPosed/LSPosed | 8.1-13 | 否 |
VirtualXposed | https://github.com/android-hacker/VirtualXposed | 5.0-10.0 | 是 |
太极 | https://www.coolapk.com/apk/me.weishu.exp | 5.0-13 | 是 |
两仪 | https://www.coolapk.com/apk/io.twoyi | 8.1-13 | 是 |
天鉴 | https://github.com/Katana-Official/SPatch-Update | 6-10 | 是 |
4.Xposed可以做什么?
1.修改app布局:上帝模式
2.劫持数据,修改参数值、返回值、主动调用等。例:微信防撤回、步数修改、一键新机
3.自动化操作,例:微信抢红包
学习项目:
2022 最好的Xposed模块: GravityBox, Pixelify, XPrivacyLua
基于Xposed的抖音爬虫,抖音风控后自动一键新机,模拟一个全新的运行环境
A Xposed Module for Android Penetration Test, with NanoHttpd.
自动化创建Xposed模块及钩子,让Xposed模块编写时只需关注钩子实现
5.Xposed环境配置
前置
ubuntu虚拟机镜像,感谢沐阳哥提供的镜像!!!
内置:
Frida
开发环境- 动态分析及开发工具:android-studio
- 动态分析工具:ddms
- 静态分析工具:jadx1.4.4
- 动静态分析工具:jeb
- 动态分析工具:集成HyperPwn
- 静态分析工具:010 editor
- 抓包工具:Charles
- 抓包工具:WireShark
- 动态分析工具:unidbg
vm虚拟机:https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html
(或下载我打包好的)
激活码自行百度哦
第一步,安装虚拟机调整路径,输入激活码
第二步,导入镜像,文件->打开->选择解压好的镜像
第三步,点击运行,待初始化,输入密码:toor
1.Android Studio创建新项目
2.将下载的xposedBridgeApi.jar包拖进libs文件夹
3.右击jar包,选择add as library
4.修改xml文件配置
1
2
3
4
5
6
7
8
9
10
11
12<!-- 是否是xposed模块,xposed根据这个来判断是否是模块 -->
<meta-data
android:name="xposedmodule"
android:value="true" />
<!-- 模块描述,显示在xposed模块列表那里第二行 -->
<meta-data
android:name="xposeddescription"
android:value="这是一个Xposed模块" />
<!-- 最低xposed版本号(lib文件名可知) -->
<meta-data
android:name="xposedminversion"
android:value="89" />5.修改build.gradle,将此处修改为compileOnly 默认的是implementation
1
2**implementation 使用该方式依赖的库将会参与编译和打包
compileOnly 只在编译时有效,不会参与打包**6.新建–>Folder–>Assets Folder,创建xposed_init(不要后缀名):只有一行代码,就是说明入口类
这里用来填写我们具体的hook入口,先建一个hook文档。根据新建的Hook文档来声明入口
com.cc.xoseddemo1.Hook
7.新建Hook类,实现IXposedHookLoadPackage接口,然后在handleLoadPackage函数内编写Hook逻辑
继承了IXposedHookLoadPackag便拥有了hook的能力
1
2
3
4
5
6
7
8
9import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class Hook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
}
}
6.Xpoosed常用API
An efficient Hook API and Xposed Module solution built in Kotlin
示例
- 使用logcat对日志进行捕捉过滤
- 找到我们要Hook的普通方法,发现传入了a,我们对a进行Hook
- 在hook之前需要先做一个判断,如果直接进行打包的话,会Hook手机中的所有应用,我们通过包名进行过滤
- 将第2步复制的xposed片段复制进去,利用快捷键进行导包,完成Hook该函数方法的基本框架,如下图方法前后的两个回调函数。
- 修改传入的参数值,这里我们可以使用日志输出(可以在lsp软件中的日志里查看)的方式验证一下函数(用XP自带的API)
语法解析
一、代码逐层解析
1.
XposedBridge.log()
- 作用:Xposed框架提供的日志输出接口,将内容写入Xposed Installer的日志系统(可通过Xposed应用查看)。
- 语法特性:
XposedBridge
是Xposed框架的核心类,提供模块与框架的交互接口。log()
是静态方法,可直接调用,无需实例化对象。- 参数类型为
String
,需传递字符串内容。
2.
param.args
param
的来源:来自Xposed Hook回调函数的参数,通常是
XC_MethodHook.MethodHookParam
对象。例如在Hook方法时定义的回调:
1
2
3
4
5
6
7
8
9
10
11XposedHelpers.findAndHookMethod("com.example.TargetClass",
loadPackageParam.classLoader,
"targetMethod",
String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
// 这里的param即MethodHookParam实例
XposedBridge.log(param.args[0].toString());
}
});
args
属性:- 是被Hook方法的参数列表(
Object[]
数组)。 - 例如:若被Hook方法签名是
void func(String arg1, int arg2)
,则:param.args[0]
对应arg1
(类型为String)param.args[1]
对应arg2
(类型为Integer,自动装箱)
- 是被Hook方法的参数列表(
3.
param.args[0]
- 表示获取被Hook方法的 第一个参数。
- 索引从0开始:
param.args[0]
→ 第1个参数param.args[1]
→ 第2个参数- 依此类推。
- 注意事项:
- 若方法无参数(如
void func()
),访问args[0]
会导致ArrayIndexOutOfBoundsException
。 - 需确保目标方法至少有一个参数。
- 若方法无参数(如
4.
.toString()
- 将参数对象转换为字符串:
- 若参数是Java对象(如String、自定义类实例),调用其
toString()
方法。 - 若参数是基本类型(如int、boolean),自动装箱为Integer、Boolean后调用
toString()
。
- 若参数是Java对象(如String、自定义类实例),调用其
- 风险点:
若参数为
null
,调用null.toString()
会抛出NullPointerException
。可优化为安全调用:
1
(param.args[0] != null ? param.args[0].toString() : "null")
二、完整代码流程
- Hook目标方法:通过Xposed框架注册Hook点,拦截某个类的方法调用。
- 进入回调函数:当目标方法被调用时,触发
beforeHookedMethod
或afterHookedMethod
回调。 - 访问参数:通过
param.args
数组获取方法参数值。 - 转换字符串:调用
toString()
将参数转为可读字符串。 - 记录日志:通过
XposedBridge.log()
输出到Xposed日志系统。
三、典型应用场景
场景:监控某个应用的登录密码参数
假设Hook一个登录方法
login(String username, String password)
:java
复制
1
2XposedBridge.log("用户名:" + param.args[0].toString());
XposedBridge.log("密码:" + param.args[1].toString());输出结果:
复制
1
2用户名:admin
密码:123456防御措施(逆向对抗):
- 参数混淆:将参数类型从String改为byte数组,增加可读性难度。
- 主动检测Xposed:在关键方法中检测Xposed环境,若存在则拒绝执行。
- 同样我们也可以用安卓自带的log
语法解析
1.
Log.d("zj2595", param.args[0.toString());
- 作用:通过Android原生日志系统输出被Hook方法的第一个参数值。
- 语法:
Log.d(String tag, String msg)
:Android的调试日志接口,tag
为日志标签,msg
为日志内容。param.args[0]
:获取被Hook方法的第一个参数(索引从0开始)。toString()
:将参数对象转为字符串(可能因对象类型不同而结果不同)。
- 风险:
若参数为
null
,toString()
会抛出NullPointerException
。建议优化为安全调用:
java
复制
1
2String arg0 = (param.args[0] != null) ? param.args[0].toString() : "null";
Log.d("zj2595", arg0);
2.
XposedBridge.log(param.args[0].toString());
- 作用:通过Xposed框架的日志系统记录参数值。
- 特点:
- 日志内容可在Xposed Installer的日志页面查看,独立于Android原生日志。
- 适合长期调试,但缺乏日志标签分类。
3.
String a = "pt";
- 作用:定义一个字符串变量
a
,值为"pt"
。 - 潜在用途:
- 硬编码替换目标参数值(如修改密码、密钥等)。
- 可作为中间变量防止直接暴露敏感逻辑。
4.
param.args[0] = a;
- 作用:修改被Hook方法的第一个参数值为
"pt"
。 - 原理:
- Xposed框架的
MethodHookParam
对象允许在beforeHookedMethod
阶段修改参数值。 - 修改后的值会传递给原始方法执行,从而实现运行时参数篡改。
- Xposed框架的
- 限制:
- 只能在
beforeHookedMethod
回调中修改参数(afterHookedMethod
阶段修改无效)。 - 参数类型需匹配目标方法声明类型,否则可能引发类型转换异常。
- 只能在
5.
Log.d("zj2595", param.args[0].toString());
- 作用:验证参数是否被成功修改。
- 预期输出:
- 若修改成功,日志应显示
pt
。 - 若修改失败,日志仍显示原始值(需检查Hook点是否正确)。
- 若修改成功,日志应显示
完整流程
- 拦截方法调用:通过Xposed Hook目标方法,进入
beforeHookedMethod
回调。 - 记录原始参数:通过Android日志和Xposed日志输出原始参数值。
- 篡改参数值:将第一个参数替换为硬编码值
"pt"
。 - 传递修改后的参数:Xposed框架将修改后的参数传递给原始方法。
- 验证篡改结果:通过第二次日志输出确认修改是否生效。
典型应用场景
场景:绕过密码验证
假设目标方法为
checkPassword(String password)
:java
复制
1
2
3public boolean checkPassword(String password) {
return password.equals("secret");
}通过以下代码篡改参数:
java
复制
1
param.args[0] = "secret"; // 强制让密码验证通过
此时无论用户输入什么密码,方法都会返回
true
。防御措施(逆向对抗):
参数校验:在方法内部对参数值进行二次校验。
java
复制
1
2
3
4
5
6public boolean checkPassword(String password) {
if (password == null || password.length() != 6) {
throw new SecurityException("Invalid password format");
}
return password.equals("secret");
}检测Hook环境:通过检查调用栈或类加载器,发现Xposed存在时拒绝执行。
log.d和log.e的区别
在Android开发中,
Log.d()
和Log.e()
是android.util.Log
类提供的不同级别的日志输出方法。它们的核心区别在于日志级别、使用场景和输出表现。以下是详细对比:
1. 日志级别与语义
方法 级别(Verbosity) 语义含义 Log.d(String tag, String msg)
DEBUG 输出调试信息,用于开发阶段追踪程序流程、变量值等非关键信息。 Log.e(String tag, String msg)
ERROR 输出错误信息,表示发生了严重问题(如崩溃、功能失效等),需要优先处理。
2. 使用场景
Log.d()
(调试日志)适用场景:
- 追踪代码执行流程(如方法调用顺序)。
- 输出临时变量的值(如参数、中间计算结果)。
- 调试逻辑分支(如条件判断是否进入某个分支)。
示例:
java
复制
1
2Log.d("MainActivity", "onCreate() called"); // 跟踪生命周期
Log.d("Network", "Response data: " + response); // 输出网络响应数据
Log.e()
(错误日志)适用场景:
- 记录程序异常(如
try-catch
块中的错误)。 - 标记不可恢复的故障(如数据库连接失败、关键API调用错误)。
- 输出对用户体验有重大影响的错误(如支付失败、数据丢失)。
- 记录程序异常(如
示例:
java
复制
1
2
3
4
5try {
parseJson(rawData);
} catch (JSONException e) {
Log.e("Parser", "JSON解析失败: " + e.getMessage()); // 记录致命错误
}
3. 输出表现
特性 Log.d()
Log.e()
Logcat显示颜色 默认黑色(或灰色,取决于IDE主题) 红色(高亮提示,易于快速定位) 过滤优先级 低 高( ERROR
>WARN
>INFO
>DEBUG
>VERBOSE
)默认可见性 通常在调试版本(Debug Build)启用 始终输出(即使发布版本也可能保留)
4. 实际应用建议
何时用
Log.d
?开发阶段临时调试,需在发布版本中移除(通过
BuildConfig.DEBUG
控制):1
2
3if (BuildConfig.DEBUG) {
Log.d("Tag", "调试信息"); // 仅调试版本输出
}输出高频次、非关键信息(如循环内的变量值)。
何时用
Log.e
?- 记录必须修复的问题(如崩溃、数据错误)。
- 需要长期监控的严重事件(即使应用已发布)。
5. 逆向工程中的特殊用途
在Android逆向分析中,日志输出常用于动态调试:
Log.d
:用于追踪目标方法的参数传递或流程分支。1
2
3// Hook方法并输出参数
XposedBridge.log("参数1: " + param.args[0]);
Log.d("Hook", "参数1: " + param.args[0]); // 双保险记录Log.e
:标记关键错误(如加密算法失败、反调试检测触发)。1
2
3if (detectXposed()) { // 检测到Xposed环境
Log.e("Security", "Xposed框架已注入!"); // 高亮提示风险
}
6. 其他日志方法对比
方法 级别 用途 Log.v()
VERBOSE 最详细的日志(极少使用) Log.i()
INFO 普通流程信息(如服务启动) Log.w()
WARN 警告(潜在问题,但未导致错误)
7.我们修改好后运行程序,并在手机上勾选我们的Demo模块,再进行查看会发现参数已修改
- 返回值的Hook,先打印返回值,然后进行修改
语法分析
代码作用
Log.d("zj2595", param.getResult().toString())
:记录被Hook方法的原始返回值到Android调试日志(Logcat)。
param.setResult("222222")
:强制修改被Hook方法的最终返回值为字符串
"222222"
。
使用场景(逆向工程典型用途)
绕过返回值校验:
例如,Hook一个验证函数
boolean checkLicense()
,将其布尔型返回值从false
改为true
,实现破解授权逻辑。篡改敏感数据:
例如,Hook获取设备ID的方法
String getDeviceId()
,返回伪造的ID值。动态调试分析:
通过输出原始返回值,观察目标方法的执行结果,辅助静态反编译分析。
代码执行原理
1. 依赖的上下文
param
的类型:XC_MethodHook.MethodHookParam
(Xposed框架的回调参数对象)。触发阶段:
代码需写在
afterHookedMethod
回调中(若在beforeHookedMethod
阶段调用getResult()
,返回值可能为null
)。
2. 关键方法说明
方法 作用 param.getResult()
获取被Hook方法的原始返回值(需在 afterHookedMethod
阶段调用有效)。param.setResult()
强制修改方法的返回值(可完全覆盖原始逻辑的执行结果)。 3. 执行流程
- 拦截目标方法:通过Xposed Hook目标方法(如
getToken()
)。 - 进入回调阶段:在
afterHookedMethod
中执行代码。 - 记录原始返回值:通过
Log.d
输出原始值到Logcat。 - 篡改返回值:使用
setResult
覆盖为指定值(如"222222"
)。 - 传递结果:修改后的值返回给调用者,原始逻辑被绕过。
完整示例(Xposed模块代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19XposedHelpers.findAndHookMethod("com.example.TargetClass",
loadPackageParam.classLoader,
"getSecretData",
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
// 1. 记录原始返回值
Object originalResult = param.getResult();
String logMsg = (originalResult != null) ? originalResult.toString() : "null";
Log.d("zj2595", "原始返回值: " + logMsg);
XposedBridge.log("原始返回值: " + logMsg);
// 2. 篡改返回值
param.setResult("222222");
// 3. 验证修改结果
Log.d("zj2595", "修改后返回值: " + param.getResult().toString());
}
});
逆向对抗与防御
针对返回值篡改的防护
签名校验:
在关键方法中增加对返回值的二次校验(如哈希校验):
1
2
3
4
5
6
7public String getData() {
String data = fetchFromServer();
if (!verifyHash(data)) { // 校验数据完整性
throw new SecurityException("数据被篡改!");
}
return data;
}混淆返回值类型:
使用复杂对象(非基本类型)作为返回值,增加篡改难度:
1
2
3public ResultBean getResult() {
return new ResultBean(200, "OK", encryptedData);
}检测Hook环境:
在方法内部检查调用栈或类加载器,识别Xposed注入:
1
2
3if (isXposedActive()) {
throw new SecurityException("检测到Hook框架!");
}
1.Hook普通方法
修改返回值
1 |
|
语法详解
XposedHelpers.findAndHookMethod(…)
这是Xposed框架提供的一个辅助方法,用于在指定类中查找某个方法并对其进行hook(拦截)。
- 目标类:第一个参数
"com.zj.wuaipojie.Demo"
表示我们希望hook的类。 - 类加载器:第二个参数
loadPackageParam.classLoader
指定了加载目标类的类加载器,这样可以确保我们能够正确定位到类文件。 - 方法名称:第三个参数
"a"
指明了要hook的方法名。如果该类中有多个同名方法,则后续参数(即参数类型)会帮助确定具体哪一个重载版本。 - 方法参数:第四个参数
String.class
表示该方法接收一个类型为String的参数。 - 回调对象:最后一个参数是一个
new XC_MethodHook()
实例,里面重写了afterHookedMethod
方法,这个方法将在原方法执行完毕后被调用。
- 目标类:第一个参数
XC_MethodHook 的作用
XC_MethodHook 是Xposed框架提供的一个钩子回调类,它允许你在原方法调用之前(onEnter)或者之后(onLeave/afterHookedMethod)插入自己的代码。
- 此处我们只重写了
afterHookedMethod
方法,意味着在原方法执行完成后,我们会执行我们自己的代码逻辑。
- 此处我们只重写了
afterHookedMethod 方法中的操作
super.afterHookedMethod(param);
调用了父类的方法,这通常是为了保持一些内部处理流程。如果不需要特殊逻辑,可以省略这行,但有时保留有助于确保默认行为得以执行。
param.setResult(999);
这行代码非常关键:它修改了原方法的返回值。无论原方法执行后产生了什么返回值,这里都会将其替换为整数999。
换句话说,当应用中任何地方调用
com.zj.wuaipojie.Demo
类中名为a
的方法时(传入一个String参数),即便该方法本身实现了某种逻辑,最终返回的结果都会被hook后改为999。
总结
- 目的:通过Xposed框架拦截目标类中方法的执行,并在方法执行完成后强制修改其返回值。
- 应用场景:常用于逆向工程和动态调试,比如绕过某些安全检查或修改应用行为。
- 关键点:
- 使用
findAndHookMethod
来定位和hook目标方法; - 在
afterHookedMethod
回调中使用param.setResult
修改返回值,从而覆盖原方法的执行结果。
- 使用
修改参数
1 |
|
语法详解
XposedHelpers.findAndHookMethod(…)
这是 Xposed 框架提供的一个便捷方法,用于在指定的类中查找并 hook 某个方法。
"com.zj.wuaipojie.Demo"
:目标类的全限定名,即我们要 hook 的类。loadPackageParam.classLoader
:用于加载目标类的类加载器,确保能找到该类。"a"
:目标方法的名称,表示要 hook 的方法名是"a"
。String.class
:目标方法的参数类型,这里表示方法"a"
接收一个String
类型的参数。new XC_MethodHook() { ... }
:传入一个匿名内部类,用于定义 hook 后的回调行为。
beforeHookedMethod 回调
这个方法会在目标方法执行之前被调用。其主要作用是允许我们在原方法运行之前修改传入的参数或执行其他操作。
1
2
3
4
5
6@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String a = "pt";
param.args[0] = a;
}super.beforeHookedMethod(param);
调用父类的方法,通常是为了保持默认行为。这一行在很多场景中可以省略,但有时保留能确保框架内部逻辑正常执行。
String a = “pt”;
定义了一个字符串变量
a
,并赋值为"pt"
。param.args[0] = a;
修改了
param.args
数组中索引为 0 的参数值(即传入目标方法的第一个参数)。原来传入方法的String
参数将被替换为"pt"
。换句话说,每当目标类
com.zj.wuaipojie.Demo
中名为"a"
的方法被调用时,无论调用方传递的参数是什么,都会在方法执行前被强制改为"pt"
。
应用场景
- 修改行为:这种 hook 技术常用于修改应用逻辑。例如,若目标方法依据传入的字符串参数执行不同操作,改变参数值可以绕过某些检查或改变原有逻辑。
- 调试与逆向工程:在分析应用时,通过修改方法参数,可以观察不同参数对方法执行结果的影响,帮助开发者更好地理解程序内部流程。
总结
这段代码的作用是在目标方法
"a"
被调用之前,将它的第一个参数(String 类型)修改为固定值"pt"
。这种方式允许开发者动态改变应用行为,而无需修改原始代码,是 Xposed 框架在逆向工程和动态调试中的常见用法。
2.Hook复杂&自定义参数
1 |
|
示例
- 查找Hook复杂函数的类名,方法名
2.修改好相应数据后,通过log打印观察
语法解析
1.
loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo")
作用:
- 动态加载
com.zj.wuaipojie.Demo
类。 loadPackageParam.classLoader
是 Xposed 提供的ClassLoader
,用于在 Hook 目标 APP 时正确加载目标类。
解析:
loadPackageParam
是 XposedhandleLoadPackage
方法中的参数,表示当前 Hook 目标应用的上下文信息。loadClass("com.zj.wuaipojie.Demo")
加载Demo
类,返回一个Class<?>
对象。
2.
XposedBridge.hookAllMethods(a, "complexParameterFunc", new XC_MethodHook() {...})
作用:
- Hook
a
类中所有名为complexParameterFunc
的方法,无论其参数类型和返回值是什么。 XC_MethodHook
允许我们在方法 执行前(beforeHookedMethod)或执行后(afterHookedMethod) 进行操作。
解析:
XposedBridge.hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback)
hookClass
:需要 Hook 的类对象,即a
。methodName
:要 Hook 的方法名,此处是"complexParameterFunc"
。callback
:一个XC_MethodHook
实例,包含beforeHookedMethod
和afterHookedMethod
两个可重写的方法。
3.
beforeHookedMethod(MethodHookParam param) throws Throwable
作用:
- 在
complexParameterFunc
执行之前 执行beforeHookedMethod
方法。 - 可以修改参数、阻止方法执行、获取方法的输入参数等。
解析:
MethodHookParam param
- 传递给 Hook 方法的 参数信息。
param.args
:存储方法的所有参数,param.args[0]
是第一个参数。param.thisObject
:如果 Hook 的是实例方法,这个字段表示该方法所属的对象。param.setResult(Object obj)
:直接修改方法返回值,使原方法不执行,返回obj
。
4.
Log.d("zj2595", param.args[0].toString());
作用:
- 通过
Log.d
打印complexParameterFunc
方法的 第一个参数值,用于调试或分析目标应用的行为。
解析:
param.args[0]
:获取complexParameterFunc
的第一个参数。.toString()
:将参数转换为字符串,避免直接打印对象导致logcat
显示null
或Object@hexcode
。
5.
super.beforeHookedMethod(param);
作用:
- 调用
XC_MethodHook
的beforeHookedMethod
,通常用于 保持默认行为,但在 Xposed 中 可以省略,不会影响 Hook 逻辑。
代码的核心作用
- 找到 目标应用
com.zj.wuaipojie.Demo
类。 - Hook 其
complexParameterFunc
方法(可能有多个重载)。 - 在方法执行前,打印第一个参数的值,便于分析应用行为或进一步修改参数。
关键 Xposed API
方法 作用 loadClass("com.xxx")
通过 ClassLoader
加载类hookAllMethods(Class, String, XC_MethodHook)
Hook 目标类中所有指定名称的方法 beforeHookedMethod(MethodHookParam param)
在方法执行前拦截,可修改参数或阻止执行 param.args[n]
获取/修改方法参数 param.setResult(Object obj)
修改方法返回值并跳过原方法执行 逆向对抗与加固建议
若你是应用开发者,可采取以下措施防止此类Hook:
代码混淆:
混淆方法名和参数名,增加定位难度。
1
2
3
4
5public class a { // 原始类名 Demo
public static void b(String c) { // 原始方法名 complexParameterFunc
// 业务逻辑
}
}参数校验逻辑:
在方法内部校验参数合法性。
1
2
3
4
5
6public void complexParameterFunc(String input) {
if (input == null || input.length() < 8) {
throw new IllegalArgumentException("非法参数");
}
// 正常逻辑
}运行时环境检测:
检测Xposed框架存在性。
1
2
3
4
5
6
7
8public static boolean isXposedActive() {
try {
Class.forName("de.robv.android.xposed.XposedBridge");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
- 动态加载
- 查看代码发现string是我们打印出来的参数值,同理可以用上述普通方法的Hook进行参数修改
3.Hook替换函数
1 |
|
示例
1.同理得到替换函数的包名,方法名
2.我们完全替换 目标方法
repleaceFunc
的实现,使其始终返回一个空字符串语法详解
📌 代码解析
1. 加载目标类
1
Class a = loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo");
作用
- 通过 ClassLoader 动态加载
com.zj.wuaipojie.Demo
类。 loadPackageParam.classLoader
是 Xposed 提供的 ClassLoader,确保能够正确加载 Hook 目标应用中的类。
2. Hook 目标方法
1
XposedBridge.hookAllMethods(a, "repleaceFunc", new XC_MethodReplacement() {...});
作用
- Hook
repleaceFunc
方法的所有重载版本。 - 使用
XC_MethodReplacement
完全替换 目标方法,使其不执行原来的逻辑,而是执行replaceHookedMethod
方法。
3. 替换方法逻辑
1
2
3
4@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return "";
}作用
replaceHookedMethod
直接返回""
(空字符串)。- 无论
repleaceFunc
原本的实现是什么,都会被此逻辑取代。
🔍 深入解析:Xposed API
🔹
XposedBridge.hookAllMethods(Class, String, XC_MethodReplacement)
- 作用:Hook 所有 名为
repleaceFunc
的方法(包括不同参数的重载)。 - 区别:
XC_MethodHook
不会改变原始方法的执行逻辑,只能在 执行前/后 进行拦截。XC_MethodReplacement
完全替换原始方法,使其不再执行原有代码,而是执行replaceHookedMethod
。
🔹
replaceHookedMethod(MethodHookParam param)
- 作用:完全取代目标方法的实现逻辑。
- 返回值:直接作为方法的返回值,原方法 不会执行。
示例
假设
repleaceFunc
方法在Demo
类中的原始代码如下:1
2
3public String repleaceFunc() {
return "Hello, World!";
}在 Hook 之后,该方法会始终返回
""
:1
2Demo demo = new Demo();
System.out.println(demo.repleaceFunc()); // 输出:""即使
repleaceFunc
之前可能包含复杂逻辑,例如访问数据库、进行计算、返回动态内容,它们都会被 完全跳过。
📌 总结
代码部分 作用 loadPackageParam.classLoader.loadClass("com.zj.wuaipojie.Demo")
加载目标类 XposedBridge.hookAllMethods(Class, String, XC_MethodReplacement)
Hook 目标方法并完全替换 replaceHookedMethod(MethodHookParam param)
定义新的方法逻辑,不执行原方法 return ""
让方法始终返回 ""
🔥 适用场景
✅ 修改返回值(例如:让某个方法始终返回
true/false
、0 或者空字符串)✅ 跳过某些功能(例如:阻止服务器请求、去除广告、绕过校验)
✅ 提高性能(如果方法执行的是不必要的复杂逻辑,可以直接返回固定值)
⚠️ 注意事项
确保目标方法返回类型匹配
return ""
适用于 返回String
类型 的方法。如果方法返回
int
,需要返回0
:1
return 0;
如果返回
boolean
,需要返回true
或false
:1
return true;
避免 Hook 关键系统方法
- Hook 关键 API(如
System.exit()
、Application.onCreate()
)可能导致 APP 崩溃。
- Hook 关键 API(如
确保
repleaceFunc
方法存在- 如果
Demo
类中没有repleaceFunc
,Hook 不会生效,可能导致日志报错。
- 如果
🚀 实战案例
1️⃣ 绕过应用 VIP 限制
1
2
3
4
5
6XposedBridge.hookAllMethods(a, "isVipUser", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true; // 强制返回 true,让所有用户变成 VIP
}
});2️⃣ 禁止 APP 退出
1
2
3
4
5
6XposedBridge.hookAllMethods(System.class, "exit", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return null; // 直接跳过 System.exit(),防止 APP 退出
}
});逆向对抗与防护
若你是应用开发者,可采取以下防护措施:
方法签名混淆
混淆方法名和参数类型,增加定位难度:
1
2
3
4
5public class a { // 原类名 Demo
public static String b() { // 原方法名 replaceFunc
return "敏感数据";
}
}返回值校验
在调用处增加二次校验逻辑:
1
2
3
4String result = replaceFunc();
if (result.hashCode() != 0x12345678) { // 校验哈希值
throw new SecurityException("返回值被篡改");
}Native层实现
将核心逻辑移至Native层(C/C++),降低被Xposed Hook的概率:
1
public native String replaceFunc(); // JNI 实现
环境检测
检测Xposed框架存在性:
1
2
3
4
5
6
7public static boolean isHooked() {
try {
return XposedBridge.getXposedVersion() != null;
} catch (Throwable ignored) {
return false;
}
}
- 通过 ClassLoader 动态加载
4.Hook加固通杀
1 |
|
示例
- Hook
Application.attach()
方法,并在attach
执行完成后执行自定义的 Hook 逻辑。它通常用于 在目标应用的attach
阶段获取Context
和ClassLoader
,以便 Hook 目标应用的类。必须要将classLoader
传入进去才能通过Application
获取classLoader
。
代码详解
📌 代码解析
1️⃣ Hook
Application.attach()
方法1
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {...});
作用:
findAndHookMethod()
用于 精准 Hook 指定类的特定方法。- 这里 Hook 的是
android.app.Application
类的attach(Context context)
方法。 - 由于
attach()
在应用启动时调用,因此 可以在这里获取Context
和ClassLoader
,以便 Hook 目标应用的类。
为什么要 Hook
attach(Context context)
?- 许多应用会使用 自定义的
ClassLoader
,如果在handleLoadPackage()
里直接 Hook 某些类,可能会找不到(因为类还没加载)。 attach()
方法执行时,应用的Context
和ClassLoader
已经初始化,Hook 逻辑可以安全执行。
2️⃣
afterHookedMethod()
方法1
2
3
4
5
6@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
//hook逻辑在这里面写
}解析:
afterHookedMethod()
代表 在原方法执行完毕后 执行 Hook 逻辑。param.args[0]
获取attach()
方法的Context
参数,这个Context
是 应用的Context
,用于加载资源、访问系统服务等。context.getClassLoader()
获取当前应用的ClassLoader
,用于后续 动态加载和 Hook 目标类。
📌 代码执行流程
1️⃣
Application.attach()
被系统调用。2️⃣ Xposed Hook 该方法,在 方法执行完毕后 获取
Context
和ClassLoader
。3️⃣ 在
afterHookedMethod()
里执行 Hook 逻辑,比如 Hook 目标类的方法。
🔥 适用场景
✅ Hook 目标应用的类
✅ 绕过
loadClass
限制(有些应用使用自定义ClassLoader
,如果不 Hookattach()
可能无法正确加载类)✅ 动态分析应用行为(例如 Hook
WebView
、拦截网络请求等)✅ 逆向破解(比如 Hook 登录验证逻辑、修改返回值等)
🚀 进阶应用示例
1️⃣ Hook 目标应用的方法
假设要 Hook
com.zj.wuaipojie.Demo
类的getData()
方法,我们可以在attach()
里执行 Hook 逻辑:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo", classLoader, "getData", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("Hooked getData() before execution");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("Original return value: " + param.getResult());
param.setResult("Hooked Data!"); // 修改返回值
}
});
}
});📌 作用:Hook
com.zj.wuaipojie.Demo.getData()
,并修改返回值。
2️⃣ Hook WebView 进行 JS 注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16osedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
XposedHelpers.findAndHookMethod("android.webkit.WebView", classLoader, "loadUrl", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String url = (String) param.args[0];
XposedBridge.log("WebView is loading: " + url);
}
});
}
});📌 作用:Hook
WebView.loadUrl()
方法,打印所有加载的 URL。
⚠️ 注意事项
1️⃣ 不能直接在
handleLoadPackage()
里 Hook 目标应用的类- 有些应用使用 自定义 ClassLoader,导致
handleLoadPackage()
时目标类还未加载。 - 解决方案就是 先 Hook
Application.attach()
,等ClassLoader
初始化后再 Hook 目标方法。
2️⃣
beforeHookedMethod()
vsafterHookedMethod()
beforeHookedMethod()
:在方法 执行前 Hook,可修改参数或阻止执行。afterHookedMethod()
:在方法 执行后 Hook,可获取原始返回值并修改。
3️⃣ 适用于所有 Android 应用
Application.attach(Context)
是 所有 Android 应用都会调用的方法,所以这个 Hook 方式适用于 所有 APP,无论它是否混淆或有复杂的ClassLoader
机制。
💡 总结
代码部分 作用 findAndHookMethod(Application.class, "attach", Context.class, XC_MethodHook)
Hook attach(Context)
,在应用初始化时执行 Hookcontext.getClassLoader()
获取 ClassLoader
,用于加载目标类findAndHookMethod("目标类", classLoader, "方法名", XC_MethodHook)
Hook 目标类的方法 param.args[n]
获取或修改方法参数 param.setResult(value)
修改返回值 beforeHookedMethod()
方法执行前 Hook,可修改参数或阻止执行 afterHookedMethod()
方法执行后 Hook,可获取或修改返回值 ✅ 这段代码的核心思想是先 Hook
Application.attach()
,确保ClassLoader
初始化后,再去 Hook 目标应用的其他方法。✅ 适用于所有 Android 应用,尤其是带有自定义
ClassLoader
的应用。逆向对抗与加固
若你是应用开发者,可采取以下防护措施:
检测Application.attach是否被Hook
在
attach()
方法内检查调用栈:1
2
3
4
5
6
7
8
9public void attach(Context context) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement element : stackTrace) {
if (element.getClassName().contains("xposed")) {
throw new SecurityException("Xposed框架注入!");
}
}
super.attach(context);
}混淆关键类名和方法名
增加定位难度:
1
2
3
4
5public class a { // 原类名Application
protected void b(Context c) { // 原方法名attach
// 原始逻辑
}
}使用自定义ClassLoader
防止通过系统ClassLoader获取类:
1
2
3public class SecureClassLoader extends ClassLoader {
// 重写loadClass方法,防止外部Hook
}
- Hook
5.Hook变量
静态变量与实例变量:
- 静态变量(static):类被初始化,同步进行初始化
- 非静态变量:类被实例化(产生一个对象的时候),进行初始化
静态变量
1 |
|
实例变量
1 |
|
示例
- Hook静态变量,先根据原码找到我们需要的包名,方法名等信息
- 根据
代码解析
代码作用
这两行代码通过Xposed框架修改目标类的静态整型字段值,常用于动态篡改应用的配置、状态标志或绕过限制(如试用次数、权限校验等)。
逐行分析
1
2
3
4
5// 1. 加载目标类
final Class clazz = XposedHelpers.findClass("类名", classLoader);
// 2. 修改静态整型字段值
XposedHelpers.setStaticIntField(clazz, "变量名", 999);
关键点解析
1.
XposedHelpers.findClass()
- 功能:通过类加载器查找目标类。
- 参数:
"类名"
:目标类的全限定名(如com.example.Config
)。classLoader
:应用的ClassLoader(通常从loadPackageParam
或Context
获取)。
- 风险:若类不存在,抛出
ClassNotFoundException
。
2.
XposedHelpers.setStaticIntField()
- 功能:强制修改目标类的 静态int字段。
- 参数:
clazz
:目标类的Class对象。"变量名"
:静态字段名称。999
:新值。
- 限制:
- 字段必须存在且类型为
int
(非Integer
)。 - 字段访问权限不限(Xposed可绕过
private
限制)。
- 字段必须存在且类型为
扩展操作
1. 修改非int字段
字符串字段:
1
XposedHelpers.setStaticObjectField(clazz, "API_KEY", "hacked_key");
布尔字段:
1
XposedHelpers.setStaticBooleanField(clazz, "isVIP", true);
2. 动态监听字段变化
结合Hook和反射,在字段被访问时触发逻辑:
1
2
3
4
5
6
7XposedHelpers.findAndHookMethod(clazz, "checkLicense", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
// 每次调用checkLicense前重置试用次数
XposedHelpers.setStaticIntField(clazz, "TRIAL_COUNT", 999);
}
});防御措施(逆向对抗):
字段混淆:
将字段名改为无意义字符串(如
a
):1
private static int a = 3;
值校验逻辑:
在代码关键位置校验字段哈希值:
1
2
3if (TRIAL_COUNT != calculateHash()) {
throw new SecurityException("数据篡改!");
}迁移到Native层:
使用JNI将关键字段存储在C++层:
1
private static native int getTrialCount();
- Hook实例变量
- Hook所有的构造函数,我们发现静态变量与动态变量Hook完成。
代码解析
代码作用
该代码通过Hook目标类的所有构造函数,在对象实例化完成后,修改其
publicInt
字段的值为2222
。常用于篡改对象初始化状态,如绕过校验、强制启用隐藏功能等。代码解析
1.
XposedBridge.hookAllConstructors(clazz, new XC_MethodHook())
XposedBridge.hookAllConstructors
:- 这是 Xposed 框架提供的一个方法,用于钩挂指定类的所有构造函数。
- 参数:
clazz
:要钩挂的类。new XC_MethodHook()
:创建一个XC_MethodHook
的匿名类实例,用于定义钩挂逻辑。
2.
@Override protected void afterHookedMethod(MethodHookParam param) throws Throwable
afterHookedMethod
:- 这是
XC_MethodHook
类中的一个方法,用于在目标方法(这里是构造函数)执行之后执行自定义逻辑。 - 参数:
param
:MethodHookParam
类型,包含钩挂方法的相关信息,如当前对象实例、方法参数、方法返回值等。
- 这是
3.
super.afterHookedMethod(param);
- 这一行调用了父类的
afterHookedMethod
方法,虽然是可选的,但保持代码完整性。 - 父类的实现是空的,所以这行代码可以省略。
4.
Object ob = param.thisObject;
param.thisObject
:- 获取当前构造函数所创建的对象实例。
- 在构造函数中,
thisObject
指向正在被构造的对象。
5.
XposedHelpers.setIntField(ob, "publicInt", 2222);
XposedHelpers.setIntField
:- 这是 Xposed 框架提供的一个工具方法,用于设置对象的字段值。
- 参数:
ob
:目标对象实例。"publicInt"
:字段名。2222
:要设置的值。
- 这行代码的作用是将目标对象的
publicInt
字段值修改为2222
。
逆向对抗与加固
若你是应用开发者,可采取以下防护措施:
字段混淆:
将关键字段名改为无意义字符串(如
a
):1
2
3public class Config {
private int a = 0; // 原字段名 publicInt
}字段值校验:
在关键方法中校验字段哈希值:
1
2
3
4
5public void validate() {
if (publicInt != calculateHash()) {
throw new SecurityException("字段被篡改!");
}
}构造函数逻辑保护:
在构造后二次加密字段值:
1
2
3
4
5
6
7
8
9
10
11
12public class SecureClass {
private int publicInt;
public SecureClass() {
publicInt = 100;
publicInt = encrypt(publicInt); // 构造函数内二次处理
}
private int encrypt(int value) {
return value ^ 0x12345678; // 简单加密
}
}
扩展操作
1. 条件篡改
仅在满足条件时修改字段值:
1
2
3if (XposedHelpers.getIntField(instance, "publicInt") < 1000) {
XposedHelpers.setIntField(instance, "publicInt", 2222);
}2. 多字段操作
同时修改多个字段:
1
2XposedHelpers.setIntField(instance, "publicInt", 2222);
XposedHelpers.setObjectField(instance, "mode", "DEBUG");
6.Hook构造函数
无参构造函数
1 |
|
有参构造函数
1 |
|
示例
1.Hook有参构造函数,查看源代码,发现传入的是str字符串
2.我们Hook传入的str参数进行修改
代码详解
1. XposedHelpers.findAndHookConstructor
1
2XosedHelpers.findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback)
findAndHookConstructor
方法- 功能:查找并Hook指定类的构造函数。
- 参数解析:
"com.zj.wuaipojie.Demo"
:目标类的全限定名。loadPackageParam.classLoader
:使用目标应用的类加载器,确保正确加载类(避免ClassNotFound)。String.class
:构造函数的参数类型(需与实际参数类型严格匹配)。XC_MethodHook
:Hook行为的回调实现。
XC_MethodHook
回调beforeHookedMethod
:- 执行时机:目标方法(构造函数)执行前。
- 操作:通过
param.args
修改传入参数(此处将第一个参数替换为"CC"
)。
afterHookedMethod
:- 执行时机:目标方法执行后。
- 操作:可访问返回值(
param.getResult()
)或异常(param.getThrowable()
)。
className
: 要 Hook 的类名(这里是"com.zj.wuaipojie.Demo"
)。classLoader
: 目标应用的类加载器(这里loadPackageParam.classLoader
提供了APK
的ClassLoader
)。parameterTypesAndCallback
: 先列出构造函数的参数类型(这里是String.class
),然后是 Hook 回调对象(XC_MethodHook
)。
作用:找到
com.zj.wuaipojie.Demo
类中 形参为String
的构造方法,然后 Hook 该构造方法。
2. beforeHookedMethod
1
2
3
4
5
6@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
param.args[0] = "CC"; // 修改构造函数的参数
}param.args[0]
代表构造函数的第一个参数。param.args[0] = "CC";
将传入Demo
类构造函数的String
参数修改为"CC"
。
作用:在
Demo
类构造方法执行前,修改它的参数,使得传入的字符串无论原来是什么,都会变成"CC"
。
3. afterHookedMethod
1
2
3
4
5@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}afterHookedMethod
发生在构造函数 执行后,但本代码没有做额外操作,仅调用super.afterHookedMethod(param)
。
作用:在
Demo
对象创建后可以执行其他操作,如修改this
指向的对象,但本代码未做额外操作。4. Android 类加载机制
loadPackageParam.classLoader
提供的是目标应用的ClassLoader
,常见的类加载器有:PathClassLoader
:用于加载APK
内的dex
文件。DexClassLoader
:可以动态加载外部dex
文件。
为什么需要提供
classLoader
?
因为Xposed
运行在宿主应用环境(如Zygote
),而Demo
这个类属于目标应用,因此 需要使用目标应用的ClassLoader
加载它,否则找不到类。这段代码适合用于分析
Demo
类的实例创建情况,或者绕过Demo
可能存在的参数检查逻辑。
3.对于无参构造函数可以使用替换函数将内容替换。
7.Hook multiDex方法
1 |
|
示例
1.教程Demo中只有一个Dex文件,但在平常Hook中会有多个甚至几十个这种情况下,如果不在第一个Dex文件夹里面,我们需要先找到相关的类,然后将类传进去再进行普通方法的Hook。
2.在Dex文件中最多只能存在65535个方法
代码详解
1. Hook
Application.attach
的意图时机控制:
Application.attach(Context)
是应用初始化时较早调用的方法,通过在此处获取ClassLoader
,确保目标类已加载到内存。解决类隔离问题:
某些应用可能使用多
ClassLoader
(如插件化框架),直接使用系统ClassLoader
可能无法加载目标类。
2. 延迟Hook(Lazy Hook)
问题背景:
若直接Hook目标类的方法,可能因类尚未被加载导致
ClassNotFoundException
。解决方案:
先Hook
Application.attach
,在其执行后通过目标应用的ClassLoader
加载类,确保类已初始化。
3.
ClassLoader
的关键作用上下文隔离:
Android中不同应用(或模块)使用独立的
ClassLoader
,需通过目标Context获取其ClassLoader
才能正确加载类。示例来源:
param.args[0]
是attach
方法的Context
参数,代表当前应用的上下文。
4. 动态方法Hook
灵活性与隐蔽性:
通过运行时加载类并Hook方法,无需提前知道目标类的具体实现,适用于对抗混淆或动态代码加载(如DexClassLoader)。
延迟Hook技术 的典型实现,通过Hook
Application.attach
方法获取目标ClassLoader
,再动态加载并Hook目标类的方法。在安卓逆向中,这种技术常用于:- 处理混淆后的类和方法。
- 应对动态代码加载(如热修复、插件化)。
- 绕过类初始化顺序导致的Hook失败问题。
8.主动调用
静态方法:
1 |
|
实例方法:
1 |
|
示例
- 主动调用方法,同样分为静态方法和动态方法两种。主要看是否有static修饰符。在 Java 中,
static
关键字用于修饰 变量、方法、代码块和内部类,表示它们属于 类 而不是类的实例(对象)。这意味着static
成员可以在不创建对象的情况下被直接访问。 - 这是教程Demo中未被调用的函数,我们尝试主动调用该函数
- 我们发现日志通过主动调用输出
代码详解
1. XposedHelpers.callMethod 方法
- 作用:简化反射调用流程,无需手动获取
Method
对象。 - 参数:
- 对象实例:需先创建目标类的实例。
- 方法名:要调用的方法名称。
- 可变参数:方法的参数值(可选)。
- 底层实现:内部通过反射调用
Method.invoke()
。
2. clazz.newInstance() 的限制
- 无参构造要求:目标类必须存在 public 无参构造函数。
- 潜在问题:
- 若构造函数为私有(常见单例模式),抛出
InstantiationException
。 - 若构造函数需参数,抛出
IllegalArgumentException
。
- 若构造函数为私有(常见单例模式),抛出
3. 方法调用的条件
- 方法存在性:目标类需存在名为
refl
的方法。 - 方法签名匹配:需匹配参数类型和数量(此处未传参,默认调用无参方法)。
- 访问权限:若方法为
private
,需通过setAccessible(true)
突破限制。
代码 作用 findClass("类名", classLoader)
查找目标类 callStaticMethod(clazz, "方法名", 参数...)
调用静态方法 callMethod(clazz.newInstance(), "方法名", 参数...)
创建对象并调用实例方法 这些 API 在 Android 逆向、Xposed Hook、Frida Hook 中非常常用,主要用于:
- 调用未公开的类和方法。
- 绕过安全检测(如
isRooted()
、isSecureMode()
)。 - 伪造 API 返回值(如
getDeviceID()
、getToken()
)。
- 作用:简化反射调用流程,无需手动获取
- 主动调用方法,同样分为静态方法和动态方法两种。主要看是否有static修饰符。在 Java 中,
9.Hook内部类
内部类:类里还有一个类class
1 |
|
示例
- Hook类里面还有一个class
- 用$符拼接内部类名
3.触发内部类的方法
代码解析
1. 内部类的Hook方式
类名格式:
内部类的全限定名格式为
外部类名$内部类名
,例如Demo$InnerClass
。- 匿名内部类命名通常为
外部类名$编号
(如Demo$1
)。
- 匿名内部类命名通常为
访问权限:
若内部类为
private
,Xposed仍可直接Hook,无需额外处理访问权限。
2. 方法签名匹配
参数类型:
需严格匹配目标方法的参数列表。此处目标方法
innerFunc
的签名为void innerFunc(String)
。参数索引:
param.args[0]
对应方法的第一个参数(非静态方法隐含的this
对象不计入参数索引)。
代码 作用 findAndHookMethod("类名", classLoader, "方法名", 参数类型, new XC_MethodHook())
Hook 目标类的方法 param.args[0] = "新参数";
修改方法参数 param.setResult("新返回值");
修改方法返回值 XC_MethodReplacement
直接替换方法实现 findClass("外部类$内部类")
获取内部类的 Class
应用场景
- Hook 加密解密方法,修改参数/返回值。
- Hook 日志打印方法,截获敏感信息。
- Hook 检测方法(如
isRooted()
),绕过安全校验。
1. 匿名内部类的Hook
定位匿名内部类:
通过反编译工具查找类名(如
Demo$1
),或通过代码行为分析确定目标方法。示例代码:
1
2
3
4
5
6
7// Hook匿名内部类Demo$1的某个方法
XposedHelpers.findAndHookMethod("com.zj.wuaipojie.Demo$1", classLoader, "run", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
// 修改逻辑
}
});
2. 静态内部类的处理
类名格式:
静态内部类名为
外部类名$静态内部类名
,Hook方式与非静态内部类一致。访问权限:
静态内部类无需外部类实例即可访问,可直接实例化。
3. 修改非String类型参数
扩展应用:
支持任意类型参数修改(需匹配目标方法签名)。
1
2
3
4
5// 修改int类型参数
param.args[0] = 123;
// 修改对象类型参数
param.args[1] = new CustomObject();
10.反射大法
反射基本了解
反射(Reflection) 是 Java 提供的一种 在运行时动态操作类、方法、字段、构造方法 的机制。
在安卓逆向(如 Xposed Hook、Frida、脱壳、动态调试)中,反射是常用技术,尤其是:
- 绕过隐藏 API 限制
- 调用私有/未公开方法
- 动态修改/访问类的私有成员
- 绕过
ProGuard
混淆
1. 反射的基本原理
Java 反射主要依赖
java.lang.reflect
包,包含:类 作用 Class
获取类的信息(如方法、字段、构造器) Method
代表类的方法,可用于调用 Field
代表类的字段(变量),可用于读写 Constructor
代表类的构造方法,可用于实例化对象 - 反射允许动态访问 & 修改类的成员(方法、变量)
- 可以绕过 Java 访问权限(私有方法 & 变量)
- 常用于绕过检测(如
isRooted()
、getDeviceID()
) - 配合 Xposed / Frida 进行逆向分析
- Hook
Class.forName()
、Method.invoke()
可以拦截所有反射调用
2. 反射的核心API
API 用途 示例 Class.forName(String)
动态加载类 Class<?> clazz = Class.forName("com.example.Target");
clazz.getDeclaredMethod()
获取方法(包括私有) Method method = clazz.getDeclaredMethod("methodName", paramTypes);
method.setAccessible(true)
突破访问权限限制 method.setAccessible(true);
method.invoke(Object,args)
调用方法 method.invoke(instance, arg1, arg2);
clazz.getDeclaredField()
获取字段(包括私有) Field field = clazz.getDeclaredField("fieldName");
field.set(Object, value)
修改字段值 field.set(instance, newValue);
1 |
|
示例
- 查找目标类,Hook内部类方法并在回调中反射调用另一个方法
- 有修饰符,说明它是一个私有的方法,需要先将权限设置为true,然后再通过invoke来实现主动调用
- 实例需要先通过API找到当前类名的字节码,传
clazz.newInstance()
来作为一个实例,通过反射来进行主动调用。
4.如果不通过主动调用,而是set值来进行一个修改
代码解析
1. XposedHelpers的作用
- 简化Hook流程:封装反射API,直接通过类名和方法名进行Hook。
- 自动处理ClassLoader:使用目标应用的
lpparam.classLoader
,避免类加载隔离问题。
2. 内部类的Hook
- 命名规则:内部类的全限定名为
外部类名$内部类名
,如Demo$InnerClass
。 - 逆向意义:内部类常包含关键业务逻辑(如加密、校验),Hook后可干预执行流程。
3. 反射调用私有方法
- 核心步骤:
- 加载目标类:
Class.forName()
或XposedHelpers.findClass
。 - 获取方法对象:
getDeclaredMethod("refl")
(假设无参)。 - 突破访问限制:
setAccessible(true)
。 - 创建实例并调用:
invoke(clazz.newInstance())
。
- 加载目标类:
- 逆向应用:主动调用私有方法可绕过正常逻辑触发敏感操作(如生成密钥、解锁功能)。
4. 动态实例化对象
clazz.newInstance()
的限制:要求目标类有 public无参构造函数,否则抛出
InstantiationException
。若构造函数为私有,需使用
XposedHelpers.newInstance()
替代:1
2// 示例:强制调用私有构造方法
Object instance = XposedHelpers.newInstance(clazz);
2. Xposed Hook 机制
①
findClass()
:查找类1
Class clazz = XposedHelpers.findClass("com.zj.wuaipojie.Demo", lpparam.classLoader);
相关知识点
XposedHelpers.findClass(className, classLoader)
:作用:在目标应用的
ClassLoader
里查找com.zj.wuaipojie.Demo
类。lpparam.classLoader
:由Xposed
提供,确保加载的是目标应用的类。等效的 Java 代码:
1
2Class<?> clazz = Class.forName("com.zj.wuaipojie.Demo", false, lpparam.classLoader);
②
findAndHookMethod()
:Hook 内部类的方法1
2
3
4
5
6
7
8
9XposedHelpers.findAndHookMethod(
"com.zj.wuaipojie.Demo$InnerClass",
lpparam.classLoader,
"innerFunc",
String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);相关知识点
findAndHookMethod(className, classLoader, methodName, paramTypes..., XC_MethodHook)
:- 作用:Hook 目标类的方法,修改
innerFunc(String)
的执行行为。 com.zj.wuaipojie.Demo$InnerClass
:$
表示 内部类,即InnerClass
是Demo
类的 成员内部类。InnerClass
必须是 非静态(非 static),否则它的this
需要Demo
实例。
beforeHookedMethod()
:- 在原方法执行前 运行,可以 修改参数、拦截执行。
- 作用:Hook 目标类的方法,修改
3. Java 反射调用
Demo.refl()
③
Class.forName()
:通过反射查找类1
2Class democlass = Class.forName("com.zj.wuaipojie.Demo", false, lpparam.classLoader);
相关知识点
Class.forName(className, initialize, classLoader)
:作用:动态加载
Demo
类,false
代表 不触发静态初始化。等效的 Xposed 方法:
1
2Class<?> clazz = XposedHelpers.findClass("com.zj.wuaipojie.Demo", lpparam.classLoader);
④
getDeclaredMethod()
:获取私有方法1
2
3Method demomethod = democlass.getDeclaredMethod("refl");
demomethod.setAccessible(true);相关知识点
getDeclaredMethod(methodName, 参数类型...)
- 作用:查找
refl()
方法(包括private
)。 - 区别:
getMethod()
:只能获取public
方法。getDeclaredMethod()
:可获取private
/protected
方法。
- 作用:查找
setAccessible(true)
- 解除 Java 访问权限,允许调用
private
方法。 - 绕过 ProGuard 混淆:
- 即使
refl()
被混淆成a()
,仍可通过反射调用。
- 即使
- 解除 Java 访问权限,允许调用
⑤
invoke()
:反射调用refl()
方法1
2demomethod.invoke(clazz.newInstance());
相关知识点
invoke(对象, 参数...)
作用:在
clazz.newInstance()
对象上调用refl()
方法。clazz.newInstance()
等价于:1
2Object demoInstance = democlass.getConstructor().newInstance();
demomethod.invoke(demoInstance);注意:
newInstance()
只能用于 无参构造方法。如果
Demo
没有无参构造方法,应使用:1
2
3Constructor<?> constructor = democlass.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object demoInstance = constructor.newInstance("参数");
11.遍历所有类下的所有方法
1 |
|
示例
- 将加载该界面时所用的方法加载遍历了出来
代码解析
1. Hook
ClassLoader.loadClass
的意图- 动态监控类加载:在类被加载到内存时触发回调,实时捕获目标类的所有方法。
- 逆向价值:
- 批量Hook混淆后的类和方法(无需提前知道具体名称)。
- 跟踪应用初始化流程,定位关键逻辑(如加密、网络请求)。
2. 方法过滤逻
- 排除抽象方法:抽象方法无实际代码,Hook无意义。
- 排除Native方法:Native方法(JNI实现)无法通过Java层Hook。
- 排除接口方法:接口方法由实现类具体定义,直接Hook接口可能导致重复拦截。
3. 动态插桩(Instrumentation)
- 技术本质:在目标方法执行前/后插入自定义代码(如日志、参数修改)。
- 逆向应用:
- 监控方法调用顺序,分析代码执行流程
- 动态修改方法参数或返回值(需在
beforeHookedMethod
中操作)。
(1) Hook
ClassLoader.loadClass()
1
2
3
4
5
6
7
8XposedHelpers.findAndHookMethod(
ClassLoader.class,
"loadClass",
String.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);🔹 作用
Hook
ClassLoader.loadClass(String)
方法,在类被加载后执行自定义逻辑。等效 Java 代码:
1
2ClassLoader classLoader = getClass().getClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.MyClass");Xposed Hook 方式:
afterHookedMethod()
确保loadClass()
方法执行后,我们可以获取到被加载的Class<?>
对象。
(2) 获取
loadClass()
结果(即新加载的类)1
2Class clazz = (Class) param.getResult();
String clazzName = clazz.getName();🔹 作用
param.getResult()
:获取loadClass(String)
的返回值,即Class<?>
对象。clazz.getName()
:获取类名,例如"com.zj.wuaipojie.Demo"
。
(3) 过滤目标包名
1
if(clazzName.contains("com.zj.wuaipojie")) {
🔹 作用
只 Hook 目标应用的类,避免 Hook 系统类(如
android.*
)。这里使用了
contains()
,可以优化为:1
if(clazzName.startsWith("com.zj.wuaipojie"))
这样能 精确匹配包名开头,减少误判。
(4) 获取
clazz
的所有方法🔹 作用
获取 所有 方法,包括
private
、protected
和public
方法。等效 Java 代码:
1
Method[] methods = clazz.getDeclaredMethods();
(5) 遍历方法,筛选可 Hook 方法
1
2
3
4
5
6
7for (int i = 0; i < mds.length; i++) {
final Method md = mds[i];
int mod = mds[i].getModifiers();
// 去除抽象、native、接口方法
if (!Modifier.isAbstract(mod)
&& !Modifier.isNative(mod)
&& !Modifier.isInterface(mod)) {🔹 作用
- 过滤掉无法直接 Hook 的方法:
Modifier.isAbstract(mod)
:抽象方法不能直接 Hook。Modifier.isNative(mod)
:native
方法需要 Frida 或 JNI Hook。Modifier.isInterface(mod)
:接口方法不能 Hook。
(6) Hook 选定的方法
1
2
3
4
5
6
7XposedBridge.hookMethod(mds[i], new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.d("zj2595", md.toString());
}
});🔹 作用
Hook 目标方法,在方法执行前打印方法签名:
1
Log.d("zj2595", md.toString());
这将在 Logcat 里输出:
1
D/zj2595: public void com.zj.wuaipojie.Demo.testMethod()
12.Xposed妙用
字符串赋值定位:
1 |
|
示例
- 找到方法后,先输出一下,查看是什么字符串进行的settext
- 对字符串进行过滤,输出堆栈,我们发现调用settext的是oncreate方法,如果要修改字符串通过堆栈调用找到该方法,对字符串及进行修改。
代码详解
(1) Hook
TextView.setText()
1
2
3
4
5
6
7
8
9XposedHelpers.findAndHookMethod(
"android.widget.TextView",
lpparam.classLoader,
"setText",
CharSequence.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);🔹 作用
- 拦截
TextView.setText(CharSequence)
方法,用于监控 App 内的 所有 UI 文本更新。 param.args[0]
代表setText()
传入的文本内容。
(2) 记录
setText()
的内容1
Log.d("zj2595", param.args[0].toString());
🔹 作用
打印
TextView
设置的文本,例如:1
2D/zj2595: 试用期已过
D/zj2595: 请更新到最新版
(3) 检测目标文本
"已过期"
1
2
3if (param.args[0].equals("已过期")) {
printStackTrace();
}🔹 作用
- 如果文本是
"已过期"
,调用printStackTrace()
打印调用栈,帮助分析是谁调用了setText()
方法。
(4) 打印调用栈
1
2
3
4
5
6
7
8private static void printStackTrace() {
Throwable ex = new Throwable();
StackTraceElement[] stackElements = ex.getStackTrace();
for (int i = 0; i < stackElements.length; i++) {
StackTraceElement element = stackElements[i];
Log.d("zj2595", "at " + element.getClassName() + "." + element.getMethodName() + "(" + element.getFileName() + ":" + element.getLineNumber() + ")");
}
}🔹 作用
创建
Throwable
对象,获取当前方法的 调用堆栈(Stack Trace)。遍历
StackTraceElement[]
,打印方法调用路径,例如:1
2D/zj2595: at com.example.MainActivity.onCreate(MainActivity.java:42)
D/zj2595: at android.app.Activity.performCreate(Activity.java:789)帮助分析是谁在
setText("已过期")
,进而找到业务逻辑!
关键API说明
param.args[0]
:setText
方法的第一个参数(CharSequence
类型),即要显示的文本内容。Throwable.getStackTrace()
:获取当前线程的调用堆栈,用于追踪方法调用层级。
- 拦截
点击事件监听:
1 |
|
示例
- 我们可以发现点击按钮后的方法被打印出来
代码解析
(1) Hook
View.performClick()
1
2Class clazz = XposedHelpers.findClass("android.view.View", lpparam.classLoader);
XposedBridge.hookAllMethods(clazz, "performClick", new XC_MethodHook() {🔹作用
performClick()
是 所有点击事件的核心方法,最终会触发OnClickListener.onClick(View v)
方法。- 通过 Hook
performClick()
,可以拦截所有View
的点击事件,获取具体的OnClickListener
监听器。
(2) 获取
View
的mListenerInfo
1
Object listenerInfoObject = XposedHelpers.getObjectField(param.thisObject, "mListenerInfo");
🔹 作用
View
并不会直接存储OnClickListener
,而是放在mListenerInfo
这个内部对象中。- 这里 通过 Xposed 反射
getObjectField()
读取mListenerInfo
对象,它是View.ListenerInfo
类的实例,包含mOnClickListener
字段。
(3) 获取
mOnClickListener
1
Object mOnClickListenerObject = XposedHelpers.getObjectField(listenerInfoObject, "mOnClickListener");
🔹 作用
- 从
mListenerInfo
中获取mOnClickListener
字段,即实际处理点击事件的OnClickListener
实例。 - 这个
mOnClickListenerObject
就是View.setOnClickListener()
绑定的OnClickListener
实现类。
(4) 打印
OnClickListener
的实现类1
2String callbackType = mOnClickListenerObject.getClass().getName();
Log.d("zj2595", callbackType);🔹 作用
通过
mOnClickListenerObject.getClass().getName()
获取OnClickListener
的真实类名。用于分析
View
点击事件的具体处理逻辑,例如:1
D/zj2595: com.example.MainActivity$1
这里的
com.example.MainActivity$1
表示OnClickListener
是MainActivity
的匿名内部类。
1. Hook
View.performClick
的意图- 监控点击事件:捕获所有View的点击操作(按钮、列表项等),分析用户交互行为。
- 定位点击处理逻辑:通过监听器类型追踪到具体实现类(如Activity、匿名内部类),辅助逆向工程师快速定位关键代码。
2. 反射获取私有字段
字段访问:
mListenerInfo
和mOnClickListener
均为View的私有字段,通过XposedHelpers.getObjectField
突破访问限制。逆向意义:
直接访问私有字段可绕过常规方法(如
View.getListenerInfo()
),避免触发潜在的反调试检测。
3. 监听器类型分析
- 常见类型:
- 匿名内部类:
MainActivity$1
(自动生成的类名)。 - Lambda表达式:
$$Lambda$MainActivity$Xo4wq...
(ProGuard混淆后名称)。 - Activity自身实现:
com.example.MainActivity
(实现View.OnClickListener
接口)。
- 匿名内部类:
改写布局:
1 |
|
示例
- 在onCreate方法中我们可以找到一些布局的绑定,如button控件等内容都是在onCreate方法中实例。
- 传入控件16进制的值
- 我们会发现原来这部分的图片已经被隐藏起来
代码解析
1. Hook Activity.onCreate的意图
- 篡改UI初始化:在Activity创建时修改界面布局,隐藏或替换关键元素(如验证提示、广告)。
- 绕过界面限制:例如隐藏“未授权”提示,直接展示功能界面。
2. 资源ID的逆向定位
- 资源ID格式:
0x7f0800de
表示APK资源ID,通常对应R.id.xxx
。 - 逆向定位方法:
- 反编译工具:使用 Jadx/Ghidra 查找
R.java
或资源映射表,将ID转换为具体名称(如R.id.btn_submit
)。 - 动态调试:通过Android Studio的Layout Inspector实时查看界面元素ID。
- 反编译工具:使用 Jadx/Ghidra 查找
3. 动态调用findViewById
反射调用:通过
XposedHelpers.callMethod
直接调用Activity的findViewById
方法,等价于:1
View img = ((Activity) param.thisObject).findViewById(0x7f0800de);
逆向意义:无需修改APK代码,动态操控UI元素。
(1) Hook
onCreate()
方法1
2XposedHelpers.findAndHookMethod("com.zj.wuaipojie.ui.ChallengeSixth", lpparam.classLoader,
"onCreate", Bundle.class, new XC_MethodHook() {🔹 作用
- 目标类:
com.zj.wuaipojie.ui.ChallengeSixth
- 目标方法:
onCreate(Bundle savedInstanceState)
- Hook
onCreate()
的原因:onCreate()
是Activity
生命周期的入口,通常在这里进行 UI 初始化。- Hook 该方法可以拦截 UI 初始化过程,并修改
View
的属性。
(2) 获取
View
1
2View img = (View) XposedHelpers.callMethod(param.thisObject,
"findViewById", 0x7f0800de);🔹 作用
通过
findViewById()
获取 ID 为0x7f0800de
的View
对象。param.thisObject
代表ChallengeSixth
实例,相当于:1
View img = ((Activity) param.thisObject).findViewById(0x7f0800de);
0x7f0800de
是一个View ID
,可以通过R.id.xxx
解析:1
int viewId = R.id.targetView; // 例如:R.id.imageView
在 Xposed Hook 代码中通常只能使用
ID 值
(0x7fxxxxxx
),而不能用R.id.xxx
。
(3) 隐藏
View
1
img.setVisibility(View.GONE);
🔹 作用
将目标
View
设置为GONE
(完全隐藏,不占空间)。View
的可见性状态:1
2
3View.VISIBLE // 可见
View.INVISIBLE // 隐藏但仍占空间
View.GONE // 完全隐藏,不占空间等效 Java 代码:
1
2ImageView img = findViewById(R.id.targetView);
img.setVisibility(View.GONE);
Xposed模块patch
PS:最低支持安卓9
将项目编译为apk过程示例
- 点击build选择apk
- 先新建一个签名信息文件,生成一个.jks文件
- 配置后让应用可以发布选择release建立
- 建立好后我们发现目录中可以查看到release文件和生成的apk
- 我们在路径下找到我们新生成的apk
- 在LSPatch中选择我们教程Demo进行修补
- 我们使用便携模式嵌入模块(在没有root的手机中也可以使用)
- 修补后因为暂时缺少模块我们手动安装一下该模块
- 我们发现图标消失,说明它在运行时已经加载了相关模块
Xposed快速Hook
使用方法示例
- 在设置中将smail转化为配置打开,然后回到首页进行添加配置。在MT管理器中找到方法签名进行复制。
- smail自动填充之后,我们进行参返实践。
- 保存同意root权限后,将配置打开,我们打开apk后可以在记录处得到参返。
- 同理我们还可以进行Hook参数值并进行修改等操作。
Hook变量示例
- Hook实例变量,同上先复制签名进行相关配置
- 可以发现没有修改前是300