当前位置:   article > 正文

网络爬虫逆向(艺恩年度票房)_python爬取艺恩数据

python爬取艺恩数据


抓取网址:https://www.endata.com.cn/BoxOffice/BO/Year/index.html

抓取目标:影片相关信息

image-20230505110918995

网页分析

打开网页后,按F12打开开发者工具,然后修改年份。

可以看到生成了一个XHR的数据包,并且还包含其他影片的封面图片。

image-20230505111514890

点击该数据包,查看数据包内容。可以看到该数据包返回的结果为加密数据。

image-20230505111608699

现在还无法确定该数据是否为我们想要的影片信息,为了搞清楚这个问题,我们点击标头查看其请求的过程。

image-20230505111705160

可以看到该数据包通过发送POST请求到API接口 https://www.endata.com.cn/API/GetData.ashx,去获取数据。然后点击载荷,查看POST请求发送的表单。

image-20230505111842280

表单结构很简单,只有两个值。

year:顾名思义即为查询的年份

MethodName:为固定值BoxOffice_GetYearInfoData

然后查看该数据包的请求头。

image-20230505112030561

从请求头和表单数据可以看出,通过POST请求去获取该数据包的返回值很简单。难点其实是获取到的返回值是经过加密的数据,那么如何将其还原为正常的结果呢?这个就是我们这个案例需要解决的主要问题。

点击启动器,可以看到该数据包请求调用堆栈,不难看出,前两个是jquery的JS代码。我们从第三个开始分析。

image-20230505112317215

可以发现该JS代码调用的是一块ajax代码,去发送数据。

image-20230505112515749

通过其success部分的JS代码不难分析出其逻辑。

1 == (e = "{" == e[0] ? JSON.parse(e) : JSON.parse(webInstace.shell(e))).Status || 200 == e.Code ? r(e.Data) : 200 == e.code ? r(e.data) : a(e.Msg)
  • 1

该代码的逻辑是,首先判断e的第一个字符是否是{,如果是说明eJSON数据,直接调用JSON.parse()去解析即可;如果不是说明e为加密数据,这个时候调用的是webInstace.shell()方法对其进行解密。到此我们就直接找到了解密方法为webInstace.shell()

在此打上断点,刷新网页,程序就会在断点处截断。

image-20230505113037856

可以看到e即为我们的加密数据,通过控制台我们也可以直接将其打印出来。

image-20230505113125739

选中webInstace.shell,进入到该函数内部。

image-20230505113212320

进来之后我们可以看到该方法所在数据包变量名和相关逻辑十分复杂。

image-20230505113327619

这是我们常见的代码混淆模式之一——OB混淆

结果解密

扣JS

有一种简单做法是直接将整个数据包复制到JS文件中,使用JS代码对其进行解密。

整个JS代码,由于文件内容比较大,在此处省略
  • 1

直接运行JS代码,发现会报错。navigator is not defined

image-20230505113859096

我们可以把该代码复制到控制台中运行查看结果。

image-20230505114021017

该代码的作用是检测请求是否是浏览器发送的,并且是否有userAgent字段。如果条件成立直接返回空。这里我们是要对结果进行解密,这个条件肯定是永远不成立的,直接删除该部分代码即可。

image-20230505114224429

重新运行代码。可以发现返回结果已经正常。

image-20230505114301877

接下来编写Python代码调用JS对获取的加密数据进行解析即可。

from functools import partial  # 这个函数可以锁定一个函数的参数
import subprocess

# 固定subprocess.Popen的encoding参数为UTF-8
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')


import requests
import execjs



url = 'https://www.endata.com.cn/API/GetData.ashx'

data = {
    'year': 2023,
    'MethodName': 'BoxOffice_GetYearInfoData'
}

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',
}

res = requests.post(url, data=data, headers=headers).text

# 解密
with open('04_OB混淆_扣JS.js', 'r', encoding='UTF-8') as f:
    js_code = f.read()

js_compile = execjs.compile(js_code)
print(js_compile.call('analyse', res))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

可以获取到真实数据

image-20230505120117849

分析JS逻辑使用Python改写

上述做法的特点是不用分析直接抓取整个JS文件,比较粗暴,如果JS代码过多就可能出现效率低下的问题。

接下来我们来使用分析JS代码逻辑,使用Python代码按照其逻辑对其进行改写的方式解密数据。

我们把webDES类复制到一个JS文件中,对其进行逻辑分析,还原代码。

var webDES = function() {
    var _0x4da59e = {
        'bUIIa': function _0x2a2af9(_0x779387, _0x4a4fec) {
            return _0x779387 + _0x4a4fec;
        }
    };
    var _0x9843d3 = function(_0x29d556, _0xcc6df, _0x3d7020) {
        if (0x0 == _0xcc6df)
            return _0x29d556[_0x2246('0x254', '4VZ$')](_0x3d7020);
        var _0x48914b;
        _0x48914b = '' + _0x29d556[_0x2246('0x255', 'GL3Q')](0x0, _0xcc6df);
        return _0x48914b += _0x29d556['substr'](_0x4da59e[_0x2246('0x256', 'DK[&')](_0xcc6df, _0x3d7020));
    };
    this[_0x2246('0x257', 'nArV')] = function(_0xa0c834) {
        var _0x51eedc = {
            'pKENi': function _0x2f627(_0x5b6f5a, _0x440924) {
                return _0x5b6f5a === _0x440924;
            },
            'wnfPa': 'ZGz',
            'VMmle': '7|1|8|9|5|2|3|6|0|4',
            'GKWFf': function _0x1a4e13(_0x40cfde, _0x16f3c2) {
                return _0x40cfde == _0x16f3c2;
            },
            'MUPgQ': function _0x342f0d(_0x19038b, _0x4004d6) {
                return _0x19038b >= _0x4004d6;
            },
            'hLXma': function _0x55adaf(_0x45a871, _0x161bdf) {
                return _0x45a871 + _0x161bdf;
            },
            'JdOlO': function _0x13e00a(_0x5899a9, _0x4bb34d) {
                return _0x5899a9 + _0x4bb34d;
            },
            'qrTpg': function _0x1198fb(_0x55b317, _0x22e1db, _0x1b091a) {
                return _0x55b317(_0x22e1db, _0x1b091a);
            },
            'pdmMk': function _0xe2b022(_0x4af286, _0x4c2fd4) {
                return _0x4af286 - _0x4c2fd4;
            },
            'xVKWW': function _0x1094a3(_0x5f3627, _0x2a0ac5, _0x3ad2e5) {
                return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
            }
        };
        if (_0x51eedc[_0x2246('0x258', '@1Ws')](_0x2246('0x259', 'E&PI'), _0x51eedc['wnfPa'])) {
            this['_append'](a);
            return this[_0x2246('0x25a', 'GL3Q')]();
        } else {
            var _0x492a62 = _0x51eedc[_0x2246('0x25b', '&59Q')][_0x2246('0x25c', ')q#9')]('|')
              , _0x356b01 = 0x0;
            while (!![]) {
                switch (_0x492a62[_0x356b01++]) {
                case '0':
                    _0x554c90 = _grsa_JS[_0x2246('0x25d', 'E&PI')]['decrypt']({
                        'ciphertext': _grsa_JS['enc'][_0x2246('0x25e', 'sy^o')]['parse'](_0xa0c834)
                    }, _0x2cf8ae, {
                        'iv': _0x554c90,
                        'mode': _grsa_JS[_0x2246('0x16c', 'O^50')][_0x2246('0x25f', 'Who^')],
                        'padding': _grsa_JS[_0x2246('0x260', '7IfV')][_0x2246('0x261', 'E&PI')]
                    })[_0x2246('0x1c', 'yY#5')](_grsa_JS['enc'][_0x2246('0x262', ']2BX')]);
                    continue;
                case '1':
                    if (_0x51eedc[_0x2246('0x263', 'Jsmq')](null, _0xa0c834) || _0x51eedc[_0x2246('0x264', '!2eC')](0x10, _0xa0c834['length']))
                        return _0xa0c834;
                    continue;
                case '2':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 0x8);
                    continue;
                case '3':
                    _0x2cf8ae = _grsa_JS[_0x2246('0x265', 'RQ2o')][_0x2246('0x266', '3j7z')][_0x2246('0x267', 'RQ2o')](_0x554c90);
                    continue;
                case '4':
                    return _0x554c90[_0x2246('0x268', 'cs*4')](0x0, _0x51eedc[_0x2246('0x269', 'MVsm')](_0x554c90[_0x2246('0x26a', '0J6f')]('}'), 0x1));
                case '5':
                    _0x554c90 = _0xa0c834[_0x2246('0x26b', 'UwHa')](_0x2cf8ae, 0x8);
                    continue;
                case '6':
                    _0x554c90 = _grsa_JS[_0x2246('0x26c', '4VZ$')]['Utf8']['parse'](_0x554c90);
                    continue;
                case '7':
                    if (!navigator || !navigator[_0x2246('0x26d', '0I#o')])
                        return '';
                    continue;
                case '8':
                    var _0x554c90 = _0x51eedc[_0x2246('0x26e', 'Yb4P')](_0x51eedc[_0x2246('0x26f', 'BQ5p')](parseInt, _0xa0c834[_0x51eedc[_0x2246('0x270', 'Z2VK')](_0xa0c834['length'], 0x1)], 0x10), 0x9)
                      , _0x2cf8ae = _0x51eedc[_0x2246('0x271', 'yY#5')](parseInt, _0xa0c834[_0x554c90], 0x10);
                    continue;
                case '9':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 0x1);
                    continue;
                }
                break;
            }
        }
    }
    ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

将其整理一下可以看到该类的结果十分简单,一共就定义了一个变量和两个方法。

image-20230505120724848

先不管上面的变量和方法,分析最后一个方法,将其展开。

现在这个代码无法直接对其进行逻辑分析,因为开发者使用工具对其进行了混淆,变量名和方法名都是一些比较长的字符。现在要做的是将其变量名简化。

第一个要做的是将_0x2246进行还原改写。

image-20230505121110497

我们通过控制台可以对其进行还原。可以发现其实这些内容就是字符串。

image-20230505121324751

我们一个个对其进行还原即可。过程很简单,但是重复次数多。

var webDES = function () {
    var _0x4da59e = {
        'bUIIa': function _0x2a2af9(_0x779387, _0x4a4fec) {
            return _0x779387 + _0x4a4fec;
        }
    };
    var _0x9843d3 = function (_0x29d556, _0xcc6df, _0x3d7020) {
        if (0x0 == _0xcc6df)
            return _0x29d556['substr'](_0x3d7020);
        var _0x48914b;
        _0x48914b = '' + _0x29d556['substr'](0x0, _0xcc6df);
        return _0x48914b += _0x29d556['substr'](_0x4da59e['bUIIa'](_0xcc6df, _0x3d7020));
    };
    this['shell'] = function (_0xa0c834) {
        var _0x51eedc = {
            'pKENi': function _0x2f627(_0x5b6f5a, _0x440924) {
                return _0x5b6f5a === _0x440924;
            },
            'wnfPa': 'ZGz',
            'VMmle': '7|1|8|9|5|2|3|6|0|4',
            'GKWFf': function _0x1a4e13(_0x40cfde, _0x16f3c2) {
                return _0x40cfde == _0x16f3c2;
            },
            'MUPgQ': function _0x342f0d(_0x19038b, _0x4004d6) {
                return _0x19038b >= _0x4004d6;
            },
            'hLXma': function _0x55adaf(_0x45a871, _0x161bdf) {
                return _0x45a871 + _0x161bdf;
            },
            'JdOlO': function _0x13e00a(_0x5899a9, _0x4bb34d) {
                return _0x5899a9 + _0x4bb34d;
            },
            'qrTpg': function _0x1198fb(_0x55b317, _0x22e1db, _0x1b091a) {
                return _0x55b317(_0x22e1db, _0x1b091a);
            },
            'pdmMk': function _0xe2b022(_0x4af286, _0x4c2fd4) {
                return _0x4af286 - _0x4c2fd4;
            },
            'xVKWW': function _0x1094a3(_0x5f3627, _0x2a0ac5, _0x3ad2e5) {
                return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
            }
        };
        if (_0x51eedc['pKENi']('tgg', _0x51eedc['wnfPa'])) {
            this['_append'](a);
            return this['_process']();
        } else {
            var _0x492a62 = _0x51eedc['VMmle']['split']('|')
                , _0x356b01 = 0x0;
            while (!![]) {
                switch (_0x492a62[_0x356b01++]) {
                    case '0':
                        _0x554c90 = _grsa_JS['DES']['decrypt']({
                            'ciphertext': _grsa_JS['enc']['Hex']['parse'](_0xa0c834)
                        }, _0x2cf8ae, {
                            'iv': _0x554c90,
                            'mode': _grsa_JS['mode']['ECB'],
                            'padding': _grsa_JS['pad']['Pkcs7']
                        })['toString'](_grsa_JS['enc']['Utf8']);
                        continue;
                    case '1':
                        if (_0x51eedc['GKWFf'](null, _0xa0c834) || _0x51eedc['MUPgQ'](0x10, _0xa0c834['length']))
                            return _0xa0c834;
                        continue;
                    case '2':
                        _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 0x8);
                        continue;
                    case '3':
                        _0x2cf8ae = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
                        continue;
                    case '4':
                        return _0x554c90['substring'](0x0, _0x51eedc['hLXma'](_0x554c90['lastIndexOf']('}'), 0x1));
                    case '5':
                        _0x554c90 = _0xa0c834['substr'](_0x2cf8ae, 0x8);
                        continue;
                    case '6':
                        _0x554c90 = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
                        continue;
                    case '7':
                        if (!navigator || !navigator['userAgent'])
                            return '';
                        continue;
                    case '8':
                        var _0x554c90 = _0x51eedc['JdOlO'](_0x51eedc['qrTpg'](parseInt, _0xa0c834[_0x51eedc['pdmMk'](_0xa0c834['length'], 0x1)], 0x10), 0x9)
                            , _0x2cf8ae = _0x51eedc['xVKWW'](parseInt, _0xa0c834[_0x554c90], 0x10);
                        continue;
                    case '9':
                        _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 0x1);
                        continue;
                }
                break;
            }
        }
    }
    ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

其中还有一些16进制的数值,也对其进行还原。

image-20230505122024683

还原后如下:

var webDES = function () {
    var _0x4da59e = {
        'bUIIa': function _0x2a2af9(_0x779387, _0x4a4fec) {
            return _0x779387 + _0x4a4fec;
        }
    };
    var _0x9843d3 = function (_0x29d556, _0xcc6df, _0x3d7020) {
        if (0x0 == _0xcc6df)
            return _0x29d556['substr'](_0x3d7020);
        var _0x48914b;
        _0x48914b = '' + _0x29d556['substr'](0, _0xcc6df);
        return _0x48914b += _0x29d556['substr'](_0x4da59e['bUIIa'](_0xcc6df, _0x3d7020));
    };
    this['shell'] = function (_0xa0c834) {
        var _0x51eedc = {
            'pKENi': function _0x2f627(_0x5b6f5a, _0x440924) {
                return _0x5b6f5a === _0x440924;
            },
            'wnfPa': 'ZGz',
            'VMmle': '7|1|8|9|5|2|3|6|0|4',
            'GKWFf': function _0x1a4e13(_0x40cfde, _0x16f3c2) {
                return _0x40cfde == _0x16f3c2;
            },
            'MUPgQ': function _0x342f0d(_0x19038b, _0x4004d6) {
                return _0x19038b >= _0x4004d6;
            },
            'hLXma': function _0x55adaf(_0x45a871, _0x161bdf) {
                return _0x45a871 + _0x161bdf;
            },
            'JdOlO': function _0x13e00a(_0x5899a9, _0x4bb34d) {
                return _0x5899a9 + _0x4bb34d;
            },
            'qrTpg': function _0x1198fb(_0x55b317, _0x22e1db, _0x1b091a) {
                return _0x55b317(_0x22e1db, _0x1b091a);
            },
            'pdmMk': function _0xe2b022(_0x4af286, _0x4c2fd4) {
                return _0x4af286 - _0x4c2fd4;
            },
            'xVKWW': function _0x1094a3(_0x5f3627, _0x2a0ac5, _0x3ad2e5) {
                return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
            }
        };
        if (_0x51eedc['pKENi']('tgg', _0x51eedc['wnfPa'])) {
            this['_append'](a);
            return this['_process']();
        } else {
            var _0x492a62 = _0x51eedc['VMmle']['split']('|')
                , _0x356b01 = 0;
            while (!![]) {
                switch (_0x492a62[_0x356b01++]) {
                    case '0':
                        _0x554c90 = _grsa_JS['DES']['decrypt']({
                            'ciphertext': _grsa_JS['enc']['Hex']['parse'](_0xa0c834)
                        }, _0x2cf8ae, {
                            'iv': _0x554c90,
                            'mode': _grsa_JS['mode']['ECB'],
                            'padding': _grsa_JS['pad']['Pkcs7']
                        })['toString'](_grsa_JS['enc']['Utf8']);
                        continue;
                    case '1':
                        if (_0x51eedc['GKWFf'](null, _0xa0c834) || _0x51eedc['MUPgQ'](16, _0xa0c834['length']))
                            return _0xa0c834;
                        continue;
                    case '2':
                        _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 8);
                        continue;
                    case '3':
                        _0x2cf8ae = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
                        continue;
                    case '4':
                        return _0x554c90['substring'](0, _0x51eedc['hLXma'](_0x554c90['lastIndexOf']('}'), 1));
                    case '5':
                        _0x554c90 = _0xa0c834['substr'](_0x2cf8ae, 8);
                        continue;
                    case '6':
                        _0x554c90 = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
                        continue;
                    case '7':
                        if (!navigator || !navigator['userAgent'])
                            return '';
                        continue;
                    case '8':
                        var _0x554c90 = _0x51eedc['JdOlO'](_0x51eedc['qrTpg'](parseInt, _0xa0c834[_0x51eedc['pdmMk'](_0xa0c834['length'], 1)], 16), 9)
                            , _0x2cf8ae = _0x51eedc['xVKWW'](parseInt, _0xa0c834[_0x554c90], 16);
                        continue;
                    case '9':
                        _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 1);
                        continue;
                }
                break;
            }
        }
    }
    ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

然后分析shell函数,该函数首先定义了一个变量_0x51eedc,该变量为对象类型。定义了很多变量与函数的对应关系,便于后续对其进行调用。image-20230505122448415

这里的函数功能都比较简单,我们可以直接去分析对象下方的代码逻辑,遇到调用_0x51eedc的内容再回过来看即可。

if (_0x51eedc['pKENi']('tgg', _0x51eedc['wnfPa'])) {
            this['_append'](a);
            return this['_process']();
        }
  • 1
  • 2
  • 3
  • 4

该代码调用了_0x51eedc中的wnfPapKENi

经过查询可以知道wnfPa为固定值ZGzpKENi为函数(功能是比较两个值是否相等,返回true或者false)

所以这里的这个代码实现的功能是判断tggZGz的值是否一样。明显这个条件是永远无法成立的。删除即可。

使用类似这样的方法对其他代码进行分析,将代码进行删除和简化。

var webDES = function () {
    var _0x4da59e = {
        'bUIIa': function _0x2a2af9(_0x779387, _0x4a4fec) {
            return _0x779387 + _0x4a4fec;
        }
    };
    var _0x9843d3 = function (_0x29d556, _0xcc6df, _0x3d7020) {
        if (0 == _0xcc6df)
            return _0x29d556['substr'](_0x3d7020);
        var _0x48914b;
        _0x48914b = '' + _0x29d556['substr'](0, _0xcc6df);
        _0x48914b += _0x29d556['substr'](_0xcc6df + _0x3d7020);
        return _0x48914b
    };
    this['shell'] = function (_0xa0c834) {

        // 死循环
        while (true) {
            var _0x554c90 = parseInt(_0xa0c834[_0xa0c834['length'] - 1], 16) + 9
            var _0x2cf8ae = parseInt(_0xa0c834[_0x554c90], 16);

            _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 1);

            _0x554c90 = _0xa0c834['substr'](_0x2cf8ae, 8);
            _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 8);
            _0x2cf8ae = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
            _0x554c90 = _grsa_JS['enc']['Utf8']['parse'](_0x554c90);
            _0x554c90 = _grsa_JS['DES']['decrypt']({
                        'ciphertext': _grsa_JS['enc']['Hex']['parse'](_0xa0c834)
                    }, _0x2cf8ae, {
                        'iv': _0x554c90,
                        'mode': _grsa_JS['mode']['ECB'],
                        'padding': _grsa_JS['pad']['Pkcs7']
                    })['toString'](_grsa_JS['enc']['Utf8']);
                    continue;
            return _0x554c90['substring'](0, _0x554c90['lastIndexOf']('}') + 1);  
        }
    }

    ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

然后使用编辑器的替换功能,将复杂变量替换为简单变量。

_0x51eedc--> a

_0x554c90--> b

_0x2cf8ae--> c

_0x9843d3--> func

再把func内部的局部变量也进行简化。

_0x29d556--> a

_0xcc6df--> b

_0x3d7020--> c

_0x48914b--> result

现在代码就变得简单很多,如下所示

var webDES = function () {
    var func = function (a, b, c) {
        if (0 == b)
            return a['substr'](c);
        var result;
        result = '' + a['substr'](0, b);
        result += a['substr'](b + c);
        return result
    };
    this['shell'] = function (a) {
        var b = parseInt(a[a['length'] - 1], 16) + 9
        var c = parseInt(a[b], 16);

        a = func(a, b, 1);

        b = a['substr'](c, 8);
        a = func(a, c, 8);
        c = _grsa_JS['enc']['Utf8']['parse'](b);
        b = _grsa_JS['enc']['Utf8']['parse'](b);
        b = _grsa_JS['DES']['decrypt']({
            'ciphertext': _grsa_JS['enc']['Hex']['parse'](a)
        }, c, {
            'iv': b,
            'mode': _grsa_JS['mode']['ECB'],
            'padding': _grsa_JS['pad']['Pkcs7']
        })['toString'](_grsa_JS['enc']['Utf8']);
        return b['substring'](0, b['lastIndexOf']('}') + 1);
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

对其进行解读添加注释如下

var webDES = function () {
    var func = function (a, b, c) {
        if (0 == b)
            return a['substr'](c);
        var result;
        result = '' + a['substr'](0, b);
        result += a['substr'](b + c);
        return result
    };
    this['shell'] = function (a) {
        // 获取a的最后一个字符将其转化为10进制数值+9赋值为b
        var b = parseInt(a[a['length'] - 1], 16) + 9
        // 将a的b位置的字符转化为10进制赋值为c
        var c = parseInt(a[b], 16);

        // 运行函数获得新值赋值给a
        a = func(a, b, 1);

        // 对新值进行切片,从c位置开始切片获取8个值赋值给b
        b = a['substr'](c, 8);
        // 再次调用函数获取新值赋值给a
        a = func(a, c, 8);

        c = _grsa_JS['enc']['Utf8']['parse'](b);  // 将b转化使用UTF-8编码方式转化为字节赋值给c
        b = _grsa_JS['enc']['Utf8']['parse'](b);  // 将b转化使用UTF-8编码方式转化为字节赋值给b
        b = _grsa_JS['DES']['decrypt']({
                    'ciphertext': _grsa_JS['enc']['Hex']['parse'](a)  // 将a使用16进制编码方式转化为字节
                }, c, {
                    'iv': b,  // 解密密钥,由于是ECB编码这里b和c其实是一样的,在Python中不用写iv。
                    'mode': _grsa_JS['mode']['ECB'],  // 解密方法
                    'padding': _grsa_JS['pad']['Pkcs7']  // 填充
                })['toString'](_grsa_JS['enc']['Utf8']);  // 转化为UTF-8格式
        return b['substring'](0, b['lastIndexOf']('}') + 1);  // 删除多余的结果,将}后面的字符都删除
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

接下来使用Python语言按照其逻辑进行改写。

# endata,ob混淆
# https://www.endata.com.cn/BoxOffice/BO/Year/index.html
from Crypto.Cipher import DES
import binascii
import json

import requests

def func(a, b, c):
    if 0 == b:
        return a[c:]
    result = a[:b]
    result += a[(b+c):]
    return result

def shell(a):
    b = int(a[-1], 16) + 9
    c = int(a[b], 16)

    a = func(a, b, 1)
    b = a[c:c+8]
    a = func(a, c, 8)
	
    # 使用UTF-8编码转化为字节
    c = b.encode('UTF-8')
    b = b.encode('UTF-8')

    a = binascii.a2b_hex(a)  # 将16进制数据还原成字节
    # DES解密
    des = DES.new(key=c, mode=DES.MODE_ECB)
    result = des.decrypt(a).decode('utf-8')

    # 删除最右侧}后面的内容
    result = result[:result.rindex('}') + 1]
    return json.loads(result)

if __name__ == '__main__':
    url = 'https://www.endata.com.cn/API/GetData.ashx'

    data = {
        'year': 2023,
        'MethodName': 'BoxOffice_GetYearInfoData'
    }

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',
    }

    res = requests.post(url, data=data, headers=headers).text
    print(shell(res))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

运行结果如下:

image-20230505144032733

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/371923
推荐阅读
相关标签
  

闽ICP备14008679号