首页 / 资讯中心 / 文章详情

App逆向入门实战:新闻详情页接口抓包与签名算法还原

App逆向入门实战:新闻详情页接口抓包与签名算法还原 ★ FEATURED ARTICLE
1. 项目缘起与整体思路拆解1.1 为什么选一个新闻详情页作为逆向入门靶子做移动端数据采集的朋友大概都有个共识网页端能拿到的东西尽量别碰App。但现实往往不遂人愿——很多内容平台把核心数据往App里一收网页端只留个摘要或者干脆给你个请下载客户端查看。我这次接到的需求就是这种情况某新闻类应用的详情页正文、发布时间、作者署名、阅读量这些字段在网页端要么缺失要么被截断只能从App的接口里想办法。选新闻详情页作为逆向入门靶子是我反复权衡后的决定。原因有三第一新闻类App的接口结构通常比社交、电商类简单字段命名直白返回体多是规整的JSON不像电商那样层层嵌套还带各种加密签名第二详情页的请求参数相对固定一般就是文章ID加几个设备标识不像列表页那样带分页游标、推荐算法参数第三新闻内容的时效性要求没那么苛刻允许你慢慢调试不像抢购类接口那样窗口期只有几秒。对第一次实战的人来说这三点决定了你踩坑后还有回旋余地。这里说的逆向指的是通过分析App与服务器之间的通信过程还原出接口的请求构造方式从而在App之外复现数据获取。它和破解、篡改是两码事——我们只关心怎么正常地把数据请求出来不碰任何绕过授权、伪造身份的灰色操作。这个边界一开始就要划清楚后面所有工作都在这个框架内进行。1.2 整体方案选型抓包为主、静态分析为辅入门阶段最容易犯的错是一上来就扎进反编译工具里啃smali代码。我见过不少新手抱着几十万行的反编译结果找了两天最后连请求地址都没定位到。我的思路是反过来的先抓包看清通信全貌再针对性地做静态分析。抓包的价值在于它直接告诉你App发了什么、服务器回了什么这是最接近真相的一手材料。你拿到一个详情页请求能看到完整的URL、请求头、请求体、响应体剩下的工作就是搞清楚这些参数各自怎么来的。大部分参数其实是设备信息、时间戳、版本号这类看一眼就懂的东西真正需要逆向的往往只有一两个签名字段。静态分析则是在抓包遇到障碍时用的补充手段。比如你发现请求头里有个sign字段每次请求都不一样抓包只能看到结果看不到算法这时候才需要把安装包拆开找到生成这个字段的代码逻辑。工具上抓包我用的是系统代理配合证书信任的方式具体工具名不展开市面主流抓包工具逻辑相通静态分析用反编译套件看Java层逻辑遇到native层再上动态调试。这套组合对新闻类App足够用了。需要强调的是整个过程中要严格遵守目标平台的服务条款与相关法律法规采集行为仅限个人学习研究、且不涉及任何非公开或需授权的内容。这一点不是套话是决定你这条路能走多远的底线。1.3 环境准备清单与前置认知动手之前把环境搭利索能省掉后面一半的折腾。我列一下自己实际用到的东西以及每样东西的作用方便你对照准备。环节用途关键注意点抓包工具捕获App与服务器的HTTP/HTTPS通信需在设备上安装并信任其根证书测试设备运行目标App建议用独立测试机避免影响日常使用反编译套件查看App的Java层代码逻辑关注签名、加密相关类动态调试环境跟踪运行时变量入门阶段可暂缓抓包卡住再上接口调试工具手动重放请求、验证参数支持自定义请求头与请求体前置认知这块有三件事必须先想明白。第一HTTPS不是抓不到是需要证书信任。很多人第一次抓包发现全是CONNECT请求、看不到明文就以为加密无解其实是证书没装对或者没被系统信任。第二App的请求不一定都走同一个通道有的走标准HTTP库有的走自研网络层抓包工具要能覆盖到。第三参数分静态和动态两类静态的抄下来就行动态的必须搞清楚生成规则否则重放必失败。把这三条刻在脑子里后面遇到问题能快速定位到是哪一环出了岔子。2. 抓包实战从一片混沌到锁定目标请求2.1 证书配置这一步九成人卡在这里抓包的第一道坎永远是证书。我按步骤说但重点讲那些文档里不写的坑。先在测试设备上把抓包工具的根证书下载下来。常规做法是设备连上抓包工具开的代理然后浏览器访问工具的证书下载页。下载完安装这一步大部分人能过。真正的坑在信任环节——很多系统版本里用户手动安装的证书默认是不被应用信任的你得进到证书管理里找到刚装的那张手动把它标记为受信任。不同系统版本入口不一样有的在安全里有的在加密与凭据里找的时候耐心点。装完证书打开目标App随便点进一篇文章。这时候抓包工具里应该开始刷请求了。如果看到的全是加密的隧道请求、没有明文八成是证书没被这个App信任。这里有个细节部分App会做证书校验只信任系统内置证书不认用户安装的。遇到这种情况入门阶段可以先换个思路——看看App有没有网页版或者轻量版接口是否通用实在不行再考虑更深入的手段但那已经超出初学第一次实战的范畴了不建议一上来就啃。提示证书配置完成后先拿一个已知的、简单的请求验证一下能不能看到明文确认通道打通了再去分析目标App否则你会在到底是没抓到还是抓到了看不懂之间反复怀疑人生。2.2 在请求洪流里精准定位详情页接口App一启动请求是成百上千地刷。列表页、推荐位、广告、埋点、配置下发……全混在一起。新手最容易在这里迷失挨个点开看看到眼花。我的做法是用操作来缩小范围先清空抓包记录然后在App里只做一个动作——点进某篇文章停住不再做任何操作。这时候新冒出来的请求大概率就包含详情页数据。定位到候选请求后看响应体。新闻详情页的响应通常有几个特征包含大段正文文本、有标题字段、有发布时间、有作者信息。你甚至可以直接在响应体里搜文章标题里的几个字能搜到的基本就是它了。我这次定位到的接口URL路径里带article/detail字样请求方式是GET参数不多响应是标准JSON正文放在一个content字段里还带了publishTime、author、readCount这些字段。到这一步其实已经成功了一大半。但别急着高兴。把这条请求的完整信息URL、所有请求头、所有查询参数原样复制到接口调试工具里重放一次看看能不能拿到同样的数据。如果能说明这个接口没有强校验直接构造请求就行如果返回错误或者空数据说明有参数在起作用进入下一节的参数分析。2.3 请求头里的玄机哪些必须带哪些可以省重放的时候我习惯做一个减法实验把请求头一个个删掉看哪个删了会导致失败。这样能快速区分出必需头和可有可无头。实测下来新闻类App的详情页接口必需的头通常就这几类标识客户端身份的比如版本号、平台标识、标识设备的设备ID之类、以及一个签名字段。像User-Agent、Accept这些很多时候服务器并不严格校验删了照样返回数据。但签名字段是硬骨头删了必挂改了也必挂它往往是对请求参数加时间戳加密钥做哈希得来的。我这次遇到的接口请求头里有个X-Sign字段32位十六进制一看就是MD5或者类似哈希。查询参数里有个ts时间戳还有个nonce随机串。这三个东西凑一起基本可以断定签名规则是参数排序拼接密钥哈希的经典套路。抓包能看到签名结果但看不到算法这就必须上静态分析了。注意做减法实验时每次只改一个变量改完立刻重放验证。一次改多个失败了你也说不清是哪个导致的。3. 静态分析把签名算法从代码里挖出来3.1 反编译后的第一件事全局搜关键词拿到安装包用反编译套件拆开得到一堆smali文件和资源。别急着从头读那是不可能的。第一件事是全局搜索。搜什么搜你抓包时看到的那些特征字符串——签名字段的头名X-Sign、参数名nonce、ts或者接口路径里的article/detail。搜索命中后顺着引用关系往上找。通常你会落在一个网络请求封装类或者拦截器里那里就是拼接参数、计算签名的地方。我这次搜X-Sign直接定位到一个拦截器类里面有个方法在请求发出前统一给请求头塞签名。点进去看逻辑很清晰取出所有查询参数按key的字典序排序拼成key1value1key2value2的形式末尾追加密钥然后做MD5转小写塞进X-Sign头。这里有个经验签名逻辑往往集中在一个工具类里方法名可能叫getSign、buildSign、signParams之类。找到它整个算法就摊在你面前了。如果搜不到可能是被混淆了那就换个思路——搜MD5相关的调用或者搜时间戳格式化相关的代码从侧面逼近。3.2 读懂签名逻辑排序、拼接、加盐、哈希把那段逻辑翻译成人话大概是这么个流程收集本次请求的所有业务参数不含签名本身放进一个映射表按参数名的ASCII码升序排序遍历排序后的参数拼成kv并用连接跳过空值在拼接串末尾追加一个固定的盐值salt这个盐通常硬编码在代码里也可能从配置下发对最终字符串做MD5输出小写十六进制即为签名。我用Python把这个过程复现出来核心就几行import hashlib def build_sign(params: dict, salt: str) - str: items sorted((k, v) for k, v in params.items() if v not in (None, )) raw .join(f{k}{v} for k, v in items) salt return hashlib.md5(raw.encode(utf-8)).hexdigest()盐值从哪来就在刚才那个拦截器类里通常以一个字符串常量出现或者从某个配置类里读。我这次是在同一个类的静态字段里找到的一串看起来毫无规律的字母数字组合。把它抄下来签名就能自己算了。3.3 时间戳与随机串动态参数的生成规则ts是当前毫秒时间戳这个没什么好说的int(time.time() * 1000)。nonce是随机串代码里一般是取UUID去掉横线或者用随机数生成器拼一个定长字符串。这两个参数本身不参与保密它们的作用是让每次请求的签名都不同防止重放攻击和缓存命中。这里有个容易忽略的点时间戳的时区和精度。有的接口要求秒级有的要求毫秒级还有的要求和服务器时间偏差不超过一定范围。我这次是毫秒级且实测偏差在几分钟内都能通过容错还算宽松。如果你的请求一直返回签名错误或请求过期先检查时间戳精度对不对再检查设备时间准不准。提示把签名函数封装好之后先用抓包抓到的那条真实请求做验证——用相同的参数、相同的时间戳、相同的nonce算出来的签名应该和抓包里的一模一样。对上了说明算法还原正确对不上回去检查排序规则、空值处理、盐值有没有抄错。4. 完整复现从单条请求到稳定采集4.1 构造请求的完整参数清单把前面所有分析结果汇总一条详情页请求需要的东西就齐了。我整理成表方便对照参数位置参数名来源是否动态查询参数articleId从列表页或目标文章获取否查询参数ts当前毫秒时间戳是查询参数nonce随机字符串是查询参数platform固定值如android否查询参数appVersion固定值从安装包信息取否请求头X-Sign上述参数排序拼接加盐后MD5是请求头deviceId设备标识可用固定值测试否构造请求时先把静态参数填好再生成动态参数最后算签名。顺序不能乱因为签名依赖前面所有参数。我用接口调试工具手动拼了一次返回200正文完整字段齐全。到这一步单条请求的复现就算成功了。4.2 用脚本把流程串起来手动拼一次是验证真正要用还得写成脚本。我用Python的requests库把签名函数、参数构造、请求发送串成一个完整流程import time, uuid, requests from sign_util import build_sign # 前面写好的签名函数 SALT 从代码里抄出来的盐值 BASE_URL https://example.com/article/detail def fetch_article(article_id: str) - dict: params { articleId: article_id, ts: str(int(time.time() * 1000)), nonce: uuid.uuid4().hex, platform: android, appVersion: x.y.z, } headers { X-Sign: build_sign(params, SALT), deviceId: 固定的测试设备标识, User-Agent: 目标App的UA, } resp requests.get(BASE_URL, paramsparams, headersheaders, timeout10) resp.raise_for_status() return resp.json()跑通之后拿几个不同的articleId试都能正常返回说明参数构造是稳的。这里我特意把deviceId写成固定值实测服务器并不校验它和真实设备的绑定关系省了不少事。如果你的目标接口校验严格可能需要在首次请求时动态获取设备标识那就得再往前追一层。4.3 控制节奏别让采集变成攻击脚本能跑通不代表可以放开跑。新闻类接口对高频请求通常有频率限制触发后轻则返回空数据重则临时封禁。我的做法是单线程加随机间隔每次请求之间sleep 1到3秒的随机值既不影响采集效率也不会给服务器造成压力。import random, time for aid in article_ids: data fetch_article(aid) process(data) time.sleep(random.uniform(1.0, 3.0))另外采集范围要克制。只取你真正需要的字段别把整个响应体无差别存下来只采公开可见的内容别去碰需要登录或付费的部分。这些不是技术问题是使用边界问题但恰恰是决定你这项工作是否站得住脚的关键。5. 踩坑实录与排查速查表5.1 那些让我卡了半天的典型问题签名一直对不上。最开始我算出来的签名和抓包的差几位反复检查算法没发现问题。后来发现是排序规则理解错了——代码里用的是TreeMap按key的自然顺序排而我一开始按参数出现顺序拼的。改成字典序排序后立刻对上了。这个坑很典型排序规则一定要以代码为准别想当然。重放返回设备异常。有次换了个deviceId重放接口直接拒绝。回去看代码发现服务端会拿deviceId和请求头里的另一个标识做关联校验。解决办法是把抓包时那套设备标识原样保留别自作聪明改。响应体里正文是空的。抓包看得到正文脚本请求回来content字段却是空字符串。排查半天发现是请求头少了一个Accept-Encoding服务器根据这个头决定返回压缩格式还是明文缺了它走了另一条分支。补上就好了。这种看起来无关紧要的头其实影响返回内容的情况在减法实验时特别容易漏掉。时间戳精度搞错。有一版脚本用了秒级时间戳接口一直返回请求过期。改成毫秒级立刻正常。这个错误很低级但新手真的容易犯因为不同接口要求不一样得逐个确认。5.2 问题排查速查表现象可能原因排查方向抓包全是加密隧道证书未信任检查证书是否被系统信任重放返回签名错误算法还原有误用真实请求反向验证签名函数重放返回空数据缺少必需请求头逐个补回头做减法实验的逆操作返回请求过期时间戳精度或偏差确认秒/毫秒校准设备时间返回设备异常设备标识关联校验保留抓包时的完整设备标识正文为空但状态200编码或压缩头缺失检查Accept-Encoding等头高频后返回空触发频率限制降频、加随机间隔5.3 给初学者的几条实在建议第一先跑通一条再谈批量。我见过太多人单条还没验证成功就开始写并发框架最后问题堆在一起根本没法定位。单条请求从抓包到重放再到脚本复现每一步都验证通过再往下走。第二把分析过程记下来。哪个参数从哪来、签名算法长什么样、盐值是多少随手记在一个文档里。逆向过程是探索性的你今天搞明白了过一周再看脚本可能就忘了当初为什么这么写。第三遇到native层别硬刚。如果签名逻辑不在Java层而在so库里入门阶段可以先放一放看看有没有其他接口或者网页端能迂回拿到数据。不是所有问题都必须正面解决绕过去也是一种能力。第四边界意识比技术更重要。能抓到什么、能采什么、采了怎么用这三件事心里要有杆秤。技术本身中性但使用方式决定了性质。我个人的原则是只碰公开内容、只做低频采集、只用于学习研究超出这个范围的一律不碰。这套流程走下来一个新闻详情页的接口从抓包到稳定复现前后大概花了我两个晚上。第一个晚上全耗在证书和定位请求上第二个晚上啃签名算法和调脚本。现在回头看最难的不是技术本身而是在一片混沌里保持先看清全貌再动手的耐心。很多人卡住不是因为不会是因为太急抓到一个请求就埋头分析结果分析的是个广告接口。先把范围缩小、把目标锁定后面的路会顺很多。
阅读完成 · 觉得有帮助?
咨询建站