CTF 安卓逆向 APK 实战:3 种方法定位并解密 Timer 题目中的 Flag
CTF 安卓逆向 APK 实战:3 种方法定位并解密 Timer 题目中的 Flag
在CTF竞赛中,安卓逆向工程一直是热门挑战方向。本文将以阿里CTF中的Timer题目为例,通过三种不同的技术路线,深入剖析如何高效定位关键代码并获取Flag。不同于简单的解题过程复现,我们将重点探讨逆向工程中的决策逻辑和工具链选择,帮助中高级爱好者建立系统化的分析思维。
1. 环境准备与基础分析
1.1 工具链配置
完整的安卓逆向需要以下工具组合:
- 静态分析工具:JEB Pro 3.0+、JD-GUI 2.0.1
- 动态调试工具:Android Studio 4.2 + smalidea插件
- 辅助工具集:
# APK解包工具 apktool d target.apk # dex转jar工具 d2j-dex2jar.sh classes.dex # 签名工具 keytool + apksigner
1.2 初始文件分析
使用JEB加载APK后,首先检查AndroidManifest.xml:
<activity android:name="net.bluelotus.tomorrow.easyandroid.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity>重点关注加载的native库:
static { System.loadLibrary("lhm"); }2. 静态分析方法对比
2.1 JEB静态反编译
在JEB中定位到MainActivity的核心逻辑:
public void run() { t = System.currentTimeMillis(); now = (int)(t / 1000L); t = 1500L - t % 1000L; if (beg - now <= 0) { tv2.setText("alictf{" + stringFromJNI2(k) + "}"); } if (isPrime(beg - now)) { k += 100; } else { k--; } }关键发现:
beg初始值为当前时间戳+200000秒(约55小时)isPrime()函数控制k值变化- Flag生成依赖native方法
stringFromJNI2
2.2 JD-GUI辅助分析
通过JD-GUI查看反编译代码时,需注意:
- 匿名内部类可能显示为
MainActivity$1 - Smali代码可读性优于混淆后的Java代码
- 使用以下命令增强反编译效果:
java -jar jd-gui.jar --outputDir src/ classes-dex2jar.jar
2.3 关键算法还原
时间校验逻辑的Python实现:
def is_prime(n): if n <= 3: return n > 1 if n%2==0 or n%3==0: return False i = 5 while i*i <= n: if n%i ==0 or n%(i+2)==0: return False i +=6 return True k = 0 for t in range(200000): if is_prime(t): k += 100 else: k -= 1 print(k) # 输出: 16163843. 动态调试方案
3.1 无源码调试配置
- 使用apktool反编译后,在Android Studio中导入smali源码:
apktool d timer.apk -o timer_src - 修改smali代码关键位置:
# MainActivity$1.smali 第139行 const v3, 1616384 # 直接注入计算好的k值 # 第113行修改条件判断 if-ltz v0, :cond_0 # 原为if-gtz
3.2 调试过程要点
- 在
onCreate()方法设置断点 - 使用JEB的调试器附加进程时,需注意:
调试器可能触发反调试检测,建议修改android:debuggable=true
3.3 内存数据提取
当程序运行到Flag生成位置时,通过DDMS或Frida脚本dump内存:
// Frida脚本打印Flag Interceptor.attach(Module.findExportByName("liblhm.so", "Java_net_bluelotus_tomorrow_easyandroid_MainActivity_stringFromJNI2"), { onLeave: function(retval) { console.log("Flag: " + retval.readUtf8String()); } });4. 三种方法对比与实践建议
4.1 技术路线对比表
| 方法类型 | 所需时间 | 技术难度 | 适用场景 | 成功率 |
|---|---|---|---|---|
| 静态分析+模拟 | 2小时 | ★★★☆ | 无动态检测逻辑 | 85% |
| Smali代码修改 | 1.5小时 | ★★★★ | 存在复杂校验逻辑 | 95% |
| 动态调试 | 3小时 | ★★★★☆ | 需要验证内存数据 | 70% |
4.2 常见问题解决
APK反编译失败:
- 尝试使用最新版apktool
- 添加
--no-src参数仅解包资源
Native方法定位:
nm -D liblhm.so | grep stringFrom签名验证绕过:
# 删除META-INF后再签名 zip -d repack.apk META-INF/* apksigner sign --ks debug.keystore repack.apk
5. 进阶技巧与延伸思考
在实际比赛中,Timer类题目往往存在多个变种。建议掌握以下高级技巧:
- Hook系统时间函数:通过Frida拦截
System.currentTimeMillis() - 控制流混淆处理:使用IDAPython分析native代码块
- 多线程同步问题:注意Handler.postDelayed的时序问题
通过本案例可以看出,优秀的逆向工程师需要具备:
- 快速理解业务逻辑的能力
- 灵活选择技术路线的判断力
- 对安卓系统机制的深入理解
下次遇到类似题目时,不妨先花10分钟分析所有可能的突破口,再选择最适合当前场景的解法。记住,逆向工程中没有"唯一正确"的方法,只有最适合当前情境的解决方案。
