目录
声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!
目标
目标:某乎的文章评论
网址:aHR0cHM6Ly93d3cuemhpaHUuY29tL3NlYXJjaD90eXBlPWNvbnRlbnQmcT0lRTglOEIlQjklRTYlOUUlOUM=
运行结果
知乎的x-zse-9还是三年前搞过
现在有空重新梳理一下流程,前面几部都大差不差,关键是后面函数的补环境.
x-zse-96参数分析
1、在加载包那边搜索评论,就可以看到加载评论的包啦,还是和以前一样,是x-zse-96加密在里面,其他的可以固定
2、很好奇不需要x-zse-81参数加密,但是影响也不大在里面,x-zse-81也是固定的
3、先全局搜索x-zse-96,不行的话可以hook头部或者下xhr
4、可以看到有两处有搜索到x-zse-96 ,全部打上断点后往下滑,断住后把另一个取消掉
5、发现就是t0这个结果,此时只需要分析,可以往上找
6、参数就需要注意,其他的参数没什么重要的
te:是请求的链接
tS:是cookie的d_c0
7、跟ed函数里面就可以看到
ta、tu、tc、tf、td、tp就不再过多的分析了
8、看看t8函数加密的,跟栈进去
发现调用挺简单的,可以合并在一起
t8 = function(t, e) {
return void 0 === e && (e = 4096),
!!t && function(t) {
return new Blob([t]).size
}(t) <= e
}
9、重点还是研究 下面的signature参数是怎么样加密的
可以看的出ty是一个很标准的md5加密的库(加密后的数据是32位的话,可以怀疑他是不是md5加密)
10、继续跟栈tJ(ti).encrypt这个加密函数
可以看到__g._encrypt(encodeURIComponent(tt)) 实现了加密在里面
可以看到上面有很长的字符串,这个就是VMP加密在里面
VMP就是把你的JS代码转换成一长串的字符串,然后再通过switch....case进行还原一系列操作,今天的课题是属于一个简单的
10、往上找,很容易就会发现是一个webpack加密在里面
// webpack加载器
var fff;
!function (e){
var h = {};
function f(n){
if(h[n])
return h[n].exports;
console.log('-->',n);
var u=h[n]={
i:n,
l:!1,
exports:{}
};
return e[n].call(u.exports,u,u.exports,f),
u.l=!0,
u.exports
}
fff =f;
}({
//函数
})
11 、在另外的一个页面里面创建脚本
这个错误是告诉你缺少模块,把需要的模块补上去即可
12、浏览器运行的已经出来了结构
13、在nodejs环境中运行结构,就会发现是报错的 TypeError: __g._encrypt is not a function
其实是缺少了浏览器环境导致js走错了
这里我用的是jsdom补环境,需要安装jsdom这个库,直接调用npm命令就行了
npm install jsdom
14、再补上以下环境,需要自己把地址换了
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`,{url:"aHR0cHM6Ly93d3cuemhpaHUuY29tDQo="});
window = dom.window
location = window.location
navigator = window.navigator
document = window.document;
history = window.history;
screen = window.screen;
15、补完发现可以生成结果,但是带上这个参数的请求是拿不了数据的,还缺了环境
16、回到浏览器里,可以发现多次加密同一组数据竟然结果不同,可能是时间戳,也可能是随机数,试着hook了一下随机数,发现结果不变了,证明是随机数的问题,可以在node里hook随机数再调试,直到出现和浏览器一样的结果
会发现nodejs里面和浏览器加密出来的参数是不一样的
补环境
17、直接运行结果不一样,也没有任何提示,可以加上proxy代理再调试
proxy_ = function(func){
return new Proxy(func,{
set(target,property,value){
console.table([{"类型":"set","调用者":target,"调用属性":property,"设置值":value}]);
return Reflect.set(...arguments)
},
get(target,property,receiver){
console.table([{"类型":"get","调用者":target,"调用属性":property,"获取值":target[property]}]);
return target[property]
},
})
}
window = proxy_(window)
location = proxy_(location)
navigator = proxy_(navigator)
document = proxy_(document)
history = proxy_(history)
screen = proxy_(screen)
18、观察代理会发现Document的Symbol 浏览器里返回的是'[object HTMLDocument]',node里返回的是[object Object]
浏览器
node的
19 、可以在原型里修改这个方法
let ObjectToString = Object.prototype.toString
Object.prototype.toString = function () {
if (this.constructor.name === 'Document') {
return '[object HTMLDocument]'
}
return ObjectToString.call(this, arguments)
}
在原型里面判断name是Document的话就返回[object HTMLDocument],其他的不变
20、可以发现补完document,但结果还是不对
21、安装canvas这个包
npm install canvas
22、 在刚才定义原型的方法里面添加
let ObjectToString = Object.prototype.toString
Object.prototype.toString = function () {
if (this.constructor.name === 'Document') {
return '[object HTMLDocument]'
}
else if(this.constructor.name === 'CanvasRenderingContext2D') {
return '[object CanvasRenderingContext2D]'
}
return ObjectToString.call(this, arguments)
}
23、再次运行会告诉你 _resourceLoader,浏览器里是undefined,把这个补上后再次运行
24、刚才那处已经过了,_sessionHistory,浏览器也是undefined,接着补上
25、global这里报错,node的全局变量是global,浏览器里面没有global,补一个alert = window.alert
26、__proto__还在出错,这里其实是再次对tostring进行了了检测,打印一下发现是大Window的问题
补一个判断window的
let FunctionToString = Function.prototype.toString
Function.prototype.toString = function () {
if(this.name === 'Window') {
return 'function Window() { [native code] }'
}
return FunctionToString.call(this, arguments)
}
27、运行一下就可以看见结构和浏览器一样啦 -->看16步操作
28、最后就可以记得把代码的hook随机数的删除
总结
1、出于安全考虑,本章未提供完整流程,调试环节省略较多,只提供大致思路,具体细节要你自己还原,相信你也能调试出来.
2、本人写作水平有限,如有讲解不到位或者讲解错误的地方,还请各位大佬在评论区多多指教,共同进步
3、如果这篇文章对你有帮助,就点赞、关注、收藏、三连击一下
4、本篇分享到这里就结束了,欢迎大家关注下期,我们不见不散☀️☀️😊