爬虫-Webpack逆向实战 有习题
爬虫-Webpack逆向实战
![](/assets/blank.gif)
我只要喝点果粒橙关注IP属地: 青海
0.1052022.04.30 19:21:46字数 4,875阅读 5,142
![](/assets/blank.gif)
全文目录
webpack打包是前端js模块化压缩打包常用的手段,特征明显,比如下方的形式的代码就是webpack分发器
// 分发器
!function(x){function xx(n){return ..call(**.exports, ***, ***.exports, xx)}
}()
又或者更直观的表现n["xxx"]这种,你可以大概知道了这是调用了webpack打包的js模块代码。
webpack打包后JS依赖模块代码的固定结构:
(this["webpackJsonpzsgk-pc"] = this["webpackJsonpzsgk-pc"] || []).push([[15], [function(e, t, n) {"use strict";e.exports = n(693)
}
// 参数固定为e, t, n
, function(e, t, n) {e.exports = n(697)()
}
说个逆向webpack的通用方法:
先去找加密网站的加密入口。这应该是加密网站都必须要做的==> 直接根据参数名搜索参数
找到分发器的位置,或者说是加载器,n["xxx"]这种的n就是分发器,就比如下方中的exports的位置,最后执行了d函数==>一般是runtimexxx.js中(提供环境);一般以
! function(e) {
的形式出现
![](/assets/blank.gif)
分发器.jpg
寻找分发编号、加密使用模块(用到了哪些模块就导入哪些模块)==>一般在chunk-lib.js,以
(window.webpackJsonp = window.webpackJsonp || []).push([
的形式出现将函数入口的地方返回全局变量,最终返回:
var sign; var window = global;!function(){... sign = d}
, 赋值为分发器返回的d使用自定义的sign代替webpack代码中的n进行加密
from: https://blog.csdn.net/weixin_41586984/article/details/116268341
调试技巧
定位请求参数
打开开发者工具后,F5刷新后
Ctrl + Shift + F
搜索参数名,如signdata,会显示多个JS文件,选择后仔细查看(点击左下角{}
美观格式化按钮)。more: 如果文件太多,则直接通过请求的链接去找,比如
user/login
Network找到新发出的xhr条目后,查看Initiator里的调用栈信息,如Login;
注: 如果加密参数名称比较简单如s,比较难定位的话,可以借助请求的其他参数来查找,比如verificationCode
调试工具
- 断点调试breakpoints
- XHR断点: XHR/fetch breakpoints
附录-Js记录
时间戳:
(new Date).getTime()
var a = (f1(), f2(), f3())
后,f1、f2、f3函数都会执行,而a最后的结果为f3的返回值javascript:void(0): void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。
TypeError: window.btoa is not a function
btoa-atob 模块没有输出一个编程接口,它只提供命令行工具。
如果你需要转换为Base64,你可以用Buffer来完成。
console.log(Buffer.from('Hello World!').toString('base64'));
相反的,假设你要解码的内容是一个base64编码过的字符串。
console.log(Buffer.from(b64Encoded, 'base64').toString());
做题记录
n["str"]题型:
天安财险
var m = this.newEncrypt(JSON.stringify(h));
需要对this.privaKey的值细化下,传入拿到固定的str
// , p = t("NFKh") , s = t("cg2h") l.prototype.newEncrypt = function(l) {var n = p.enc.Utf8.parse(this.privaKey), t = p.enc.Utf8.parse(this.privaKey), e = p.enc.Utf8.parse(l), a = p.AES.encrypt(e, n, {iv: t,mode: p.mode.CBC,padding: p.pad.Pkcs7});return p.enc.Base64.stringify(a.ciphertext)}
财新网
password: this.encode(this.encrypt(this.form.password)),
c = a("3452")
、n = a.n(c)
,看到需要依赖3452后立马Ctrl+shift+F全局搜3452,然后把整个webpack模块扒下来
中远海运
- n("MuMZ")中又有
r = n("XBrZ");
,在另一个文件中,module需要放两个
天翼云
var t = encodeURIComponent(c["c"].Des.encrypt(this.form.email, this.form.pwd)),
webpack实现
c = (mycode("ac6a"), mycode("b3ae"))
- 分发器和ac6a模块在同一个文件中、而ac6a模块依赖模块在另一个文件内;
- 分发器()({}),无感叹号
- 分发器()({})大括号中自带较多依赖模块
自己实现:直接扒下来encrypt加密的JS内容
看准网
- 分发器n("xxx")定位后跟一般的固定格式返回a、n、r不同,写的是个函数==>还是可以根据obj.Func来赋值
mycode = obj;
- JS逆向实战分析--看准网webpack加解密分析——Python中使用execjs示范
企名片
u = i("x4Ab")
return e.encrypt_data && (e.data = Object(u.a)(e.encrypt_data)),
x4Ab模块依赖aqBw,aqBw又依赖YuTi、yLpj,因此依赖项中放"x4Ab"、"aqBw"、"YuTi"、"yLpj"函数定义
模拟解析函数
function encrypt(data){return data && (Object(u.a)(data))}
n[num]题型:
大麦
- 删除分发器多余代码
var navigator = {}
掌上高考:
- 分发器在html文件内
o = (u=a(42), a.n(u))
,使用到了a.n(u)
即点n函数- 依赖函数的给出是以数组的形式,而不是字典的形式
- 模块中依赖更多模块==>引入整个模块文件,但是跟"xxx"模式不同的是,由于没有用字典
{"xxx": function()}
的形式,因此直接require也没用TypeError: Cannot read property '42' of undefined
,而是将依赖模块数组作为参数写入到分发器依赖函数中!function(e){}([...])
即方括号中,从而才能找到42函数
酷我
t.data.reqId = n,
直接通过n(109)定位可能不那么准确(双击后定位的函数),可以试着直接在分发器位置进行断点,然后console输出e["109"]
只要分发器定义部分(其他的删了,因为只用了
l=n(109)、c=n.n(l)
)+依赖模块中定义109函数(整个function而不是t.exports),以及观察其中还依赖什么如n(202)、n(203)就补充拿什么- n.n(l)是传入什么就返回什么:https://www.bilibili.com/video/BV1gq4y1D781?from=search&seid=7720105602891609746&spm_id_from=333.337.0.0
// 如果不删分发器中其他部分 l = mycode(109) c = mycode.n(l) // ==>得到l var r = c()(); console.log(r)// 由于只用到了n.n(l),所以可以删分发器大代码中其他部分, 在使用时直接让c=l l = mycode(109) c = l(); console.log(c) // 等价于 r = l(); console(r)
文章: https://blog.csdn.net/weixin_43189702/article/details/119860838
注意:
require模块内容可以放在逆向JS文件里一起,而不是一定得创建新的JS文件导入
先登录然后找到加密处加断点,这个断点会在发起登录请求时才触发;往上找分发器,加上断点,分发器位置的断点是在页面刷新时触发,因此要触发这个断点需要刷新页面
找加密函数
c["c"].Des.encrypt(this.form.email, this.form.pwd)
的时候,找完整的函数如c["c"].Des.encrypt,而不是直接找cn(42), 或者n("xxx"),可以直接搜xxx,也可以在console里面输出后找到对应的FunctionLocation来快速定位
如果依赖模块是字典的形式,则分发器依赖中写字典,如
!function(e){..}({ 32:function(){...}})
(一般情况n(32)、n("ABCD")
), 如果不是则需要传函数数组,如n(42)
,此处42表示的是第42个函数,见掌上高考。var mycode;
后赋值的位置直接在分发器的下方即可,不用在最后面提示缺少
window
时,定义全局变量var window = global;
,(JS逆向文件、依赖文件)window表示浏览器打开的窗口,在客户端JavaScript中window对象是全局的对象,所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。但在nodejs中直接调用window是不存在的,而代替的是global,所以要用nodejs运行时,得用
var window = global;
var navigator = this
等价于var navigator = {}
,因为在NodeJS文件中运行输出this后可以发现this={}
,而在浏览器中this默认为window(函数或类作用域内为函数或者类实例)from:JS中document和window的区别
心得
①所有webpack打包的的js都要先看懂打包后代码运行的顺序,找到加密处;②找到webpack对象,一般是 n(数字) 调用③确定分发器。④找依赖模块,有时候各包的依赖关系太多,可以直接把文件爬下来引入,如果各个包的依赖关系不多,就可以只把调用到的函数找出来放到依赖中。⑤最后剩下的就是找到你要的代码,慢慢复现调用加密/解密函数就好了。
做题案例学习视频
- webpack师承:爬取webpack流程-视频——大多都是n("Xvmd")
- 而如果n中调用的不是字典的形式,而是列表的形式,则看js逆向安全指南(3)-- webpack解包指南、使用 webpack 的 js 加密参数的分析——酷我
进阶资料
- 9-爬虫高级实战【js逆向】
- JS逆向学习笔记 - 持续更新中
- webpack补充依赖项做法:
- 逆向webpack - 简书
- webpack4之《模块运行机制原理》
- JS逆向视频
- 爬虫工程师进阶必会之JS逆向(反爬)
- 遇到网站加密爬不了??这几个视频教你如何学会爬虫高阶内容js逆向,入狱必学!
掌上高考解密过程
解析响应data.text
相应的data.text是加密的,页面通过JS解密后渲染
确定加密位置
return null != l && null !== (a = l.data) && void 0 !== a && a.text && (l.data = (n = (e = {iv: u.uri,text: l.data.text,SIGN: h}).iv,
确定分发器位置,在html内
通过打断
o = (u=a(42), a.n(u)) // 等价于 o = a(42)
确定依赖模块:给
return e[a].call(c.exports, c, c.exports, r),
打断点后console输出e["42"]
查看a(42)位置:
function(e, t, n) {e.exports = (e = n(21),n(201),n(825),...n(847), e)
}
可以看到需要依赖多个,因此直接把整个文件引入
- 扣解密函数:注意
return
表达式后是逗号的情况:会从左到右执行执行,并返回最后一个。注意:JS函数并不能返回多个返回值
then((function(l) {
var e, a, t, b, n;
return null != l && null !== (a = l.data) && void 0 !== a && a.text && (l.data = (n = (e = {iv: u.uri,text: l.data.text,SIGN: h}).iv,a = e.text,e = e.SIGN,e = o.a.PBKDF2(e, "secret", {keySize: 8,iterations: 1e3,hasher: o.a.algo.SHA256}).toString(),n = o.a.PBKDF2(n, "secret", {keySize: 4,iterations: 1e3,hasher: o.a.algo.SHA256}).toString(),a = o.a.lib.CipherParams.create({ciphertext: o.a.enc.Hex.parse(a)}),n = o.a.AES.decrypt(a, o.a.enc.Hex.parse(e), {iv: o.a.enc.Hex.parse(n)}),// data.text解析结果JSON.parse(n.toString(o.a.enc.Utf8)))),v && (t = r,b = l,null !== (n = window.apiConfig) && void 0 !== n && null !== (n = n.filterCacheList) && void 0 !== n && n.length ? window.apiConfig.filterCacheList.forEach((function(l) {new RegExp(l).test(t) || d.set(t, b)})) : d.set(t, b)),l
}
难点:
跟"xxx"模式不同的是,由于没有用字典
{"xxx": function()}
的形式,因此直接require也没用TypeError: Cannot read property '42' of undefined
,而是将依赖模块函数数组作为参数写入到分发器依赖函数中!function(e){}([...])
即方括号中,从而才能找到42函数挑选push后第二个
[]
中的函数数组(this["webpackJsonpzsgk-pc"] = this["webpackJsonpzsgk-pc"] || []).push([[15], [function(e, t, n) {"use strict";e.exports = n(693) },...} ]]); // 第一个]
理解了a.n的含义后,可以直接把
o = (u=a(42), a.n(u))
转化为o=a(42)
获得加密参数signsafe
大致流程跟data.text差不多,但是
p = c()(g)
执行时,会报错Md5.prototype.update = function(e) {if (!this.finalized) {var t, n = typeof e;if ("string" != n) {if ("object" != n)throw ERROR;if (null === e)
根据一步步调试之后发现,还是
c = (u=a(291),a.n(u))
直接替换出的问题
Ctrl + shift + F定位参数
g = void 0,g = (t = {SIGN: h,str: f.replace(/^\/|https?:\/\/\/?/, "") }).SIGN,t = t.str,g = o.a.HmacSHA1(o.a.enc.Utf8.parse(t), g),g = o.a.enc.Base64.stringify(g).toString(),p = c()(g),u.signsafe = p,
往上找c和o.a:
o = (u = a(42),a.n(u))
,c = (u = a(291),a.n(u))
找到分发器扣出==>这次不能删除分发器中多余的函数,比如r.a、r.d、r.n因为后面得用
将依赖模块跟data.text一样,放入分发器依赖模块中
扣加密函数
网页上是return后多段内容,以及g变量不断被修改,因此通过一步步调试确定入参,以及分解return抽离出真正的加密参数signsafe
h = "D23ABC@#56" var o = {}o.a = mycode(42) // ▲ c = (u = mycode(291), mycode.n(u))/*** 对url进行加密*/ function encrypt(f) {g = (t = {SIGN: h,str: f.replace(/^\/|https?:\/\/\/?/, "")}).SIGN,t = t.str;// console.log(t, g);g = o.a.HmacSHA1(o.a.enc.Utf8.parse(t), g);// console.log(g)g = o.a.enc.Base64.stringify(g).toString();// console.log(g)p = c()(g);return p; }res = encrypt("https://api.eol.cn/web/api/counter?cid=1&did=263") console.log(res)
可以看到o.a和c的赋值是不一样的,虽然说大多数情况x = a.n(u)等价于x=u,但难免有时会有不一样,因此谨慎期间,还是还原到底最好。
Python调用
import execjs
def get_signsafe_by_javascript(url):# 两个 JavaScript 脚本,两种方法均可with open('gk_signsafe.js', 'r', encoding='utf-8') as f:exec_js = f.read()signsafe = execjs.compile(exec_js).call('encrypt', url)return signsafe
signsafe = get_encrypted_password_by_javascript("https://api.eol.cn/web/api/counter?cid=1&did=263")
print(signsafe)
RSA的加密步骤
- 获取公钥
- 实例化 ===> 扣出网站RSA实例化对象的代码
- 设置公钥
- 对文本进行加密 ==> 扣出复现RSA加密的逻辑代码
注: var window=global
、var navigator={}
B站RSA:
// window.JSEncrypt is not a constructor 在抠出来的JS Encrypt代码中加上 window.JSEncrpt = ze // 网页中位var n = new JSEncrypt ==> JSEncrypt is not defined var n = new window.JSEncrypt; n.setPublicKey... var a = n.encrypt(t.data.hash + password); console.log(a)
网易云爬评论:python通过execjs来调用JS代码,代码中用到了
CryptoJS
库, 需要os.environ["NODE_PATH"]="F:/..../node_modules"
把库导入JS逆向实战分析--某铁网分析:document返回类型,initiator是一条条文本(Other),因为其没有用ajax(XHR),而是通过原生的网页表单提交
loginForm.password.value = encryptByDES(loginForm.password.value), loginForm.publickey.value); loginForm.submit();
直接require导入CryptoJS模块
or直接扣encryptByDES的加密函数==>出现
cannot read property 'createEncryptor' of undefined
MD5加密:
JS:
const crypto = require("CryptoJS"); crypto.MD5('待加密字符串').toString()
Python: https://blog.csdn.net/weixin_44799217/article/details/112486097
# 法一:创建md5对象 hl = hashlib.md5() # Tips # 此处必须声明encode,若写法为hl.update(str) 报错为: Unicode-objects must be encoded before hashing hl.update(str.encode(encoding='utf-8'))# 法二: str_md5 = hashlib.md5(str.encode(encoding='utf-8')).hexdigest()
base64编码
JS:
CryptoJS.enc.Base64.parse("待解密字符串").toString(CryptoJS.enc.Utf8)
Python
# 字符串 encode_str = base64.encodebytes(test_str.encode('utf8')) # b'aGVsbG8gd29ybGQh\n' print(encode_str.decode()) # 默认以utf8解码,结果 aGVsbG8gd29ybGQh# 图片 with open("D:\\redis.png", 'rb') as f:encode_img = base64.b64encode(f.read())file_ext = os.path.splitext("D:\\redis.png")[1]print('data:image/{};base64,{}'.format(file_ext[1:], encode_img.decode()))f.close()
加密、摘要算法结果特征
urlencode
urlencode是一个函数,可将字符串以URL编码,用于编码处理。
URL编码(URL encoding),也称作百分号编码(Percent-encoding), 是特定上下文的统一资源定位符 (URL)的编码机制。
Base64特征
最常见的用于传输8Bit字节码的编码方式之一
- 相同内容,结果是相同的
- a-zA-Z,0-9,+/共64个字符进行编码;每3个字节编码成4个字节,不足的在结尾有无意义的=来填补
- 一般情况下结尾都会有1个或者2个等号,明文长度是3的倍数时没有=;
- 内容越长,结果越长
注:跟下面的算法区分一下,base64是编码方式,并不能算加密算法。应用场景还有传输图片:data:image/jpg;base64,/9j/4QMZRXhpZgAASUk...
md5特征
消息摘要算法
- 确定唯一性:相同内容,结果是相同的;但一般会有时间戳等参数,所以导致了每次不同
- 不可逆性:有损的加密过程,理论上无法解密(逆向推出),除非暴力破解。安全,这也是其成为校验是否被修改的最关键的性质
- 碰撞性:原始数据与其MD5值并不是一一对应的,有可能多个原始数据计算出来的MD5值是一样的,这就是碰撞。
- 一般MD5值是32位,由数字“0-9”和字母“a-f”所组成的字符串;字母可以是全大写或者全小写
- 密文一般为 16 位或者 32 位,其中 16 位是取的 32 位第 9~25 位的值;
- 长度:32个十六进制字符组成的字符串 (128位)
RSA特征
- 相同内容,结果也是不同的
- 明文长度需要小于密钥长度,而密文长度则等于密钥长度。一般为1024、2048、3072、4096或512(低于1024的安全不建议)
- 通过公钥加密结果,必须私钥解密。 同样私钥加密结果,公钥可以解密
注:RSA加解密中必须考虑到的密钥长度、明文长度和密文长度问题;
▲.一般会使用 JSEncrypt 库,会有 new 一个实例对象的操作;
SHA 系列
SHA 是比 MD5 更安全一点的摘要算法,SHA 通常指 SHA 家族算法,
sha1
字母(a-f)和数字(0-9)混合
密文特征跟MD5差不多,只不过数字是40位,bit位数(160)==>4位十六进制表示一个数
Sha256
字母(a-f)和数字(0-9)混合
对于任意长度的消息,SHA256都会产生一个256位的哈希值,即64位十六进制数,称作消息摘要。
HMAC
在md5和sha1加密的基础上引入了秘钥,而秘钥又只有传输双方才知道,所以基本上是破解不了的,常用于接口签名验证
AES、DES、3DES、RC4、Rabbit 等
AES、DES、3DES、RC4、Rabbit 等加密算法的密文通常没有固定的长度,他们通常使用crypto-js
库来实现
参考:https://juejin.cn/post/7052978567390429215
爬虫-Webpack逆向实战 有习题相关推荐
- 【JS 逆向百例】webpack 改写实战,G 某游戏 RSA 加密
关注微信公众号:K哥爬虫,QQ交流群:808574309,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途 ...
- 【Python 爬虫简单的JavaScript逆向实战】抓取中国电影票房的数据
开篇闲聊 最近也是看完了崔庆才爬虫52讲里面JavaScript逆向部分,里面介绍了从简单到复杂整个JavaScript逆向的方法,里面也有一些实战案例(可以练习的),跟着视频练习之后,虽然也成功了, ...
- 21.网络爬虫—js逆向详讲与实战
网络爬虫-js逆向 js逆向 JavaScript逆向的详细讲解 实战演示 有道翻译 设置密钥和初始向量 对密钥和初始向量进行哈希处理 创建AES对象并解密消息 移除padding并返回结果 前言:
- python爬虫之逆向破解_js逆向爬虫实战(2)--新快之加密参数破解
爬虫js逆向系列 我会把做爬虫过程中,遇到的所有js逆向的问题分类展示出来,以现象,解决思路,以及代码实现.我觉得做技术分享,不仅仅是要记录问题,解决办法,更重要的是要提供解决问题的思路.怎么突破的, ...
- **超防 ja3+加速乐(三种加密(md5,sha1,sha256)) 实战(python爬虫js逆向)
**超防 加速乐+ja3 实战(python爬虫js逆向) 地址 aHR0cHM6Ly93d3cuaGVmZWkuZ292LmNuL2NvbnRlbnQvY29sdW1uLzY3OTQ4MTE/cGF ...
- python爬虫进阶js逆向实战 | 爬取 破解某安部加速乐cookie
很久没有写技术性的文章了,最近打算更新一系列的js逆向实战 今天的网站是某安部的一个加密cookie的分析跟破解 aHR0cHM6Ly93d3cubXBzLmdvdi5jbi9uMjI1NDA5OC9 ...
- python爬虫案例-Python3爬虫三大案例实战分享
课程名称: [温馨提示:1. 你可以在PC端浏览器或者微信收藏该页面,以方便你快速找到这个课程:2. 课程相关资料可在课程PC端公告查看下载:3.加入课程后,点(课时)列表即可观看视频 ] 课程须知: ...
- Python3网络爬虫快速入门实战解析(一小时入门 Python 3 网络爬虫)
Python3网络爬虫快速入门实战解析(一小时入门 Python 3 网络爬虫) https://blog.csdn.net/u012662731/article/details/78537432 出 ...
- 《nodejs+gulp+webpack基础实战篇》课程笔记(四)-- 实战演练
一.用gulp 构建前端页面(1)---静态构建 npm install gulp-template --save-dev 通过这个插件,我们可以像写后台模板(譬如PHP)一样写前端页面.我们首先学习 ...
最新文章
- c语言小数点进制转换,新手求教,关于含小数的二进制转换成十进制
- centos7 yum下载路径
- Java为什么会存成undefined,为什么我在Java中获得NoClassDefFoundError?
- log4cplus导致主进程不能退出问题解决
- js倒计时代码最简单的_代码设计开发-6大基本原则解读(最简单扼要的理解)
- asset文件夹路径 unity_我们来捣鼓一下Unity的平台跳跃Microgame
- 六年级计算机应用计划,2017六年级信息技术下册教学计划
- 求关于运动的英语(收集)
- 计算机网络学习笔记--网络层知识点整理
- C#窗体控件-列表框控件ListBox
- eclipse 设置默认编码为Utf-8
- 鸟哥linux私房菜读后,鸟哥的Linux私房菜读书笔记(1)
- 明尼苏达计算机科学与工程,明尼苏达大学计算机科学专业排名第29(2020年USNEWS美国排名)...
- 基于Hadoop平台使用MapReduce统计某银行信用卡违约用户数量
- Ubuntu18.04 用一条命令 快速安装 FBReader
- html5 footer header,W3C HTML5标准阅读笔记 – header、footer、main、address
- 短信宝发送短信验证码
- 【房卡棋牌教程】,制作进入房间小界面
- 分布式事务—Lec12课前资料
- Android开发实例-自动生成题库的数独