移动端逆向实战:从抓包到算法复现的完整安全分析流程

发布时间:2026/7/4 17:53:03

移动端逆向实战:从抓包到算法复现的完整安全分析流程 1. 项目概述从“秀动”APP看一场完整的移动端逆向实战最近在分析一些音乐票务类APP的客户端安全机制其中“秀动”APP是一个比较典型的案例。它承载了演出查询、票务购买、社区互动等核心功能其客户端与服务器之间的通信安全、本地数据保护以及业务逻辑的实现方式都值得我们深入探究。逆向分析这类APP不仅能帮助我们理解其技术实现更能从安全研究的角度审视其潜在的风险点比如参数加密是否可逆、本地校验是否可绕过、关键业务接口是否存在未授权访问等。这不仅仅是破解几个参数那么简单而是一次对移动应用安全架构的全面“体检”。无论你是移动安全研究员、对客户端加密好奇的开发人员还是想了解如何保护自己APP的开发者通过拆解这样一个真实案例都能获得宝贵的实操经验。接下来我将以“秀动”APP为例带你走一遍完整的逆向分析流程从环境准备、抓包定位到静态分析与动态调试最后实现关键算法的复现。2. 逆向分析环境与工具链搭建工欲善其事必先利其器。一个稳定、高效的逆向分析环境是成功的第一步。与很多教程直接丢出一堆工具列表不同我会结合我多年的踩坑经验告诉你为什么选这些工具以及如何配置才能避免后续90%的奇怪问题。2.1 核心工具选型与避坑指南逆向分析主要分为静态分析和动态分析两大块工具链也围绕这两方面展开。1. 静态分析工具反编译利器Jadx-GUI / JEB / GDAJadx-GUI开源免费对Java代码的反编译效果极佳速度快视图清晰是我首选的快速浏览工具。它的“搜索”功能非常强大支持类名、方法名、字符串、代码片段等多种搜索方式是定位关键代码的“雷达”。JEB商业软件中的佼佼者反编译质量更高对混淆代码的处理能力更强特别是对Native层SO库的分析支持很好。如果项目预算允许JEB是深度分析的不二之选。避坑点不要指望任何一个工具能100%完美反编译特别是经过高强度混淆的APP。经常需要结合多个工具的输出来理解代码逻辑。Jadx有时会将复杂的控制流图CFG简化可能丢失一些细节这时需要用更底层的工具如IDA查看Smali或字节码。2. 动态分析工具抓包代理Burp Suite / Charles / FiddlerBurp Suite安全测试的瑞士军刀功能最全。其Repeater、Intruder、Decoder模块在逆向中非常有用可以方便地重放请求、爆破参数、编解码数据。Charles界面更友好对HTTPS抓包的证书安装流程更直观适合初学者。它的Map Local/Remote功能可以方便地Mock服务器响应用于测试客户端逻辑。避坑点确保手机和电脑在同一局域网并正确安装了抓包工具的CA证书到手机系统信任区。Android 7.0以上需要将证书安装到系统分区否则可能抓不到部分APP的HTTPS包。对于“秀动”这类可能做了证书绑定的APP可能需要使用JustTrustMe等Xposed模块或Frida脚本绕过。动态调试框架Frida为什么是Frida它基于注入技术可以动态地Hook Java方法和Native函数修改内存数据调用任意函数。相比Xposed它不需要重启设备脚本用JavaScript编写开发调试效率极高。版本兼容性是最大陷阱Frida-server的版本必须与PC端的frida-tools和frida Python库版本匹配。不同版本的Android系统、不同架构的CPUarm, arm64, x86都需要对应的frida-server。我强烈建议使用frida --version检查PC端版本然后去GitHub Releases下载完全一致的server端可执行文件。实操心得我习惯在电脑上通过adb push将frida-server推送到手机/data/local/tmp/然后adb shell进去赋权并运行。为了持久化可以写个简单的shell脚本放在/data/local/tmp下每次重启后执行。对于“秀动”这类可能有反调试的APP可能需要使用frida -U -f com.xiudong.app --no-pause在APP启动前就注入或者使用Spawn模式。汇编级调试器IDA Pro当分析深入到Native层特别是那些核心加密算法被编译进.so库文件时IDA Pro就是我们的“显微镜”。它可以反汇编SO库生成可读性相对较好的伪代码F5功能并支持动态调试。避坑点调试Android SO库需要配置Android SDK/NDK路径并使用adb forward进行端口转发。过程稍显繁琐但一旦配置成功就能进行单步跟踪、查看寄存器内存对理解复杂算法逻辑至关重要。3. 辅助工具Android模拟器/真机推荐使用真机已Root或基于Android x86的模拟器如雷电模拟器、夜神模拟器。纯原生模拟器如Android Studio AVD性能较差且部分基于ARM的APP可能无法运行。adb (Android Debug Bridge)基础中的基础用于安装APK、推送文件、获取日志、端口转发等。算法识别工具如findcrypt-yara插件用于IDA可以快速识别SO库中的常见加密算法常量如AES的S盒、MD5的初始化向量。2.2 针对“秀动”APP的特殊环境配置根据对“秀动”APP的初步探测它很可能具备一些常见的安全防护措施我们的环境需要提前做好应对。Root检测与绕过很多APP一检测到Root环境就会闪退。我们可以使用Magisk如果手机已Root并开启“Magisk Hide”或“Zygisk”模式来隐藏Root。更高级的方法是通过Frida Hook检测Root的相关函数如检查/su、/system/bin/su文件是否存在或getprop ro.build.tags是否包含test-keys等。证书绑定SSL Pinning绕过如果配置好代理后仍无法抓到“秀动”的HTTPS请求包大概率是它做了证书绑定。我们可以使用Frida脚本如frida-ssl-unpinning或Xposed模块如JustTrustMe来绕过。在Frida中通常需要Hook证书验证相关的类如TrustManager、SSLSocketFactory等让它们无条件信任我们的代理证书。反调试检测Native层的反调试会检测ptrace、TracerPid等。我们可以使用Frida的Interceptor来Hook这些检测函数使其返回错误值或直接跳过。也可以使用frida的--no-pause和Spawn模式在进程启动初期就注入抢占先机。注意所有绕过行为仅用于安全研究和个人学习务必在合法授权的范围内进行。分析自己拥有合法权限的APP或从公开渠道获取的样本。3. 初步侦察抓包与接口参数分析一切准备就绪后我们开始对“秀动”APP进行第一次“接触”——网络抓包。这个阶段的目标不是立刻破解而是摸清敌情有哪些关键接口参数结构如何哪些是明文的哪些是加密的3.1 配置抓包与捕获登录请求首先确保手机代理设置正确Burp Suite或Charles监听端口无误。打开“秀动”APP进行登录操作。为了便于分析我们可以使用一个测试账号如果APP有公开测试入口或尝试注册一个新账号。成功抓取到登录请求包后我们通常会看到一个POST请求URL可能类似于https://api.xiudong.com/v1/user/login。重点观察请求体Body部分。一个典型的、可能经过初步分析的请求体如下所示POST /v1/user/login HTTP/1.1 Host: api.xiudong.com Content-Type: application/x-www-form-urlencoded ... 其他Headers ... username18888888888passwordvY5PsCkJJqdRfjzSLblTwdeviceed3e6a1744f84c6abde27fda0a7102192b1f70a8signa1b2c3d4e5f67890abcdef1234567890timestamp1644567890123关键参数解析username明文通常是手机号。password密文明显是Base64编码后的字符串末尾可能有填充。这是我们需要破解的第一个重点。device一个40位的十六进制字符串看起来像SHA-1哈希值。这很可能是一个设备唯一标识符。sign另一个哈希字符串通常是请求签名用于防止参数篡改。它的生成规则往往涉及多个参数如device、username、password、timestamp等和某个密钥是安全校验的核心。timestamp时间戳用于防止重放攻击。3.2 参数加密特征快速判断面对这些参数我们需要快速形成分析思路password加密判断Base64这是外壳解码后得到二进制数据。对称加密可能性高登录密码通常使用对称加密如AES后传输。因为服务器需要用相同的密钥解密验证。看到固定长度的密文解码后是16/32字节等AES的可能性很大。如何验证尝试用固定的用户名密码多次登录观察password密文是否变化。如果不变说明加密是确定性的可能使用ECB模式或固定的IV如果变化则可能使用了CBC模式且IV随机生成每次加密结果不同。device生成逻辑判断类似哈希值40位十六进制长度符合SHA-1160位40个十六进制字符。固定值还是可变值在同一设备上多次抓包看device值是否变化。如果不变说明它是基于设备的硬件信息如IMEI、Android ID、序列号、MAC地址等生成的且生成后可能被缓存。如果每次请求都变那逻辑就更复杂。sign签名算法判断这是最复杂的部分。签名算法可能是自定义的也可能是标准哈希如MD5、SHA-256的变种。常见的模式是将所有参数按特定顺序拼接成一个字符串然后加上一个“盐值”secret key最后计算哈希。初步测试方法尝试修改请求中的任何一个参数如timestamp加1然后重放请求观察服务器是否返回“签名错误”。如果可以说明sign确实参与了校验。接着尝试不修改参数但重放完全相同的请求如果也被拒绝说明sign可能还包含了timestamp或nonce来防重放。通过抓包我们明确了主攻方向password的加解密、device的生成规则、sign的签名算法。接下来就要深入APP内部从代码中寻找答案。4. 静态代码分析定位关键加密函数抓包给了我们线索静态分析则是寻找这些线索在代码中的源头。我们将使用Jadx-GUI打开“秀动”APP的APK文件需先使用apktool或直接解压获取classes.dex等文件。4.1 搜索与定位策略在Jadx中我们可以利用强大的搜索功能搜索明文字符串直接搜索抓包看到的参数名如password、device、sign。注意代码中可能使用变量名如pwd、devId、signature等可以多尝试几种可能。搜索URL路径搜索登录接口的路径如/user/login或/v1/user/login。这能快速定位到处理登录请求的代码区域。搜索加密相关类名/方法名搜索AES、DES、RSA、MD5、SHA、Base64、Cipher、MessageDigest、encrypt、decrypt、sign等关键词。以搜索password为例我们可能会找到类似下面的代码片段此为模拟还原代码// 可能位于某个LoginPresenter或UserApiService中 public void login(String username, String plainPassword) { String encryptedPwd DayimaPrivateUtil.privateStrHandle(plainPassword, username); // ... 将encryptedPwd和其他参数组装成请求 ... }这立刻将我们引向一个名为DayimaPrivateUtil.privateStrHandle的工具类方法。双击跟进这个方法。4.2 深入分析privateStrHandle方法// 模拟代码 public class DayimaPrivateUtil { public static String privateStrHandle(String str, String str2) { try { // 1. 对str2可能是用户名取MD5并取前16字节作为Key String keyMd5 md5(str2).substring(0, 16); // 2. 固定的IV String iv yoloho_dayima!%_; // 3. 使用AES/CBC/PKCS5Padding进行加密 Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding); SecretKeySpec keySpec new SecretKeySpec(keyMd5.getBytes(UTF-8), AES); IvParameterSpec ivSpec new IvParameterSpec(iv.getBytes(UTF-8)); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte[] encrypted cipher.doFinal(str.getBytes(UTF-8)); // 4. 结果进行Base64编码 return Base64.encodeToString(encrypted, Base64.NO_WRAP); } catch (Exception e) { e.printStackTrace(); return null; } } }分析结果算法AESCBC模式PKCS5Padding填充。密钥用户名str2的MD5值的前16个字符16字节即128位。初始化向量固定的字符串yoloho_dayima!%_。输出Base64编码的字符串。验证我们可以立刻写一个Python或Java的小程序用上述算法加密测试密码看结果是否与抓包得到的password值一致。如果一致那么password的加密算法就完全掌握了。解密过程同理只需将Cipher.ENCRYPT_MODE改为Cipher.DECRYPT_MODE即可。4.3 追踪device与sign的生成用同样方法搜索device可能会定位到一个getDeviceCode()或generateDeviceId()的方法。分析其实现很可能发现它拼接了多项设备信息如Build.SERIAL,android.provider.Settings.Secure.ANDROID_ID,Build.MODEL等然后进行SHA-1哈希。对于sign搜索可能找到encrypt_data或generateSign方法。关键是要找到它调用的是Java方法还是Native方法。如果发现类似Crypt.encrypt_data(param1, param2, param3)的调用并且Crypt类使用了native关键字声明该方法那么加密逻辑就在Native层的SO库中。public class Crypt { public static native String encrypt_data(String str, String str2, String str3); static { System.loadLibrary(Crypt); // 加载 libCrypt.so } }这标志着分析进入了更复杂的阶段Native逆向。我们需要从APK的lib目录下找到对应的SO文件如lib/armeabi-v7a/libCrypt.so用IDA Pro打开进行分析。5. 动态调试与Hook验证与提取算法静态分析给出了算法假设动态调试则是验证这些假设并获取运行时数据的终极手段。Frida在这里大显身手。5.1 Hook Java层验证password加密我们可以编写Frida脚本直接HookDayimaPrivateUtil.privateStrHandle方法打印其输入和输出与我们抓包的数据对比。Java.perform(function() { var DayimaPrivateUtil Java.use(com.xiudong.util.DayimaPrivateUtil); DayimaPrivateUtil.privateStrHandle.implementation function(str, str2) { console.log([] privateStrHandle called:); console.log( str (password): str); console.log( str2 (username): str2); var result this.privateStrHandle(str, str2); console.log( encrypted result: result); console.log(-----------------------------------); return result; }; });运行脚本后操作APP登录如果控制台打印出的str2和str与输入的用户名密码一致且result与抓包中的password值一致那么就100%确认了我们的静态分析。5.2 Hookdevice生成过程同样HookgetDeviceCode方法打印其返回值并可以进一步Hook其内部拼接字符串的过程看看具体拼接了哪些设备信息。Java.perform(function() { var targetClass Java.use(com.xiudong.device.DeviceInfoManager); targetClass.getDeviceCode.implementation function() { var result this.getDeviceCode(); console.log([] DeviceCode generated: result); // 可以在这里打印调用栈看是谁调用了它 // console.log(Java.use(android.util.Log).getStackTraceString(Java.use(java.lang.Exception).$new())); return result; }; });5.3 攻克Native层Frida RPC调用sign函数对于sign的生成在SO库里的情况静态分析SO库可能很耗时。一个更高效的方法是使用Frida的RPCRemote Procedure Call功能直接主动调用这个Native函数并观察其输出。首先我们需要知道encrypt_data函数的参数是什么。通过静态分析调用它的Java代码我们可以推断出参数。假设调用是Crypt.encrypt_data(param1, param2, param3)。我们可以写一个Frida脚本暴露一个RPC方法给PC端调用Java.perform(function() { var Crypt Java.use(com.xiudong.libcore.util.Crypt); rpc.exports { generatesign: function (arg1, arg2, arg3) { var result null; Java.perform(function () { result Crypt.encrypt_data(arg1, arg2, arg3); }); return result; } }; });在PC端的Python脚本中我们可以连接Frida并调用这个RPC方法import frida import sys def on_message(message, data): print(message) # 连接设备 session frida.get_usb_device().attach(com.xiudong.app) # 替换为秀动的包名 with open(rpc_script.js, r) as f: script_code f.read() script session.create_script(script_code) script.on(message, on_message) script.load() # 获取RPC接口 api script.exports # 猜测参数并调用 # 通常param1可能是某个固定字符串或空param2是待签名的原始字符串param3可能是密钥或盐 # 我们可以从抓包和Java代码中推测param2的组成例如device “user/login” username encryptedPassword test_param2 “ed3e6a1744f84c6abde27fda0a7102192b1f70a8” “user/login” “18888888888” “vY5PsCkJJqdRfjzSLblTw” sign_result api.generatesign(“”, test_param2, “”) print(“Generated sign:”, sign_result) # 与抓包中的sign对比通过反复试验和比对我们就能确定encrypt_data函数各个参数的意义和签名算法的有效输入格式。一旦确定我们就可以在外部如Python中完全复现这个签名算法。如果算法逻辑不复杂甚至可以通过Frida直接Dump出编译好的SO库中的这个函数或者分析其伪代码来理解。6. 算法复现与请求构造在完全理解了password、device、sign的生成规则后我们就可以脱离APP用任何编程语言Python是最常用的来模拟整个登录请求了。6.1 复现password的AES加密import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 def encrypt_password(password, username): # 1. 用用户名生成Key key_md5 hashlib.md5(username.encode()).hexdigest()[:16].encode() # 2. 固定IV iv byoloho_dayima!%_ # 3. AES-CBC加密 cipher AES.new(key_md5, AES.MODE_CBC, iv) encrypted_bytes cipher.encrypt(pad(password.encode(), AES.block_size)) # 4. Base64编码 return base64.b64encode(encrypted_bytes).decode() # 测试 username 18888888888 password 123456 encrypted_pwd encrypt_password(password, username) print(fEncrypted password: {encrypted_pwd}) # 应与抓包数据一致6.2 复现device的生成根据Hook到的信息拼接设备信息并计算SHA-1。import hashlib def generate_device_id(android_id, board, brand, ...): # 传入各种设备信息 device_str f{android_id}{board}{brand}... # 按Hook看到的顺序拼接 return hashlib.sha1(device_str.encode()).hexdigest() # 对于固定设备device_id是固定的可以直接使用抓包到的值。6.3 复现sign的生成如果sign算法在Java层就复现Java代码如果在Native层且通过RPC调通了就可以直接调用RPC或者根据分析出的逻辑例如是HMAC-SHA256自己实现。# 假设分析出sign是 param2 的MD5 import hashlib def generate_sign(param2): return hashlib.md5(param2.encode()).hexdigest() # 组装param2 device “ed3e6a1744f84c6abde27fda0a7102192b1f70a8” api_path “user/login” username “18888888888” encrypted_pwd “vY5PsCkJJqdRfjzSLblTw” param2 device api_path username encrypted_pwd sign generate_sign(param2)6.4 组装完整请求使用requests库发送登录请求。import requests import time api_url “https://api.xiudong.com/v1/user/login” headers { ‘Content-Type’: ‘application/x-www-form-urlencoded’, ‘User-Agent’: ‘...’ # 模仿APP的User-Agent } timestamp int(time.time() * 1000) data { ‘username’: username, ‘password’: encrypted_pwd, ‘device’: device, ‘sign’: sign, ‘timestamp’: timestamp } response requests.post(api_url, datadata, headersheaders) print(response.json())如果返回了成功的状态码和用户token恭喜你整个逆向分析与算法复现流程就成功了。7. 逆向过程中的常见问题与解决思路在实际操作中绝不会一帆风顺。下面是我总结的一些常见坑点及解决方案。7.1 抓不到包或HTTPS报文乱码问题配置好代理后APP无网络或抓到的HTTPS请求是乱码。原因APP可能使用了证书绑定SSL Pinning或非标准端口。解决安装系统级证书对于Android 7.0以上将Burp/Charles的CA证书导出通过adb push到/system/etc/security/cacerts/并修改权限需Root。使用绕过工具在Root环境下安装Xposed模块如JustTrustMe或使用Frida脚本Hook证书验证逻辑。一个简单的Frida脚本可以HookOkHttp或X509TrustManager。检查非标准端口有些APP可能使用自定义端口确保代理工具监听正确。7.2 APP闪退或检测到调试环境问题一打开APP就闪退或者附加Frida后崩溃。原因Root检测、反调试、Frida检测。解决隐藏Root使用Magisk Hide或Kitsune Mask原Magisk Delta隐藏Root权限。对于模拟器有些检测会检查ro.build.fingerprint等属性可能需要修改build.prop。反反调试Frida检测APP可能检测frida-server进程或端口。可以重命名frida-server二进制文件或使用-l参数监听非默认端口27042并在Frida连接时指定端口。ptrace检测Hookptrace函数使其返回-1。Hookfopen、readlink等函数当它们尝试读取/proc/self/status检查TracerPid时返回伪造的内容。先启动后注入使用frida -U -f com.xiudong.app --no-pause让Frida在APP启动的瞬间就注入抢占先机。或者使用Spawn模式。7.3 代码混淆导致分析困难问题Jadx反编译出的代码类名、方法名都是a,b,c难以阅读。解决字符串搜索混淆通常不会混淆字符串常量除非用了字符串加密。搜索关键的URL、参数名、错误提示信息这些是定位代码的“灯塔”。调用关系分析从入口点如MainActivity或确定的方法如搜索到的”password”赋值处开始沿着调用链向上或向下分析逐步理清逻辑。动态Hook辅助在关键位置如网络请求库的入口下Hook打印出混淆后的方法名和参数然后在静态代码中对应查找给这些方法添加有意义的注释。7.4 Native层函数分析受阻问题IDA Pro打开SO库后函数名是混淆的逻辑复杂。解决识别标准库函数IDA通常能识别出libc、liblog等标准库的函数。关注这些函数的调用可以帮助理解程序流。查找加密常量使用findcrypt插件快速定位AES的S盒、MD5的初始化向量等从而识别出加密算法。动态调试在IDA中附加进程进行动态调试可以观察函数执行时的参数和返回值大大降低分析难度。需要配置好Android SDK/NDK和adb forward。Frida Hook Native函数使用Frida的Interceptor来Hook SO库中的导出函数甚至内部函数打印参数、返回值和寄存器状态。这是理解复杂Native逻辑的利器。7.5 算法看似复杂无法理解问题sign算法看起来是一大堆位运算和循环难以看出是什么标准算法。解决黑盒测试用Frida RPC大量调用该函数输入不同的数据观察输出。分析输入输出对看其是否具有哈希函数的特性固定长度、雪崩效应等。比对已知算法将输入输出对与标准算法如HMAC-SHA256、CRC32等的计算结果进行比对。或者将SO库文件上传到一些在线逆向分析平台看是否有自动识别结果。“搬运”代码如果算法逻辑不涉及复杂的系统调用或第三方库一个取巧的办法是直接用Frida将整个函数体在内存中的汇编指令Dump出来或者尝试用C语言“翻译”IDA生成的伪代码然后编译成一个小库供Python调用。虽然不优雅但能解决问题。逆向分析“秀动”这类APP是一个典型的由外而内、动静结合的过程。从网络抓包发现加密点到静态分析定位关键代码再到动态调试验证和提取算法逻辑最后完成外部复现。整个过程考验的不仅是工具使用的熟练度更是分析问题的逻辑思维和耐心。每一个看似神秘的加密参数背后都有一套可以被分析和理解的逻辑。掌握这套方法你就能揭开大多数移动应用客户端安全机制的面纱。记住思路比工具更重要耐心比技巧更关键。在实际操作中多结合静态代码阅读和动态运行验证大胆假设小心求证你总能找到突破口。

相关新闻