爬虫Set-Cookies混淆加密思路

954 阅读3分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

前言

在爬取某些网站的时候,获取的返回数据不是意料中的html,而是一大串毫无格式的js,例如:

var arg1='38B18065C640DD60B8A3AD8BFA4DE2D694EDD37C';
var _0x4818=['\x63\x73\..

具体如图所示: 在这里插入图片描述

解密过程

格式化JS

其实,js中字符就是被\0x50这种给的十六进制加密,只需要粘贴去tool.lu/js解密即可 在这里插入图片描述

在此图中,可以知道在请求网站的时候部分原理:在请求网站加载html的时候,会检测cookie是否含有acw_sc__v2属性,如果没有,js会调用reload(x)方法来执行setCookie()将计算出来的x赋值给acw_sc__v2并生成cookie,网页会reload重新加载,如果cookie没有acw_sc__v2,则再次刷新网页,重复上述过程直至cookie存在,然后则带着此cookie请求并正确返回html。

找到x

接下来我们需要找到哪里调用了reload()并传入x,查找结果如下

setTimeout('reload(arg2)', 0x2);

找到arg2

我们已经知道了x就是arg2,接着去寻找arg2是怎样生成的就可以了

var _0x23a392 = arg1_0x55f3('0x19', 'Pg54'); 
arg2 = _0x23a392_0x55f3('0x1b', 'z5O&'); 

接着找到_0x5e8b26的赋值语句:

var _0x5e8b26 = _0x55f3('0x3', 'jS1Y'); 

看到上面的代码觉得一脸懵逼,这些_0x23a392这些是什么?接下来第四步

解密混淆函数

_0x55f3()函数是混淆函数,_0x23a392是混淆变量,和我们平时使用的函数和变量名称都是一样的,你在格式化的js中肯定找到了_0x55f3()这个方法,但是如果你想一行一行读代码是不太现实的。所以探索了一个简单的方法,那就是在开发者工具的控制台打印此函数的返回值

首先打开chrome的控制台,打开断点调试功能,访问这个网站,程序就会在debugger断点,不会接着执行 在这里插入图片描述

如图 打印变量值: 在这里插入图片描述

那么,第三步中的代码就转换为这种格式

var arg1 = '37EAB765A2E11E6F44CF4E4B95B3EADA60ED2AEB';
arg2 = arg1['unsbox']()['hexXor']('3000176000856006061501533003690027800375')

意思是:arg1调用了unsbox()获取返回值之后,传入参数"300176..."又调用hexXor(),然后生成arg2的值,即x,也是cookie中acw_sc__v2的值。

unsbox和hexXor

那么最终的任务就是找到这两个方法的逻辑,我们知道arg1是个字符串,我们又从js中看到如图代码

String['prototype '][_0x55f3('0x14', 'Z*DM')] = function()

控制台打印_0x55f3('0x14', 'Z*DM')结果是'unbox',同样的方法找出hexXor 在这里插入图片描述 最终代码如下:

// 这种写法等同于String.prototyoe.hexXor
String['prototype']['hexXor'] = function(_0x4e08d8) {
  var _0x5a5d3b = '';
  for (var _0xe89588 = 0x0; _0xe89588 < this[_0x55f3('0x8', ')hRc')] && _0xe89588 < _0x4e08d8[_0x55f3('0xa', 'jE&^')]; _0xe89588 += 0x2) {
    var _0x401af1 = parseInt(this[_0x55f3('0xb', 'V2KE')](_0xe89588, _0xe89588 + 0x2), 0x10);
    var _0x105f59 = parseInt(_0x4e08d8[_0x55f3('0xd', 'XMW^')](_0xe89588, _0xe89588 + 0x2), 0x10);
    var _0x189e2c = (_0x401af1 ^ _0x105f59)[_0x55f3('0xf', 'W1FE')](0x10);
    if (_0x189e2c[_0x55f3('0x11', 'MGrv')] == 0x1) {
      _0x189e2c = '0' + _0x189e2c;
    }
    _0x5a5d3b += _0x189e2c;
  }
  return _0x5a5d3b;
};
String['prototype']['unsbox'] = function() {
  var _0x4b082b = [0xf, 0x23, 0x1d, 0x18, 0x21, 0x10, 0x1, 0x26, 0xa, 0x9, 0x13, 0x1f, 0x28, 0x1b, 0x16, 0x17, 0x19, 0xd, 0x6, 0xb, 0x27, 0x12, 0x14, 0x8, 0xe, 0x15, 0x20, 0x1a, 0x2, 0x1e, 0x7, 0x4, 0x11, 0x5, 0x3, 0x1c, 0x22, 0x25, 0xc, 0x24];
  var _0x4da0dc = [];
  var _0x12605e = '';
  for (var _0x20a7bf = 0x0; _0x20a7bf < this['length']; _0x20a7bf++) {
    var _0x385ee3 = this[_0x20a7bf];
    for (var _0x217721 = 0x0; _0x217721 < _0x4b082b[_0x55f3('0x16', 'aH*N')]; _0x217721++) {
      if (_0x4b082b[_0x217721] == _0x20a7bf + 0x1) {
        _0x4da0dc[_0x217721] = _0x385ee3;
      }
    }
  }
  _0x12605e = _0x4da0dc['join']('');
  return _0x12605e;
};

python实现

将其中的变量名进行替换,自行调试,十六进制转换为十进制(如0x0 = 0),用python实现unsbox和hexXor两个方法后,在每次请求这个网站时,用正则获取arg1,然后调用这两个方法生成acw_sc_v2放到cookie中再次请求。

部分代码如下:


arg1 = re.search('arg1=\'[0-9A-Z]+\'', response.text).group().replace('arg1=', '').replace('\'', '')
cookie = hexXor(unsbox(arg1)).replace('0x', '')
cookies = {'acw_sc__v2': cookie}
response = requests.get(url, headers=header, cookies=cookies)

知识点 - Prototype

1. 所有 String 的实例都继承自 String.prototype, 任何String.prototype上的改变都会影响到所有的String实例
2. 例如String.prototype.length:返回了字符串的长度
3. prototype是String类型的所有属性和方法的集合,通过String.prototype.xxx=function(){}来添加方法,通过str.xxx()来调用方法
4. String.prototyoe.hexXor等同于String['prototype']['hexXor'],str['hexXor']等同于str.hexXor

其他方法

上面就是解密js的步骤,如果是想要获取网站部分数据,花费这么多时间不值得,那么也可以在浏览器访问这个网站之后,打开控制台,copy其acw_sc_v2属性,放到cookie中进行请求,有效期是30min。 在这里插入图片描述 在Linux/Mac环境下可以使用curl测试:

curl http://xxxx -H "Cookie: acw_sc__v2=粘贴的值" -H "User-Agent: Chrome/54.0 (Windows NT 10.0)

结语

当初解密这个js的时候,搞了一天放弃了,一是网上资料比较少,二是只是临时爬取数据,无关紧要。后来还是求知欲让我在几天后的某一天通过摸索找到这个解密方法,在开发爬虫的过程,还遇到过字体加密、eval的js嵌套加密等,后续会在工作之余把更多的问题解决方法分享出来,共同学习和成长。 公众号文章地址