VNCTF2025 逆向kotlindroid wp
字数 1341 2025-08-29 08:30:24

Kotlin Android逆向分析:VNCTF2025逆向题解

题目概述

这是一个使用Kotlin编写的Android逆向题目,主要考察对Kotlin编译后的代码分析、AES-GCM加密算法的理解以及JNI本地代码的分析能力。

关键保护机制

  1. ProGuard保护:编译时启用了ProGuard进行混淆
  2. SO符号保留:保留了native库的符号信息

分析工具

  • Jadx:反编译有问题
  • JEB:成功反编译出关键逻辑
  • Frida/动态调试:可用于获取运行时数据
  • 静态分析:适用于native库分析

关键加密信息

  1. 加密算法:AES-GCM模式
  2. IV(初始化向量)114514
  3. 密文MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA==
  4. Tag长度:128位(通过getGCMParameterSpec设置)

密钥获取

在Button事件处理中传递了两个数组,通过异或运算得到AES密钥:
atrikeyssyekirta

JNI部分分析

关键函数

  1. sec函数:

    • 调用JNI类获取数组arr_b1作为add
    • 最终生成的密文放在IV后面
  2. native_natget本地方法:

    • 传入参数:new byte[]{0x7B, 0x71, 109, 99, 97, 0x7A, 0x7C, 105}
    • 返回add值(每次调用返回相同)

Native代码分析

  1. 符号保留:由于保留了符号,可以直接搜索native_natget函数
  2. 参数传递
    • 传入3个参数,最后一个是我们提供的数组
    • 初始化传入的数组并创建实例入栈
  3. 本地数据01 1f 09 11 15 1f 0e 0a 17

加密循环逻辑

  1. 两个关键异或操作:

    • Kotlin_ByteArray_get(x0_43, 8 s% x0_45)):取本地数据第9个字节0x17
    • x0_42:循环取前8位
    • x0_37:取初始化的传入数组实例
  2. 解密mysecret

    • 根据上述逻辑可以解出mysecret
    • 后续函数在mysecret后追加add字符串,返回给Java层

解密步骤总结

  1. 获取AES密钥:atrikeyssyekirta
  2. 获取IV:114514
  3. 获取密文:MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA==
  4. 使用AES-GCM模式解密(Tag长度128位)

静态分析技巧

  1. 对于Kotlin编译的SO库,保留符号时可以直接搜索关键函数
  2. 注意Kotlin特有的数据结构和方法调用方式
  3. 关注数组操作和循环逻辑,特别是异或等位运算

动态调试技巧

  1. 使用Frida hook关键JNI方法获取参数和返回值
  2. 动态调试可以获取add值的实际内容
  3. 监控加密过程中的中间值变化

完整解密流程

  1. 分析Java层获取加密参数
  2. 逆向JNI部分获取add
  3. 组合所有参数进行AES-GCM解密
  4. 验证解密结果

通过以上步骤,可以完整还原题目的加密逻辑并成功解密出原始数据。

Kotlin Android逆向分析:VNCTF2025逆向题解 题目概述 这是一个使用Kotlin编写的Android逆向题目,主要考察对Kotlin编译后的代码分析、AES-GCM加密算法的理解以及JNI本地代码的分析能力。 关键保护机制 ProGuard保护 :编译时启用了ProGuard进行混淆 SO符号保留 :保留了native库的符号信息 分析工具 Jadx:反编译有问题 JEB:成功反编译出关键逻辑 Frida/动态调试:可用于获取运行时数据 静态分析:适用于native库分析 关键加密信息 加密算法 :AES-GCM模式 IV(初始化向量) : 114514 密文 : MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA== Tag长度 :128位(通过 getGCMParameterSpec 设置) 密钥获取 在Button事件处理中传递了两个数组,通过异或运算得到AES密钥: atrikeyssyekirta JNI部分分析 关键函数 sec 函数: 调用JNI类获取数组 arr_b1 作为 add 值 最终生成的密文放在IV后面 native_natget 本地方法: 传入参数: new byte[]{0x7B, 0x71, 109, 99, 97, 0x7A, 0x7C, 105} 返回 add 值(每次调用返回相同) Native代码分析 符号保留 :由于保留了符号,可以直接搜索 native_natget 函数 参数传递 : 传入3个参数,最后一个是我们提供的数组 初始化传入的数组并创建实例入栈 本地数据 : 01 1f 09 11 15 1f 0e 0a 17 加密循环逻辑 两个关键异或操作: Kotlin_ByteArray_get(x0_43, 8 s% x0_45)) :取本地数据第9个字节 0x17 x0_42 :循环取前8位 x0_37 :取初始化的传入数组实例 解密 mysecret : 根据上述逻辑可以解出 mysecret 后续函数在 mysecret 后追加 add 字符串,返回给Java层 解密步骤总结 获取AES密钥: atrikeyssyekirta 获取IV: 114514 获取密文: MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA== 使用AES-GCM模式解密(Tag长度128位) 静态分析技巧 对于Kotlin编译的SO库,保留符号时可以直接搜索关键函数 注意Kotlin特有的数据结构和方法调用方式 关注数组操作和循环逻辑,特别是异或等位运算 动态调试技巧 使用Frida hook关键JNI方法获取参数和返回值 动态调试可以获取 add 值的实际内容 监控加密过程中的中间值变化 完整解密流程 分析Java层获取加密参数 逆向JNI部分获取 add 值 组合所有参数进行AES-GCM解密 验证解密结果 通过以上步骤,可以完整还原题目的加密逻辑并成功解密出原始数据。