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 分析与绕过

程序行为分析

  1. 启动时检测root状态,检测到则退出
  2. 通过检测后,显示输入框和验证按钮
  3. 验证逻辑在a.a()方法中实现AES解密

关键代码定位

  • Root检测: MainActivity中的检测代码块
  • 验证逻辑: sg.vantagepoint.a.a类的a方法
  • 密钥和密文: 直接存在于Java代码中

绕过方案

Xposed方案

  1. 绕过root检测:

    • Hook System.exit()防止程序退出
    • 或直接修改APK删除检测代码
  2. 获取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()中实现

关键代码分析

  1. Java层入口: CodeCheck类的a方法
  2. Native层验证: Java_sg_vantagepoint_uncrackable2_CodeCheck_bar
  3. 反调试机制:
    • init()函数中fork子进程并ptrace附加
    • 子进程监控父进程状态

IDA分析技巧

  1. 导入JNIEnv结构定义:
    • File → Load file → Parse C header file
    • 使用修改后的jni.h
  2. 修改函数类型:
    • 选中函数,按Y键
    • 手动输入JNIEnv*

绕过方案

Frida动态注入

  1. 绕过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));
                }
            }
        });
    });
});
  1. 注入命令:
frida -U -f owasp.mstg.uncrackable2 -l exploit.js --no-pause

静态Patch方案

  1. 使用IDA打开libfoo.so
  2. 定位init函数,用keypatch将其nop掉
  3. 回编APK并重新签名

Uncrackable3 分析与绕过

程序特点

  • 增加了多层级反调试:
    • ptrace反调试
    • 内存映射检测(frida/xposed)
    • CRC校验(classes.dex和so文件)
  • flag验证使用异或加密
  • 需要满足计数器条件(dword_705c == 2)

关键代码分析

  1. Java层:

    • MainActivityverifyLibs()进行CRC校验
    • 传入初始化字符串"pizzapizzapizzapizzapizz"
  2. 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方案

  1. 反调试绕过:

    • Nop掉init()中的sub_3910(ptrace)
    • Nop掉sub_38A0(内存检测线程)
  2. CRC校验绕过:

    • 修改后必须重新打包APK,使CRC值一致

关键知识点总结

反调试技术

  1. Java层检测:

    • Root检测
    • 调试器检测(android:debuggable)
    • 调用System.exit终止进程
  2. Native层检测:

    • ptrace自附加
    • /proc/self/maps检测注入工具
    • 多进程守护

对抗策略

  1. 动态注入:

    • Hook关键函数(System.exit, 验证函数)
    • 早期注入(frida -f)
    • 内存操作(Memory.readByteArray等)
  2. 静态修改:

    • 反编译回编(apktool)
    • SO文件patch(IDA + keypatch)
    • 必须处理签名验证

Frida高级技巧

  1. 无导出函数hook:

    var func_addr = lib_base.add(offset);
    Interceptor.attach(func_addr, { ... });
    
  2. 内存操作:

    • Memory.readByteArray
    • hexdump显示内存内容
    • Memory.write修改内存
  3. 多层级hook:

    • Java层和Native层同时hook
    • 处理异步线程和回调

扩展学习

推荐工具

  • r2frida: radare2与frida结合
  • Objection: Frida的CLI工具
  • Jadx-gui: 更好的Java反编译

进阶挑战

  1. 自动化脚本开发

  2. 对抗更复杂的反调试:

    • 定时检测
    • 完整性校验
    • 混淆和加壳
  3. 开发Xposed模块通用框架

通过这三个难度递增的crackme练习,可以系统性地掌握Android逆向分析和动态调试的核心技术,建议按照顺序实践并理解每个技术点的应用场景和解决方案。

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删除检测代码 获取flag : Frida方案 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反调试 : 注入命令 : 静态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综合方案 静态Patch方案 反调试绕过 : Nop掉 init() 中的 sub_3910 (ptrace) Nop掉 sub_38A0 (内存检测线程) 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 : 内存操作 : Memory.readByteArray hexdump 显示内存内容 Memory.write 修改内存 多层级hook : Java层和Native层同时hook 处理异步线程和回调 扩展学习 推荐工具 r2frida : radare2与frida结合 Objection : Frida的CLI工具 Jadx-gui : 更好的Java反编译 进阶挑战 自动化脚本开发 对抗更复杂的反调试: 定时检测 完整性校验 混淆和加壳 开发Xposed模块通用框架 通过这三个难度递增的crackme练习,可以系统性地掌握Android逆向分析和动态调试的核心技术,建议按照顺序实践并理解每个技术点的应用场景和解决方案。