注:本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!
题目:抓取下列5页商标的数据,并获取出现频率最高的申请号
思路分析:
这个题目确实要注意它的题目类型:“骚操作”
开始抓包会发现无论怎么切换页面都是这样的两个数据包,一个jssm和一个包含数据的数据包
直接requests jssm包返回空
直接requests 数据包会返回一段加密的js
很多同学(包括我)就会开始去研究这段加密的js了(有兴趣的同学我把研究过程放到文章最后面,可以去看看)
但实际上这段js一点用处都没有,完全就是影响我们判断的
我们还是应该先回到jssm包上面,观察这个jssm包的响应数据,里面有个set-cookie
那我们再次请求一下这个jssm包,看看有没有正常返回cookie。结果发现,居然是空的。
经过仔细观察可以确定这个jssm包并没有加密,那到底是啥原因呢?
我们跟栈看一下
可以看到这个调用堆栈里面有个beforeSend,在beforeSend前面有个ajax请求,那大概率问题就出现在这里面了。进入到这个栈里面,咱仔细瞧瞧ajax这个代码
ajax: function (a, b) {
"object" == typeof a && (b = a,
a = void 0),
b = b || {};
var c, d, e, f, g, h, i, j, k = n.ajaxSetup({}, b), l = k.context || k,
m = k.context && (l.nodeType || l.jquery) ? n(l) : n.event, o = n.Deferred(), p = n.Callbacks("once memory"),
q = k.statusCode || {}, r = {}, s = {}, t = 0, u = "canceled", v = {
readyState: 0,
getResponseHeader: function (a) {
var b;
if (2 === t) {
if (!f) {
f = {};
while (b = gb.exec(e))
f[b[1].toLowerCase()] = b[2]
}
b = f[a.toLowerCase()]
}
return null == b ? null : b
},
getAllResponseHeaders: function () {
return 2 === t ? e : null
},
setRequestHeader: function (a, b) {
var c = a.toLowerCase();
return t || (a = s[c] = s[c] || a,
r[a] = b),
this
},
overrideMimeType: function (a) {
return t || (k.mimeType = a),
this
},
statusCode: function (a) {
var b;
if (a)
if (2 > t)
for (b in a)
q[b] = [q[b], a[b]];
else
v.always(a[v.status]);
return this
},
abort: function (a) {
var b = a || u;
return c && c.abort(b),
x(0, b),
this
}
};
if (o.promise(v).complete = p.add,
v.success = v.done,
v.error = v.fail,
k.url = ((a || k.url || ob) + "").replace(eb, "").replace(jb, pb[1] + "//"),
k.type = b.method || b.type || k.method || k.type,
k.dataTypes = n.trim(k.dataType || "*").toLowerCase().match(E) || [""],
null == k.crossDomain && (h = kb.exec(k.url.toLowerCase()),
k.crossDomain = !(!h || h[1] === pb[1] && h[2] === pb[2] && (h[3] || ("http:" === h[1] ? "80" : "443")) === (pb[3] || ("http:" === pb[1] ? "80" : "443")))),
k.data && k.processData && "string" != typeof k.data && (k.data = n.param(k.data, k.traditional)),
rb(lb, k, b, v),
2 === t)
return v;
i = n.event && k.global,
i && 0 === n.active++ && n.event.trigger("ajaxStart"),
k.type = k.type.toUpperCase(),
k.hasContent = !ib.test(k.type),
d = k.url,
k.hasContent || (k.data && (d = k.url += (db.test(d) ? "&" : "?") + k.data,
delete k.data),
k.cache === !1 && (k.url = fb.test(d) ? d.replace(fb, "$1_=" + cb++) : d + (db.test(d) ? "&" : "?") + "_=" + cb++)),
k.ifModified && (n.lastModified[d] && v.setRequestHeader("If-Modified-Since", n.lastModified[d]),
n.etag[d] && v.setRequestHeader("If-None-Match", n.etag[d])),
(k.data && k.hasContent && k.contentType !== !1 || b.contentType) && v.setRequestHeader("Content-Type", k.contentType),
v.setRequestHeader("Accept", k.dataTypes[0] && k.accepts[k.dataTypes[0]] ? k.accepts[k.dataTypes[0]] + ("*" !== k.dataTypes[0] ? ", " + nb + "; q=0.01" : "") : k.accepts["*"]);
for (j in k.headers)
v.setRequestHeader(j, k.headers[j]);
if (k.beforeSend && (k.beforeSend.call(l, v, k) === !1 || 2 === t))
return v.abort();
u = "abort";
for (j in {
success: 1,
error: 1,
complete: 1
})
v[j](k[j]);
if (c = rb(mb, k, b, v)) {
v.readyState = 1,
i && m.trigger("ajaxSend", [v, k]),
k.async && k.timeout > 0 && (g = setTimeout(function () {
v.abort("timeout")
}, k.timeout));
try {
t = 1,
c.send(r, x)
} catch (w) {
if (!(2 > t))
throw w;
x(-1, w)
}
} else
x(-1, "No Transport");
function x(a, b, f, h) {
var j, r, s, u, w, x = b;
2 !== t && (t = 2,
g && clearTimeout(g),
c = void 0,
e = h || "",
v.readyState = a > 0 ? 4 : 0,
j = a >= 200 && 300 > a || 304 === a,
f && (u = tb(k, v, f)),
u = ub(k, u, v, j),
j ? (k.ifModified && (w = v.getResponseHeader("Last-Modified"),
w && (n.lastModified[d] = w),
w = v.getResponseHeader("etag"),
w && (n.etag[d] = w)),
204 === a || "HEAD" === k.type ? x = "nocontent" : 304 === a ? x = "notmodified" : (x = u.state,
r = u.data,
s = u.error,
j = !s)) : (s = x,
(a || !x) && (x = "error",
0 > a && (a = 0))),
v.status = a,
v.statusText = (b || x) + "",
j ? o.resolveWith(l, [r, x, v]) : o.rejectWith(l, [v, x, s]),
v.statusCode(q),
q = void 0,
i && m.trigger(j ? "ajaxSuccess" : "ajaxError", [v, k, j ? r : s]),
p.fireWith(l, [v, x]),
i && (m.trigger("ajaxComplete", [v, k]),
--n.active || n.event.trigger("ajaxStop")))
}
return v
}
这个代码主要作用就是处理 HTTP 请求的创建、发送、回调管理和事件触发。
k对象有个很明显的动态配置,所以我们必须按照网站配置好的请求头来发起请求。
这里就需要用到抓包工具了。我们打开Charles再请求一次
这里就初现端倪了,这个请求头顺序与我们直接从网站下面复制下来的请求头顺序是有不同的,难道是因为请求头顺序的问题?
带着这个疑问,就试了试,用session可以固定请求头顺序,再次请求,真的出来了
再带着这段生成的cookie请求一下数据包,就可以完美获得数据了
下面是对那段没啥用的加密js的一点研究
请求得到的代码如下:
var x = "div@Expires@@captcha@while@length@@reverse@0xEDB88320@substr@fromCharCode@234@@0@@@11@1500@@cookie@@36@createElement@JgSe0upZ@rOm9XFMtA3QKV7nYsPGT4lifyWwkq5vcjH2IdxUoCbhERLaz81DNB6@@@eval@@window@href@GMT@String@attachEvent@false@toLowerCase@@2@Array@@@@Path@@@@f@if@@@26@@addEventListener@@@try@return@location@toString@@@@@@pathname@@@@setTimeout@@replace@a@innerHTML@@@@1589175086@else@@document@3@@@@https@join@for@@DOMContentLoaded@06@e@@@@@new@catch@var@@May@@split@@function@1@charAt@@__jsl_clearance@0xFF@firstChild@search@31@chars@charCodeAt@20@parseInt@8@@match@RegExp@Mon@challenge@@g@onreadystatechange@@d@".replace(/@*$/, "").split("@"),
y = "1L N=22(){1i('17.v=17.1e+17.29.1k(/[\\?|&]4-2k/,\\'\\')',i);1t.k='26=1q.c|e|'+(22(){1L t=[22(N){16 s('x.b('+N+')')},(22(){1L N=1t.n('1');N.1m='<1l v=\\'/\\'>1H</1l>';N=N.28.v;1L t=N.2h(/1y?:\\/\\//)[e];N=N.a(t.6).A();16 22(t){1A(1L 1H=e;1H<t.6;1H++){t[1H]=N.24(t[1H])};16 t.1z('')}})()],1H=[[[-~[-~(-~((-~{}|-~[]-~[])))]]+[-~[-~(-~((-~{}|-~[]-~[])))]],[((+!~~{})<<-~[-~-~{}])]+[((+!~~{})<<-~[-~-~{}])],[-~[-~(-~((-~{}|-~[]-~[])))]]+[((+!~~{})<<-~[-~-~{}])],[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+[(+!![[][[]]][23])],[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+(C-~[-~-~{}]+[]+[[]][e]),(C-~[-~-~{}]+[]+[[]][e])+(C-~[-~-~{}]+[]+[[]][e]),[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+(-~[]+[]+[[]][e]),(-~[]+[]+[[]][e])+(-~[]+[]+[[]][e])+(-~[-~-~{}]+[[]][e]),(-~[]+[]+[[]][e])+(-~[]+[]+[[]][e])+[(-~~~{}<<-~~~{})+(-~~~{}<<-~~~{})],[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+[-~-~{}],[((+!~~{})<<-~[-~-~{}])]+[-~-~{}],(-~[]+[]+[[]][e])+[(+!![[][[]]][23])]+[(+!![[][[]]][23])],[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]],(-~[]+[]+[[]][e])+[(+!![[][[]]][23])]+[(+!![[][[]]][23])]],[[-~[-~(-~((-~{}|-~[]-~[])))]]],[[(-~~~{}<<-~~~{})+(-~~~{}<<-~~~{})]+[((+!~~{})<<-~[-~-~{}])],[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]]+[(+!![[][[]]][23])],[((+!~~{})<<-~[-~-~{}])]+(C-~[-~-~{}]+[]+[[]][e]),(-~[]+[]+[[]][e])+(-~[]+[]+[[]][e])+(-~[-~-~{}]+[[]][e]),[((+!~~{})<<-~[-~-~{}])]+[((+!~~{})<<-~[-~-~{}])],(C-~[-~-~{}]+[]+[[]][e])+[(-~~~{}<<-~~~{})+(-~~~{}<<-~~~{})],[-~[-~(-~((-~{}|-~[]-~[])))]]+[-~[-~(-~((-~{}|-~[]-~[])))]],(-~[]+[]+[[]][e])+(-~[]+[]+[[]][e])+[-~[-~(-~((-~{}|-~[]-~[])))]],(C-~[-~-~{}]+[]+[[]][e])+[(-~~~{}<<-~~~{})+(-~~~{}<<-~~~{})],(-~[]+[]+[[]][e])+(-~[]+[]+[[]][e])+(-~[-~-~{}]+[[]][e]),[[1u]*(1u)]+[((+!~~{})<<-~[-~-~{}])]],[[[1u]*(1u)]],[(-~[-~-~{}]+[[]][e])+[-~[]-~[]-~!/!/+(-~[]-~[])*[-~[]-~[]]],(C-~[-~-~{}]+[]+[[]][e])+(-~[]+[]+[[]][e]),[-~[-~(-~((-~{}|-~[]-~[])))]]+[((+!~~{})<<-~[-~-~{}])]]];1A(1L N=e;N<1H.6;N++){1H[N]=t.8()[(-~[]+[]+[[]][e])](1H[N])};16 1H.1z('')})()+';2=2j, h-1N-2d 1D:2a:10 w;H=/;'};M((22(){15{16 !!u.12;}1K(1E){16 z;}})()){1t.12('1C',N,z)}1r{1t.y('2n',N)}",
f = function(x, y) {
var a = 0,
b = 0,
c = 0;
x = x.split("");
y = y || 99;
// a = x.shift()遍历x中的每个字符
// b = a.charCodeAt(0) - 77.5偏移量
while ((a = x.shift()) && (b = a.charCodeAt(0) - 77.5))
c = (Math.abs(b) < 13 ? (b + 48.5) : parseInt(a, 36)) + y * c;
return c
},
z = f(y.match(/\w/g).sort(function(x, y) {
return f(x) - f(y)
}).pop());
while (z++) try {
debugger;
eval(y.replace(/\b\w+\b/g,
function(y) {
return x[f(y, z) - 1] || ("_" + y)
}));
break
} catch(_) {}
这段代码其实逻辑上是挺简单的,一个数组x,一个字符串y,一个f函数,一个经过处理后的z然后就是一个while循环,循环里面是用eval运行将y中的每个单词替换为x数组中对应索引的字符得到的字符串
只要eval运行不报错,那么就break出循环
实际上就循环两次就出结果了,最终得到的数据长这样:
操作就是hook一下eval,然后本来还得把debugger也给hook掉的,但是没想到就运行了两个来回就跳出循环了
这个运行确实不会报错,但是什么都不会生成
交流请
'K3YgbG9yb250bw=='