同花顺js逆向cookie

案例网址:https://q.10jqka.com.cn/

逆向目标:

cookie 中的参数 v,具有时效性,刷新一下就会发生变化:

所以不好做长期的爬虫,需要进行逆向解密。

进行逆向的第一步是要找到其生成逻辑的地方。可以尝试全局搜索 cookie,一个一个找。

可以找到最后一个是其生成逻辑的文件:

这是一个立即执行函数。上面有个全局变量,从变量名可知是时间,所以 cookie 的变化可能跟这个有关系。

这是一种方法,需要一个一个排查,有点耗时耗力。

最好的方法就是 hook 注入。可以用油猴插件,也可以用浏览器自带的脚本片段。

// 自执行函数
(function(){
var cookie_cache = document.cookie;
Object.defineProperty(document, 'cookie',{
get:function(){ // 访问cookie触发
console.log('Get cookie');
return cookie_cache;
},
set:function(val){ // 设置cookie触发
console.log('Set cookie',val);
debugger
return cookie_cache;
},
});
})();

cookie 是 document 对象的一个属性。所以这个 hook 脚本意思就是定义一个方法用来监听 document 的 set-cookie 方法,如果监听成功,则打上断点。

运行一下,然后点击其它页,发生断点。

然后查看旁边的作用域和调用堆栈。作用域就是当前的结果,而作用堆栈就是出现这个结果所经历的路程。

调到 o,发现 cookie 传给了变量 t:

这里只是表面上的函数,继续往下查看。

发现 cookie 传给了 n,而 n=rt.update()

在控制台里运行一下这个方法。

发现就是生成 cookie 的方法。查看这个方法:

跳转到对应的页面:

发现是个 D 函数,里面返回一个 O 函数。

网上找找,发现 O 函数就在上面。

ok,再看间距发现这不是一个单独的函数,而是一个方法。跑到上面,发现是一个立即执行函数。

那么也就是说 cookie 的具体生成逻辑就在这个立即执行函数里。将其整体复制到编辑器中进行下一步操作。

在编辑器中运行这段代码肯定会报错。原因就是 nodejs 和浏览器相比缺少 dom 和 bom 环境。

什么是 dom 和 bom?

dom 就是 document 对象模型,而 bom 就是 browser 对象模型也就是 window 对象。

两者最本质的区别就在这里,所以要想运行成功必须要补上缺失的环境。

补上 document 对象,并且尝试打印他的 cookie 属性。记住一定要保存,然后运行,不出意外报错:

window 对象为定义,那么继续补 window 对象:

保存并运行,出现另一个错误:

r[51].getElementsByTagName 这个函数并不知道是什么。属于那个对象。

这里就需要环境代理了。正所谓缺啥补啥。

环境代理:

// 环境代理
function getEnvs(proxyObjs) {
for (let i = 0; i < proxyObjs.length; i++) {
const handler = `{
get: function(target, property, receiver) {
console.log("方法:", "get ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);
return target[property];
},
set: function(target, property, value, receiver) {
console.log("方法:", "set ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);
return Reflect.set(...arguments);
}
}`;
eval(`try {
${proxyObjs[i]};
${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
} catch (e) {
${proxyObjs[i]} = {};
${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
}`);
}
}
proxyObjs = ['window', 'document', 'location', 'navigator', 'history', 'screen']
// ....
getEnvs(proxyObjs);

具体代码意思就是监听方法的对象类型。能监听的对象可以设置。

目前设置有:[‘window’, ‘document’, ‘location’, ‘navigator’, ‘history’, ‘screen’]

保存再次运行:

这样就知道 getElementsByTagName 这方法是哪个对象里的了。

那么缺啥补啥,补在 document 对象里。

但是还有个未知,就是这个方法有没有参数,如果有那是什么。

所以在补这个方法的时候可以让其自己输出是什么方法。

运行:

传参是 head,那么返回的是什么呢。

这个方法所有的条件知道了,就可以在浏览器的控制台里=查看返回的是什么。

返回的是一个对象。那么也照样返回对象,返回个空对象看看有没有报错。

这回发现是另一个错误,从错误和环境代理的输出可以看出不是空对象导致的。

同样的将 createElement 放入 document 对象里,并补上方法。

参数 div,同样的返回到浏览器的控制台查看

那么添上。并运行。

还是同样的报错,说明还是有问题。

看看具体的问题:

去浏览器进行局部搜索看看,这里面到底是什么问题。

然后打上断点,看看:

在控制台里尝试看看这一行代码是什么意思

t + X in s[66].createElement(s[171])

t + X 是 onwheel,意思是 onwheel 在不在 s[66].createElement(s[171]) 里面,如果不在则执行 s[172]in c[66] ? u[173] : v + K + f + Q 这是三目运算符。

也就是说 createElement 这个方法返回的东西或许不是单单的字符串而是一个属性。那么在控制台里查看这个方法返回的所有属性,用 dir 函数。

果然是属性,那么在属性里找 onwheel,因为这一行代码主要是检测 onwheel。

onwheel:null,那么就填进去。

在运行:

出现新的错误

跟之前一样填进去。

发现并没有错误了,但是 cookie 并没有正常输出。

这是因为还有些未定义的东西没有进行添加。

一个一个来,首先是 head,这是一个属性所以在浏览器里查一查。

是一段字符串,应该没用。可添可不添

下一个是

添到 window 对象里。去浏览器查一下看看是方法还是属性。

看来是方法,那么跟 document 一样。

保存运行,出现新的错误:

由输出可以看出这里的 addEventListener 是 document 对象的,那再补一下。

再次不报错,继续补未定义的东西。

下一个:

去浏览器里搜搜。

navigator 是个对象,这里是它里面的 plugins 未定义,那么添进去。

接下来是

同样的去浏览器里看看

本身就是未定义,那么就不用去管它了。

下一个

又是字符串,不用管。

但是如果不添加,那么好像不好输出下面一些未定义的东西,导致最后报错。这里还是放入吧,随便定义一个类型就行了,貌似没什么影响。

那么运行之后又出现了一个新的未定义的东西:

去浏览器里找,并·添上。

这样没有属性值为未定义的了,接下来保存运行,发现没有报错,但是依旧没有 cookie 输出。

这是因为 cookie 被封装在立即执行函数里,需要手动拉个接口。找到一开始定位到的 O 方法,在下面拉个接口,怎么拉都行。

最后运行输出 cookie:

去 python 编辑器里输入 cookie

看看能不能用:

先让 v 为空:

运行结果如下:

什么都没有。

再添上刚刚获取的 cookie

发现完全不一样了。说明有效。

那么如何将 js 代码运用到 python 里呢,有两种方法,一种是用 execjs 库来运行 js 代码,另一种是用 subprocess 库来运行。第一种较难,容易报错。第二种代码如下:

import subprocess
import re

# 使用 subprocess 调用 Node.js
try:
# 调用 Node.js 并捕获输出
result = subprocess.run(
[r'D:\爬虫工具\JavaScript逆向\node.js\node.exe', r'D:\python_for_practise\JsReverse、\THSjsCookieReverse.js'],
capture_output=True, # 捕获输出
text=True, # 将输出作为字符串处理
encoding='utf-8'
)
# 使用正则表达式提取 cookie
cookie_match = re.search(r'cookie is:(.*?)\n', result.stdout)
if cookie_match:
cookie = cookie_match.group(1).strip() # 去除多余空格
print(cookie)
else:
print('error')
except Exception as e:
print(f"Error: {e}")