不想再上班啦

V1

2022/12/06阅读:80主题:萌绿

JS逆向系列-猿人学web第一题

JS逆向系列-猿人学web第一题

富有的,给他更多;没有的,把他仅有的也拿走。

爬虫真是一个有意思的东西,简单的页面,不费力就能爬取,复杂的页面,费尽心思也不知如何爬取。就像玩游戏,刚开始时在新手村砍稻草人,等出了新手村,你面对的直接就是最终boss。

最近有点喜欢爬虫js逆向了,喜欢那种被虐的死去活来,悲痛欲绝的感觉(没错说的就是你,只狼)。

最近学习爬虫,发现网上免费的资料教程,要么很简单,在headers中添加user-agent就能爬取,要么很高深,照着读了好几篇也搞不懂到底是怎么实现的,并且还因为某种原因不会放出所有的代码,只能浅尝辄止。

所以说:绝知此事要躬行。于是打算好好钻研钻研爬虫,多多动手,把过程也记录下来,跟大家分享,我尽可能用最详细的描述,如果你跟着我一起学的话,你也一定能学会噢!

今天要做的是猿人学js逆向的web第一题:抓取机票的价格。准备好小板凳哦,本文较长。

此题标注为简单题,主要思路是寻找实现加密字符串的代码,提取并复现。

准备工作

  • 浏览器安装Cookie AutoDelete插件,便于清除cookie (关注本公众号(程序员小木屋)回复"cookie"可获取下载地址)
  • 安装好python,安装好nodejs,安装好pycahrm,安装好requests包,安装好execjs
  1. 打开网址https://match.yuanrenxue.com/match/1#,并打开开发者工具。

首先要解决的是无限debugger问题,上述代码的意思是,有一个每隔500毫秒就运行一次的函数,函数的内容就是在此处暂停,我们需要解决的是,不让函数在此处暂停。

解决方法:右键debugger行,选择一律不在此处暂停

  1. 网络tab栏查看所有接口,找到传输机票价格的接口

经过比较,此接口是返回价格的接口

  1. 查看接口传参,检查接口是否有加密参数

经过查看,headers中没有异常参数,但是传参中有一个参数m,我们需要找到这个m是如何生成的。

  1. 点击启动器tab页,查看调用堆栈 调用堆栈是指此接口执行的代码顺序

  2. 依次点击进入堆栈,并在能打断点的地方打上断点

技巧讲解

  • 进入js文件后,通过点击左下角的{}进行格式化
  • 找到对应的行后,点击左侧的行号就能打断点
  1. 使用插件清除cookie后,重新刷新界面,查看断点位置的数据,找到第一次生成m参数的位置

技巧讲解

一定要知道这几个按钮的含义和用法!

c.send位置没有m参数,继续执行

f.send也没有看到,继续执行

这一长串这也没看到,继续执行

欸,最后终于看到m了!

  1. 观察此处的代码,分析m是如何生成的

技巧讲解

  • 此处的代码一看就进行了混淆,我们可以逐个拆分,输出到控制台中,查看每个变量都是什么含义。
_0x5d83a3['\x70\x61\x67\x65'] = window['\x70\x61\x67\x65'], _0x5d83a3['\x6d'] = _0x57feae + '\u4e28' + _0x2268f9 / (-1 * 3483 + -9059 + 13542);

例如,第5行的代码,如上,我们依次在控制台中输入这些变量,查看是什么含义

看到了没有,_0x5d83a3['\x6d']就是m_0x57feaem的前半部分,_0x2268f9 * 1000就是m的后半部分(即时间戳)。

  1. 现在得到了m的后半部分,继续查找前半部分,看第2行,前半部分是在这里生成的
  • 因为程序已经执行到了第6行,我们现在查看第2行的结果,可能已经被污染了,所以我们选择在第2行打断点,重新执行,让程序变为第2行还未执行的状态。

我们依次执行第二行的函数,如下

发现_0x2268f9是时间戳,oo0O0(_0x2268f9['\x74\x6f\x53\x74\x72' + '\x69\x6e\x67']())是一个暂时不知道干啥的函数,window['\x66']正确输出了m的前半段。

  1. 寻找window['\x66']

'\x66''f',我们要找到window.f这个变量。 但是根据我的ctrl+f查找,并没有找到window.f,这是我们大胆假设,难道这个变量是在前面那一段不知道干啥用的函数生成的?我们进入oo0O0函数中

  1. 解混淆

代码跳转到了这里,oo0O0函数就隐藏在其中,这可怎么办呢?

不要慌!我们试着把它解混淆和格式化。

首先,解混淆是针对js代码的,这行代码里混合着<script></script,我们把这行代码复制到pycharm中,查找其中的非js代码,把它删掉

删掉后,现在就只剩js代码了。

百度搜索"js解混淆在线",进入到一个解混淆网址 解混淆 把代码复制进去,点击js格式化,就能看到规范的oo0O0代码了!

注意!

如果你的代码没有正确格式化,说明你没有正确删除,或语法格式有问题(比如落了;分号)。 这里需要一点js基础哈

正确的oo0O0如下

function oo0O0(mw) {
    window.b = '';
    for (var i = 0,
    len = window.a.length; i < len; i++) {
        console.log(window.a[i]);
        window.b += String[document.e + document.g](window.a[i][document.f + document.h]() - i - window.c)
    }
    var U = ['W5r5W6VdIHZcT8kU''WQ8CWRaxWQirAW=='];
    var J = function(o, E) {
        o = o - 0x0;
        var N = U[o];
        if (J['bSSGte'] === undefined) {
            var Y = function(w) {
                var m = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=',
                T = String(w)['replace'](/=+$/, '');
                var A = '';
                for (var C = 0x0,
                b, W, l = 0x0; W = T['charAt'](l++);~W && (b = C % 0x4 ? b * 0x40 + W: W, C++%0x4) ? A += String['fromCharCode'](0xff & b >> ( - 0x2 * C & 0x6)) : 0x0) {
                    W = m['indexOf'](W)
                }
                return A
            };
            var t = function(w, m) {
                var T = [],
                A = 0x0,
                C,
                b = '',
                W = '';
                w = Y(w);
                for (var R = 0x0,
                v = w['length']; R < v; R++) {
                    W += '%' + ('00' + w['charCodeAt'](R)['toString'](0x10))['slice']( - 0x2)
                }
                w = decodeURIComponent(W);
                var l;
                for (l = 0x0; l < 0x100; l++) {
                    T[l] = l
                }
                for (l = 0x0; l < 0x100; l++) {
                    A = (A + T[l] + m['charCodeAt'](l % m['length'])) % 0x100,
                    C = T[l],
                    T[l] = T[A],
                    T[A] = C
                }
                l = 0x0,
                A = 0x0;
                for (var L = 0x0; L < w['length']; L++) {
                    l = (l + 0x1) % 0x100,
                    A = (A + T[l]) % 0x100,
                    C = T[l],
                    T[l] = T[A],
                    T[A] = C,
                    b += String['fromCharCode'](w['charCodeAt'](L) ^ T[(T[l] + T[A]) % 0x100])
                }
                return b
            };
            J['luAabU'] = t,
            J['qlVPZg'] = {},
            J['bSSGte'] = !![]
        }
        var H = J['qlVPZg'][o];
        return H === undefined ? (J['TUDBIJ'] === undefined && (J['TUDBIJ'] = !![]), N = J['luAabU'](N, E), J['qlVPZg'][o] = N) : N = H,
        N
    };
    eval(atob(window['b'])[J('0x0'']dQW')](J('0x1''GTu!'), '\x27' + mw + '\x27'));
    return ''
};

但是呢,观察代码,并没有发现代码中有定义或生成window.f的地方啊。

说得对!同时细心的你应该也发现了这样一行代码

eval(atob(window['b'])[J('0x0'']dQW')](J('0x1''GTu!'), '\x27' + mw + '\x27'));

接下来又是大胆猜想小心求证的时间:是不是eval...这行代码生成的呢?

将其复制到控制台,试着运行一下

J变量不存在,但是atob(window['b']输出了一段代码,看到代码里的md5字样,你的心有没有激动起来呀~

将上面代码进行格式化,然后尝试运行一下

咦报错了?不要慌!这里缺少的mwqqppz不就是执行oo0O0函数时传进来的时间戳嘛

重新执行一下 出来了! 跟继续执行的结果比较一下 一摸一样! 11. 太棒了!接下来试验一下

  • 创建一个hex_md5.js文件,把atob(window['b']展示的这段代码复制进去
  • 把时间戳的生成方法也搞进去(还记得在哪里吧),然后定义一个run函数去执行它(这里又需要一些js基础)
  • 创建一个run.py文件,然后输入以下代码,我们先试试能不能运行这个js文件
import execjs
from pathlib import Path

context = execjs.compile(Path("./hex_md5.js").read_text())

[ts, hex_md5] = context.call('run')

print(ts, hex_md5)

可以运行太棒了!

  1. 页面查看接口的规律

依次点击第2345页,发现两个重要的点:

  • 不同页的接口page参数发生了改变,第二页是page=2,第三页是page=3...;
  • 四五这两页进行了锁定,需要添加设置 user-agent='yuanrenxue.project'
  1. python实现

run.py中写入如下代码(不懂的地方看注释哦)

import execjs
from pathlib import Path
import requests

context = execjs.compile(Path("./hex_md5.js").read_text())

# url地址
url = "https://match.yuanrenxue.com/api/match/1"
# 定义headers
headers = {"user-agent""yuanrenxue.project"}
[ts, hex_md5] = context.call("run")

# 生成m参数
m = f"{hex_md5}丨{ts}"
# 记录总和
res = 0

for page in range(1, 6):
    # 生成params
    params = {"page": page, "m": m}
    # 获取数据
    resp = requests.get(url, params=params, headers=headers)
    json_data = resp.json()
    # {'status': '1', 'state': 'success', 'data': [{'value': 8179}, {'value': 6177}, 
    # {'value': 4174}, {'value': 5945}, {'value': 9556}, {'value': 2318}, {'value': 4}, 
    # {'value': 2653}, {'value': 4855}, {'value': 1370}]}
    res += sum(map(lambda x: x["value"], json_data["data"]))

print("总和为", res)

运行结果:

总和为 235000
  1. 因为我没有注册账号,所以没法提交,但是接口响应都正确,结果肯定没问题啦。

最后

end

分类:

后端

标签:

后端

作者介绍

不想再上班啦
V1