本篇文章仅用于交流与学习,严禁用于任何商业于非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!
感谢关注!!您的关注和点赞就是我的动力❤❤
文章目录
01.逆向目标
目标网址:atob("aHR0cHM6Ly9oYW93YWxscGFwZXIuY29tLw==")
近期也是惊奇的发现一个4K高清壁纸网站,这对于高清壁纸爱好的我来说无疑是巨大的诱惑,但该网站下载高清图片需要进行滑动验证,并且对IP有着限制,在不登录的情况下,只能获取下载保存5张高清壁纸。这种情况对我也是重大打击,那么在我的不懈努力下,也是稳稳拿下4K图片的下载验证逻辑。
下面是对该网站滑动验证的算法分析。
02.抓包分析
首先第一步肯定是对页面进行分析。
进入主页面我们很明显的可以确定,该网页是一个动态网页,尝试翻动两页。对数据包进行分析,我们发现,每一页的数据包 请求参数 都是经过加密的,并且 响应 的JSON数据中关于壁纸的数据也是经过加密处理的(暂时不必理会加密方式,先对网页进行分析)
请求参数加密:
响应体加密:
其次,对单张壁纸的4K高清图下载接口进行分析。
当我们点击下载图片时,出现滑动验证的两张图片,并且NetWork面板中也是出现了slider的数据包(GET请求,并且不携带任何参数),通过字面意思我们可以知道它是滑动验证码的数据。
那么,我们在我们滑动滑块的时候,可能会发送一个携带滑动轨迹参数的请求。但是我们发现这里发现他的请求参数只有这么一捏捏,正常来说滑动轨迹的参数不会只有这么点,我们先继续往下进行分析。
果真如此!当我们进行滑动验证的时候,发送了一个POST请求(validate)进行验证,并且payload携带了经过加密处理的data参数(这里我猜测可能只传入了滑动距离(px)),响应体也是我们猜测的结果。
同时,当滑动验证成功后,出现了一个新的POST请求(getCompleteUrl),通过URL路径字面意思我们可以知道,这是获取真实完整的4K高清壁纸下载地址。
这个请求也是携带了一串经过加密处理的data参数,真实完整的4K高清壁纸的下载路径也藏在经过加密处理过后的data中。
03.逆向过程
经过上面的分析处理,我们就可以大致的确定了自己的爬虫逆向思路。先对图片列表页面进行加解密获取真实的数据(其中肯定有每张壁纸的ID),拿到所有壁纸的ID就可以对每张图片的4K高清图进行滑动验证发送请求下载(IP限制,需加代理)。
由简入深,先完成单张4K高清壁纸的逆向,剩下的都是相同的逻辑。
先对滑块验证码的两张图片数据进行解密(slider响应),全局搜索data肯定不行,这里我选择搜索slider请求路径(/pc/public/slider)。
很幸运,直接就找到了,并且通过在JSON.parse()出打上断点,可以发现传入的参数 t 正是响应体中的data数据。
我们在控制台输出断点处的 i().cryptoValue(t) 可以清楚的看到,传入的 data 经过解密后,正是两张验证码图片的 Base64 编码 格式。
注意:数据中有两张图片 "originalImage" 和 "sliderImage",并且验证码图片是webp的格式(可忽略),数据中的 "random" 目前发现暂时没有什么用处 。
我们进入到cryptoValue()函数中,逐步调试,发现解密的地方就在这句js中,通过对这句js进行分析,它使用了apply方法调用了函数 I ,并且将我们需要解密的数据传了进去,那么我们可以确定,解密的关键就藏在 I 这个函数的内部!!
我们进入到I函数内部,可以一眼看到是一个标准的AES解密,并且密钥key和初始化向量Iv都是固定的字符串。
它将传进来的加密数据先进行的16进制的转换,然后进行AES的解密,最后对解密完的数据通过正则的方式去除\0后面的多余字符,这样我们就可以通过python模拟,解密出两张滑块验证码的Base64数据。(同时,我们也可以看到下面也是相同的AES加密,说不定请求参数的加密也是在这个地方)
def aes_decrypt(b64_data:str):
b64_bs = bytes.fromhex(base64.b64decode(b64_data).hex())
key = "68zhehao2O776519".encode("utf-8")
iv = "aa176b7519e84710".encode("utf-8")
aes = AES.new(key=key,mode=AES.MODE_CBC,iv=iv)
return json.loads(unpad(aes.decrypt(b64_bs),AES.block_size).decode().split("\0")[0])
验证码图片下载:
我们尝试性在加密处打上断点,滑动验证码图片发送下一个请求,发现发送验证请求时,果然断到了这个AES加密的地方。并且当前传入的值W正是我们滑动的像素距离(px),并且它的加密同样使用AES加密,Key和Iv也是相同的。
我们通过python模拟相同加密逻辑,传入参数 "172" 可以得出相同的加密结果,那么我们就可以向第二个接口(/pc/public/slider/validate)发送请求,进行滑块验证。
def aes_encrypt(data:str):
key = "68zhehao2O776519".encode("utf-8")
iv = "aa176b7519e84710".encode("utf-8")
aes = AES.new(key=key,mode=AES.MODE_CBC,iv=iv)
enc_data_bs = aes.encrypt(pad(data.encode("utf-8"),AES.block_size))
enc_data_str = base64.b64encode(bytes.fromhex(enc_data_bs.hex())).decode()
return enc_data_str
编写算法求出滑块需要滑动的距离,进行加密就可以发送请求得到结果。
滑块验证结果:
紧接着就是向最后一个接口发送请求(/common/file/getCompleteUrl),获取到真实的4K壁纸下载链接数据。
getCompleteUrl的参数有很大一串, 我们在上一个请求的响应没有得到数据,就需要在JS代码中读取它的加密算法。
搜索接口(/common/file/getCompleteUrl)我们可以发现请求参数data是通过向s函数传入一个参数G得到,而G又是通过ES6之后的模版字符串拼接得来的。
通过观察这个js语句我们发现:参数G是通过变量G和P.wtId以及两次urlencode编码后的Fe拼接得来。而变量G是一串固定的字符串,而P.wtId就是壁纸Id,最后的Fe来自上面的函数。
观察上面的Fe的来源,我们发现它和下面一样调用了s这个函数。通过调试发现这也是一个加密函数,并且传入函数的对象中wtId就是壁纸Id,token是请求标头里面的token(随机生成的),soType是固定值。
我们猜测这个s函数是不是也是我们之前找到的AES加密。通过进入到s函数内部进行调试,我们果然进入到了熟悉的地方,传入的是那几个参数,return的是我们需要的最后一个Fe。而加密算法我们已经写过了,现在只需要把G这个参数搞定就可以了。
通过python模拟它的算法,进行拼接得到参数G(Fe变量需要通过两次quote处理),然后对字符串参数G,再进行一次转JSON字符串处理(JSON.stringify),传入到函数s中进行加密,携带到请求参数中。
注意:这里是有两个双引号的(我也踩了坑,纠结了好久,在好友的谩骂下发现的)(看在作者被骂的这么可怜的份上,给作者点个赞,点点关注喽!❤)
def third_request(img_id):
dic = {
"wtId":img_id,
"token":headers.get("token"),
"soType":1
}
fe = aes_encrypt(json.dumps(dic,separators=(',', ':')))
g = f"\"https://x.haowallpaper.com/link/common/file/getFileImg/{img_id}.png?data={quote_plus(quote_plus(fe))}\""
param = aes_encrypt(g)
resp = session.post(f"https://haowallpaper.com/link/common/file/getCompleteUrl?data={quote_plus(param)}",headers=headers)
return resp.json()
最后获取到请求的加密参数,就可以发送请求得到真实的4K高清壁纸下载图片链接了(当然是加密过后的,对数据进行AES解密即可得到)
同样的,壁纸的 wtId 在壁纸列表就可以得到(/pc/wallpaper/getWallpaperList),加解密用的都是同一套逻辑。
编写不易,兄弟们点点赞!!点点关注!!你们的支持,就是我的动力o(* ̄︶ ̄*)o