题目链接:www.python-spider.com/challenge/3
题目的最终目的是求100页数据之和
一 分析
-
分析目的
加密参数是啥?参数在哪里?
-
分析过程
找到数据所在的请求包
通过curl转python本地请求测试
curl地址
import requests
headers = {
"accept": "application/json, text/javascript, */*; q=0.01",
"accept-language": "zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"origin": "https://www.python-spider.com",
"priority": "u=1, i",
"referer": "https://www.python-spider.com/challenge/3",
"sec-ch-ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"x-requested-with": "XMLHttpRequest"
}
cookies = {
"HMACCOUNT": "9E91DB5F595FBD76",
"sessionid": "r8imncwfgycnhzwdvk6h1y33tfel9098",
"Hm_lvt_337e99a01a907a08d00bed4a1a52e35d": "1720796710",
"no-alert": "true",
"sign": "1721146993~YWlkaW5nX3dpbjE3MjExNDY5OTI2Nzg=|d5d049538309316e16ce5b63553af0a7",
"m": "091b9eb11943954e1a0e99b9ff7713b4|1721198310000",
"Hm_lpvt_337e99a01a907a08d00bed4a1a52e35d": "1721198312"
}
url = "https://www.python-spider.com/api/challenge3"
data = {
"page": "1"
}
response = requests.post(url, headers=headers, cookies=cookies, data=data)
print(response.text)
print(response)
这里就只有cookie参数有嫌疑,一个一个的删除就能看到只有m参数才是有用的
3.分析结果
header中加密的m参数才是关键参数
二 找加密参数
找加密参数的时候记得浏览器保持干净或者以访客模式访问
这里可以看大同一个请求请求了两次,而且在第一次请求完以后就发现m参数就已经有了,所以可以判定m参数一定是在这个请求就生成了,但是第一次的请求结果在浏览器中是看不到的,所以可以通过抓包软件或者通过curl第一次的请求就可以发现返回了一个script标签包裹的js
由于是script文件的加载可以通过监听script的加载在浏览器中找到该js文件
刷新网页 时候记得将m这个cookie参数删除,这里我已经删除了
开始刷新网页!!!!!!!!!!!!!!!!!!!!
这个就是我们要的文件了
代码收缩后就只有四大段,定义了一个a列表,两个自执行的函数
面对这几段展开的js代码,是不是开始懵逼了!!!!!don't be 担心
两种方法可以快速定位
1 2.1 猜测
> 既然是cookie 那么肯定是要写入的, 记住document[cookie] = xxxx,或者window这两个关键词
2 2.2 hook
> 既然我们知道加密参数是从这个js运行结束后生成的,那么在js文件顶部打上断点
Object.defineProperty(document,'cookie',{
set:function(val){
debugger; return val
}})
通过下一个断点的运行就能看到上一个断点的运行位置
document['cookie'] = xxx
现在开始扣代码把!!!!!!!!!!!!
现在我们知道第四段是在生成cookie,那前面三段是干啥了呢,先扣下来到本地运行一下
发现报了一个莫名其妙的错误
这个可以看到是在g["setCookie"]调用的
这就意味着 j 这个参数结果是false,这里实际调用的就是 h 这个函数
这里由于本地是出现了错误的,但是线上是没有的,所以我们可以在 j 这个参数的位置打上一个断点
这就能看出来线上返回了一个true,但是线下确实一个false
k['test'](g['removeCookie']['toString']())
这段代码意思比较简单,用正则判断了一个字符串
g['removeCookie']['toString']()
// 线上
"function(){return'dev';}"
// 线下
"function() { return 'dev'; }"
可以很明显的看到是有区别的,线上没有换行符,修改一下线下的代码
缩成一行在运行看结果就发现没有问题了,至此就解决了第一个小问题了
接着来!!!!!!!!!!!
剩下一个b会不会有问题 我们可以简单的测试调用一下,全局中随便找一个调用了$b函数的测试
调用结果如下,很明显也有某个地方和线上不一致了,但是不知道是哪里,但是这个错误很明显的就是有什么东西在吃疯狂吃内存,如果是要吃内存简单的猜测可能就是死循环之类的
这里看到有两处使用了for循环
自执行函数内部
f 函数原型上
方法一 认真排查篇
由于吃内存的话你的断点会执行很久,所以可以先在线下debug一下
直接在函数的下一步打断点看是否可以马上执行到该断点,测试发现可以,那就可以排除上面那个for的问题了
断点直接点在第二个for循环的位置
发现这里一直在跑,
this['fnwxbJ']['length'] 这个长度每次循环都会更新 导致无限循环
问题找到了,为啥线下会出这个问题呢?还是从最新的源头开始和线上做对比
这个死循环的函数是从上面开始调用的,所以在最开始调用的地方打断点查看
就问你熟悉不?所以也应该知道如何修改了撒
修改后 运行不卡顿,很顺畅!!!!!!!!!!
最后就是看cookie是怎么生成的了
可以直接抠出来
开始补代码 缺啥补啥
当你补完M在补完B函数 时候 发现运行又卡住了,这个时候你很气!!!!!!!!!,我也很气!!!!!!!!
气没办法还是得看,打上断点一个一个看
这个M函数的大致意思就是有a0这个列表这么多事件,每个循环拿一列表一个数,每个数都有对应的事件,然后就执行呗,就这么回事是,我们debug每个case事件都看一下 哪里会存在死循环就行,不必深究
执行到这个的时候可能会有点懵逼,线上可以执行一下看看会是什么结果,但是只要是这里没有卡住就可以不用管的
发现是undefined
调式到下面这个事件的时候就要注意一下了 他修改了console对象,当你打印日志的时候就会死循环执行了,直接干掉他
最后在缺啥补上就基本ok了 最后运行一下看看结果
发现不是m 而是munderfined 为啥 虽然后面的结果是对的
找到m生成相关的核心js
这就能看到实际调了上 只有 V(Y) 才是最关键的,但是本着研究到底的原则,还是可以看下把 顺带看下 人生苦短,何必python? 是在哪儿出现的 underfined 实际上是调用的M()
就是这儿了,好了
完结 撒花 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!