python爬虫-教你js解密

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

查询加密的大招:
全域搜索enc.utf8.parse,在此打断点,调试查看运行过程

第一个网站

  1. 分析请求
    请求方式
    请求参数
    请求参数是经过加密的,因此需要分析发送请求的过程。

  2. ajax请求分析
    2.1 开启ajax断点
    打开ajax抓包
    2.2 点击按钮后会进入调试模式,一直按 F10 键,跑完一次请求,并观察。
    请求参数位置
    2.3 发现请求参数,然后在其前面打断点(下次直接按 F8即可到达断点位置)

  3. 分析代码
    3.1 通过url全局搜索,来寻找函数入口。
    url全局搜索
    Sources栏下,右击网站网址,出现 search in all files,点击进入输入部分url
    此处可以输入:ScoreLines/UCodes/QueryList
    即可到达请求入口函数
    ajax请求入口函数
    该处可以得到原始请求参数和对参数进行加密处理的函数
    3.2 继续向下搜索,
    此处即为全局搜索:(函数名称) youzyEpt
    加密函数
    当然,还要根据调用路径找到所有调用函数及文件。
    然后对每行代码进行分析,可以尝试使用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进入正常调试模式。

多次调试发现,每次请求都会调用此函数
加密混淆函数
每次调试,可能函数名不一样,使用了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更新
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值