当前位置: 首页 > article >正文

爬虫逆向学习(十五):Akamai 3.0反爬分析与sensor-data算法逆向经验

此分享只用于学习用途,不作商业用途,若有冒犯,请联系处理

Akamai 3.0反爬分析与sensor-data算法逆向经验

  • Akamai
  • 开始正题前须知
  • 站点信息
  • 接口分析反爬点
    • 反爬点定位
    • _abck
    • 定位结果
  • 逆向前准备工作
    • sensor_data生成位置
    • 本地替换文件
  • 请求体sensor_data逆向分析
    • qtx五次赋值分析
      • 第一次赋值
      • 第二次赋值
      • 第三次赋值
      • 第四次赋值
      • 第五次赋值
  • 请求测试
  • 结尾

Akamai

Akamai是啥就不多说了,这在爬虫圈可是有名的反爬产品。Akamai 3.0以前的产品我没有研究过,不过在扣Akamai 3.0 sensor-data算法时也看了其他博主的分享,感觉逆向流程差不多的,只不过Akamai 3.0现在每个一两天就会变换js文件,然后js文件每隔十分钟又会变换代码(不同站点规则可能不一样)

严重怀疑变换js文件后相当于变更了算法,而同一份js文件的更新则是对参数的重新混淆和打乱大数组长度与取值

开始正题前须知

我是2024年12月份开始研究的,期间一直是拿保存到本地js文件去跟流程扣算法的,然后到2025年1月份破解算法还能拿到成功数据。

这里要着重说一下:大家研究时代码肯定跟我的不一样了,所以不要抱着能一一跟学的想法,这篇博客重点是提供给大家扣算法思路和一些处理经验。

我尽可能写详细,有点难写hhh…

站点信息

  • 网站:https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343
  • 接口:https://www.dhl.com/utapi?trackingNumber=1232343&language=zh&requesterCountryCode=CN&source=tt

接口分析反爬点

反爬点定位

复制utapi接口的发包内容,然后将其转成python requests请求格式,拿到本地python环境下执行看看结果。
在这里插入图片描述

Akamai有tls指纹检测,所以需要用curl_cffi这个库来请求。
可以看到这里是可以直接请求拿到数据的,然后观察请求头和请求连接并没有反爬点,这时可以推测cookies存在反爬点。
在这里插入图片描述

当然不可能全部cookie都是反爬点,这里可以用排除法来确定真正的反爬点。
这里试出来确定请求必须携带_abck cookie,不然请求响应码是428
在这里插入图片描述
在这里插入图片描述

既然知道cookie _abck是反爬点,那我们就得知道它从哪里生成的。
看cookie列表这个参数的Secure是勾上的,说明它是服务器响应返回的,这就好找了,用fiddler抓包搜一下就知道。
在这里插入图片描述

打开fiddler然后清空浏览器数据,刷新页面,并搜索一下接口,拿到最新的 _abck,拿到fiddler,快捷键Ctrl F打开搜索框搜索,匹配到的包就会标为黄色,找到第一次出现的包就是了。
在这里插入图片描述
在这里插入图片描述

拿到目标链接后,在开发者工具那搜一下,发现这个接口请求了两次,第一次是GET请求,第二次是POST请求,而第二次请求响应结果返回了需要的cookie _abck
在这里插入图片描述
在这里插入图片描述

这两次请求是啥关系呢:第一个GET请求返回的是JS代码,然后这些JS代码会生成得到请求体sensor_data,用来第二次POST请求。
在这里插入图片描述

那这个接口链接又是在哪里得到的呢,我们继续在fiddler搜一下这个接口,发现它第一次出现在网站链接响应内容里
在这里插入图片描述
在这里插入图片描述

_abck

这个cookie其实在首页就有返回,包括后面的请求有些接口也会返回,但是这里它的中间值都是~-1~,而对于Akamai来说,它的中间值需要是~0~才有效。
在这里插入图片描述

定位结果

  • 请求首页链接拿到JS代码接口
  • 通过接口返回的JS代码生成得到请求体sensor_data
  • 使用POST请求接口,并带上请求体sensor_data,得到需要的cookie _abck
  • _abck中间内容为~0~说明cookie有效,反之cookie无效

逆向前准备工作

sensor_data生成位置

把JS接口加入xhr断点捕捉,这个能在接口发包时断住,我这里这个接口结尾是BHBSM,大家按自己的来,然后清空浏览器数据,刷新页面。
在这里插入图片描述

看到没,OIY的值就是我们想要的请求体sensor_data,理论上只要逆向出OIY的生成算法就能实现破解了。
在这里插入图片描述

本地替换文件

一开始我就说了Akamai同一份js文件的代码会不定时更新,所以我们需要将首页链接和JS接口GET链接保存到本地并进行替换。
在这里插入图片描述
在这里插入图片描述

这样就替换成功了,后续刷新就不会再变更代码或者文件了
在这里插入图片描述

请求体sensor_data逆向分析

承接上文,这里我们开始分析请求体sensor_data生成算法,由于JS代码太多太乱,所以我们选择逆向推理,从结果推理过程。
再次强调,本文所演示的代码肯定跟大家实操时不一致,大家重点学历逆向思路

上文我们已经找到了请求体sensor_data的生成位置,这里是RmX,我们看下图右边,发现pUXRmX有关联,我们先创建个空白js文件,将它俩的赋值代码扣过去。
在这里插入图片描述

大家看下pUX的赋值:Yx[Sm()[vd(dd)](Vd, Ym)][Fn()[ft(Y9)](cE, sO)](qtX);,看着是很复杂,但是打印一下发现其实就是某些方法的混淆。看下图,这就是Akamai另一个比较恶心的地方了,如果会AST的可以尝试去解混淆,这里就手动解混淆了
在这里插入图片描述

后面不会在这一块讲解太多,大家自己来…
在这里插入图片描述

通过上图我们需要找一下qtX的生成位置,直接在代码那搜索qtX = ,发现有五个位置,这里把五个位置全部打上断点并把代码全部扣下来,然后刷新页面,这样后面才能解混淆。
在这里插入图片描述

老样子,先手动解混淆,然后补未定义的参数,如果不知道哪些未定义的可以执行一下脚本,按报错的内容补也行。
在这里插入图片描述

qtx五次赋值分析

题外话大家往下看会发现很多参数都是写死的,这里原因有两个:

  1. 参数属于Akamai 3.0核心部分,后续再详细讲解;
  2. 参数属于常量,是在js文件执行时就生成固定了,我们只要知道它怎么取值就行;

言归正传,对于这五个赋值我们这里选择从前往后推。

第一次赋值

var qtX = ‘’;

第二次赋值

qtX = JSON['stringify'](MUX);中的MUX先写死,复制浏览器的值就行,这是Akamai 3.0核心部分大字典,后面再细究

第三次赋值

qtX = qO(45, [qtX, xQX[1]]);
看下xQXvar xQX = h9X || bb(); -> bb();所以咱直接处理bb();就行,把它代码拿下来
bb方法需要用到cookie bm_sz
在这里插入图片描述

然后看下q0,这里跟进入拿代码
q0方法本身是一个switch控制流,但是这里它不会执行很多次,只会进入LQ这一步,拿下这一步的代码即可,打上断点,执行到这里。
在这里插入图片描述

q0方法解混淆后
在这里插入图片描述

搞定后我们打印一下执行结果然后跟浏览器对比一下,没问题。
在这里插入图片描述
在这里插入图片描述

第四次赋值

qtX = hDX(qtX, xQX[0]);看下hDX方法,直接把它扣下来解混淆
在这里插入图片描述

搞定后我们打印一下执行结果然后跟浏览器对比一下,没问题。
在这里插入图片描述
在这里插入图片描述

第五次赋值

qtX = ''['concat'](CqX, ';')['concat'](kQX, ';')['concat'](qtX);看下CqXkQX,往上找就行
在这里插入图片描述

先看看var kQX = ''['concat'](WV() - MmX, ',')['concat'](0, ',')['concat'](0, ',')['concat'](ZOX, ',')['concat'](xBX, ',')['concat'](0);

WV:直接复制js文件提供的方法,或者返回当前时间
在这里插入图片描述
MmXZOXxBXWV有关,且它们是在qtX多次赋值操作前后
在这里插入图片描述

再看var CqX = InX(xQX);,直接拿下InX方法解混淆
在这里插入图片描述

到这里理论上已经拿到了请求体sensor-data的结果了,后面我们需要测试一下能够成功拿到数据。
在这里插入图片描述

请求测试

这是根据Akamai反爬流程开发的python请求代码

import re

import execjs
from bs4 import BeautifulSoup
from curl_cffi import requests


class DhlAkamai:
    index_url = 'https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343'
    data_api = 'https://www.dhl.com/utapi?trackingNumber=1232343&language=zh&requesterCountryCode=CN&source=tt'

    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            'accept': '*/*',
            'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
            'content-type': 'text/plain;charset=UTF-8',
            'dnt': '1',
            'origin': 'https://www.dhl.com',
            'priority': 'u=1, i',
            'referer': 'https://www.dhl.com/cn-zh/home/tracking/tracking-supply-chain.html?submit=1&tacking-id=1232343',
            'user-agent': 'user-agent'
        })
        self.session.cookies.set('cookieDisclaimer', 'seen')

    def get_crack_url(self):
        resp = self.session.get(self.index_url)
        de_url = re.search('type="text/javascript" {2}src="(.*?)">', resp.text).group(1)
        return 'https://www.dhl.com' + de_url

    def get_js_data(self, js_url):
        self.session.get(js_url)  # 代码可以不拿,但是要得到返回的cookie

    def post_js_data(self, js_url):
        with open('crack_00.js', 'r', encoding='utf8') as js_file:
            js_text = js_file.read()
            js = execjs.compile(js_text)
            sensor_data = js.call('gen_sensor_data', self.session.cookies.get('bm_sz'))
        print('sensor_data: ', sensor_data)
        resp = self.session.post(js_url, data=sensor_data)
        return resp

    def get_data(self):
        resp = self.session.get(self.data_api)
        return resp

    def str_cookie(self):
        return '; '.join([f'{k}={v}' for k, v in dict(self.session.cookies).items()])

    def run(self):
        js_url = self.get_crack_url()
        print(js_url)
        print('index _abck: ', dict(self.session.cookies)['_abck'])
        self.get_js_data(js_url)
        print('getJs _abck: ', dict(self.session.cookies)['_abck'])
        p_resp = self.post_js_data(js_url)
        print('posJs _abck: ', dict(self.session.cookies)['_abck'])
        print(p_resp.status_code, p_resp.text)
        d_resp = self.get_data()
        print('gData _abck: ', dict(self.session.cookies)['_abck'])
        print(d_resp.status_code, d_resp.text)


if __name__ == '__main__':
    dhl_akamai = DhlAkamai()
    dhl_akamai.run()

crack_00.js文件就是扣出来的js代码文件,只不过需要封装出来一个gen_sensor_data方法。

执行后发现是可以成功拿到数据的。
在这里插入图片描述

结尾

这篇先写到这,主要是介绍了Akamai的反爬内容和破解入口,以及对请求体sensor_data的逆向分析,至于核心部分写死那块我们后面再出博客讲解。

其实最后能拿到数据也是网站风控没那么严格,细心的朋友会发现代码写死了三个位置,而那三个位置其实是很重要的,但凡你随便改了一个都无法请求成功。

我研究过一段时间,虽然能扣出它们的生成算法,但是这个算法只能适用当前JS文件提供的代码(或者一部分),已更新JS文件就不适用了,归根结底我觉得是每个JS文件会生成一个大数组,这三个位置的生成都需要用到它,而每个JS文件的大数组长度和每次取值都不一定相同,也就导致无规律性了。

估计是还没研究透,后续有时间再战…


http://www.kler.cn/a/503367.html

相关文章:

  • HTTP详解——HTTP基础
  • Python----Python高级(函数基础,形参和实参,参数传递,全局变量和局部变量,匿名函数,递归函数,eval()函数,LEGB规则)
  • 【Linux】从零开始:编写你的第一个Linux进度条小程序
  • 开发人员学习书籍推荐(C#、Python方向)
  • VUE3 自定义指令的介绍
  • pandas与sql对应关系【帮助sql使用者快速上手pandas】
  • 基于微信小程序的游泳馆管理系统设计与实现(LW+源码+讲解)
  • 平滑算法 效果比较
  • Linux简介和环境搭建
  • 005__ubuntu终端说明、linux命令
  • 《重生到现代之从零开始的C++生活》—— 入门基础语法
  • 【算法】复制含有随机指针节点的链表
  • 源码安装httpd2.4
  • 【AI游戏】基于OpenAI打造自动生成剧情的 Python 游戏
  • 14.STM32F407ZGT6-SPI
  • 什么是 XSS(跨站脚本攻击)?
  • 理解STC15F2K60S2单片机的最小电路
  • 当comfyui-reactor-node 安装失败urllib.error.HTTPError: HTTP Error 403: Forbidden解决方法
  • 空指针:HttpSession异常,SpringBoot集成WebSocket
  • tmux 中鼠标滚动异常:^[[A和^[[B是什么以及如何解决
  • 51c~Pytorch~合集4
  • 【按钮防抖】el-button和普通按钮防抖,点击一次禁用一秒再恢复
  • 9分布式微服务架构
  • Windows安装HDC工具及鸿蒙手机开启HDC调试
  • Java开发关键步骤:Windows与macOS系统环境变量详细配置指南
  • 一种ESP8266+OLED时间天气显示