项目场景:

第一次写不知道能否表达清楚

在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逆向学习记录某真气网相关推荐

  1. 某短视频(dy)创作者平台发布视频JS逆向学习(1)

    [本文仅供学习,请勿用于非法用途,若非法使用概不负责] 前话 某音创作者平台视频发布大致上可分为三个部分: 视频上传资源申请. 视频上传. 视频发布表单提交. 本章节主要讲解视频上传资源申请相关接口, ...

  2. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  3. python3爬虫进阶JS逆向学习(十一)

    目的 目的:JS逆向的学习与交流 目标:分析咪咕音乐参数 目标网址:https://music.migu.cn/v3 // 若有侵权,请联系作者删除,谢谢! 思路分析 一.内容概览 二.请求参数分析 ...

  4. 【JS 逆向百例】37网游登录接口参数逆向

    文章目录 声明 逆向目标 逆向过程 抓包分析 参数逆向 完整代码 37_encrypt.js 37_login.py 声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切 ...

  5. Python3爬虫进阶JS逆向学习(三)

    目的 目的:JS逆向的学习与交流 目标:分析某我音乐网站中加密参数 目标网址:http://www.kuwo.cn/ 思路分析 一.内容概览 思路分析: 1.发送请求,搜索指定歌曲. 2.对返回的歌单 ...

  6. JS / JQ 学习记录

    <一起学前端 之 JS 篇>是记录从 2020.4.29 开始,学习 JS 的收获与感悟. 2020.4.29 数据存储单位 JavaScript介绍 JS是什么 运行在客户端的脚本语言, ...

  7. 【JS】学习记录【页面打印】

    [JS]记录一下今天遇到的问题和解决方法 需求:所有的已发布的工单和标准要能打印,打印时隐藏页面所有按钮,效果如下. 问题一:隐藏按钮. 解决办法: 获取按钮,设置样式display=none: fu ...

  8. 【逆向学习记录】格式化字符串漏洞原理及其利用

    1 概述 前面学习完成栈溢出的漏洞利用,接下来最长用到的就是格式化字符串了,由于懒散,春节之前耽误的很多时间,这里统一整理一下 学习的过程中,主要参考文章: 格式化字符串利用小结 CTF WIKI 格 ...

  9. js逆向爬虫入门-03.凡科网逆向分析模拟登录

    目录 逆向分析 python代码 git仓库    https://gitee.com/cycle1/py_re_js.git 逆向分析 凡科网,让经营更简单-凡科网登录 和其他网站一样 随便输入点啥 ...

最新文章

  1. Spring Boot版微信支付教程(视频 源码笔记)
  2. python创建一个集合_python如何创建一个集合
  3. 直播 | ICML 2021论文解读:具有局部和全局结构的自监督图表征学习
  4. Contains Duplicate --包含相同值
  5. Spring Boot定时任务-SpringBoot整合Quartz
  6. SCVMM 2012 R2运维管理九之:添加非信任的Hyper-v主机和群集
  7. 2021-07-27-jeesite学习笔记
  8. 第97课 寻找亲密数对_例97.1 《小学生C++编程入门》
  9. mysql系统变量_MySQL系统变量
  10. 江苏专转本计算机考试模拟试卷,江苏专转本计算机模拟试题 11
  11. OFFICE技术讲座:标点压缩是各大OFFICE软件差异关键,总体考量有哪些
  12. SecureCRT安装与使用
  13. SQL SERVER2008查询分析器的基本使用
  14. 小涴熊漫画CMS一款不错的开源免费的漫画连载系统带采集API
  15. python安装pygame的命令是什么_Python的pygame安装教程详解
  16. 抖音上热门的7大小技巧
  17. kettle Dummy介绍
  18. 《烈烈先秦》9、世界奇迹的缔造者——全才冤臣蒙恬
  19. Android自定义控件:NestedScrolling实现仿魅族flyme6应用市场应用详情弹出式layout
  20. python读取txt文档乱码解决

热门文章

  1. webrtc代码走读:发送端NACK和FEC的packet压到队列走读
  2. 基于机器学习的服装搭配问题分析
  3. python 调用函数实现——斐波纳契数列
  4. 以太坊解析之二——POA共识过程与一些可能的修改方案
  5. 响应式编程(反应式编程)的来龙去脉(同步编程、多线程编程、异步编程再到响应式编程)
  6. 到店维修要注意以下三点
  7. mysql的id生成uuid
  8. 作为父亲,给儿子留下些什么? | 黄华书房
  9. 使用uni-app开发一个取流播放器(网络电视)app简陋版
  10. 【新业务搭建】竞争情报业务规划及体系构建的思考——By Team