python爬虫-教你js解密

该文章是在工作碰到的需要js破解的网站,记录下破解过程。希望对js破解有一定的帮助,用以参考。

查询加密的大招:

全域搜索enc.utf8.parse,在此打断点,调试查看运行过程

第一个网站

  1. 分析请求
    python爬虫-教你js解密_第1张图片
    请求参数
    请求参数是经过加密的,因此需要分析发送请求的过程。

  2. ajax请求分析
    2.1 开启ajax断点
    python爬虫-教你js解密_第2张图片
    2.2 点击按钮后会进入调试模式,一直按 F10 键,跑完一次请求,并观察。
    python爬虫-教你js解密_第3张图片
    2.3 发现请求参数,然后在其前面打断点(下次直接按 F8即可到达断点位置)

  3. 分析代码
    3.1 通过url全局搜索,来寻找函数入口。
    python爬虫-教你js解密_第4张图片
    Sources栏下,右击网站网址,出现 search in all files,点击进入输入部分url
    此处可以输入:ScoreLines/UCodes/QueryList
    即可到达请求入口函数
    python爬虫-教你js解密_第5张图片
    该处可以得到原始请求参数和对参数进行加密处理的函数
    3.2 继续向下搜索,
    此处即为全局搜索:(函数名称) youzyEpt
    python爬虫-教你js解密_第6张图片
    当然,还要根据调用路径找到所有调用函数及文件。
    然后对每行代码进行分析,可以尝试使用python实现。

var text = JSON.stringify(data);# 该行对字典进行字符串化,可以用python实现
var textBytes = youzyEptService.utils.utf8.toBytes(text);
var aesCtr = new youzyEptService.ModeOfOperation.ctr(tzyCollegeFirst.aesKey(), new youzyEptService.Counter(5));
var encryptedBytes = aesCtr.encrypt(textBytes);
var encryptedHex = youzyEptService.utils.hex.fromBytes(encryptedBytes);
  1. 最快速的js破解方式
    最快的方式就是使用 execjs包直接调用js函数实现加密破解。
    4.1 首先要将js代码剥离出来
    -.自建一个js文件,将 youzyEpt函数copy到文件内,在将执行过程中的js代码依次分析并剥离,copy到自己的js文件中。
    -.此处相对较为容易,因为加密代码都在一个文件(即youzyEpt.sever.js)中,只是将加密的密匙进行了几个文件处理,由于是AES加密,密匙肯定固定可以进行分析得到。
    -.此处我的处理就是:将youzyEpt.sever.js文件整个copy,将youzyEpt函数copy到文件内,然后根据调用顺序,找到最低层调用函数,将这些函数copy,独立为独立函数。
    python调用js代码如下:
# 如果运行报错,可能需要安装node.js
import execjs 

def get_js(path):
    f = open(path, 'r', encoding='utf-8')  # 打开JS文件
    line = f.readline()
    htmlstr = ''
    while line:
        htmlstr = htmlstr + line
        line = f.readline()
    return execjs.compile(htmlstr)
 
 path1 = "./youzyEpt.sever1.js"
ctx = get_js(path1)
data = {
        "provinceId": provinceId,
        "collegeId": collegeId
    }
res_str = ctx.call('youzyEptt', data)

4.2 然后,运行文件,根据报错来提取函数,再运行,如此循环处理,直到跑通,得到需要的结果。
在这过程中,不管是单一方法,还是类对象,都是copy,提到最外层,
最终的效果如下:

# 加密函数  此处的加密密匙已经固化处理
var youzyEptt = function (data) {
                var text = JSON.stringify(data);
                var textBytes = toBytes(text);
                var aesCtr = new ModeOfOperationCTR(处理后的key值, new Counter(5));
                var encryptedBytes = aesCtr.encrypt(textBytes);
                var encryptedHex = fromBytes(encryptedBytes);
                return encryptedHex;
        };
 # 以下都是提取出的函数,包括function,常量,new对象
function toBytes(text) {}

var Hex = '0123456789abcdef';

function fromBytes(bytes) {}

function coerceArray(arg, copy) {}

function checkInts(arrayish) {}

function checkInt(value) {}

var Counter = function(initialValue) {}
Counter.prototype.setValue = function(value) {}
Counter.prototype.setBytes = function(bytes) {};
Counter.prototype.increment = function() {}

function createArray(length) {}

var ModeOfOperationCTR = function(key, counter) {}
ModeOfOperationCTR.prototype.encrypt = function(plaintext) {}
ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;

var AES = function(key) {}
AES.prototype._prepare = function() {}
AES.prototype.encrypt = function(plaintext) {}
AES.prototype.decrypt = function(ciphertext) {}

var numberOfRounds = {16: 10, 24: 12, 32: 14}
var rcon = [];
var S = [];
var Si =[];
var T1 = [];
var T2 = [];
var T3 = [];
var T4 = [];
var T5 = [];
var T6 = [];
var T7 = [];
var T8 = [];
var U1 = [];
var U2 = [];
var U3 = [];
var U4 = [];
function convertToInt32(bytes) {}
  1. 处理请求结果
    获取响应数据后,还发现有前端加密,发现解密处理函数如下
var _cnDeCrypt = function (zlVjhiyMm1) {
    var YB2 = "";
    zlVjhiyMm1['\x73\x70\x6c\x69\x74']("\x7c")['\x66\x6f\x72\x45\x61\x63\x68'](function ($lvd3) {
        if ($lvd3['\x73\x65\x61\x72\x63\x68'](/��(.*?)��/) != -1) {
            YB2 += $lvd3['\x72\x65\x70\x6c\x61\x63\x65']("\u3010", "")['\x72\x65\x70\x6c\x61\x63\x65']("\u3011", "")
        } else {
            $lvd3 = $lvd3['\x72\x65\x70\x6c\x61\x63\x65'](/[g-t]/ig, "");
            YB2 += "\x26\x23\x78" + $lvd3 + "\x3b"
        }
    });
    return YB2
# 使用console.log('\x73\x70\x6c\x69\x74')===> split
# 最终使用python重写,得到
def handle(txt):
    result = ''
    tt = txt.split('|')
    for t in tt:
        if '【' in t:
            result += t.replace('【', '').replace('】', '')
        else:
            ttt = re.sub('[g-tG-T]', '', t)
            result += '&#x' + ttt + ';'
    return result

  1. 前端字体反爬
    得到前端字符后的解密过程参考我的另一篇文章
    python字体反爬

第二个网站

同样,第一步分析请求
分析结果是:请求参数没问题,但是有cookie加密,每次请求都会变化。
cookie加密
前一个分析是时间戳
后一个是cookie加密
此处全局搜索使用cooke中的键值,比如:Hm_lpvt
但是dR9BixLC1sIxD0tw搜索不到,因此只能调试,但是该网站有前端反调试,处理方式如下:
google浏览器按 F12 ,出现如下:
前端反调试
此时,右击 1 处,选择 Never pause here,按F10出现的代码行,如此处理多行。
就可以点击时按F12进入正常调试模式。

多次调试发现,每次请求都会调用此函数
python爬虫-教你js解密_第7张图片
每次调试,可能函数名不一样,使用了js混淆,但是代码行号都是一样的。
在调试过程中,将鼠标放在变量名上,可以发现有些变量名是常量,可以进行替换
例如:_$Jg=>“concat”,_$QN=>“slice”,_$He=>1,_$Ng=>“dR9BixLC1sIxD0tw”

# 替换前
function _$7T() {
    _$UY(_$oS, _$O4, _$xQ);
    _$UY(_$oS, _$FK, _$D7);
    _$UY(_$oS, _$UU, _$vG);
    _$UY(_$oS, _$Nj, _$b7);
    _$UY(_$oS, _$GO, _$FA);
    _$UY(_$Wq, _$wG, _$tC);
}
# 替换后
function _$7T() {
    _$UY(_$oS,  "mouseup", _$xQ);
    _$UY(_$oS, "mousemove", _$D7);
    _$UY(_$oS, "keydown", _$vG);
    _$UY(_$oS, "touchmove", _$b7);
    _$UY(_$oS, "click", _$FA);
    _$UY(_$Wq, 'load', _$tC);
}

如此可以猜到要干什么,进一步对这些函数进行python编写实现
例如:

function _$jl(_$te) {
    var _$iB = [];
    for (var _$4S = 0; _$4S < _$te; _$4S++) {
        _$iB.push(_$K0(16));
    }
    return _$iB;
}

def create_random(num):
    return math.floor(random.random() * num)

# _$jl函数
def create_list(num):
    org = []
    for i in range(num):
        org.append(create_random(16))
    return org

当然,有些方法不能python实现的可以使用execjs调用实现其功能
最后实现效果如下

def get_str(l_53,click_times,keydown_times,load_times,mouseup_times,mousemove_times):
    stamp1 = java_to_stamp(l_53[41:])
    org_list = []
    l_59 = create_list(59)  # 随机数组
    org_list.extend(ssss(stamp1, 12))  # 时间戳相对固定
    append_to_list(1, 2, org_list)
    append_to_list(3, 2, org_list)
    append_to_list(2, 2, org_list)
    
    # 1 参数待确定  "click"
    append_to_list(click_times, 4, org_list)
    # 1 参数待确定  keydown
    append_to_list(keydown_times, 4, org_list)
    # 1 参数待确定 load
    append_to_list(load_times, 4, org_list)
    # 1 参数待确定  mouseup
    append_to_list(mouseup_times, 4, org_list)
    # 1 参数待确定  mousemove
    append_to_list(mousemove_times, 4, org_list)
    #  touchmove
    append_to_list(0, 4, org_list)
    stamp2 = get_timestamp()
    org_list.extend(ssss(stamp2, 12))
    org_list.extend(l_53[:41])
    list_2 = two_len_list(org_list)
    # 填充奇数项
    ot(list_2, org_list, l_59)
    # 填充偶数项
    xj(list_2, l_59)
    txt = base_str(l_53)
    result = funcb(txt, funcv(list_2))
    return result

观察发现每天初始化的数组l_53都固定,鼠标事件每次请求都不一样
因此需要每天对数组l_53更新

你可能感兴趣的:(爬虫)