安卓逆向签名破解:从Fiddler抓包到JPype调用JAR还原Signature算法
安卓逆向工程实战:从网络请求签名破解到算法还原
在移动应用安全研究和数据爬取领域,逆向工程是一项至关重要的技能。本文将深入探讨如何破解安卓应用中常见的网络请求签名机制,并以一个实际案例(引力播App)为例,展示从抓包定位到算法还原的完整流程。
1. 逆向工程基础与环境准备
逆向工程的核心目标是理解应用程序的内部工作机制,尤其是那些未公开的通信协议和数据加密方式。对于安卓应用来说,这通常涉及以下几个关键步骤:
1.1 工具链配置
进行安卓逆向需要准备以下工具环境:
- 抓包工具:Fiddler/Charles(HTTP/HTTPS)、Wireshark(底层协议)
- 反编译工具:Jadx(Java反编译)、Apktool(资源文件解析)
- 动态调试工具:Frida/Xposed(运行时注入)、IDA Pro(Native层分析)
- 开发环境:Python 3.x + JPype(Java交互)、Android Studio(代码验证)
# Python环境依赖 pip install frida-tools jpype1 requests1.2 应用初步分析
拿到目标APK后,首先需要进行静态分析:
- 使用Apktool解包查看资源文件
- 通过Jadx查看反编译的Java代码
- 检查lib目录下的.so文件(可能存在核心算法)
注意:许多应用会使用加固技术(如腾讯乐加固、360加固等),需要先脱壳才能获取真实代码。
2. 网络协议逆向实战
2.1 抓包定位关键参数
使用Fiddler配置手机代理后,观察应用发出的网络请求。重点关注以下特征:
- 每个请求都变化的参数(如timestamp、nonce)
- 名称包含"sign"、"sig"、"token"等字段
- 参数值呈现规律性变化(长度固定、字符集有限)
在引力播案例中,我们发现每个请求都携带一个signature参数,其值类似:
a3f5d8e2c1b7f409e6d825c47a1b2d3f2.2 静态代码分析
通过Jadx搜索关键词"signature",定位到核心代码段:
public class C2628d implements C0157u { public C0110ac mo26a(C0157u.C0158a aVar) throws IOException { // 获取设备唯一标识 String udid = MyApplication.getInstance().getUDID(); // 获取时间戳 String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // 构造请求参数 C0104aa a2 = a.mo284e() .mo295b("sys", "Android") .mo295b("sysVersion", Build.VERSION.RELEASE) .mo295b("appVersion", MyApplication.getInstance().getAppVersion()) .mo295b("udid", udid) .mo295b("timestamp", timestamp) .mo295b("signature", m12059a(udid, timestamp)) // 关键签名方法 .mo293a(); return aVar.mo104a(a2); } // 签名生成方法 public static String m12059a(String str, String str2) { return C2387a.m11218a( String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}) ); } }2.3 算法还原
分析可知签名生成流程为:
- 拼接字符串:
UDID + "&&" + 时间戳 + "&&" + 固定UUID - 对拼接结果进行某种加密(跟踪
m11218a方法发现是MD5)
3. Python实现签名生成
由于算法涉及Java代码,我们可以通过JPype直接调用扣出来的Java方法:
3.1 准备Java代码
将关键算法类保存为MySig.java:
import java.security.MessageDigest; public class MySig { public static String generateSignature(String input) { if (input == null) return null; StringBuilder sb = new StringBuilder(); try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(input.getBytes()); for (byte b : md.digest()) { sb.append(Integer.toString((b >>> 4) & 0xF, 16)) .append(Integer.toString(b & 0xF, 16)); } } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } }编译并打包为JAR:
javac MySig.java jar cvf MySig.jar MySig.class3.2 Python调用实现
from jpype import * import time import requests # 初始化JVM jvm_path = getDefaultJVMPath() jpype.startJVM(jvm_path, "-ea", "-Djava.class.path=MySig.jar") # 加载Java类 MySig = JClass("MySig") def generate_signature(udid, timestamp): fixed_uuid = "f1190aca-d08e-4041-8666-29931cd89dde" original_str = f"{udid}&&{timestamp}&&{fixed_uuid}" return MySig.generateSignature(original_str) # 示例调用 udid = "IMEI863254012240478-IMSI460072240477434" timestamp = str(int(time.time())) signature = generate_signature(udid, timestamp) print(f"生成的签名: {signature}") # 关闭JVM jpype.shutdownJVM()4. 进阶技巧与问题排查
在实际逆向过程中,可能会遇到以下挑战及解决方案:
4.1 对抗加固技术
| 加固类型 | 特征 | 脱壳方法 |
|---|---|---|
| 腾讯乐加固 | libshellx-*.so | Frida内存Dump |
| 360加固 | libjiagu.so | Xposed Hook ClassLoader |
| 百度加固 | libbaiduprotect.so | 动态调试Dex加载 |
4.2 常见问题处理
混淆处理:
- 使用JADX的"重命名"功能恢复有意义的方法名
- 通过调用关系推断关键方法
Native层加密:
- IDA Pro分析.so文件
- Frida Hook JNI方法
证书校验:
- 使用JustTrustMe模块绕过SSL Pinning
- 修改smali代码移除校验逻辑
提示:对于复杂的加密算法,可以考虑使用Unidbg模拟执行Native代码。
5. 法律与道德考量
在进行任何逆向工程前,必须注意:
- 仅对拥有合法权限的应用进行分析
- 不得用于商业牟利或破坏服务
- 遵守《网络安全法》及相关法律法规
逆向工程的价值在于提升应用安全性,促进技术交流。我曾在一个金融类App的分析中发现,其签名算法存在重放攻击漏洞,通过 responsibly disclosure 帮助厂商修复了该问题。
