APK逆向分析-攻防世界安卓逆向CTF
字数 1267 2025-08-22 12:23:00
Android逆向分析实战教学文档
一、基础逆向分析方法
1.1 工具准备
- 静态分析工具:JADX、JEB、Android Killer
- 动态分析工具:Frida、Xposed
- 辅助工具:apktool、smali2java
1.2 基本分析流程
- APK解包:使用apktool或直接拖入分析工具
- 代码查看:
- 使用JADX/JEB查看Java代码
- 使用Android Killer查看smali代码
- 关键点定位:
- 查找MainActivity
- 查找按钮点击事件
- 查找字符串比较逻辑
二、CTF题目实战分析
2.1 Ph0en1x-100题目分析
关键代码:
getSecret(getFlag()).equals(getSecret(encrypt(sInput)))
解题步骤:
- 使用Frida hook
getSecret方法:
Java.perform(function() {
let MainActivity = Java.use("com.ph0en1x.android_crackme.MainActivity");
MainActivity["getSecret"].implementation = function (string) {
console.log('getSecret is called, string: ' + string);
let ret = this.getSecret(string);
console.log('getSecret ret value is ' + ret);
return ret;
};
});
- 分析发现
getSecret是MD5加密 - 逆向逻辑:
getFlag()和encrypt(sInput)需要相等 encrypt函数是ASCII减一操作,逆向处理:
s = 'ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|'
for i in range(0, len(s)):
print(chr(ord(s[i])+1), end='')
2.2 ill-intentions题目分析
关键点:
- 广播接收器处理:
if (msgText.equalsIgnoreCase("ThisIsTheRealOne")) {
// 启动对应Activity
}
- 正确的Activity包含native方法:
public native String perhapsThis(String str, String str2, String str3);
- 字符串处理逻辑:
def doBoth(input):
return translate(customEncodeValue(input))
def translate(input):
# 数字替换逻辑
table = {1:'W', 2:'h', 3:'a', 4:'t', 5:'i', 6:'s', 7:'d', 8:'o', 9:'n', 0:'e'}
# ...
解决方案:
- 修改smali添加日志输出:
const-string v0, "Message-"
invoke-static {v0, v6}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
- 使用adb触发广播:
adb shell am start -n com.example.hellojni/com.example.application.IsThisTheRealOne
2.3 ERROR404题目分析
关键加密逻辑:
public String a() {
byte[] mid_flag = new byte[0x23];
byte[] fake_flag = flag.getBytes();
for(int i = 0x0; i < fake_flag.length; i = i + 0x1) {
fake_flag[i] = (byte)((fake_flag[i] + 0x14) ^ (fake_flag[i] + 0x12));
mid_flag[i] = (byte)((enc_flag[i] ^ (fake_flag[i] + 0x11)) - 0xb);
}
// ...
}
2.4 CTF-easy题目分析
关键点:
- 直接比较:
if(this.n.getText().toString().equals(this.i())) {
// 正确
}
- 解密脚本:
p = [-40, -62, 107, 66, 0x82, ...]
q = [-57, -90, 53, -71, 0x8B, ...]
v2=[]
v4=[]
for i in range(len(p)):
v2.append(p[i]^q[i])
v3 = v2[0]
# ...后续处理
2.5 献给最好的你题目分析
加密流程:
- 输入密码 → Base64编码
- 大小写转换
- 与
AgfJA2vYz2fTztiWmtL3AxrOzNvUiq==比较
解密方法:
将目标字符串再次大小写转换后Base64解码
2.6 Easy-apk1题目分析
验证逻辑:
if (v0.length == 4 && v14.length() == 19
&& Long.parseLong(xx1(v0[0]),16) + Long.parseLong(xx1(v0[1]),16) - Long.parseLong(xx1(v0[2]),16) == 0x11337452L
&& Long.parseLong(xx1(v0[0]),16) * Long.parseLong(xx1(v0[1]),16) == 0x10E02D1DAE11BF3CL
&& Long.parseLong(xx1(v0[2]),16) - Long.parseLong(xx1(v0[1]),16) == 0x1F3CBF1CL
&& md5(v0[3]).equals("b7e20e3e9d02209c9f3284cb29ed719d"))
xx1函数分析:
public static String xx1(String arg4) {
char[] v0 = "0123456789ABCDEF".toCharArray();
StringBuilder v1 = new StringBuilder("");
byte[] v4 = arg4.getBytes();
for(int v2 = 0; v2 < v4.length; ++v2) {
v1.append(v0[(v4[v2] & 0xF0) >> 4]);
v1.append(v0[v4[v2] & 15]);
}
return v1.toString().trim();
}
实际功能:将字符串转换为16进制表示
2.7 AreYouRich题目分析
密码生成逻辑:
byte[] v1 = new byte[]{0x40, 0x30, 0x30, 49};
String v8 = "abcdefghij";
byte[] v3 = v8.getBytes();
for (v5 = 0; v5 < v3.length; v5++) {
v3[v5] = (byte)(v3[v5] ^ 34);
}
String a1 = new String(v3);
String a2 = new String(v1);
String password = a1 + a2;
解决方案:
- 修改smali代码绕过金钱检查
- 或逆向找到正确的用户名密码组合
三、高级技巧总结
3.1 Frida高级用法
- Hook构造函数:
Java.use("com.example.Class").$init.overload(...).implementation = ...
- 修改返回值:
let ret = this.method(args);
return modified_ret;
3.2 Smali修改技巧
- 条件判断修改:
# 原始
if-eqz v0, :cond_0
# 修改为
if-nez v0, :cond_0
- 常量修改:
const/4 v0, 0x0 # 改为0x1
3.3 动态调试技巧
- 使用adb logcat查看日志
- IDA Pro动态调试so文件
- 注入代码打印关键变量
四、常见加密算法识别
- MD5:32位哈希,常见于
MessageDigest.getInstance("MD5") - SHA:
MessageDigest.getInstance("SHA-1/SHA-256") - Base64:特征字符集
A-Za-z0-9+/,常以=结尾 - 异或加密:常见
^运算符 - AES/DES:
Cipher.getInstance("AES/DES")
五、实战注意事项
- 注意字符串编码问题(UTF-8/GBK)
- 注意大小端问题(尤其在so逆向中)
- 注意加解密顺序(如先Base64再异或)
- 注意代码混淆(名称混淆、控制流混淆)
- 多尝试不同工具交叉验证分析结果
六、附录:常用工具命令
- apktool:
apktool d app.apk -o output_dir
apktool b output_dir -o new.apk
- adb:
adb install app.apk
adb shell am start -n package/activity
adb logcat | grep "keyword"
- Frida:
frida -U -f package -l script.js
frida-ps -U
- jarsigner:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore app.apk alias_name