使用Autodecoder插件逆向破解移动应用加密与重放防护

📅 2026/6/30 5:47:04
使用Autodecoder插件逆向破解移动应用加密与重放防护
1. 项目概述一次逆向工程中的实战对抗最近在分析一个移动应用时遇到了一个典型的“硬骨头”它的网络请求数据包被加密并且服务端还部署了重放攻击防护机制。这意味着你既无法直接窥探到明文的请求和响应也无法简单地抓取一个有效的数据包然后无限次重放来测试接口。对于安全测试、协议分析或者自动化脚本开发来说这无疑是两道高墙。我的目标很明确就是要穿透这两层防护实现对应用通信协议的清晰理解和可控调用。经过一番探索和工具链的整合我最终采用了一套以Autodecoder插件为核心的解决方案成功绕过了加密和重放防护。整个过程更像是一次精细的“外科手术”而非暴力破解。Autodecoder 在这里扮演了“翻译官”和“流水线工人”的角色它能在抓包工具如 Burp Suite中自动识别、解密、修改、再加密数据包并将这个过程无缝集成到工作流中。而对抗重放防护则需要更深入地理解其机制常见如时间戳、随机数、签名并编写相应的处理逻辑。这篇文章我将详细拆解这次实战的全过程。从最初的困境分析到工具选型与配置再到核心的加解密算法逆向与重放防护逻辑破解最后分享整个自动化流程的搭建心得和踩过的坑。无论你是移动安全研究员、爬虫工程师还是对协议逆向感兴趣的开发者相信这套思路和具体操作方法都能给你带来直接的参考价值。2. 核心思路与工具链选型面对加密和重放防护盲目动手只会事倍功半。首先必须建立清晰的解决思路并选择合适的“兵器”。2.1 问题拆解我们需要解决什么数据包加密/解密应用发出的请求体Body和服务器返回的响应体很可能不是 JSON 或 XML 等明文而是一串无法直接阅读的二进制数据或密文。我们需要找到加解密算法和密钥。重放防护绕过即使你解密了一个数据包原样不动地再次发送给服务器也会被拒绝提示“请求已过期”或“签名错误”。这说明请求中包含了随时间或每次请求变化的一次性凭证。流程自动化理想状态是我们的抓包工具或脚本能够像处理明文一样处理这些数据自动解密供我们查看和修改发送前自动按规则添加防重放参数并重新加密。2.2 工具链构建为什么是 Autodecoder 工欲善其事必先利其器。我的核心工具链如下抓包平台Burp Suite Professional。这是 Web/移动端安全测试的瑞士军刀。其强大的代理、重放Repeater、扫描Scanner和插件扩展能力是我们工作的基础舞台。社区版Free功能受限专业版是必备。核心插件Burp Suite Autodecoder 插件。这是本次攻坚的“主角”。它不是一个特定的解密工具而是一个框架。它允许你编写自定义的 Python 或 Java 代码Decoder来定义如何解码解密/解压/编码转换和编码加密/压缩经过 Burp 的 HTTP 消息。你可以为不同的 Host、Path 甚至 Content-Type 配置不同的 Decoder实现精准处理。辅助分析工具Jadx / JEB / IDA用于静态分析 Android/iOS 应用寻找加解密算法和密钥的硬编码位置或追踪相关函数调用。Jadx 免费且反编译 Java 代码效果很好是首选。Frida / Objection用于动态运行时RuntimeHook。当静态分析遇到混淆或算法复杂时通过注入脚本直接 Hook 加解密函数打印出输入、输出和密钥这是获取算法细节的“终极手段”。Python 脚本用于算法验证、参数生成如签名计算和编写 Autodecoder 的 Decoder 模块。为什么选择 Autodecoder 而不是其他市面上也有一些其他 Burp 插件能处理加解密但 Autodecoder 的设计哲学更灵活。它不绑定任何特定算法完全由你的代码决定行为。这意味着无论应用使用 AES、RSA、SM4、自定义 XOR 甚至多种算法的组合只要你能用代码实现Autodecoder 就能集成。它完美地将逆向工程的分析成果算法与安全测试的日常工作流Burp结合了起来。3. 逆向工程定位加解密与重放逻辑工具准备好后下一步就是“侦察”找出应用的防御机制到底是如何工作的。3.1 静态分析寻找线索首先将目标 APK 文件拖入 Jadx。搜索关键词是第一步加密相关关键词搜索encrypt,decrypt,AES,DES,RSA,Cipher,SecretKey,Key,Crypto等。关注工具类*Utils、管理类*Manager或网络层类*HttpClient,*Interceptor。网络库与拦截器现代应用多使用 OkHttp3 或 Retrofit。搜索OkHttpClient、Interceptor、addInterceptor。自定义的拦截器往往是统一处理请求加密和响应解密的地方是“黄金宝藏”。重放防护关键词搜索nonce随机数,timestamp时间戳,sign/signature签名,replay。查看它们是如何被生成并添加到请求头Header或请求体Body中的。在我的案例中通过搜索encrypt很快定位到一个名为SecurityUtils的类里面包含了encryptData和decryptData两个静态方法。进一步查看发现它使用了AES/CBC/PKCS5Padding模式密钥Key和初始向量IV是通过一个固定的字符串经过 MD5 哈希后取前16位生成的。这是一个典型的“静态密钥”场景安全性较弱但很常见。同时在名为ApiInterceptor的 OkHttp 拦截器中我看到了在encryptData调用前代码会向请求的 JSON 数据中添加timestamp和nonce字段然后将整个 JSON 字符串进行加密。此外还有一个sign字段它的计算方式是MD5( sorted(所有参数) secret)。这个secret是另一个硬编码在代码中的字符串。这就是重放防护的核心timestamp确保请求时效性服务器会校验nonce确保唯一性服务器会缓存一段时间内的 nonce 防重放sign确保参数完整性防篡改。注意并非所有应用都如此“友好”。遇到高强度混淆、密钥来自服务器或白盒加密方案时静态分析会异常困难必须借助动态分析。3.2 动态验证与关键信息提取静态分析给了我们蓝图但需要动态运行来确认和获取细节。这里 Frida 大显身手。我编写了一个 Frida 脚本用于 Hook 上述发现的SecurityUtils.encryptData和decryptData方法。// frida_script.js Java.perform(function() { var SecurityUtils Java.use(com.example.app.util.SecurityUtils); // Hook 加密方法 SecurityUtils.encryptData.implementation function(input) { console.log([] encryptData called.); console.log( Plaintext (input): input); var result this.encryptData(input); // 调用原方法 console.log( Ciphertext (output): result); console.log( Hex Output: bytesToHex(result.getBytes())); return result; }; // Hook 解密方法 SecurityUtils.decryptData.implementation function(input) { console.log([] decryptData called.); console.log( Ciphertext (input hex): bytesToHex(input)); var result this.decryptData(input); console.log( Plaintext (output): result); return result; }; // 辅助函数字节数组转Hex function bytesToHex(bytes) { var hex []; for (var i 0; i bytes.length; i) { hex.push((bytes[i] 4).toString(16)); hex.push((bytes[i] 0xF).toString(16)); } return hex.join(); } });通过frida -U -f com.example.app -l frida_script.js命令注入脚本然后在应用中操作触发网络请求。控制台清晰地打印出了加密前的明文 JSON包含 timestamp, nonce, sign 和其他业务参数和加密后的密文 Hex 字符串。对比静态分析的算法AES/CBC/PKCS5Padding用获取到的密钥和 IV 在 Python 中验证确认加解密过程完全正确。至此我们掌握了加解密算法AES-128-CBCPKCS5Padding。密钥与IV由固定字符串经 MD5 生成。重放防护三要素timestampUnix 毫秒时间戳nonce16位随机字符串signMD5(按参数名排序后的键值对字符串 secret)。4. 编写 Autodecoder 插件实现自动化有了完整的算法和逻辑就可以开始打造我们的自动化武器了。Autodecoder 插件需要编写两个核心脚本一个Decoder用于解密/解码和一个Encoder用于加密/编码。通常它们在一个文件里。4.1 创建自定义 Decoder 脚本在 Burp 的 Autodecoder 插件界面点击 “Add”选择 Python。下面是一个针对本次案例的完整示例# custom_app_decoder.py import hashlib import json import time import random import string from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 # 注意需要安装 pycryptodome 库: pip install pycryptodome # 配置区域 # 从逆向工程中获取的固定种子 KEY_SEED ThisIsASecretSeed123! IV_SEED ThisIsAnIVSeed456! # 生成固定的 KEY 和 IV (AES-128) KEY hashlib.md5(KEY_SEED.encode()).hexdigest()[:16].encode() # 16 bytes IV hashlib.md5(IV_SEED.encode()).hexdigest()[:16].encode() # 16 bytes # 签名用的 secret SIGN_SECRET MySignatureSecret789 # 工具函数 def aes_encrypt(plaintext): AES-128-CBC 加密 cipher AES.new(KEY, AES.MODE_CBC, IV) padded_data pad(plaintext.encode(utf-8), AES.block_size) ciphertext cipher.encrypt(padded_data) # 应用返回的是Base64这里也保持一致 return base64.b64encode(ciphertext).decode(utf-8) def aes_decrypt(ciphertext_b64): AES-128-CBC 解密 cipher AES.new(KEY, AES.MODE_CBC, IV) ciphertext base64.b64decode(ciphertext_b64) decrypted_data cipher.decrypt(ciphertext) # 去除PKCS5/PKCS7填充 plaintext_bytes unpad(decrypted_data, AES.block_size) return plaintext_bytes.decode(utf-8) def generate_nonce(length16): 生成指定长度的随机字符串作为 nonce return .join(random.choices(string.ascii_letters string.digits, klength)) def generate_sign(params_dict): 生成请求签名 # 1. 参数排序并拼接成 key1value1key2value2 格式 sorted_params sorted(params_dict.items(), keylambda x: x[0]) param_str .join([f{k}{v} for k, v in sorted_params]) # 2. 拼接 secret sign_str param_str SIGN_SECRET # 3. 计算 MD5 return hashlib.md5(sign_str.encode(utf-8)).hexdigest() # Autodecoder 接口函数 def decode(message): 解码函数将Burp中捕获的加密响应或请求解密为明文。 Autodecoder会自动调用此函数处理配置匹配的消息。 # 提取HTTP消息体 body message.getRequest() if message.isRequest() else message.getResponse() body_str body.tostring() if hasattr(body, tostring) else str(body) # 判断是否是我们的目标请求/响应这里简单通过URL或Content-Type判断实际应更精确 # 例如只处理特定API路径的请求和响应 host message.getHost() path message.getPath() if /api/v1/ in path: # 假设目标API路径包含 /api/v1/ try: # 请求和响应体可能是Base64编码的密文 # 先尝试Base64解码再AES解密 ciphertext_b64 body_str.strip() plaintext aes_decrypt(ciphertext_b64) # 将解密后的明文JSON格式美化方便查看 parsed_json json.loads(plaintext) pretty_json json.dumps(parsed_json, indent2, ensure_asciiFalse) # 返回处理后的文本和新的Content-Length new_body pretty_json.encode(utf-8) message.setBody(new_body) # 更新Content-Length头部非常重要 message.setHeader(Content-Length, str(len(new_body))) # 可以修改Content-Type为application/json以便Burp更好解析 message.setHeader(Content-Type, application/json; charsetutf-8) except Exception as e: # 如果解密失败可能是其他请求原样返回 print(f[Autodecoder Decode] Error processing {path}: {e}) pass return message def encode(message): 编码函数将Burp中修改后的明文请求重新加密并添加防重放参数。 在消息发送前Autodecoder会调用此函数。 if not message.isRequest(): return message # 只处理请求 host message.getHost() path message.getPath() if /api/v1/ in path: try: # 1. 获取当前明文的请求体JSON字符串 body_str message.getBody().tostring() if hasattr(message.getBody(), tostring) else str(message.getBody()) original_params json.loads(body_str) # 2. 生成或更新重放防护参数 # 保留原有的 timestamp/nonce/sign如果有或者生成新的 current_time_ms int(time.time() * 1000) new_nonce generate_nonce() # 更新参数 original_params[timestamp] current_time_ms original_params[nonce] new_nonce # 注意需要先删除旧的sign因为参数变了 if sign in original_params: del original_params[sign] # 3. 生成新的签名 new_sign generate_sign(original_params) original_params[sign] new_sign # 4. 将参数字典转换为JSON字符串并加密 new_json_str json.dumps(original_params, separators(,, :), ensure_asciiFalse) encrypted_body_b64 aes_encrypt(new_json_str) # 5. 设置新的请求体 new_body encrypted_body_b64.encode(utf-8) message.setBody(new_body) # 6. 更新必要的头部 message.setHeader(Content-Length, str(len(new_body))) # 保持原有的Content-Type可能是 application/octet-stream 或 text/plain # message.setHeader(Content-Type, text/plain; charsetutf-8) except Exception as e: print(f[Autodecoder Encode] Error processing request to {path}: {e}) # 出错时可以选择不修改消息避免破坏请求 pass return message4.2 配置 Autodecoder 规则脚本写好后需要在 Autodecoder 插件界面进行精细配置创建新规则点击 “New Profile”。作用域设置在 “Scope” 标签页添加目标主机和路径如*.example.com和/api/v1/*确保只有目标流量会被插件处理避免干扰其他网站。Decoder 关联在 “Decoders” 标签页添加我们编写的 Python 脚本文件并确保decode和encode函数被正确识别。启用与测试保存配置并启用该 Profile。打开 Burp 的 Proxy 历史记录或 Repeater找到一条目标应用的请求。如果配置成功你将在“Autodecoder”这个新标签页下看到解密后的明文请求和响应而原始的 “Raw” 标签页显示的还是密文。实操心得配置作用域非常关键。初期我因为作用域太宽导致访问其他网站时 Burp 也尝试解密引发一连串错误。建议先用单个具体 URL 测试成功后再放宽到域名或路径通配符。5. 集成测试与问题排查实录配置完成后真正的挑战才刚刚开始。集成测试中会遇到各种边界情况和异常。5.1 典型问题与解决方案以下是我在测试过程中遇到的主要问题及解决方法问题现象可能原因排查步骤与解决方案Autodecoder 标签页无显示1. 作用域不匹配。2. Python 脚本有语法错误或依赖未安装。3. Decoder/Encoder 函数名不是decode/encode。1. 检查 Burp 的 Target Scope 和 Autodecoder 的 Profile Scope确保当前请求的 URL 被包含。2. 查看 Burp 的 Extender - Errors 标签页是否有 Python 相关的报错。在系统终端尝试python -c “import Crypto”验证依赖。3. 确认脚本中两个函数命名完全正确。解密失败显示乱码或报错1. 密钥或 IV 错误。2. 加密模式或填充方式不匹配。3. 数据不是标准的 Base64或含有额外字符如换行符。1. 用 Frida 动态 Hook 确认运行时使用的 KEY/IV 是否与脚本一致。2. 确认算法细节是 CBC 还是 ECB是 PKCS5/PKCS7 填充还是无填充用已知的明文-密文对在独立脚本中验证算法。3. 在解密前对密文进行strip()或replace(‘\n’, ”)处理。修改后重放请求服务器返回签名错误1. 签名生成逻辑有误。2. 参数顺序不对。服务器要求按参数名 ASCII 排序。3. 数值类型问题JSON 中数字123和字符串”123″签名不同。1. 用 Burp 的 “Compare” 功能对比修改前后 Autodecoder 生成的密文Raw 视图确认是否不同。2. 在 Python 脚本中打印出用于签名的参数字符串与 Frida Hook 到的原请求签名源字符串进行逐字对比。3. 确保json.dumps时不要无意中修改了数据类型。使用separators(‘,’, ‘:’)避免添加空格确保与客户端序列化方式一致。时间戳过期错误timestamp与服务器时间不同步或容差过小。1. 在encode函数中获取更精确的时间戳如int(time.time() * 1000)。2. 在请求前少量增加时间戳如 5秒模拟网络延迟。但这只是权宜之计最好同步时间。Nonce 重复错误脚本生成的随机数质量不高或范围太小在快速重放时碰撞。使用更安全的随机源import secrets; nonce secrets.token_hex(8)生成 16 位十六进制随机数碰撞概率极低。5.2 流程验证与实战应用当所有问题解决后整个工作流就顺畅了透明查看在 Burp Proxy 历史记录中所有目标 API 的请求和响应都以解密后的 JSON 明文显示在 “Autodecoder” 标签页一目了然。轻松修改在 Repeater 中你可以直接在 Autodecoder 的明文视图中修改任何业务参数如用户ID、金额、状态。自动重放点击 “Send”插件会自动触发encode函数生成新的timestamp和nonce计算新的sign将整个 JSON 重新加密替换请求体并更新Content-Length。发送出去的就是一个全新的、有效的请求。安全测试你可以使用 Burp Scanner 对解密的明文接口进行主动漏洞扫描或者使用 Intruder 对某个明文参数进行模糊测试Autodecoder 会在每次攻击 payload 替换后自动完成重新签名和加密。这个流程将原本黑盒的、加密的通信彻底变成了白盒的、可交互的明文协议极大地提升了安全评估和协议分析的效率。6. 进阶思考与防护建议成功绕过不是终点从防御者角度思考能让我们对问题有更深的理解。6.1 更复杂的防护场景应对本次案例的防护属于中等强度。在实际中你可能会遇到更复杂的挑战动态密钥密钥不是硬编码而是每次登录后由服务器下发或通过密钥协商算法如 ECDH动态生成。解决方案需要 Hook 密钥获取或生成的函数并在 Autodecoder 脚本中实现同样的逻辑可能需要维护一个会话级的密钥存储。请求体嵌套加密外层是整体加密内层关键字段还可能单独加密。解决方案需要分层解密在 Autodecoder 的decode函数中实现多轮解密逻辑。代码混淆与反调试应用可能使用 ProGuard、DexProtector 等工具进行混淆并检测 Frida 等调试工具。解决方案使用更隐蔽的 Hook 框架如 Frida 的隐身模式或转向基于 ROM 刷机、Xposed 的静态 Hook 方案或进行人工的、艰苦的反混淆代码分析。证书绑定应用校验服务器证书或客户端证书。解决方案使用 Burp 导入应用信任的客户端证书或者通过逆向修改应用的证书校验逻辑风险较高。6.2 给开发者的安全加固建议作为开发者如何让应用更难以被这样分析呢避免客户端存储敏感密钥加解密密钥、签名盐值secret不应硬编码在客户端。可以采用动态获取、白盒加密或基于硬件安全环境如 TEE/SE的方案。强化签名算法不要使用 MD5。至少使用 HMAC-SHA256。签名应包含更多元且易变的要素如设备指纹、会话令牌等。时间戳与 Nonce 联动服务器端应严格校验时间戳窗口如 ±5分钟并维护一个短期缓存来拒绝重复的 nonce。nonce 应有足够的随机性和长度。协议混淆可以定期更新通信协议格式或加密算法通过热更新增加逆向工程的持续成本。实施端到端加密对于极高安全要求的场景考虑使用 TLS 双向认证并对关键业务数据在 TLS 之上再进行一层非对称加密如使用服务器公钥加密确保只有目标服务器能解密。6.3 工具链的维护与扩展本次搭建的工具链具有很好的扩展性多应用配置Autodecoder 支持多个 Profile。你可以为不同的应用创建不同的配置和脚本方便切换。算法库化将常用的加解密函数如 AES, RSA, SM4和签名函数如 HMAC封装成独立的 Python 模块供不同的 Decoder 脚本调用提高代码复用率。与爬虫框架结合将 Autodecoder 中验证成功的加解密和签名逻辑移植到 Scrapy 或 Requests 库的中间件中即可实现自动化的协议爬虫。这次通过 Autodecoder 插件绕过加密和重放防护的实战是一次非常典型的移动端协议逆向工程。其核心思想可以概括为静态分析定位关键点 - 动态验证提取算法参数 - 编写自动化脚本集成到工作流。整个过程对耐心、细心和工具链的熟练度要求很高每一个环节的疏忽都可能导致失败。但一旦打通你会发现面前豁然开朗应用的内部逻辑变得清晰可见为后续的安全评估、数据分析和自动化工作铺平了道路。最后提醒一点所有技术都应在法律和授权范围内使用用于提升自身产品的安全防护或进行授权的安全测试。