赞
踩
url:'aHR0cHM6Ly93d3cucGluZHVvZHVvLmNvbS9ob21lL2JveXNoaXJ0Lw=='
目标:抓取到商品数据,难点:补环境+export时间戳
声明:本文只作学习研究,禁止用于非法用途,否则后果自负,如有侵权,请告知删除,谢谢!(手动狗头)
很明显,咱要的东西就是在这,然后要逆向的参数就是anti_content
直接搜索
发现有两个地方,很开心,分别打上断点,然后刷新页面
发现断到这里了,然后发现此时还没有找到anti_content
然后走t.next = 10,最后return Object(l.a)();
控制台打印一下Object(l.a)();
发现是个Promise对象(pending状态),那么我们就打印看看它的返回值
发现就在这里!进去l.a看看
走y这个函数,此时this指向什么?就是new y,也就是y的实例,然后怎么办呢?找y函数呗
仔细一瞧,发现y的函数就在下面
- function y() {
- return (y = u(i.a.mark(function e() {
- return i.a.wrap(function(e) {
- for (; ; )
- //前面都是乱七八糟的,不用看
- switch (e.prev = e.next) { //一开始就是0=0
- case 0:
- if (r) {
- e.next = 3;
- break
- }
- return e.next = 3, //这里敲个断点
- new Promise(function(e) {//promise对象
- c.push(e),
- f()
- }
- );
- case 3:
- return e.next = 5,
- r.messagePackSync();//这里也敲一个断点
- case 5:
- return e.abrupt("return", e.sent);
- case 6:
- case "end":
- return e.stop()//结束值
- }
- }, e)
- }))).apply(this, arguments)
- }
仔细分析发现,先来到return e.next = 3 ,先看看这个promise的结果
很明显,不是这里,接着来到r.messagePackSync();//这里也敲一个断点
歪日,发现结果就在这,进去干
wc,混淆,不要怕,这是好消息至少位置对了,不然为啥要混淆,此地无银三百两((//̀Д/́/)),先断到promise对象这里
发现值已经生成了,很明显最后的n[r("0x15c", "S]Zj")](t, nr("0x1bb", "A3e0"))有很大问题
发现咱要的结果出来了,嘿嘿,简化一下看看,就是dt()
跳过来终于发现咱要的就是dt()函数,提前预定window.dog = dt()
将所有代码扣下来拉到Notepad++里面,折叠所有层次
发现是webpack,简单~首要任务就是找调度器
先把dt()所在的层次扣下来
发现就是fbeZ这一个层次,直接弄过去,然后顺带在dt函数执行完了之后加个window.dog = dt()
那么我们之后就是要执行下fbeZ这个函数,让得到的anti_content值赋值给window.dog变量
那么想要执行webpack,第一步找调度器
找到最开始的fbeZ,打印输出n函数,然后跳转到调度器
发现就在这里,那咱甭跟它客气,直接全扣,
然后再增加这两个地方用来输出r ,以及将调度器赋值给全局变量
找到调用fbeZ所需要的三个参数
- function get_data(){
- //获得三个参数
- var e = {"i":"fbeZ","l":false,"exports":{}},
- t = {},
- n = window.loaders //就是n函数
-
- fbeZ(e,t,n)//执行fbeZ
- console.log(window.dog())
-
- }
执行结果:
又到了愉快的补环境时间~
直接搜索8oxB
发现在这个文件,直接扣下来
同理可得YuTi
找到这个地方 ("0x3f", "LZ%H")in re[P];
注意:搜索的时候必须要取消空格,不然根本搜不到
很明显,看到document就知道精彩的东西来了,开始一些基本的补环境,那就是windows、location、document、history、navigator
location
navigator
screen
在控制台分别打印输出
- // 一、补充window环境
- window = {}
- window = global;
-
- //二、补充 document
- document = {
- referrer: 'https://www.pinduoduo.com'
- }
-
- Document = function Document() {
-
- }
- Object.setPrototypeOf(document, Document.prototype)
-
-
- Document.prototype.addEventListener = function () {
-
- }
- Document.prototype.cookie = '_nano_fp=Xpmon5EoXpCqX0doXT_ubdWsBT4vZ2vjB~PCm7Pn; api_uid=rBUUO2W4rXuDCDYK0P5+Ag=='
-
- //三、补充 location
- location = {
- 'hash': "",
- 'host': "www.pinduoduo.com",
- 'hostname': "www.pinduoduo.com",
- 'href': "https://www.pinduoduo.com/home/girlclothes/",
- 'origin': "https://www.pinduoduo.com",
- 'pathname': "/home/girlclothes/"
- }
-
- Location = function Location() {
-
- }
-
- Object.setPrototypeOf(location, Location.prototype)
-
- // 四、补充 navigator
- navigator = {
- appCodeName: "Mozilla",
- appName: "Netscape",
- appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0",
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0'
- }
-
- Navigator = function Navigator() {
-
- }
- Object.setPrototypeOf(navigator, Navigator.prototype)
-
- Navigator.prototype.hasOwnProperty = function () {
- return true
- }
-
- // 五、补充 screen
- screen = {
- // width: 1728,
- }
-
- Screen = function Screen() {
-
- }
- Object.setPrototypeOf(screen, Screen.prototype)
-
-
- // 六、补充 history对象
- history = {}
- History = function History() {
-
- }
- Object.setPrototypeOf(history, History.prototype)
- History.prototype.back = function () {
-
- }
-
发现此时已经出了值,但是真的那么简单吗?
那我们拿到值去试一试
很明显,没那么简单
那么我们到底是哪里的问题呢?
第一反应是补环境没补全,可以用node-inspect 打印输出看看,到底哪些值没有弄。先配置好proxy代理(说白了就是hook)
- // proxy代理声明
- function proxy_watch(obj) {
-
- return new Proxy(obj, {
- get(target, p, receiver) {
- debugger;
- let val = Reflect.get(...arguments);
- console.log("get", target, "=获取属性>", p, "=值>", val);
- return val;
- },
- set(target, p, value, receiver) {
- debugger;
- console.log("set", target, "=设置属性>", p, "=值>", value);
- return Reflect.set(...arguments);
- }
- });
- };
-
- window = proxy_watch(window)
- document = proxy_watch(document)
- location = proxy_watch(location)
- navigator = proxy_watch(navigator)
-
随便补齐一点点,发现还是不能通过,后来我意识到这样补下去没有尽头,于是我就思考,有些浏览器不能执行,而node里可以执行,也就是检测到node
www.zhihu.com/question/584082357
- //在node中删去以下的东西
- delete global;
- delete Buffer;
发现还是没有获得值
让我们先思考下,整个调用流程是什么,首先毫无疑问就是先用调度器(自执行函数)这里毫无疑问,所有的都要执行
然后将调度器赋值给全局变量window.loaders,作为参数n传递给fbeZ,问题就在于fbeZ执行过程中,绝对有哪一部分没有执行到。
先看看dt有没有执行到
执行了并赋值了,说明上面的代码都没问题,继续往下拉代码看看
分别在每一段可疑的地方插上的console输出,看看到底哪里没有执行
那么我们可以直接在网站上找到t[_("0x1d0", "&CF7")]
发现是exports,已经是说浏览器上加载了这个exports,而本地环境中没有去加载
- var lt = new ut;
- t[_("0x1d0", "&CF7")] = function() {
- var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}
- , t = _;
- return e[j] && re && lt[t("0x1f", "@0Zy")](e[j]), //发现此时执行的lt(ut对象的实例)
- lt
- }
- /*
- t("0x1f", "@0Zy") == "updateServerTime"
- e[j] == 1706758046 时间戳
- 很明显,咱的对象就是围绕时间,没有经过exports,我们用的都是错误的时间,导致得到的结果就是错误的,那么如何才能保持时间正确呢?
- 两种方法:
- 一、找到ut函数,去寻找跟时间有关的东西,然后把它变成动态的
- 二、
- */
-
然后往下查找
发现该地方出现了时间戳,很可疑,打上断点然后刷新页面,此时 e[j] = undefined,t("0x135", "O3]W") = “updateServerTime”
很明显就是将一个固定的数值当作时间戳赋值过去,那么我们要做的正是替换 改成如下代码,获得动态的时间戳
this[t("0x135", "O3]W")](new Date().getTime());
此时结果出来了,看着像是我们瞎猫碰到死耗子,看着挺简单的,但实际上这东西需要思考几小时才能发现,狗
- return e[j] && re && lt[t("0x1f", "@0Zy")](e[j]), //发现此时执行lt,lt[t("0x1f", "@0Zy")]作为函数
-
- lt["updataServerTime"](e[j])
- //e[j]此时为固定值,所以需要持续更新的时间戳
到此为止我们已经把恶意的时间戳给补齐,也算是个大坑,平常谁会想到这个时间竟然是个检测点,太nb了。
现在我们就去找找updataServerTime到底是怎么更新时间戳的,我们直接全局搜索servertime
分别到这两个地方打上断点,刷新页面寻找时间是如何过来的
发现此时断点断在了这里
e是从t.sent传过来的,而t.sent作为上一步的返回值,需要找的就是W(),进入到l后发现又是个控制流
从上到下执行时,很明显case2后面都是一些判断逻辑,而t.server_time是已经拿到时间戳了,真相就在我打断点的那一行,也就是o.a
- Object(o.a)("/api/server/_stm", "get", {}, "https://apiv2.pinduoduo.com");
- /*
- 这里我们可以知道传入参数分别为url,请求方式是get请求
- 然后我们就要找o.a函数
- */
-
- function i(e, t, n, r) {
- //t == get, e、r都是一段url
- "" === t && (t = "post");
- var i = {
- url: e,
- method: t,
- data: n
- };
- //返回的结果是一个Promise对象
- return new Promise(function(e, t) {
- Date.now();//猜的没错是个时间戳
- o.a.create({
- headers: {
- Accept: "application/json, text/javascript",
- "Content-Type": "application/json;charset=UTF-8"
- },
- timeout: 3e3,
- baseURL: r || "https://home-api.pinduoduo.com"
- })(i).then(function(t) {
- e(t)
- }).catch(function(e) {
- t(e)
- })
- }
- )
- }
- //到这里我有点好奇baseurl + url(e+r) 发送请求后的返回值是啥
发现就是请求该接口返回到server_time的值,而promise对象就是调用接口
此时e已经是时间戳了,作为一个对象{ serverTime: e };
传递给i函数,new i是个实例,我们可以跳到i函数看看
发现这不就是export
这个t[G]就是时间戳,X = 879609302220 也是最初的值,要来到这里重新赋值
这就是恶意时间戳,我只能说一句NB
歪日,干了那么久终于要结束了,都不知道以后瑞数、Akamai、航司、227该如何下手,诶,走一步看一步吧
js代码就不弄上来了,2w多行自己去扣吧,现在就扣下python代码,然后给你们看看执行结果吧
- import requests
- import subprocess
- from functools import partial
- subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
- import execjs
-
- headers = {
- "Accept": "application/json, text/javascript",
- "Accept-Language": "zh-CN,zh;q=0.9",
- "Cache-Control": "no-cache",
- "Connection": "keep-alive",
- "Origin": "https://www.pinduoduo.com",
- "Pragma": "no-cache",
- "Referer": "https://www.pinduoduo.com/",
- "Sec-Fetch-Dest": "empty",
- "Sec-Fetch-Mode": "cors",
- "Sec-Fetch-Site": "same-site",
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0",
- "sec-ch-ua": "^\\^Not",
- "sec-ch-ua-mobile": "?0",
- "sec-ch-ua-platform": "^\\^Windows^^"
- }
-
- url = "https://apiv2.pinduoduo.com/api/gindex/tf/query_tf_goods_info"
- with open('anti_content.js', 'r', encoding='utf-8') as f:
- JS = f.read()
- code = execjs.compile(JS)
- anti_content = code.call('get_data')
- print(anti_content)
- params = {
- "tf_id": "TFRQ0v00000Y_13397",
- "page": "1",
- "size": "100",
- "anti_content": anti_content
- }
- response = requests.get(url, headers=headers, params=params)
-
- print(response.text)
-
OK,完结撒花,记录下踩的坑,还得继续学习
提前祝各位新年快乐!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。