如需转载请注明出处.欢迎小伙伴一起讨论技术.
逆向网址: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 或 return
var 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处的地