ctf中的Frida——脚本详解(零基础)
字数 1233 2025-08-22 12:23:18
Frida脚本详解与实战指南
一、Frida基础概念
1.1 什么是Hook
Hook(钩子)是一种在程序运行时拦截和修改函数行为的技术。在Frida中,我们可以通过编写脚本来"钩住"目标函数,实现参数修改、返回值替换、函数调用监控等操作。
1.2 Frida脚本基本框架
Java.perform(function () {
// 在这里编写hook代码
console.log("Frida脚本已附加到进程");
});
Java.perform(fn):确保当前线程连接到Java虚拟机并执行回调函数console.log():在控制台输出信息- JavaScript语法:对缩进无要求,分号可选
二、Frida核心API详解
2.1 获取Java类
Java.use("完整类名")
示例:
var LoginActivity = Java.use("com.example.LoginActivity");
2.2 主动调用函数
var result = LoginActivity.a("123", "123");
console.log(result);
2.3 修改函数实现
LoginActivity.a.implementation = function(x, y) {
// 修改原始逻辑
return "修改后的返回值";
};
2.4 调用非静态函数
Java.choose("com.example.TargetClass", {
onMatch: function(instance) {
instance.nonStaticMethod();
},
onComplete: function() {
console.log("搜索完成");
}
});
2.5 修改成员变量
instance._variableName.value = newValue;
注意:当成员变量名与函数名相同时,需要在变量名前加下划线
2.6 处理动态加载的Dex
Java.enumerateClassLoaders({
onMatch: function(loader) {
try {
if (loader.findClass("动态加载的类名")) {
Java.classFactory.loader = loader;
}
} catch (e) {}
},
onComplete: function() {}
});
三、实战场景与解决方案
3.1 场景一:修改返回值绕过验证
问题:函数返回的字符串与固定值比较,需要修改返回值使其匹配
解决方案:
TargetClass.targetMethod.implementation = function() {
return "需要匹配的固定值";
};
3.2 场景二:调用非静态方法
问题:需要调用没有static修饰的成员方法
解决方案:
Java.choose("com.example.TargetClass", {
onMatch: function(instance) {
instance.setBool_var();
},
onComplete: function() {}
});
3.3 场景三:修改成员变量
问题:需要修改类的成员变量值
解决方案:
Java.choose("com.example.TargetClass", {
onMatch: function(instance) {
instance._bool_var.value = true;
}
});
3.4 场景四:Hook动态加载的类
问题:目标类通过DexClassLoader动态加载,直接Hook会找不到类
解决方案:
- 枚举所有类加载器
- 找到能加载目标类的loader
- 设置当前classFactory的loader
Java.enumerateClassLoaders({
onMatch: function(loader) {
try {
if (loader.findClass("com.example.DynamicCheck")) {
Java.classFactory.loader = loader;
var DynamicCheck = Java.use("com.example.DynamicCheck");
DynamicCheck.check.implementation = function() {
return true;
};
}
} catch (e) {}
}
});
3.5 场景五:从Native层获取数据
问题:关键数据(如加密密钥)存储在native层
解决方案:
// Hook native方法获取key
var NativeClass = Java.use("com.example.NativeHelper");
NativeClass.getKey.implementation = function() {
var original = this.getKey();
console.log("获取到Key: " + original);
return original;
};
// Hook native方法获取IV
NativeClass.getIV.implementation = function() {
var original = this.getIV();
console.log("获取到IV: " + original);
return original;
};
四、CTF实战案例解析
4.1 案例一:SHA256验证绕过
题目逻辑:
- a方法接受两个相同参数,返回第一个参数的SHA256值
- 验证条件:username的SHA256值与password相等
解决方案:
Java.perform(function () {
var LoginActivity = Java.use("com.example.LoginActivity");
var result = LoginActivity.a("123", "123");
console.log("Password should be: " + result);
});
4.2 案例二:字符串比较绕过
题目逻辑:
- 函数返回的字符串与固定值比较
- 需要修改返回值使其匹配
解决方案:
TargetClass.a.implementation = function() {
return "R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=";
};
4.3 案例三:动态DEX检查绕过
题目逻辑:
- check方法在动态加载的DEX中实现
- 需要Hook动态加载的类的方法
解决方案:
Java.perform(function() {
Java.enumerateClassLoaders({
onMatch: function(loader) {
try {
if (loader.findClass("com.example.DynamicCheck")) {
Java.classFactory.loader = loader;
var DynamicCheck = Java.use("com.example.DynamicCheck");
DynamicCheck.check.implementation = function() {
return true;
};
}
} catch (e) {}
}
});
});
五、最佳实践与技巧
- 错误处理:在Hook脚本中加入try-catch块防止崩溃
- 调试输出:使用console.log输出关键信息
- 多线程处理:确保在主线程执行Java.perform
- 性能考虑:避免在频繁调用的函数中实现复杂逻辑
- 脚本模块化:将常用功能封装为函数便于复用
六、总结
Frida提供了强大的动态插桩能力,掌握以下核心技能:
- 理解Java.perform的作用和用法
- 熟练使用Java.use获取目标类
- 掌握implementation修改函数实现
- 了解Java.choose处理非静态方法
- 能够处理动态加载的DEX
- 熟悉从Native层获取数据的方法
通过实际CTF题目练习,可以快速提升Frida脚本编写能力,灵活应对各种逆向分析场景。