参数跟丢了之JS生成器和包装器
如需转载请注明出处.欢迎小伙伴一起讨论技术.
逆向网址:aHR0cHM6Ly91bmlvbi5qZC5jb20vcHJvTWFuYWdlci9pbmRleD9wYWdlTm89MQ==
跟踪接口:aHR0cHM6Ly9hcGkubS5qZC5jb20vYXBp
跟踪参数:h5st
本文目标:记录学习下自定义的生成器和包装器,不做具体的参数加密逻辑分析
直接启动器进入,跟战后找到以下代码位置.
问题:跟栈的时候在case20的位置找到最终h5st的结果值,再往上跟栈找不到生成和拼接h5st的位置
那么就尝试理解下这段代码
function X() {return (X = (0,u.Z)(h().mark((function e(t) {var n, o, a, i, r, l, s, c, d, u, p, f, g, v, m, b, x, w, _, y, C;return h().wrap((function (e) {for (; ;)switch (e.prev = e.next) {case 0:return n = t.functionId,o = t.method,a = void 0 === o ? "GET" : o,i = t.params,r = t.isEncode,l = void 0 === r || r,s = t.needCode,c = void 0 !== s && s,d = t.appid,u = void 0 === d ? M.ZP.APP_ID.UNION_PC : d,p = t.payload,f = void 0 === p ? {} : p,g = "https://api.m.jd.com/api?",v = $().get("__jda"),m = null == v ? void 0 : v.split(".")[1],b = {functionId: n,appid: u,_: Date.now(),loginType: "3",uuid: m,"x-api-eid-token": Q},e.prev = 5,x = new window.ParamsSign({appId: "586ae"}),i = H(H({}, i), {}, {clientPageId: "jingfen_pc"}),w = {functionId: n,appid: u,body: F()(k()(i)).toString()},e.next = 12,x.sign(w);case 12:_ = e.sent,y = _.h5st,b = H(H({}, b), {}, {h5st: encodeURI(y)}),e.next = 20;break;case 17:e.prev = 17,e.t0 = e.catch(5),console.log(e.t0);case 20:"POST" === a ? (C = "body=".concat(l ? encodeURIComponent(k()(i)) : k()(i)),f.data = C) : b.body = l ? encodeURIComponent(k()(i)) : i,g += (0,Z.L7)(b),f.extendParams || (f.extendParams = {});try {n && (f.extendParams.functionId = n),i && i.funName && (f.extendParams.funName = i.funName)} catch (e) {console.log("error: ", e)}return e.next = 26,J(H(H({method: a,url: g}, f), {}, {withCredentials: !0,needCode: c})).then((function (e) {return !e.page && e.pageNo && (e.page = {pageNo: e.pageNo,pageSize: e.pageSize,hasNext: e.hasNext,totalCount: e.total || e.totalNum}),e}));case 26:return e.abrupt("return", e.sent);case 27:case "end":return e.stop()}}), e, null, [[5, 17]])})))).apply(this, arguments)
}
知识点1: (0,u.Z),这种写法;
作用1,在 JavaScript 中,u.Z 是一个函数。如果你直接调用 u.z(),那么 u将成为函数 Z的上下文(即 this
),而 (0, u.Z)
的写法可以确保 u.Z
在全局上下文或其他上下文中被调用,避免 this
被意外改变
作用2,如果代码经过工具(比如 Babel)处理,它可能将一些较复杂的函数调用转化为 (0, u.Z)
形式。这样做的目的是保证代码在不同环境下都能正常工作,特别是在模块系统中,有时需要显式地调用函数而不是通过方法调用
知识点2:h().mark函数,进入mark函数,扣下主要代码
var h = "suspendedStart", d = "suspendedYield", f = "executing", p = "completed", v = {};
function g() { }
function m() { }
function b() { }
var y = {};
s(y, i, (function () {return this
}
));
//E的原型是一个数组
var w = Object.getPrototypeOf, _ = w && w(w(M([])));
_ && _ !== n && o.call(_, i) && (y = _);
var E = b.prototype = g.prototype = Object.create(y);
//...省略一大段
t.mark = function (t) {return Object.setPrototypeOf ? Object.setPrototypeOf(t, b) : (t.__proto__ = b,s(t, l, "GeneratorFunction")),//返回一个对象,设置该对象继承E的原型t.prototype = Object.create(E),t
}
从上面可以看出,最终t是返回一个生成器函数
知识点3:h().wrap,扣下主要代码
//包装器
function c(t, e, n, o) {var r = e && e.prototype instanceof g ? e : g, i = Object.create(r.prototype), a = new C(o || []);//生成器的核心逻辑return i._invoke = function (t, e, n) {//o为记录状态,next、throw 或 returnvar o = h;return function (r, i) {if (o === f)throw new Error("Generator is already running");if (o === p) {if ("throw" === r)throw i;return A()}for (n.method = r,n.arg = i; ;) {var a = n.delegate;if (a) {var l = O(a, n);if (l) {if (l === v)continue;return l}}if ("next" === n.method)n.sent = n._sent = n.arg;else if ("throw" === n.method) {if (o === h)throw o = p,n.arg;n.dispatchException(n.arg)} else"return" === n.method && n.abrupt("return", n.arg);o = f;//代用传下来的函数,在u中调用var s = u(t, e, n);if ("normal" === s.type) {if (o = n.done ? p : d,s.arg === v)continue;return {value: s.arg,done: n.done}}"throw" === s.type && (o = p,n.method = "throw",n.arg = s.arg)}}}(t, n, a),i
}
function u(t, e, n) {try {return {type: "normal",//最终调用位置,使用call方法arg: t.call(e, n)}} catch (t) {return {type: "throw",arg: t}}
}
//把包装器暴露c函数,让外部使用wrap方法调用
t.wrap = c;
wrap方法是一个包装器,里面封装了一个生成器
那么理解了关键的两个函数mark和wrap,大概就知道wrap里面的参数中的匿名函数什么时候执行
a 使用next方法每次调用生成器,进入生成器i._invoke
b 生成器内部调用u函数,最后使用call方法调用匿名函数
c 在匿名函数中每个case的结尾都会定义下一次执行的case
最后推测h5st参数是在匿名函数中case0位置生成,但是在case0代码块并没有发现拼接h5st参数的位置,而关键点是在window.ParamsSign这个类中,关键的加密逻辑都在里面
最终的生成结果会在s对象的PromiseResult属性中,这也是很坑的,很容易看不到导致跟丢了参数;
就可以回答开始提出的问题,在case20处可以看到h5st的结果,case处没有找到拼接h5st处的地