OWASP Android Uncrackable1~3练习
字数 2017 2025-08-22 12:22:24
OWASP Android Uncrackable1~3逆向分析与Frida/Xposed实战教学
环境准备
工具清单
- 逆向分析工具:
- apktool 2.4.0 (用于APK反编译和回编)
- jadx (Java代码反编译)
- IDA Pro (SO文件分析)
- keypatch (IDA插件,用于二进制补丁)
运行环境
- 模拟器/设备:
- x86_64 Android 10 Pixel_2_API_29_2 (主要环境)
- 夜神模拟器(Android 5, x86) (部分题目需要)
动态分析工具
- Frida 12.7.20 + frida-server 12.7.12
- Xposed框架
Uncrackable1 分析与绕过
程序行为分析
- 启动时检测root状态,检测到则退出
- 通过检测后,显示输入框和验证按钮
- 验证逻辑在
a.a()方法中实现AES解密
关键代码定位
- Root检测:
MainActivity中的检测代码块 - 验证逻辑:
sg.vantagepoint.a.a类的a方法 - 密钥和密文: 直接存在于Java代码中
绕过方案
Xposed方案
-
绕过root检测:
- Hook
System.exit()防止程序退出 - 或直接修改APK删除检测代码
- Hook
-
获取flag:
XposedHelpers.findAndHookMethod("sg.vantagepoint.a.a",
loadPackageParam.classLoader, "a",
byte[].class, byte[].class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) {
String flag = new String((byte[]) param.getResult());
XposedBridge.log("FLAG IS:" + flag);
}
});
Frida方案
Java.perform(function() {
// 绕过退出检测
var sysexit = Java.use("java.lang.System");
sysexit.exit.overload("int").implementation = function(var_0) {
send("Avoiding exit");
};
// 获取解密结果
var a = Java.use("sg.vantagepoint.a.a");
a.a.overload('[B', '[B').implementation = function(arg1, arg2) {
var ret = this.a.overload("[B", "[B").call(this, arg1, arg2);
var flag = "";
for(var i = 0; i < ret.length; i++) {
flag += String.fromCharCode(ret[i]);
}
send("flag: " + flag);
return ret;
}
});
Uncrackable2 分析与绕过
程序特点
- flag存储在native层(libfoo.so)
- 增加了ptrace反调试机制
- 验证逻辑在native函数
bar()中实现
关键代码分析
- Java层入口:
CodeCheck类的a方法 - Native层验证:
Java_sg_vantagepoint_uncrackable2_CodeCheck_bar - 反调试机制:
init()函数中fork子进程并ptrace附加- 子进程监控父进程状态
IDA分析技巧
- 导入JNIEnv结构定义:
- File → Load file → Parse C header file
- 使用修改后的jni.h
- 修改函数类型:
- 选中函数,按Y键
- 手动输入
JNIEnv*
绕过方案
Frida动态注入
- 绕过ptrace反调试:
setImmediate(function() {
Java.perform(function() {
// 绕过System.exit
const exitClass = Java.use("java.lang.System");
exitClass.exit.implementation = function() {
console.log("[*] System.exit called");
};
// Hook strncmp获取flag
var strncmp = undefined;
var imports = Module.enumerateImportsSync("libfoo.so");
for(var i = 0; i < imports.length; i++) {
if(imports[i].name == "strncmp") {
strncmp = imports[i].address;
break;
}
}
Interceptor.attach(strncmp, {
onEnter: function(args) {
if(args[2].toInt32() == 23 &&
Memory.readUtf8String(args[0], 23) == "01234567890123456789012") {
console.log("[*] Secret string: " +
Memory.readUtf8String(args[1], 23));
}
}
});
});
});
- 注入命令:
frida -U -f owasp.mstg.uncrackable2 -l exploit.js --no-pause
静态Patch方案
- 使用IDA打开libfoo.so
- 定位
init函数,用keypatch将其nop掉 - 回编APK并重新签名
Uncrackable3 分析与绕过
程序特点
- 增加了多层级反调试:
- ptrace反调试
- 内存映射检测(frida/xposed)
- CRC校验(classes.dex和so文件)
- flag验证使用异或加密
- 需要满足计数器条件(dword_705c == 2)
关键代码分析
-
Java层:
MainActivity的verifyLibs()进行CRC校验- 传入初始化字符串"pizzapizzapizzapizzapizz"
-
Native层:
init(): ptrace反调试sub_38A0: 内存映射检测线程bar(): 主验证逻辑sub_12C0: 密钥生成函数
绕过方案
Frida综合方案
Java.perform(function() {
// 绕过退出和CRC校验
var sysexit = Java.use("java.lang.System");
sysexit.exit.implementation = function(var_0) {
send("Avoiding exit");
};
// Hook native函数获取密钥
function do_native_hooks_libfoo() {
var libfoo_base = Module.findBaseAddress("libfoo.so");
if(!libfoo_base) {
send("libfoo not found");
return;
}
// Hook密钥生成函数(偏移0x12C0)
var complex_function = libfoo_base.add(0x12C0);
Interceptor.attach(complex_function, {
onEnter: function(args) {
this.pointer = args[0];
},
onLeave: function(retval) {
// 读取生成的密钥
var buf = Memory.readByteArray(this.pointer, 64);
console.log(hexdump(buf, {
offset: 0, length: 64,
header: true, ansi: true
}));
// 读取pizza字符串(偏移0x7040)
var xorkey_location = libfoo_base.add(0x7040);
var xorkey = Memory.readByteArray(xorkey_location, 64);
console.log(hexdump(xorkey, {
offset: 0, length: 64,
header: true, ansi: true
}));
}
});
}
do_native_hooks_libfoo();
});
静态Patch方案
-
反调试绕过:
- Nop掉
init()中的sub_3910(ptrace) - Nop掉
sub_38A0(内存检测线程)
- Nop掉
-
CRC校验绕过:
- 修改后必须重新打包APK,使CRC值一致
关键知识点总结
反调试技术
-
Java层检测:
- Root检测
- 调试器检测(android:debuggable)
- 调用System.exit终止进程
-
Native层检测:
- ptrace自附加
- /proc/self/maps检测注入工具
- 多进程守护
对抗策略
-
动态注入:
- Hook关键函数(System.exit, 验证函数)
- 早期注入(frida -f)
- 内存操作(Memory.readByteArray等)
-
静态修改:
- 反编译回编(apktool)
- SO文件patch(IDA + keypatch)
- 必须处理签名验证
Frida高级技巧
-
无导出函数hook:
var func_addr = lib_base.add(offset); Interceptor.attach(func_addr, { ... }); -
内存操作:
Memory.readByteArrayhexdump显示内存内容Memory.write修改内存
-
多层级hook:
- Java层和Native层同时hook
- 处理异步线程和回调
扩展学习
推荐工具
- r2frida: radare2与frida结合
- Objection: Frida的CLI工具
- Jadx-gui: 更好的Java反编译
进阶挑战
-
自动化脚本开发
-
对抗更复杂的反调试:
- 定时检测
- 完整性校验
- 混淆和加壳
-
开发Xposed模块通用框架
通过这三个难度递增的crackme练习,可以系统性地掌握Android逆向分析和动态调试的核心技术,建议按照顺序实践并理解每个技术点的应用场景和解决方案。