本帖最后由 heyechuanmei 于 2024-03-12 21:51 编辑
前言注册好几年了,才几十积分,想着写点东西刷刷积分争取早日到 200,可是空瓶子又倒不出啥技术,前段时间不是发了个某云盘每日任务的脚本么,干脆就记录一下抓取 API 的过程吧。 没有技术含量,就想着记录仔细点,万一哪个萌新看到了呢。 活动分析先看看活动位置 理一下任务步骤 - 点击去查看,到达活动页面,发现就是底部栏的"发现"页面
- 点击分享,弹出分享方式
- 不分享直接退出活动,查看任务是否完成。(答案是并没有)
- 回到第二步,不过这次需要点击某个方式了
- 分享成功后,查看任务是否完成。(ok 的)
- 云盘这个任务点进 QQ 就行了,不用真发送也能成功完成任务
那么抓包就是重点分析第 4 步了,如果没成再看前面的(因为有时候活动需要前置请求激活才能完成)。 HttpCanary 抓包通过上面的分析,我们在弹出分享方式后,打开抓包,点击 QQ 后停止抓包,这样可以获得较少的请求(如果没有找到想要的才扩大范围)。 抓取到 12 条请求 简单的查看 url 和 请求体,并没有发现可疑的有 share 关键字或者类似的请求。 先尝试下重放请求。发一条请求查看一下云朵记录,结果重放第一条就行了,这就是运气吧!再重放两次也是 OK 的。 请求分析POST /datacenter/ HTTP/1.1 Host: datacenter.mail.10086.cn Connection: keep-alive Content-Length: 1731 sec-ch-ua: "Not A(Brand";v="99", "Android WebView";v="121", "Chromium";v="121" Content-type: application/x-www-form-urlencoded platform: h5 source: content-open User-Agent: Mozilla/5.0 (Linux; Android 13; mi6 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/121.0.6167.178 Mobile Safari/537.36 MCloudApp/10.5.1 sec-ch-ua-mobile: ?1 sec-ch-ua-platform: "Android" Accept: */* Origin: https://h.139.com X-Requested-With: com.chinamobile.mcloud Sec-Fetch-Site: cross-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://h.139.com/content/discoverNewVersion?columnId=20&token=STuid0000011008510086uasdjikoadakhbsw&targetSourceId=001005 Accept-Encoding: gzip, deflate, br, zstd Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
data=eyJ0cmFjZUlkIjo5Nzxxxxxxxxxxxxxxxxn0%3D&ext=crc%3D-1412813306
可以看到在 Header 中既无 Cookie 也无 Authorization,那么鉴权信息只能是在请求体中了。 先看 data,eyJxxx 开头,最后%3D(就是=编码后的),典型的 base64 啊,打开在线工具查看一下,先 URL 解码(因为这个复制出来有%3D 之类的),再 Base64 解码 得到了 不急着分析字段,接下来看 ext。 ext=crc=-1412813306 这硬看也看不出啥名堂,只知道是 CRC 循环冗余校验
目光再次来到请求,发现 Referer 是个网页 这也就简单多了,直接查看调用栈,试着 debuger 先搜素一下关键字 crc= 搜索下 hashCode,好家伙,直接出结果了,debugger 都免了。这不就是 Java 的 hashCode 用 JavaScript 实现的么? _.hashCode = function (str) { if (typeof str !== 'string') { return 0; } var hash = 0; var char = null; if (str.length == 0) { return hash; } for (var i = 0; i < str.length; i++) { char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return hash; };
把 data 传入尝试一下得到 -1412813306 ,ok,结束了。 编写代码直接在浏览器中运行一下 const data = { traceId: Number(Math.random().toString().substring(10)), tackTime: Date.now(), distinctId: '18d7cccee76678-08d30cfe3e81ee8-4c657b58-1327104-18d7cccee7711fc', eventName: 'discoverNewVersion.Page.Share', event: '$manual', flushTime: Date.now(), model: 'Nexus 5', osVersion: '', appVersion: '', manufacture: 'LG', screenHeight: 700, os: 'Android', screenWidth: 352, lib: 'js', libVersion: '1.17.2', networkType: '', resumeFromBackground: '', screenName: '', title: '【精选】一站式资源宝库', eventDuration: '', elementPosition: '', elementId: '', elementContent: '', elementType: '', downloadChannel: '', crashedReason: '', phoneNumber: '10086', storageTime: '', channel: '', activityName: '', platform: 'h5', sdkVersion: '1.0.1', elementSelector: '', referrer: '', scene: '', latestScene: '', source: 'content-open', urlPath: '', IP: '', url: 'https://h.139.com/content/discoverNewVersion?columnId=20&token=STuid0000011008510086uasdjikoadakhbsw&targetSourceId=001005', elementName: '', browser: 'Edge', elementTargetUrl: '', referrerHost: '', browerVersion: '122.0.0.0', latitude: '', pageDuration: '', longtitude: '', urlQuery: '', shareDepth: '', arriveTimeStamp: '', spare: { mobile: '10086', channel: '' }, public: '', province: '', city: '', carrier: '', };
function hashCode(str) { if (typeof str !== 'string') { return 0; } var hash = 0; var char = null; if (str.length == 0) { return hash; } for (var i = 0; i < str.length; i++) { char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; } return hash; }
// btoa 是浏览器的内置方法,nodejs 没有,node 直接使用 Buffer const base64 = btoa(JSON.stringify(data));
const res = await fetch('https://datacenter.mail.10086.cn/datacenter/', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', platform: 'h5', }, body: `data=${base64}&ext=${encodeURIComponent('crc=' + hashCode(base64))}`, });
console.log(await res.json());
起初用的是最精简的 Header,并没有增加云朵,依次测试后发现 platform 是必须的。 直接修改 phoneNumber 为小号的手机号码,测试成功。 修改 traceId,distinctId 这些后也是成功的,最后发现其实大部分字段都是可以删除的,确定用户身份唯一字段就是 phoneNumber。也就是不需要登录,只要手机号码就可以完成这个任务了。 其他编辑 有同学已经发现之前的代码再浏览器中并不能成功运行,出现了 VM2516:1 Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range. at <anonymous>:1:1
值得庆幸的是,有人真的试了。但是为啥不去尝试解决呢。 翻译一下 无法在“Window”上执行“btoa”:要编码的字符串包含 Latin1 范围之外的字符。
直接百度你就能得到一个 btoa(unescape(encodeURIComponent(JSON.stringify(data)))) 这样的结果,不是太简单了么? 当然,你已经看完这篇文章,_.hashCode 找到了,用同样方式找到 _.base64Encode 不就行了。 发这个的目的不是为了发最后那几行代码,而是分析一个简单的过程,授人以鱼不如授人以渔。这几行代码没有太多的意义,我已经在站内分享了这个开源程序(前言提到的)
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|