js逆向学习记录某真气网
项目场景:
第一次写不知道能否表达清楚
在B站上学习JS逆向,结果学到最后一个练习项目时。因为之前学习得人把人家网站搞崩了。到了我来爬取得时候,这个网站反爬已经大幅度升级了,爬取难度急据升高。
声明:JS逆向成功后我就没有继续爬了,没有攻击对方服务器,如果有什么违规地方,立马删除。(网站url:私聊吧,不然又崩了可不太好)
相关工具
语言:
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
node.js v14.16.0
第三方库:
requests execjs json subprocess partial
开发工具:pycharm,chrome
找到可疑点:
提示:JS逆向主要是在开发者模式中,断点调试找到加密得地方
进入开发者模式模式:
理论上这段是不用说的,但这个网站反爬技术很完善
如图该网站禁用了右键和F12
所以可以通过浏览器右上角三个点---->更多工具---->开发者工具,进入
无限debugger
进入之后就会发现自动进入到了断点调试的状态,是因为下面这据代码
(function anonymous(
) {
debugger
})
破解方法:选择"debugger"前的行号,右键弹出下图窗口。可以选择1、或者2、
选择1、要在以后输入一个false。选择2、直接就可。两者都要再点击“跳到下一个断点”
具体参照:web反调试之无限debugger饶过方案汇总 - 996station
到这里还不行,你会看到下面的情况
它的的原理是,监视页面的窗口大小,小于某个值就会显示以上内容。破解方法很简单,如下。
两种找到可疑点的方法:
第一种全局搜索fromData参数名:
chrome抓包找到对应的请求
第二种,通过按钮查看绑定的事件,一层层解析
接下来可以看Python爬虫从入门到大神到JS逆向再到APP逆向 持续更新中~_哔哩哔哩_bilibili
最后还是到这
逆向分析
从这里可以看到FromData来源与data:{hT5i4Eu2x:pwG2gyr}。pwG2gyr是由“
var pwG2gyr =pIywvl1oiy(mvpDrF875,ooISoR2Gom);”得到的。所以关键函数就是pIywvl1oiy()如下
return function(method, obj){var appId = '2b3d1754aac69d4e84b3bb5620ba578e';var clienttype = 'WEB';var timestamp = new Date().getTime();// console.log(method, obj,ObjectSort(obj),appId + method + timestamp + 'WEIXIN' + JSON.stringify(ObjectSort(obj)));var param = {appId: appId,method: method,timestamp: timestamp,clienttype: clienttype,object: obj,secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))//加密处};param = BASE64.encrypt(JSON.stringify(param));//加密处param = AES.encrypt(param, ackvMDije6Ku, aciiBrTMXFqu);//加密处return param;
};
})();
可以看出影响结果地方是“hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))“和 ”param = AES.encrypt(param, ackvMDije6Ku, aciiBrTMXFqu);“
而timestamp、clienttype是不变的,method,obj是我们自己输入的。appId,ackvMDije6Ku,aciiBrTMXFqu乍一看是写死的,其实不是。如果你多次抓包就可以发现它们三在每个文件中的值是变化的,所以必须实时获取。这处很坑,如果不注意就会落入陷阱,觉得一切都是对的但结果就是出不来。appId来自当前函数,ackvMDije6Ku,aciiBrTMXFqu出现在文件开头,如下。
你有没有发现有几个参数的名字特别怪,例如ackvMDije6Ku,pIywvl1oiy,mvpDrF875等。这是因为这些关键字,甚至包括这个文件都是被动态加密的。在这其中有一个很重要的关键字FromData的键,如下图。这个值不能写死,他又时间限制,一段时间后会失效。必须实时获取。见前文这个关键字在Data里。
总上如果我们想要成功加密,就必须动态获取以上几个参数。而在写参数都来自于那个JS文件。这个文件的url,通过左侧的全局搜索得到https://www.xxxxx.cn/js/encrypt_e6Q87gvKk99t.min.js?v=1658658361
而这个URL也是实时生成的如这e6Q87gvKk99t和v=1658658361。后面的是时间戳,而前面的是对方后台生成的,我们无法自己生成。但是学过WEB前端的都知道,js文件一般放在html文件的<script>标签中。可以通过HTML文件来找到JS文件。想到这点就可以通过全局搜索js/encrypt这就会出来几个HTML文件,随便选择一个都行。例如我选择"https://www.xxxx.cn/html/city_detail.php?v=1.10"。接下来就是通过requests发起get请求等到HTML文件,在通过xpath来获取JS文件URL,然后获取URL文件并解析想要的参数。
s = requests.session()headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36","Referer": "https://www.aqistudy.cn/html/city_detail.php?v=1.10",}get_js_value_page=s.get(url="https://www.aqistudy.cn/html/city_realtime.php?v=2.3",headers=headers).texttree=etree.HTML(get_js_value_page)get_js_value=tree.xpath("//script/@src")[1]js_url="https://www.aqistudy.cn"+get_js_value.replace("..","")response_funEnData=s.get(url=js_url,headers=headers,allow_redirects=False).text
但是得到的JS文件长下面这样,跟想要的文件大相径庭。通过网上翻阅资料,发现他是一个JS混淆的一种方式。通过eval(),是利用了eval()函数的特性,它跟Python里的eval函数差不多,能把字符串解析成代码来执行,具体见:JS逆向 | 面向小白之eval混淆 - 知乎
可以通过console.log方式破解。把"eval"替换成console.log()就可以了。如下
虽然可以但console.log()输出到控制台上。不能直接复制给变量。这里我想到了C语言的printf,他本质就是将一个字符串用sys_write()写道显示器那块显存里。log()也一定是这样,但如果直接写入到显存,那应该和之前看到的一样是eval(.......)。所以他一定在写道显存中调用了其它方法。网上搜了下它的源码,果然它调用了toString()方法。接下来就有点饶了。如下
eval("var data="+enData.replace("eval","")+".toString();");
enData就是那个字符串eval(.......),先是让"eval"消失,然后调用toString()方法。在这里会发现我是拼接了".toString();"。因为enData原本就是字符串,在调用toString()是无效的。但是如果是拼接了".toString();他们就在一个层次了。然后这里有人就要问了,字符串有什么用,又不是语句。对字符串是常量,但不代表不能执行,eval不就是把字符串解析成代码来执行吗。eval消失了又回来了。又发现我在前面拼接了"var data="提前把他赋值了。因为里面这个东西有点奇怪,如果你不赋值给其它变量,他会先执行自己然后就报错,不会执行toString()。我也不知道为啥。然后就可以得到源码了吗???????
当然不是,之后多次请求发现toString()后并没有得到源码,而是下面两种情况。
然后全局搜索dswejwehxt()发现是
//将加密函数进行解密,此处DeFun是我自己起的,原来叫做dswejwehxt
function DeFun(enData) {var b = new Base64();return b.decode(enData);
}
这个函数就是把加密的函数进行解密。也就是要提取(我是用正则提取的)中间引号里的字符串,传给DeFun()之后会有概率得到源码,但大多数情况是
乍一看和之前的一样,好像是被toString()解密了,然后又被DeFun()加密了。到这里我也很崩溃,以为错了。不想放弃,因为明明有成功得到源码的情况。之后又重复了上述行为,就真的得到源码了。因为文件是对方动态生成的,所以得到源码的路径又多条,如下
function getDeData(enData){//原始的--》toString-->dswejwehxtdswejwehxt-->DeFun(DeFun-->fun-->toString-->dswejwehxt--DeFun-->finalDefuneval("var data="+enData.replace("eval","")+".toString();");var myRe=/'(.*?)'/g;//dswejwehxtdswejwehxtvar count=data.split("(").length;switch (count){case 3: //dswejwehxtdata=data.match(myRe)[0];//DeFundata=DeFun(data);if(!data.includes("eval"))break;eval(" data="+data.replace("eval","")+".toString();");break;case 4://只有eval中才用要解密if(data.includes("eval")) {data = data.match(myRe)[0];//DeFun(DeFundata = DeFun(DeFun(data));if(!data.includes("eval"))break;//funeval(" data=" + data.replace("eval", "") + ".toString();");//dswejwehxt//只有eval中才用要解密的字符串if (data.includes("eval")) {data = data.match(myRe)[0];//DeFundata = DeFun(data);}}break;}return data;
由此就得到了/js/encrypt文件源码,之后在通过正则匹配到那几个动态加载的数据,如下
function getEncryptData(data){data=getDeData(data);var encryptData={param:"",appId:"",AESList:[]};//获得AESvar myRe=/"(.*?)"/g;encryptData.AESList=data.match(myRe);//获得fromdatamyRe=/data:\s?\{(.*?):/g;data.match(myRe);encryptData.param=RegExp.$1;myRe=/appId\s?=\s?'(.*?)'/g;data.match(myRe);encryptData.appId=RegExp.$1;encryptData=JSON.stringify(encryptData)return encryptData;
}
然后在python文件中,调用js。但不可以通过execjs这个库。因为会莫名其妙的报错,而且错误还不唯一,错误原因也很空,但在node里可以正常执行。我是用以下subprocess 调用node执行JS文件
具体参照:execjs 运行结果和 nodejs 结果不一样的解决方法_白御空的博客-CSDN博客_execjs使用nodejs
如何将 python3 中 os.popen()的默认编码由 ascii 修改为 ‘utf-8’ ? - 知乎
var arguments = process.argv; //JS
var args=arguments.splice(2);if(args.length!=0){if(args[0]=="getEncryptData")console.log(getEncryptData(args[1]));}
#python process=Popen(["node","./jiemi.js","getEncryptData",response_funEnData],stdout=PIPE,stderr=PIPE)stdout,stderr=process.communicate()encryptData=stdout.decode("utf-8")
#encryptData时js传来的数据,json格式encryptData_json=json.loads(encryptData)param=encryptData_json["param"]appId=encryptData_json["appId"]aesList=[]for i in encryptData_json["AESList"]:
#的到的数据是""data"",需要去除一对引号aesList.append(eval(i))
总上,动态实时变化的参数已经全部获取了。接下来就是fromdata进行加密了,就是复刻对方代码,如下
function getFromData(city,method,startTime,endTime,type,id,ack,aci) {ackvMDije6Ku =ack;//AESkey,可自定义aciiBrTMXFqu =aci;//我想要修改全局变量但把全局变量和形参得名称相同了,所以并没有修改appId全局appId=id;var param = {};param.city = city;param.type = type;//type :HOURparam.startTime = startTime;//2022-07-20 15:00:00param.endTime = endTime;//2022-07-21 16:00:00var fromData = pz0QXkl8bZn(method, param);return fromData;
}
var pz0QXkl8bZn = (function(){return function(method, obj){// var appId = '2b3d1754aac69d4e84b3bb5620ba578e';var clienttype = 'WEB';var timestamp = new Date().getTime();// console.log(method, obj,ObjectSort(obj),appId + method + timestamp + 'WEIXIN' + JSON.stringify(ObjectSort(obj)));var param = {appId: appId,method: method,timestamp: timestamp,clienttype: clienttype,object: obj,secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))};param = BASE64.encrypt(JSON.stringify(param));// param = AES.encrypt(param, ackvMDije6Ku, aciiBrTMXFqu); //之句话好像有和没有都可以return param;
};
})();
之后就是请求服务器,获取响应数据了,如下
ctx=execjs.compile(open("jiemi.js",encoding="utf-8").read())funName='getFromData("{}","{}","{}","{}","{}","{}","{}","{}")'.format("南京","GETCITYWEATHER","2022-07-13 15:00:00","2022-07-23 16:00:00","HOUR",appId,aesList[6],aesList[7])fromData=ctx.eval(funName)
#调用js,获得fromdata相关参数url="https://www.aqistudy.cn/apinew/aqistudyapi.php"data={param: fromData}page_text=requests.post(url=url,headers=headers,data=data).text
这样就得到了加密后的响应数据,通过抓包知道是
再跟进,发现函数也在/js/encrypt文件。但还是跟之前一样有动态变化的参数,不过好在之前都获取了。重新赋值以下就可了,如下。
function getFinalData(enData,a,b,d,f,h,x,o,p) {ask7U5FgBhMS=a;asioJThvmqnh=b;ackvMDije6Ku=d;aciiBrTMXFqu=f;dskcLK6StPtw=h;dsi6hLAy1Fd4=x;dckCXFfq0HiS=o;dciK2RcdgCIs=p;return dhGrEIFMzqtu7te9N1pka9(enData);//这里的函数其实就是上面那个图片的dbcvv.....函数,只是名字不一样,毕竟是实时变化的吗。
}
最后python里调用以下这个函数就成功的道解密后的数据了,如下。
funName="getFinalData('{}','{}','{}','{}','{}','{}','{}','{}','{}')".format(page_text,aesList[0],aesList[1],aesList[2],aesList[3],aesList[4],aesList[5],aesList[6],aesList[7])print(ctx.eval(funName))
结果
js逆向学习记录某真气网相关推荐
- 某短视频(dy)创作者平台发布视频JS逆向学习(1)
[本文仅供学习,请勿用于非法用途,若非法使用概不负责] 前话 某音创作者平台视频发布大致上可分为三个部分: 视频上传资源申请. 视频上传. 视频发布表单提交. 本章节主要讲解视频上传资源申请相关接口, ...
- JS逆向学习笔记 - 持续更新中
JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...
- python3爬虫进阶JS逆向学习(十一)
目的 目的:JS逆向的学习与交流 目标:分析咪咕音乐参数 目标网址:https://music.migu.cn/v3 // 若有侵权,请联系作者删除,谢谢! 思路分析 一.内容概览 二.请求参数分析 ...
- 【JS 逆向百例】37网游登录接口参数逆向
文章目录 声明 逆向目标 逆向过程 抓包分析 参数逆向 完整代码 37_encrypt.js 37_login.py 声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切 ...
- Python3爬虫进阶JS逆向学习(三)
目的 目的:JS逆向的学习与交流 目标:分析某我音乐网站中加密参数 目标网址:http://www.kuwo.cn/ 思路分析 一.内容概览 思路分析: 1.发送请求,搜索指定歌曲. 2.对返回的歌单 ...
- JS / JQ 学习记录
<一起学前端 之 JS 篇>是记录从 2020.4.29 开始,学习 JS 的收获与感悟. 2020.4.29 数据存储单位 JavaScript介绍 JS是什么 运行在客户端的脚本语言, ...
- 【JS】学习记录【页面打印】
[JS]记录一下今天遇到的问题和解决方法 需求:所有的已发布的工单和标准要能打印,打印时隐藏页面所有按钮,效果如下. 问题一:隐藏按钮. 解决办法: 获取按钮,设置样式display=none: fu ...
- 【逆向学习记录】格式化字符串漏洞原理及其利用
1 概述 前面学习完成栈溢出的漏洞利用,接下来最长用到的就是格式化字符串了,由于懒散,春节之前耽误的很多时间,这里统一整理一下 学习的过程中,主要参考文章: 格式化字符串利用小结 CTF WIKI 格 ...
- js逆向爬虫入门-03.凡科网逆向分析模拟登录
目录 逆向分析 python代码 git仓库 https://gitee.com/cycle1/py_re_js.git 逆向分析 凡科网,让经营更简单-凡科网登录 和其他网站一样 随便输入点啥 ...
最新文章
- Spring Boot版微信支付教程(视频 源码笔记)
- python创建一个集合_python如何创建一个集合
- 直播 | ICML 2021论文解读:具有局部和全局结构的自监督图表征学习
- Contains Duplicate --包含相同值
- Spring Boot定时任务-SpringBoot整合Quartz
- SCVMM 2012 R2运维管理九之:添加非信任的Hyper-v主机和群集
- 2021-07-27-jeesite学习笔记
- 第97课 寻找亲密数对_例97.1 《小学生C++编程入门》
- mysql系统变量_MySQL系统变量
- 江苏专转本计算机考试模拟试卷,江苏专转本计算机模拟试题 11
- OFFICE技术讲座:标点压缩是各大OFFICE软件差异关键,总体考量有哪些
- SecureCRT安装与使用
- SQL SERVER2008查询分析器的基本使用
- 小涴熊漫画CMS一款不错的开源免费的漫画连载系统带采集API
- python安装pygame的命令是什么_Python的pygame安装教程详解
- 抖音上热门的7大小技巧
- kettle Dummy介绍
- 《烈烈先秦》9、世界奇迹的缔造者——全才冤臣蒙恬
- Android自定义控件:NestedScrolling实现仿魅族flyme6应用市场应用详情弹出式layout
- python读取txt文档乱码解决