登录+JS逆向进阶【过咪咕登录】(附带源码)
JS渗透之咪咕登录
- 每篇前言:
- 咪咕登录
- 参数对比
- captcha参数
- enpassword参数
- 搜索enpassword参数
- 搜索J_RsaPsd参数
- setPublic函数
- encrypt加密函数
- 运行时可能会遇到的问题
- 此部分改写的最终形态JS代码:
- 运行结果
- python编写脚本运行此JS代码:
- 运行结果:
- loginID 参数
- 步骤同上面的enpassword,一步步搜索会发现找到了图中的J_RsaAccout,这个就是loginID参数的加密:(我们继续一步步分析,会发现此参数的加密方式和刚刚的enpassword的加密方式一模一样,那处理方法不就简单了!)
- 运行结果
- FingerPrint和FingerPrintDetail参数
- 找到指纹加密的主函数(会发现就在刚刚的函数下面)
- 分析函数功能
- 修改FingerPrint的JS函数
- 运行结果
- 编写爬虫脚本:
- (1)首先,编写脚本构造出post表单!
- (2)然后,编写脚本发送请求并通过获取响应判断是否登录成功!
- 第一步:直接向其发送请求,观察响应,是否为登录成功的界面!
- 第二步:所以下面我们要做的就是,通过分析浏览器登录,找到一系列的重定向的URL,并找到最终返回登录成功界面的URL:
- 第三步:构造登录成功之后的重定向URL,并通过依此请求模拟网站登录时候的一次次重定向跳转,最终达到获取咪咕登录成功的界面!
- (3)观察可知登录成功!
- 项目源码链接:
每篇前言:
🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者
- 🔥🔥本文已收录于爬虫进阶+实战系列教程专栏:《爬虫进阶+实战系列教程》
- 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
- 📝📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
- 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
- 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!
![]()
咪咕登录
之前的请求中找数据:
-
上级页面搜索: 静态页面搜索参数内容
-
本级页面:Network里搜索内容
复杂登录特点:
- 参数多
- 加密参数来源复杂
- 多次请求
简单拓展:
- Ajax请求的底层是基于XMLHttpRequests对象实现,所以在抓包时有两个特征:页面不刷新 + 请求类型为xhr。
参数对比
captcha参数
这个值对应的是验证码的数据,第一次登录时并没有这个验证码,提交空值给表单就可以了,验证码是账号或密码输错导致登录失败的时候会出现。这个时候会给服务器发送一条请求,返回值就是验证码经过base64编码后的数据。
enpassword参数
通过观察两次请求的参数可以看出来enpassword参数是不相同的,所以这个参数显然是动态变化生成的,再仔细观察一下发现这个值每个字符的范围都是0-9和a-f,通过我们的经验可以猜想这个参数是16进制的字符串,而且很有可能是经过加密的。
搜索enpassword参数
但是搜索发现都是HTML源码里,显然不可能包含此参数的值。经验可知此参数肯定是JS动态生成的,所以我们就看HTML源码中此参数对应的代码:(既然name值参数搜索没用,那么就来搜索一下class的值J_RsaPsd)
搜索J_RsaPsd参数
分析JS文件,会发现三个地方都有此参数,而且结构几乎一致,所以为了找到目标数据,我们将这三个都打上断点进行观察!执行观察会停在哪一个上,那我们就只分析那一个即可!
执行过程,分析可知是个RSA加密!
分析最近的一个函数,当选中var b = $(this)这句时,密码输入框标记选中,可知此句是将密码赋值给b。
进入db函数
这里看不出有什么东西,先跳过不管。
setPublic函数
这里需要传入两个参数,这两个值在上一个请求的response中可以得到,而且都是固定值。
这两个参数可以作为固定值写在代码里。
encrypt加密函数
传进去的参数b.val()在console中执行发现就是我们在密码框中输入的明文。
encrypt函数实际就是gb函数
这里是将gb函数赋值给db对象的原型对象,下面又把db对象赋值给c.RSAKey,这就是最开始定义c的语句。
gb函数执行到c.toString(16)的时候就得到了加密结果
改写JS加密基本逻辑(下面的代码就写入改写的JS代码中)
function getEncryptedPwd(pwd, modulus, publicExponent) {c = new RSAKey;c.setPublic(modulus, publicExponent);var d = c.encrypt(pwd);return d;
}
pwd传入明文密码,modulus和publicExponent是固定值,现在只要把里面有关联的函数复制出来再进行调试就可以了。
这里是RSA加密函数定义的起止位置(注意:我们复制两个大括号内部的所有代码!)
Python调用JS加密函数(最终编写爬虫文件中使用!)
def make_encrypt_password(self, password, modulus, publicExponent):"""生成加密后的密码Args:password (str): 输入框内输入的原始密码modulus (str): rsa加密参数,两个质数的乘积,固定值,前一个请求获得publicExponent (str): rsa加密参数,大于1的奇数,固定值,前一个请求获得Returns:str: 加密后的密码"""return self.js_object.call('getEncryptedPwd', password, modulus, publicExponent)
运行时可能会遇到的问题
-
遇到的错误1
- 报错
- 报错
- 解决方法 把alert中的中文删去
-
遇到的错误2
- 报错
- 报错
- 解决方法 定义navigator和window
-
遇到的错误3
- 报错
- 报错
- 解决方法 修改如下代码
此部分改写的最终形态JS代码:
var navigator = {},window={};function d(a, b, c) {null != a && ("number" == typeof a ? this.fromNumber(a, b, c) : null == b && "string" != typeof a ? this.fromString(a, 256) : this.fromString(a, b))
}
function e() {return new d(null)
}
function f(a, b, c, d, e, f) {for (; --f >= 0; ) {var g = b * this[a++] + c[d] + e;e = Math.floor(g / 67108864),c[d++] = 67108863 & g}return e
}
function g(a, b, c, d, e, f) {for (var g = 32767 & b, h = b >> 15; --f >= 0; ) {var i = 32767 & this[a], j = this[a++] >> 15, k = h * i + j * g;i = g * i + ((32767 & k) << 15) + c[d] + (1073741823 & e),e = (i >>> 30) + (k >>> 15) + h * j + (e >>> 30),c[d++] = 1073741823 & i}return e
}
function h(a, b, c, d, e, f) {for (var g = 16383 & b, h = b >> 14; --f >= 0; ) {var i = 16383 & this[a], j = this[a++] >> 14, k = h * i + j * g;i = g * i + ((16383 & k) << 14) + c[d] + e,e = (i >> 28) + (k >> 14) + h * j,c[d++] = 268435455 & i}return e
}
function i(a) {return nb.charAt(a)
}
function j(a, b) {var c = ob[a.charCodeAt(b)];return null == c ? -1 : c
}
function k(a) {for (var b = this.t - 1; b >= 0; --b)a[b] = this[b];a.t = this.t,a.s = this.s
}
function l(a) {this.t = 1,this.s = 0 > a ? -1 : 0,a > 0 ? this[0] = a : -1 > a ? this[0] = a + this.DV : this.t = 0
}
function m(a) {var b = e();return b.fromInt(a),b
}
function n(a, b) {var c;if (16 == b)c = 4;else if (8 == b)c = 3;else if (256 == b)c = 8;else if (2 == b)c = 1;else if (32 == b)c = 5;else {if (4 != b)return void this.fromRadix(a, b);c = 2}this.t = 0,this.s = 0;for (var e = a.length, f = !1, g = 0; --e >= 0; ) {var h = 8 == c ? 255 & a[e] : j(a, e);0 > h ? "-" == a.charAt(e) && (f = !0) : (f = !1,0 == g ? this[this.t++] = h : g + c > this.DB ? (this[this.t - 1] |= (h & (1 << this.DB - g) - 1) << g,this[this.t++] = h >> this.DB - g) : this[this.t - 1] |= h << g,g += c,g >= this.DB && (g -= this.DB))}8 == c && 0 != (128 & a[0]) && (this.s = -1,g > 0 && (this[this.t - 1] |= (1 << this.DB - g) - 1 << g)),this.clamp(),f && d.ZERO.subTo(this, this)
}
function o() {for (var a = this.s & this.DM; this.t > 0 && this[this.t - 1] == a; )--this.t
}
function p(a) {if (this.s < 0)return "-" + this.negate().toString(a);var b;if (16 == a)b = 4;else if (8 == a)b = 3;else if (2 == a)b = 1;else if (32 == a)b = 5;else {if (4 != a)return this.toRadix(a);b = 2}var c, d = (1 << b) - 1, e = !1, f = "", g = this.t, h = this.DB - g * this.DB % b;if (g-- > 0)for (h < this.DB && (c = this[g] >> h) > 0 && (e = !0,f = i(c)); g >= 0; )b > h ? (c = (this[g] & (1 << h) - 1) << b - h,c |= this[--g] >> (h += this.DB - b)) : (c = this[g] >> (h -= b) & d,0 >= h && (h += this.DB,--g)),c > 0 && (e = !0),e && (f += i(c));return e ? f : "0"
}
function q() {var a = e();return d.ZERO.subTo(this, a),a
}
function r() {return this.s < 0 ? this.negate() : this
}
function s(a) {var b = this.s - a.s;if (0 != b)return b;var c = this.t;if (b = c - a.t,0 != b)return this.s < 0 ? -b : b;for (; --c >= 0; )if (0 != (b = this[c] - a[c]))return b;return 0
}
function t(a) {var b, c = 1;return 0 != (b = a >>> 16) && (a = b,c += 16),0 != (b = a >> 8) && (a = b,c += 8),0 != (b = a >> 4) && (a = b,c += 4),0 != (b = a >> 2) && (a = b,c += 2),0 != (b = a >> 1) && (a = b,c += 1),c
}
function u() {return this.t <= 0 ? 0 : this.DB * (this.t - 1) + t(this[this.t - 1] ^ this.s & this.DM)
}
function v(a, b) {var c;for (c = this.t - 1; c >= 0; --c)b[c + a] = this[c];for (c = a - 1; c >= 0; --c)b[c] = 0;b.t = this.t + a,b.s = this.s
}
function w(a, b) {for (var c = a; c < this.t; ++c)b[c - a] = this[c];b.t = Math.max(this.t - a, 0),b.s = this.s
}
function x(a, b) {var c, d = a % this.DB, e = this.DB - d, f = (1 << e) - 1, g = Math.floor(a / this.DB), h = this.s << d & this.DM;for (c = this.t - 1; c >= 0; --c)b[c + g + 1] = this[c] >> e | h,h = (this[c] & f) << d;for (c = g - 1; c >= 0; --c)b[c] = 0;b[g] = h,b.t = this.t + g + 1,b.s = this.s,b.clamp()
}
function y(a, b) {b.s = this.s;var c = Math.floor(a / this.DB);if (c >= this.t)return void (b.t = 0);var d = a % this.DB, e = this.DB - d, f = (1 << d) - 1;b[0] = this[c] >> d;for (var g = c + 1; g < this.t; ++g)b[g - c - 1] |= (this[g] & f) << e,b[g - c] = this[g] >> d;d > 0 && (b[this.t - c - 1] |= (this.s & f) << e),b.t = this.t - c,b.clamp()
}
function z(a, b) {for (var c = 0, d = 0, e = Math.min(a.t, this.t); e > c; )d += this[c] - a[c],b[c++] = d & this.DM,d >>= this.DB;if (a.t < this.t) {for (d -= a.s; c < this.t; )d += this[c],b[c++] = d & this.DM,d >>= this.DB;d += this.s} else {for (d += this.s; c < a.t; )d -= a[c],b[c++] = d & this.DM,d >>= this.DB;d -= a.s}b.s = 0 > d ? -1 : 0,-1 > d ? b[c++] = this.DV + d : d > 0 && (b[c++] = d),b.t = c,b.clamp()
}
function A(a, b) {var c = this.abs(), e = a.abs(), f = c.t;for (b.t = f + e.t; --f >= 0; )b[f] = 0;for (f = 0; f < e.t; ++f)b[f + c.t] = c.am(0, e[f], b, f, 0, c.t);b.s = 0,b.clamp(),this.s != a.s && d.ZERO.subTo(b, b)
}
function B(a) {for (var b = this.abs(), c = a.t = 2 * b.t; --c >= 0; )a[c] = 0;for (c = 0; c < b.t - 1; ++c) {var d = b.am(c, b[c], a, 2 * c, 0, 1);(a[c + b.t] += b.am(c + 1, 2 * b[c], a, 2 * c + 1, d, b.t - c - 1)) >= b.DV && (a[c + b.t] -= b.DV,a[c + b.t + 1] = 1)}a.t > 0 && (a[a.t - 1] += b.am(c, b[c], a, 2 * c, 0, 1)),a.s = 0,a.clamp()
}
function C(a, b, c) {var f = a.abs();if (!(f.t <= 0)) {var g = this.abs();if (g.t < f.t)return null != b && b.fromInt(0),void (null != c && this.copyTo(c));null == c && (c = e());var h = e(), i = this.s, j = a.s, k = this.DB - t(f[f.t - 1]);k > 0 ? (f.lShiftTo(k, h),g.lShiftTo(k, c)) : (f.copyTo(h),g.copyTo(c));var l = h.t, m = h[l - 1];if (0 != m) {var n = m * (1 << this.F1) + (l > 1 ? h[l - 2] >> this.F2 : 0), o = this.FV / n, p = (1 << this.F1) / n, q = 1 << this.F2, r = c.t, s = r - l, u = null == b ? e() : b;for (h.dlShiftTo(s, u),c.compareTo(u) >= 0 && (c[c.t++] = 1,c.subTo(u, c)),d.ONE.dlShiftTo(l, u),u.subTo(h, h); h.t < l; )h[h.t++] = 0;for (; --s >= 0; ) {var v = c[--r] == m ? this.DM : Math.floor(c[r] * o + (c[r - 1] + q) * p);if ((c[r] += h.am(0, v, c, s, 0, l)) < v)for (h.dlShiftTo(s, u),c.subTo(u, c); c[r] < --v; )c.subTo(u, c)}null != b && (c.drShiftTo(l, b),i != j && d.ZERO.subTo(b, b)),c.t = l,c.clamp(),k > 0 && c.rShiftTo(k, c),0 > i && d.ZERO.subTo(c, c)}}
}
function D(a) {var b = e();return this.abs().divRemTo(a, null, b),this.s < 0 && b.compareTo(d.ZERO) > 0 && a.subTo(b, b),b
}
function E(a) {this.m = a
}
function F(a) {return a.s < 0 || a.compareTo(this.m) >= 0 ? a.mod(this.m) : a
}
function G(a) {return a
}
function H(a) {a.divRemTo(this.m, null, a)
}
function I(a, b, c) {a.multiplyTo(b, c),this.reduce(c)
}
function J(a, b) {a.squareTo(b),this.reduce(b)
}
function K() {if (this.t < 1)return 0;var a = this[0];if (0 == (1 & a))return 0;var b = 3 & a;return b = b * (2 - (15 & a) * b) & 15,b = b * (2 - (255 & a) * b) & 255,b = b * (2 - ((65535 & a) * b & 65535)) & 65535,b = b * (2 - a * b % this.DV) % this.DV,b > 0 ? this.DV - b : -b
}
function L(a) {this.m = a,this.mp = a.invDigit(),this.mpl = 32767 & this.mp,this.mph = this.mp >> 15,this.um = (1 << a.DB - 15) - 1,this.mt2 = 2 * a.t
}
function M(a) {var b = e();return a.abs().dlShiftTo(this.m.t, b),b.divRemTo(this.m, null, b),a.s < 0 && b.compareTo(d.ZERO) > 0 && this.m.subTo(b, b),b
}
function N(a) {var b = e();return a.copyTo(b),this.reduce(b),b
}
function O(a) {for (; a.t <= this.mt2; )a[a.t++] = 0;for (var b = 0; b < this.m.t; ++b) {var c = 32767 & a[b], d = c * this.mpl + ((c * this.mph + (a[b] >> 15) * this.mpl & this.um) << 15) & a.DM;for (c = b + this.m.t,a[c] += this.m.am(0, d, a, b, 0, this.m.t); a[c] >= a.DV; )a[c] -= a.DV,a[++c]++}a.clamp(),a.drShiftTo(this.m.t, a),a.compareTo(this.m) >= 0 && a.subTo(this.m, a)
}
function P(a, b) {a.squareTo(b),this.reduce(b)
}
function Q(a, b, c) {a.multiplyTo(b, c),this.reduce(c)
}
function R() {return 0 == (this.t > 0 ? 1 & this[0] : this.s)
}
function S(a, b) {if (a > 4294967295 || 1 > a)return d.ONE;var c = e(), f = e(), g = b.convert(this), h = t(a) - 1;for (g.copyTo(c); --h >= 0; )if (b.sqrTo(c, f),(a & 1 << h) > 0)b.mulTo(f, g, c);else {var i = c;c = f,f = i}return b.revert(c)
}
function T(a, b) {var c;return c = 256 > a || b.isEven() ? new E(b) : new L(b),this.exp(a, c)
}
function U() {this.i = 0,this.j = 0,this.S = new Array
}
function V(a) {var b, c, d;for (b = 0; 256 > b; ++b)this.S[b] = b;for (c = 0,b = 0; 256 > b; ++b)c = c + this.S[b] + a[b % a.length] & 255,d = this.S[b],this.S[b] = this.S[c],this.S[c] = d;this.i = 0,this.j = 0
}
function W() {var a;return this.i = this.i + 1 & 255,this.j = this.j + this.S[this.i] & 255,a = this.S[this.i],this.S[this.i] = this.S[this.j],this.S[this.j] = a,this.S[a + this.S[this.i] & 255]
}
function X() {return new U
}
function Y(a) {qb[rb++] ^= 255 & a,qb[rb++] ^= a >> 8 & 255,qb[rb++] ^= a >> 16 & 255,qb[rb++] ^= a >> 24 & 255,rb >= sb && (rb -= sb)
}
function Z() {Y((new Date).getTime())
}
function $() {if (null == pb) {for (Z(),pb = X(),pb.init(qb),rb = 0; rb < qb.length; ++rb)qb[rb] = 0;rb = 0}return pb.next()
}
function _(a) {var b;for (b = 0; b < a.length; ++b)a[b] = $()
}
function ab() {}
function bb(a, b) {return new d(a,b)
}
function cb(a, b) {if (b < a.length + 11)return alert("Message too long for RSA"),null;for (var c = new Array, e = a.length - 1; e >= 0 && b > 0; ) {var f = a.charCodeAt(e--);128 > f ? c[--b] = f : f > 127 && 2048 > f ? (c[--b] = 63 & f | 128,c[--b] = f >> 6 | 192) : (c[--b] = 63 & f | 128,c[--b] = f >> 6 & 63 | 128,c[--b] = f >> 12 | 224)}c[--b] = 0;for (var g = new ab, h = new Array; b > 2; ) {for (h[0] = 0; 0 == h[0]; )g.nextBytes(h);c[--b] = h[0]}return c[--b] = 2,c[--b] = 0,new d(c)
}
function db() {this.n = null,this.e = 0,this.d = null,this.p = null,this.q = null,this.dmp1 = null,this.dmq1 = null,this.coeff = null
}
function eb(a, b) {null != a && null != b && a.length > 0 && b.length > 0 ? (this.n = bb(a, 16),this.e = parseInt(b, 16)) : alert("")
}
function fb(a) {return a.modPowInt(this.e, this.n)
}
function gb(a) {var b = cb(a, this.n.bitLength() + 7 >> 3);if (null == b)return null;var c = this.doPublic(b);if (null == c)return null;var d = c.toString(16);return 0 == (1 & d.length) ? d : "0" + d
}
var hb, ib = 0xdeadbeefcafe, jb = 15715070 == (16777215 & ib);
jb && "Microsoft Internet Explorer" == navigator.appName ? (d.prototype.am = g,
hb = 30) : jb && "Netscape" != navigator.appName ? (d.prototype.am = f,
hb = 26) : (d.prototype.am = h,
hb = 28),
d.prototype.DB = hb,
d.prototype.DM = (1 << hb) - 1,
d.prototype.DV = 1 << hb;
var kb = 52;
d.prototype.FV = Math.pow(2, kb),
d.prototype.F1 = kb - hb,
d.prototype.F2 = 2 * hb - kb;
var lb, mb, nb = "0123456789abcdefghijklmnopqrstuvwxyz", ob = new Array;
for (lb = "0".charCodeAt(0),
mb = 0; 9 >= mb; ++mb)ob[lb++] = mb;
for (lb = "a".charCodeAt(0),
mb = 10; 36 > mb; ++mb)ob[lb++] = mb;
for (lb = "A".charCodeAt(0),
mb = 10; 36 > mb; ++mb)ob[lb++] = mb;
E.prototype.convert = F,
E.prototype.revert = G,
E.prototype.reduce = H,
E.prototype.mulTo = I,
E.prototype.sqrTo = J,
L.prototype.convert = M,
L.prototype.revert = N,
L.prototype.reduce = O,
L.prototype.mulTo = Q,
L.prototype.sqrTo = P,
d.prototype.copyTo = k,
d.prototype.fromInt = l,
d.prototype.fromString = n,
d.prototype.clamp = o,
d.prototype.dlShiftTo = v,
d.prototype.drShiftTo = w,
d.prototype.lShiftTo = x,
d.prototype.rShiftTo = y,
d.prototype.subTo = z,
d.prototype.multiplyTo = A,
d.prototype.squareTo = B,
d.prototype.divRemTo = C,
d.prototype.invDigit = K,
d.prototype.isEven = R,
d.prototype.exp = S,
d.prototype.toString = p,
d.prototype.negate = q,
d.prototype.abs = r,
d.prototype.compareTo = s,
d.prototype.bitLength = u,
d.prototype.mod = D,
d.prototype.modPowInt = T,
d.ZERO = m(0),
d.ONE = m(1),
U.prototype.init = V,
U.prototype.next = W;
var pb, qb, rb, sb = 256;
if (null == qb) {qb = new Array,rb = 0;var tb;if (window.crypto && window.crypto.getRandomValues) {var ub = new Uint8Array(32);for (window.crypto.getRandomValues(ub),tb = 0; 32 > tb; ++tb)qb[rb++] = ub[tb]}if ("Netscape" == navigator.appName && navigator.appVersion < "5" && window.crypto) {var vb = window.crypto.random(32);for (tb = 0; tb < vb.length; ++tb)qb[rb++] = 255 & vb.charCodeAt(tb)}for (; sb > rb; )tb = Math.floor(65536 * Math.random()),qb[rb++] = tb >>> 8,qb[rb++] = 255 & tb;rb = 0,Z()
}
ab.prototype.nextBytes = _,
db.prototype.doPublic = fb,
db.prototype.setPublic = eb,
db.prototype.encrypt = gb,
RSAKey = dbfunction getEncryptedPwd(pwd, modulus, publicExponent) {c = new RSAKey;c.setPublic(modulus, publicExponent);var d = c.encrypt(pwd);return d;
}
运行结果
python编写脚本运行此JS代码:
import execjspassword = '123' # 模拟的密码(随便写的!)
modulus = '00833c4af965ff7a8409f8b5d5a83d87f2f19d7c1eb40dc59a98d2346cbb145046b2c6facc25b5cc363443f0f7ebd9524b7c1e1917bf7d849212339f6c1d3711b115ecb20f0c89fc2182a985ea28cbb4adf6a321ff7e715ba9b8d7261d1c140485df3b705247a70c28c9068caabbedbf9510dada6d13d99e57642b853a73406817'
publicExponent = '010001'def make_execjs_object():with open('en_pwd.js', 'r') as f:js = f.read()return execjs.compile(js)js_object = make_execjs_object()a = js_object.call('getEncryptedPwd', password, modulus, publicExponent)
print(a)
运行结果:
loginID 参数
步骤同上面的enpassword,一步步搜索会发现找到了图中的J_RsaAccout,这个就是loginID参数的加密:(我们继续一步步分析,会发现此参数的加密方式和刚刚的enpassword的加密方式一模一样,那处理方法不就简单了!)
JS加密账号函数(直接将下面的JS函数加入刚刚自己改写的JS代码文件中即可!)
function getEncryptedAccount(account, modulus, publicExponent) {c = new RSAKey;c.setPublic(modulus, publicExponent);var d = c.encrypt(account);return d;
}
Python调用JS加密函数(编写爬虫脚本文件时用以下函数调用JS文件生成所需参数!形同enpassword的写法)
def make_encrypt_account(self, account, modulus, publicExponent):"""生成加密后的账号Args:account (str): 输入框内输入的账号modulus (str): 同上publicExponent (str): 同上Returns:str: 加密后的账号"""return self.js_object.call('getEncryptedAccount', account, modulus, publicExponent)
运行结果
FingerPrint和FingerPrintDetail参数
找到指纹加密的主函数(会发现就在刚刚的函数下面)
分析函数功能
这里比较不好理解的是$.fingerprint.details
和$.fingerprint.result
,这两个变量的定义位置是下面图片这里
这两个值也都是固定值,据观察,其中c的值就是使用者计算机的一些属性,只要计算机不换,这些值就是固定的,所以我们直接传固定值即可(它也不知道你电脑信息是啥样的呀);而且d的值是根据c的值得出的,所以也可以写死!后面编写爬虫代码时直接复制此处的值即可!
修改FingerPrint的JS函数
function rsaFingerprint(a, b, details, result) { //details和result是固定值,直接作为参数传进来赋值; 此处的a和b的值通过观察可知就是a.result.modulus和a.result.publicExponent这俩值,前面获取过!var c = details, d = result, e = c.length, f = "", g = new RSAKey;g.setPublic(a, b);for (var h = g.encrypt(d), i = 0; e > i; i += 117)f += g.encrypt(c.substr(i, 117));// return {// details: f,// result: h// }return {fingerPrintDetail: f, //这里代码会返回字典类型的数据,直接可以合并到表单里,所以直接修改为表单里可以用的key值。当然不改也可!fingerPrint: h}
}
Python调用JS加密函数
def make_rsa_fingerprint(self, modulus, publicExponent, details, result):"""生成rsa指纹参数Args:details (str): 请求headers信息result (str): headers的加密信息,如果headers不改变这个值也是固定的Returns (dict):details: 加密后的fingerprint_details信息,fingerPrintDetail表单参数result: 加密后的fingerprint_result信息,fingerPrint表单参数"""return self.js_object.call('rsaFingerprint', modulus, publicExponent, details, result)
运行结果
编写爬虫脚本:
(1)首先,编写脚本构造出post表单!
import requests
import jsonimport execjsclass Migu_login(object):def __init__(self, account, password):modules = '00833c4af965ff7a8409f8b5d5a83d87f2f19d7c1eb40dc59a98d2346cbb145046b2c6facc25b5cc363443f0f7ebd9524b7c1e1917bf7d849212339f6c1d3711b115ecb20f0c89fc2182a985ea28cbb4adf6a321ff7e715ba9b8d7261d1c140485df3b705247a70c28c9068caabbedbf9510dada6d13d99e57642b853a73406817'publicExponent = '010001'# 获取FingerPrint和FingerPrintDetail参数中JS函数所需的参数c和ddetails_params = '"{"user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89","language":"zh-CN","color_depth":"24","pixel_ratio":"1.25","hardware_concurrency":"8","resolution":"1536,864","available_resolution":"1536,834","timezone_offset":"-480","session_storage":"1","local_storage":"1","indexed_db":"1","open_database":"1","cpu_class":"unknown","navigator_platform":"Win32","do_not_track":"unknown","regular_plugins":"Chrome PDF Plugin::Portable Document Format::application/x-google-chrome-pdf~pdf,Chrome PDF Viewer::","webgl_vendor":"Google Inc.~ANGLE (Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0)","adblock":"false","has_lied_languages":"false","has_lied_resolution":"false","has_lied_os":"false","has_lied_browser":"false","touch_support":"0,false,false","js_fonts":"Arial,Arial Black,Arial Narrow,Arial Unicode MS,Book Antiqua,Bookman Old Style,Calibri,Cambria,Cambr"}"'result_params = '984eb0bda24963a23008f493d58a84e0'headers = {"Accept": "application/json, text/javascript, */*; q=0.01","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Cache-Control": "no-cache","Connection": "keep-alive","Host": "passport.migu.cn","Origin": "https://passport.migu.cn","Referer": "https://passport.migu.cn/login?sourceid=208003&apptype=0&forceAuthn=false&isPassive=false&authType=MiguPassport&passwordControl=0&display=web&referer=https://www.migu.cn/&logintype=1&qq=null&weibo=null&alipay=null&weixin=null&andPass=null&phoneNumber=&callbackURL=https%3A%2F%2Fwww.migu.cn%2F&relayState=","Sec-Fetch-Dest": "empty","Sec-Fetch-Mode": "cors","Sec-Fetch-Site": "same-origin","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36","X-Requested-With": "XMLHttpRequest",}self.form = {# 需要传递的构造出来的参数先写空,后面再使用update即可!"sourceID": "208003","appType": "0","relayState": "","loginID":"", # 加密后的账号"enpassword": "", # 加密后的密码"captcha": "","rememberMeBox": "1","fingerPrint": "", # 指纹"fingerPrintDetail":"", # 指纹detail"isAsync": True,}# 开启会话,保持连接!self.session = requests.Session()self.session.headers = headersself.get_cookie()# Python执行JS代码,生成所需参数并更新进表单self.js_object = self.compile_js()update_form = self.make_rsa_fingerprint(modules, publicExponent, details_params, result_params)update_form['loginID'] = self.make_encrypt_account(account, modules, publicExponent)update_form['enpassword'] = self.make_encrypt_password(password, modules, publicExponent)self.form.update(update_form)def compile_js(self):"""python直接执行本地编写的JS代码:return:"""with open('en_pwd.js', 'r') as f:js = f.read()return execjs.compile(js)def get_cookie(self):"""为了让会话携带cookie,所以进行如下操作。因为使用的session会话技术,所以即使不return也携带了cookie等一系列参数。:return:"""url = 'https://passport.migu.cn/'self.session.get(url)return self.session.cookiesdef make_encrypt_password(self, password, modulus, publicExponent):"""生成加密后的密码Args:password (str): 输入框内输入的原始密码modulus (str): rsa加密参数,两个质数的乘积,固定值,前一个请求获得publicExponent (str): rsa加密参数,大于1的奇数,固定值,前一个请求获得Returns:str: 加密后的密码"""return self.js_object.call('getEncryptedPwd', password, modulus, publicExponent)def make_encrypt_account(self, account, modulus, publicExponent):"""生成加密后的账号Args:account (str): 输入框内输入的账号modulus (str): 同上publicExponent (str): 同上Returns:str: 加密后的账号"""return self.js_object.call('getEncryptedAccount', account, modulus, publicExponent)def make_rsa_fingerprint(self, modulus, publicExponent, details, result):"""生成rsa指纹参数Args:details (str): 请求headers信息result (str): headers的加密信息,如果headers不改变这个值也是固定的Returns (dict):details: 加密后的fingerprint_details信息,fingerPrintDetail表单参数result: 加密后的fingerprint_result信息,fingerPrint表单参数"""return self.js_object.call('rsaFingerprint', modulus, publicExponent, details, result)
(2)然后,编写脚本发送请求并通过获取响应判断是否登录成功!
分析可知,我们应该向如上图中的url携带表单发送post请求!
第一步:直接向其发送请求,观察响应,是否为登录成功的界面!
def login(self):"""登录函数:return: """url = 'https://passport.migu.cn/authn'response = self.session.post(url, data=self.form)print(response.text)if __name__ == '__main__':# 注意:账号密码放在了info.txt中with open('info.txt', 'r') as f:info = json.loads(f.read())act = info['account']pwd = info['password']mg = Migu_login(act,pwd)mresponse = mg.login()
观察响应分析可知,当我们登录成功之后,页面进行了重定向,而不是直接返回给我们登录成功的界面:
第二步:所以下面我们要做的就是,通过分析浏览器登录,找到一系列的重定向的URL,并找到最终返回登录成功界面的URL:
第一个登录成功之后出现的URL,很容易知道这就是登录后的重定向,其URL的构造只需要拼接token参数即可(token参数在刚刚的响应中又存在,直接提取即可!)
第二个登录成功之后出现的URL,多次测试可知其URL是固定值!
第三个登录成功之后出现的URL,图一中多次测试也可知其URL是固定值;图二中可知此URL刚好是由上一个URL跳转来的;图三可知此URL的响应刚好是登录成功的界面!
第三步:构造登录成功之后的重定向URL,并通过依此请求模拟网站登录时候的一次次重定向跳转,最终达到获取咪咕登录成功的界面!
def login(self):"""登录函数:return:"""url = 'https://passport.migu.cn/authn'response = self.session.post(url, data=self.form)response_dict = json.loads(response.text)token = response_dict.get('result',{}).get('token', '')if not token:returnrest_url = ['https://passport.migu.cn/portal/sso/authn?callbackURL=&relayState=&token={}'.format(token),'https://passport.migu.cn/portal/sso/authn_success?relateToMiguPassport=1&callbackURL=&relayState=','https://passport.migu.cn/portal/home/profile?sourceid=100001','https://passport.migu.cn/portal/home/profile?sourceid=100001',]for ru in rest_url:response = self.session.get(ru)return responseif __name__ == '__main__':with open('info.txt', 'r') as f:info = json.loads(f.read())act = info['account']pwd = info['password']mg = Migu_login(act,pwd)mresponse = mg.login()print(mresponse.text)
(3)观察可知登录成功!
项目源码链接:
代码:
链接:https://pan.baidu.com/s/1zM19dGyvEpX4BuXsr_8s7Q
提取码:xkxh
复制这段内容后打开百度网盘手机App,操作更方便哦
相关文章:

登录+JS逆向进阶【过咪咕登录】(附带源码)
JS渗透之咪咕登录 每篇前言:咪咕登录参数对比 captcha参数enpassword参数搜索enpassword参数搜索J_RsaPsd参数setPublic函数encrypt加密函数运行时可能会遇到的问题此部分改写的最终形态JS代码:运行结果python编写脚本运行此JS代码:运行结果&…...

CTF秀 ctfshow WEB入门 web1-10 wp精讲
目录 web1_查看源码 web3_抓包 web4-9_目录文件 web10_cookie web1_查看源码 ctrlu 查看源码 web3_抓包 查看源码,无果 抓包,找到flag web4-9_目录文件 GitHub - maurosoria/dirsearch: Web path scanner 下载dirsearch工具扫一下就都出来了 web4-…...

centos安装inpanel
前置条件 安装python yum -y install python 安装 cd /usr/local git clone https://gitee.com/WangZhe168_admin/inpanel.git cd inpanel python install.py 安装过程需要设置账户 密码 端口号 我设置的是admin:admin 10050 使用 打开浏览器,输入 http://192.168.168.…...
聊聊PowerJob Worker的ServerAddress
序 本文主要研究一下PowerJob Worker的ServerAddress PowerJobAutoConfiguration tech/powerjob/worker/autoconfigure/PowerJobAutoConfiguration.java BeanConditionalOnMissingBeanpublic PowerJobSpringWorker initPowerJob(PowerJobProperties properties) {PowerJobPr…...
师傅带练|大数据人工智能在线实习项目特色
大数据人工智能八大在线实习项目: 某实习网站招聘信息采集与分析 股票价格形态聚类与收益分析 某平台网络入侵用户自动识别 某平台广东省区采购数据分析 产品订单的数据分析与需求预测 基于注意力机制的评论者满意度分析 基于锅炉工况实现…...

ant-design-vue表格嵌套子表格,实现子表格有数据才显示左侧加号图标
ant-design-vue表格嵌套子表格,实现子表格有数据才显示左侧加号图标 通过使用插槽的方式,以下为全部项目的代码,关键的代码就两块,看注释 <template><a-card><a-form class"kit_form" ref"formRef…...

浅谈垃圾回收、内存泄漏与闭包
什么是垃圾? 在js中,垃圾通常指的是不再被程序使用的内存或对象。也就是说,垃圾是指程序中分配的内存空间或对象,但不再被程序使用或无法被访问到的内容 function createIncrease() {const doms new Array(100000).fill(0).map((…...

2 月 7 日算法练习- 数据结构-树状数组
树状数组 lowbit 在学习树状数组之前,我们需要了解lowbit操作,这是一种位运算操作,用于计算出数字的二进制表达中的最低位的1以及后面所有的0。 写法很简单: int lowbit(int x){return x &am…...
[AIGC] 开源流程引擎哪个好,如何选型?
开源流程引擎是指一种自动化的工作流解决方案,它可以帮助你管理和协调你的业务流程和决策。但是,在开源世界里,有许多不同的流程引擎可以选择。因此,如何选择适合你的开源流程引擎,是一个具有挑战性和价值的话题。 文章…...

服务器使用过程中遇到常见故障及解决方案(包括蓝屏死机、无法删除的文件如何清理、网络卡、服务器连接不上等)
互联网时代,服务器的安全性和稳定性尤为重要,支撑着整个互联网行业的信息和数据安全。最近经常有客户咨询服务器的日常故障排除方法。由于服务器复杂的硬件结构和繁琐的运行原理,经常会出现这样那样的问题,有时即使是最小的问题也…...
【推荐算法】userid是否需要建模
看到一个din的源码,将userid也构建了emb table。 于是调研了一下。即推荐算法需要建模userid吗? 深度学习推荐算法中user-id和item-id是否需要放入模型中作为特征进行训练呢? 深度学习推荐算法中user-id和item-id是否需要放入模型中作为特…...

图解支付-金融级密钥管理系统:构建支付系统的安全基石
经常在网上看到某某公司几千万的个人敏感信息被泄露,这要是放在持牌的支付公司,可能就是一个非常大的麻烦,不但会失去用户的信任,而且可能会被吊销牌照。而现实情况是很多公司的技术研发人员并没有足够深的安全架构经验来设计一套…...
新概念英语第二册(58)
【New words and expressions】生词和短语(16) blessing n. 福分,福气 disguise n. 伪装 tiny adj. 极小的 possess v. 拥有 cursed …...
java和javascript的区别和联系
Java和JavaScript是两种非常流行的编程语言,尽管它们的名称相似,但实际上它们在设计、用途和运行环境等方面有很大的不同。以下是Java和JavaScript之间的主要区别和联系: 区别 设计目的和用途: Java 是一种通用的、面向对象的编程…...

uniapp中配置开发环境和生产环境
uniapp在开发的时候,可以配置多种环境,用于自动切换IP地址,用HBuilder X直接运行的就是开发环境,用HBuilder X发布出来的,就是生产环境。 1.使用HBuilder X创建原生的uniapp程序 选择vue3 2.什么都不改,就…...
prometheus之mysqld_exporter部署
mysql_exporter部署 下载解压压缩包 mkdir /opt/mysqld_exporter/ cd /opt/mysqld_exporter/ # 修改为自己的软件下载地址 wget http://soft.download/soft/linux/prometheus/mysqld_exporter/mysqld_exporter-0.14.0.linux-amd64.tar.gz tar -zxvf mysqld_exporter-0.14.0.…...
<网络安全>《19 安全态势感知与管理平台》
1 概念 安全态势感知与管理平台融合大数据和机器学习技术,提供可落地的安全保障能力,集安全可视化、监测、预警和响应处置于一体。它集中收集并存储客户I环境的资产、运行状态、漏洞、安全配置、日志、流量等安全相关数据,内置大数据存储和多…...

sqli靶场完结篇!!!!
靶场,靶场,一个靶场打一天,又是和waf斗智斗勇的一天,waf我和你拼啦!! 31.多个)号 先是一套基本的判断 ,发现是字符型,然后发现好像他什么都不过滤?于是开始poc 3213131…...

堆结构的解读
对于数据结构堆来说,堆事一种特定的数据结构,其与二叉树非常类似,但是又与二叉树有所不同,其不同点在于堆不需要左右指针指向孩子节点,而给定一个数组,将数组中的元素进行特定排序之后,就可以得…...
7、Qt5开发及实列(笔记)
文章目录 第二章 Qt5模板库、工具类及控件2.2 容器类2.2.1 QList类 # 2.3 QVariant类 #2.4 算法及正则表达式2.5控件 第二章 Qt5模板库、工具类及控件 2.2 容器类 2.2.1 QList类 //2.2容器类 - QList类QList<QString> list;//声明了一个QList<QString>栈对象{QSt…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...