ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

App签名与加密码逆向实战:从抓包到重签名还原小红书、瑞幸签名逻辑

App签名与加密码逆向实战:从抓包到重签名还原小红书、瑞幸签名逻辑 简介这份资源面向移动应用、小程序与网站开发者聚焦数字签名与加密这一安全核心环节整理了多个主流生活服务类App的签名或加密实现素材。包内共15个文件以11个Python脚本为主辅以2个JavaScript文件、1份README说明和1份LICENSE授权文件压缩包约26KB体量轻便便于快速查阅与二次参考。内容覆盖自如、小红书、蛋壳公寓、瑞幸咖啡等项目的目录模块并包含signature_algorithm相关实现可帮助读者理解非对称加密、散列签名、密钥交换与HTTPS安全传输等机制在实际项目中的落地方式。目前已有435人学习下载适合需要研究App签名校验、接口加密逻辑或搭建安全通信流程的开发者参考也可作为安全测试与逆向分析的学习素材。1. 从抓包到重签名这套 App 签名与加密码资源到底能解决什么你有没有遇到过这种场景想分析某个 App 的接口抓包工具一开请求体全是乱码想复现小程序的登录流程发现请求头里挂着一串X-Sign改一个参数就报 403想给自家 App 做安全加固却不知道签名校验该从哪一层下手。这套资源就是冲着这类问题来的——它把自如、小红书、蛋壳公寓、瑞幸咖啡这几个真实 App 的签名或加密码实现整理在一起外加一个ba.zip压缩包里面大概率是配套的代码、脚本或分析笔记。它适合三类人做爬虫和逆向的需要搞清楚签名参数怎么生成做移动端安全的想看看商业 App 的加密码设计思路还有做小程序或网站接口防护的想参考别人怎么把签名机制落地。不适合纯新手——你得至少能看懂 HTTP 请求、会用抓包工具、知道什么是 MD5 和 HMAC。资源本身不是“一键破解工具”而是一组可对照的样本让你从真实案例里反推签名逻辑。下面按“先搞懂签名在干嘛 → 再动手复现 → 最后避坑”的顺序拆开讲。2. 签名与加密码的底层逻辑为什么改一个参数就 4032.1 签名不是加密它只解决“请求有没有被篡改”很多人把签名和加密混在一起其实两者目标不同。加密是让别人看不懂内容签名是让别人改不了内容。App 里的签名通常长这样客户端把请求参数按某种规则拼成一个字符串加上一个只有客户端和服务端知道的密钥做一次哈希把结果作为sign字段塞进请求头或请求体。服务端收到后用同样的规则和密钥再算一遍对不上就拒绝。以小红书早期版本为例常见做法是把device_id、timestamp、nonce、body按字典序拼起来末尾追加一个固定 salt然后走 MD5。瑞幸咖啡的接口则更偏向 HMAC-SHA256密钥可能藏在 so 文件或小程序包里的某个常量。自如和蛋壳公寓的加密码有时会多一层 AES先把 body 加密再签名这样即使抓包看到密文改一个字节也会导致解密失败或签名不匹配。提示签名密钥一旦泄露整套机制就形同虚设。所以分析时重点找“密钥从哪来”而不是死磕哈希算法本身。2.2 加密码的三种常见形态固定 salt、动态 token、设备指纹固定 salt 最简单也最容易被逆向——字符串搜索就能找到。动态 token 通常是登录后服务端下发一个access_token后续请求用它参与签名过期就失效。设备指纹则更麻烦它把imei、android_id、mac地址甚至传感器数据混进去换设备就签名失败。这套资源里自如和蛋壳公寓偏向设备指纹加固定 salt 的组合小红书和瑞幸更依赖登录态 token。ba.zip里如果包含抓包样本或反编译片段你可以直接对照看哪些字段参与了运算。判断方法很简单用抓包工具重放请求逐个删字段看哪个字段删掉后签名报错那个字段大概率就是签名因子。2.3 从请求结构反推签名因子的实操步骤先抓一个正常请求把 URL、headers、body 完整保存。然后用 curl 或 Postman 重放确认能通。接着做三组对照实验第一组只改timestamp第二组只改nonce第三组改 body 里某个业务参数。如果改 timestamp 就 403说明时间戳参与签名如果改业务参数也 403说明 body 被纳入签名范围。# 用 curl 重放请求观察签名校验是否通过 curl -X POST https://example.com/api/v1/order \ -H Content-Type: application/json \ -H X-Sign: 3a7f2b... \ -H X-Timestamp: 1700000000 \ -d {order_id:123,amount:100}上面命令里X-Sign和X-Timestamp是重点观察对象。如果服务端返回{code:403,msg:sign invalid}说明签名不对如果返回业务数据说明这组签名有效。参数说明-H加请求头-d加 body-X POST指定方法。失败时先看时间戳是否过期——很多接口允许的时间窗口只有 5 分钟。3. 动手复现用 Python 还原小红书与瑞幸的签名流程3.1 环境准备与依赖安装我一般用 Python 3.10 以上配合requests做请求hashlib和hmac做哈希frida只在需要动态 hook 时才装。如果你要分析小程序还得准备一个能解包的工具比如wxappUnpacker或unveilr。ba.zip解压后如果里面有.so或.dex可以用jadx或IDA看静态逻辑。pip install requests hashlib hmac # 如果需要动态调试 pip install frida-toolsrequests负责发请求hashlib提供 MD5/SHA 系列hmac专门做带密钥的哈希。frida-tools用于 hook App 运行时直接看签名函数的入参和出参。注意不要在生产环境跑 hook容易触发风控。3.2 还原小红书 MD5 签名参数排序与 salt 拼接假设抓包看到请求体是{note_id:123,user_id:456,timestamp:1700000000}请求头有X-Sign。常见还原思路是把 body 里的 key 按字典序排拼成key1value1key2value2末尾加 salt做 MD5。import hashlib def xhs_sign(params: dict, salt: str) - str: # 按 key 字典序排序 sorted_keys sorted(params.keys()) # 拼成 kvkv 形式 raw .join([f{k}{params[k]} for k in sorted_keys]) # 末尾追加 salt raw_with_salt raw salt # MD5 并转十六进制 return hashlib.md5(raw_with_salt.encode(utf-8)).hexdigest() params {note_id: 123, user_id: 456, timestamp: 1700000000} salt xhs_salt_2024 # 这个值需要从反编译或 hook 里找 print(xhs_sign(params, salt))逻辑说明sorted_keys保证顺序一致raw是待签名串salt是密钥。参数说明params里必须包含所有参与签名的字段少一个结果就错salt如果找不到可以尝试用 frida hookMessageDigest或Mac类看传入的字符串是什么。失败时先检查 body 是否被 JSON 序列化后再签名——有些 App 签的是原始 JSON 字符串不是排序后的 kv。3.3 还原瑞幸 HMAC-SHA256密钥提取与时间窗口瑞幸的接口更偏向 HMAC-SHA256。典型结构是sign HMAC-SHA256(secret, timestamp nonce body)。密钥可能硬编码在 so 里也可能由服务端下发。import hmac import hashlib import time def luckin_sign(secret: str, body: str, nonce: str) - str: timestamp str(int(time.time())) # 拼接待签名内容 message timestamp nonce body # HMAC-SHA256 signature hmac.new( secret.encode(utf-8), message.encode(utf-8), hashlib.sha256 ).hexdigest() return signature, timestamp secret luckin_secret_key # 需从逆向或 hook 获取 body {product_id:123,count:1} nonce abc123 sig, ts luckin_sign(secret, body, nonce) print(fX-Sign: {sig}, X-Timestamp: {ts})逻辑说明hmac.new第一个参数是密钥第二个是消息第三个是哈希算法。参数说明secret是核心找不到就复现不了nonce通常随机生成服务端会缓存已用过的 nonce 防重放timestamp参与签名所以本地时间要准。失败时先看服务端返回的错误码——如果是sign expired说明时间窗口过了如果是nonce reused说明 nonce 重复了。3.4 用 frida hook 动态抓取签名入参静态分析找不到密钥时frida 是最直接的手段。挂上目标 Apphookjavax.crypto.Mac的doFinal方法打印入参和密钥。Java.perform(function () { var Mac Java.use(javax.crypto.Mac); Mac.doFinal.overload([B).implementation function (data) { console.log(Mac input: bytesToString(data)); console.log(Mac algorithm: this.getAlgorithm()); var result this.doFinal(data); console.log(Mac output: bytesToString(result)); return result; }; }); function bytesToString(bytes) { var arr []; for (var i 0; i bytes.length; i) arr.push(bytes[i]); return arr.join(,); }逻辑说明Java.perform确保在 Java 环境里执行overload([B)匹配字节数组参数。参数说明data是待签名内容result是签名结果。打印出来后你就能看到密钥拼接的原始字符串。注意hook 时机要早最好在 App 启动前就 attach否则可能错过初始化。4. 避坑与排查签名复现时最容易翻车的五个点4.1 现象本地算出的 sign 和服务端要求的不一致原因通常有三种一是参数字典序排错了比如把timestamp排在了body前面但服务端是按 ASCII 码排的二是编码问题中文参数没做 URL encode 或 UTF-8 编码三是 body 被序列化了两次签的是原始字符串你签的是 JSON 对象转出来的字符串。解决先用固定参数在本地算再和服务端返回的 sign 对比逐字段排查。常见做法是打印出待签名原始串肉眼核对。4.2 现象请求能通一次第二次就 403原因nonce 或 timestamp 被服务端缓存了重复使用直接拒绝。解决每次请求生成新的 noncetimestamp 用当前时间。如果服务端要求 nonce 全局唯一可以用 UUID。注意不要用固定 nonce 做测试否则第一次成功后面全失败。4.3 现象frida hook 不到目标方法原因App 用了加固类被抽到 native 层或者 hook 时机太晚。解决换用frida -U -f com.example.app -l script.js在启动时就注入如果还不行尝试 hookSystem.loadLibrary看加载了哪些 so再针对 so 里的函数做 hook。血泪经验有些 App 会检测 frida 端口发现就闪退这时候需要改端口或换注入方式。4.4 现象ba.zip解压后代码跑不起来原因缺少依赖、Python 版本不对、或者代码里引用了本地路径。解决先看requirements.txt或README没有就手动装requests、pycryptodome这些常见库。如果报ModuleNotFoundError用pip install补上。注意不要直接在生产环境跑来源不明的脚本先在虚拟机里试。4.5 现象签名算法找对了但密钥不对原因密钥可能是动态下发的或者被拆成多段拼接。解决hook 密钥生成函数看它从哪读数据。常见做法是密钥存在SharedPreferences或so的.data段里。如果密钥是服务端下发的先抓登录接口看返回里有没有secret字段。5. 进阶技巧用签名样本做接口防护与自动化验证5.1 把签名逻辑抽象成可配置的校验模块分析完几个 App 后你会发现签名逻辑无非是“排序 拼接 哈希 加时间戳”。我一般会写一个通用校验模块把 salt、算法、字段列表做成配置这样换一个 App 只需要改配置不用重写代码。import hashlib import hmac class SignValidator: def __init__(self, algorithmmd5, salt, fieldsNone): self.algorithm algorithm self.salt salt self.fields fields or [] def sign(self, params: dict) - str: # 只取配置里指定的字段 filtered {k: params[k] for k in self.fields if k in params} raw .join([f{k}{filtered[k]} for k in sorted(filtered)]) raw self.salt if self.algorithm md5: return hashlib.md5(raw.encode()).hexdigest() elif self.algorithm hmac-sha256: return hmac.new(self.salt.encode(), raw.encode(), hashlib.sha256).hexdigest() else: raise ValueError(unsupported algorithm) # 用法 validator SignValidator(algorithmmd5, saltxhs_salt, fields[note_id, user_id, timestamp]) print(validator.sign({note_id: 123, user_id: 456, timestamp: 1700000000}))逻辑说明fields控制哪些参数参与签名salt和algorithm可配。参数说明如果服务端要求 body 整体参与签名就把body作为一个字段传进来。这个模块可以直接嵌到你的爬虫或测试脚本里换 App 时只改初始化参数。5.2 用自动化脚本批量验证签名有效性复现完签名后别急着上量。先写一个验证脚本用固定参数跑 100 次看成功率。如果低于 95%说明还有隐藏因子没找到。import requests import time def validate_sign(url, headers, body, times100): success 0 for i in range(times): # 每次重新生成签名 headers[X-Timestamp] str(int(time.time())) headers[X-Sign] compute_sign(body, headers[X-Timestamp]) resp requests.post(url, headersheaders, jsonbody) if resp.status_code 200 and resp.json().get(code) 0: success 1 time.sleep(0.5) # 避免触发风控 print(f成功率: {success}/{times}) # compute_sign 用你复现出来的函数逻辑说明循环里每次刷新时间戳和签名time.sleep降低频率。参数说明times是测试次数0.5秒是间隔。如果成功率低先看失败请求的返回码——如果是sign invalid说明签名逻辑还有遗漏如果是rate limit说明请求太快加长间隔。5.3 从签名样本反推服务端校验逻辑拿到多个 App 的签名样本后你可以反向设计自己的接口防护。比如时间戳窗口设 5 分钟nonce 用 Redis 缓存 10 分钟签名字段强制包含 body 的 SHA256。这样即使别人抓包改一个参数也会导致签名失败。我一般会在服务端加一层中间件统一做签名校验业务代码不用关心。从那以后我每次复现新 App 的签名都强制先跑一遍 100 次验证再上量。希望帮到你。本文还有配套的精品资源点击获取
返回列表