JavaScript 逆向分析中的常用 Hook 技术详解 在 JavaScript 逆向工程中，Hook（钩子）技术是定位关键逻辑、拦截数据生成、调试加密函数的核心手段。通过修改或监控原生方法的行为，我们可以深入理解目标网站的运行机制。本文整理了 11 种常见的 Hook 技术，并附上实用示例代码，适用于油猴脚本（Tampermonkey）、浏览器调试及安全研究场景。 📌 提示：以下代码建议在浏览器控制台或用户脚本中使用，用于辅助分析，切勿用于非法用途。 01. DOM 操作 Hook 通过修改 DOM 元素的属性或样式，实现对页面行为的干预，常用于隐藏元素、触发事件或篡改界面状态。 // 修改元素属性 document.getElementById('elementId').setAttribute('attrName', 'attrValue'); // 修改元素样式 document.getElementById('elementId').style.color = 'red'; document.getElementById('elementId').style.display = 'none'; 02. Cookie 操作 Hook 用于监控 document.cookie 的读写操作，定位 Cookie 中关键字段（如 __dfp、token）的生成位置。 方式一：使用 Object.defineProperty (function () { 'use strict'; let cookieTemp = ''; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.includes('__dfp')) { debugger; // 断点触发 } console.log('Hook 捕获到 Cookie 设置 ->', val); cookieTemp = val; return val; }, get: function () { return cookieTemp; } }); })(); 方式二：使用 __defineSetter__（已废弃，兼容旧环境） (function () { 'use strict'; var org = document.__lookupSetter__('cookie'); document.__defineSetter__('cookie', function (cookie) { if (cookie.includes('__dfp')) { debugger; } org = cookie; }); document.__defineGetter__('cookie', function () { return org; }); })(); ⚠️ 注意：__defineSetter__ 已被弃用，推荐使用 Object.defineProperty。 03. 事件监听 Hook 监听用户交互事件（如点击、输入），用于追踪行为触发逻辑。 // 监听按钮点击 document.getElementById('buttonId').addEventListener('click', function () { console.log('按钮被点击！'); // 可在此注入自定义逻辑 }); 也可用于监听 copy、input、submit 等敏感事件。 04. AJAX 请求拦截 Hook 通过重写 XMLHttpRequest.prototype.send，拦截所有 AJAX 请求，便于分析参数与加密逻辑。 // 保存原始方法 XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (data) { console.log('拦截到 AJAX 请求，发送数据：', data); // 可在此修改 data 或插入断点 debugger; // 继续执行原方法 this._send.apply(this, arguments); }; 05. 函数替换 Hook 替换全局函数以插入日志或断点，常用于追踪加密函数调用。 // 保存原始函数 const originalFunction = window.encryptData; // 替换为自定义函数 window.encryptData = function (...args) { console.log('encryptData 被调用，参数：', args); debugger; // 调用原函数并返回结果 return originalFunction.apply(this, args); }; 06. HTTP Header 操作 Hook 监控 setRequestHeader，定位如 Authorization、X-Token 等关键头部字段的设置位置。 (function () { const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.setRequestHeader = function (key, value) { if (key === 'Authorization') { console.log('检测到 Authorization Header:', value); debugger; } // 继续执行原方法 return originalSetRequestHeader.apply(this, arguments); }; })(); 07. URL 请求地址 Hook 在 XMLHttpRequest.open 中检测请求 URL，用于定位登录、加密接口等关键请求。 (function () { const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, async) { if (url.includes('login')) { console.log('拦截到包含 "login" 的请求:', url); debugger; } return originalOpen.apply(this, arguments); }; })(); 🔍 注意：indexOf("login") != -1 才是正确判断，原文 != 1 有误，已修正。 08. JSON.stringify Hook 监控 JSON.stringify 调用，常用于分析加密前的数据结构。 (function () { const originalStringify = JSON.stringify; JSON.stringify = function (params) { console.log('Hook JSON.stringify ——> ', params); debugger; return originalStringify(params); }; })(); 09. JSON.parse Hook 监控 JSON.parse，用于查看解密后或响应返回的原始数据。 (function () { const originalParse = JSON.parse; JSON.parse = function (params) { console.log('Hook JSON.parse ——> ', params); debugger; return originalParse(params); }; })(); 10. eval 函数 Hook eval 常用于动态执行加密代码，Hook 它可查看被加密的 JS 源码。 (function () { // 保存原始 eval window.__cr_eval = window.eval; // 自定义 eval 包装函数 const myeval = function (src) { console.log('eval 执行代码:\n', src); console.log('=============== eval end ==============='); debugger; return window.__cr_eval(src); }; // 模拟原生 toString，绕过检测 const _myeval = myeval.bind(null); _myeval.toString = function () { return 'function eval() { [native code] }'; }; // 重新定义 window.eval Object.defineProperty(window, 'eval', { value: _myeval, writable: false, configurable: false }); })(); 11. Function 构造函数 Hook 监控通过 new Function() 创建的函数，常用于反混淆。 (function () { // 保存原始 Function 构造函数 window.__cr_fun = window.Function; // 重写 Function const myfun = function () { const args = Array.from(arguments); const lastArg = args.pop(); // 最后一个参数是函数体 console.log('Function 构造函数调用，函数体:\n', lastArg); console.log('=============== Function end ==============='); debugger; return new window.__cr_fun(...args, lastArg); }; // 模拟原生 toString 行为 myfun.toString = function () { return window.__cr_fun.toString(); }; // 重新定义 Function Object.defineProperty(window, 'Function', { value: myfun, writable: false, configurable: false }); })(); 总结 技术 用途 关键点 DOM 操作 控制页面元素 属性/样式修改 Cookie Hook 定位 Cookie 生成 Object.defineProperty 事件监听 跟踪用户行为 addEventListener AJAX 拦截 分析请求参数 重写 send/open 函数替换 调试加密函数 保存原函数引用 Header Hook 定位认证头 setRequestHeader URL Hook 拦截关键请求 open 方法重写 JSON.stringify/parse 查看数据结构 前后数据对比 eval / Function 反混淆动态代码 绕过 native 检测
docker run -d \ --name v2raya \ --network=host \ -e V2RAYA_ADDRESS=0.0.0.0:2017 \ -v /lib/modules:/lib/modules:ro \ -v /etc/resolv.conf:/etc/resolv.conf \ -v /home/docker/v2raya:/etc/v2raya \ --restart=always \ --privileged \ mzz2017/v2raya 这个方式是使用全局透明代理，宿主机和 docker 容器都会走这个代理，如果不想这么做，你可能需要如下方式启动： docker run -d \ --name v2raya \ -p 2017:2017 \ -p 20170-20172:20170-20172 \ -e V2RAYA_ADDRESS=0.0.0.0:2017 \ -v /home/docker/v2raya:/etc/v2raya \ --restart=always \ --privileged \ mzz2017/v2raya 访问 2017 端口，进行添加和配置节点，这些就不多说了。主要是配置好后，选择节点并启动，右上角显示正在运行即可。 如果需要使用代理，可以使用如下方式。 curl -x localhost:2017 google.com -vL
# uni-app 安全区域适配：防止刘海屏遮挡 ## 🎯 核心 API ```javascript // 获取设备安全区域信息 const { safeAreaInsets } = uni.getSystemInfoSync() console.log(safeAreaInsets) // 输出： // { // top: 44, // 顶部安全距离（刘海屏为44px，普通设备为20px） // bottom: 34, // 底部安全距离（Home指示器高度） // left: 0, // 左侧安全距离 // right: 0 // 右侧安全距离 // } 📱 应用场景 1. 自定义导航栏适配 <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> <view class="title">页面标题</view> </view> 2. 底部工具栏适配 <view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"> 操作按钮 </view> 3. 页面内容区域适配 <view class="content" :style="{ paddingTop: safeAreaInsets?.top + 'px', paddingBottom: safeAreaInsets?.bottom + 'px' }"> </view> ⚙️ pages.json 配置 { "pages": [ { "path": "pages/index/index", "style": { "navigationStyle": "custom" // 使用自定义导航栏才需要手动适配 } } ] } 🎨 CSS 方式（备选） .navbar { /* CSS 安全区域常量 */ padding-top: constant(safe-area-inset-top); /* iOS 11.0-11.2 */ padding-top: env(safe-area-inset-top); /* iOS 11.2+ */ } .toolbar { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); } 🔧 实用代码片段 兜底处理 const { safeAreaInsets } = uni.getSystemInfoSync() const safeTop = safeAreaInsets?.top || 20 // 默认状态栏高度 const safeBottom = safeAreaInsets?.bottom || 0 组合式函数 // composables/useSafeArea.ts export const useSafeArea = () => { const { safeAreaInsets } = uni.getSystemInfoSync() return { safeTop: safeAreaInsets?.top || 0, safeBottom: safeAreaInsets?.bottom || 0, safeStyle: { paddingTop: `${safeAreaInsets?.top || 0}px`, paddingBottom: `${safeAreaInsets?.bottom || 0}px` } } } H5 端配置 📋 重点提醒 ✅ 只有 navigationStyle: "custom" 的页面需要手动适配 ✅ iPhone X 系列：top: 44px, bottom: 34px ✅ 普通设备：top: 20px, bottom: 0px ✅ 使用动态样式绑定：:style="{ paddingTop: safeAreaInsets?.top + 'px' }"
光猫超管密码查看教程 一、操作前准备 ​确认设备连接​：确保电脑或移动设备已通过网线或 Wi-Fi 成功连接至目标光猫。 ​获取基础账户​：找到光猫设备背面的标签，获取 user 账户信息（通常标注为 “普通用户”），用于后续登录光猫后台。 ​准备工具​：提前打开常用的浏览器（如 Chrome、Edge、Safari 等），同时准备好能运行 Telnet 命令的终端（Windows 系统可使用命令提示符，Mac 或 Linux 系统可使用终端应用） 。 二、详细操作步骤 步骤 1：登录光猫普通用户后台 打开浏览器，在地址栏输入光猫默认管理地址 192.168.1.1 ，回车后进入登录界面。输入光猫背面标签上标注的 user 账户和密码，点击 “登录” 进入后台。 步骤 2：获取光猫 MAC 值 在光猫后台界面中，找到 “状态” 选项卡（不同光猫界面可能不同，部分可能命名为 “设备信息”“网络状态” 等） 。在 “上报云平台网关信息” 区域，找到 MAC 地址字段，记录完整的 MAC 值（例如：956E784E8880），注意​MAC 值区分大小写​。 步骤 3：开启 Telnet 功能 根据光猫型号，在浏览器地址栏输入对应开启 Telnet 的链接： ​**通用型号（非 HG6145D、HG6142a1）**​： http://192.168.1.1/telnet?enable=1&key=956E784E8880 将956E784E8880替换为步骤 2 中获取的实际 MAC 值。 ​HG6145D 和 HG6142a1 型号​： http://192.168.1.1/cgi-bin/telnetenable.cgi?telnetenable=1&key=956E784E8880 同样将956E784E8880替换为实际 MAC 值。 输入链接并回车后，若页面显示Open telnet success!，则表示 Telnet 功能开启成功。 步骤 4：通过 Telnet 连接光猫 打开命令提示符（Windows）或终端（Mac/Linux） 。 在终端窗口输入命令：telnet 192.168.1.1，回车后尝试连接光猫。 连接成功后，系统会提示输入用户名和密码： ​用户名​：尝试admin或root 。 ​密码​：格式为Fh@4E8880，将4E8880替换为步骤 2 中 MAC 值的后六位 。 步骤 5：进入工厂模式并查看超管信息 登录成功后，在命令行输入load_cli factory，回车进入工厂模式。 输入show admin_pwd，回车即可显示管理员密码。 输入show admin_name，回车显示管理员账户名称。 步骤 6：使用超管账户登录 在浏览器地址栏输入http://192.168.1.1/cu.html，进入登录界面。使用步骤 5 中获取的管理员账户和密码，即可登录光猫超管后台。
检测到 localhost 代理配置，但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理解决方法
WSL 中使用 V2 代理的配置方法 在使用 Windows 上的 V2 代理软件时，可能会遇到 WSL 无法通过 localhost 访问代理的问题，提示如下错误： wsl: 检测到 localhost 代理配置，但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。 这是因为 WSL 默认运行在 NAT 模式 下，与 Windows 处于不同的网络命名空间中，导致 WSL 无法访问 Windows 的 localhost 代理服务。 🧩 问题原因分析 原因 说明 NAT 模式 WSL 默认使用 NAT 网络模式，与 Windows 网络隔离 无法访问 localhost WSL 无法直接访问 Windows 的 localhost 代理（如 V2Ray 监听在 127.0.0.1:1080） 代理类型 V2Ray、Shadowsocks 等通常监听在 127.0.0.1，而非 0.0.0.0 ✅ 解决方案：修改 WSL 网络模式为 Mirrored 🔧 步骤一：创建或编辑 .wslconfig 文件 .wslconfig 应该放在 Windows 的用户目录下： C:\Users\<YourUsername>\.wslconfig 替换 <YourUsername> 为你的实际用户名。 如果文件不存在，请新建一个，并写入以下内容： [experimental] autoMemoryReclaim=gradual networkingMode=mirrored networkingMode=mirrored：启用“镜像”模式，允许 WSL 共享 Windows 的网络设置（包括代理） autoMemoryReclaim=gradual：优化内存回收（可选） 🔄 步骤二：重启 WSL 在 Windows 命令行中执行： wsl --shutdown 然后重新启动 WSL： wsl 🌐 步骤三：在 WSL 中设置代理 现在 WSL 已经可以访问 Windows 的 localhost 代理了。你可以手动设置环境变量来使用代理。 export http_proxy="http://127.0.0.1:10808" export https_proxy="http://127.0.0.1:10809" 如果你使用的是 V2Ray，默认端口通常是 10808 或 10809，请根据你的代理软件配置调整。 ⚠️ 注意事项 确保代理监听在 0.0.0.0 而非 127.0.0.1 如果你的代理软件只监听 127.0.0.1，那么即使 WSL 设置了 localhost 代理也无法访问。你需要将代理配置为监听 0.0.0.0，以便 WSL 可以访问。 例如，在 V2Ray 配置文件中，将监听地址改为： "listen": "0.0.0.0", "port": 1080 临时设置代理 如果不想每次登录都手动设置，可以将代理配置写入 shell 配置文件（如 ~/.bashrc 或 ~/.zshrc）。 📚 总结 问题 解决方式 WSL 无法访问 Windows 的 localhost 代理 修改 .wslconfig，设置 networkingMode=mirrored 代理监听在 127.0.0.1 修改代理监听地址为 0.0.0.0 设置 WSL 代理 手动设置 http_proxy 和 https_proxy 环境变量
亲测有效，此处以phpstrom为例，按理其他产品也可以用相同的方式激活。 原理是我们通过代码搜索其他授权服务器进行永久激活。 方式一 通过censys https://search.censys.io/ 用到的代码： [hide]services.http.response.headers.location: account.jetbrains.com/fls-auth[/hide] 我们将上面这串代码复制进censys搜索框中进行搜索，会出现： 可以看到出现了很多对应跳转到 jetbrains 的服务器IP和网址,我们随便点击一个看下状态是不是 302 只有 302 的才能正常使用 。 然后我们复制域名或者IP到jetbrains全家桶进行激活,比如我们使用第一个 49.234.70.205 复制到 License server 。 可以看到已经连接到jetbrains授权服务器成功了,然后我们点击 ACTIVATE 进行启动就可以了。 方式二 通过shodan或fofa https://fofa.info/ https://www.shodan.io 用到的代码： [hide]Location: https://account.jetbrains.com/fls-auth[/hide] 和方式一同样我们将代码复制到搜索框进行搜索就可以了。 通过shodan我们可以直接看到 HTTP/1.1 302 不用再每个都要点进去看了,相对来说比方式一方便。 激活使用jetbrains全家桶方式和censys一样,激活之后我们可以看到用的是 天津理工大学 的 jetbrains授权服务器 。 jetbrains激活原理 通过以上方式激活 jetbrains全家桶 主要是用到了爬取网站服务这一类的搜索引擎实现的。通过搜索引擎我们找到全世界的 jetbrains授权服务器进行激活。 注意事项 每个服务器IP承载激活的数量优先, 如果激活时候提示失败,可以多换几个. 本文章仅用于学习研究勿用于非法. 请支持正版
JS逆向常见问题解决方式** 一、AES加密 JS实现 const CryptoJS = require('crypto-js'); //引用AES源码js const key = CryptoJS.enc.Utf8.parse("1234123412ABCDEF"); //十六位十六进制数作为密钥 const iv = CryptoJS.enc.Utf8.parse('ABCDEF1234123412'); //十六位十六进制数作为密钥偏移量 //解密方法 function Decrypt(word) { let encryptedHexStr = CryptoJS.enc.Hex.parse(word); let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr); let decrypt = CryptoJS.AES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8); return decryptedStr.toString(); } //加密方法 function Encrypt(word) { let srcs = CryptoJS.enc.Utf8.parse(word); let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encrypted.ciphertext.toString().toUpperCase(); } export default { Decrypt , Encrypt } Python实现 import base64 import binascii from Crypto.Cipher import AES from Crypto.Util.Padding import pad class AESUtil(object): config = { 'ECB': 1, 'CBC': 2, 'CFB': 3, 'OFB': 5, 'CTR': 6, 'OPENPGP': 7, 'CCM': 8, 'EAX': 9, 'SIV': 10, 'GCM': 11, 'OCB': 12 } @classmethod def encrypt(cls, text, key, iv=None, encoding='b64', mode='ECB', padding='pkcs7'): key = key.encode() # 秘钥，b就是表示为bytes类型 if mode != 'ECB': iv = iv.encode() aes = AES.new(key, mode=cls.config[mode], iv=iv) # 创建一个aes对象 else: aes = AES.new(key, mode=cls.config[mode]) # 创建一个aes对象 text = text.encode() if padding: text = pad(text, AES.block_size, style='pkcs7') # 选择pkcs7补全 encrypt_aes = aes.encrypt(text) if encoding == 'b64': encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 解码 elif encoding == 'hex': encrypted_text = str(binascii.b2a_hex(encrypt_aes), encoding='utf-8') else: raise ValueError('错误的编码') encrypted_text_str = encrypted_text.replace("\n", "") return encrypted_text_str if __name__ == '__main__': t = '{"YSZH":"2023","XMMC":"","verifyCode":"bg34"}' key = 'dbd99c5139145091' # iv = '0000000000000000' m = 'ECB' encoding = 'hex' res = AESUtil.encrypt(t, key=key, iv=None, mode=m, encoding=encoding) print(res) import base64 import binascii from Crypto.Cipher import AES # 需要补位，str不是16的倍数那就补足为16的倍数 def add_to_16(value): while len(value) % 16 != 0: value += '\0' return str.encode(value) # 加密方法 def aes_encrypt(key, t, iv): aes = AES.new(add_to_16(key), AES.MODE_CBC, add_to_16(iv)) # 初始化加密器 encrypt_aes = aes.encrypt(add_to_16(t)) # 先进行 aes 加密 encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 执行加密并转码返回 bytes return encrypted_text # 解密方法 def aes_decrypt(key, t, iv): aes = AES.new(add_to_16(key), AES.MODE_CBC, add_to_16(iv)) # 初始化加密器 base64_decrypted = binascii.a2b_hex(t) # 优先逆向解密 hex 成 bytes decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '') # 执行解密密并转码返回str return decrypted_text if __name__ == '__main__': secret_key = 'jo8j9wGw%6HbxfFn' encrypted_str = '' iv = '0123456789ABCDEF' decrypted_str = aes_decrypt(secret_key, encrypted_str, iv) print('解密字符串：', decrypted_str) import rsa import json import struct import base64 import random import binascii import hashlib from Crypto.Cipher import AES from Crypto.PublicKey import RSA from Crypto.Util.Padding import pad from Crypto.Cipher import PKCS1_v1_5 as Cipher_pksc1_v1_5 class AESUtil(object): config = { 'ECB': 1, 'CBC': 2, 'CFB': 3, 'OFB': 5, 'CTR': 6, 'OPENPGP': 7, 'CCM': 8, 'EAX': 9, 'SIV': 10, 'GCM': 11, 'OCB': 12 } @classmethod def encrypt(cls, text, key, iv='0000000000000000', mode='CBC', padding='pkcs7'): key = key.encode() # 秘钥，b就是表示为bytes类型 iv = iv.encode() text = text.encode() if padding: text = pad(text, AES.block_size, style='pkcs7') # 选择pkcs7补全 aes = AES.new(key, mode=cls.config[mode], iv=iv) # 创建一个aes对象 encrypt_aes = aes.encrypt(text) integers = struct.unpack('>' + 'i'*int(len(encrypt_aes)/4), encrypt_aes) s = [] for i in range(len(encrypt_aes)): c = integers[i >> 2] >> 24 - i % 4 * 8 & 255 s.append(c) return s @classmethod def decrypt(cls, text, key='jo8j9wGw%6HbxfFn', iv='0123456789ABCDEF',): key = key.encode() # 秘钥，b就是表示为bytes类型 iv = iv.encode() text = text.encode() aes = AES.new(key, AES.MODE_CBC, iv) # 初始化加密器 base64_decrypted = binascii.a2b_hex(text) # 优先逆向解密 hex 成 bytes decrypted_text = aes.decrypt(base64_decrypted).decode() # 执行解密密并转码返回str unpad = lambda x: x[0:-ord(x[-1])] return unpad(decrypted_text) class RsaUtil: @staticmethod def rsa_encrypt(password, public_key): public_key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----' rsa_key = RSA.importKey(public_key) cipher = Cipher_pksc1_v1_5.new(rsa_key) cipher_text = base64.b64encode(cipher.encrypt(password.encode())) return cipher_text.decode() @staticmethod def encrypt(e=None, n=None, text=None): e = int(e, 16) n = int(n, 16) pub_key = rsa.PublicKey(e=e, n=n) m = rsa.encrypt(text.encode(), pub_key) return m.hex() def gen_rsa_number(): def _e(): return str(hex(int(65536 * (1 + random.random())))).replace('0x', '')[1:] def _rand_str_16(): random_16_str = ''.join([_e() for i in range(4)]) return random_16_str def _gen_u(): while True: n = "00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81" e = '10001' random_str = _rand_str_16() u = RsaUtil().encrypt(e, n, random_str) if len(u) == 256: return u, random_str return _gen_u() def _gen_p(e): def t(e, t): n = 0 for r in range(23, -1, -1): if 1 == (t >> r & 1): n = (n << 1) + (e >> r & 1) return n def bggf(e): t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()" return "." if e < 0 or e >= len(t) else t[e] n = '' r = '' o = len(e) for _ in range(0, o, 3): if _ + 2 < o: a = (e[_] << 16) + (e[_ + 1] << 8) + e[_ + 2] n += bggf(t(a, 7274496)) + bggf(t(a, 9483264)) + bggf(t(a, 19220)) + bggf(t(a, 235)) else: c = o % 3 if 2 == c: a = e[_] << 16 + e[_ + 1] << 8 n += bggf(t(a, 7274496)) + bggf(t(a, 9483264)) + bggf(t(a, 19220)) r = '.' elif 1 == c: a = e[_] << 16 n += bggf(t(a, 7274496)) + bggf(t(a, 9483264)) r = '.' + '.' # print(n) return { 'res': n, 'end': r } def gen_i(data, u, random_str): text = json.dumps(data, separators=(',', ':')) o = AESUtil.encrypt(text, key=random_str) r = ''.join([v for k, v in _gen_p(o).items()]) w = r + u return w def get_ord_md5(magic): magic_ord_list = [ord(i) for i in magic] r = ''.join([v for k, v in _gen_p(magic_ord_list).items()]) md5_str = hashlib.md5(r.encode()).hexdigest() return md5_str def get_md5(magic): md5_str = hashlib.md5(magic.encode()).hexdigest() return md5_str if __name__ == '__main__': # t = '{"lang":"zh-cn","passtime":570,"a":"1885_8784","pic":"/nerualpic/space_l1_zh_2019.07.17/space/6e4b452f02a5bc85a995e189b550affd.jpg","tt":"M.O8Pjn5(?PjUA3(/@AJ@Ff0B-1/9j5)*)**O,/jK:*)))P3M9)Lp**5*)M9Y9G1(gY85555,8e,b(c,((beo94*M?0)(Y7)4)(fD)(d8)(N4)(?5Y-,SD)N5/,bE1I-*-E-(ME-P:TOU-4SURjM9JTM99j-*M9/)(0qq(()(*gSLM0qM/2wk-b(b1(56(99,-9)/39A0(MA,)b52(MF*9b9(((","ep":{"ca":[{"x":1225,"y":305,"t":1,"dt":734},{"x":1364,"y":391,"t":3,"dt":583},{"x":1176,"y":322,"t":1,"dt":22696},{"x":1336,"y":384,"t":3,"dt":569}],"v":"3.0.8","te":false,"me":true,"tm":{"a":1690862300260,"b":1690862300321,"c":1690862300322,"d":0,"e":0,"f":1690862300261,"g":1690862300266,"h":1690862300266,"i":1690862300266,"j":1690862300299,"k":1690862300282,"l":1690862300300,"m":1690862300320,"n":1690862300320,"o":1690862300323,"p":1690862301099,"q":1690862301099,"r":1690862301099,"s":1690862301428,"t":1690862301428,"u":1690862301428}},"h9s9":"1816378497","rp":"ebf4ed2074c7d048821a9e00bf2b9fdf"}' # key = 'fa45533e57615018' # iv = '0000000000000000' # m = 'CBC' # encoding = 'hex' # res = AESUtil.encrypt(t, key=key, iv=iv, mode=m, encoding=encoding) # print(res) # t = '04aba42c7f286c7387e7ca2d1134c344fb8f4e142ac42aba39d1ed76735d249ef44e8ff177c74512439b932d70aa9c17' # key = 'jo8j9wGw%6HbxfFn' # iv = '0123456789ABCDEF' # res = AESUtil.decrypt(t, key, iv) # print(res) # gen_rsa_number() print(AESUtil.decrypt( 'fa8f1891eb026fa5ffabbd521d5acd4dace8066fd57432ad7076b9deb603f4890bdd337cec56fe776c64ff0de5a9be054b76ab1f4952248619c76d1f88cbb64714242b80311914b51c2e53b76e51cf4a0f09c4a8a39be3a9e2abd92009e9ab04cf5ec42feecf1e9470e7e4838ce9c65afcdd4d453697f8883ba4ce19d9000a6fae0e704c1c3504dfd8fed03c02e7cc85a7e3138c2c951d224c1c5089978080f1a764f33210c08b0079d4efc539a78450b97fa9196dcb25c46f088cf7c2fe1d130e393eceda5d71539e6e77bfe3b4d51acaf3485d493db3c06bee24c992bd3ca8')) 二、DES加密 JS代码 // DES-128-CBC-Zero-crypt function desZeroEncrypt(data, key) { let iv = CryptoJS.enc.Utf8.parse(key); iv = CryptoJS.enc.Base64.stringify(iv).toString().substring(0,8);//base64加密取前8位 iv = CryptoJS.enc.Utf8.parse(iv); key = CryptoJS.enc.Utf8.parse(key); return CryptoJS.DES.encrypt(data, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding }).toString(); } Python代码 # -*- coding:utf-8 -*- import base64 import json from Crypto.Cipher import DES class Crypt(object): """加密和解密工具类""" des_key = "fipFfVsZsTda94hJNKJfLoaqyqMZFFimwLt" block_size = DES.block_size pad_str = ['\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08'] @classmethod def des_base64_encrypt(cls, reqdata): """ 基于DES和base64的加密算法 @:param reqdata 需要加密的请求数据 """ key = cls.des_key.encode()[:8] length = len(reqdata) if length < cls.block_size: add = cls.block_size - length elif length > cls.block_size: add = cls.block_size - (length % cls.block_size) else: add = 8 reqdata = reqdata + (cls.pad_str[add - 1] * add) des = DES.new(key, DES.MODE_ECB) encrypt_data = des.encrypt(reqdata.encode()) return base64.b64encode(encrypt_data).decode() @classmethod def des_base64_decrypt(cls, retdata): """DES解密 @:param retdata: lakala reponse retData """ key = cls.des_key.encode()[:8] debase64_data = base64.b64decode(retdata) des = DES.new(key, DES.MODE_ECB) decrypt_data = des.decrypt(debase64_data) return decrypt_data if __name__ == '__main__': item = { 'name': '凯文-杜兰特', 'image': 'durant.png', 'birthday': '1988-09-29', 'height': '208cm', 'weight': '108.9KG' } string = f'{base64.b64encode(item["name"].encode()).decode()}{item["birthday"]}{item["height"]}{item["weight"]}' print(string) print(Crypt.des_base64_encrypt(string)) 三、RSA加密 JS代码 import JSEncrypt from "jsencrypt"; export const encryption = (params: string) => { const encryptor = new JSEncrypt(); // 创建加密对象实例 // openssl生成的公钥，复制的时候要小心不要有空格 let pubKey = '' pubKey = "-----BEGIN PUBLIC KEY-----" + "这里放公钥" "-----END PUBLIC KEY-----"; encryptor.setPublicKey(pubKey); // 设置公钥 const rsaPassWord = encryptor.encrypt(params); // 对内容进行加密 return rsaPassWord; }; Python代码 import base64 from Crypto.Cipher import PKCS1_v1_5 as Cipher_pksc1_v1_5 from Crypto.PublicKey import RSA def encrypt(password, public_key): public_key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----' rsakey = RSA.importKey(public_key) cipher = Cipher_pksc1_v1_5.new(rsakey) cipher_text = base64.b64encode(cipher.encrypt(password.encode())) return cipher_text.decode() import rsa def encrypt(e, n, text): e = int(e, 16) n = int(n, 16) pub_key = rsa.PublicKey(e=e, n=n) m = rsa.encrypt(text.encode(), pub_key) return m.hex() 四、DES3加密 JS代码 // 引用 crypto-js 加密模块 var CryptoJS = require('crypto-js') function tripleDesEncrypt() { var key = CryptoJS.enc.Utf8.parse(desKey), iv = CryptoJS.enc.Utf8.parse(desIv), srcs = CryptoJS.enc.Utf8.parse(text), // ECB 加密方式，Iso10126 填充方式 encrypted = CryptoJS.TripleDES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Iso10126 }); return encrypted.toString(); } function tripleDesDecrypt() { var key = CryptoJS.enc.Utf8.parse(desKey), iv = CryptoJS.enc.Utf8.parse(desIv), srcs = encryptedData, // ECB 加密方式，Iso10126 填充方式 decrypted = CryptoJS.TripleDES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Iso10126 }); return decrypted.toString(CryptoJS.enc.Utf8); } var text = "I love Python!" // 待加密对象 var desKey = "6f726c64f2c2057c" // 密钥 var desIv = "0123456789ABCDEF" // 偏移量 var encryptedData = tripleDesEncrypt() var decryptedData = tripleDesDecrypt() console.log("加密字符串: ", encryptedData) console.log("解密字符串: ", decryptedData) // 加密字符串: 3J0NX7x6GbewjjhoW2HKqg== // 解密字符串: I love Python! Python代码 from Cryptodome.Cipher import DES3 from Cryptodome import Random # 需要补位，str不是16的倍数那就补足为16的倍数 def add_to_16(value): while len(value) % 16 != 0: value += '\0' return str.encode(value) def des_encrypt(key, text, iv): # 加密模式 OFB cipher_encrypt = DES3.new(add_to_16(key), DES3.MODE_OFB, iv) encrypted_text = cipher_encrypt.encrypt(text.encode("utf-8")) return encrypted_text def des_decrypt(key, text, iv): # 加密模式 OFB cipher_decrypt = DES3.new(add_to_16(key), DES3.MODE_OFB, iv) decrypted_text = cipher_decrypt.decrypt(text) return decrypted_text if __name__ == '__main__': key = '12345678' # 密钥，16 位 text = 'I love Python!' # 加密对象 iv = Random.new().read(DES3.block_size) # DES3.block_size == 8 secret_str = des_encrypt(key, text, iv) print('加密字符串：', secret_str) clear_str = des_decrypt(key, secret_str, iv) print('解密字符串：', clear_str) # 加密字符串：b'\xa5\x8a\xd4R\x99\x16j\xba?vg\xf2\xb6\xa9' # 解密字符串：b'I love Python!' 五、无限Debugger绕过 Function.prototype.constructor_bc=Function.prototype.constructor; Function.prototype.constructor=function(){ if(arguments && arguments[0]==="debugger"){"什么都不做"} //利用arguments关键字的属性获取当前方法里面的参数 else{ return Function.prototype.constructor_bc.apply(this,arguments) //跟上面处理debugger和定时器一起用的处理方法一样 } } 六、Hook cookie // ==UserScript== // @name Hook Cookie // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @include * // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; var cookie_cache = document.cookie; Object.defineProperty(document, 'cookie', { get: function () { return cookie_cache; }, set: function (val) { console.log('Setting cookie', val); // 填写cookie名 if (val.indexOf('lqWVdQzgOVyaT') != -1) { debugger; } var cookie = val.split(";")[0]; var ncookie = cookie.split("="); var flag = false; var cache = cookie_cache.split("; "); cache = cache.map(function (a) { if (a.split("=")[0] === ncookie[0]) { flag = true; return cookie; } return a; }) cookie_cache = cache.join("; "); if (!flag) { cookie_cache += cookie + "; "; } return cookie_cache; } }); })(); (function () { 'use strict'; var cookie_cache = document.cookie; Object.defineProperty(document, 'cookie', { get: function () { return cookie_cache; }, set: function (val) { console.log('Setting cookie', val); // 填写cookie名 if (val.indexOf('lqWVdQzgOVyaT') != -1) { debugger; } var cookie = val.split(";")[0]; var ncookie = cookie.split("="); var flag = false; var cache = cookie_cache.split("; "); cache = cache.map(function (a) { if (a.split("=")[0] === ncookie[0]) { flag = true; return cookie; } return a; }) cookie_cache = cache.join("; "); if (!flag) { cookie_cache += cookie + "; "; } return cookie_cache; } }); })(); 七、Hook JSON.parse (function() { var parse = JSON.parse; JSON.parse = function(params) { console.log("Hook JSON.parse ——> ", params); debugger; return parse(params); } })(); 八、Proxy代理设置 function vmProxy(object){ return new Proxy(object, { get: function(target, property, receiver) { if(target[property] == undefined){ console.log("get: ", target, property, target[property]); } return target[property]; }, set: function(target, property, value) { console.log("set: ", target, property, value); return Reflect.set(...arguments); } }) } ld = {}; ld.config = {}; ld.config.proxy = true; // 代理开关 ld.getType = function getType(obj){ return Object.prototype.toString.call(obj); } ld.proxy = function proxy(obj, objName){ // obj: 原始对象 // objName: 原始对象的名字 if(!ld.config.proxy){ return obj; } let handler = { get:function (target,prop,receiver){ // 三个参数 获取属性时调用 let result; try {//防止报错 result = Reflect.get(target,prop,receiver); let type = ld.getType(result); if(result instanceof Object){ console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); // 递归代理 result = ld.proxy(result, `${objName}.${prop.toString()}`); }else if(typeof result === "symbol"){ console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result.toString()}]}`); }else{ console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result}]}`); } }catch (e) { console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, set:function (target,prop,value,receiver){ // 设置属性时调用 let result; try{ result = Reflect.set(target,prop,value,receiver); let type = ld.getType(value); if(value instanceof Object){ console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); }else if(typeof value === "symbol"){ console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value.toString()}]}`); }else{ console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value}]}`); } }catch (e){ console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, getOwnPropertyDescriptor:function (target, prop){ // 获取属性描述符时调用 let result;// undefined, 描述符对象 try{ result = Reflect.getOwnPropertyDescriptor(target, prop); let type = ld.getType(result); // debugger; console.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`); // if(typeof result !== "undefined"){ // result = ld.proxy(result, `${objName}.${prop.toString()}.PropertyDescriptor`); // } }catch (e){ console.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, defineProperty: function (target, prop, descriptor){ // 定义属性时调用 let result; try{ result = Reflect.defineProperty(target, prop, descriptor); console.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}]}`); }catch (e) { console.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`); } return result; }, apply:function (target, thisArg, argumentsList){ // 函数调用时使用 // target: 函数对象 // thisArg: 调用函数的this指针 // argumentsList: 数组， 函数的入参组成的一个列表 let result; try{ result = Reflect.apply(target, thisArg, argumentsList); let type = ld.getType(result); if(result instanceof Object){ console.log(`{apply|function:[${objName}], type:[${type}]}`); }else if(typeof result === "symbol"){ console.log(`{apply|function:[${objName}], result:[${result.toString()}]}`); }else{ console.log(`{apply|function:[${objName}], result:[${result}]}`); } }catch (e) { console.log(`{apply|function:[${objName}],error:[${e.message}]}`); } return result; }, construct:function (target, argArray, newTarget) { // 构造器，实例化的时候调用 // target: 函数对象 // argArray： 参数列表 // newTarget：代理对象 let result; try{ result = Reflect.construct(target, argArray, newTarget); let type = ld.getType(result); console.log(`{construct|function:[${objName}], type:[${type}]}`); }catch (e) { console.log(`{construct|function:[${objName}],error:[${e.message}]}`); } return result; }, deleteProperty:function (target, propKey){ //删除属性 let result = Reflect.deleteProperty(target, propKey); console.log(`{deleteProperty|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`); return result; }, has:function (target, propKey){ // in 操作符 let result = Reflect.has(target, propKey); console.log(`{has|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`); return result; }, ownKeys: function (target){ // 是否 let result = Reflect.ownKeys(target); console.log(`{ownKeys|obj:[${objName}]}`); return result }, getPrototypeOf:function(target){ // 获取原型对象 let result = Reflect.getPrototypeOf(target); console.log(`{getPrototypeOf|obj:[${objName}]}`); return result; }, setPrototypeOf:function(target, proto){ // 设置原型对象 let result = Reflect.setPrototypeOf(target, proto); console.log(`{setPrototypeOf|obj:[${objName}]}`); return result; }, preventExtensions:function(target){ // 扩展 let result = Reflect.preventExtensions(target, proto); console.log(`{preventExtensions|obj:[${objName}]}`); return result; }, // isExtensible:function(target){ // let result = Reflect.isExtensible(target, proto); // console.log(`{isExtensible|obj:[${objName}]}`); // return result; // } }; return new Proxy(obj, handler); };
web js逆向 web页面大家都不陌生，在web开发过程中后端负责程序架构和数据管理，前端负责页面展示和用户交互，有种不严谨的说法是：前端代码给浏览器看，后端代码给服务器看。 有开发经验的同学对前后端交互的理解会更深一点，在这种前后端分离的开发方式中，以接口为标准来进行联调整合。为了保证接口在调用时数据的安全性，也是为了防止请求的参数被篡改，大多数接口都进行了请求签名、身份认证、动态Cookie等机制。另外部分网站会对返回的数据进行加密，通常利用AES、RSA等加密方式，也有在传输时对数据进行序列化，比如Protobuf等，这些会在后面进行详细讲解。 请求签名十分常见，比如URL中的加密参数sign，身份认证也有很多例子，比如动态Cookie。这些参数的生成方法都是Js来控制的，如果想要直接从接口上获取数据，就要去调试分析JavaScript的调用逻辑、堆栈调用关系来弄清楚网站加密的实现方法，根据网站的参数生成规则还原加密参数，可以称这个过程为Js逆向。 我们总结了一下目前加密参数的常用逆向方式，一种是根据源码的生成逻辑还原加密代码，另一种是补环境Copy源码模拟加密参数生成，还有一种是通过RPC的方式远程调用。 在一些逆向案例中，其中的关键就是将浏览器环境移植到Node环境中，Node Js采用的内核也为V8引擎，该引擎调用对方Js的可行性并不是100%，同时由于Node没有界面渲染，因此在浏览器中可使用的例如window、navigator、dom等操作在node中是不存在的，所以对于Node Js的环境搭建和浏览器环境补齐也是Js逆向需要掌握的。 还有就是Chrome作为Js逆向的核心工具，熟练掌握Chrome的控制台、插件编写就足够应对绝大多数的抓包、调试、Hook等，这些内容都会在后续进行讲解。 第一章 逆向基础 1.1 语法基础 Js调试相对方便，通常只需要chrome或者一些抓包工具、扩展插件，就能顺利的完成逆向分析。但是Js的弱类型和语法多样，各种闭包，逗号表达式等语法让代码可读性变得不如其他语言顺畅。所以需要学习一下基础语法。 基本数据类型 字符串 String 数字 Number 布尔 Boolean 空值 Null 未定义 Undefined 独一无二的值 Symbol 引用数据类型 对象 Object 数组 Array 函数 Function 语句标识符 在条件为true时重复执行 do……while 在条件为true时执行 while 循环遍历 for 条件判断 if……else 根据情况执行代码块 switch 退出循环 break 异常捕获 try……catch……finally 抛出异常 Throw 声明固定值的变量 const 声明类 class 停止函数并返回 return 声明块作用域的变量 let 声明变量 var 断点调试 debugger 当前所属对象 this 算数运算符 加 + 减 - 乘 * 除 / 取余 % 累加 ++ 递减 – 比较运算符 等于 == 相等值或者相等类型 === 不等于 != 不相等值或者不相等类型 !== 大于 > 小于 < 大于等于 >= 小于等于 <= 在JavaScript中将数字存储为64位浮点数，但所有按位运算都以32位二进制数执行。在执行位运算之前，JavaScript将数字转换位32位有符号整数。执行按位操作后，结果将转换回64位JavaScript数。 Javascript 函数 JavaScript 中的函数是头等公民，不仅可以像变量一样使用它，同时它还具有十分强大的抽象能力 定义函数的 2 种方式 在JavaScript 中，定义函数的方式如下： function abs(x) { if (x >= 0) { return x; } else { return -x; } } 上述 abs() 函数的定义如下： function 指出这是一个函数定义； abs 是函数的名称； (x) 括号内列出函数的参数，多个参数以，分隔； {……} 之间的代码是函数体，可以包含若干语句，甚至可以没有任何语句。 请注意，函数体内部的语句在执行时，一旦执行到 return 时，函数就执行完毕，并将结果返回。因此，函数内部通过条件判断和循环可以实现非常复杂的逻辑。 如果没有 return 语句，函数执行完毕后也会返回结果，只是结果为 undefined。 由于JavaScript的函数也是一个对象，上述定义的 abs()函数实际上是一个函数对象，而函数名 abs可以视为指向该函数的变量。 因此，第二种定义函数的方式如下： var abs = function (x) { if (x >= 0) { return x; } else { return -x; } }; 在这种方式下，function（x）{……} 是一个匿名函数，它没有函数名。但是，这个匿名函数赋值给了变量 abs，所以，通过变量 abs 就可以调用该函数。 注意：上述两种定义 完全等价 ，第二种方式按照完整语法需要在函数体末尾加一个 ;，表示赋值语句结束。（ 加不加都一样，如果按照语法完整性要求，需要加上） 调用函数时，按顺序传入参数即可： abs(10); // 返回10 abs(-9); // 返回9 由于JavaScript 允许传入任意个参数而不影响调用，因此传入的参数比定义的参数多也没有问题，虽然函数内部并不需要这些参数： abs(10, 'blablabla'); // 返回10 abs(-9, 'haha', 'hehe', null); // 返回9 传入的参数比定义的少也没有问题： abs(); // 返回NaN 此时 abs(s) 函数的参数 x 将收到 undefined，计算结果为NaN。要避免收到undefined，可以对参数进行检查： function abs(x) { if (typeof x !== 'number') { throw 'Not a number'; // 停止并抛出错误信息 } if (x >= 0) { return x; } else { return -x; } } 1.2 作用域 Js中有一个被称为作用域的特性。作用域是在运行时代码中的某些特定部分中变量、函数和对象的可访问性。换句话说，作用域决定了代码区块中变量和其他资源的可见性。作用域最大的用处就是隔离变量，不同作用域下同名变量不会有冲突。 Js的作用域分为三种：全局作用域、函数作用域、块级作用域。全局作用域可以让用户在任何位置进行调用，需要注意的是最外层函数和在最外层函数外面定义的变量拥有全局作用域，所有未定义直接赋值的变量会自动声明为拥有全局作用域，所有window对象的属性也拥有全局作用域。函数作用域也就是说只有在函数内部可以被访问，当然函数内部是可以访问全局作用域的。块级作用域则是在if和switch的条件语句或for和while的循环语句中，块级作用域可通过新增命令let和const声明，所声明的变量在指定的作用域外无法被访问。 1.3 窗口对象属性 我们总结了浏览器window的常见属性和方法。因为很多环境监测都是基于这些属性和方法的，在补环境前，需要了解window对象的常用属性和方法。 Window Window对象表示浏览器当前打开的窗口。 Document对象 document History对象 history Location对象 location Navigator对象 navigator Screen对象 screen 按照指定的像素值来滚动内容 scrollBy() 把内容滚动到指定的坐标 scrollTo() 定时器 setInterval() 延时器 setTimeout() 弹出警告框 alert() 弹出对话框 prompt() 打开新页面 open() 关闭页面 close() Document 载入浏览器的HTML文档。 元素 body 当前cookie cookie 文档域名 domain 文档最后修改日期和时间 lastModified 访问来源 referrer 文档标题 title 当前URL URL 返回指定id的引用对象 getElementById() 返回指定名称的对象集合 getElementByName() 返回指定标签名的对象集合 getElementByTagName() 打开流接收输入输出 open() 向文档输入 write() Navigator Navigator对象包含的属性描述了当前使用的浏览器，可以使用这些属性进行平台专用的配置。 用户代理 userAgent 浏览器代码名 AppCodeName 浏览器名称 AppName 浏览器版本 AppVersion 浏览器语言 browserLanguage 指明是否启用cookie的布尔值 cookieEnabled 浏览器系统的cpu等级 cpuClass 是否处于脱机模式 onLine 浏览器的操作系统平台 platform 插件，所有嵌入式对象的引用 plugins 是否启用驱动 webdriver 引擎名 product 硬件支持并发数 hardwareConcurrency 网络信息 connection 是否启用java javaEnabled() 是否启用数据污点 taintEnabled() Location Location对象包含有关当前URL的信息 URL锚 hash 当前主机名和端口号 host 当前主机名 hostname 当前URL href 当前URL的路径 pathname 当前URL的端口号 port 当前URL的协议 protocol 设置URL查询部分 search 加载新文件 assign() 重新加载文件 reload() 替换当前文档 replace() Screen 每个window对象的screen属性都引用一个Screen对象。Screen对象中存放着有关显示浏览器屏幕的信息。 屏幕高度 avaiHeight 屏幕宽度 availWidth 调色版比特深度 bufferDepth 显示屏每英寸水平点数 deviceXDPI 显示屏每英寸垂直点数 deviceYDPI 是否启用字体平滑 fontSmoothingEnabled 显示屏高度 height 显示屏分辨率 pixeIDepth 屏幕刷新率 updateInterval 显示屏宽度 width History History对象包含用户在浏览器窗口中访问过的URL。 浏览器历史列表中的URL数量 length 加载前一个URL back() 加载下一个URL forward() 加载某个具体页面 go() window中还有很多属性和方法，这里就不再过多的描述，大家可以自行查看。 1.4 事件 HTML 事件是发生在 HTML 元素上的事情。 当在 HTML 页面中使用 JavaScript 时， JavaScript 可以触发这些事件。 HTML 事件可以是浏览器行为，也可以是用户行为。 以下是 HTML 事件的实例： HTML 页面完成加载 HTML input 字段改变时 HTML 按钮被点击 通常，当事件发生时，你可以做些事情。 在事件触发时 JavaScript 可以执行一些代码。 HTML 元素中可以添加事件属性，使用 JavaScript 代码来添加 HTML 元素。 事件可以用于处理表单验证，用户输入，用户行为及浏览器动作: 页面加载时触发事件 页面关闭时触发事件 用户点击按钮执行动作 验证用户输入内容的合法性 等等 … 可以使用多种方法来执行 JavaScript 事件代码： HTML 事件属性可以直接执行 JavaScript 代码 HTML 事件属性可以调用 JavaScript 函数 你可以为 HTML 元素指定自己的事件处理程序 你可以阻止事件的发生。 等等 .. HTML事件 事件 描述 onclick 当用户单击HTML元素时触发 ondblclick 当用户双击对象时触发 onmove 当对象移动时触发 onmoveend 当对象停止移动时触发 onmovestart 当对象开始移动时触发 onkeydown 当用户按下键盘按键时触发 onkeyup 当用户释放键盘按键时触发 onload 当某个页面或图像被完成加载 onselect 当文本被选定 onblur 当元素失去焦点 onchange 当HTML元素改变时触发 onfocusin 当元素将要被设置为焦点之前触发 onhelp 当用户在按F1键时触发 onkeypress 当用户按下字面键时触发 onmousedown 当用户用任何鼠标按钮单击对象时触发 onmousemove 当用户将鼠标划过对象时触发 onmouseover 当用户从一个HTML元素上移动鼠标时触发 onmouseout 当用户从一个HTML元素上移开鼠标时触发 onmouseup 当用户在对象之上释放鼠标按钮时触发 onmousewheel 当鼠标滚轮按钮旋转时触发 onstop 当用户单击停止按钮或者离开页面时触发 onactivate 当对象设置为活动元素时触发 onreadystatechange 当在对象上发生对象属性更改时触发 ondragend 当用户拖拽操作结束后释放鼠标时触发 接下来说一下HTMl中绑定事件的几种方法，分别是行内绑定、动态绑定、事件监听、bind和on绑定。 行内绑定是指把触发事件直接写到元素的标签中 点击 动态绑定是指先获取到dom元素，然后在元素上绑定事件 事件监听主要通过addEventListener()方法来实现 bind()和on()绑定都是属于JQuery的事件绑定方法，bind()的事件函数只能针对已经存在的元素进行事件的设置 $("button").bind("click",function(){ $("p").slideToggle(); }); on()支持对将要添加的新元素设置事件 $(document).ready(function(){ $("p").on("click", function(){}); }); 还有live()和delegate()等事件绑定方法，目前并不常用。 第二章 浏览器控制台 首先介绍一下浏览器控制台的使用，以开发者使用最多的chrome为例。Windows操作系统下的F12键可以打开控制台，mac操作系统下用Fn+F12键打开。我们选择平时使用较多的模块进行介绍 2.1 Network Network是Js调试的重点，面板上由控制器、过滤器、数据流概览、请求列表、数据统计这五部分组成。 控制器：Presserve Log是保留请求日志的作用，在跳转页面的时候勾选上可以看到跳转前的请求。Disable cache是禁止缓存的作用，Offline是离线模拟。 过滤器：根据规则过滤器请求列表的内容，可以选择XHR,JS,CSS,WS等。 数据流概览：显示HTTP请求、响应的时间轴。 请求列表：默认是按时间排序，可以看到浏览器所有的请求，主要用于网络请求的查看和分析，可以查看请求头、响应状态和内容、Form表单等。 数据统计：请求总数、总数据量、总花费时间等。 浏览器控制台Network下各项属性的含义 作用: 可以查看调取接口是否正确，后台返回的数据； 查看请求状态、请求类型、请求地址 2.1.1 Network-Headers 首先打开控制台，找到Network. 刷新页面可以看到Name Name对应的是资源的名称及路径, Status Code 是请求服务器返回的状态码，一般情况下当状态码为200时，则表示接口匹配成功。点击任一文件名，右侧会出现Header选项。 Network-Header-General Request URL: 资源请求的url Request Method: 请求方法（HTTP方法） Status Code: 状态码 200(状态码) OK 301 - 资源（网页等）被永久转移到其它URL 404 - 请求的资源（网页等）不存在 500 - 内部服务器错误(后台问题) Remote Address: 远程地址; Referrer Policy: 控制请求头中 refrrer 的内容 包含值的情况: “”, 空串默认按照浏览器的机制设置referrer的内容，默认情况下是和no-referrer-when-downgrade设置得一样 “no-referrer”, 不显示 referrer的任何信息在请求头中 “no-referrer-when-downgrade”, 默认值。当从https网站跳转到http网站或者请求其资源时（安全降级HTTPS→HTTP），不显示 referrer的信息，其他情况（安全同级HTTPS→HTTPS，或者HTTP→HTTP）则在 referrer中显示完整的源网站的URL信息 “same-origin”, 表示浏览器只会显示 referrer信息给同源网站，并且是完整的URL信息。所谓同源网站，是协议、域名、端口都相同的网站 “origin”, 表示浏览器在 referrer字段中只显示源网站的源地址（即协议、域名、端口），而不包括完整的路径 “strict-origin”, 该策略更为安全些，和 origin策略相似，只是不允许 referrer信息显示在从https网站到http网站的请求中（安全降级） “origin-when-cross-origin”, 当发请求给同源网站时，浏览器会在 referrer中显示完整的URL信息，发个非同源网站时，则只显示源地址（协议、域名、端口） “strict-origin-when-cross-origin”, 和 origin-when-cross-origin相似，只是不允许 referrer信息显示在从https网站到http网站的请求中（安全降级） “unsafe-url” 浏览器总是会将完整的URL信息显示在 referrer字段中，无论请求发给任何网站 补充: 什么是referrer? 当一个用户点击页面中的一个链接，然后跳转到目标页面时，本变页面会收到一个信息,即用户是从哪个源链接跳转过来的。 也就是说当你发起一个HTTP请求，请求头中的 referrer 字段就说明了你是从哪个页面发起该请求的; Network-Header-Response Headers Access-Control-Allow-Origin: 请求头中允许设置的请求方法 Connection: 连接方式 content-length: 响应数据的数据长度，单位是byte content-type: 客户端发送的类型及采用的编码方式 Date: 客户端请求服务端的时间 Vary: 用来指示缓存代理（例如squid）根据什么条件去缓存一个请求 Last-Modified: 服务端对该资源最后修改的时间 Server: 服务端的web服务端名 Content-Encoding: gzip 压缩编码类型 Transfer-Encoding:chunked: 分块传递数据到客户端 Network-Header-Request Headers Accept: 客户端能接收的资源类型 Accept-Encoding: 客户端能接收的压缩数据的类型 Accept-Language: 客户端接收的语言类型 Cache-Control: no-cache 服务端禁止客户端缓存页面数据 Connection: keep-alive 维护客户端和服务端的连接关系 Cookie:客户端暂存服务端的信息 Host: 连接的目标主机和端口号 Pragma: no-cache 服务端禁止客户端缓存页面数据 Referer: 来于哪里(即从哪个页面跳转过来的) User-Agent: 客户端版本号的名字 2.2 Sources Sources按列分为三列，从左至右分别是文件列表区、当前文件区、断点调试区。 文件列表区中有Page、Snippets、FileSytem等。Page可以看到当前所在的文件位置，在Snippets中单击New Snippets可以添加自定义的Js代码，FileSytem可以把本地的文件系统导入到chrome中。 当前文件区是需要重点操作的区域，单击下方的{}来格式化代码，就能看到美观的Js代码，然后可以根据指定行数进行断点调试。 断点调试区也非常重要，每个操作点都需要了解是什么作用。最上方的功能区分别是暂停、跳过、进入、跳出、步骤进入、禁用断点、异常断点。 Watch：变量监听，对加入监听列表的变量进行监听。 Call Stack：断点的调用堆栈列表，完整地显示了导致代码被暂停的执行路径。 Scope：当前断点所在函数执行的作用域内容。 Breakpoints：断点列表，将每个断点所在文件/行数/改成简略内容进行展示。 DOM Breakpoints：DOM断点列表。 XHR/fetch Breakpoints：对达到满足过滤条件的请求进行断点拦截。 Event Listener Breakpoints：打开可监听的事件监听列表，可以在监听事件并且触发该事件时进入断点，调试器会停留在触发事件代码行。 2.3 Application Application是应用管理部分，主要记录网站加载的所有资源信息。包括存储数据（Local Storage、Session Storage、InDexedDB、Web SQL、Cookies）、缓存数据、字体、图片、脚本、样式表等。Local Storage（本地存储）和 Session Storage中可以查看和管理其存储的键值对。这里使用最多的是对Cookies的管理了，有时候调试需要清除Cookies，可以在Application的Cookies位置单击鼠标右键，选择Clear进行清除，或者根据Cookies中指定的Name和Value来进行清除，便于进一步调试。 注意：我们辨别Cookie来源时，可以看httpOnly这一栏，有√的是来自于服务端，没有√的则是本地生成的。 2.4 Console 谷歌控制台中的Console区域用于审查DOM元素、调试JavaScript代码、查看HTML解析，一般是通过Console.log()来输出调试信息。在Console中也可以输出window、document、location等关键字查看浏览器环境，如果对某函数使用了断点，也可以在Console中调用该函数。 如果你平时只是用console.log()来输出一些变量的值，那你肯定还没有用过console的强大的功能。下面带你用console玩玩花式调试。 来看下主要的调试函数及用法： console.log(), console.error(), console.warn(), console.info() 最基本也是最常用的用法了，分别表示输出普通信息、错误信息、警示信息和提示性信息，且error和warn方法有特定的图标和颜色标识。 console.assert(expression, message) 参数： expression: 条件语句，语句会被解析成 Boolean，且为 false 的时候会触发message语句输出 message: 输出语句，可以是任意类型 该函数会在 expression 为 false 的时候，在控制台输出一段语句，输出的内容就是传入的第二个参数 message 的内容。当我们在只需要在特定的情况下才输出语句的时候，可以使用 console.assert 示例如下： ``` function greaterThan(a,b) { console.assert(a > b, {“message”:”a is not greater than b”,”a”:a,”b”:b}); } greaterThan(5,6); * console.count(label) * 参数： * label: 计算数量的标识符 * 该函数用于计算并输出特定标识符为参数的console.count函数被调用的次数。下面的例子更能直观的了解： * ``` function login(name) { console.count(name + ' logged in'); } console.dir(object) 参数： object：被输出扎实的对象 该函数用于打印出对象的详细的属性、函数及表达式等信息。如果该对象已经被记录为一个HTML元素，则该HTML元素的DOM表达式的属性会被像下面这样打印出来： ``` console.dir(document.body); * console.dirxml(object) * 该函数将打印输出XML元素及其子孙后代元素，且对HTML和XML元素调用 console.dirxml() 和 调用 console.log() 是等价的 * console.group([label]), console.groupEnd([label]) * 参数： * label: group分组的标识符 * 在控制台创建一个新的分组，随后输出到控制台上的内容都会自动添加一个缩进，表示该内容属于当前分组，知道调用 console.groupEnd() 之后，当前分组结束。 * 举个例子： * ``` console.log("This is the outer level"); console.group(); console.log("Level 2"); console.group(); console.log("Level 3"); console.warn("More of level 3"); console.groupEnd(); console.log("Back to level 2"); console.groupEnd(); console.log("Back to the outer level"); console.groupCollapsed(label) 该函数同console.group()，唯一的区别是该函数的输出默认不展开分组，而console.group()是默认展开分组。 console.time([label]), console.timeEnd([label]) label: 用于标记计时器的名称，不填的话，默认为 default console.time() 会开始一个计时器，并当执行到 console.timeEnd() 函数时（需要两个函数的lable参数相同），结束计时器，并将计时器的总时间输出到控制台上。 再举几个例子： console.time(); var arr = new Array(10000); for (var i = 0; i < arr.length; i++) { arr[i] = new Object(); } console.timeEnd(); // default: 3.696044921875ms 对 console.time(label) 设置一个自定义的 label 字段，并使用console.timeEnd(label) 设置相同的 label 字段来结束计时器。 console.time('total'); var arr = new Array(10000); for (var i = 0; i < arr.length; i++) { arr[i] = new Object(); } console.timeEnd('total'); // total: 3.696044921875ms 设置多个 label 属性，开启多个计时器同步计时。 console.time('total'); console.time('init arr'); var arr = new Array(10000); console.timeEnd('init arr'); for (var i = 0; i < arr.length; i++) { arr[i] = new Object(); } console.timeEnd('total'); // init arr: 0.0546875ms // total: 2.5419921875ms console.trace(object) 该函数将在控制台打印出从 console.trace() 被调用的位置开始的堆栈信息。 第三章 加密参数的定位方法 想要找到Js加密参数的生成过程，就必须要找到参数的位置，然后通过debug来进行观察调试。我们总结了目前通用的调试方式。每种方法都有其独特的运用之道，大家只有灵活运用这些参数定位方法，才能更好地提高逆向效率。 3.1 巧用搜索 搜索操作比较简单，打开控制台，通过快捷键Ctrl + F打开搜索框。在Network中的不同位置使用Ctrl + F会打开不同的搜索区域，有全局搜索、页面搜索。 另外关于搜索也有一定的技巧，如果加密参数的关键词是signature，可以直接全局搜索signature，搜索不到可以尝试搜索sign或者搜索接口名。如果还没有找到位置，则可以使用下面几种方法。 3.2 堆栈调试 控制台的 Initiator 堆栈调试是我们比较喜欢的调试方式之一，不过新版本的谷歌浏览器才有，如果没有 Initiator 需要更新Chrome版本。Initiator主要是为了监听请求是怎样发起的，通过它可以快速定位到调用栈中。 具体使用方法是先确定请求的接口，然后进入Initiator，单击第一个Request call stack参数，进入Js文件后，在跳转行上打上断点，然后刷新页面等待调试。 3.3 控制台调试 控制台的Console中可以由console.log()方法来执行某些函数，该方法对于开发调试很有帮助，有时通过输出会比找起来更便捷。在断点到某一处时，可以通过console.log()输出此时的参数来查看状态和属性,console.log()方法在后面的参数还原中也很重要。 3.4 监听XHR XHR是XMLHttpRequest的简称，通过监听XHR的断点，可以匹配URl中params参数的触发点和调用堆栈，另外post请求中From Data的参数也可以用XHR来拦截。 使用方法：打开控制台，单击Sources，右侧有一个XHR/fetch Breakpoints，单击+号即可添加监听事件。像一些URL中的_signature参数就很适合使用XHR断点。 3.5 事件监听 这里其实和监听XHR有些相似，为了方便记忆，我们将其单独放在一个小节中。 有的时候找不到参数位置，但是知道它的触发条件，此时可以使用事件监听器进行断点，在Sources中有 DOM Breakpoints、Global Listeners、Event Listener Breakpoints都可以进行DOM事件监听。 比如需要对Canvas进行断点，就在Event Listener Breakpoints中选择Canvas，勾选Create canvas context时就是对创建canvas时的事件进行了断点。 3.6 添加代码片 在控制台中添加代码片来完成Js代码注入，也是一种不错的方式。 使用方法：打开控制台，单击Sources，然后单击左侧的snippets，新建一个Script Snippet，就可以在空白区域编辑Js代码了。 3.7 Hook 在Js中也需要用到Hook技术，例如当想分析某个cookie是如何生成时，如果想通过直接从代码里搜索该cookie的名称来找到生成逻辑，可能会需要审核非常多的代码。这个时候，如果能够用hook document.cookie的set方法，那么就可以通过打印当时的调用方法堆栈或者直接下断点来定位到该cookie的生成代码位置。 什么是hook？ 在 JS 逆向中，我们通常把替换原函数的过程都称为 Hook。一般使用 Object.defineProperty() 来进行hook。 以下先用一段简单的代码理解Hook的过程： function a() { console.log("I'm a."); } a = function b() { console.log("I'm b."); }; a() // I'm b. 直接覆盖原函数是以最简单的做法，以上代码将a函数进行了重写，再次调用a函数将会输出I’m b. 如果还想执行原来a函数的内容，可以使用中间变量进行存储： function a() { console.log("I'm a."); } var c = a; a = function b() { console.log("I'm b."); }; a() // I'm b. c() // I'm a. 此时，调用 a 函数会输出 I’m b.，调用 c 函数会输出 I’m a. 这种原函数直接覆盖的方法通常只用来进行临时调试，实用性不大，但是它能够帮助我们理解 Hook 的过程，在实际 JS 逆向过程中，我们会用到更加高级一点的方法，比如 Object.defineProperty()。 Object.defineProperty() Object.defineProperty(obj, prop, descriptor) obj：需要定义属性的当前对象； prop：当前需要定义的属性名； descriptor：属性描述符，可以取以下值； 属性描述符的取值通常为以下： 属性名 默认值 含义 get undefined 存取描述符，目标属性获取值的方法 set undefined 存取描述符，目标属性设置值的方法 value undefined 数据描述符，设置属性的值 writable false 数据描述符，目标属性的值是否可以被重写 enumerable false 目标属性是否可以被枚举 configurable false 目标属性是否可以被删除或是否可以再次修改特性 通常情况下，对象的定义与赋值是这样的： var people = {} people.name = "Bob" people["age"] = "18" console.log(people) // { name: 'Bob', age: '18' } 使用 defineProperty() 方法： var people = {} Object.defineProperty(people, 'name', { value: 'Bob', writable: true // 是否可以被重写 }) console.log(people.name) // 'Bob' people.name = "Tom" console.log(people.name) // 'Tom' 我们一般hook使用的是get和set方法： var people = { name: 'Bob', }; var count = 18; // 定义一个 age 获取值时返回定义好的变量 count Object.defineProperty(people, 'age', { get: function () { console.log('获取值！'); return count; }, set: function (val) { console.log('设置值！'); count = val + 1; }, }); console.log(people.age); people.age = 20; console.log(people.age); 输出： 获取值！ 18 设置值！ 获取值！ 21 通过这样的方法，我们就可以在设置某个值的时候，添加一些代码，比如 debugger； 让其断下，然后利用调用栈进行调试，找到参数加密、或者参数生成的地方，需要注意的是，网站加载时首先要运行我们的Hook代码，再运行网站自己的代码，才能够成功断下，这个过程我们可以称之为Hook代码的注入。 TamperMonkey 注入 TamperMonkey 俗称油猴插件，是一款免费的浏览器扩展和最为流行的用户脚本管理器，支持很多主流的浏览器， 包括 Chrome、Microsoft Edge、Safari、Opera、Firefox、UC 浏览器、360 浏览器、QQ 浏览器等等，基本上实现了脚本的一次编写，所有平台都能运行，可以说是基于浏览器的应用算是真正的跨平台了。用户可以在 GreasyFork、OpenUserJS 等平台直接获取别人发布的脚本，功能众多且强大，比如视频解析、去广告等。 来到谋奇异首页，可以看到cookie里面有个__dfp值： 我们想通过Hook的方式，让在生成__dfp的地方断下，就可以编写如下函数： 我们以某奇艺的 cookie 为例来演示如何编写 TamperMonkey 脚本，首先去应用商店安装 TamperMonkey，安装过程不再赘述，然后点击图标，添加新脚本，或者点击管理面板，再点击加号新建脚本，写入以下代码： (function () { 'use strict'; var cookieTemp = ''; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.indexOf('__dfp') != -1) { debugger; } console.log('Hook捕获到cookie设置->', val); cookieTemp = val; return val; }, get: function () { return cookieTemp; }, }); })(); if (val.indexOf('__dfp') != -1) {debugger;} 的意思是检索 __dfp 在字符串中首次出现的位置，等于 -1 表示这个字符串值没有出现，反之则出现。如果出现了，那么就 debugger 断下，这里要注意的是不能写成 if (val == '__dfp') {debugger}，因为 val 传过来的值类似于 __dfp=xxxxxxxxxx，这样写是无法断下的。 写入后进行保存 主体的JavaScript自执行函数和前面的都是一样的，这里需要注意的是最前面的注释，每个选项都是有意义的，所有的选项参考 TamperMonkey 官方文档，以下列出了比较常用、比较重要的部分选项（其中需要特别注意 @match、@include、@run-at） 选项 含义 @name 脚本的名称 @namespace 命名空间，用来区分相同名称的脚本，一般写作者名字或者网址就可以 @version 脚本版本，油猴脚本的更新会读取这个版本号 @description 描述这个脚本是干什么用的 @author 编写这个脚本的作者的名字 @match 从字符串的起始位置匹配正则表达式，只有匹配的网址才会执行对应的脚本，例如 * 匹配所有，https://www.baidu.com/* 匹配百度等，可以参考 Python re 模块里面的 re.match() 方法，允许多个实例 @include 和 @match 类似，只有匹配的网址才会执行对应的脚本，但是 @include 不会从字符串起始位置匹配，例如 *://*baidu.com/* 匹配百度，具体区别可以参考 TamperMonkey 官方文档 @icon 脚本的 icon 图标 @grant 指定脚本运行所需权限，如果脚本拥有相应的权限，就可以调用油猴扩展提供的 API 与浏览器进行交互。如果设置为 none 的话，则不使用沙箱环境，脚本会直接运行在网页的环境中，这时候无法使用大部分油猴扩展的 API。如果不指定的话，油猴会默认添加几个最常用的 API @require 如果脚本依赖其他 JS 库的话，可以使用 require 指令导入，在运行脚本之前先加载其它 @run-at 脚本注入时机，该选项是能不能 hook 到的关键，有五个值可选：document-start：网页开始时；document-body：body出现时；document-end：载入时或者之后执行；document-idle：载入完成后执行，默认选项；context-menu：在浏览器上下文菜单中单击该脚本时，一般将其设置为 document-start 清除 cookie，开启 TamperMonkey 插件，再次来到某奇艺首页，可以成功被断下，也可以跟进调用栈来进一步分析 __dfp 值的来源。 第四章 常见的压缩和混淆 在Web系统发展早期，Js在Web系统中承担的职责并不多，Js文件比较简单，也不需要任何的保护。随着Js文件体积的增大和前后端交互增多，为了加快http传输速度并提高接口的安全性，出现了很多的压缩工具和混淆加密工具。 代码混淆的本质是对于代码标识和结构的调整，从而达到不可读不可调用的目的，常用的混淆有字符串、变量名混淆，比如把字符串转换成_0x，把变量重命名等，从结构的混淆包括控制流平坦化，虚假控制流和指令替换，代码加密主要有通过veal方法去执行字符串函数，通过escape()等方法编码字符串、通过转义字符加密代码、自定义加解密方法（RSA、Base64、AES、MD5等），或者通过一些开源的工具进行加密。 另外目前市面上比较常见的混淆还有ob混淆(obfuscator),特征是定义数组，数组位移。不仅Js中的变量名混淆，运行逻辑等也高度混淆，应对这种混淆可以使用已有的工具ob-decrypt或者AST解混淆或者使用第三方提供的反混淆接口。大家平时可以多准备一些工具，在遇到无法识别的Js时，可以直接使用工具来反混淆和解密，当然逆向工作本身就很看运气。 例如翻看网站的Javascript源代码，可以发现很多压缩或者看不太懂的字符，如javascript文件名被编码，文件的内容被压缩成几行，变量被修改成单个字符或者一些十六进制的字符——这些导致我们无法轻易根据Javascript源代码找出某些接口的加密逻辑。 4.1 JavaScript压缩 这个非常简单，Javascript压缩即去除JavaScript代码中不必要的空格、换行等内容或者把一些可能公用的代码进行处理实现共享，最后输出的结果都压缩为几行内容，代码的可读性变得很差，同时也能提高网站的加载速度。 如果仅仅是去除空格、换行这样的压缩方式，其实几乎是没有任何防护作用的，因为这种压缩方式仅仅是降低了代码的直接可读性。因为我们有一些格式化工具可以轻松将JavaScirpt代码变得易读，比如利用IDE、在线工具或Chrome浏览器都能还原格式化的代码。 这里举一个最简单的JavaScript压缩示例。原来的JavaScript代码是这样的： function echo(stringA, stringB){ const name = "Germey"; alert("hello " + name); } 压缩之后就变成这样子： function echo(d,c){const e="Germey";alert("hello "+e)}; 可以看到，这里参数的名称都被简化了，代码中的空格也被去掉了，整个代码也被压缩成了一行，代码的整体可读性降低了。 目前主流的前端开发技术大多都会利用webpack、Rollup等工具进行打包。webpack、Rollup会对源代码进行编译和压缩，输出几个打包好的JavaScript文件，其中我们可以看到输出的JavaScript文件名带有一些不规则的字符串，同时文件内容可能只有几行，变量名都用一些简单字母表示。这其中就包含JavaScript压缩技术，比如一些公共的库输出成bundle文件，一些调用逻辑压缩和转义成冗长的几行代码，这些都属于JavaScript压缩，另外，其中也包含了一些很基础的JavaScript混淆技术，比如把变量名、方法名替换成一些简单的字符，降低代码的可读性。 但整体来说，JavaScript压缩技术只能在很小的程度上起到防护作用，想要真正的提高防护效果，还得依靠JavaScript混淆和加密技术。 4.2 JavaScript混淆 JavaScript混淆完全是在JavaScript上面进行的处理，它的目的就是使得JavaScript变得难以阅读和分析，大大降低代码的可读性，是一种很实用的JavaScript保护方案。 JavaScript混淆技术主要有一下几种。 变量名混淆：将带有含义的变量名、方法名、常量名随机变为无意义的类乱码字符串，降低代码的可读性，如转换成单个字符或十六进制字符串。 字符串混淆：将字符串阵列化集中并可进行MD5或Base64加密存储，使代码中不出现明文字符串，这样可以避免使用全局搜索字符串的方式定位到入口。 对象键名替换：针对JavaScript对象的属性进行加密转化，隐藏代码之间的调用关系。 控制流平坦化：打乱函数原有代码的执行流程及函数调用关系，使代码逻辑变得混乱无序。 无用代码注入：随机在代码中插入不会被执行到的无用代码，进一步使代码看起来更加混乱。 调试保护：基于调试器特征，对当前运行环境进行检查，加入一些debugger语句，使其在调试模式下难以顺利执行JavaScript代码。 多态变异：使JavaScript代码每次被调用时，将代码自身立刻自动发生变异，变为与之前完全不同的代码，即功能完全不变，只是代码形式变异，以此杜绝代码被动态分析和调试。 域名锁定：使JavaScript代码只能在指定域名下执行。 代码自我保护：如果对JavaScript代码进行格式化，则无法执行，导致浏览器假死。 特殊编码：将JavaScript完全编码为人不可读的代码，如表情符号、特殊表示内容、等等。 总之，以上方案都是JavaScript混淆的实现方式，可以在不同程度上保护JavaScript代码。 在前端开发中，现在JavaScript混淆的主流实现使javascript-obfuscator和terser这两个库。它们都能提供一些代码混淆功能，也都有对应的webpack和Rollup打包工具的插件。利用它们，我们可以非常方便地实现页面的混淆，最终输出压缩和混淆后的JavaScript代码，使得JavaScript代码的可读性大大降低。 下面我们以javascript-obfuscator为例来介绍一些代码混淆的实现，了解了实现，那么我们自然就对混淆的机制有了更加深刻的认识。 javascript-obfuscator的官方介绍内容如下： 链接：https://www.javascriptobfuscator.com/ 它是支持ES8的免费、高效的JavaScript混淆库，可以使得JavaScript代码经过混淆后难以被复制、盗用、混淆后的代码具有和原来的代码一模一样的功能。 首先我们需要安装好Node.js 12.x及以上版本，确保可以正常使用npm命令。 具体的安装方式如下： 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。 Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境，基于 Google 的 V8 引擎，V8 引擎执行 Javascript 的速度非常快，性能非常好。 如果你是一个前端程序员，你不懂得像 PHP、Python 或 Ruby 等动态编程语言，然后你想创建自己的服务，那么 Node.js 是一个非常好的选择。 Node.js 是运行在服务端的 JavaScript，如果你熟悉 Javascript，那么你将会很容易的学会 Node.js。 官网：https://nodejs.org/ 文档：https://nodejs.org/en/docs/ 中文网：http://nodejs.cn/ 基础教程：https://www.runoob.com/nodejs/nodejs-tutorial.html 接着新建一个文件夹，比如js-ob然后进入该文件夹，初始化工作空间： npm init 这里会提示我们输入一些信息，然后创建package.json文件，这就完成了项目的初始化了。 接下来，我们来安装javascript-obfuscator这个库： npm i -D javascript-obfuscator 稍等片刻，即可看到本地js-ob文件下生成了一个node_modules文件夹，里面就包含了javascript-obfuscator这个库，这就说明安装成功了。 接下来，我们就可以编写代码来实现一个混淆样例了。比如，新建main.js文件，其内容如下： const code = ` let x = '1' + 1 console.log('x', x) ` const options = { compact: false, } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 这里我们定义了两个变量：一个是code，即需要被混淆的代码；另一个是混淆选项options，是一个Object。接下来，我们引入了javascript-obfuscator这个库，然后定义了一个方法，给其传入code和options来获取混淆之后的代码，最后控制台输出混淆后的代码。 代码逻辑比较简单，我们来执行一下代码： node main.js 输出结果如下： 看到了吧，那么简单的代码，被我们混淆成了这个样子，其实这里我们就是设定了“控制流平坦化”选项。整体看来，代码的可读性大大降低了，JavaScript调试的难度也大大加强了。 4.3 javascript-obfuscator示例 下面的例子代码同上 4.3.1 代码压缩 这里javascript-obfuscator也提供了代码压缩的功能，使用其参数compact即可完成JavaScript代码的压缩，输出为一行内容。参数compact的默认值是true，如果定义为false，则混淆后的代码会分行显示。 如果不设置或者把compact设置为true，结果如下： 可以看到，单行显示的时候，对变量名进行了进一步的混淆，这里变量的命名都变成了十六进制形式的字符串，这是因为启用了一些默认压缩和混淆的方式。总之我们可以看到代码的可读性相比之前大大降低。 4.3.2 变量名混淆 变量名混淆可以通过在javascript-obfuscator中配置identifierNamesGenerator参数来实现。我们通过这个参数可以控制变量名混淆的方式，如将其设置为hexadecimal，则会将变量名替换为十六进制形式的字符串。该参数的取值如下。 hexadecimal：将变量名替换为十六进制形式的字符串，如0xabc123。 mangled：将变量名替换为普通的简写字符，如a，b，c等。 该参数的默认值为：hexadecimal 我们将该参数改为：mangled 来试一下： const code = ` let x = '1' + 1 console.log('x', x) ` const options = { compact: true, identifierNamesGenerator: 'mangled' } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 运行结果如下： 另外，我们还可以通过设置identifiersPrefix参数来控制混淆后的变量前缀，示例如下： const code = ` let x = '1' + 1 console.log('x', x) ` const options = { identifiersPrefix: 'kk', } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 运行结果如下： 可以看到，混淆后的变量前缀加上了我们自定义的字符串kk。 另外，renameGlobals这个参数还可以指定是否混淆全局变量和函数名称，默认值为false。示例如下： const code = ` var $ = function(id){ return document.getElementById(id); }; ` const options = { renameGlobals: true, } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 运行结果如下： 可以看到，这里我们声明了一个全局变量$，在renameGlobals设置为true之后，这个变量也被替换了。如果后文用到了这个变量，可能就会有找不到定义的错误，因此这个参数可能导致代码执行不通。 如果我们不设置 renameGlobals 或者将其设置为false，结果如下： 可以看到，最后还是有$的声明，其全局名称没有被改变。 4.3.3 字符串混淆 字符串混淆，就是将一个字符串声明放到一个数组里面，使之无法被直接搜索到。这可以通过stringArray参数来控制，默认为true。 此外，我们还可以通过rotateStringArray参数来控制数组化后结果的元素顺序，默认为true。 示例如下： const code = ` var a = 'helloworld' ` const options = { stringArray: true, rotateStringArray: true, } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 运行结果如下： 另外，我们还可以使用unicodeEscapeSequence这个参数对字符串进行Unicode转码，使之更加难以辨认，示例如下： const code = ` var a = 'hello world' ` const options = { compact: false, unicodeEscapeSequence: true } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options) 可以看到，这里字符串被数字化和Unicode化，非常难以辨认。 在很多JavaScript逆向过程中，一些关键的字符串可能会作为切入点来查找加密入口，用了这种混淆之后，如果有人想通过全局搜索的方式搜索hello这样的字符串找加密入口，也就没法搜到了。 4.3.4 代码自我保护 我们可以通过设置selfDefending参数来开启代码自我保护功能。开启之后混淆后的JavaScript会强制以一行显示。如果我们将混淆后的代码进行格式化或者重命名，该段代码将无法执行。 示例如下： const code = ` console.log('hello world') ` const options = { selfDefending: true } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 如果我们将上述代码放到控制台，它的执行结果和之前是一模一样的，没有任何问题。 如果我们将其进行格式化，然后粘贴到浏览器控制台里面，浏览器会直接卡死无法运行。这样如果有人对代码进行了格式化，就无法正常对代码进行运行和调试，从而起到了保护作用。 4.3.5 控制流平坦化 控制流平坦化其实就是将代码的执行逻辑混淆，使其变得复杂、难度。其基本的思想是将一些逻辑处理块都统一加上一个前驱逻辑块，每个逻辑块都由前驱逻辑块进行条件判断个分发，构成一个个闭环逻辑，这导致整个执行逻辑十分复杂、难度。 比如说这里有一段示例代码： console.log(c); console.log(a); console.log(b); 代码逻辑一目了然，一次在控制台输出了c, a, b三个变量的值。但是如果把这段代码进行控制流平坦化处理，代码就会变成这样： const code = ` console.log(c); console.log(a); console.log(b); ` const options = { compact: false, controlFlowFlattening: true, } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 使用控制流平坦化可以使得执行逻辑更加复杂、难读，目前非常多的前端混淆都会加上这个选项。但启用控制流平坦化之后，代码的执行时间会变长。 4.3.6 无用代码注入 无用代码即不会被执行的代码或对上下文没有任何影响的代码，注入之后可以对现有的JavaScript代码的阅读形成干扰。我们可以使用deadCodeInjection参数开启这个选项，其默认值为false。 示例： const code = ` console.log(c); console.log(a); console.log(b); ` const options = { compact: false, deadCodeInjection: true } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 这种混淆方式通过混入一些特殊的判断条件并加入一些不会被执行的代码，可以对代码起到一定的干扰作用。 4.3.7 对象键名替换 如果是一个对象，可以使用transformObjectKeys来对对象的键值进行替换，示例如下： const code = ` (function(){ var object = { foo: 'test1', bar: { baz: 'test2' } }; })(); ` const options = { compact: false, transformObjectKeys: true } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } 可以看到，Object的变量名被替换为了特殊的变量，代码的可读性变差，这样我们就不好直接通过变量名进行搜寻了，这也可以起到一定的防护作用。 4.3.8 禁用控制台输出 我们可以使用disableConsoleOutput来禁用掉console.log输出功能，加大调试难度，示例如下： const code = ` console.log('hello world') ` const options = { disableConsoleOutput: true } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 此时，我们如果执行这段代码，发现是没有任何输出的，这里实际上就是将console的一些功能禁用了。 4.3.9 调试保护 我们知道，如果Javascript代码中加入关键字debugger关键字，那么执行到该位置的时候，就会进入断点调试模式。如果在代码多个位置都加入debugger关键字，或者定义某个逻辑来反复执行debugger，就会不断进入断点调试模式，原本的代码就无法顺畅执行了。这个过程可以称为调试保护，即通过反复执行debugger来使得原来的代码无法顺畅执行。 其效果类似于执行了如下代码： setInterval(() => {debugger;}, 3000) 如果我们把这段代码粘贴到控制台，它就会反复执行debugger语句，进入断点调试模式，从而干扰正常的调试流程。 在javascript-obfuscator中，我们可以使用debugProtection来启用调试保护机制，还可以使用debugProtectionInterval来启用无限调试（debug），使得代码在调试过程中不断进入断点模式，无法顺畅执行。配置如下： const options = { debugProtection: true, } 混淆后的代码会跳到debugger代码的位置，使得整个代码无法顺畅执行，对JavaScript代码的调试形成干扰。 4.3.10 域名锁定 我们还可以通过控制domainLock来控制JavaScript代码只能在特定域名下运行，这样就可以降低代码被模拟或者盗用的风险。 示例如下： const code = ` console.log('hello world') ` const options = { domainLock: ['kk.com'] } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options)) 这里我们使用domainLock指定了一个域名kk.com，也就是设置了一个域名白名单，混淆后的代码结果如下： 这段代码就只能在指定的域名kk.com下运行，不能在其他网站运行。这样的话，如果一些相关JavaScript代码被单独剥离出来，想在其他网站运行或者使用程序模拟运行的话，运行结果只有失败，这样就可以有效降低代码被模拟或盗用的风险。 4.3.11特殊编码 另外，还有一些特殊的工具包（jjencode，aaencode等）他们可以对代码进行混淆和编码。 链接：https://www.sojson.com 示例如下： var kk = 100 使用jjencode工具的结果： 使用aaencode工具的结果： 可以看到，通过这些工具，原本非常简单的代码被转化为一些几乎完全不可读的代码，但实际上运行效果还是相同的。这些混淆方式比较另类，看起来虽然没有什么头绪，但实际上找到规律是非常好还原的，并没有真正达到强力混淆的效果。 关于这种混淆代码的解码方法，一般直接复制到控制台运行或者用解码工具进行转换，如果运行失败，就需要按分号分割语句，逐行调试分析源码。 以上便是对JavaScript混淆方式的介绍和总结。总的来说经过混淆的JavaScript代码的可读性大大降低，同时其防护效果也大大增强。 第五章 常见的编码和加密 我们在爬取网站的时候，会遇到一些需要分析接口或URL信息的情况，这时会有各种各样的类似加密的情形。 某个网站的URL带有一些看不太懂的长串加密参数，要抓取就得必须懂得这些参数是怎么构造的，否则我们连完整的URL都构造不出来，更不用说爬取了。 在分析某个网站的Ajax接口时，可以看到接口的一些参数也是加密的，Request Headers 里面也可能带有一些加密参数，如果不知道这些参数的具体构造逻辑，就没法直接用程序来模拟这些Ajax请求。 常见的编码有base64、unicode、urlencode编码，加密有MD5、SHA1、HMAC、DES、RSA等。 本节简单介绍一下常见的编码加密，同时附上Python实现加密的方法。 5.1 base64 base64是一种基于64个可打印ASCLL字符对任意字节数据进行编码的算法，其在编码后具有一定意义的加密作用。在逆向过程中经常会碰到base64编码（不论是Js逆向还是安卓逆向）。 浏览器提供了原生的base64编码、解码方法，方法名就是btoa和atob如下图所示： 在python中使用base64： import base64 print(base64.b64encode('msb'.encode())) print(base64.b64decode('bXNi'.encode()))var str1 = "msb"; unicode和urlencode比较简单，unicode是计算机中字符集、编码的一项业界标准，被称为统一码、万国码，表现形式一般以“\u” 或者 “&#”开头。urlencode是URL编码，也称作百分号编码用于把URL中的符号进行转换。 5.2 MD5 MD5消息摘要算法（英文：MD5 Message-Digest Algorithm），一种被广泛使用的密码散列函数，可以产出一个128位（16字节）的散列值（hash value），用于确保信息传输完整一致。MD5加密算法是不可逆的，所以解密一般都是通过暴力穷举方法，以及网站的接口实现解密。 python代码实现加密： import hashlib pwd = "123" # 生成MD5对象 m = hashlib.md5() # 对数据进行加密 m.update(pwd.encode('utf-8')) # 获取密文 pwd = m.hexdigest() print(pwd) 解密工具：https://www.cmd5.com/ 5.3 SHA1 SHA1（Secure Hash Algorithm）安全哈希算法主要适用于数字签名标准里面定义的数字签名算法，SHA1比MD5的安全性更强。对于长度小于2^64位的消息，SHA1会产生一个160位的消息摘要。 一般在没有高度混淆的Js代码中，SHA1加密的关键词就是sha1。 Python实现代码： import hashlib sha1 = hashlib.sha1() data1 = "msb" data2 = "kkk" sha1.update(data1.encode("utf-8")) sha1_data1 = sha1.hexdigest() print(sha1_data1) sha1.update(data2.encode("utf-8")) sha1_data2 = sha1.hexdigest() print(sha1_data2) 运行结果： 解密工具：http://tool.geekapp.cn/index.php 5.4 HMAC HMAC全称：散列消息鉴别码。HMAC加密算法是一种安全的基于加密hash函数和共享密钥的消息认证协议。实现原理是用公开函数和密钥产生一个固定长度的值作为认证标识，用这个标识鉴别消息的完整性。 python实现代码： new（key,msg=None,digestmod）方法 创建哈希对象 key和digestmod参数必须指定，key和msg（需要加密的内容）均为bytes类型，digestmod指定加密算法，比如‘md5’,’sha1’等 对象digest（）方法：返回bytes类型哈希值 对象hexdigest（）方法：返回十六进制哈希值 import hmac import hashlib key = "key".encode() text = "msb".encode() m = hmac.new(key, text, hashlib.sha256) print(m.digest()) print(m.hexdigest()) 5.5 DES DES全称：数据加密标准（Data Encryption Standard），属于对称加密算法。DES是一个分组加密算法，典型的DES以64位为分组对数据加密，加密和解密用的是同一个算法。它的密钥长度是56位（因为每个第8位都用作奇偶校验），密钥可以是任意的56位数，而且可以任意时候改变。 Js逆向时，DES加密的搜索关键词有DES、mode、padding等。 python实现代码： # pyDes需要安装 from pyDes import des, CBC, PAD_PKCS5 import binascii # 秘钥 KEY = 'dsj2020q' def des_encrypt(s): """ DES 加密 :param s: 原始字符串 :return: 加密后字符串，16进制 """ secret_key = KEY iv = secret_key k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5) en = k.encrypt(s, padmode=PAD_PKCS5) return binascii.b2a_hex(en).decode() def des_decrypt(s): """ DES 解密 :param s: 加密后的字符串，16进制 :return: 解密后的字符串 """ secret_key = KEY iv = secret_key k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5) de = k.decrypt(binascii.a2b_hex(s), padmode=PAD_PKCS5) return de.decode() text = 'msb' secret_str = des_encrypt(text) print(secret_str) clear_str = des_decrypt(secret_str) print(clear_str) 5.5 AES AES全程：高级加密标准，在密码学中又称Rijndael加密法，是美国联邦政府采用的一种区块加密标准。 AES也是对称加密算法，如果能够获取到密钥，那么就能对密文解密。 Js逆向时，AES加密的搜索关键词有AES、mode、padding等。 python代码实现之前 pip install pycryptodome python实现代码： import base64 from Crypto.Cipher import AES # AES # 需要补位，str不是16的倍数那就补足为16的倍数 def add_to_16(value): while len(value) % 16 != 0: value += '\0' return str.encode(value) # 返回bytes # 加密方法 def encrypt(key, text): aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器 encrypt_aes = aes.encrypt(add_to_16(text)) # 先进行aes加密 encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') return encrypted_text # 解密方法 def decrypt(key, text): aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器 base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8')) decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '') # 执行解密密并转码返回str return decrypted_text 5.6 RSA RSA全称：Rivest-Shamir-Adleman, RSA加密算法是一种非对称加密算法，在公开密钥加密和电子商业中RSA被广泛使用，它被普遍认为是目前最优秀的公钥方案之一。RSA是第一个能同时用于加密和数字签名的算法，它能够抵抗目前为止已知的所有密码攻击。 注意Js代码中的RSA常见标志setPublickey。 算法原理参考：https://www.yht7.com/news/184380 实现代码之前先安装 ： pip install pycryptodome python代码实现： import base64 from Crypto.Cipher import PKCS1_v1_5 from Crypto import Random from Crypto.PublicKey import RSA # ------------------------生成密钥对------------------------ def create_rsa_pair(is_save=False): """ 创建rsa公钥私钥对 :param is_save: default:False :return: public_key, private_key """ f = RSA.generate(2048) private_key = f.exportKey("PEM") # 生成私钥 public_key = f.publickey().exportKey() # 生成公钥 if is_save: with open("crypto_private_key.pem", "wb") as f: f.write(private_key) with open("crypto_public_key.pem", "wb") as f: f.write(public_key) return public_key, private_key def read_public_key(file_path="crypto_public_key.pem") -> bytes: with open(file_path, "rb") as x: b = x.read() return b def read_private_key(file_path="crypto_private_key.pem") -> bytes: with open(file_path, "rb") as x: b = x.read() return b # ------------------------加密------------------------ def encryption(text: str, public_key: bytes): # 字符串指定编码（转为bytes） text = text.encode("utf-8") # 构建公钥对象 cipher_public = PKCS1_v1_5.new(RSA.importKey(public_key)) # 加密（bytes） text_encrypted = cipher_public.encrypt(text) # base64编码，并转为字符串 text_encrypted_base64 = base64.b64encode(text_encrypted).decode() return text_encrypted_base64 # ------------------------解密------------------------ def decryption(text_encrypted_base64: str, private_key: bytes): # 字符串指定编码（转为bytes） text_encrypted_base64 = text_encrypted_base64.encode("utf-8") # base64解码 text_encrypted = base64.b64decode(text_encrypted_base64) # 构建私钥对象 cipher_private = PKCS1_v1_5.new(RSA.importKey(private_key)) # 解密（bytes） text_decrypted = cipher_private.decrypt(text_encrypted, Random.new().read) # 解码为字符串 text_decrypted = text_decrypted.decode() return text_decrypted if __name__ == "__main__": # 生成密钥对 # create_rsa_pair(is_save=True) # public_key = read_public_key() # private_key = read_private_key() public_key, private_key = create_rsa_pair(is_save=False) # 加密 text = "msb" text_encrypted_base64 = encryption(text, public_key) print("密文：", text_encrypted_base64) # 解密 text_decrypted = decryption(text_encrypted_base64, private_key) print("明文：", text_decrypted) 第六章 加密参数还原与模拟 加密参数还原的逻辑很简单，找到代码中加密参数的生成过程，然后模拟出相同的方法。很多时候会卡到加密参数定位上，然后遇到混淆加密过的复杂Js，导致还原的过程无比艰辛。本章我们准备了由易到难的逆向案例，带大家体验精彩的逆向过程。 6.1 Newrank榜单逆向案例 本节内容是分析新榜榜单接口的加密参数，网页上的微信榜、微博榜、抖音榜、快手榜、bilibili榜、资讯等都使用了相同的参数。 网站链接：https://www.newrank.cn/public/info/list.html 首先通过控制台抓包，以微信榜单的文化日榜为例查看接口，如图所示： 已知post请求，再看From Data，发现两个加密参数nonce和xyz，如图所示： 通过Ctrl+F快捷键全局搜索nonce和xyz关键字，如图所示： 可以在控制台输入j查看该方法的内容： 可用鼠标双击函数跳转查看： 回到刚才的位置打上断点，刷新一下往下走一步： 可以发现h是当前的Api加一些params。 接着通过d（h）点进去查看，如图所示： 到这里就结束了，b中的具体运算这里不做深究，整体流程分析完毕，此时将b（a）复制出来即可。 Js模拟示例： j = function() { for (var a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"], b = 0; b < 500; b++) for (var c = "", d = 0; d < 9; d++) { var e = Math.floor(16 * Math.random()); c += a[e] } return c } var nonce = j(); var a = "/xdnphb/common/account/getFull?AppKey=joker&nonce=" + nonce; function b(a) { function b(a) { return d(c(e(a))) } function c(a) { return g(h(f(a), 8 * a.length)) } function d(a) { for (var b, c = p ? "0123456789ABCDEF" : "0123456789abcdef", d = "", e = 0; e < a.length; e++) b = a.charCodeAt(e), d += c.charAt(b >>> 4 & 15) + c.charAt(15 & b); return d } function e(a) { for (var b, c, d = "", e = -1; ++e < a.length; ) b = a.charCodeAt(e), c = e + 1 < a.length ? a.charCodeAt(e + 1) : 0, 55296 <= b && b <= 56319 && 56320 <= c && c <= 57343 && (b = 65536 + ((1023 & b) << 10) + (1023 & c), e++), b <= 127 ? d += String.fromCharCode(b) : b <= 2047 ? d += String.fromCharCode(192 | b >>> 6 & 31, 128 | 63 & b) : b <= 65535 ? d += String.fromCharCode(224 | b >>> 12 & 15, 128 | b >>> 6 & 63, 128 | 63 & b) : b <= 2097151 && (d += String.fromCharCode(240 | b >>> 18 & 7, 128 | b >>> 12 & 63, 128 | b >>> 6 & 63, 128 | 63 & b)); return d } function f(a) { for (var b = Array(a.length >> 2), c = 0; c < b.length; c++) b[c] = 0; for (var c = 0; c < 8 * a.length; c += 8) b[c >> 5] |= (255 & a.charCodeAt(c / 8)) << c % 32; return b } function g(a) { for (var b = "", c = 0; c < 32 * a.length; c += 8) b += String.fromCharCode(a[c >> 5] >>> c % 32 & 255); return b } function h(a, b) { a[b >> 5] |= 128 << b % 32, a[14 + (b + 64 >>> 9 << 4)] = b; for (var c = 1732584193, d = -271733879, e = -1732584194, f = 271733878, g = 0; g < a.length; g += 16) { var h = c , i = d , o = e , p = f; c = j(c, d, e, f, a[g + 0], 7, -680876936), f = j(f, c, d, e, a[g + 1], 12, -389564586), e = j(e, f, c, d, a[g + 2], 17, 606105819), d = j(d, e, f, c, a[g + 3], 22, -1044525330), c = j(c, d, e, f, a[g + 4], 7, -176418897), f = j(f, c, d, e, a[g + 5], 12, 1200080426), e = j(e, f, c, d, a[g + 6], 17, -1473231341), d = j(d, e, f, c, a[g + 7], 22, -45705983), c = j(c, d, e, f, a[g + 8], 7, 1770035416), f = j(f, c, d, e, a[g + 9], 12, -1958414417), e = j(e, f, c, d, a[g + 10], 17, -42063), d = j(d, e, f, c, a[g + 11], 22, -1990404162), c = j(c, d, e, f, a[g + 12], 7, 1804603682), f = j(f, c, d, e, a[g + 13], 12, -40341101), e = j(e, f, c, d, a[g + 14], 17, -1502002290), d = j(d, e, f, c, a[g + 15], 22, 1236535329), c = k(c, d, e, f, a[g + 1], 5, -165796510), f = k(f, c, d, e, a[g + 6], 9, -1069501632), e = k(e, f, c, d, a[g + 11], 14, 643717713), d = k(d, e, f, c, a[g + 0], 20, -373897302), c = k(c, d, e, f, a[g + 5], 5, -701558691), f = k(f, c, d, e, a[g + 10], 9, 38016083), e = k(e, f, c, d, a[g + 15], 14, -660478335), d = k(d, e, f, c, a[g + 4], 20, -405537848), c = k(c, d, e, f, a[g + 9], 5, 568446438), f = k(f, c, d, e, a[g + 14], 9, -1019803690), e = k(e, f, c, d, a[g + 3], 14, -187363961), d = k(d, e, f, c, a[g + 8], 20, 1163531501), c = k(c, d, e, f, a[g + 13], 5, -1444681467), f = k(f, c, d, e, a[g + 2], 9, -51403784), e = k(e, f, c, d, a[g + 7], 14, 1735328473), d = k(d, e, f, c, a[g + 12], 20, -1926607734), c = l(c, d, e, f, a[g + 5], 4, -378558), f = l(f, c, d, e, a[g + 8], 11, -2022574463), e = l(e, f, c, d, a[g + 11], 16, 1839030562), d = l(d, e, f, c, a[g + 14], 23, -35309556), c = l(c, d, e, f, a[g + 1], 4, -1530992060), f = l(f, c, d, e, a[g + 4], 11, 1272893353), e = l(e, f, c, d, a[g + 7], 16, -155497632), d = l(d, e, f, c, a[g + 10], 23, -1094730640), c = l(c, d, e, f, a[g + 13], 4, 681279174), f = l(f, c, d, e, a[g + 0], 11, -358537222), e = l(e, f, c, d, a[g + 3], 16, -722521979), d = l(d, e, f, c, a[g + 6], 23, 76029189), c = l(c, d, e, f, a[g + 9], 4, -640364487), f = l(f, c, d, e, a[g + 12], 11, -421815835), e = l(e, f, c, d, a[g + 15], 16, 530742520), d = l(d, e, f, c, a[g + 2], 23, -995338651), c = m(c, d, e, f, a[g + 0], 6, -198630844), f = m(f, c, d, e, a[g + 7], 10, 1126891415), e = m(e, f, c, d, a[g + 14], 15, -1416354905), d = m(d, e, f, c, a[g + 5], 21, -57434055), c = m(c, d, e, f, a[g + 12], 6, 1700485571), f = m(f, c, d, e, a[g + 3], 10, -1894986606), e = m(e, f, c, d, a[g + 10], 15, -1051523), d = m(d, e, f, c, a[g + 1], 21, -2054922799), c = m(c, d, e, f, a[g + 8], 6, 1873313359), f = m(f, c, d, e, a[g + 15], 10, -30611744), e = m(e, f, c, d, a[g + 6], 15, -1560198380), d = m(d, e, f, c, a[g + 13], 21, 1309151649), c = m(c, d, e, f, a[g + 4], 6, -145523070), f = m(f, c, d, e, a[g + 11], 10, -1120210379), e = m(e, f, c, d, a[g + 2], 15, 718787259), d = m(d, e, f, c, a[g + 9], 21, -343485551), c = n(c, h), d = n(d, i), e = n(e, o), f = n(f, p) } return Array(c, d, e, f) } function i(a, b, c, d, e, f) { return n(o(n(n(b, a), n(d, f)), e), c) } function j(a, b, c, d, e, f, g) { return i(b & c | ~b & d, a, b, e, f, g) } function k(a, b, c, d, e, f, g) { return i(b & d | c & ~d, a, b, e, f, g) } function l(a, b, c, d, e, f, g) { return i(b ^ c ^ d, a, b, e, f, g) } function m(a, b, c, d, e, f, g) { return i(c ^ (b | ~d), a, b, e, f, g) } function n(a, b) { var c = (65535 & a) + (65535 & b); return (a >> 16) + (b >> 16) + (c >> 16) << 16 | 65535 & c } function o(a, b) { return a << b | a >>> 32 - b } var p = 0; return b(a) } console.log(b(a)); 经过调试发现，每个接口对应的a(“/xdnphd/******”)是不同的，所以要根据不同接口进行修改。 6.3 MD5加密逆向案例 本节内容是对MD5加密的逆向案例，主要内容是对POST表单的加密分析。 目标网站链接：https://fanyi.youdao.com/ 在输入框输入”你好”会自动翻译成我们的目标内容”hello” 通过开发者工具查看该网站链接发送的请求头内容，发现POST表单数据： 参数i是我们手动输入的内容；salt，sign，lts，bv是我们需要获取的。 可以在一个一个js请求里面搜索sign，找到我们需要的关键字sign。进行断点： 可以看出来n.md5就是我们要解密的方法了，点击进入该方法查看详细内容： 我们找到加密参数，就可以编写python代码了。 import execjs # 安装一下PyExeJS with open('test.js', 'r') as f: ctx = execjs.compile(f.read()) # 获取代码编译完成后的对象 result = ctx.call('get', '你好') # 调用函数get，传入它的参数 print(result) 将我们找到的js代码保存在”test.js”文件中，替换变量t的值。 function get(e) { var t = 'db3e6fc05e1796cb0030cda74ecb0567' , r = "" + (new Date).getTime() , i = r + parseInt(10 * Math.random(), 10); return { ts: r, bv: t, salt: i, sign: md5("fanyideskweb" + e + i + "Ygy_4c=r#e#4EX^NUGUc5") } }; 在程序运行过程中，发现代码会报错显示参数未定义，例如：execjs._exceptions.ProgramError: ReferenceError: md5 is not defined，所以接下来核心方法就是「缺啥补啥」。其中md5方法我们已获取，直接补充到js文件中，然后根据代码报错的提示不断填补参数代码，直到程序正常运行。 p = function(e) { var t, n = "", r = ""; for (t = 0; t <= 3; t++) n += (r = "0" + (e >>> 8 * t & 255).toString(16)).substr(r.length - 2, 2); return n } s = function(e, t, n) { return t ^ (e | ~n) } d = function(e, t, i, o, a, l, c) { return e = r(e, r(r(s(t, i, o), a), c)), r(n(e, l), t) } a = function(e, t, n) { return e ^ t ^ n } u = function(e, t, i, o, s, l, c) { return e = r(e, r(r(a(t, i, o), s), c)), r(n(e, l), t) } o = function(e, t, n) { return e & n | t & ~n } c = function(e, t, i, a, s, l, c) { return e = r(e, r(r(o(t, i, a), s), c)), r(n(e, l), t) } n = function(e, t) { return e << t | e >>> 32 - t } i = function(e, t, n) { return e & t | ~e & n } r = function(e, t) { var n, r, i, o, a; return i = 2147483648 & e, o = 2147483648 & t, n = 1073741824 & e, r = 1073741824 & t, a = (1073741823 & e) + (1073741823 & t), n & r ? 2147483648 ^ a ^ i ^ o : n | r ? 1073741824 & a ? 3221225472 ^ a ^ i ^ o : 1073741824 ^ a ^ i ^ o : a ^ i ^ o } l = function(e, t, o, a, s, l, c) { return e = r(e, r(r(i(t, o, a), s), c)), r(n(e, l), t) } f = function(e) { for (var t, n = e.length, r = n + 8, i = 16 * ((r - r % 64) / 64 + 1), o = Array(i - 1), a = 0, s = 0; s < n; ) a = s % 4 * 8, o[t = (s - s % 4) / 4] = o[t] | e.charCodeAt(s) << a, s++; return t = (s - s % 4) / 4, a = s % 4 * 8, o[t] = o[t] | 128 << a, o[i - 2] = n << 3, o[i - 1] = n >>> 29, o } h = function(e) { e = e.replace(/\x0d\x0a/g, "\n"); for (var t = "", n = 0; n < e.length; n++) { var r = e.charCodeAt(n); if (r < 128) t += String.fromCharCode(r); else if (r > 127 && r < 2048) t += String.fromCharCode(r >> 6 | 192), t += String.fromCharCode(63 & r | 128); else if (r >= 55296 && r <= 56319) { if (n + 1 < e.length) { var i = e.charCodeAt(n + 1); if (i >= 56320 && i <= 57343) { var o = 1024 * (r - 55296) + (i - 56320) + 65536; t += String.fromCharCode(240 | o >> 18 & 7), t += String.fromCharCode(128 | o >> 12 & 63), t += String.fromCharCode(128 | o >> 6 & 63), t += String.fromCharCode(128 | 63 & o), n++ } } } else t += String.fromCharCode(r >> 12 | 224), t += String.fromCharCode(r >> 6 & 63 | 128), t += String.fromCharCode(63 & r | 128) } return t } function md5(e) { var t, n, i, o, a, s, m, g, v, y = Array(); for (e = h(e), y = f(e), s = 1732584193, m = 4023233417, g = 2562383102, v = 271733878, t = 0; t < y.length; t += 16) n = s, i = m, o = g, a = v, s = l(s, m, g, v, y[t + 0], 7, 3614090360), v = l(v, s, m, g, y[t + 1], 12, 3905402710), g = l(g, v, s, m, y[t + 2], 17, 606105819), m = l(m, g, v, s, y[t + 3], 22, 3250441966), s = l(s, m, g, v, y[t + 4], 7, 4118548399), v = l(v, s, m, g, y[t + 5], 12, 1200080426), g = l(g, v, s, m, y[t + 6], 17, 2821735955), m = l(m, g, v, s, y[t + 7], 22, 4249261313), s = l(s, m, g, v, y[t + 8], 7, 1770035416), v = l(v, s, m, g, y[t + 9], 12, 2336552879), g = l(g, v, s, m, y[t + 10], 17, 4294925233), m = l(m, g, v, s, y[t + 11], 22, 2304563134), s = l(s, m, g, v, y[t + 12], 7, 1804603682), v = l(v, s, m, g, y[t + 13], 12, 4254626195), g = l(g, v, s, m, y[t + 14], 17, 2792965006), m = l(m, g, v, s, y[t + 15], 22, 1236535329), s = c(s, m, g, v, y[t + 1], 5, 4129170786), v = c(v, s, m, g, y[t + 6], 9, 3225465664), g = c(g, v, s, m, y[t + 11], 14, 643717713), m = c(m, g, v, s, y[t + 0], 20, 3921069994), s = c(s, m, g, v, y[t + 5], 5, 3593408605), v = c(v, s, m, g, y[t + 10], 9, 38016083), g = c(g, v, s, m, y[t + 15], 14, 3634488961), m = c(m, g, v, s, y[t + 4], 20, 3889429448), s = c(s, m, g, v, y[t + 9], 5, 568446438), v = c(v, s, m, g, y[t + 14], 9, 3275163606), g = c(g, v, s, m, y[t + 3], 14, 4107603335), m = c(m, g, v, s, y[t + 8], 20, 1163531501), s = c(s, m, g, v, y[t + 13], 5, 2850285829), v = c(v, s, m, g, y[t + 2], 9, 4243563512), g = c(g, v, s, m, y[t + 7], 14, 1735328473), m = c(m, g, v, s, y[t + 12], 20, 2368359562), s = u(s, m, g, v, y[t + 5], 4, 4294588738), v = u(v, s, m, g, y[t + 8], 11, 2272392833), g = u(g, v, s, m, y[t + 11], 16, 1839030562), m = u(m, g, v, s, y[t + 14], 23, 4259657740), s = u(s, m, g, v, y[t + 1], 4, 2763975236), v = u(v, s, m, g, y[t + 4], 11, 1272893353), g = u(g, v, s, m, y[t + 7], 16, 4139469664), m = u(m, g, v, s, y[t + 10], 23, 3200236656), s = u(s, m, g, v, y[t + 13], 4, 681279174), v = u(v, s, m, g, y[t + 0], 11, 3936430074), g = u(g, v, s, m, y[t + 3], 16, 3572445317), m = u(m, g, v, s, y[t + 6], 23, 76029189), s = u(s, m, g, v, y[t + 9], 4, 3654602809), v = u(v, s, m, g, y[t + 12], 11, 3873151461), g = u(g, v, s, m, y[t + 15], 16, 530742520), m = u(m, g, v, s, y[t + 2], 23, 3299628645), s = d(s, m, g, v, y[t + 0], 6, 4096336452), v = d(v, s, m, g, y[t + 7], 10, 1126891415), g = d(g, v, s, m, y[t + 14], 15, 2878612391), m = d(m, g, v, s, y[t + 5], 21, 4237533241), s = d(s, m, g, v, y[t + 12], 6, 1700485571), v = d(v, s, m, g, y[t + 3], 10, 2399980690), g = d(g, v, s, m, y[t + 10], 15, 4293915773), m = d(m, g, v, s, y[t + 1], 21, 2240044497), s = d(s, m, g, v, y[t + 8], 6, 1873313359), v = d(v, s, m, g, y[t + 15], 10, 4264355552), g = d(g, v, s, m, y[t + 6], 15, 2734768916), m = d(m, g, v, s, y[t + 13], 21, 1309151649), s = d(s, m, g, v, y[t + 4], 6, 4149444226), v = d(v, s, m, g, y[t + 11], 10, 3174756917), g = d(g, v, s, m, y[t + 2], 15, 718787259), m = d(m, g, v, s, y[t + 9], 21, 3951481745), s = r(s, n), m = r(m, i), g = r(g, o), v = r(v, a); return (p(s) + p(m) + p(g) + p(v)).toLowerCase() } function get(e) { var t = 'db3e6fc05e1796cb0030cda74ecb0567' , r = "" + (new Date).getTime() , i = r + parseInt(10 * Math.random(), 10); return { ts: r, bv: t, salt: i, sign: md5("fanyideskweb" + e + i + "Ygy_4c=r#e#4EX^NUGUc5") } } 运行python代码： 6.4 RSA参数加密逆向案例 本节是对RSA加密的逆向案例，主要内容是对登录参数的RSA加密分析。 网站链接：https://login.10086.cn/html/login/email_login.html 进入之后选择电脑版： 去到登陆界面： 登录之前我们打开控制台，然后登陆进行抓包。 输入用户名和密码之后点击登录，会抓到一个login的包。 它是一个post的请求，我们观察一下Form Data里面的数据： 我们发现用户和密码都进行了加密，但是不确定是哪种加密方式。 此时我们可以全局搜索一下password关键字，发现能搜索到很多相关的信息： 如果一个一个的找会很麻烦，这里告诉大家一个技巧，我们可以搜索encrypt这个关键字（它其实就是表示加密的操作） 这时我们就能找到加密的地方，我们在加密的位置打上断点，再重新登录，看能不能拦截到。 拦截之前先进入资源面板打上断点。 登录的时候可以被拦截，点进去看看它的方法： 发现有明显的RSA标志setPublicKey，接下来就用代码进行模拟。 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 import base64 def encrypt_str(data): key = "[binary]" rsakey = RSA.import_key(base64.b64decode(key)) cipher = PKCS1_v1_5.new(rsakey) cipher_text = base64.b64encode(cipher.encrypt(data.encode(encoding="utf-8"))) return cipher_text password = encrypt_str("123") print(password.decode()) Crypto是第三方库，属于对PyCrypto库的扩展，所以需要进行安装，Windows安装命令pip install pycryptodome。 6.5 AES数据加密逆向案例 前面小节中的案例是对接口中的参数进行逆向还原，本节内容来看看针对行行查网站响应内容的加密逆向案例。 网站链接：https://www.hanghangcha.com/ 这里我们需要先登录，然后尝试点击产业概述： 通过控制台可以发现，当我们点击右边菜单栏的时候，该网站返回的response都是经过加密的，如图所示： 首先通过堆栈调试进行断点，在调用中看到了一个名为feachData的堆栈，（因为名字里面有Data），猜测就是我们要找的部分： 尝试点进去看看： 这时会发现有decrypt和JSON.parse，在这里进行断点。 尝试刷新页面触发断点，不难发现V[“a”].decrypt(e)就是解密。 进入该方法查看详细内容： 这就不难看出是AES加密了，继续断点看一下： 可以发现密钥是 “3sd&d24h@$udD2s*”，mode是ECB，padding是Pkcs7。解密算法分析完成，接下来就可以编写还原代码了。 import requests import base64, json, re from Crypto.Cipher import AES def decrypt(info): key = '3sd&d24h@$udD2s*'.encode(encoding='utf-8') cipher = AES.new(key, mode=AES.MODE_ECB) json_str = str(cipher.decrypt(base64.b64decode(info)), encoding='utf-8') data = re.sub('[\x00-\x09|\x0b-\x0c|\x0e-\x1f]', '', json_str) return json.loads(data) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36', 'Cookie': 'UM_distinctid=18354eaabe89d7-0970db4b1f0d01-26021c51-144000-18354eaabe9158a; Hm_lvt_1521e0fb49013136e79181f2888214a7=1663577010; Hm_lpvt_1521e0fb49013136e79181f2888214a7=1663577010; JSESSIONID=BE4CBF8DDEF824CAAA58E75F33E7DCCF; _ACCOUNT_=YjBiZjRkMTA3YmM1NDY3N2E4NGJkNTljMDRlNGRiNWUlNDAlNDBtb2JpbGU6MTY2NDc5NjI4NDc4NDoyM2FkNDY2ZDYxOWY2ZmFmOTg4YzQzMTYxOWMzMDljNQ' }# 需要把你的header复制进去 url = "https://api.hanghangcha.com/hhc/api/member/chains/getList?filter=%7B%22keyword%22%3A%22%22%2C%22limit%22%3A20%2C%22skip%22%3A0%2C%22isOpen%22%3A1%7D" # 产业图谱接口 res = requests.get(url, headers=headers) payload = json.loads(res.content)['data'] data = decrypt(payload) print(data) 运行代码得到解密后的结果： 第七章 浏览器环境补充 现在很多网站的Js都引入了浏览器的特征，就是检测浏览器环境和浏览器使用的一些方法。比如用Node直接去运行复制下来的Js代码，可能会报未定义的错误或者找不到方法，导致无法运行或者得到的结果与浏览器不一致。因为Node环境和浏览器具有一定的区别，比如window对象区别，this指向区别、Js引擎区别，以及一些DOM操作的区别，很多网站也会以此检测来判断是不是真实用户，此时就需要对Js代码进行补充。 又比如对浏览器一些参数的设置，网站设置了window、navigate的某一个属性，在模拟的时候没有进行这些设置，就会被检测到。 通常情况下简单的补充只需要补上window或者document以及定义一些变量，比如直接在开头定义一个window=global；或者根据报错和调试结果缺啥补啥。但是不同网站的检测标准和补充难度参差不齐，先来看看哪些环境是经常被检测的。 7.1 常被检测的环境 在常被检测的环境中，有window、location、navigate、document、native、canvas等。除了这些属性外，还有针对自动化的检测、Node环境的检测，以及浏览器指纹检测、TLS指纹校验等。下面列出了一些经常用来做检测的属性和方法。 window检测： window是否为方法 window对象是否freeze 各属性检测 location检测： hostname protocol host hash origin navigator检测： AppName AppVersion cookieEnabled language userAgent product platform plugins浏览器插件 javaEnabled()方法 taintEnabled()方法 document检测： referrer cookie createElement()方法 canvas指纹： 不同类型图片的canvas指纹应当不一样，如 .jpg .png 不同质量quality的canvas指纹应该不一样 不同属性的canvas指纹应该不一样 同一个条件的canvas多次绘制时应该保持一致 浏览器指纹信息： window.screen屏幕分辨率/宽高 navigator.useragent location.href/host navigator.platform平台、语言等信息 cancas2D图像指纹 navigator.plugin浏览器插件信息 webgl3D图像指纹 浏览器字体信息 本地存储的cookie信息 除了上面列出的之外，还有对自动化痕迹的检测，比如chromedriver属性检测。还有异常堆栈检测，通过检测堆栈来判断所处的执行环境。native方法检测，检测某个方法是否被重写。这里不再多说了，在接下来的小节中来了解一下补充环境时的几种方案。 补充：浏览器指纹很重要，在数据采集、搜索引擎、埋点分析、网站测试等方面都有体现。指纹通常是指服务端的为了做识别而收集的客户端设备信息。即使没有cookie，指纹也可用于识别个人用户或设备。比如常用于记录的指纹 Header、Cookie、IP、DNS、UserAgent，Font（字体列表），Language，localStorage、Plugin（插件），Canvas（渲染绘图），WebGL（3D渲染图形），Web Vendor，Timezone（时区），WebRTC，ScreenResolution（分辨率），Platform（系统），Audio（音频设置和硬件特征指纹），以及enumerateDevices（其他媒体设备）、CPU、GPU信息等等。 7.2 手动补充环境 一般在手动补充时，根据报错信息缺什么补什么。比如’window’ is not defined，就补上window=global；或者widow={}。 如果报错没有plugins，可以在navigator中写上plugins。同时用Symbol.toStringTag标识一下该对象的类型标签，因为目前有很多toString检测，而toStringTag也能被toString()方法识别并返回。 var navigator = { plugins:{ 0:{ 0:{description:"", type:""}, name:"", length:1, filename:"", description:"", length:1 } } }; navigator.plugins[Symbol.toStringTag] = "PluginArry"; navigator.plugins[0][Symbol.toStringTag] = "Plugin"; 如果报错没有getElementByTagName，就到document中去定义一个，但是参数和方法中具体实现，以及返回内容都需要根据调试结果来进行补充。 document = { getElementsByTagName:function(x){ return{} }, createElement:function(x){ return{} } } 如果报错没有canvas，那么可以用createElement去创建一个简单的元素节点，通过此方法可返回一个canvas对象。 var document = { createElement:function createElement(x){ if (x=="canvas"){ return{ toDataURL:function toDataURL(){ return "data:image/png" } } } } } 7.3 JSDOM环境补充 js中的DOM指的是“Document Object Model”，也就是文档对象模型的意思，是HTML和XML文档的编程接口；它提供了对文档的结构化的表述，并定义了一种方式可以使从程序中对该结构进行访问，从而改变文档的结构，样式和内容。 JSDOM对浏览器环境的还原非常到位，它可以被当作无头浏览器使用。一般来说，在node环境下需要补充最多的就是document和window对象。通过下面的代码来看JSDOM是如何模拟成浏览器的。 通过HTML文本来创建一个JSDOM的实例，然后就可以通过实例取得window对象和document对象。JSDOM生成的window对象下还实现了history、location、postMessage、setTimeout、setInterval等熟悉的Api。 JSDOM安装：npm install jsdom 查看jsdom的位置：npm root -g。 const { JSDOM } = require ('jsdom'); const NewjsDom = new JSDOM( ` Title web js逆向 ` ); const window = NewjsDom.window; //window对象 const document = window.document; //document对象 console.log('123'); 另外要提的一点是Python中的execjs库也可以切换成jsdom来执行含有document、window等对象的Js代码。 代码示例： signature = execjs.compile(js, cwd=jsdom_path).call("sign") JSDOM能满足大部分测试场景下对浏览器环境的还原，但在一些场景下也会被检测出来。某些时候并不知道网站检测了什么属性，也不知道修补环境时，可以使用真实的浏览器驱动来加载Js代码。比如像抖音web版、头条新闻页面，这两个站点的Js代码都具备深度检测浏览器身份的功能。 7.4 Selenium环境模拟 我们喜欢用的方法是使用selenium来驱动浏览器，先把Js和参数都写到本地的html文件，然后用selenium打开html文件加载Js，生成加密参数。当然也可以使用其他的Web自动化测试工具，比如Pyppeteer、htmlunit等。 具体方法如下： HTML文件： web js逆向 我们在学习js逆向 python文件： 使用selenium需要安装selenium库。 还得安装浏览器驱动，这里提供chrome的驱动： 下载地址：http://chromedriver.storage.googleapis.com/index.html 注意需下载和自己本地谷歌浏览器版本一致的驱动。 import os from selenium import webdriver import time PRO_DIR = os.path.dirname(os.path.abspath(__file__)) def driver_sig(html_file): driver = webdriver.Chrome() # 请求当前目录下的'get_sign.html'文件 driver.get("file://" + PRO_DIR + "/" + html_file) time.sleep(5) sig = driver.title driver.quit() return sig if __name__ == '__main__': html_file = 'get_sign.html' sig = driver_sig(html_file) print(sig) 7.5 puppeteer环境模拟 puppeteer是一个Node.js的库，支持调用Chrome的Api来操纵Web，相比较Selenium或是PhantomJs，它最大的特点就是操作DOM可以完全在内存中进行模拟（可用代码驱动浏览器操作），既在v8引擎中处理，又不打开浏览器，而且这个是Chrome团队在维护，会拥有更好的兼容性和发展前景。 Puppeteer 是一个Chrome官方团队提供的node库，它可以通过 Puppeteer 的提供的 API 直接控制 Chrome 或 Chromium。 Puppeteer可以做什么？ 1） 生成网页截图或者 PDF 2） 爬取SPA应用，并生成预渲染内容（即“SSR” 服务端渲染） 3） 高级爬虫，可以爬取大量异步渲染内容的网页 4） 模拟键盘输入、表单自动提交、登录网页等 5） 创建一个最新的自动化测试环境，实现 UI 自动化测试 6） 捕获站点的时间线，以便追踪网站、帮助分析网站性能问题 7） 用于测试 Chrome 扩展程序 Puppeteer安装： 下面是简单的调用代码： puppeteer.launch 启动浏览器实例 browser.newPage() 创建一个新页面 page.goto 进入指定网页 page.screenshot 截图 const puppeteer = require('puppeteer'); (async () => { //打开浏览器 const browser = await puppeteer.launch({ headless: false //是否在 无头模式 下运行浏览器 false显示浏览器，true不显示 }); //打开新页面 const page = await browser.newPage(); //设置页面大小 page.setViewport({ width:1024, height:800 }); //打开百度 await page.goto('https://www.baidu.com'); //截图 await page.screenshot({path: 'example.png'}); //关闭浏览器 await browser.close(); })(); puppeteer的中文文档：https://www.w3cschool.cn/puppeteer/ 示例：哔哩哔哩音乐爬取 const puppeteer = require('puppeteer'); (async () => { const args = [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-infobars', '--window-position=0,0', '--ignore-certifcate-errors', '--ignore-certifcate-errors-spki-list', '--user-agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3312.0 Safari/537.36"' ]; const options = { args, headless: false, ignoreHTTPSErrors: true, userDataDir: './tmp2', defaultViewport:{width:1280,height:800} }; const browser = await puppeteer.launch(options); const browserWSEndpoint = browser.wsEndpoint(); console.log(browserWSEndpoint,'site'); const page = await browser.newPage(); await page.goto('https://www.bilibili.com/v/music/original/?spm_id_from=333.5.b_6d757369635f6f726967696e616c.59#/all/click/0/1/?open=hot'); await page.waitForSelector('#videolist_box > div.vd-list-cnt > ul > li > div > div.r > a'); let title = await page.$$eval('#videolist_box > div.vd-list-cnt > ul > li > div > div.r > a', link=>link.map(v=>{ return { title:v.innerText, href:v.href} }) ); console.log(title) })(); 参数详解：https://blog.csdn.net/qq_43382853/article/details/103688551 手动补充环境相对耗时耗力，如果采集频率要求不高时，直接在浏览器中模拟环境调用即可，在接下来的小节中来学习一下对浏览器环境的检测。 第八章 加密方法远程调用 加密方法的远程调用主要是使用了RPC协议，RPC（Remote Procedure Call）是远程调用的意思。RPC的应用十分广泛，比如在分布式中的进程间通信、微服务中的节点通信。 我们这里使用的rpc其实是实现两个不同进程通信的一种方式，比如在浏览器执行一些方法，将结果返回给本地使用。 在Js逆向时，本地可以和浏览器以服务端和客户端的形式通过webSocket协议进行RPC通信，这样可以直接调用浏览器中的一些函数方法，不必去在意函数具体的执行逻辑，可以省去大量的逆向调试时间。 在RPC中，发出请求的程序是客户端，而提供服务的程序是服务端，所以大家的浏览器是客户端，本地是服务端。 8.1 微博登录参数RPC 本节内容以新浪微博网页版为例，来讲解一下如何在Web上使用RPC协议完成加密参数的获取。 网站链接：https://weibo.com 首先输出账号：（随机）密码：（随机）进行登录，通过控制台进行抓包，可以发现POST的登录接口： 在Form Data中有很多经过加密的参数su、rsakv、sp如图所示： 此时如果按照正常的逆向流程，需要对每个加密参数进行分析和逆向，比较浪费时间和精力。 先通过搜索定位一个加密参数的位置，比如全局搜索关键词rsakv，只有一个Js文件中有该关键词，点击进去并格式化代码，继续搜索准确位置，如图所示： 如果有很多搜索结果，而且不确定具体位置，就在所有关键字前打上断点进行分析。 刷新，再重新登录： 经过简单分析我们发现，a是账号，b是密码，e.sp是对b进行加密后的结果 我们尝试在console调用该方法： 调用成功后，接下来可以对Js文件进行修改，添加一个WebSocket客户端，供大家进行RPC调用。 首先需要修改Js文件，鉴于方便操作和讲解，我们选择通过控制台的Overriders来进行Js文件替换。当然也可以选择通过Fiddler、Mitmproxy等抓包工具替换，或者通过谷歌的GRPC协议来进行Js内容替换。 在source中选择Overrides，然后创建一个本地目录，需要勾选Enable Local Overrrides： 然后在没有格式化的Js文件上单击鼠标右键，选择【Save for overrides】命令： 接下来就可以进行修改了，将格式化后的代码复制到save的文件中，然后按Ctrl+s快捷键进行保存（注意微博的inDex.js文件，它每隔一个小时会进行重命名，所以需要重新进行覆盖） 接着要做的就是选择一个位置来插入一个webSocket连接供大家进行RPC调用。 首先要确定代码注入位置，可以写到makeRequest = function(a, b, c, d){}中，然后手动激活该函数。 不能单纯地在该函数中写入创建客户端，这样会导致死循环，无限创建客户端。所以需要进行代码上的判断，如果已经创建过，就不再创建客户端。 注入的Js代码如下： !function(){ if (window.flagMsb){} else{ window.weiboMsb = makeRequest; var ws = new WebSocket("ws://127.0.0.1:9999"); window.flagMsb =true; ws.open = function(evt){}; ws.onmessage = function(evt){ var Msb = evt.data; var result = Msb.split(","); var res = window.weiboMsb(result[0],result[1],7,false); ws.send(JSON.stringify(res)); }} }(); 接下来用python创建一个webSocket服务端。 import asyncio import websockets async def check_permit(websocket): # 账号列表 for send_text in [ '11111111111,111', '11111111112,112', '11111111113,113', '11111111114,114' ]: await websocket.send(send_text) return True async def recv_msg(websocket): while 1: recv_text = await websocket.recv() print(recv_text) async def main_logic(websocket, path): await check_permit(websocket) await recv_msg(websocket) start_server = websockets.serve(main_logic, '127.0.0.1', 9999) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() 运行python代码开启本地服务，然后刷新页面，填入账号密码单击“登录”按钮，触发Js代码。可以在请求中看到来自127.0.0.1:9999的ws数据包： 在大家的控制台也有了对应的输出： 此时一个RPC调用案例已经结束，学会了RPC可以在某些时候快速解决问题，毕竟不需要追代码逻辑和补浏览器环境。案例中实现的方法需要手动替换Js文件，可以通过谷歌的开发者工具远程调试协议来实现Js文件的注入。更多的应用需要大家动手实践。 第九章 AST技术简介 前面我们介绍了一些javascript混淆的基本知识，可以看到混淆方式多种多样，比如字符串混淆、变量名混淆、对象键名替换、控制流平坦化等，当然，我们也学习了一些相关的调试技巧，比如Hook、断点调试等。但是这些方法本质上其实还是在已经混淆的代码上进行的操作，所以代码的可读性依然比较差。 有没有什么办法可以直接提高代码的可读性呢？比如说，字符串混淆了，我们想办法把它还原了；对象键名替换了，我们想办法把它们重新组装好了，控制流平坦化之后逻辑不直观了，我们想办法把它还原成一个代码控制流。 到底应该怎么做呢？这就需要用到AST相关的知识了。本节中，我们就来了解AST相关的基础知识，并介绍操作AST的相关方法。 9.1 AST介绍 首先，我们来了解什么是AST。AST的全称叫作Abstract Syntax Tree，中文翻译叫作抽象语法树。 如果你对编译原理有所了解的话，一段代码在执行之前，通常要经历这么三个步骤。 词法分析：一段代码首先会被分解成一段段有意义的词法单元，比如const name = “msb”这段代码，它可以被拆分成四部分：const、name、=、“msb”，每个部分都具备一定的含义。 语法分析：接着编译器会尝试对一个个词法单元进行语法分析，将其转换为能代表程序语法结构的数据结构。比如，const 就被分析为 VariableDeclaration 类型，代表变量声明的具体定义；name就被分析为Identifier类型，代表一个标识符。代码内容多了，这一个个词法就会有依赖、嵌套等关系，因此表示语法结构的数据结构就构成了一个树状的结构，也就成了语法树，即AST。 指令生成：最后将AST转换为实际真正可执行的指令并执行即可。 AST是源代码的抽象语法结构的树状表示，树上的每个节点都表示源代码的一种结构，这种数据结构其实可以类比成一个大的JSON对象。 在前端开发中，AST技术应用十分广泛，有了AST，我们可以方便地对JavaScript代码进行转换和改写，因此还原混淆后的Javascript代码也就不在话下了。 接下来，我们通过一些实例了解AST的一些基本理念和操作。 9.2 实例引入 首先，推荐一个AST在线解析的网站 http://astexplorer.net/，我们先通过一个非常简单的实例来感受一下AST究竟是什么样子的，输入上述的示例代码： const name = "msb" 这时候我们就可以看到在右侧就出现了一个树状结构，这就是AST，如图所示： 这就是一个层层嵌套的数据结构，可以看到他把代码的每一个部分都进行了拆分并分析出对应的类型、位置和值。比如说，name被解析成一个type为Identifier的数据结构，start和end分别代表代码的起始和终止位置，name属性代表该Identifier的名称。另外，msb这个字符串被解析成了StringLiteral类型的数据结构，它同样有start、end等属性，同时还有extra属性。我们所看到的这些数据结构就构成了一个层层嵌套的AST。 另外，在右上角，我们还看到一个Parser标识。这是一个目前最流行的JavaScript语法编译器Babel的Node.js包，同时它也是主流前端开发技术中必不可少的一个包。它内置了很多分析JavaScript代码的方法，可以实现JavaScript代码到AST的转换。 接下来，我们使用Babel来实现一下AST的解析、修改。 9.3 准备工作 由于本节内容需要用到babel，而Babel是基于Node.js的，所以这里需要先安装Node.js。 安装好Node.js之后，我们便可以使用npm命令了，接着，我们还需要安装一个Babel的命令行工具@babel/node，安装命令如下： npm install -g @babel/node 接下来，我们再初始化一个Node.js项目learn-ast，然后在learn-ast目录下运行初始化命令，具体如下： npm install -D @babel/core @babel/cli @babel/preset-env 运行完毕之后，就会生成一个package.json文件并在devDependencies中列出了刚刚安装的几个Node.js包。 接着，我们需要在learn-ast目录下创建一个，.babelrc文件，其内容如下： { "presets":[ "@babel/preset-env" ] } 这样我们就算完成了初始化操作。 9.4 节点类型 在刚才的示例中，我们看到不同的代码词法单元被解析成了不同的类型，所以这里先简单列举Babel中所支持的一些类型。 Literal：中文可以理解为字面量，既简单的文字表示，比如3、”abc”、null这些都是基本的字面表示。 Declarations：声明，比如声明方法或者变量。 Expressions：表达式，它本身会返回一个计算结果，通常有两个作用：一个是放在赋值语句的右边进行赋值，另外还可以作为方法的参数。 Statements：语句。 Identifier：标识符，指代一些变量的名称，比如上述例子中的name就是Identifier。 Classes：类，代表一个类的定义。 Functions：方法声明。 Modules：模块，可以理解为一个Node.js模块。 Program：程序，整个代码可以称为Program。 当然除此之外还有很多的类型，具体可以参考：https://babeljs.io/docs/en/babel-types。 9.5 @babel/parser的使用 @babel/parser是Babel中的Javascript解析器，也是一个Node.js包，他提供了一个重要的方法，就是parse和parseExpression方法，前者支持解析一段JavaScript代码，后者则是尝试解析单个JavaScript表达式并考虑了性能问题。一般来说，我们直接使用parse方法就够了。 对于parse方法来说，输入和输出如下。 输入：一段JavaScript代码。 输出：该段JavaScript代码对应的抽象语法树，即AST，它基于ESTree规范。 现在我们来测试一下。 新建一个JavaScript文件，将其保存为codes/code1.js，其内容如下： const a = 3; let string = "hello"; for (let i = 0; i < a; i++){ string += "world"; } console.log("string", string); 下面我们需要使用parse方法将其转化为一个抽象语法树，即AST。 新建一个basic1.js文件，内容如下： import { parse } from "@babel/parser"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); console.log(ast); 接着我们可以使用babel-node运行： babel-node basic1.js 结果如下： 可以看到，整个AST的根节点就是一个Node，其中type是File，代表一个File类型的节点，其中包括type、start、end、loc、program等属性。其中program也是一个Node，但它的type是Program，代表一个程序。同样，Program也包括了一些属性，比如start、end、loc、interpreter、body等。其中，body是最为重要的属性，是一个列表类型，列表中的每个元素也都是一个Node，但这些不同的Node其实也是不同的类型，它们的type多种多样，不过这里控制台并没有把其中的节点内容输出出来。 我们可以增加一行代码，再专门输出一下body的内容： console.log(ast.program.body); 重新运行，可以发现这里又多输出了一些内容，具体如下： 由于内容过多，这里省略了一些内容。可以看到，我们直接通过ast.program.body即可将body获取到。可以看到，刚才的四个Node的具体结构也被输出出来了。前面两个Node都是VariableDeclaration类型，这正好对应了前面两行代码： const a = 3; let string = "hello"; 这里我们分别声明了一个数字类型和字符串类型的变量，所以每句都被解析为VariableDeclaration类型。每个VariableDeclaration都包含了一个declarations属性，其内部又是一个Node列表，其中包含了具体的详情信息。 接着，我们再继续观察下一个Node，它是ForStatement类型，代表一个for循环语句，对应的代码如下： for (let i = 0; i < a; i++){ string += "world"; } for循环通常包括四个部分，for初始逻辑、判断逻辑、更新逻辑以及以及for循环区块的主循环执行逻辑，所以对于一个ForStatement，它也自然有几个对应的属性表示这些内容，分别为init、test、update和body。 对于init，即循环的初始逻辑，其代码如：let i = 0； 它相当于声明一个变量声明，所以它又被解释为VariableDeclaration类型，这和上文是一样的。 对于test，即判断逻辑，其代码如下： i < a 它是一个逻辑表达式，被解析BinaryExpression，代表逻辑运算。 对于update，即更新逻辑，其代码如下： i++ 它就是对i+1，也是一个表达式，被解析为UpdateExpression类型。 对于body，它被一个大括号包围，其内容为： { string += "world" } 整个内容算作一个代码块，所以被解析为BlockStatement类型，其body属性又是一个列表。 对于最后一行，代码如下： console.log(“string”, string); 它被解析为ExpressionStatement类型，expression的属性是CallExpression。CallExpression又包含了callee和arguments属性，对应的就是console对象的log方法的调用逻辑。 到现在为止，我们应该能弄明白这个基本过程了。 parser会将代码根据逻辑区块进行划分，每个逻辑区块根据其作用都会归类成不同的类型，不同的类型拥有不同的属性表示。同时代码和代码之间有嵌套关系，所以最终整个代码就会被解析成一个层层嵌套的表示结果。 9.6 @babel/generator的使用 @babel/generator也是一个Node.js包，它提供了generate方法将AST还原成JavaScript代码，调用如下： import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); const { code: output } = generate(ast) console.log(output); 重新运行可以得到如下结果： 这时候我们可以看到，利用generate方法，我们成功地把一个AST对象转化成为Javascript代码。 到这里我们就清楚了，如果要把一段JavaScript解析成AST对象，就用parse方法。如果要把AST对象还原成代码，就用generate方法。 9.7 @babel/traverse的使用 前面我们了解了AST的解析，输入任意一段JavaScript代码，我们便可以分析出其AST。但是只是了解AST，我们并不能实现JavaScript代码的反混淆。下面我们还需要进一步了解另一个强大的功能，就是AST的遍历和修改。 遍历我们使用的是@babel/traverse，它可以接收一个AST利用tracerse方法就可以遍历其中的所有节点。在遍历方法中，我们便可以对每一个节点进行对应的操作了。 我们先来感受一下遍历的基本实现。新建一个JavaScript文件，将其命名为basic2.js，内容如下： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); traverse(ast, { enter(path) { console.log(path) }, }); 这里我们调用了traverse方法，给第一个参数传入AST对象，给第二个参数定义了相关的处理逻辑，这里声明了一个enter方法，它接收path参数。这个enter方法在每个节点被遍历到时都会被调用，其中path里面就包含了当前被遍历到的节点相关信息。这里我们先把path输出出来，看看遍历时能拿到什么信息。 运行如下代码： babel-node basic2.js 这时我们看到控制台输出了非常多的内容，调用很多次log方法输出了对应的内容。每次输出都代表一个path对象，我们拿其中一次输出结果看下。 可以看到内容比较复杂。首先我们可以看到它的类型是NodePath，拥有parent、container、node、scope、type等多个属性。比如node属性是一个Node类型的对象，他代表当前正在遍历的节点。比如，利用parent也能获得一个Node类型对象，它代表该节点的父节点。 所以我们可以利用path.node拿到当前对应的Node对象，利用path.parent拿到当前Node对象的父节点。 既然如此，我们便可以使用他来对Node进行一些处理。比如，我们可以把值变化一下，原来的代码如下： const a = 3; let string = "hello"; for (let i = 0; i < a; i++){ string += "world"; } console.log("string", string); 我们想要通过修改AST的方式对如上代码进行修改，比如修改一下a变量和string变量的值，变成如下代码： const a = 5; let string = "hi"; for (let i = 0; i < a; i++){ string += "world"; } console.log("string", string); 我们可以这样实现逻辑: import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); traverse(ast, { enter(path) { let node = path.node; if (node.type === "NumericLiteral" && node.value === 3) { node.value = 5; } if (node.type === "StringLiteral" && node.value === "hello") { node.value = "hi"; } }, }); const { code: output } = generate(ast, { retainLines: true, }); console.log(output); 这里我们判断了node的类型和值，然后将node的value进行了替换，这样执行完毕traverse方法之后，AST就被更新完毕了。 运行结果如下： 可以看到，原始的JavaScript代码就被成功更改了！ 另外，除了定义enter方法外，我们还可以直接定义对应特定类型的解析方法，这样遇到此类型的节点时，该方法就会被自动调用，用法类似如下： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); traverse(ast, { NumericLiteral(path) { if (path.node.value === 3) { path.node.value = 5; } }, StringLiteral(path) { if (path.node.value === "hello") { path.node.value = "hi"; } }, }); const { code: output } = generate(ast, { retainLines: true, }); console.log(output); 运行结果完全是相同的，单独定义特定的类型的解析方法会显得更有条理。 另外，我们可以再看下其他的操作方法，比如，删除某个node，这里可以试着删除最后一行代码对应的节点，此时直接调用remove方法即可，用法如下： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code1.js", "utf-8"); let ast = parse(code); traverse(ast, { CallExpression(path) { let node = path.node; if ( node.callee.object.name === "console" && node.callee.property.name === "log" ) { path.remove(); } }, }); const { code: output } = generate(ast, { retainLines: true, }); console.log(output); 这样我们就可以删除所有的console.log语句。 运行结果如下： 上面说了简单的替换和删除，那么如果我们要插入一个节点，该怎么办呢？插入新的节点时，需要先声明一个节点，怎么声明呢？这时候就要用到types了。 9.8 @babel/types的使用 @babel/types是一个Node.js包，里面定义了各种各样的对象，我们可以方便地使用types声明一个新的节点。 比如说，这里有这样一个代码： const a = 1; 我们想增加一行代码，将原始的代码变成： const a = 1; const b = a + 1; 该怎么办呢？这时候我们可以借助types实现如下操作： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import * as types from "@babel/types"; const code = "const a = 1;"; let ast = parse(code); traverse(ast, { VariableDeclaration(path) { let init = types.binaryExpression( "+", types.identifier("a"), types.numericLiteral(1) ); let declarator = types.variableDeclarator(types.identifier("b"), init); let declaration = types.variableDeclaration("const", [declarator]); path.insertAfter(declaration); path.stop(); }, }); const { code: output } = generate(ast, { retainLines: true, }); console.log(output); 运行结果如下： 这里我们成功使用AST完成了节点的插入，增加了一行代码。 但上面的代码看起来似乎不知道怎么实现的，init、declarator、declaration都是怎么来的呢？ 别担心我们，接下来我们详细剖析一下。首先我们可以把最终想要变换的代码进行AST解析，结果如图所示： 这时候我们可以看到第二行代码的节点结构了，现在需要做的就是构造这个节点，需要从内而外依次构造。 首先看到整行代码对应的节点是VariableDeclaration。要生成VariableDeclaration，我们可以借助types的variableDeclaration的方法，二者的差别仅仅是后者的开头字母是小写的。 API怎么用呢？这就需要查阅官方文档了。我们查到variableDeclaration的用法如下： t.variableDeclaration(kind, declarations) 可以看到，构造他需要两个参数，具体如下。 kind：必需，可以是“var”|“let”|“const”。 declarations：必需，是Array<VariableDeclarator>，即VariableDeclarator组成的列表。 这里kind我们可以确定了，那么declarations怎么构造呢？ 要构造declarations，我们需要进一步构造VariableDeclarator，它也可以借助types的variableDeclarator方法，用法如下： t.variableDeclarator(id, init) 他需要id和init两个参数。 id：必需，即Identifier对象 init：Epression对象，默认为空。 因此，我们还需要构造 id 和 init 对象。这里 id 其实就是 b 了，我们可以借助于types的identifier方法来构造。而对于init，它是expression，在AST中我们可以观察到它是BinaryExpression类型，所以我们可以借助于types的binaryExpression来构造。binaryExpression的用法如下： t.binaryExpression(operator, left, right) 他有三个参数，具体如下。 operator：必需，”+” | “-“ | “/“ | “%” | “*” | “**” | “&” | “|” | “>>” | “>>>” | “<<” | “^” | “” | “===” | “!=” | “!” | “in” | “instanceof” | “>” | “<” | “>=” | “<=”。 left：必需，Expression，即operator左侧的表达式。 rught：必需，Expression，即operator右侧的表达式。 这里又需要三个参数，operator就是运算符，left就是运算符左侧的内容，right就是运算符右侧的内容。后面两个参数都需要是Expression，根据AST，这里的Expression可以直接声明为Identifier和NumericLiteral，所以又可以分别用types的identifier和numericLiteral创建。 这样梳理清楚后，我们从里到外将代码实现出来，一层一层构造，最后就声明了一个VariableDeclaration类型的节点。 最后，调用path的insertAfter方法便可以成功将节点插入到path对相应的节点。 第十章 使用AST技术还原混淆代码 在上一节中，我们介绍了AST相关的基本知识和基础的操作方法，本节中我们就来实际应用这些方法来还原JavaScript混淆后的代码，即反混淆的实现。 由于JavaScript混淆方式多种多样，这里就介绍一些常见的反混淆方案，如表达式还原，字符串还原，无用代码剔除，反控制流平坦化等。 10.1 表达式还原 有时候，我们会看到有一些混淆的JavaScript代码其实就是把简单的东西复杂化，比如说一个布尔常量true，被写成 !![] ; 一个数字，被转化为parseInt加一些字符串的拼接。通过这些方式，一些简单又直观的表达式就被复杂化了。 看下面的这几个例子，代码如下：code2.js const a = !![]; const b = "abc" == "bcd"; const c = (1 << 3) | 2; const d = parseInt("5" + "0"); 对于这种情况，有没有还原的方法呢？当然有，借助于AST，我们可以轻松实现。 首先，在=的右侧，其实都是一些表达式的类型，比如 “abc” = “bcd” 就是一个BinaryExpression，他代表的是一个布尔类型的结果。 怎么处理呢？我们将上述代码保存为code2.js，根据上一章节学到的知识，可以编写如下还原代码： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import * as types from "@babel/types"; import fs from "fs"; const code = fs.readFileSync("codes/code2.js", "utf-8"); let ast = parse(code); traverse(ast, { "UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression": ( path ) => { const { confident, value } = path.evaluate(); if (value == Infinity || value == -Infinity) return; confident && path.replaceWith(types.valueToNode(value)); }, }); const { code: output } = generate(ast); console.log(output); 这里我们使用traverse方法对AST对象进行遍历，，使用 “UnaryExpression|BinaryExpression|ConditionalExpression|CallExpression“作为对象的键名，分别用于处理一元表达式、布尔表达式、条件表达式、调用表达式。如果AST对应的path对象符合这几种表达式，就会执行我们定义的回调方法。在回调方法里面，我们调用了path的evaluate方法，该方法会对path对象进行执行，计算所得到的结果。其内部实现会返回一个confident的value字段表示置信度，如果认定结果是可信的，那么confident就是true，我们可以调用path的replaceWith方法把执行的结果value进行替换，否侧不替换。 运行结果如下： 可以看到，原本看起来不怎么直观的代码现在被还原得非常直观了。 所以，利用这个原理，我们可以实现一些表达式的还原和计算，提高整个代码的可读性。 10.2 字符串还原 之前我们了解到，JavaScript被混淆后，有些字符串会被转化为Unicode或者UTF-8编码的数据，比如说这个样子：const string = [“\x68\x65\x6c\x6c\x6f”, “\x77\x6f\x72\x6c\x64”]; 其实这原本就是一个简单的字符串，被转换成UTF-8编码之后，其可读性大大降低了，如果这样的字符串被隐藏在JavaScript代码里面，我们想通过搜索字符串的方式寻找关键突破口，就搜不到了。 对于这种字符串，我们能用AST还原码？当然可以。 我们先在https://astexplorer.net/里面把这行代码粘贴进去，结果如图所示： 可以看到，两个字符串都被识别成了 StringLiteral类型，它们都有一个extra属性。extra属性里卖有一个raw属性和rawValue属性，二者是不一样的，rawValue的真实值已经被分析出来了。 因此，我们只需要将 StringLiteral 中 extra 属性的 raw 值替换为 rawValue 的值即可，实现如下： import traverse from "@babel/traverse"; import { parse } from "@babel/parser"; import generate from "@babel/generator"; import fs from "fs"; const code = fs.readFileSync("codes/code3.js", "utf-8"); let ast = parse(code); traverse(ast, { StringLiteral({ node }) { if (node.extra && /\\[ux]/gi.test(node.extra.raw)) { node.extra.rawValue = node.extra.raw; } }, }); const { code: output } = generate(ast); console.log(output); 输出结果如下： 这样我们就成功实现了混淆字符串的还原。 如果我们把这个脚本应用于混杂了混淆字符串的JavaScript文件，那么其中的混淆字符串就可以被还原出来。 第十一章 WebAssembly案例分析和爬取实战 WebAssembly是一种可以使用非JavaScript编程语言编写代码并且能在浏览器上运行的技术方案。 前面我们也简单介绍过了，借助Emscripten编译工具，我们能将C/C++文件转成wasm格式的文件，JavaScript可以直接调用该文件执行其中的方法。 这样做的好处如下。 一些核心逻辑（比如API参数的加密逻辑）使用C/C++实现，这样这些逻辑就可以“隐藏”在编译生成的wasm文件中，其逆向难度比JavaScript更大。 一些逻辑是基于C/C++编写的，有更高的执行效率，这使得以各种语言编写的代码都可以以接近原生的速度在Web中运行。 对于这种类型的网站，一般我们会看到网站会加载一些wasm后缀的文件，这就是WebAssembly技术常见的呈现形式，即原生代码被编译成了wasm后缀的文件，JavaScript通过调用wasm文件得到对应的计算结果，然后配合其他JavaScript代码实现页面数据的加载和页面的渲染。 本节中，我们就来通过一个集成WebAssembly的案例网站来认识下WebAssembly，并通过简易的模拟技术来实现网站的爬取。 11.1 案例介绍 下面我们来看一个案例，网址是 https://spa14.scrape.center/，这个网站表面上和之前非常类似，但是实际上其API的加密参数是通过WebAssembly实现的。 首先，我们还是像之前一样，加载首页，然后通过Network面版分析Ajax请求，如图所示： 可以看到，这里就找到了第一页数据的Ajax请求。和之前的案例类似，limit、offset参数用来控制分页，sign参数用来做校验，它的值是一个数字。通过观察后面几页的内容，我们发现sign的值一直在变化。 因此，这里的关键就是在于找到sign值的生成逻辑，我们再模拟请求即可。 接下来，我们就进行一下逆向，先看看这个参数生成的逻辑在哪里吧。 这里我们还是设置一个Ajax断点，再Sources面板的 XHR/fetch Breakpoints 这里添加一个断点，内容为/api/movie，就是在请求加载数据的时候进如断点，如图所示： 接下来，重新刷新页面，可以看到页面执行到断点的位置后停下来，如图所示： 这里我们还是通过Call Stack找到构造逻辑。经过简单的查找和推测，可以判断逻辑的入口在onFetchData方法里面，如图所示： 点击onFetchData方法，找到方法所在的JavaScript代码如图所示： 和之前的案例类似，params的参数有三个——limit、offset、sign，这和Ajax请求一致。 这里关键的参数就是sign了，可以看到它的值是用变量e表示的，而e的生成代码就在上面，如下所示： 可以看到，它通过调用this.$wasm.asm对象的encrypt方法传入了n和一个时间戳构造出来了。 接下来，我们进一步在此处调试一下，在2100行添加断点，如图所示： 重新刷新页面，可以发现页面运行到该段点的位置并停了下来，如图： 这相当于JavaScript上下文处于onFetchData方法内部，所以现在我们可以访问方法内部的所有变量，比如this、this.$wasm等。 接下来，我们在Watch面板中添加一个变量this.$wasm，先看看它是什么对象，如图所示： 可以看到这个this.$wasm对象里面又定义了很多对象和方法，其中就包括了asm对象。因为代码中又调用了asm对象的encrypt来生产sign，所以我们进一步看看asm对象、encrypt方法都是什么。将asm对象直接展开即可： 这时候我们可以看到asm对象里面又包含了 几个对象和方法，比较重要的就是encrypt方法了，其中它的 FunctionLocation指向了另外一个位置，名称是 Wasm.wasm:0xd9。因为我们就是想知道这个方法内部究竟是什么逻辑，所以直接点击进入，如图： 可以看到，我们进入了一个似乎不是JavaScript代码的位置，文件名称叫作Wasm.wasm。 代码跳转的位置可以看到encrypt字样，其代码定义如下： 如果你了解汇编语言的话，会发现这有点汇编语言的味道。 这其实就是wasm文件，这里面的逻辑其实原本使用C++编写的，通过Emscripten转化为wasm文件，就成了现在的这个样子。 这时候我们可以找下Network请求，搜索wasm后缀的文件，如图：(把断点先去掉) 可以看到，这里就有一个wasm后缀的文件，其逻辑就是刚才看到的内容。 到了这里，wasm代码已经完全看不懂了，接下来怎么做呢？ 有两种办法，一种是直接把wasm文件进行反编译，还原成C++代码，此种方法上手难度大，需要了解WebAssembly和逆向相关的知识，另外一种就是通过模拟执行的方式来直接得到加密结果。 本节我们主要来了解第二种方案。拿到wasm文件，然后通过Python模拟执行的方式调用wasm文件，模拟调用它的encrypt方法，传入对应的参数即可。 11.2 模拟执行 首先，我们把wasm文件下载下来，地址为 https://spa14.scrape.center/js/Wasm.wasm，将其保存为Wasm.wasm文件 要使用Python模拟执行wasm，可以使用两个Python库，一个叫作pywasm，另一个叫作wasmer-python，前者使用更加简单，后者功能更加强大。我们使用任意一个库都可以完成wasm文件的模拟。 这里我们以pywasm为例 这个库比较简单，其主要功能就是加载一个wasm文件，然后用Python执行。 安装命令如下： pip install pywasm 安装完之后，我们可以用如下代码来加载一个wasm文件： import pywasm runtime = pywasm.load("./Wasm.wasm") print(runtime) 这里我们调用了pywasm的load方法，直接将wasm文件的路径传入，实现了wasm文件的读取，输出结果如下： 可以看到，返回结果就是一个pywasm.Runtime类型的对象。 有了这个Runtime对象之后，我们就可以调用它的exec方法来模拟执行Wasm里面的方法。 比如，在网页中我们可以看到他执行了encrypt方法，并传入了两个参数。我们也来试一下，要模拟调用wasm的方法，只需要调用Runtime对象的exec方法并传入对应的方法名和参数内容即可。 我们可以将代码改写如下： import pywasm runtime = pywasm.load("./Wasm.wasm") result = runtime.exec("encrypt", [1, 2]) print(result) 这里我们调用了exec方法来，第一个参数就是要调用的wasm中的方法名，这里我们传入字符串encrypt，第二个参数是一个列表，代表encrypt方法接收的参数，如果是两个，那么列表长度就是2，参数和列表的元素一一对应即可。 运行结果如下： 调用成功！ 成功的输出了结果，但是这似乎并不是我们想要的，因为这里传入的参数其实是我们自定义的，要真正模拟网站的Ajax请求，就要用网站里面的真实参数。 通过分析逻辑，我们知道传入的参数其实一个是offset，一个是时间戳。 其中时间戳的实现是这样的： parseInt(Math.round((new Date).getTime() / 1e3).toString()) 这是JavaScript中的实现，我们将其输出到控制台，可以看到运行结果如图所示： 输出的其实就是一个时间戳，结果是数值类型，位数是10位。使用Python实现同样的结果，可以这样写： import time print(int(time.time())) 最终，我们可以将爬虫逻辑实现，具体如下： import pywasm import time import requests base_url = "https://spa14.scrape.center" page = 10 runtime = pywasm.load("./Wasm.wasm") for i in range(page): offset = i * 10 sign = runtime.exec('encrypt', [offset, int(time.time())]) url = f'{base_url}/api/movie/?limit=10&offset={offset}&sign={sign}' response = requests.get(url) print(response.json()) 这里我们先定义了page是10，就是10页，然后开始一个for循环遍历，i就是0~9的数字，offset就是0、10、20、30……、90，sign就是利用刚才的实现，将参数转化为offset变量和时间戳，最后构造url请求即可： 运行结果如下： 可以看到，Ajax请求被成功模拟了，成功爬取到了结果。 第十二章 无限debugger的原理与绕过 debugger是JavaScript中定义的一个专门用于断点调试的关键字，只要遇到他，JavaScritp的执行便会在此处中断，进入调试模式。 有了debugger这个关键字，我们就可以非常方便地对JavaScript代码进行调试，比如使用JavaScript Hook时，我们可以加入debugger关键字，使其在关键的位置停下来，以便查找逆向突破口。 但有时候，debugger会被网站开发者利用，使其成为阻挠我们正常调试的拦路虎。 本节我们介绍一个案例来绕过无限debugger 12.1 案例介绍 我们先看一个案例，网址是 https://antispider8.scrape.center/，打开这个网站，一般操作和之前的网站没有什么不同。但是，一旦我们打开开发者工具，就发现它立即进入了断点模式，如图： 我们既没有设置任何断点，也没有执行任何额外的脚本，它就直接进入了断点模式。这时候我们可以点击 Resume script execution（恢复脚本执行）按钮，尝试跳过这个断点继续执行。如图所示： 然而不管我们点击多少次按钮，它仍然一次次地进入断点模式，无限循环下去，我们称这样的情况为无限debugger。 怎么办呢？似乎无法正常添加断点调试了，有什么解决办法吗？ 肯定是有办法的，本节我们就来总结一下无限debugger的应对方案。 12.2 实现原理 我们首先要做的是找到无限debugger的源头。在Sources面板中可以看到，debugger关键字出现在一个JavaScript文件里，这时点击左下角的格式化按钮，如图： 格式化后可以发现这里通过setInterval循环，每秒执行1次debugger语句。 当然，还有很多类似的实现，比如无限for循环、无限while循环、无限递归调用等，它们都可以实现这样的效果，原理大同小异。 了解了原理，下面我们就对症下药吧！ 12.3 禁用断点 因为debugger其实就是对应的一个断点，它相当于用代码显式地声明了一个断点，要解除它，我们只需要禁用所有断点就好了。 首先，我们可以禁用所有断点。全局禁用开关位于Sources面板地右上角，叫作 Deactivate breakpoints，如图所示： 这时候我们重新点击一下 Resume script execution 按钮，跳过当前断点，页面就不会进入到无限debugger的状态了。 但是这种全局禁用其实并不是一个好的方案因为禁用之后我们也无法在其他位置增加断点进行调试了，所有的断点都失效了。 这时候我们可以选择禁用局部断点。取消刚才的 Deactivate breakpoints 模式，页面会重新进入无限debugger模式，我们尝试使用另一种方法来跳过这个无限循环debugger。 我们可以在debugger语句所在的行的行号上单击鼠标右键，此时会出现一个快捷菜单如图： 这里会有一个 Never pause here选项，意思是从不在此处暂停。选择这个选项，于是页面变成如图： 当前断点显示为橙色，并且断点前面多了一个？符号，同时Breakpoints也出现了刚才添加的断点位置。这时再次点击 Resume script execution 按钮，就可以发现不会再进入无限debugger模式了。 当然，我们也可以选择另外一个选项 Add conditional breakpoint 如图： 这个模式更加高级，我们可以设置进入断点的条件，比如在调试过程中，期望某个变量的值大于某个具体值的时候才停下来。但在本案例中，由于这里是无限循环，我们没有什么具体的变量可以作为判断依据，因此可以直接写一个简单的表达式来控制。 选择 Add conditional breakpoint 选项，直接填入false 即可。 按下回车键，就跟 Never pause here 选项一样，重新点击 Resume script execution 按钮，也不会进入无限debugger循环了。 12.4 替换文件 之前我们介绍过Overrides面板的用法，利用它可以将远程的JavaScript文件替换成本地的JavaScript文件，这里我们依然可以使用这个方法来对文件进行替换。 很简单，只需要在新的文件里面把debugger这个关键字删除或者是注释。 我们将当前的JavaScript文件复制到文本编辑器中，删除或者直接注释掉debugger这个关键字，修改如下： 替换完成之后，重新刷新网页，这时候发现不会进入无限debugger模式了。 第十三章 JavaScript逆向技巧总结 前面我们已经学习了不少JavaScript逆向相关的知识，包括浏览器调试、Hook、AST、无限debugger的绕过以及JavaScript的模拟调用等，这些知识点都比较松散，有时大家学完了可能觉得没有形成一个知识体系，或者说没有一个常规“套路”来应对一些JavaScript逆向的处理流程。 本节中，我们就对前面的知识点做一个串联和总结，总结出JavaScript逆向过程中常用的流程，这个流程适用于大多数JavaScript逆向过程。大家熟练运用之后，可以在不同情况下运用不同的技巧来进行JavaScript逆向操作。 总的来说，JavaScript逆向可以分为三大部分：寻找入口、调试分析、模拟执行。下面我们来分别介绍。 寻找入口：这是非常关键的一步，逆向在大部分情况下就是找一些加密参数到底是怎么来的，比如请求中token、sign等参数到底是在哪里构造的，这个关键逻辑可能写在某个关键的方法里面或者隐藏在某个关键变量里面。一个网站加载了很多JavaScript文件，那么怎么从这么多JavaScript代码里面找到关键的位置，那就是一个关键问题。这就是寻找入口。 调试分析：找到入口之后，比如说我们可以定位到某个参数可能是在某个方法里面执行的了，那么里面的逻辑究竟是怎样的，里面调用了多少加密算法，经过了多少变量赋值和转换等，这些我们需要先把整体思路搞清楚，以便于我们后面进行模拟调用或者逻辑改写。在这个过程中，我们主要借助于浏览器的调试工具进行调试分析，或者借助于一些反混淆工具进行代码的反混淆等。 模拟执行：经过调试分析之后，我们差不多已经搞清楚整个逻辑了，但我们的最终目的还是写爬虫，怎么爬到数据才是根本，因此这里就需要对整个加密过程进行逻辑复写或者模拟执行，以把整个加密流程模拟出来，比如输入一些已知变量，调用之后我们就可以拿到一些token内容，再用合格token来进行数据爬取即可。 13.1 寻找入口 首先，我们来看下怎么寻找入口，其中包括查看请求、搜索参数、分析发起调用、断点、Hook等操作，下面我们来分别介绍一下。 13.1.1 查看请求 一般来说，我们都是先分析想要的数据到底是从哪里来的，比如说对于示例网站 https://spa6.scrape.center/，我们可以看到首页有一条条数据，如“霸王别姬”、“这个杀手不太冷”等，这些数据肯定是某个请求返回的，那它究竟是从哪个请求里面来的呢？我们可以先尝试搜索一下。 打开浏览器开发者工具，打开Network面板，然后点击搜索按钮，比如这里我们就搜索“霸王别姬”这四个字，如图： 此时可以看到对应的搜索结果，点击搜索到的结果，我们就可以定位到对应的响应结果的位置。 找到对应的响应之后，我们也就可以顺便找到是哪个请求发起的了。 比如这里，我们就顺利找到想要的数据所对应的请求位置了，可以看到这是一个GET请求，同时还有一个token参数，我们可以在后面继续分析。 一般来说，我们可以通过这种方法来尝试寻找最初的突破口。如果这个请求带有加密参数，就顺着继续找下这个参数究竟是在哪里生成的，如果这个请求对应的参数甚至都没有什么加密参数，那么这个请求都可以直接模拟爬取了。 13.1.2 搜索参数 在上一步中，我们找到了最初的突破口，也就是关键请求是怎么发起的，带有什么加密参数，比如，在上面的例子中，我们发现这里有一个关键的加密参数token，那这又是怎么构造出来的呢？ 一种简单有效的方法就是直接进行全局搜索。一般来说，参数名大多数情况下就是一个普通的字符串，比如这里就叫作token，那么这个字符串肯定隐藏在某个JavaScript文件里面，我们可以尝试进行搜索，也可以加冒号、空格、引号等来配合搜索。因为一般来说这个参数通常会配合一些符号一起出现，比如我们可以搜索：token、token：、“token”等。 在哪里搜索呢？我们可以直接利用浏览器调试面板的搜索功能，如图： 这是一个资源搜索的入口，比如可以搜索下载下来的JavaScript文件的内容，这里我们输入token来进行搜索，结果如图： 这样我们就可以找到一些关键的位置点了，一共五个结果，结果不多，我们可以进一步点击并定位到对应的JavaScript文件中，然后进一步进行分析。 13.1.3 分析发起调用 上述的搜索是其中一种查找入口的方式，这是从源码级别上直接查找。当然，我们也可以通过其他的思路来查找入口，比如可以查看发起调用的流程，怎么查看呢？ 可以直接从Network请求里面的Initiator查看当前请求的相关逻辑，如图： 右侧显示了一步步调用对应的源码的位置，我们可以顺次点进去找到对应的位置，比如这里第8层调用，里面有个 onFetchData 方法。点击右侧的代码位置，就可以找到一些相关的逻辑，如图： 这里可以看到一些 token 相关的逻辑调用过程了。 13.1.4 断点 另外，我们还可以通过断点来进行入口的查找，比如XHR断点，DOM断点，事件断点等。 我们可以在开发者工具Sources面板里面调价设置，比如我们之前添加了XHR断点，如图： 这样网页就可以在发起Ajax请求的时候停下来，进入断点调试模式，也就是说，通过浏览器断点调试功能，我们也可以找到到对应的入口。 13.2 调试分析 找到对应的入口位置之后，接下来我们就需要进行调试分析了。在这个步骤中，我们通常需要进行一些格式化、断点调试、反混淆等操作来辅助整个流程的分析。 13.2.1 格式化 格式化这个流程是非常重要的，它可以大大增加代码的可读性，一般来说很多JavaScript代码都是经过打包和压缩的，多数情况下，我们可以使用Sources面板下的JavaScript窗口左下角的格式化按钮对代码就行格式化，如图： 13.2.2 断点调试 代码格式化之后，我们就可以进入正式的调试流程了，基本操作就是给想要调试的代码添加断点，同时在对应的面板里面观察对应变量的值。 如图所示：我们在190行添加断点，然后逐行运行对应的代码，这时代码页面就会出现对应变量的值，同时我们也可以在Watch面板上监听关注的变量。 通过这样的方式，我们就可以对整个代码流程有一个大致的了解。 13.2.3 反混淆 在某些情况下，我们还有可能遇到一些混淆的方式，此时通过之前章节所讲的反混淆技术还原代码即可。 13.3 模拟执行 经过一系列调试，现在我们已经可以理清其中的逻辑了，接下来就是一些调用执行的过程了。在前面的章节中，我们已经讲过一些案例执行的流程了。 13.3.1 python改写或者模拟执行 由于python简单易用，同时也能够模拟调用执行JavaScript。如果整体逻辑不复杂的话，我们可以尝试使用python来把整个加密流程完整实现一遍。如果整体流程相对复杂，我们可以尝试使用python来模拟调用JavaScript的执行。 13.3.2 JavaScript模拟执行+API 由于整个逻辑是JavaScript实现的，使用python来执行JavaScript难免会有一些不太方便的地方。 而node.js天生就有对javaScript的支持。为了更通用地实现JavaScript的模拟调用。我们可以用express来模拟调用JavaScript，同时将其暴露成一个API，从而实现跨域语言的调用。 13.3.3 浏览器模拟执行 由于整个逻辑是运行在浏览器里面的，我们当然也可以将浏览器当作整个执行环境，比如使用Selenium等工具尝试执行一些JavaScript代码，得到一些返回结果。 调用的方式有很多，不同的情况我们可以根据实现的难以程度来选择不同的方案。 第十四章 JavaScript逆向爬取实战 前面我们学习了各种JavaScirpt逆向技巧，本节中我们综合应用之前学习到的知识点进行一个完整的JavaScript逆向分析和爬取实战。 14.1 案例介绍 本节的案例网站不仅在API参数有加密，而且前端JavaScript也带有压缩和混淆，其前端压缩打包工具使用webpack，混淆工具使用javascript-obfuscator。分析该网站需要熟练掌握浏览器的开发者工具和一定的调试技巧，另外还需要用到一些Hook技术等辅助分析手段。 案例的地址为 https://spa6.scrape.center/,页面首页如图所示： 和之前的网站并没有什么不同之处，但仔细观察可以发现其Ajax请求接口和每部电影的url都包含了加密参数。 比如我们点击任意一部电影，观察一下URL的变化，如图： 可以看到详情页的url包含了一个长字符串，看上去像是Base64编码的内容。 接下来，看看Ajax请求。我们从列表页的第1页到第10页依次点一下，观察Ajax请求时怎样的，如图所示： 可以看到，Ajax接口的url里多了一个token，而且在不同的页码，token是不一样的，它们同样看似时Base64编码的字符串。 另外，更困难的是，这个接口还有时效性。如果我们把Ajax接口的url直接复制下来，短期内可以访问，但是过一段时间之后就无法访问了，会直接返回401状态码。 我们再看一下列表的返回结果，比如打开第一个请求，看看第一部电影数据的返回结果，如图所示： 这里看似是把第一部电影的返回结果全展开了，但是刚才我们观察到第一部电影的url是https://spa6.scrape.center/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx，看起来是Base64编码，我们对它进行解码，结果为 ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb1（用在线解码工具）看起来还是没有规律，这个解码后的结果又是怎么来的？返回的结果里并不包含这个字符串，这又是怎么构造的呢？ 还有，这仅仅是某个详情页页面的url，其真实数据是通过Ajax加载的，那么Ajax请求又是怎么样的呢？我们再观察一下如图所示： 这里我们发现其Ajax接口除了包含刚才所说的url中携带的字符串，又多了一个token，同样也是类似Base64编码的内容。总结下来，这个网站就有如下特点： 列表页的Ajax接口参数带有加密token； 详情页的URL参数带有加密id； 详情页的Ajax接口参数带有加密id和加密token。 如果我们要想通过接口的形式进行爬取，必须把这些加密id和token构造出来才行，而且必须一步一步来。首先我们要构造出列表页Ajax接口的token参数，然后获取每部电影的数据信息，接着根据数据信息构造出加密id和token。 到现在为止，我们知道了这个网站接口的加密情况，下一步就是去找这个加密实现逻辑。 由于是网页，所以其加密逻辑一定藏在前端代码里，但上一节我们也说了，前端为了保护其接口加密逻辑不被轻易分析出来，会采取压缩、混淆等方式来加大分析的难度。下面我们就来看看这个网站的源代码和JavaScript文件是怎样的。 首先看网站的源代码，我们在网站上点击鼠标右键，此时会弹出快捷菜单，然后点击“查看源代码”选项，如图所示： 内容如下： Scrape | Movie We're sorry but portal doesn't work properly without JavaScript enabled. Please enable it to continue. 这是一个典型的SPA(单页Web应用)页面，其JavaScript文件名带有编码字符、chunk、vendors等关键字，这就是经过webpack打包压缩后的源代码，目前主流的前端开发框架Vue.js、React.js等的输出结果都是类似这样的。 接下来，我们再看一下其JavaScript代码是什么样子的。再开发者工具中打开Sources选项卡下的page选项，然后打开js文件夹，在这里我们能看到JavaScript的源代码，如图： 我们随便复制一些出来，看看是什么样子的，结果如下： (window['webpackJsonp']=window['webpackJsonp']||[])['push']([['chunk-4dec7ef0'],{'00bb':function(_0x4f8837,_0x262b1b,_0x47f85e){(function(_0x353526,_0x29d305,_0x4a28d3){_0x4f8837['exports']=_0x29d305(_0x47f85e('21bf'),_0x47f85e('38ba'));}(0x0,function(_0x47ffa6){return _0x47ffa6['mode']['CFB']=function(){var _0x130a3a=_0x47ffa6['lib']['BlockCipherMode']['extend']();function _0x56a78e(_0x38b9fa,_0x5a30b0,_0x15e394,_0x4b6cda){var _0x4f1b23=this['_iv'];if(_0x4f1b23){var _0x36e95d=_0x4f1b23['slice'](0x0);this['_iv']=void 0x0;}else _0x36e95d=this['_prevBlock'];_0x4b6cda['encryptBlock'](_0x36e95d,0x0);for(var _0x593f2f=0x0;_0x593f2f<_0x15e394;_0x593f2f++)_0x38b9fa[_0x5a30b0+_0x593f2f]^=_0x36e95d[_0x593f2f];}return _0x130a3a['Encryptor']=_0x130a3a['extend']({'processBlock':function(_0x52b87e,_0x7bf075){var _0x552022=this['_cipher'],_0x146f2c=_0x552022['blockSize'];_0x56a78e['call'](this,_0x52b87e,_0x7bf075,_0x146f2c,_0x552022),this['_prevBlock']=_0x52b87e['slice'](_0x7bf075,_0x7bf075+_0x146f2c);}}),_0x130a3a['Decryptor']=_0x130a3a['extend']({'processBlock':function(_0x30eec8,_0x2f0556){var _0x5f1763=this['_cipher'],_0x397e68=_0x5f1763['blockSize'],_0x132675=_0x30eec8['slice'](_0x2f0556,_0x2f0556+_0x397e68);_0x56a78e['call'](this,_0x30eec8,_0x2f0556,_0x397e68,_0x5f1763),this['_prevBlock']=_0x132675;}}),_0x130a3a;}(),_0x47ffa6['mode']['CFB'];}));},'0bfb':function(_0x3a7403,_0x16a22c,_0x3a87dd){'use strict';var _0x4083a8=_0x3a87dd('cb7c');_0x3a7403['exports']=function(){var _0x17ba27=_0x4083a8(this),_0x146dd9='';return _0x17ba27['global']&&(_0x146dd9+='g'),_0x17ba27['ignoreCase']&&(_0x146dd9+='i'),_0x17ba27['multiline']&&(_0x146dd9+='m'),_0x17ba27['unicode']&&(_0x146dd9+='u'),_0x17ba27['sticky']&&(_0x146dd9+='y'),_0x146dd9;};},'10b7':function(_0x1b566f,_0xf63964,_0x3674a7){(function(_0x3bed90,_0x285472){_0x1b566f['exports']=_0x285472(_0x3674a7('21bf'));}(0x0,function(_0x361f76){/** @preserve (c) 2012 by Cédric Mesnil. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ return function(_0x2ed6a3){var _0x331eab=_0x361f76,_0x189535=_0x331eab['lib'],_0x17f5f4=_0x189535['WordArray'],_0x40c120=_0x189535['Hasher'],_0x403605=_0x331eab['algo'],_0x2e6fec=_0x17f5f4['create']([0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x7,0x4,0xd,0x1,0xa,0x6,0xf,0x3,0xc,0x0,0x9,0x5,0x2,0xe,0xb,0x8,0x3,0xa,0xe,0x4,0x9,0xf,0x8,0x1,0x2,0x7,0x0,0x6,0xd,0xb,0x5,0xc,0x1,0x9,0xb,0xa,0x0,0x8,0xc,0x4,0xd,0x3,0x7,0xf,0xe,0x5,0x6,0x2,0x4,0x0,0x5,0x9,0x7,0xc,0x2,0xa,0xe,0x1,0x3,0x8,0xb,0x6,0xf,0xd]),_0x87cf80=_0x17f5f4['create']([0x5,0xe,0x7,0x0,0x9,0x2,0xb,0x4,0xd,0x6,0xf,0x8,0x1,0xa,0x3,0xc,0x6,0xb,0x3,0x7,0x0,0xd,0x5,0xa,0xe,0xf,0x8,0xc,0x4,0x9,0x1,0x2,0xf,0x5,0x1,0x3,0x7,0xe,0x6,0x9,0xb,0x8,0xc,0x2,0xa,0x0,0x4,0xd,0x8,0x6,0x4,0x1,0x3,0xb,0xf,0x0,0x5,0xc,0x2,0xd,0x9,0x7,0xa,0xe,0xc,0xf,0xa,0x4,0x1,0x5,0x8,0x7,0x6,0x2,0xd,0xe,0x0,0x3,0x9,0xb]),_0x16f5f8=_0x17f5f4['create']([0xb,0xe,0xf,0xc,0x5,0x8,0x7,0x9,0xb,0xd,0xe,0xf,0x6,0x7,0x9,0x8,0x7,0x6,0x8,0xd,0xb,0x9,0x7,0xf,0x7,0xc,0xf,0x9,0xb,0x7,0xd,0xc,0xb,0xd,0x6,0x7,0xe,0x9,0xd,0xf,0xe,0x8,0xd,0x6,0x5,0xc,0x7,0x5,0xb,0xc,0xe,0xf,0xe,0xf,0x9,0x8,0x9,0xe,0x5,0x6,0x8,0x6,0x5,0xc,0x9,0xf,0x5,0xb,0x6,0x8,0xd,0xc,0x5,0xc,0xd,0xe,0xb,0x8,0x5,0x6]),_0x3bf4b6=_0x17f5f4['create']([0x8,0x9,0x9,0xb,0xd,0xf,0xf,0x5,0x7,0x7,0x8,0xb,0xe,0xe,0xc,0x6,0x9,0xd,0xf,0x7,0xc,0x8,0x9,0xb,0x7,0x7,0xc,0x7,0x6,0xf,0xd,0xb,0x9,0x7,0xf,0xb,0x8,0x6,0x6,0xe,0xc,0xd,0x5,0xe,0xd,0xd,0x7,0x5,0xf,0x5,0x8,0xb,0xe,0xe,0x6,0xe,0x6,0x9,0xc,0x9,0xc,0x5,0xf,0x8,0x8,0x5,0xc,0x9,0xc,0x5,0xe,0x6,0x8,0xd,0x6,0x5,0xf,0xd,0xb,0xb]),_0x476366=_0x17f5f4['create']([0x0,0x5a827999,0x6ed9eba1,0x8f1bbcdc,0xa953fd4e]),_0xe04516=_0x17f5f4['create']([0x50a28be6,0x5c4dd124,0x6d703ef3,0x7a6d76e9,0x0]),_0x4578f4=_0x403605['RIPEMD160']=_0x40c120['extend']({'_doReset':function(){this['_hash']=_0x17f5f4['create']([0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]);},'_doProcessBlock':function(_0x544b29,_0x4b8691){for(var _0xa7aa64=0x0;_0xa7aa64<0x10;_0xa7aa64++){var _0x444cba=_0x4b8691+_0xa7aa64,_0x509c8e=_0x544b29[_0x444cba];_0x544b29[_0x444cba]=0xff00ff&(_0x509c8e<<0x8|_0x509c8e>>>0x18)|0xff00ff00&(_0x509c8e<<0x18|_0x509c8e>>>0x8);}var _0x4543ab,_0xfe3678,_0x2fb20f,_0x11467d,_0x2dfb0f,_0x3716af,_0x3a0ef8,_0x5643ff,_0xe8aedc,_0x2dd504,_0x5ecd24,_0x2e2a43=this['_hash']['words'],_0x1ab7cb=_0x476366['words'],_0x439df4=_0xe04516['words'],_0x4e925f=_0x2e6fec['words'],_0x4cbb1a=_0x87cf80['words'],_0xa7eaeb=_0x16f5f8['words'],_0x492ac7=_0x3bf4b6['words'];_0x3716af=_0x4543ab=_0x2e2a43[0x0],_0x3a0ef8=_0xfe3678=_0x2e2a43[0x1],_0x5643ff=_0x2fb20f=_0x2e2a43[0x2],_0xe8aedc=_0x11467d=_0x2e2a43[0x3],_0x2dd504=_0x2dfb0f=_0x2e2a43[0x4];for(_0xa7aa64=0x0;_0xa7aa64<0x50;_0xa7aa64+=0x1)_0x5ecd24=_0x4543ab+_0x544b29[_0x4b8691+_0x4e925f[_0xa7aa64]]|0x0,_0x5ecd24+=_0xa7aa64<0x10?_0x47834c(_0xfe3678,_0x2fb20f,_0x11467d)+_0x1ab7cb[0x0]:_0xa7aa64<0x20?_0x30ff01(_0xfe3678,_0x2fb20f,_0x11467d)+_0x1ab7cb[0x1]:_0xa7aa64<0x30?_0x1c28cf(_0xfe3678,_0x2fb20f,_0x11467d)+_0x1ab7cb[0x2]:_0xa7aa64<0x40?_0x4956ff(_0xfe3678,_0x2fb20f,_0x11467d)+_0x1ab7cb[0x3]:_0x1b34a7(_0xfe3678,_0x2fb20f,_0x11467d)+_0x1ab7cb[0x4],_0x5ecd24|=0x0,_0x5ecd24=_0xcae23c(_0x5ecd24,_0xa7eaeb[_0xa7aa64]),_0x5ecd24=_0x5ecd24+_0x2dfb0f|0x0,_0x4543ab=_0x2dfb0f,_0x2dfb0f=_0x11467d,_0x11467d=_0xcae23c(_0x2fb20f,0xa),_0x2fb20f=_0xfe3678,_0xfe3678=_0x5ecd24,_0x5ecd24=_0x3716af+_0x544b29[_0x4b8691+_0x4cbb1a[_0xa7aa64]]|0x0,_0x5ecd24+=_0xa7aa64<0x10?_0x1b34a7(_0x3a0ef8,_0x5643ff,_0xe8aedc)+_0x439df4[0x0]:_0xa7aa64<0x20?_0x4956ff(_0x3a0ef8,_0x5643ff,_0xe8aedc)+_0x439df4[0x1]:_0xa7aa64<0x30?_0x1c28cf(_0x3a0ef8,_0x5643ff,_0xe8aedc)+_0x439df4[0x2]:_0xa7aa64<0x40?_0x30ff01(_0x3a0ef8,_0x5643ff,_0xe8aedc)+_0x439df4[0x3]:_0x47834c(_0x3a0ef8,_0x5643ff,_0xe8aedc)+_0x439df4[0x4],_0x5ecd24|=0x0,_0x5ecd24=_0xcae23c(_0x5ecd24,_0x492ac7[_0xa7aa64]),_0x5ecd24=_0x5ecd24+_0x2dd504|0x0,_0x3716af=_0x2dd504,_0x2dd504=_0xe8aedc,_0xe8aedc=_0xcae23c(_0x5643ff,0xa),_0x5643ff=_0x3a0ef8,_0x3a0ef8=_0x5ecd24;_0x5ecd24=_0x2e2a43[0x1]+_0x2fb20f+_0xe8aedc|0x0,_0x2e2a43[0x1]=_0x2e2a43[0x2]+_0x11467d+_0x2dd504|0x0,_0x2e2a43[0x2]=_0x2e2a43[0x3]+_0x2dfb0f+_0x3716af|0x0,_0x2e2a43[0x3]=_0x2e2a43[0x4]+_0x4543ab+_0x3a0ef8|0x0,_0x2e2a43[0x4]=_0x2e2a43[0x0]+_0xfe3678+_0x5643ff|0x0,_0x2e2a43[0x0]=_0x5ecd24;},'_doFinalize':function(){var _0x2aa283=this['_data'],_0x3dc03b=_0x2aa283['words'],_0x5c0f43=0x8*this['_nDataBytes'],_0x19e605=0x8*_0x2aa283['sigBytes'];_0x3dc03b[_0x19e605>>>0x5]|=0x80<<0x18-_0x19e605%0x20,_0x3dc03b[0xe+(_0x19e605+0x40>>>0x9<<0x4)]=0xff00ff&(_0x5c0f43<<0x8|_0x5c0f43>>>0x18)|0xff00ff00&(_0x5c0f43<<0x18|_0x5c0f43>>>0x8),_0x2aa283['sigBytes']=0x4*(_0x3dc03b['length']+0x1),this['_process']();for(var _0x3e6532=this['_hash'],_0x4e127d=_0x3e6532['words'],_0x293470=0x0;_0x293470<0x5;_0x293470++){var _0xe7716b=_0x4e127d[_0x293470];_0x4e127d[_0x293470]=0xff00ff&(_0xe7716b<<0x8|_0xe7716b>>>0x18)|0xff00ff00&(_0xe7716b<<0x18|_0xe7716b>>>0x8);}return _0x3e6532;},'clone':function(){var _0xc67c87=_0x40c120['clone']['call'](this);return _0xc67c87['_hash']=this['_hash']['clone'](),_0xc67c87;}});function _0x47834c(_0x394fcc,_0x403949,_0x4b52e3){return _0x394fcc^_0x403949^_0x4b52e3;}function _0x30ff01(_0x4ec5d1,_0x1ffda8,_0x2dba25){return _0x4ec5d1&_0x1ffda8|~_0x4ec5d1&_0x2dba25;}function _0x1c28cf(_0x16cf38,_0x439560,_0x3fa31c){return(_0x16cf38|~_0x439560)^_0x3fa31c;}function _0x4956ff(_0x10da61,_0x42daac,_0x2814a7){return _0x10da61&_0x2814a7|_0x42daac&~_0x2814a7;}function _0x1b34a7(_0x5e55b6,_0x52c42f,_0x1cb645){return _0x5e55b6^(_0x52c42f|~_0x1cb645);}function _0xcae23c(_0xf18de8,_0x16ebd1){return _0xf18de8<<_0x16ebd1|_0xf18de8>>>0x20-_0x16ebd1;}_0x331eab['RIPEMD160']=_0x40c120['_createHelper'](_0x4578f4),_0x331eab['HmacRIPEMD160']=_0x40c120['_createHmacHelper'](_0x4578f4);}(Math),_0x361f76['RIPEMD160'];}));},1132:function(_0x22c790,_0x370bf8,_0x344368){(function(_0x33f30c,_0x52fee8){_0x22c790['exports']=_0x52fee8(_0x344368('21bf'));}(0x0,function(_0x23416a){return function(){var _0x2f62b8=_0x23416a,_0x5c8e1b=_0x2f62b8['lib'],_0x59277d=_0x5c8e1b['WordArray'],_0x510efe=_0x2f62b8['enc'];_0x510efe['Base64']={'stringify':function(_0x333619){var _0x15e563=_0x333619['words'],_0x38f909=_0x333619['sigBytes'],_0x1f6ceb=this['_map'];_0x333619['clamp']();for(var _0x1fdac6=[],_0x42f1e3=0x0;_0x42f1e3<_0x38f909;_0x42f1e3+=0x3)for(var _0x234ac1=_0x15e563[_0x42f1e3>>>0x2]>>>0x18-_0x42f1e3%0x4*0x8&0xff,_0x31bc94=_0x15e563[_0x42f1e3+0x1>>>0x2]>>>0x18-(_0x42f1e3+0x1)%0x4*0x8&0xff,_0x10ac96=_0x15e563[_0x42f1e3+0x2>>>0x2]>>>0x18-(_0x42f1e3+0x2)%0x4*0x8&0xff,_0x43b384=_0x234ac1<<0x10|_0x31bc94<<0x8|_0x10ac96,_0x133f57=0x0;_0x133f57<0x4&&_0x42f1e3+0.75*_0x133f57<_0x38f909;_0x133f57++)_0x1fdac6['push'](_0x1f6ceb['charAt'](_0x43b384>>>0x6*(0x3-_0x133f57)&0x3f));var _0x1fe1bb=_0x1f6ceb['charAt'](0x40);if(_0x1fe1bb)while(_0x1fdac6['length']%0x4)_0x1fdac6['push'](_0x1fe1bb);return _0x1fdac6['join']('');},'parse':function(_0x97f123){var _0x33ed13=_0x97f123['length'],_0x1df8e0=this['_map'],_0x2824b=this['_reverseMap'];if(!_0x2824b){_0x2824b=this['_reverseMap']=[];for(var _0x4b24ef=0x0;_0x4b24ef<_0x1df8e0['length'];_0x4b24ef++)_0x2824b[_0x1df8e0['charCodeAt'](_0x4b24ef)]=_0x4b24ef;}var _0x2efd60=_0x1df8e0['charAt'](0x40);if(_0x2efd60){var _0x208a98=_0x97f123['indexOf'](_0x2efd60);-0x1!==_0x208a98&&(_0x33ed13=_0x208a98);}return _0x437adf(_0x97f123,_0x33ed13,_0x2824b);},'_map':'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='};function _0x437adf(_0x106e77,_0x45795b,_0x2e7899){for(var _0x5d4e4e=[],_0x4bb179=0x0,_0x35a3a1=0x0;_0x35a3a1<_0x45795b;_0x35a3a1++)if(_0x35a3a1%0x4){var _0x4a3921=_0x2e7899[_0x106e77['charCodeAt'](_0x35a3a1-0x1)]<<_0x35a3a1%0x4*0x2,_0x2d54a7=_0x2e7899[_0x106e77['charCodeAt'](_0x35a3a1)]>>>0x6-_0x35a3a1%0x4*0x2;_0x5d4e4e[_0x4bb179>>>0x2]|=(_0x4a3921|_0x2d54a7)<<0x18-_0x4bb179%0x4*0x8,_0x4bb179++;}return _0x59277d['create'](_0x5d4e4e,_0x4bb179);}}(),_0x23416a['enc']['Base64'];}));},1382:function(_0xfe3270,_0x37a6ce,_0x371ab5){(function(_0x55e563,_0x52322c,_0x236324){_0xfe3270['exports']=_0x52322c(_0x371ab5('21bf'),_0x371ab5('1132'),_0x371ab5('72fe'),_0x371ab5('2b79'),_0x371ab5('38ba'));}(0x0,function(_0x23e79f){return function(){var _0x34275c=_0x23e79f,_0x812fb0=_0x34275c['lib'],_0x4f5b23=_0x812fb0['StreamCipher'],_0xd4784a=_0x34275c['algo'],_0x547adb=[],_0x214ba0=[],_0x5ce89d=[],_0x5280ea=_0xd4784a['Rabbit']=_0x4f5b23['extend']({'_doReset':function(){for(var _0x28f4c7=this['_key']['words'],_0x1cfa70=this['cfg']['iv'],_0x2bfc47=0x0;_0x2bfc47<0x4;_0x2bfc47++)_0x28f4c7[_0x2bfc47]=0xff00ff&(_0x28f4c7[_0x2bfc47]<<0x8|_0x28f4c7[_0x2bfc47]>>>0x18)|0xff00ff00&(_0x28f4c7[_0x2bfc47]<<0x18|_0x28f4c7[_0x2bfc47]>>>0x8);var _0x3a3f4a=this['_X']=[_0x28f4c7[0x0],_0x28f4c7[0x3]<<0x10|_0x28f4c7[0x2]>>>0x10,_0x28f4c7[0x1],_0x28f4c7[0x0]<<0x10|_0x28f4c7[0x3]>>>0x10,_0x28f4c7[0x2],_0x28f4c7[0x1]<<0x10|_0x28f4c7[0x0]>>>0x10,_0x28f4c7[0x3],_0x28f4c7[0x2]<<0x10|_0x28f4c7[0x1]>>>0x10],_0x2f2ac4=this['_C']=[_0x28f4c7[0x2]<<0x10|_0x28f4c7[0x2]>>>0x10,0xffff0000&_0x28f4c7[0x0]|0xffff&_0x28f4c7[0x1],_0x28f4c7[0x3]<<0x10|_0x28f4c7[0x3]>>>0x10,0xffff0000&_0x28f4c7[0x1]|0xffff&_0x28f4c7[0x2],_0x28f4c7[0x0]<<0x10|_0x28f4c7[0x0]>>>0x10,0xffff0000&_0x28f4c7[0x2]|0xffff&_0x28f4c7[0x3],_0x28f4c7[0x1]<<0x10|_0x28f4c7[0x1]>>>0x10,0xffff0000&_0x28f4c7[0x3]|0xffff&_0x28f4c7[0x0]];this['_b']=0x0;for(_0x2bfc47=0x0;_0x2bfc47<0x4;_0x2bfc47++)_0x22ee51['call'](this);for(_0x2bfc47=0x0;_0x2bfc47<0x8;_0x2bfc47++)_0x2f2ac4[_0x2bfc47]^=_0x3a3f4a[_0x2bfc47+0x4&0x7];if(_0x1cfa70){var _0x56883a=_0x1cfa70['words'],_0x28f8f8=_0x56883a[0x0],_0x275a5f=_0x56883a[0x1],_0xc7d441=0xff00ff&(_0x28f8f8<<0x8|_0x28f8f8>>>0x18)|0xff00ff00&(_0x28f8f8<<0x18|_0x28f8f8>>>0x8),_0x250a69=0xff00ff&(_0x275a5f<<0x8|_0x275a5f>>>0x18)|0xff00ff00&(_0x275a5f<<0x18|_0x275a5f>>>0x8),_0x4750be=_0xc7d441>>>0x10|0xffff0000&_0x250a69,_0x27db3c=_0x250a69<<0x10|0xffff&_0xc7d441;_0x2f2ac4[0x0]^=_0xc7d441,_0x2f2ac4[0x1]^=_0x4750be,_0x2f2ac4[0x2]^=_0x250a69,_0x2f2ac4[0x3]^=_0x27db3c,_0x2f2ac4[0x4]^=_0xc7d441,_0x2f2ac4[0x5]^=_0x4750be,_0x2f2ac4[0x6]^=_0x250a69,_0x2f2ac4[0x7]^=_0x27db3c;for(_0x2bfc47=0x0;_0x2bfc47<0x4;_0x2bfc47++)_0x22ee51['call'](this);}},'_doProcessBlock':function(_0x4cb1ad,_0x37871e){var _0x50a328=this['_X'];_0x22ee51['call'](this),_0x547adb[0x0]=_0x50a328[0x0]^_0x50a328[0x5]>>>0x10^_0x50a328[0x3]<<0x10,_0x547adb[0x1]=_0x50a328[0x2]^_0x50a328[0x7]>>>0x10^_0x50a328[0x5]<<0x10,_0x547adb[0x2]=_0x50a328[0x4]^_0x50a328[0x1]>>>0x10^_0x50a328[0x7]<<0x10,_0x547adb[0x3]=_0x50a328[0x6]^_0x50a328[0x3]>>>0x10^_0x50a328[0x1]<<0x10;for(var _0x17af74=0x0;_0x17af74<0x4;_0x17af74++)_0x547adb[_0x17af74]=0xff00ff&(_0x547adb[_0x17af74]<<0x8|_0x547adb[_0x17af74]>>>0x18)|0xff00ff00&(_0x547adb[_0x17af74]<<0x18|_0x547adb[_0x17af74]>>>0x8),_0x4cb1ad[_0x37871e+_0x17af74]^=_0x547adb[_0x17af74];},'blockSize':0x4,'ivSize':0x2});function _0x22ee51(){for(var _0x17ee36=this['_X'],_0x10dd54=this['_C'],_0x2d7bd6=0x0;_0x2d7bd6<0x8;_0x2d7bd6++)_0x214ba0[_0x2d7bd6]=_0x10dd54[_0x2d7bd6];_0x10dd54[0x0]=_0x10dd54[0x0]+0x4d34d34d+this['_b']|0x0,_0x10dd54[0x1]=_0x10dd54[0x1]+0xd34d34d3+(_0x10dd54[0x0]>>>0x0<_0x214ba0[0x0]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x2]=_0x10dd54[0x2]+0x34d34d34+(_0x10dd54[0x1]>>>0x0<_0x214ba0[0x1]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x3]=_0x10dd54[0x3]+0x4d34d34d+(_0x10dd54[0x2]>>>0x0<_0x214ba0[0x2]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x4]=_0x10dd54[0x4]+0xd34d34d3+(_0x10dd54[0x3]>>>0x0<_0x214ba0[0x3]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x5]=_0x10dd54[0x5]+0x34d34d34+(_0x10dd54[0x4]>>>0x0<_0x214ba0[0x4]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x6]=_0x10dd54[0x6]+0x4d34d34d+(_0x10dd54[0x5]>>>0x0<_0x214ba0[0x5]>>>0x0?0x1:0x0)|0x0,_0x10dd54[0x7]=_0x10dd54[0x7]+0xd34d34d3+(_0x10dd54[0x6]>>>0x0<_0x214ba0[0x6]>>>0x0?0x1:0x0)|0x0,this['_b']=_0x10dd54[0x7]>>>0x0<_0x214ba0[0x7]>>>0x0?0x1:0x0;for(_0x2d7bd6=0x0;_0x2d7bd6<0x8;_0x2d7bd6++){var _0x46d530=_0x17ee36[_0x2d7bd6]+_0x10dd54[_0x2d7bd6],_0x4e3135=0xffff&_0x46d530,_0x3a0cfc=_0x46d530>>>0x10,_0x555553=((_0x4e3135*_0x4e3135>>>0x11)+_0x4e3135*_0x3a0cfc>>>0xf)+_0x3a0cfc*_0x3a0cfc,_0x4f504e=((0xffff0000&_0x46d530)*_0x46d530|0x0)+((0xffff&_0x46d530)*_0x46d530|0x0);_0x5ce89d[_0x2d7bd6]=_0x555553^_0x4f504e;}_0x17ee36[0x0]=_0x5ce89d[0x0]+(_0x5ce89d[0x7]<<0x10|_0x5ce89d[0x7]>>>0x10)+(_0x5ce89d[0x6]<<0x10|_0x5ce89d[0x6]>>>0x10)|0x0,_0x17ee36[0x1]=_0x5ce89d[0x1]+(_0x5ce89d[0x0]<<0x8|_0x5ce89d[0x0]>>>0x18)+_0x5ce89d[0x7]|0x0,_0x17ee36[0x2]=_0x5ce89d[0x2]+(_0x5ce89d[0x1]<<0x10|_0x5ce89d[0x1]>>>0x10)+(_0x5ce89d[0x0]<<0x10|_0x5ce89d[0x0]>>>0x10)|0x0,_0x17ee36[0x3]=_0x5ce89d[0x3]+(_0x5ce89d[0x2]<<0x8|_0x5ce89d[0x2]>>>0x18)+_0x5ce89d[0x1]|0x0,_0x17ee36[0x4]=_0x5ce89d[0x4]+(_0x5ce89d[0x3]<<0x10|_0x5ce89d[0x3]>>>0x10)+(_0x5ce89d[0x2]<<0x10|_0x5ce89d[0x2]>>>0x10)|0x0,_0x17ee36[0x5]=_0x5ce89d[0x5]+(_0x5ce89d[0x4]<<0x8|_0x5ce89d[0x4]>>>0x18)+_0x5ce89d[0x3]|0x0,_0x17ee36[0x6]=_0x5ce89d[0x6]+(_0x5ce89d[0x5]<<0x10|_0x5ce89d[0x5]>>>0x10)+(_0x5ce89d[0x4]<<0x10|_0x5ce89d[0x4]>>>0x10)|0x0,_0x17ee36[0x7]=_0x5ce89d[0x7]+(_0x5ce89d[0x6]<<0x8|_0x5ce89d[0x6]>>>0x18)+_0x5ce89d[0x5]|0x0;}_0x34275c['Rabbit']=_0x4f5b23['_createHelper'](_0x5280ea);}(),_0x23e79f['Rabbit'];}));},'17e1':function(_0x3268dc,_0x4d21fb,_0x39e59e){(function(_0x17fcdb,_0x418a42){_0x3268dc['exports']=_0x418a42(_0x39e59e('21bf'));}(0x0,function(_0x3074a2){return function(){if('function'==typeof ArrayBuffer){var _0x21b957=_0x3074a2,_0x1fb359=_0x21b957['lib'],_0x54d19c=_0x1fb359['WordArray'],_0x4cf988=_0x54d19c['init'],_0x421eba=_0x54d19c['init']=function(_0x18949c){if(_0x18949c instanceof ArrayBuffer&&(_0x18949c=new Uint8Array(_0x18949c)),(_0x18949c instanceof Int8Array||'undefined'!==typeof Uint8ClampedArray&&_0x18949c instanceof Uint8ClampedArray||_0x18949c instanceof Int16Array||_0x18949c instanceof Uint16Array||_0x18949c instanceof Int32Array||_0x18949c instanceof Uint32Array||_0x18949c instanceof Float32Array||_0x18949c instanceof Float64Array)&&(_0x18949c=new Uint8Array(_0x18949c['buffer'],_0x18949c['byteOffset'],_0x18949c['byteLength'])),_0x18949c instanceof Uint8Array){for(var _0x50f5de=_0x18949c['byteLength'],_0x2b4fcf=[],_0x1cae2b=0x0;_0x1cae2b<_0x50f5de;_0x1cae2b++)_0x2b4fcf[_0x1cae2b>>>0x2]|=_0x18949c[_0x1cae2b]<<0x18-_0x1cae2b%0x4*0x8;_0x4cf988['call'](this,_0x2b4fcf,_0x50f5de);}else _0x4cf988['apply'](this,arguments);};_0x421eba['prototype']=_0x54d19c;}}(),_0x3074a2['lib']['WordArray'];}));},'191b':function(_0x3a842d,_0x579ebd,_0x43b264){(function(_0x498a81,_0x5f2259,_0x569dc4){_0x3a842d['exports']=_0x5f2259(_0x43b264('21bf'),_0x43b264('94f8'));}(0x0,function(_0x327fe6){return function(){var _0x55605d=_0x327fe6,_0x35294f=_0x55605d['lib'],_0x6cb577=_0x35294f['WordArray'],_0x125864=_0x55605d['algo'],_0x4e2279=_0x125864['SHA256'],_0x19d55e=_0x125864['SHA224']=_0x4e2279['extend']({'_doReset':function(){this['_hash']=new _0x6cb577['init']([0xc1059ed8,0x367cd507,0x3070dd17,0xf70e5939,0xffc00b31,0x68581511,0x64f98fa7,0xbefa4fa4]);},'_doFinalize':function(){var _0x30b122=_0x4e2279['_doFinalize']['call'](this);return _0x30b122['sigBytes']-=0x4,_0x30b122;}});_0x55605d['SHA224']=_0x4e2279['_createHelper'](_0x19d55e),_0x55605d['HmacSHA224']=_0x4e2279['_createHmacHelper'](_0x19d55e);}(),_0x327fe6['SHA224'];}));},'21bf':function(_0x1ea449,_0x376d84,_0x5d1328){(function(_0x374b0a,_0x3d0fcf){_0x1ea449['exports']=_0x3d0fcf();}(0x0,function(){var _0x2458a9=_0x2458a9||function(_0x5f1119,_0x592896){var _0x970d98=Object['create']||function(){function _0x24a196(){}return function(_0x47e06a){var _0x12d2e9;return _0x24a196['prototype']=_0x47e06a,_0x12d2e9=new _0x24a196(),_0x24a196['prototype']=null,_0x12d2e9;};}(),_0x42035f={},_0x139bed=_0x42035f['lib']={},_0x21e0fe=_0x139bed['Base']=function(){return{'extend':function(_0x3a0ad5){var _0x30e853=_0x970d98(this);return _0x3a0ad5&&_0x30e853['mixIn'](_0x3a0ad5),_0x30e853['hasOwnProperty']('init')&&this['init']!==_0x30e853['init']||(_0x30e853['init']=function(){_0x30e853['$super']['init']['apply'](this,arguments);}),_0x30e853['init']['prototype']=_0x30e853,_0x30e853['$super']=this,_0x30e853;},'create':function(){var _0x1cdcfb=this['extend']();return _0x1cdcfb['init']['apply'](_0x1cdcfb,arguments),_0x1cdcfb;},'init':function(){},'mixIn':function(_0x50a7ef){for(var _0x3ea751 in _0x50a7ef)_0x50a7ef['hasOwnProperty'](_0x3ea751)&&(this[_0x3ea751]=_0x50a7ef[_0x3ea751]);_0x50a7ef['hasOwnProperty']('toString')&&(this['toString']=_0x50a7ef['toString']);},'clone':function(){return this['init']['prototype']['extend'](this);}};}(),_0x4c95df=_0x139bed['WordArray']=_0x21e0fe['extend']({'init':function(_0x38f8c9,_0x3c3cde){_0x38f8c9=this['words']=_0x38f8c9||[],this['sigBytes']=_0x3c3cde!=_0x592896?_0x3c3cde:0x4*_0x38f8c9['length'];},'toString':function(_0x466cc7){return(_0x466cc7||_0x4fb3bf)['stringify'](this);},'concat':function(_0x16aebe){var _0x361552=this['words'],_0x3bebb4=_0x16aebe['words'],_0x9d2e2a=this['sigBytes'],_0x122646=_0x16aebe['sigBytes'];if(this['clamp'](),_0x9d2e2a%0x4)for(var _0x4f2b63=0x0;_0x4f2b63<_0x122646;_0x4f2b63++){var _0x1e3f63=_0x3bebb4[_0x4f2b63>>>0x2]>>>0x18-_0x4f2b63%0x4*0x8&0xff;_0x361552[_0x9d2e2a+_0x4f2b63>>>0x2]|=_0x1e3f63<<0x18-(_0x9d2e2a+_0x4f2b63)%0x4*0x8;}else for(_0x4f2b63=0x0;_0x4f2b63<_0x122646;_0x4f2b63+=0x4)_0x361552[_0x9d2e2a+_0x4f2b63>>>0x2]=_0x3bebb4[_0x4f2b63>>>0x2];return this['sigBytes']+=_0x122646,this;},'clamp':function(){var _0x1699cc=this['words'],_0x262fbe=this['sigBytes'];_0x1699cc[_0x262fbe>>>0x2]&=0xffffffff<<0x20-_0x262fbe%0x4*0x8,_0x1699cc['length']=_0x5f1119['ceil'](_0x262fbe/0x4);},'clone':function(){var _0x677697=_0x21e0fe['clone']['call'](this);return _0x677697['words']=this['words']['slice'](0x0),_0x677697;},'random':function(_0x239f1b){for(var _0x108788,_0x8079d7=[],_0x3bec18=function(_0x4d234e){_0x4d234e=_0x4d234e;var _0x2dca1f=0x3ade68b1,_0x49ee04=0xffffffff;return function(){_0x2dca1f=0x9069*(0xffff&_0x2dca1f)+(_0x2dca1f>>0x10)&_0x49ee04,_0x4d234e=0x4650*(0xffff&_0x4d234e)+(_0x4d234e>>0x10)&_0x49ee04;var _0x1e24f8=(_0x2dca1f<<0x10)+_0x4d234e&_0x49ee04;return _0x1e24f8/=0x100000000,_0x1e24f8+=0.5,_0x1e24f8*(_0x5f1119['random']()>0.5?0x1:-0x1);};},_0xa4f411=0x0;_0xa4f411<_0x239f1b;_0xa4f411+=0x4){var _0x1469ac=_0x3bec18(0x100000000*(_0x108788||_0x5f1119['random']()));_0x108788=0x3ade67b7*_0x1469ac(),_0x8079d7['push'](0x100000000*_0x1469ac()|0x0);}return new _0x4c95df['init'](_0x8079d7,_0x239f1b);}}),_0x3d0c70=_0x42035f['enc']={},_0x4fb3bf=_0x3d0c70['Hex']={'stringify':function(_0x1e1ac6){for(var _0xed1d96=_0x1e1ac6['words'],_0x261fc7=_0x1e1ac6['sigBytes'],_0x25fa39=[],_0x5c53ae=0x0;_0x5c53ae<_0x261fc7;_0x5c53ae++){var _0x42cb9a=_0xed1d96[_0x5c53ae>>>0x2]>>>0x18-_0x5c53ae%0x4*0x8&0xff;_0x25fa39['push']((_0x42cb9a>>>0x4)['toString'](0x10)),_0x25fa39['push']((0xf&_0x42cb9a)['toString'](0x10));}return _0x25fa39['join']('');},'parse':function(_0x3f70e9){for(var _0x936207=_0x3f70e9['length'],_0xfbc904=[],_0x5089ce=0x0;_0x5089ce<_0x936207;_0x5089ce+=0x2)_0xfbc904[_0x5089ce>>>0x3]|=parseInt(_0x3f70e9['substr'](_0x5089ce,0x2),0x10)<<0x18-_0x5089ce%0x8*0x4;return new _0x4c95df['init'](_0xfbc904,_0x936207/0x2);}},_0x4c247c=_0x3d0c70['Latin1']={'stringify':function(_0x2ffd1c){for(var _0x40f9f4=_0x2ffd1c['words'],_0x459356=_0x2ffd1c['sigBytes'],_0x2f2d54=[],_0x10579d=0x0;_0x10579d<_0x459356;_0x10579d++){var _0x92f8b1=_0x40f9f4[_0x10579d>>>0x2]>>>0x18-_0x10579d%0x4*0x8&0xff;_0x2f2d54['push'](String['fromCharCode'](_0x92f8b1));}return _0x2f2d54['join']('');},'parse':function(_0x5cf7f1){for(var _0x41bd9f=_0x5cf7f1['length'],_0x2d0ca7=[],_0x427403=0x0;_0x427403<_0x41bd9f;_0x427403++)_0x2d0ca7[_0x427403>>>0x2]|=(0xff&_0x5cf7f1['charCodeAt'](_0x427403))<<0x18-_0x427403%0x4*0x8;return new _0x4c95df['init'](_0x2d0ca7,_0x41bd9f);}},_0x5d2772=_0x3d0c70['Utf8']={'stringify':function(_0x31c39d){try{return decodeURIComponent(escape(_0x4c247c['stringify'](_0x31c39d)));}catch(_0x628581){throw new Error('Malformed\x20UTF-8\x20data');}},'parse':function(_0x3b1921){return _0x4c247c['parse'](unescape(encodeURIComponent(_0x3b1921)));}},_0x1f2d5e=_0x139bed['BufferedBlockAlgorithm']=_0x21e0fe['extend']({'reset':function(){this['_data']=new _0x4c95df['init'](),this['_nDataBytes']=0x0;},'_append':function(_0x386fa2){'string'==typeof _0x386fa2&&(_0x386fa2=_0x5d2772['parse'](_0x386fa2)),this['_data']['concat'](_0x386fa2),this['_nDataBytes']+=_0x386fa2['sigBytes'];},'_process':function(_0x5a85e2){var _0x24b14e=this['_data'],_0x411856=_0x24b14e['words'],_0xf6d078=_0x24b14e['sigBytes'],_0x4f0cfc=this['blockSize'],_0x18e951=0x4*_0x4f0cfc,_0x4de16e=_0xf6d078/_0x18e951;_0x4de16e=_0x5a85e2?_0x5f1119['ceil'](_0x4de16e):_0x5f1119['max']((0x0|_0x4de16e)-this['_minBufferSize'],0x0);var _0x229cc1=_0x4de16e*_0x4f0cfc,_0xb08f1=_0x5f1119['min'](0x4*_0x229cc1,_0xf6d078);if(_0x229cc1){for(var _0x2be887=0x0;_0x2be887<_0x229cc1;_0x2be887+=_0x4f0cfc)this['_doProcessBlock'](_0x411856,_0x2be887);var _0xf15233=_0x411856['splice'](0x0,_0x229cc1);_0x24b14e['sigBytes']-=_0xb08f1;}return new _0x4c95df['init'](_0xf15233,_0xb08f1);},'clone':function(){var _0x3adda4=_0x21e0fe['clone']['call'](this);return _0x3adda4['_data']=this['_data']['clone'](),_0x3adda4;},'_minBufferSize':0x0}),_0x2cbd18=(_0x139bed['Hasher']=_0x1f2d5e['extend']({'cfg':_0x21e0fe['extend'](),'init':function(_0x190147){this['cfg']=this['cfg']['extend'](_0x190147),this['reset']();},'reset':function(){_0x1f2d5e['reset']['call'](this),this['_doReset']();},'update':function(_0x3fd071){return this['_append'](_0x3fd071),this['_process'](),this;},'finalize':function(_0xcbc8c9){_0xcbc8c9&&this['_append'](_0xcbc8c9);var _0x5b1b52=this['_doFinalize']();return _0x5b1b52;},'blockSize':0x10,'_createHelper':function(_0x46bc23){return function(_0x343681,_0x3360f2){return new _0x46bc23['init'](_0x3360f2)['finalize'](_0x343681);};},'_createHmacHelper':function(_0x4b8fca){return function(_0x9a6df9,_0x517c5f){return new _0x2cbd18['HMAC']['init'](_0x4b8fca,_0x517c5f)['finalize'](_0x9a6df9);};}}),_0x42035f['algo']={});return _0x42035f;}(Math);return _0x2458a9;}));},'27ae':function(_0x2bdb8a,_0x349d42,_0x247eec){(function(_0x4ce9f1){var _0x3c6e10,_0xf71c54;(function(_0x1d5472,_0x20a839){_0x2bdb8a['exports']=_0x20a839(_0x1d5472);}('undefined'!==typeof self?self:'undefined'!==typeof window?window:'undefined'!==typeof _0x4ce9f1?_0x4ce9f1:this,function(_0x2931cf){'use strict';_0x2931cf=_0x2931cf||{};var _0x3fa917=_0x2931cf['Base64'],_0xc3df37='2.5.1',_0x17926;if(_0x2bdb8a['exports'])try{_0x17926=eval('require(\'buffer\')[\'Buffer\'];');}catch(_0x32f0f2){_0x17926=void 0x0;}var _0x417579='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',_0x4298d9=function(_0x4640e6){for(var _0x38803b={},_0x1c5d9f=0x0,_0x5e1277=_0x4640e6['length'];_0x1c5d9f<_0x5e1277;_0x1c5d9f++)_0x38803b[_0x4640e6['charAt'](_0x1c5d9f)]=_0x1c5d9f;return _0x38803b;}(_0x417579),_0x3139f6=String['fromCharCode'],_0x2eaa23=function(_0x4df29c){if(_0x4df29c['length']<0x2){var _0x560718=_0x4df29c['charCodeAt'](0x0);return _0x560718<0x80?_0x4df29c:_0x560718<0x800?_0x3139f6(0xc0|_0x560718>>>0x6)+_0x3139f6(0x80|0x3f&_0x560718):_0x3139f6(0xe0|_0x560718>>>0xc&0xf)+_0x3139f6(0x80|_0x560718>>>0x6&0x3f)+_0x3139f6(0x80|0x3f&_0x560718);}_0x560718=0x10000+0x400*(_0x4df29c['charCodeAt'](0x0)-0xd800)+(_0x4df29c['charCodeAt'](0x1)-0xdc00);return _0x3139f6(0xf0|_0x560718>>>0x12&0x7)+_0x3139f6(0x80|_0x560718>>>0xc&0x3f)+_0x3139f6(0x80|_0x560718>>>0x6&0x3f)+_0x3139f6(0x80|0x3f&_0x560718);},_0x1c5ffc=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,_0x5d413d=function(_0x5bada1){return _0x5bada1['replace'](_0x1c5ffc,_0x2eaa23);},_0x3891cc=function(_0x1f0b19){var _0x520b1f=[0x0,0x2,0x1][_0x1f0b19['length']%0x3],_0x5a8abb=_0x1f0b19['charCodeAt'](0x0)<<0x10|(_0x1f0b19['length']>0x1?_0x1f0b19['charCodeAt'](0x1):0x0)<<0x8|(_0x1f0b19['length']>0x2?_0x1f0b19['charCodeAt'](0x2):0x0),_0x29494c=[_0x417579['charAt'](_0x5a8abb>>>0x12),_0x417579['charAt'](_0x5a8abb>>>0xc&0x3f),_0x520b1f>=0x2?'=':_0x417579['charAt'](_0x5a8abb>>>0x6&0x3f),_0x520b1f>=0x1?'=':_0x417579['charAt'](0x3f&_0x5a8abb)];return _0x29494c['join']('');},_0x243a2c=_0x2931cf['btoa']?function(_0x17bb1d){return _0x2931cf['btoa'](_0x17bb1d);}:function(_0x4f379c){return _0x4f379c['replace'](/[\s\S]{1,3}/g,_0x3891cc);},_0x3b6a4b=_0x17926?_0x17926['from']&&Uint8Array&&_0x17926['from']!==Uint8Array['from']?function(_0x5ab6f7){return(_0x5ab6f7['constructor']===_0x17926['constructor']?_0x5ab6f7:_0x17926['from'](_0x5ab6f7))['toString']('base64');}:function(_0x6dece9){return(_0x6dece9['constructor']===_0x17926['constructor']?_0x6dece9:new _0x17926(_0x6dece9))['toString']('base64');}:function(_0x3d0c02){return _0x243a2c(_0x5d413d(_0x3d0c02));},_0x45ae25=function(_0x53d700,_0x4a7a4c){return _0x4a7a4c?_0x3b6a4b(String(_0x53d700))['replace'](/[+\/]/g,function(_0x4f1724){return'+'==_0x4f1724?'-':'_';})['replace'](/=/g,''):_0x3b6a4b(String(_0x53d700));},_0x3a12d2=function(_0xe66ff){return _0x45ae25(_0xe66ff,!0x0);},_0x2f4f92=new RegExp(['[À-ß][-¿]','[à-ï][-¿]{2}','[ð-÷][-¿]{3}']['join']('|'),'g'),_0x2127af=function(_0x66c788){switch(_0x66c788['length']){case 0x4:var _0x19dbbd=(0x7&_0x66c788['charCodeAt'](0x0))<<0x12|(0x3f&_0x66c788['charCodeAt'](0x1))<<0xc|(0x3f&_0x66c788['charCodeAt'](0x2))<<0x6|0x3f&_0x66c788['charCodeAt'](0x3),_0x5c417e=_0x19dbbd-0x10000;return _0x3139f6(0xd800+(_0x5c417e>>>0xa))+_0x3139f6(0xdc00+(0x3ff&_0x5c417e));case 0x3:return _0x3139f6((0xf&_0x66c788['charCodeAt'](0x0))<<0xc|(0x3f&_0x66c788['charCodeAt'](0x1))<<0x6|0x3f&_0x66c788['charCodeAt'](0x2));default:return _0x3139f6((0x1f&_0x66c788['charCodeAt'](0x0))<<0x6|0x3f&_0x66c788['charCodeAt'](0x1));}},_0x37c71a=function(_0x3fd6c3){return _0x3fd6c3['replace'](_0x2f4f92,_0x2127af);},_0x5dc711=function(_0x5c61cc){var _0xe9e513=_0x5c61cc['length'],_0x122bf0=_0xe9e513%0x4,_0x5a1765=(_0xe9e513>0x0?_0x4298d9[_0x5c61cc['charAt'](0x0)]<<0x12:0x0)|(_0xe9e513>0x1?_0x4298d9[_0x5c61cc['charAt'](0x1)]<<0xc:0x0)|(_0xe9e513>0x2?_0x4298d9[_0x5c61cc['charAt'](0x2)]<<0x6:0x0)|(_0xe9e513>0x3?_0x4298d9[_0x5c61cc['charAt'](0x3)]:0x0),_0xf6cf4=[_0x3139f6(_0x5a1765>>>0x10),_0x3139f6(_0x5a1765>>>0x8&0xff),_0x3139f6(0xff&_0x5a1765)];return _0xf6cf4['length']-=[0x0,0x0,0x2,0x1][_0x122bf0],_0xf6cf4['join']('');},_0x34c4d2=_0x2931cf['atob']?function(_0x395d84){return _0x2931cf['atob'](_0x395d84);}:function(_0x4c9aa6){return _0x4c9aa6['replace'](/\S{1,4}/g,_0x5dc711);},_0x4a25fc=function(_0x3f8057){return _0x34c4d2(String(_0x3f8057)['replace'](/[^A-Za-z0-9\+\/]/g,''));},_0x2586ba=_0x17926?_0x17926['from']&&Uint8Array&&_0x17926['from']!==Uint8Array['from']?function(_0x336dce){return(_0x336dce['constructor']===_0x17926['constructor']?_0x336dce:_0x17926['from'](_0x336dce,'base64'))['toString']();}:function(_0x29eef9){return(_0x29eef9['constructor']===_0x17926['constructor']?_0x29eef9:new _0x17926(_0x29eef9,'base64'))['toString']();}:function(_0x4cee3c){return _0x37c71a(_0x34c4d2(_0x4cee3c));},_0x219a63=function(_0x410f37){return _0x2586ba(String(_0x410f37)['replace'](/[-_]/g,function(_0x2ab17b){return'-'==_0x2ab17b?'+':'/';})['replace'](/[^A-Za-z0-9\+\/]/g,''));},_0x3c9d66=function(){var _0x57d2fc=_0x2931cf['Base64'];return _0x2931cf['Base64']=_0x3fa917,_0x57d2fc;};if(_0x2931cf['Base64']={'VERSION':_0xc3df37,'atob':_0x4a25fc,'btoa':_0x243a2c,'fromBase64':_0x219a63,'toBase64':_0x45ae25,'utob':_0x5d413d,'encode':_0x45ae25,'encodeURI':_0x3a12d2,'btou':_0x37c71a,'decode':_0x219a63,'noConflict':_0x3c9d66,'__buffer__':_0x17926},'function'===typeof Object['defineProperty']){var _0x21e418=function(_0x3ddc3c){return{'value':_0x3ddc3c,'enumerable':!0x1,'writable':!0x0,'configurable':!0x0};};_0x2931cf['Base64']['extendString']=function(){Object['defineProperty'](String['prototype'],'fromBase64',_0x21e418(function(){return _0x219a63(this);})),Object['defineProperty'](String['prototype'],'toBase64',_0x21e418(function(_0x12dc48){return _0x45ae25(this,_0x12dc48);})),Object['defineProperty'](String['prototype'],'toBase64URI',_0x21e418(function(){return _0x45ae25(this,!0x0);}));};}return _0x2931cf['Meteor']&&(Base64=_0x2931cf['Base64']),_0x2bdb8a['exports']?_0x2bdb8a['exports']['Base64']=_0x2931cf['Base64']:(_0x3c6e10=[],_0xf71c54=function(){return _0x2931cf['Base64'];}['apply'](_0x349d42,_0x3c6e10),void 0x0===_0xf71c54||(_0x2bdb8a['exports']=_0xf71c54)),{'Base64':_0x2931cf['Base64']};}));}['call'](this,_0x247eec('c8ba')));},'2a66':function(_0x3b6c86,_0x1c3047,_0x1dca98){(function(_0x1a77f7,_0x47fae4,_0x33b5f0){_0x3b6c86['exports']=_0x47fae4(_0x1dca98('21bf'),_0x1dca98('38ba'));}(0x0,function(_0x32ed62){return _0x32ed62['pad']['ZeroPadding']={'pad':function(_0x2e059c,_0x131350){var _0x1ff0ab=0x4*_0x131350;_0x2e059c['clamp'](),_0x2e059c['sigBytes']+=_0x1ff0ab-(_0x2e059c['sigBytes']%_0x1ff0ab||_0x1ff0ab);},'unpad':function(_0x234488){var _0x9fff5f=_0x234488['words'],_0x523dfc=_0x234488['sigBytes']-0x1;while(!(_0x9fff5f[_0x523dfc>>>0x2]>>>0x18-_0x523dfc%0x4*0x8&0xff))_0x523dfc--;_0x234488['sigBytes']=_0x523dfc+0x1;}},_0x32ed62['pad']['ZeroPadding'];}));},'2b79':function(_0x485b61,_0x37055d,_0x10afae){(function(_0x4e623b,_0x4c1d8c,_0x32002a){_0x485b61['exports']=_0x4c1d8c(_0x10afae('21bf'),_0x10afae('df2f'),_0x10afae('5980'));}(0x0,function(_0x1aefa5){return function(){var _0x2b60c7=_0x1aefa5,_0x273d15=_0x2b60c7['lib'],_0x3d7a55=_0x273d15['Base'],_0x5400f8=_0x273d15['WordArray'],_0x4a87c4=_0x2b60c7['algo'],_0x11d264=_0x4a87c4['MD5'],_0x3a2900=_0x4a87c4['EvpKDF']=_0x3d7a55['extend']({'cfg':_0x3d7a55['extend']({'keySize':0x4,'hasher':_0x11d264,'iterations':0x1}),'init':function(_0x616c6){this['cfg']=this['cfg']['extend'](_0x616c6);},'compute':function(_0x8b84b9,_0xa48a42){var _0x3207bd=this['cfg'],_0x4d3b20=_0x3207bd['hasher']['create'](),_0x585b3e=_0x5400f8['create'](),_0x44179d=_0x585b3e['words'],_0x374f7f=_0x3207bd['keySize'],_0x5fd695=_0x3207bd['iterations'];while(_0x44179d['length']<_0x374f7f){_0x454637&&_0x4d3b20['update'](_0x454637);var _0x454637=_0x4d3b20['update'](_0x8b84b9)['finalize'](_0xa48a42);_0x4d3b20['reset']();for(var _0x1434a8=0x1;_0x1434a8<_0x5fd695;_0x1434a8++)_0x454637=_0x4d3b20['finalize'](_0x454637),_0x4d3b20['reset']();_0x585b3e['concat'](_0x454637);}return _0x585b3e['sigBytes']=0x4*_0x374f7f,_0x585b3e;}});_0x2b60c7['EvpKDF']=function(_0x41f429,_0x5bddd2,_0x502b19){return _0x3a2900['create'](_0x502b19)['compute'](_0x41f429,_0x5bddd2);};}(),_0x1aefa5['EvpKDF'];}));},3252:function(_0x1808cb,_0x4fd3b2,_0x2d4265){(function(_0xceab5a,_0x452b7c){_0x1808cb['exports']=_0x452b7c(_0x2d4265('21bf'));}(0x0,function(_0x2f739c){return function(_0x1764a2){var _0x515091=_0x2f739c,_0x7a2c02=_0x515091['lib'],_0x27d271=_0x7a2c02['Base'],_0x557fb2=_0x7a2c02['WordArray'],_0x5399f5=_0x515091['x64']={};_0x5399f5['Word']=_0x27d271['extend']({'init':function(_0x1e2ca7,_0x4155ce){this['high']=_0x1e2ca7,this['low']=_0x4155ce;}}),_0x5399f5['WordArray']=_0x27d271['extend']({'init':function(_0x4f920c,_0x2150a8){_0x4f920c=this['words']=_0x4f920c||[],this['sigBytes']=_0x2150a8!=_0x1764a2?_0x2150a8:0x8*_0x4f920c['length'];},'toX32':function(){for(var _0x445de3=this['words'],_0x594812=_0x445de3['length'],_0x5000db=[],_0x2dd496=0x0;_0x2dd496<_0x594812;_0x2dd496++){var _0x51f9c0=_0x445de3[_0x2dd496];_0x5000db['push'](_0x51f9c0['high']),_0x5000db['push'](_0x51f9c0['low']);}return _0x557fb2['create'](_0x5000db,this['sigBytes']);},'clone':function(){for(var _0x3c220e=_0x27d271['clone']['call'](this),_0x5077a7=_0x3c220e['words']=this['words']['slice'](0x0),_0x2ec29a=_0x5077a7['length'],_0x4932e6=0x0;_0x4932e6<_0x2ec29a;_0x4932e6++)_0x5077a7[_0x4932e6]=_0x5077a7[_0x4932e6]['clone']();return _0x3c220e;}});}(),_0x2f739c;}));},3452:function(_0x30ba7f,_0x547a23,_0x3a1308){(function(_0xbb0a85,_0xe15a10,_0x39e9de){_0x30ba7f['exports']=_0xe15a10(_0x3a1308('21bf'),_0x3a1308('3252'),_0x3a1308('17e1'),_0x3a1308('a8ce'),_0x3a1308('1132'),_0x3a1308('72fe'),_0x3a1308('df2f'),_0x3a1308('94f8'),_0x3a1308('191b'),_0x3a1308('d6e6'),_0x3a1308('b86b'),_0x3a1308('e61b'),_0x3a1308('10b7'),_0x3a1308('5980'),_0x3a1308('7bbc'),_0x3a1308('2b79'),_0x3a1308('38ba'),_0x3a1308('00bb'),_0x3a1308('f4ea'),_0x3a1308('aaef'),_0x3a1308('4ba9'),_0x3a1308('81bf'),_0x3a1308('a817'),_0x3a1308('a11b'),_0x3a1308('8cef'),_0x3a1308('2a66'),_0x3a1308('b86c'),_0x3a1308('6d08'),_0x3a1308('c198'),_0x3a1308('a40e'),_0x3a1308('c3b6'),_0x3a1308('1382'),_0x3a1308('3d5a'));}(0x0,function(_0x29ea8f){return _0x29ea8f;}));},3846:function(_0x121ec1,_0x157520,_0x19c084){_0x19c084('9e1e')&&'g'!=/./g['flags']&&_0x19c084('86cc')['f'](RegExp['prototype'],'flags',{'configurable':!0x0,'get':_0x19c084('0bfb')});},'38ba':function(_0x2cc469,_0x4de3aa,_0x5ebfa7){(function(_0x499e88,_0xea2bea,_0x3ec46b){_0x2cc469['exports']=_0xea2bea(_0x5ebfa7('21bf'),_0x5ebfa7('2b79'));}(0x0,function(_0x14f3da){_0x14f3da['lib']['Cipher']||function(_0xc2faac){var _0x438a58=_0x14f3da,_0xa5f483=_0x438a58['lib'],_0xf87cb5=_0xa5f483['Base'],_0x157bc3=_0xa5f483['WordArray'],_0x472516=_0xa5f483['BufferedBlockAlgorithm'],_0x2258b0=_0x438a58['enc'],_0x54111c=(_0x2258b0['Utf8'],_0x2258b0['Base64']),_0x33471c=_0x438a58['algo'],_0x37d0fd=_0x33471c['EvpKDF'],_0x5c6020=_0xa5f483['Cipher']=_0x472516['extend']({'cfg':_0xf87cb5['extend'](),'createEncryptor':function(_0xd1a62f,_0x4ed55b){return this['create'](this['_ENC_XFORM_MODE'],_0xd1a62f,_0x4ed55b);},'createDecryptor':function(_0x134a91,_0x5687c4){return this['create'](this['_DEC_XFORM_MODE'],_0x134a91,_0x5687c4);},'init':function(_0xc0933f,_0x5b215b,_0x5d5bd0){this['cfg']=this['cfg']['extend'](_0x5d5bd0),this['_xformMode']=_0xc0933f,this['_key']=_0x5b215b,this['reset']();},'reset':function(){_0x472516['reset']['call'](this),this['_doReset']();},'process':function(_0x10342f){return this['_append'](_0x10342f),this['_process']();},'finalize':function(_0x317c08){_0x317c08&&this['_append'](_0x317c08);var _0x31e4b9=this['_doFinalize']();return _0x31e4b9;},'keySize':0x4,'ivSize':0x4,'_ENC_XFORM_MODE':0x1,'_DEC_XFORM_MODE':0x2,'_createHelper':function(){function _0x4d32df(_0x2f02fa){return'string'==typeof _0x2f02fa?_0x51f071:_0x707b4a;}return function(_0x39584c){return{'encrypt':function(_0x58ed47,_0x5099c5,_0x2ad646){return _0x4d32df(_0x5099c5)['encrypt'](_0x39584c,_0x58ed47,_0x5099c5,_0x2ad646);},'decrypt':function(_0x51675c,_0x5bf815,_0x7a5135){return _0x4d32df(_0x5bf815)['decrypt'](_0x39584c,_0x51675c,_0x5bf815,_0x7a5135);}};};}()}),_0x56611e=(_0xa5f483['StreamCipher']=_0x5c6020['extend']({'_doFinalize':function(){var _0xd52196=this['_process'](!0x0);return _0xd52196;},'blockSize':0x1}),_0x438a58['mode']={}),_0x2e52e6=_0xa5f483['BlockCipherMode']=_0xf87cb5['extend']({'createEncryptor':function(_0x20f0f6,_0x457a24){return this['Encryptor']['create'](_0x20f0f6,_0x457a24);},'createDecryptor':function(_0x36fac0,_0x599c8d){return this['Decryptor']['create'](_0x36fac0,_0x599c8d);},'init':function(_0x3baa66,_0x22065e){this['_cipher']=_0x3baa66,this['_iv']=_0x22065e;}}),_0x2bb128=_0x56611e['CBC']=function(){var _0x19e7c7=_0x2e52e6['extend']();function _0x28ffe2(_0x3c744e,_0x57dae3,_0x432d8e){var _0x243d9e=this['_iv'];if(_0x243d9e){var _0x5642a0=_0x243d9e;this['_iv']=_0xc2faac;}else _0x5642a0=this['_prevBlock'];for(var _0x2dd4f9=0x0;_0x2dd4f9<_0x432d8e;_0x2dd4f9++)_0x3c744e[_0x57dae3+_0x2dd4f9]^=_0x5642a0[_0x2dd4f9];}return _0x19e7c7['Encryptor']=_0x19e7c7['extend']({'processBlock':function(_0x1a887a,_0x3c417a){var _0x52c8b3=this['_cipher'],_0x2ecc44=_0x52c8b3['blockSize'];_0x28ffe2['call'](this,_0x1a887a,_0x3c417a,_0x2ecc44),_0x52c8b3['encryptBlock'](_0x1a887a,_0x3c417a),this['_prevBlock']=_0x1a887a['slice'](_0x3c417a,_0x3c417a+_0x2ecc44);}}),_0x19e7c7['Decryptor']=_0x19e7c7['extend']({'processBlock':function(_0x5b4c30,_0x55577b){var _0x34d718=this['_cipher'],_0x57551e=_0x34d718['blockSize'],_0x28cdea=_0x5b4c30['slice'](_0x55577b,_0x55577b+_0x57551e);_0x34d718['decryptBlock'](_0x5b4c30,_0x55577b),_0x28ffe2['call'](this,_0x5b4c30,_0x55577b,_0x57551e),this['_prevBlock']=_0x28cdea;}}),_0x19e7c7;}(),_0x18417e=_0x438a58['pad']={},_0x2810fd=_0x18417e['Pkcs7']={'pad':function(_0x368fb0,_0x4c4a69){for(var _0x1fd049=0x4*_0x4c4a69,_0x261fab=_0x1fd049-_0x368fb0['sigBytes']%_0x1fd049,_0x5b38e7=_0x261fab<<0x18|_0x261fab<<0x10|_0x261fab<<0x8|_0x261fab,_0x2d3607=[],_0x358c94=0x0;_0x358c94<_0x261fab;_0x358c94+=0x4)_0x2d3607['push'](_0x5b38e7);var _0x5d9a65=_0x157bc3['create'](_0x2d3607,_0x261fab);_0x368fb0['concat'](_0x5d9a65);},'unpad':function(_0x4070a0){var _0x3113d3=0xff&_0x4070a0['words'][_0x4070a0['sigBytes']-0x1>>>0x2];_0x4070a0['sigBytes']-=_0x3113d3;}},_0x31bf2d=(_0xa5f483['BlockCipher']=_0x5c6020['extend']({'cfg':_0x5c6020['cfg']['extend']({'mode':_0x2bb128,'padding':_0x2810fd}),'reset':function(){_0x5c6020['reset']['call'](this);var _0x5d9775=this['cfg'],_0xce5523=_0x5d9775['iv'],_0x283619=_0x5d9775['mode'];if(this['_xformMode']==this['_ENC_XFORM_MODE'])var _0x1d78c0=_0x283619['createEncryptor'];else{_0x1d78c0=_0x283619['createDecryptor'];this['_minBufferSize']=0x1;}this['_mode']&&this['_mode']['__creator']==_0x1d78c0?this['_mode']['init'](this,_0xce5523&&_0xce5523['words']):(this['_mode']=_0x1d78c0['call'](_0x283619,this,_0xce5523&&_0xce5523['words']),this['_mode']['__creator']=_0x1d78c0);},'_doProcessBlock':function(_0x533320,_0x111868){this['_mode']['processBlock'](_0x533320,_0x111868);},'_doFinalize':function(){var _0x47043e=this['cfg']['padding'];if(this['_xformMode']==this['_ENC_XFORM_MODE']){_0x47043e['pad'](this['_data'],this['blockSize']);var _0x326a91=this['_process'](!0x0);}else{_0x326a91=this['_process'](!0x0);_0x47043e['unpad'](_0x326a91);}return _0x326a91;},'blockSize':0x4}),_0xa5f483['CipherParams']=_0xf87cb5['extend']({'init':function(_0x2fcf08){this['mixIn'](_0x2fcf08);},'toString':function(_0x523af5){return(_0x523af5||this['formatter'])['stringify'](this);}})),_0x204dd8=_0x438a58['format']={},_0x25a670=_0x204dd8['OpenSSL']={'stringify':function(_0x376867){var _0x5c22bb=_0x376867['ciphertext'],_0x33f188=_0x376867['salt'];if(_0x33f188)var _0x2355b7=_0x157bc3['create']([0x53616c74,0x65645f5f])['concat'](_0x33f188)['concat'](_0x5c22bb);else _0x2355b7=_0x5c22bb;return _0x2355b7['toString'](_0x54111c);},'parse':function(_0x4357ff){var _0x1ac2c2=_0x54111c['parse'](_0x4357ff),_0x30e1e1=_0x1ac2c2['words'];if(0x53616c74==_0x30e1e1[0x0]&&0x65645f5f==_0x30e1e1[0x1]){var _0x4df7cc=_0x157bc3['create'](_0x30e1e1['slice'](0x2,0x4));_0x30e1e1['splice'](0x0,0x4),_0x1ac2c2['sigBytes']-=0x10;}return _0x31bf2d['create']({'ciphertext':_0x1ac2c2,'salt':_0x4df7cc});}},_0x707b4a=_0xa5f483['SerializableCipher']=_0xf87cb5['extend']({'cfg':_0xf87cb5['extend']({'format':_0x25a670}),'encrypt':function(_0x5974aa,_0x3854a6,_0x5044c7,_0x3be07f){_0x3be07f=this['cfg']['extend'](_0x3be07f);var _0x4a8d07=_0x5974aa['createEncryptor'](_0x5044c7,_0x3be07f),_0x19dd62=_0x4a8d07['finalize'](_0x3854a6),_0x546f94=_0x4a8d07['cfg'];return _0x31bf2d['create']({'ciphertext':_0x19dd62,'key':_0x5044c7,'iv':_0x546f94['iv'],'algorithm':_0x5974aa,'mode':_0x546f94['mode'],'padding':_0x546f94['padding'],'blockSize':_0x5974aa['blockSize'],'formatter':_0x3be07f['format']});},'decrypt':function(_0x592bb0,_0x54017e,_0x3bffe1,_0x3ba76c){_0x3ba76c=this['cfg']['extend'](_0x3ba76c),_0x54017e=this['_parse'](_0x54017e,_0x3ba76c['format']);var _0x3645cc=_0x592bb0['createDecryptor'](_0x3bffe1,_0x3ba76c)['finalize'](_0x54017e['ciphertext']);return _0x3645cc;},'_parse':function(_0x2f58c6,_0x533d86){return'string'==typeof _0x2f58c6?_0x533d86['parse'](_0x2f58c6,this):_0x2f58c6;}}),_0x21b32c=_0x438a58['kdf']={},_0x27f61b=_0x21b32c['OpenSSL']={'execute':function(_0x1e511e,_0x25630d,_0x2350e0,_0x5112c3){_0x5112c3||(_0x5112c3=_0x157bc3['random'](0x8));var _0x230c22=_0x37d0fd['create']({'keySize':_0x25630d+_0x2350e0})['compute'](_0x1e511e,_0x5112c3),_0x531ea7=_0x157bc3['create'](_0x230c22['words']['slice'](_0x25630d),0x4*_0x2350e0);return _0x230c22['sigBytes']=0x4*_0x25630d,_0x31bf2d['create']({'key':_0x230c22,'iv':_0x531ea7,'salt':_0x5112c3});}},_0x51f071=_0xa5f483['PasswordBasedCipher']=_0x707b4a['extend']({'cfg':_0x707b4a['cfg']['extend']({'kdf':_0x27f61b}),'encrypt':function(_0x21e3eb,_0x539633,_0x3c4b61,_0x2bd3f4){_0x2bd3f4=this['cfg']['extend'](_0x2bd3f4);var _0x4f14c3=_0x2bd3f4['kdf']['execute'](_0x3c4b61,_0x21e3eb['keySize'],_0x21e3eb['ivSize']);_0x2bd3f4['iv']=_0x4f14c3['iv'];var _0x5e6b77=_0x707b4a['encrypt']['call'](this,_0x21e3eb,_0x539633,_0x4f14c3['key'],_0x2bd3f4);return _0x5e6b77['mixIn'](_0x4f14c3),_0x5e6b77;},'decrypt':function(_0x5e99d6,_0x4ce269,_0x1e8a3b,_0x48cddb){_0x48cddb=this['cfg']['extend'](_0x48cddb),_0x4ce269=this['_parse'](_0x4ce269,_0x48cddb['format']);var _0x40b550=_0x48cddb['kdf']['execute'](_0x1e8a3b,_0x5e99d6['keySize'],_0x5e99d6['ivSize'],_0x4ce269['salt']);_0x48cddb['iv']=_0x40b550['iv'];var _0x47b138=_0x707b4a['decrypt']['call'](this,_0x5e99d6,_0x4ce269,_0x40b550['key'],_0x48cddb);return _0x47b138;}});}();}));},'3d5a':function(_0x4bb965,_0x300c84,_0x4ad8f9){(function(_0x1eb710,_0x28193d,_0x2ac460){_0x4bb965['exports']=_0x28193d(_0x4ad8f9('21bf'),_0x4ad8f9('1132'),_0x4ad8f9('72fe'),_0x4ad8f9('2b79'),_0x4ad8f9('38ba'));}(0x0,function(_0xa10a68){return function(){var _0x43b0bd=_0xa10a68,_0x3f59f6=_0x43b0bd['lib'],_0x29a5ce=_0x3f59f6['StreamCipher'],_0x318c0b=_0x43b0bd['algo'],_0x5e48e1=[],_0x5d9967=[],_0x3e16d5=[],_0x8cdeae=_0x318c0b['RabbitLegacy']=_0x29a5ce['extend']({'_doReset':function(){var _0x3c3ad3=this['_key']['words'],_0x375a8b=this['cfg']['iv'],_0x461a88=this['_X']=[_0x3c3ad3[0x0],_0x3c3ad3[0x3]<<0x10|_0x3c3ad3[0x2]>>>0x10,_0x3c3ad3[0x1],_0x3c3ad3[0x0]<<0x10|_0x3c3ad3[0x3]>>>0x10,_0x3c3ad3[0x2],_0x3c3ad3[0x1]<<0x10|_0x3c3ad3[0x0]>>>0x10,_0x3c3ad3[0x3],_0x3c3ad3[0x2]<<0x10|_0x3c3ad3[0x1]>>>0x10],_0xf73bb7=this['_C']=[_0x3c3ad3[0x2]<<0x10|_0x3c3ad3[0x2]>>>0x10,0xffff0000&_0x3c3ad3[0x0]|0xffff&_0x3c3ad3[0x1],_0x3c3ad3[0x3]<<0x10|_0x3c3ad3[0x3]>>>0x10,0xffff0000&_0x3c3ad3[0x1]|0xffff&_0x3c3ad3[0x2],_0x3c3ad3[0x0]<<0x10|_0x3c3ad3[0x0]>>>0x10,0xffff0000&_0x3c3ad3[0x2]|0xffff&_0x3c3ad3[0x3],_0x3c3ad3[0x1]<<0x10|_0x3c3ad3[0x1]>>>0x10,0xffff0000&_0x3c3ad3[0x3]|0xffff&_0x3c3ad3[0x0]];this['_b']=0x0;for(var _0x1f9432=0x0;_0x1f9432<0x4;_0x1f9432++)_0x397a2b['call'](this);for(_0x1f9432=0x0;_0x1f9432<0x8;_0x1f9432++)_0xf73bb7[_0x1f9432]^=_0x461a88[_0x1f9432+0x4&0x7];if(_0x375a8b){var _0x31a167=_0x375a8b['words'],_0x3f4a73=_0x31a167[0x0],_0x4afe44=_0x31a167[0x1],_0x15adab=0xff00ff&(_0x3f4a73<<0x8|_0x3f4a73>>>0x18)|0xff00ff00&(_0x3f4a73<<0x18|_0x3f4a73>>>0x8),_0x9d5207=0xff00ff&(_0x4afe44<<0x8|_0x4afe44>>>0x18)|0xff00ff00&(_0x4afe44<<0x18|_0x4afe44>>>0x8),_0x2c8738=_0x15adab>>>0x10|0xffff0000&_0x9d5207,_0x1f0d23=_0x9d5207<<0x10|0xffff&_0x15adab;_0xf73bb7[0x0]^=_0x15adab,_0xf73bb7[0x1]^=_0x2c8738,_0xf73bb7[0x2]^=_0x9d5207,_0xf73bb7[0x3]^=_0x1f0d23,_0xf73bb7[0x4]^=_0x15adab,_0xf73bb7[0x5]^=_0x2c8738,_0xf73bb7[0x6]^=_0x9d5207,_0xf73bb7[0x7]^=_0x1f0d23;for(_0x1f9432=0x0;_0x1f9432<0x4;_0x1f9432++)_0x397a2b['call'](this);}},'_doProcessBlock':function(_0x10a76d,_0x3f4755){var _0x29fb04=this['_X'];_0x397a2b['call'](this),_0x5e48e1[0x0]=_0x29fb04[0x0]^_0x29fb04[0x5]>>>0x10^_0x29fb04[0x3]<<0x10,_0x5e48e1[0x1]=_0x29fb04[0x2]^_0x29fb04[0x7]>>>0x10^_0x29fb04[0x5]<<0x10,_0x5e48e1[0x2]=_0x29fb04[0x4]^_0x29fb04[0x1]>>>0x10^_0x29fb04[0x7]<<0x10,_0x5e48e1[0x3]=_0x29fb04[0x6]^_0x29fb04[0x3]>>>0x10^_0x29fb04[0x1]<<0x10;for(var _0x353444=0x0;_0x353444<0x4;_0x353444++)_0x5e48e1[_0x353444]=0xff00ff&(_0x5e48e1[_0x353444]<<0x8|_0x5e48e1[_0x353444]>>>0x18)|0xff00ff00&(_0x5e48e1[_0x353444]<<0x18|_0x5e48e1[_0x353444]>>>0x8),_0x10a76d[_0x3f4755+_0x353444]^=_0x5e48e1[_0x353444];},'blockSize':0x4,'ivSize':0x2});function _0x397a2b(){for(var _0x548f17=this['_X'],_0x4c683f=this['_C'],_0x14f318=0x0;_0x14f318<0x8;_0x14f318++)_0x5d9967[_0x14f318]=_0x4c683f[_0x14f318];_0x4c683f[0x0]=_0x4c683f[0x0]+0x4d34d34d+this['_b']|0x0,_0x4c683f[0x1]=_0x4c683f[0x1]+0xd34d34d3+(_0x4c683f[0x0]>>>0x0<_0x5d9967[0x0]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x2]=_0x4c683f[0x2]+0x34d34d34+(_0x4c683f[0x1]>>>0x0<_0x5d9967[0x1]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x3]=_0x4c683f[0x3]+0x4d34d34d+(_0x4c683f[0x2]>>>0x0<_0x5d9967[0x2]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x4]=_0x4c683f[0x4]+0xd34d34d3+(_0x4c683f[0x3]>>>0x0<_0x5d9967[0x3]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x5]=_0x4c683f[0x5]+0x34d34d34+(_0x4c683f[0x4]>>>0x0<_0x5d9967[0x4]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x6]=_0x4c683f[0x6]+0x4d34d34d+(_0x4c683f[0x5]>>>0x0<_0x5d9967[0x5]>>>0x0?0x1:0x0)|0x0,_0x4c683f[0x7]=_0x4c683f[0x7]+0xd34d34d3+(_0x4c683f[0x6]>>>0x0<_0x5d9967[0x6]>>>0x0?0x1:0x0)|0x0,this['_b']=_0x4c683f[0x7]>>>0x0<_0x5d9967[0x7]>>>0x0?0x1:0x0;for(_0x14f318=0x0;_0x14f318<0x8;_0x14f318++){var _0x510ef1=_0x548f17[_0x14f318]+_0x4c683f[_0x14f318],_0x5c2d86=0xffff&_0x510ef1,_0x4f854c=_0x510ef1>>>0x10,_0x3c0f47=((_0x5c2d86*_0x5c2d86>>>0x11)+_0x5c2d86*_0x4f854c>>>0xf)+_0x4f854c*_0x4f854c,_0x581b0e=((0xffff0000&_0x510ef1)*_0x510ef1|0x0)+((0xffff&_0x510ef1)*_0x510ef1|0x0);_0x3e16d5[_0x14f318]=_0x3c0f47^_0x581b0e;}_0x548f17[0x0]=_0x3e16d5[0x0]+(_0x3e16d5[0x7]<<0x10|_0x3e16d5[0x7]>>>0x10)+(_0x3e16d5[0x6]<<0x10|_0x3e16d5[0x6]>>>0x10)|0x0,_0x548f17[0x1]=_0x3e16d5[0x1]+(_0x3e16d5[0x0]<<0x8|_0x3e16d5[0x0]>>>0x18)+_0x3e16d5[0x7]|0x0,_0x548f17[0x2]=_0x3e16d5[0x2]+(_0x3e16d5[0x1]<<0x10|_0x3e16d5[0x1]>>>0x10)+(_0x3e16d5[0x0]<<0x10|_0x3e16d5[0x0]>>>0x10)|0x0,_0x548f17[0x3]=_0x3e16d5[0x3]+(_0x3e16d5[0x2]<<0x8|_0x3e16d5[0x2]>>>0x18)+_0x3e16d5[0x1]|0x0,_0x548f17[0x4]=_0x3e16d5[0x4]+(_0x3e16d5[0x3]<<0x10|_0x3e16d5[0x3]>>>0x10)+(_0x3e16d5[0x2]<<0x10|_0x3e16d5[0x2]>>>0x10)|0x0,_0x548f17[0x5]=_0x3e16d5[0x5]+(_0x3e16d5[0x4]<<0x8|_0x3e16d5[0x4]>>>0x18)+_0x3e16d5[0x3]|0x0,_0x548f17[0x6]=_0x3e16d5[0x6]+(_0x3e16d5[0x5]<<0x10|_0x3e16d5[0x5]>>>0x10)+(_0x3e16d5[0x4]<<0x10|_0x3e16d5[0x4]>>>0x10)|0x0,_0x548f17[0x7]=_0x3e16d5[0x7]+(_0x3e16d5[0x6]<<0x8|_0x3e16d5[0x6]>>>0x18)+_0x3e16d5[0x5]|0x0;}_0x43b0bd['RabbitLegacy']=_0x29a5ce['_createHelper'](_0x8cdeae);}(),_0xa10a68['RabbitLegacy'];}));},'3e22':function(_0x324930,_0x39029a,_0x51eef7){'use strict';_0x51eef7('6b54');var _0x11a046='ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb',_0x165112=_0x51eef7('27ae')['Base64'];function _0x2fa8f6(_0x177944){var _0x2c4f17=_0x11a046+_0x177944['toString']();return _0x165112['encode'](_0x2c4f17);}_0x39029a['a']=_0x2fa8f6;},'4ba9':function(_0x193fef,_0x86337a,_0x5e63de){(function(_0x198656,_0x1a8ff8,_0x10c365){_0x193fef['exports']=_0x1a8ff8(_0x5e63de('21bf'),_0x5e63de('38ba'));}(0x0,function(_0x316059){return _0x316059['mode']['OFB']=function(){var _0x50fc21=_0x316059['lib']['BlockCipherMode']['extend'](),_0x36ef3c=_0x50fc21['Encryptor']=_0x50fc21['extend']({'processBlock':function(_0x4879a6,_0x39cc7a){var _0x3a3256=this['_cipher'],_0x1e82d6=_0x3a3256['blockSize'],_0x18b1f5=this['_iv'],_0x1ea34c=this['_keystream'];_0x18b1f5&&(_0x1ea34c=this['_keystream']=_0x18b1f5['slice'](0x0),this['_iv']=void 0x0),_0x3a3256['encryptBlock'](_0x1ea34c,0x0);for(var _0x2ad02f=0x0;_0x2ad02f<_0x1e82d6;_0x2ad02f++)_0x4879a6[_0x39cc7a+_0x2ad02f]^=_0x1ea34c[_0x2ad02f];}});return _0x50fc21['Decryptor']=_0x36ef3c,_0x50fc21;}(),_0x316059['mode']['OFB'];}));},5980:function(_0xf7ab1b,_0x1f5734,_0x21287f){(function(_0x6566f3,_0x1bd589){_0xf7ab1b['exports']=_0x1bd589(_0x21287f('21bf'));}(0x0,function(_0x5d732a){(function(){var _0x5adf7e=_0x5d732a,_0x470629=_0x5adf7e['lib'],_0x5e7152=_0x470629['Base'],_0x26fe35=_0x5adf7e['enc'],_0x2c556a=_0x26fe35['Utf8'],_0x1721f2=_0x5adf7e['algo'];_0x1721f2['HMAC']=_0x5e7152['extend']({'init':function(_0x4e81c8,_0x1ed0b7){_0x4e81c8=this['_hasher']=new _0x4e81c8['init'](),'string'==typeof _0x1ed0b7&&(_0x1ed0b7=_0x2c556a['parse'](_0x1ed0b7));var _0x24ef79=_0x4e81c8['blockSize'],_0x480c11=0x4*_0x24ef79;_0x1ed0b7['sigBytes']>_0x480c11&&(_0x1ed0b7=_0x4e81c8['finalize'](_0x1ed0b7)),_0x1ed0b7['clamp']();for(var _0xf79133=this['_oKey']=_0x1ed0b7['clone'](),_0x1cac7d=this['_iKey']=_0x1ed0b7['clone'](),_0x575766=_0xf79133['words'],_0x34e727=_0x1cac7d['words'],_0x1cd350=0x0;_0x1cd350<_0x24ef79;_0x1cd350++)_0x575766[_0x1cd350]^=0x5c5c5c5c,_0x34e727[_0x1cd350]^=0x36363636;_0xf79133['sigBytes']=_0x1cac7d['sigBytes']=_0x480c11,this['reset']();},'reset':function(){var _0x1afcf8=this['_hasher'];_0x1afcf8['reset'](),_0x1afcf8['update'](this['_iKey']);},'update':function(_0x2c6f62){return this['_hasher']['update'](_0x2c6f62),this;},'finalize':function(_0x2c08da){var _0x24cc1e=this['_hasher'],_0x5c3552=_0x24cc1e['finalize'](_0x2c08da);_0x24cc1e['reset']();var _0x5909d7=_0x24cc1e['finalize'](this['_oKey']['clone']()['concat'](_0x5c3552));return _0x5909d7;}});}());}));},'6b54':function(_0x49ed71,_0x175c1e,_0xffba69){'use strict';_0xffba69('3846');var _0x160347=_0xffba69('cb7c'),_0x9163df=_0xffba69('0bfb'),_0x22c02c=_0xffba69('9e1e'),_0x469ab6='toString',_0x2e6ceb=/./[_0x469ab6],_0x17e901=function(_0x202527){_0xffba69('2aba')(RegExp['prototype'],_0x469ab6,_0x202527,!0x0);};_0xffba69('79e5')(function(){return'/a/b'!=_0x2e6ceb['call']({'source':'a','flags':'b'});})?_0x17e901(function(){var _0x1ec3f6=_0x160347(this);return'/'['concat'](_0x1ec3f6['source'],'/','flags'in _0x1ec3f6?_0x1ec3f6['flags']:!_0x22c02c&&_0x1ec3f6 instanceof RegExp?_0x9163df['call'](_0x1ec3f6):void 0x0);}):_0x2e6ceb['name']!=_0x469ab6&&_0x17e901(function(){return _0x2e6ceb['call'](this);});},'6d08':function(_0x4d6940,_0x4b3ddc,_0x2dfda3){(function(_0x3a7027,_0x3f7cf4,_0x2ec188){_0x4d6940['exports']=_0x3f7cf4(_0x2dfda3('21bf'),_0x2dfda3('38ba'));}(0x0,function(_0x5ecad4){return function(_0x50d4c6){var _0x1d295d=_0x5ecad4,_0xc98f78=_0x1d295d['lib'],_0x17a579=_0xc98f78['CipherParams'],_0x1546d5=_0x1d295d['enc'],_0x3d518e=_0x1546d5['Hex'],_0x691771=_0x1d295d['format'];_0x691771['Hex']={'stringify':function(_0x17b9f5){return _0x17b9f5['ciphertext']['toString'](_0x3d518e);},'parse':function(_0x58c9ad){var _0x33d96a=_0x3d518e['parse'](_0x58c9ad);return _0x17a579['create']({'ciphertext':_0x33d96a});}};}(),_0x5ecad4['format']['Hex'];}));},'72fe':function(_0x3afed0,_0xe2cc1a,_0x390997){(function(_0x776bc1,_0x128715){_0x3afed0['exports']=_0x128715(_0x390997('21bf'));}(0x0,function(_0x2c8084){return function(_0x40a714){var _0x49d531=_0x2c8084,_0x2a1a20=_0x49d531['lib'],_0x4ddf62=_0x2a1a20['WordArray'],_0x3cef6b=_0x2a1a20['Hasher'],_0x4ad2cd=_0x49d531['algo'],_0x990e30=[];(function(){for(var _0x49568e=0x0;_0x49568e<0x40;_0x49568e++)_0x990e30[_0x49568e]=0x100000000*_0x40a714['abs'](_0x40a714['sin'](_0x49568e+0x1))|0x0;}());var _0x130439=_0x4ad2cd['MD5']=_0x3cef6b['extend']({'_doReset':function(){this['_hash']=new _0x4ddf62['init']([0x67452301,0xefcdab89,0x98badcfe,0x10325476]);},'_doProcessBlock':function(_0x453608,_0x30855b){for(var _0x3bd890=0x0;_0x3bd890<0x10;_0x3bd890++){var _0x215b23=_0x30855b+_0x3bd890,_0x58a4e8=_0x453608[_0x215b23];_0x453608[_0x215b23]=0xff00ff&(_0x58a4e8<<0x8|_0x58a4e8>>>0x18)|0xff00ff00&(_0x58a4e8<<0x18|_0x58a4e8>>>0x8);}var _0x5cbf84=this['_hash']['words'],_0x5b9380=_0x453608[_0x30855b+0x0],_0x3b1557=_0x453608[_0x30855b+0x1],_0x3c0002=_0x453608[_0x30855b+0x2],_0x2a675f=_0x453608[_0x30855b+0x3],_0x2a0c66=_0x453608[_0x30855b+0x4],_0x121d18=_0x453608[_0x30855b+0x5],_0x48d437=_0x453608[_0x30855b+0x6],_0x22fe22=_0x453608[_0x30855b+0x7],_0x540a35=_0x453608[_0x30855b+0x8],_0x33b7c1=_0x453608[_0x30855b+0x9],_0x40a52=_0x453608[_0x30855b+0xa],_0x3217ef=_0x453608[_0x30855b+0xb],_0x19accb=_0x453608[_0x30855b+0xc],_0x4fcc8e=_0x453608[_0x30855b+0xd],_0x2c6d5f=_0x453608[_0x30855b+0xe],_0x5ad202=_0x453608[_0x30855b+0xf],_0x491cd6=_0x5cbf84[0x0],_0x4ac229=_0x5cbf84[0x1],_0x127f64=_0x5cbf84[0x2],_0x35d089=_0x5cbf84[0x3];_0x491cd6=_0x3070c6(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x5b9380,0x7,_0x990e30[0x0]),_0x35d089=_0x3070c6(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x3b1557,0xc,_0x990e30[0x1]),_0x127f64=_0x3070c6(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x3c0002,0x11,_0x990e30[0x2]),_0x4ac229=_0x3070c6(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x2a675f,0x16,_0x990e30[0x3]),_0x491cd6=_0x3070c6(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x2a0c66,0x7,_0x990e30[0x4]),_0x35d089=_0x3070c6(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x121d18,0xc,_0x990e30[0x5]),_0x127f64=_0x3070c6(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x48d437,0x11,_0x990e30[0x6]),_0x4ac229=_0x3070c6(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x22fe22,0x16,_0x990e30[0x7]),_0x491cd6=_0x3070c6(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x540a35,0x7,_0x990e30[0x8]),_0x35d089=_0x3070c6(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x33b7c1,0xc,_0x990e30[0x9]),_0x127f64=_0x3070c6(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x40a52,0x11,_0x990e30[0xa]),_0x4ac229=_0x3070c6(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x3217ef,0x16,_0x990e30[0xb]),_0x491cd6=_0x3070c6(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x19accb,0x7,_0x990e30[0xc]),_0x35d089=_0x3070c6(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x4fcc8e,0xc,_0x990e30[0xd]),_0x127f64=_0x3070c6(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x2c6d5f,0x11,_0x990e30[0xe]),_0x4ac229=_0x3070c6(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x5ad202,0x16,_0x990e30[0xf]),_0x491cd6=_0x5579fb(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x3b1557,0x5,_0x990e30[0x10]),_0x35d089=_0x5579fb(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x48d437,0x9,_0x990e30[0x11]),_0x127f64=_0x5579fb(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x3217ef,0xe,_0x990e30[0x12]),_0x4ac229=_0x5579fb(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x5b9380,0x14,_0x990e30[0x13]),_0x491cd6=_0x5579fb(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x121d18,0x5,_0x990e30[0x14]),_0x35d089=_0x5579fb(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x40a52,0x9,_0x990e30[0x15]),_0x127f64=_0x5579fb(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x5ad202,0xe,_0x990e30[0x16]),_0x4ac229=_0x5579fb(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x2a0c66,0x14,_0x990e30[0x17]),_0x491cd6=_0x5579fb(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x33b7c1,0x5,_0x990e30[0x18]),_0x35d089=_0x5579fb(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x2c6d5f,0x9,_0x990e30[0x19]),_0x127f64=_0x5579fb(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x2a675f,0xe,_0x990e30[0x1a]),_0x4ac229=_0x5579fb(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x540a35,0x14,_0x990e30[0x1b]),_0x491cd6=_0x5579fb(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x4fcc8e,0x5,_0x990e30[0x1c]),_0x35d089=_0x5579fb(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x3c0002,0x9,_0x990e30[0x1d]),_0x127f64=_0x5579fb(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x22fe22,0xe,_0x990e30[0x1e]),_0x4ac229=_0x5579fb(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x19accb,0x14,_0x990e30[0x1f]),_0x491cd6=_0x36c839(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x121d18,0x4,_0x990e30[0x20]),_0x35d089=_0x36c839(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x540a35,0xb,_0x990e30[0x21]),_0x127f64=_0x36c839(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x3217ef,0x10,_0x990e30[0x22]),_0x4ac229=_0x36c839(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x2c6d5f,0x17,_0x990e30[0x23]),_0x491cd6=_0x36c839(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x3b1557,0x4,_0x990e30[0x24]),_0x35d089=_0x36c839(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x2a0c66,0xb,_0x990e30[0x25]),_0x127f64=_0x36c839(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x22fe22,0x10,_0x990e30[0x26]),_0x4ac229=_0x36c839(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x40a52,0x17,_0x990e30[0x27]),_0x491cd6=_0x36c839(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x4fcc8e,0x4,_0x990e30[0x28]),_0x35d089=_0x36c839(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x5b9380,0xb,_0x990e30[0x29]),_0x127f64=_0x36c839(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x2a675f,0x10,_0x990e30[0x2a]),_0x4ac229=_0x36c839(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x48d437,0x17,_0x990e30[0x2b]),_0x491cd6=_0x36c839(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x33b7c1,0x4,_0x990e30[0x2c]),_0x35d089=_0x36c839(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x19accb,0xb,_0x990e30[0x2d]),_0x127f64=_0x36c839(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x5ad202,0x10,_0x990e30[0x2e]),_0x4ac229=_0x36c839(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x3c0002,0x17,_0x990e30[0x2f]),_0x491cd6=_0x583cc5(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x5b9380,0x6,_0x990e30[0x30]),_0x35d089=_0x583cc5(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x22fe22,0xa,_0x990e30[0x31]),_0x127f64=_0x583cc5(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x2c6d5f,0xf,_0x990e30[0x32]),_0x4ac229=_0x583cc5(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x121d18,0x15,_0x990e30[0x33]),_0x491cd6=_0x583cc5(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x19accb,0x6,_0x990e30[0x34]),_0x35d089=_0x583cc5(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x2a675f,0xa,_0x990e30[0x35]),_0x127f64=_0x583cc5(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x40a52,0xf,_0x990e30[0x36]),_0x4ac229=_0x583cc5(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x3b1557,0x15,_0x990e30[0x37]),_0x491cd6=_0x583cc5(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x540a35,0x6,_0x990e30[0x38]),_0x35d089=_0x583cc5(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x5ad202,0xa,_0x990e30[0x39]),_0x127f64=_0x583cc5(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x48d437,0xf,_0x990e30[0x3a]),_0x4ac229=_0x583cc5(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x4fcc8e,0x15,_0x990e30[0x3b]),_0x491cd6=_0x583cc5(_0x491cd6,_0x4ac229,_0x127f64,_0x35d089,_0x2a0c66,0x6,_0x990e30[0x3c]),_0x35d089=_0x583cc5(_0x35d089,_0x491cd6,_0x4ac229,_0x127f64,_0x3217ef,0xa,_0x990e30[0x3d]),_0x127f64=_0x583cc5(_0x127f64,_0x35d089,_0x491cd6,_0x4ac229,_0x3c0002,0xf,_0x990e30[0x3e]),_0x4ac229=_0x583cc5(_0x4ac229,_0x127f64,_0x35d089,_0x491cd6,_0x33b7c1,0x15,_0x990e30[0x3f]),_0x5cbf84[0x0]=_0x5cbf84[0x0]+_0x491cd6|0x0,_0x5cbf84[0x1]=_0x5cbf84[0x1]+_0x4ac229|0x0,_0x5cbf84[0x2]=_0x5cbf84[0x2]+_0x127f64|0x0,_0x5cbf84[0x3]=_0x5cbf84[0x3]+_0x35d089|0x0;},'_doFinalize':function(){var _0x16b698=this['_data'],_0x517da2=_0x16b698['words'],_0x516958=0x8*this['_nDataBytes'],_0x368fd2=0x8*_0x16b698['sigBytes'];_0x517da2[_0x368fd2>>>0x5]|=0x80<<0x18-_0x368fd2%0x20;var _0x2d9713=_0x40a714['floor'](_0x516958/0x100000000),_0x4f1cd0=_0x516958;_0x517da2[0xf+(_0x368fd2+0x40>>>0x9<<0x4)]=0xff00ff&(_0x2d9713<<0x8|_0x2d9713>>>0x18)|0xff00ff00&(_0x2d9713<<0x18|_0x2d9713>>>0x8),_0x517da2[0xe+(_0x368fd2+0x40>>>0x9<<0x4)]=0xff00ff&(_0x4f1cd0<<0x8|_0x4f1cd0>>>0x18)|0xff00ff00&(_0x4f1cd0<<0x18|_0x4f1cd0>>>0x8),_0x16b698['sigBytes']=0x4*(_0x517da2['length']+0x1),this['_process']();for(var _0xb226bb=this['_hash'],_0x2e2e08=_0xb226bb['words'],_0x2b9612=0x0;_0x2b9612<0x4;_0x2b9612++){var _0x12cfb0=_0x2e2e08[_0x2b9612];_0x2e2e08[_0x2b9612]=0xff00ff&(_0x12cfb0<<0x8|_0x12cfb0>>>0x18)|0xff00ff00&(_0x12cfb0<<0x18|_0x12cfb0>>>0x8);}return _0xb226bb;},'clone':function(){var _0x1abf01=_0x3cef6b['clone']['call'](this);return _0x1abf01['_hash']=this['_hash']['clone'](),_0x1abf01;}});function _0x3070c6(_0x656043,_0x27a61a,_0x484280,_0x4c0add,_0x4ab2ed,_0x1e2e46,_0x63ee4d){var _0x188d90=_0x656043+(_0x27a61a&_0x484280|~_0x27a61a&_0x4c0add)+_0x4ab2ed+_0x63ee4d;return(_0x188d90<<_0x1e2e46|_0x188d90>>>0x20-_0x1e2e46)+_0x27a61a;}function _0x5579fb(_0xeeed99,_0x384631,_0x1ed2ac,_0x423424,_0x31751c,_0x240902,_0x222707){var _0x417dd5=_0xeeed99+(_0x384631&_0x423424|_0x1ed2ac&~_0x423424)+_0x31751c+_0x222707;return(_0x417dd5<<_0x240902|_0x417dd5>>>0x20-_0x240902)+_0x384631;}function _0x36c839(_0x3bf8e8,_0x2a506e,_0x2c1626,_0x432468,_0x5d1010,_0x12caf8,_0x59c109){var _0x12723e=_0x3bf8e8+(_0x2a506e^_0x2c1626^_0x432468)+_0x5d1010+_0x59c109;return(_0x12723e<<_0x12caf8|_0x12723e>>>0x20-_0x12caf8)+_0x2a506e;}function _0x583cc5(_0xbfa10,_0x580c94,_0x393998,_0x4d71ba,_0x1bb156,_0x511e73,_0xf1759){var _0x2dcb80=_0xbfa10+(_0x393998^(_0x580c94|~_0x4d71ba))+_0x1bb156+_0xf1759;return(_0x2dcb80<<_0x511e73|_0x2dcb80>>>0x20-_0x511e73)+_0x580c94;}_0x49d531['MD5']=_0x3cef6b['_createHelper'](_0x130439),_0x49d531['HmacMD5']=_0x3cef6b['_createHmacHelper'](_0x130439);}(Math),_0x2c8084['MD5'];}));},'7bbc':function(_0x29d408,_0x3a781a,_0x31a39d){(function(_0x2c4850,_0x47838a,_0x3dea55){_0x29d408['exports']=_0x47838a(_0x31a39d('21bf'),_0x31a39d('df2f'),_0x31a39d('5980'));}(0x0,function(_0x46d5d1){return function(){var _0x4e8d72=_0x46d5d1,_0x376025=_0x4e8d72['lib'],_0x5a95a8=_0x376025['Base'],_0x3c4e41=_0x376025['WordArray'],_0x4e33b0=_0x4e8d72['algo'],_0x2cffec=_0x4e33b0['SHA1'],_0x31a239=_0x4e33b0['HMAC'],_0x25b85a=_0x4e33b0['PBKDF2']=_0x5a95a8['extend']({'cfg':_0x5a95a8['extend']({'keySize':0x4,'hasher':_0x2cffec,'iterations':0x1}),'init':function(_0x3075e2){this['cfg']=this['cfg']['extend'](_0x3075e2);},'compute':function(_0x581787,_0x1d3396){var _0xdb0d2e=this['cfg'],_0x3b96c8=_0x31a239['create'](_0xdb0d2e['hasher'],_0x581787),_0x971435=_0x3c4e41['create'](),_0x5b2255=_0x3c4e41['create']([0x1]),_0x272e38=_0x971435['words'],_0x299a4b=_0x5b2255['words'],_0x39ff4=_0xdb0d2e['keySize'],_0x3038e4=_0xdb0d2e['iterations'];while(_0x272e38['length']<_0x39ff4){var _0x18b6e4=_0x3b96c8['update'](_0x1d3396)['finalize'](_0x5b2255);_0x3b96c8['reset']();for(var _0x1bc1c5=_0x18b6e4['words'],_0x22f788=_0x1bc1c5['length'],_0xb47833=_0x18b6e4,_0x522156=0x1;_0x522156<_0x3038e4;_0x522156++){_0xb47833=_0x3b96c8['finalize'](_0xb47833),_0x3b96c8['reset']();for(var _0x44395a=_0xb47833['words'],_0x5e9288=0x0;_0x5e9288<_0x22f788;_0x5e9288++)_0x1bc1c5[_0x5e9288]^=_0x44395a[_0x5e9288];}_0x971435['concat'](_0x18b6e4),_0x299a4b[0x0]++;}return _0x971435['sigBytes']=0x4*_0x39ff4,_0x971435;}});_0x4e8d72['PBKDF2']=function(_0x2beb95,_0x19d7da,_0x2e1d67){return _0x25b85a['create'](_0x2e1d67)['compute'](_0x2beb95,_0x19d7da);};}(),_0x46d5d1['PBKDF2'];}));},'7d92':function(_0x1e1673,_0x29aaea,_0x34777a){'use strict';_0x34777a('6b54');var _0x189cbb=_0x34777a('3452'),_0x358b1f=_0x34777a('27ae')['Base64'];function _0x456254(){for(var _0x5da681=Math['round'](new Date()['getTime']()/0x3e8)['toString'](),_0x2a83dd=arguments['length'],_0x31a891=new Array(_0x2a83dd),_0x596a02=0x0;_0x596a02<_0x2a83dd;_0x596a02++)_0x31a891[_0x596a02]=arguments[_0x596a02];_0x31a891['push'](_0x5da681);var _0xf7c3c7=_0x189cbb['SHA1'](_0x31a891['join'](','))['toString'](_0x189cbb['enc']['Hex']),_0x3c8435=[_0xf7c3c7,_0x5da681]['join'](','),_0x104b5b=_0x358b1f['encode'](_0x3c8435);return _0x104b5b;}_0x29aaea['a']=_0x456254;},'81bf':function(_0x2e34f4,_0x5ac408,_0x383034){(function(_0x14b5ca,_0x24012e,_0x4cf641){_0x2e34f4['exports']=_0x24012e(_0x383034('21bf'),_0x383034('38ba'));}(0x0,function(_0x4112fc){return _0x4112fc['mode']['ECB']=function(){var _0x48c751=_0x4112fc['lib']['BlockCipherMode']['extend']();return _0x48c751['Encryptor']=_0x48c751['extend']({'processBlock':function(_0x578df0,_0x4a649d){this['_cipher']['encryptBlock'](_0x578df0,_0x4a649d);}}),_0x48c751['Decryptor']=_0x48c751['extend']({'processBlock':function(_0x3dc393,_0x3603e){this['_cipher']['decryptBlock'](_0x3dc393,_0x3603e);}}),_0x48c751;}(),_0x4112fc['mode']['ECB'];}));},'8cef':function(_0x272b19,_0x249db7,_0x41f973){(function(_0x2ef002,_0x1a21c3,_0x1c3eab){_0x272b19['exports']=_0x1a21c3(_0x41f973('21bf'),_0x41f973('38ba'));}(0x0,function(_0x95f729){return _0x95f729['pad']['Iso97971']={'pad':function(_0x208e17,_0x3b4c8e){_0x208e17['concat'](_0x95f729['lib']['WordArray']['create']([0x80000000],0x1)),_0x95f729['pad']['ZeroPadding']['pad'](_0x208e17,_0x3b4c8e);},'unpad':function(_0x441676){_0x95f729['pad']['ZeroPadding']['unpad'](_0x441676),_0x441676['sigBytes']--;}},_0x95f729['pad']['Iso97971'];}));},'94f8':function(_0x5c4483,_0x3bf638,_0x468d6a){(function(_0x391b74,_0x31c16e){_0x5c4483['exports']=_0x31c16e(_0x468d6a('21bf'));}(0x0,function(_0x4cbd81){return function(_0x2458d9){var _0x42234e=_0x4cbd81,_0x304cf3=_0x42234e['lib'],_0x1c8a8e=_0x304cf3['WordArray'],_0x1147c5=_0x304cf3['Hasher'],_0x566118=_0x42234e['algo'],_0x29f01f=[],_0x30e251=[];(function(){function _0x24a71f(_0x513b54){for(var _0x5602ed=_0x2458d9['sqrt'](_0x513b54),_0x392b4f=0x2;_0x392b4f<=_0x5602ed;_0x392b4f++)if(!(_0x513b54%_0x392b4f))return!0x1;return!0x0;}function _0x147268(_0x520f79){return 0x100000000*(_0x520f79-(0x0|_0x520f79))|0x0;}var _0x5d587d=0x2,_0x43c519=0x0;while(_0x43c519<0x40)_0x24a71f(_0x5d587d)&&(_0x43c519<0x8&&(_0x29f01f[_0x43c519]=_0x147268(_0x2458d9['pow'](_0x5d587d,0.5))),_0x30e251[_0x43c519]=_0x147268(_0x2458d9['pow'](_0x5d587d,0x1/0x3)),_0x43c519++),_0x5d587d++;}());var _0x43664e=[],_0x159518=_0x566118['SHA256']=_0x1147c5['extend']({'_doReset':function(){this['_hash']=new _0x1c8a8e['init'](_0x29f01f['slice'](0x0));},'_doProcessBlock':function(_0x21654b,_0x3a0c1c){for(var _0x5c0bf7=this['_hash']['words'],_0x56950d=_0x5c0bf7[0x0],_0x128932=_0x5c0bf7[0x1],_0x4e3fe6=_0x5c0bf7[0x2],_0xead20d=_0x5c0bf7[0x3],_0x481147=_0x5c0bf7[0x4],_0x2541de=_0x5c0bf7[0x5],_0xb8323d=_0x5c0bf7[0x6],_0xe66e10=_0x5c0bf7[0x7],_0x3acb59=0x0;_0x3acb59<0x40;_0x3acb59++){if(_0x3acb59<0x10)_0x43664e[_0x3acb59]=0x0|_0x21654b[_0x3a0c1c+_0x3acb59];else{var _0x2128b3=_0x43664e[_0x3acb59-0xf],_0x5f143c=(_0x2128b3<<0x19|_0x2128b3>>>0x7)^(_0x2128b3<<0xe|_0x2128b3>>>0x12)^_0x2128b3>>>0x3,_0x24773a=_0x43664e[_0x3acb59-0x2],_0x4ca0e6=(_0x24773a<<0xf|_0x24773a>>>0x11)^(_0x24773a<<0xd|_0x24773a>>>0x13)^_0x24773a>>>0xa;_0x43664e[_0x3acb59]=_0x5f143c+_0x43664e[_0x3acb59-0x7]+_0x4ca0e6+_0x43664e[_0x3acb59-0x10];}var _0xba2c0a=_0x481147&_0x2541de^~_0x481147&_0xb8323d,_0x37dda9=_0x56950d&_0x128932^_0x56950d&_0x4e3fe6^_0x128932&_0x4e3fe6,_0x3d9a39=(_0x56950d<<0x1e|_0x56950d>>>0x2)^(_0x56950d<<0x13|_0x56950d>>>0xd)^(_0x56950d<<0xa|_0x56950d>>>0x16),_0x4c06da=(_0x481147<<0x1a|_0x481147>>>0x6)^(_0x481147<<0x15|_0x481147>>>0xb)^(_0x481147<<0x7|_0x481147>>>0x19),_0x526aaa=_0xe66e10+_0x4c06da+_0xba2c0a+_0x30e251[_0x3acb59]+_0x43664e[_0x3acb59],_0x5bf56b=_0x3d9a39+_0x37dda9;_0xe66e10=_0xb8323d,_0xb8323d=_0x2541de,_0x2541de=_0x481147,_0x481147=_0xead20d+_0x526aaa|0x0,_0xead20d=_0x4e3fe6,_0x4e3fe6=_0x128932,_0x128932=_0x56950d,_0x56950d=_0x526aaa+_0x5bf56b|0x0;}_0x5c0bf7[0x0]=_0x5c0bf7[0x0]+_0x56950d|0x0,_0x5c0bf7[0x1]=_0x5c0bf7[0x1]+_0x128932|0x0,_0x5c0bf7[0x2]=_0x5c0bf7[0x2]+_0x4e3fe6|0x0,_0x5c0bf7[0x3]=_0x5c0bf7[0x3]+_0xead20d|0x0,_0x5c0bf7[0x4]=_0x5c0bf7[0x4]+_0x481147|0x0,_0x5c0bf7[0x5]=_0x5c0bf7[0x5]+_0x2541de|0x0,_0x5c0bf7[0x6]=_0x5c0bf7[0x6]+_0xb8323d|0x0,_0x5c0bf7[0x7]=_0x5c0bf7[0x7]+_0xe66e10|0x0;},'_doFinalize':function(){var _0xa889a7=this['_data'],_0x5d88ea=_0xa889a7['words'],_0x27ba27=0x8*this['_nDataBytes'],_0x1ec275=0x8*_0xa889a7['sigBytes'];return _0x5d88ea[_0x1ec275>>>0x5]|=0x80<<0x18-_0x1ec275%0x20,_0x5d88ea[0xe+(_0x1ec275+0x40>>>0x9<<0x4)]=_0x2458d9['floor'](_0x27ba27/0x100000000),_0x5d88ea[0xf+(_0x1ec275+0x40>>>0x9<<0x4)]=_0x27ba27,_0xa889a7['sigBytes']=0x4*_0x5d88ea['length'],this['_process'](),this['_hash'];},'clone':function(){var _0x163178=_0x1147c5['clone']['call'](this);return _0x163178['_hash']=this['_hash']['clone'](),_0x163178;}});_0x42234e['SHA256']=_0x1147c5['_createHelper'](_0x159518),_0x42234e['HmacSHA256']=_0x1147c5['_createHmacHelper'](_0x159518);}(Math),_0x4cbd81['SHA256'];}));},'a11b':function(_0x4de412,_0x5cb9c6,_0x55b593){(function(_0x252974,_0x5b8180,_0x44cf4c){_0x4de412['exports']=_0x5b8180(_0x55b593('21bf'),_0x55b593('38ba'));}(0x0,function(_0x535166){return _0x535166['pad']['Iso10126']={'pad':function(_0x4a0056,_0xcc6bb6){var _0x323886=0x4*_0xcc6bb6,_0x3b67d8=_0x323886-_0x4a0056['sigBytes']%_0x323886;_0x4a0056['concat'](_0x535166['lib']['WordArray']['random'](_0x3b67d8-0x1))['concat'](_0x535166['lib']['WordArray']['create']([_0x3b67d8<<0x18],0x1));},'unpad':function(_0x1e03a3){var _0x3e0421=0xff&_0x1e03a3['words'][_0x1e03a3['sigBytes']-0x1>>>0x2];_0x1e03a3['sigBytes']-=_0x3e0421;}},_0x535166['pad']['Iso10126'];}));},'a40e':function(_0x4807f1,_0x880c55,_0x314a04){(function(_0x3b01aa,_0x1bcf58,_0x29f414){_0x4807f1['exports']=_0x1bcf58(_0x314a04('21bf'),_0x314a04('1132'),_0x314a04('72fe'),_0x314a04('2b79'),_0x314a04('38ba'));}(0x0,function(_0x3e3a4b){return function(){var _0x233c6d=_0x3e3a4b,_0x4e3af7=_0x233c6d['lib'],_0x425b0f=_0x4e3af7['WordArray'],_0x7f552e=_0x4e3af7['BlockCipher'],_0xa0e001=_0x233c6d['algo'],_0x3c0b39=[0x39,0x31,0x29,0x21,0x19,0x11,0x9,0x1,0x3a,0x32,0x2a,0x22,0x1a,0x12,0xa,0x2,0x3b,0x33,0x2b,0x23,0x1b,0x13,0xb,0x3,0x3c,0x34,0x2c,0x24,0x3f,0x37,0x2f,0x27,0x1f,0x17,0xf,0x7,0x3e,0x36,0x2e,0x26,0x1e,0x16,0xe,0x6,0x3d,0x35,0x2d,0x25,0x1d,0x15,0xd,0x5,0x1c,0x14,0xc,0x4],_0x8982=[0xe,0x11,0xb,0x18,0x1,0x5,0x3,0x1c,0xf,0x6,0x15,0xa,0x17,0x13,0xc,0x4,0x1a,0x8,0x10,0x7,0x1b,0x14,0xd,0x2,0x29,0x34,0x1f,0x25,0x2f,0x37,0x1e,0x28,0x33,0x2d,0x21,0x30,0x2c,0x31,0x27,0x38,0x22,0x35,0x2e,0x2a,0x32,0x24,0x1d,0x20],_0x191df8=[0x1,0x2,0x4,0x6,0x8,0xa,0xc,0xe,0xf,0x11,0x13,0x15,0x17,0x19,0x1b,0x1c],_0x2c821a=[{0:0x808200,268435456:0x8000,536870912:0x808002,805306368:0x2,1073741824:0x200,1342177280:0x808202,1610612736:0x800202,1879048192:0x800000,2147483648:0x202,2415919104:0x800200,2684354560:0x8200,2952790016:0x808000,3221225472:0x8002,3489660928:0x800002,3758096384:0x0,4026531840:0x8202,134217728:0x0,402653184:0x808202,671088640:0x8202,939524096:0x8000,1207959552:0x808200,1476395008:0x200,1744830464:0x808002,2013265920:0x2,2281701376:0x800200,2550136832:0x8200,2818572288:0x808000,3087007744:0x800202,3355443200:0x800002,3623878656:0x8002,3892314112:0x202,4160749568:0x800000,1:0x8000,268435457:0x2,536870913:0x808200,805306369:0x800000,1073741825:0x808002,1342177281:0x8200,1610612737:0x200,1879048193:0x800202,2147483649:0x808202,2415919105:0x808000,2684354561:0x800002,2952790017:0x8202,3221225473:0x202,3489660929:0x800200,3758096385:0x8002,4026531841:0x0,134217729:0x808202,402653185:0x808000,671088641:0x800000,939524097:0x200,1207959553:0x8000,1476395009:0x800002,1744830465:0x2,2013265921:0x8202,2281701377:0x8002,2550136833:0x800202,2818572289:0x202,3087007745:0x808200,3355443201:0x800200,3623878657:0x0,3892314113:0x8200,4160749569:0x808002},{0:0x40084010,16777216:0x4000,33554432:0x80000,50331648:0x40080010,67108864:0x40000010,83886080:0x40084000,100663296:0x40004000,117440512:0x10,134217728:0x84000,150994944:0x40004010,167772160:0x40000000,184549376:0x84010,201326592:0x80010,218103808:0x0,234881024:0x4010,251658240:0x40080000,8388608:0x40004000,25165824:0x84010,41943040:0x10,58720256:0x40004010,75497472:0x40084010,92274688:0x40000000,109051904:0x80000,125829120:0x40080010,142606336:0x80010,159383552:0x0,176160768:0x4000,192937984:0x40080000,209715200:0x40000010,226492416:0x84000,243269632:0x40084000,260046848:0x4010,268435456:0x0,285212672:0x40080010,301989888:0x40004010,318767104:0x40084000,335544320:0x40080000,352321536:0x10,369098752:0x84010,385875968:0x4000,402653184:0x4010,419430400:0x80000,436207616:0x80010,452984832:0x40000010,469762048:0x84000,486539264:0x40004000,503316480:0x40000000,520093696:0x40084010,276824064:0x84010,293601280:0x80000,310378496:0x40080000,327155712:0x4000,343932928:0x40004000,360710144:0x40084010,377487360:0x10,394264576:0x40000000,411041792:0x40084000,427819008:0x40000010,444596224:0x40004010,461373440:0x80010,478150656:0x0,494927872:0x4010,511705088:0x40080010,528482304:0x84000},{0:0x104,1048576:0x0,2097152:0x4000100,3145728:0x10104,4194304:0x10004,5242880:0x4000004,6291456:0x4010104,7340032:0x4010000,8388608:0x4000000,9437184:0x4010100,10485760:0x10100,11534336:0x4010004,12582912:0x4000104,13631488:0x10000,14680064:0x4,15728640:0x100,524288:0x4010100,1572864:0x4010004,2621440:0x0,3670016:0x4000100,4718592:0x4000004,5767168:0x10000,6815744:0x10004,7864320:0x104,8912896:0x4,9961472:0x100,11010048:0x4010000,12058624:0x10104,13107200:0x10100,14155776:0x4000104,15204352:0x4010104,16252928:0x4000000,16777216:0x4010100,17825792:0x10004,18874368:0x10000,19922944:0x4000100,20971520:0x100,22020096:0x4010104,23068672:0x4000004,24117248:0x0,25165824:0x4000104,26214400:0x4000000,27262976:0x4,28311552:0x10100,29360128:0x4010000,30408704:0x104,31457280:0x10104,32505856:0x4010004,17301504:0x4000000,18350080:0x104,19398656:0x4010100,20447232:0x0,21495808:0x10004,22544384:0x4000100,23592960:0x100,24641536:0x4010004,25690112:0x10000,26738688:0x4010104,27787264:0x10104,28835840:0x4000004,29884416:0x4000104,30932992:0x4010000,31981568:0x4,33030144:0x10100},{0:0x80401000,65536:0x80001040,131072:0x401040,196608:0x80400000,262144:0x0,327680:0x401000,393216:0x80000040,458752:0x400040,524288:0x80000000,589824:0x400000,655360:0x40,720896:0x80001000,786432:0x80400040,851968:0x1040,917504:0x1000,983040:0x80401040,32768:0x80001040,98304:0x40,163840:0x80400040,229376:0x80001000,294912:0x401000,360448:0x80401040,425984:0x0,491520:0x80400000,557056:0x1000,622592:0x80401000,688128:0x400000,753664:0x1040,819200:0x80000000,884736:0x400040,950272:0x401040,1015808:0x80000040,1048576:0x400040,1114112:0x401000,1179648:0x80000040,1245184:0x0,1310720:0x1040,1376256:0x80400040,1441792:0x80401000,1507328:0x80001040,1572864:0x80401040,1638400:0x80000000,1703936:0x80400000,1769472:0x401040,1835008:0x80001000,1900544:0x400000,1966080:0x40,2031616:0x1000,1081344:0x80400000,1146880:0x80401040,1212416:0x0,1277952:0x401000,1343488:0x400040,1409024:0x80000000,1474560:0x80001040,1540096:0x40,1605632:0x80000040,1671168:0x1000,1736704:0x80001000,1802240:0x80400040,1867776:0x1040,1933312:0x80401000,1998848:0x400000,2064384:0x401040},{0:0x80,4096:0x1040000,8192:0x40000,12288:0x20000000,16384:0x20040080,20480:0x1000080,24576:0x21000080,28672:0x40080,32768:0x1000000,36864:0x20040000,40960:0x20000080,45056:0x21040080,49152:0x21040000,53248:0x0,57344:0x1040080,61440:0x21000000,2048:0x1040080,6144:0x21000080,10240:0x80,14336:0x1040000,18432:0x40000,22528:0x20040080,26624:0x21040000,30720:0x20000000,34816:0x20040000,38912:0x0,43008:0x21040080,47104:0x1000080,51200:0x20000080,55296:0x21000000,59392:0x1000000,63488:0x40080,65536:0x40000,69632:0x80,73728:0x20000000,77824:0x21000080,81920:0x1000080,86016:0x21040000,90112:0x20040080,94208:0x1000000,98304:0x21040080,102400:0x21000000,106496:0x1040000,110592:0x20040000,114688:0x40080,118784:0x20000080,122880:0x0,126976:0x1040080,67584:0x21000080,71680:0x1000000,75776:0x1040000,79872:0x20040080,83968:0x20000000,88064:0x1040080,92160:0x80,96256:0x21040000,100352:0x40080,104448:0x21040080,108544:0x0,112640:0x21000000,116736:0x1000080,120832:0x40000,124928:0x20040000,129024:0x20000080},{0:0x10000008,256:0x2000,512:0x10200000,768:0x10202008,1024:0x10002000,1280:0x200000,1536:0x200008,1792:0x10000000,2048:0x0,2304:0x10002008,2560:0x202000,2816:0x8,3072:0x10200008,3328:0x202008,3584:0x2008,3840:0x10202000,128:0x10200000,384:0x10202008,640:0x8,896:0x200000,1152:0x202008,1408:0x10000008,1664:0x10002000,1920:0x2008,2176:0x200008,2432:0x2000,2688:0x10002008,2944:0x10200008,3200:0x0,3456:0x10202000,3712:0x202000,3968:0x10000000,4096:0x10002000,4352:0x10200008,4608:0x10202008,4864:0x2008,5120:0x200000,5376:0x10000000,5632:0x10000008,5888:0x202000,6144:0x202008,6400:0x0,6656:0x8,6912:0x10200000,7168:0x2000,7424:0x10002008,7680:0x10202000,7936:0x200008,4224:0x8,4480:0x202000,4736:0x200000,4992:0x10000008,5248:0x10002000,5504:0x2008,5760:0x10202008,6016:0x10200000,6272:0x10202000,6528:0x10200008,6784:0x2000,7040:0x202008,7296:0x200008,7552:0x0,7808:0x10000000,8064:0x10002008},{0:0x100000,16:0x2000401,32:0x400,48:0x100401,64:0x2100401,80:0x0,96:0x1,112:0x2100001,128:0x2000400,144:0x100001,160:0x2000001,176:0x2100400,192:0x2100000,208:0x401,224:0x100400,240:0x2000000,8:0x2100001,24:0x0,40:0x2000401,56:0x2100400,72:0x100000,88:0x2000001,104:0x2000000,120:0x401,136:0x100401,152:0x2000400,168:0x2100000,184:0x100001,200:0x400,216:0x2100401,232:0x1,248:0x100400,256:0x2000000,272:0x100000,288:0x2000401,304:0x2100001,320:0x100001,336:0x2000400,352:0x2100400,368:0x100401,384:0x401,400:0x2100401,416:0x100400,432:0x1,448:0x0,464:0x2100000,480:0x2000001,496:0x400,264:0x100400,280:0x2000401,296:0x2100001,312:0x1,328:0x2000000,344:0x100000,360:0x401,376:0x2100400,392:0x2000001,408:0x2100000,424:0x0,440:0x2100401,456:0x100401,472:0x400,488:0x2000400,504:0x100001},{0:0x8000820,1:0x20000,2:0x8000000,3:0x20,4:0x20020,5:0x8020820,6:0x8020800,7:0x800,8:0x8020000,9:0x8000800,10:0x20800,11:0x8020020,12:0x820,13:0x0,14:0x8000020,15:0x20820,2147483648:0x800,2147483649:0x8020820,2147483650:0x8000820,2147483651:0x8000000,2147483652:0x8020000,2147483653:0x20800,2147483654:0x20820,2147483655:0x20,2147483656:0x8000020,2147483657:0x820,2147483658:0x20020,2147483659:0x8020800,2147483660:0x0,2147483661:0x8020020,2147483662:0x8000800,2147483663:0x20000,16:0x20820,17:0x8020800,18:0x20,19:0x800,20:0x8000800,21:0x8000020,22:0x8020020,23:0x20000,24:0x0,25:0x20020,26:0x8020000,27:0x8000820,28:0x8020820,29:0x20800,30:0x820,31:0x8000000,2147483664:0x20000,2147483665:0x800,2147483666:0x8020020,2147483667:0x20820,2147483668:0x20,2147483669:0x8020000,2147483670:0x8000000,2147483671:0x8000820,2147483672:0x8020820,2147483673:0x8000020,2147483674:0x8000800,2147483675:0x0,2147483676:0x20800,2147483677:0x820,2147483678:0x20020,2147483679:0x8020800}],_0x4a7a46=[0xf8000001,0x1f800000,0x1f80000,0x1f8000,0x1f800,0x1f80,0x1f8,0x8000001f],_0x121114=_0xa0e001['DES']=_0x7f552e['extend']({'_doReset':function(){for(var _0x17239e=this['_key'],_0x4d6992=_0x17239e['words'],_0x374130=[],_0x3cf08d=0x0;_0x3cf08d<0x38;_0x3cf08d++){var _0x3e6a6f=_0x3c0b39[_0x3cf08d]-0x1;_0x374130[_0x3cf08d]=_0x4d6992[_0x3e6a6f>>>0x5]>>>0x1f-_0x3e6a6f%0x20&0x1;}for(var _0x34f5c7=this['_subKeys']=[],_0x17c674=0x0;_0x17c674<0x10;_0x17c674++){var _0x6d8cb7=_0x34f5c7[_0x17c674]=[],_0x54aaa3=_0x191df8[_0x17c674];for(_0x3cf08d=0x0;_0x3cf08d<0x18;_0x3cf08d++)_0x6d8cb7[_0x3cf08d/0x6|0x0]|=_0x374130[(_0x8982[_0x3cf08d]-0x1+_0x54aaa3)%0x1c]<<0x1f-_0x3cf08d%0x6,_0x6d8cb7[0x4+(_0x3cf08d/0x6|0x0)]|=_0x374130[0x1c+(_0x8982[_0x3cf08d+0x18]-0x1+_0x54aaa3)%0x1c]<<0x1f-_0x3cf08d%0x6;_0x6d8cb7[0x0]=_0x6d8cb7[0x0]<<0x1|_0x6d8cb7[0x0]>>>0x1f;for(_0x3cf08d=0x1;_0x3cf08d<0x7;_0x3cf08d++)_0x6d8cb7[_0x3cf08d]=_0x6d8cb7[_0x3cf08d]>>>0x4*(_0x3cf08d-0x1)+0x3;_0x6d8cb7[0x7]=_0x6d8cb7[0x7]<<0x5|_0x6d8cb7[0x7]>>>0x1b;}var _0x1173d2=this['_invSubKeys']=[];for(_0x3cf08d=0x0;_0x3cf08d<0x10;_0x3cf08d++)_0x1173d2[_0x3cf08d]=_0x34f5c7[0xf-_0x3cf08d];},'encryptBlock':function(_0x3423ed,_0x54bc7e){this['_doCryptBlock'](_0x3423ed,_0x54bc7e,this['_subKeys']);},'decryptBlock':function(_0x1012ba,_0x2a5293){this['_doCryptBlock'](_0x1012ba,_0x2a5293,this['_invSubKeys']);},'_doCryptBlock':function(_0x21377e,_0x135173,_0x34e0bc){this['_lBlock']=_0x21377e[_0x135173],this['_rBlock']=_0x21377e[_0x135173+0x1],_0x360933['call'](this,0x4,0xf0f0f0f),_0x360933['call'](this,0x10,0xffff),_0x3b28cc['call'](this,0x2,0x33333333),_0x3b28cc['call'](this,0x8,0xff00ff),_0x360933['call'](this,0x1,0x55555555);for(var _0x4c4d02=0x0;_0x4c4d02<0x10;_0x4c4d02++){for(var _0x4eb075=_0x34e0bc[_0x4c4d02],_0x3c398a=this['_lBlock'],_0x5ed4a4=this['_rBlock'],_0x49155f=0x0,_0x4eb3c2=0x0;_0x4eb3c2<0x8;_0x4eb3c2++)_0x49155f|=_0x2c821a[_0x4eb3c2][((_0x5ed4a4^_0x4eb075[_0x4eb3c2])&_0x4a7a46[_0x4eb3c2])>>>0x0];this['_lBlock']=_0x5ed4a4,this['_rBlock']=_0x3c398a^_0x49155f;}var _0x2af266=this['_lBlock'];this['_lBlock']=this['_rBlock'],this['_rBlock']=_0x2af266,_0x360933['call'](this,0x1,0x55555555),_0x3b28cc['call'](this,0x8,0xff00ff),_0x3b28cc['call'](this,0x2,0x33333333),_0x360933['call'](this,0x10,0xffff),_0x360933['call'](this,0x4,0xf0f0f0f),_0x21377e[_0x135173]=this['_lBlock'],_0x21377e[_0x135173+0x1]=this['_rBlock'];},'keySize':0x2,'ivSize':0x2,'blockSize':0x2});function _0x360933(_0x1b916d,_0x5d3017){var _0x1be584=(this['_lBlock']>>>_0x1b916d^this['_rBlock'])&_0x5d3017;this['_rBlock']^=_0x1be584,this['_lBlock']^=_0x1be584<<_0x1b916d;}function _0x3b28cc(_0x48b5bc,_0x521915){var _0x1c1c6d=(this['_rBlock']>>>_0x48b5bc^this['_lBlock'])&_0x521915;this['_lBlock']^=_0x1c1c6d,this['_rBlock']^=_0x1c1c6d<<_0x48b5bc;}_0x233c6d['DES']=_0x7f552e['_createHelper'](_0x121114);var _0xab5f94=_0xa0e001['TripleDES']=_0x7f552e['extend']({'_doReset':function(){var _0x4c1a4d=this['_key'],_0x15bc2f=_0x4c1a4d['words'];this['_des1']=_0x121114['createEncryptor'](_0x425b0f['create'](_0x15bc2f['slice'](0x0,0x2))),this['_des2']=_0x121114['createEncryptor'](_0x425b0f['create'](_0x15bc2f['slice'](0x2,0x4))),this['_des3']=_0x121114['createEncryptor'](_0x425b0f['create'](_0x15bc2f['slice'](0x4,0x6)));},'encryptBlock':function(_0x40c83b,_0x326c05){this['_des1']['encryptBlock'](_0x40c83b,_0x326c05),this['_des2']['decryptBlock'](_0x40c83b,_0x326c05),this['_des3']['encryptBlock'](_0x40c83b,_0x326c05);},'decryptBlock':function(_0x4b6c68,_0xf9b26a){this['_des3']['decryptBlock'](_0x4b6c68,_0xf9b26a),this['_des2']['encryptBlock'](_0x4b6c68,_0xf9b26a),this['_des1']['decryptBlock'](_0x4b6c68,_0xf9b26a);},'keySize':0x6,'ivSize':0x2,'blockSize':0x2});_0x233c6d['TripleDES']=_0x7f552e['_createHelper'](_0xab5f94);}(),_0x3e3a4b['TripleDES'];}));},'a817':function(_0x3f2965,_0x5450ba,_0x5ef700){(function(_0xf944c5,_0x58a12f,_0x22f2e0){_0x3f2965['exports']=_0x58a12f(_0x5ef700('21bf'),_0x5ef700('38ba'));}(0x0,function(_0x3b1c85){return _0x3b1c85['pad']['AnsiX923']={'pad':function(_0x2d35fe,_0x180783){var _0x341be6=_0x2d35fe['sigBytes'],_0x381f5e=0x4*_0x180783,_0x29e06d=_0x381f5e-_0x341be6%_0x381f5e,_0x5991cd=_0x341be6+_0x29e06d-0x1;_0x2d35fe['clamp'](),_0x2d35fe['words'][_0x5991cd>>>0x2]|=_0x29e06d<<0x18-_0x5991cd%0x4*0x8,_0x2d35fe['sigBytes']+=_0x29e06d;},'unpad':function(_0x4e18cf){var _0x488345=0xff&_0x4e18cf['words'][_0x4e18cf['sigBytes']-0x1>>>0x2];_0x4e18cf['sigBytes']-=_0x488345;}},_0x3b1c85['pad']['Ansix923'];}));},'a8ce':function(_0x5a33c3,_0x338dcf,_0x431372){(function(_0x3ca2e7,_0x1591a7){_0x5a33c3['exports']=_0x1591a7(_0x431372('21bf'));}(0x0,function(_0xd223d0){return function(){var _0x531e55=_0xd223d0,_0x5488fb=_0x531e55['lib'],_0xdf18c=_0x5488fb['WordArray'],_0x19aca0=_0x531e55['enc'];_0x19aca0['Utf16']=_0x19aca0['Utf16BE']={'stringify':function(_0x39e62f){for(var _0x5dd099=_0x39e62f['words'],_0x58e029=_0x39e62f['sigBytes'],_0x38ba8f=[],_0x599d2e=0x0;_0x599d2e<_0x58e029;_0x599d2e+=0x2){var _0x30555b=_0x5dd099[_0x599d2e>>>0x2]>>>0x10-_0x599d2e%0x4*0x8&0xffff;_0x38ba8f['push'](String['fromCharCode'](_0x30555b));}return _0x38ba8f['join']('');},'parse':function(_0x42b16e){for(var _0x3297d0=_0x42b16e['length'],_0x52c144=[],_0x3f0cef=0x0;_0x3f0cef<_0x3297d0;_0x3f0cef++)_0x52c144[_0x3f0cef>>>0x1]|=_0x42b16e['charCodeAt'](_0x3f0cef)<<0x10-_0x3f0cef%0x2*0x10;return _0xdf18c['create'](_0x52c144,0x2*_0x3297d0);}};function _0x32fb7d(_0xc31af2){return _0xc31af2<<0x8&0xff00ff00|_0xc31af2>>>0x8&0xff00ff;}_0x19aca0['Utf16LE']={'stringify':function(_0x315b4e){for(var _0x2a20eb=_0x315b4e['words'],_0x22a3df=_0x315b4e['sigBytes'],_0x41a998=[],_0x1d7536=0x0;_0x1d7536<_0x22a3df;_0x1d7536+=0x2){var _0x377ff0=_0x32fb7d(_0x2a20eb[_0x1d7536>>>0x2]>>>0x10-_0x1d7536%0x4*0x8&0xffff);_0x41a998['push'](String['fromCharCode'](_0x377ff0));}return _0x41a998['join']('');},'parse':function(_0x323217){for(var _0x56bc90=_0x323217['length'],_0x2aa60b=[],_0x10e1c5=0x0;_0x10e1c5<_0x56bc90;_0x10e1c5++)_0x2aa60b[_0x10e1c5>>>0x1]|=_0x32fb7d(_0x323217['charCodeAt'](_0x10e1c5)<<0x10-_0x10e1c5%0x2*0x10);return _0xdf18c['create'](_0x2aa60b,0x2*_0x56bc90);}};}(),_0xd223d0['enc']['Utf16'];}));},'aaef':function(_0x14d534,_0x32b94f,_0x40617e){(function(_0x1d3c53,_0x336b57,_0x25e4e0){_0x14d534['exports']=_0x336b57(_0x40617e('21bf'),_0x40617e('38ba'));}(0x0,function(_0x51e6ee){/** @preserve * Counter block mode compatible with Dr Brian Gladman fileenc.c * derived from CryptoJS.mode.CTR * Jan Hruby jhruby.web@gmail.com */ return _0x51e6ee['mode']['CTRGladman']=function(){var _0x16739c=_0x51e6ee['lib']['BlockCipherMode']['extend']();function _0x327a92(_0x3698a6){if(0xff===(_0x3698a6>>0x18&0xff)){var _0x56c7cf=_0x3698a6>>0x10&0xff,_0x5a8352=_0x3698a6>>0x8&0xff,_0x44ddac=0xff&_0x3698a6;0xff===_0x56c7cf?(_0x56c7cf=0x0,0xff===_0x5a8352?(_0x5a8352=0x0,0xff===_0x44ddac?_0x44ddac=0x0:++_0x44ddac):++_0x5a8352):++_0x56c7cf,_0x3698a6=0x0,_0x3698a6+=_0x56c7cf<<0x10,_0x3698a6+=_0x5a8352<<0x8,_0x3698a6+=_0x44ddac;}else _0x3698a6+=0x1<<0x18;return _0x3698a6;}function _0x2f4f51(_0x24e35a){return 0x0===(_0x24e35a[0x0]=_0x327a92(_0x24e35a[0x0]))&&(_0x24e35a[0x1]=_0x327a92(_0x24e35a[0x1])),_0x24e35a;}var _0x41cb48=_0x16739c['Encryptor']=_0x16739c['extend']({'processBlock':function(_0x4b8846,_0x11e8ba){var _0x3c094c=this['_cipher'],_0x5a2cc8=_0x3c094c['blockSize'],_0x4f0b68=this['_iv'],_0x5c3ed9=this['_counter'];_0x4f0b68&&(_0x5c3ed9=this['_counter']=_0x4f0b68['slice'](0x0),this['_iv']=void 0x0),_0x2f4f51(_0x5c3ed9);var _0x513cba=_0x5c3ed9['slice'](0x0);_0x3c094c['encryptBlock'](_0x513cba,0x0);for(var _0x1906f1=0x0;_0x1906f1<_0x5a2cc8;_0x1906f1++)_0x4b8846[_0x11e8ba+_0x1906f1]^=_0x513cba[_0x1906f1];}});return _0x16739c['Decryptor']=_0x41cb48,_0x16739c;}(),_0x51e6ee['mode']['CTRGladman'];}));},'b86b':function(_0x1d1e2a,_0x1a3bad,_0x504ce2){(function(_0x3d0f8e,_0x3e48ae,_0x24f3ea){_0x1d1e2a['exports']=_0x3e48ae(_0x504ce2('21bf'),_0x504ce2('3252'),_0x504ce2('d6e6'));}(0x0,function(_0x12bc13){return function(){var _0x49f3ac=_0x12bc13,_0x17e4ce=_0x49f3ac['x64'],_0x2a1caf=_0x17e4ce['Word'],_0x5eac3b=_0x17e4ce['WordArray'],_0x5f567e=_0x49f3ac['algo'],_0x2d8d22=_0x5f567e['SHA512'],_0xfe83b4=_0x5f567e['SHA384']=_0x2d8d22['extend']({'_doReset':function(){this['_hash']=new _0x5eac3b['init']([new _0x2a1caf['init'](0xcbbb9d5d,0xc1059ed8),new _0x2a1caf['init'](0x629a292a,0x367cd507),new _0x2a1caf['init'](0x9159015a,0x3070dd17),new _0x2a1caf['init'](0x152fecd8,0xf70e5939),new _0x2a1caf['init'](0x67332667,0xffc00b31),new _0x2a1caf['init'](0x8eb44a87,0x68581511),new _0x2a1caf['init'](0xdb0c2e0d,0x64f98fa7),new _0x2a1caf['init'](0x47b5481d,0xbefa4fa4)]);},'_doFinalize':function(){var _0x5f032a=_0x2d8d22['_doFinalize']['call'](this);return _0x5f032a['sigBytes']-=0x10,_0x5f032a;}});_0x49f3ac['SHA384']=_0x2d8d22['_createHelper'](_0xfe83b4),_0x49f3ac['HmacSHA384']=_0x2d8d22['_createHmacHelper'](_0xfe83b4);}(),_0x12bc13['SHA384'];}));},'b86c':function(_0x26cf99,_0x3f652b,_0x543236){(function(_0x56383c,_0xbdb6d7,_0x3a0ab2){_0x26cf99['exports']=_0xbdb6d7(_0x543236('21bf'),_0x543236('38ba'));}(0x0,function(_0x235048){return _0x235048['pad']['NoPadding']={'pad':function(){},'unpad':function(){}},_0x235048['pad']['NoPadding'];}));},'c198':function(_0x5598ba,_0x56de16,_0x52aeff){(function(_0x41b0de,_0x242838,_0x4bb6f5){_0x5598ba['exports']=_0x242838(_0x52aeff('21bf'),_0x52aeff('1132'),_0x52aeff('72fe'),_0x52aeff('2b79'),_0x52aeff('38ba'));}(0x0,function(_0x3298b0){return function(){var _0x1dace4=_0x3298b0,_0x2cca84=_0x1dace4['lib'],_0x396599=_0x2cca84['BlockCipher'],_0xa7a96c=_0x1dace4['algo'],_0x4c43bf=[],_0x20ca76=[],_0xe1abba=[],_0x21be09=[],_0x56f3fd=[],_0x516391=[],_0x486bd2=[],_0x7d9071=[],_0x39ec65=[],_0x5e27ce=[];(function(){for(var _0x1d05fa=[],_0x1b5d0f=0x0;_0x1b5d0f<0x100;_0x1b5d0f++)_0x1d05fa[_0x1b5d0f]=_0x1b5d0f<0x80?_0x1b5d0f<<0x1:_0x1b5d0f<<0x1^0x11b;var _0x5f3da9=0x0,_0x4f0f1d=0x0;for(_0x1b5d0f=0x0;_0x1b5d0f<0x100;_0x1b5d0f++){var _0x7e25ca=_0x4f0f1d^_0x4f0f1d<<0x1^_0x4f0f1d<<0x2^_0x4f0f1d<<0x3^_0x4f0f1d<<0x4;_0x7e25ca=_0x7e25ca>>>0x8^0xff&_0x7e25ca^0x63,_0x4c43bf[_0x5f3da9]=_0x7e25ca,_0x20ca76[_0x7e25ca]=_0x5f3da9;var _0x11e86c=_0x1d05fa[_0x5f3da9],_0x315732=_0x1d05fa[_0x11e86c],_0x5eef20=_0x1d05fa[_0x315732],_0x2d1d2c=0x101*_0x1d05fa[_0x7e25ca]^0x1010100*_0x7e25ca;_0xe1abba[_0x5f3da9]=_0x2d1d2c<<0x18|_0x2d1d2c>>>0x8,_0x21be09[_0x5f3da9]=_0x2d1d2c<<0x10|_0x2d1d2c>>>0x10,_0x56f3fd[_0x5f3da9]=_0x2d1d2c<<0x8|_0x2d1d2c>>>0x18,_0x516391[_0x5f3da9]=_0x2d1d2c;_0x2d1d2c=0x1010101*_0x5eef20^0x10001*_0x315732^0x101*_0x11e86c^0x1010100*_0x5f3da9;_0x486bd2[_0x7e25ca]=_0x2d1d2c<<0x18|_0x2d1d2c>>>0x8,_0x7d9071[_0x7e25ca]=_0x2d1d2c<<0x10|_0x2d1d2c>>>0x10,_0x39ec65[_0x7e25ca]=_0x2d1d2c<<0x8|_0x2d1d2c>>>0x18,_0x5e27ce[_0x7e25ca]=_0x2d1d2c,_0x5f3da9?(_0x5f3da9=_0x11e86c^_0x1d05fa[_0x1d05fa[_0x1d05fa[_0x5eef20^_0x11e86c]]],_0x4f0f1d^=_0x1d05fa[_0x1d05fa[_0x4f0f1d]]):_0x5f3da9=_0x4f0f1d=0x1;}}());var _0x161760=[0x0,0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x1b,0x36],_0xf9d774=_0xa7a96c['AES']=_0x396599['extend']({'_doReset':function(){if(!this['_nRounds']||this['_keyPriorReset']!==this['_key']){for(var _0x4dfa8e=this['_keyPriorReset']=this['_key'],_0x244e6b=_0x4dfa8e['words'],_0x210cec=_0x4dfa8e['sigBytes']/0x4,_0xc8f3b8=this['_nRounds']=_0x210cec+0x6,_0x50972d=0x4*(_0xc8f3b8+0x1),_0xb46572=this['_keySchedule']=[],_0xbc8139=0x0;_0xbc8139<_0x50972d;_0xbc8139++)if(_0xbc8139<_0x210cec)_0xb46572[_0xbc8139]=_0x244e6b[_0xbc8139];else{var _0x4e31c2=_0xb46572[_0xbc8139-0x1];_0xbc8139%_0x210cec?_0x210cec>0x6&&_0xbc8139%_0x210cec==0x4&&(_0x4e31c2=_0x4c43bf[_0x4e31c2>>>0x18]<<0x18|_0x4c43bf[_0x4e31c2>>>0x10&0xff]<<0x10|_0x4c43bf[_0x4e31c2>>>0x8&0xff]<<0x8|_0x4c43bf[0xff&_0x4e31c2]):(_0x4e31c2=_0x4e31c2<<0x8|_0x4e31c2>>>0x18,_0x4e31c2=_0x4c43bf[_0x4e31c2>>>0x18]<<0x18|_0x4c43bf[_0x4e31c2>>>0x10&0xff]<<0x10|_0x4c43bf[_0x4e31c2>>>0x8&0xff]<<0x8|_0x4c43bf[0xff&_0x4e31c2],_0x4e31c2^=_0x161760[_0xbc8139/_0x210cec|0x0]<<0x18),_0xb46572[_0xbc8139]=_0xb46572[_0xbc8139-_0x210cec]^_0x4e31c2;}for(var _0x4ab28e=this['_invKeySchedule']=[],_0x14c0be=0x0;_0x14c0be<_0x50972d;_0x14c0be++){_0xbc8139=_0x50972d-_0x14c0be;if(_0x14c0be%0x4)_0x4e31c2=_0xb46572[_0xbc8139];else _0x4e31c2=_0xb46572[_0xbc8139-0x4];_0x4ab28e[_0x14c0be]=_0x14c0be<0x4||_0xbc8139<=0x4?_0x4e31c2:_0x486bd2[_0x4c43bf[_0x4e31c2>>>0x18]]^_0x7d9071[_0x4c43bf[_0x4e31c2>>>0x10&0xff]]^_0x39ec65[_0x4c43bf[_0x4e31c2>>>0x8&0xff]]^_0x5e27ce[_0x4c43bf[0xff&_0x4e31c2]];}}},'encryptBlock':function(_0xfb51c2,_0x1a3f9a){this['_doCryptBlock'](_0xfb51c2,_0x1a3f9a,this['_keySchedule'],_0xe1abba,_0x21be09,_0x56f3fd,_0x516391,_0x4c43bf);},'decryptBlock':function(_0xdffafb,_0x13e77f){var _0x587668=_0xdffafb[_0x13e77f+0x1];_0xdffafb[_0x13e77f+0x1]=_0xdffafb[_0x13e77f+0x3],_0xdffafb[_0x13e77f+0x3]=_0x587668,this['_doCryptBlock'](_0xdffafb,_0x13e77f,this['_invKeySchedule'],_0x486bd2,_0x7d9071,_0x39ec65,_0x5e27ce,_0x20ca76);_0x587668=_0xdffafb[_0x13e77f+0x1];_0xdffafb[_0x13e77f+0x1]=_0xdffafb[_0x13e77f+0x3],_0xdffafb[_0x13e77f+0x3]=_0x587668;},'_doCryptBlock':function(_0x53597e,_0x5636ca,_0x3a4ce2,_0x3077d9,_0x2ed376,_0x4eb506,_0x376e1a,_0x4b676e){for(var _0x2a9d76=this['_nRounds'],_0x4cbe25=_0x53597e[_0x5636ca]^_0x3a4ce2[0x0],_0x19f03d=_0x53597e[_0x5636ca+0x1]^_0x3a4ce2[0x1],_0x247080=_0x53597e[_0x5636ca+0x2]^_0x3a4ce2[0x2],_0x3d5ab1=_0x53597e[_0x5636ca+0x3]^_0x3a4ce2[0x3],_0x3d667b=0x4,_0xaa3d0f=0x1;_0xaa3d0f<_0x2a9d76;_0xaa3d0f++){var _0x3692a1=_0x3077d9[_0x4cbe25>>>0x18]^_0x2ed376[_0x19f03d>>>0x10&0xff]^_0x4eb506[_0x247080>>>0x8&0xff]^_0x376e1a[0xff&_0x3d5ab1]^_0x3a4ce2[_0x3d667b++],_0x69a776=_0x3077d9[_0x19f03d>>>0x18]^_0x2ed376[_0x247080>>>0x10&0xff]^_0x4eb506[_0x3d5ab1>>>0x8&0xff]^_0x376e1a[0xff&_0x4cbe25]^_0x3a4ce2[_0x3d667b++],_0x1df39c=_0x3077d9[_0x247080>>>0x18]^_0x2ed376[_0x3d5ab1>>>0x10&0xff]^_0x4eb506[_0x4cbe25>>>0x8&0xff]^_0x376e1a[0xff&_0x19f03d]^_0x3a4ce2[_0x3d667b++],_0x2ff037=_0x3077d9[_0x3d5ab1>>>0x18]^_0x2ed376[_0x4cbe25>>>0x10&0xff]^_0x4eb506[_0x19f03d>>>0x8&0xff]^_0x376e1a[0xff&_0x247080]^_0x3a4ce2[_0x3d667b++];_0x4cbe25=_0x3692a1,_0x19f03d=_0x69a776,_0x247080=_0x1df39c,_0x3d5ab1=_0x2ff037;}_0x3692a1=(_0x4b676e[_0x4cbe25>>>0x18]<<0x18|_0x4b676e[_0x19f03d>>>0x10&0xff]<<0x10|_0x4b676e[_0x247080>>>0x8&0xff]<<0x8|_0x4b676e[0xff&_0x3d5ab1])^_0x3a4ce2[_0x3d667b++],_0x69a776=(_0x4b676e[_0x19f03d>>>0x18]<<0x18|_0x4b676e[_0x247080>>>0x10&0xff]<<0x10|_0x4b676e[_0x3d5ab1>>>0x8&0xff]<<0x8|_0x4b676e[0xff&_0x4cbe25])^_0x3a4ce2[_0x3d667b++],_0x1df39c=(_0x4b676e[_0x247080>>>0x18]<<0x18|_0x4b676e[_0x3d5ab1>>>0x10&0xff]<<0x10|_0x4b676e[_0x4cbe25>>>0x8&0xff]<<0x8|_0x4b676e[0xff&_0x19f03d])^_0x3a4ce2[_0x3d667b++],_0x2ff037=(_0x4b676e[_0x3d5ab1>>>0x18]<<0x18|_0x4b676e[_0x4cbe25>>>0x10&0xff]<<0x10|_0x4b676e[_0x19f03d>>>0x8&0xff]<<0x8|_0x4b676e[0xff&_0x247080])^_0x3a4ce2[_0x3d667b++];_0x53597e[_0x5636ca]=_0x3692a1,_0x53597e[_0x5636ca+0x1]=_0x69a776,_0x53597e[_0x5636ca+0x2]=_0x1df39c,_0x53597e[_0x5636ca+0x3]=_0x2ff037;},'keySize':0x8});_0x1dace4['AES']=_0x396599['_createHelper'](_0xf9d774);}(),_0x3298b0['AES'];}));},'c3b6':function(_0xb2a8de,_0x333f65,_0x1a3eea){(function(_0x3edd72,_0x1ef540,_0x2522cf){_0xb2a8de['exports']=_0x1ef540(_0x1a3eea('21bf'),_0x1a3eea('1132'),_0x1a3eea('72fe'),_0x1a3eea('2b79'),_0x1a3eea('38ba'));}(0x0,function(_0xa46667){return function(){var _0x265771=_0xa46667,_0x384a23=_0x265771['lib'],_0x2f5d0a=_0x384a23['StreamCipher'],_0x3423d7=_0x265771['algo'],_0x5cfc2d=_0x3423d7['RC4']=_0x2f5d0a['extend']({'_doReset':function(){for(var _0x3c0f55=this['_key'],_0x17a863=_0x3c0f55['words'],_0x35958c=_0x3c0f55['sigBytes'],_0x3c5c48=this['_S']=[],_0x49dbcb=0x0;_0x49dbcb<0x100;_0x49dbcb++)_0x3c5c48[_0x49dbcb]=_0x49dbcb;_0x49dbcb=0x0;for(var _0x3f85bf=0x0;_0x49dbcb<0x100;_0x49dbcb++){var _0x275146=_0x49dbcb%_0x35958c,_0x382245=_0x17a863[_0x275146>>>0x2]>>>0x18-_0x275146%0x4*0x8&0xff;_0x3f85bf=(_0x3f85bf+_0x3c5c48[_0x49dbcb]+_0x382245)%0x100;var _0x554dd3=_0x3c5c48[_0x49dbcb];_0x3c5c48[_0x49dbcb]=_0x3c5c48[_0x3f85bf],_0x3c5c48[_0x3f85bf]=_0x554dd3;}this['_i']=this['_j']=0x0;},'_doProcessBlock':function(_0x502d0c,_0x3b52a8){_0x502d0c[_0x3b52a8]^=_0x15b286['call'](this);},'keySize':0x8,'ivSize':0x0});function _0x15b286(){for(var _0x4fd310=this['_S'],_0x45f0b1=this['_i'],_0x32ebd1=this['_j'],_0x3984b8=0x0,_0x44cf97=0x0;_0x44cf97<0x4;_0x44cf97++){_0x45f0b1=(_0x45f0b1+0x1)%0x100,_0x32ebd1=(_0x32ebd1+_0x4fd310[_0x45f0b1])%0x100;var _0x230784=_0x4fd310[_0x45f0b1];_0x4fd310[_0x45f0b1]=_0x4fd310[_0x32ebd1],_0x4fd310[_0x32ebd1]=_0x230784,_0x3984b8|=_0x4fd310[(_0x4fd310[_0x45f0b1]+_0x4fd310[_0x32ebd1])%0x100]<<0x18-0x8*_0x44cf97;}return this['_i']=_0x45f0b1,this['_j']=_0x32ebd1,_0x3984b8;}_0x265771['RC4']=_0x2f5d0a['_createHelper'](_0x5cfc2d);var _0x4d5bab=_0x3423d7['RC4Drop']=_0x5cfc2d['extend']({'cfg':_0x5cfc2d['cfg']['extend']({'drop':0xc0}),'_doReset':function(){_0x5cfc2d['_doReset']['call'](this);for(var _0x5ef505=this['cfg']['drop'];_0x5ef505>0x0;_0x5ef505--)_0x15b286['call'](this);}});_0x265771['RC4Drop']=_0x2f5d0a['_createHelper'](_0x4d5bab);}(),_0xa46667['RC4'];}));},'d6e6':function(_0x267839,_0x330831,_0x20566a){(function(_0x9b6986,_0x2e3e76,_0x4501a7){_0x267839['exports']=_0x2e3e76(_0x20566a('21bf'),_0x20566a('3252'));}(0x0,function(_0x556890){return function(){var _0x31055f=_0x556890,_0x4d3ef0=_0x31055f['lib'],_0x33ed8a=_0x4d3ef0['Hasher'],_0xb469fd=_0x31055f['x64'],_0x542b67=_0xb469fd['Word'],_0x565917=_0xb469fd['WordArray'],_0x146a12=_0x31055f['algo'];function _0x4d37c3(){return _0x542b67['create']['apply'](_0x542b67,arguments);}var _0x2a5b30=[_0x4d37c3(0x428a2f98,0xd728ae22),_0x4d37c3(0x71374491,0x23ef65cd),_0x4d37c3(0xb5c0fbcf,0xec4d3b2f),_0x4d37c3(0xe9b5dba5,0x8189dbbc),_0x4d37c3(0x3956c25b,0xf348b538),_0x4d37c3(0x59f111f1,0xb605d019),_0x4d37c3(0x923f82a4,0xaf194f9b),_0x4d37c3(0xab1c5ed5,0xda6d8118),_0x4d37c3(0xd807aa98,0xa3030242),_0x4d37c3(0x12835b01,0x45706fbe),_0x4d37c3(0x243185be,0x4ee4b28c),_0x4d37c3(0x550c7dc3,0xd5ffb4e2),_0x4d37c3(0x72be5d74,0xf27b896f),_0x4d37c3(0x80deb1fe,0x3b1696b1),_0x4d37c3(0x9bdc06a7,0x25c71235),_0x4d37c3(0xc19bf174,0xcf692694),_0x4d37c3(0xe49b69c1,0x9ef14ad2),_0x4d37c3(0xefbe4786,0x384f25e3),_0x4d37c3(0xfc19dc6,0x8b8cd5b5),_0x4d37c3(0x240ca1cc,0x77ac9c65),_0x4d37c3(0x2de92c6f,0x592b0275),_0x4d37c3(0x4a7484aa,0x6ea6e483),_0x4d37c3(0x5cb0a9dc,0xbd41fbd4),_0x4d37c3(0x76f988da,0x831153b5),_0x4d37c3(0x983e5152,0xee66dfab),_0x4d37c3(0xa831c66d,0x2db43210),_0x4d37c3(0xb00327c8,0x98fb213f),_0x4d37c3(0xbf597fc7,0xbeef0ee4),_0x4d37c3(0xc6e00bf3,0x3da88fc2),_0x4d37c3(0xd5a79147,0x930aa725),_0x4d37c3(0x6ca6351,0xe003826f),_0x4d37c3(0x14292967,0xa0e6e70),_0x4d37c3(0x27b70a85,0x46d22ffc),_0x4d37c3(0x2e1b2138,0x5c26c926),_0x4d37c3(0x4d2c6dfc,0x5ac42aed),_0x4d37c3(0x53380d13,0x9d95b3df),_0x4d37c3(0x650a7354,0x8baf63de),_0x4d37c3(0x766a0abb,0x3c77b2a8),_0x4d37c3(0x81c2c92e,0x47edaee6),_0x4d37c3(0x92722c85,0x1482353b),_0x4d37c3(0xa2bfe8a1,0x4cf10364),_0x4d37c3(0xa81a664b,0xbc423001),_0x4d37c3(0xc24b8b70,0xd0f89791),_0x4d37c3(0xc76c51a3,0x654be30),_0x4d37c3(0xd192e819,0xd6ef5218),_0x4d37c3(0xd6990624,0x5565a910),_0x4d37c3(0xf40e3585,0x5771202a),_0x4d37c3(0x106aa070,0x32bbd1b8),_0x4d37c3(0x19a4c116,0xb8d2d0c8),_0x4d37c3(0x1e376c08,0x5141ab53),_0x4d37c3(0x2748774c,0xdf8eeb99),_0x4d37c3(0x34b0bcb5,0xe19b48a8),_0x4d37c3(0x391c0cb3,0xc5c95a63),_0x4d37c3(0x4ed8aa4a,0xe3418acb),_0x4d37c3(0x5b9cca4f,0x7763e373),_0x4d37c3(0x682e6ff3,0xd6b2b8a3),_0x4d37c3(0x748f82ee,0x5defb2fc),_0x4d37c3(0x78a5636f,0x43172f60),_0x4d37c3(0x84c87814,0xa1f0ab72),_0x4d37c3(0x8cc70208,0x1a6439ec),_0x4d37c3(0x90befffa,0x23631e28),_0x4d37c3(0xa4506ceb,0xde82bde9),_0x4d37c3(0xbef9a3f7,0xb2c67915),_0x4d37c3(0xc67178f2,0xe372532b),_0x4d37c3(0xca273ece,0xea26619c),_0x4d37c3(0xd186b8c7,0x21c0c207),_0x4d37c3(0xeada7dd6,0xcde0eb1e),_0x4d37c3(0xf57d4f7f,0xee6ed178),_0x4d37c3(0x6f067aa,0x72176fba),_0x4d37c3(0xa637dc5,0xa2c898a6),_0x4d37c3(0x113f9804,0xbef90dae),_0x4d37c3(0x1b710b35,0x131c471b),_0x4d37c3(0x28db77f5,0x23047d84),_0x4d37c3(0x32caab7b,0x40c72493),_0x4d37c3(0x3c9ebe0a,0x15c9bebc),_0x4d37c3(0x431d67c4,0x9c100d4c),_0x4d37c3(0x4cc5d4be,0xcb3e42b6),_0x4d37c3(0x597f299c,0xfc657e2a),_0x4d37c3(0x5fcb6fab,0x3ad6faec),_0x4d37c3(0x6c44198c,0x4a475817)],_0x2f7371=[];(function(){for(var _0x476a4b=0x0;_0x476a4b<0x50;_0x476a4b++)_0x2f7371[_0x476a4b]=_0x4d37c3();}());var _0x5b5d51=_0x146a12['SHA512']=_0x33ed8a['extend']({'_doReset':function(){this['_hash']=new _0x565917['init']([new _0x542b67['init'](0x6a09e667,0xf3bcc908),new _0x542b67['init'](0xbb67ae85,0x84caa73b),new _0x542b67['init'](0x3c6ef372,0xfe94f82b),new _0x542b67['init'](0xa54ff53a,0x5f1d36f1),new _0x542b67['init'](0x510e527f,0xade682d1),new _0x542b67['init'](0x9b05688c,0x2b3e6c1f),new _0x542b67['init'](0x1f83d9ab,0xfb41bd6b),new _0x542b67['init'](0x5be0cd19,0x137e2179)]);},'_doProcessBlock':function(_0x4a88c5,_0x4a9a8d){for(var _0x2db49b=this['_hash']['words'],_0x15f96e=_0x2db49b[0x0],_0x22e69b=_0x2db49b[0x1],_0x204f52=_0x2db49b[0x2],_0x47dab3=_0x2db49b[0x3],_0x3c6354=_0x2db49b[0x4],_0x8ee83=_0x2db49b[0x5],_0x2becc0=_0x2db49b[0x6],_0x2e87b9=_0x2db49b[0x7],_0x1fe87e=_0x15f96e['high'],_0x1c64cf=_0x15f96e['low'],_0x57cab4=_0x22e69b['high'],_0x4e6f30=_0x22e69b['low'],_0x42207d=_0x204f52['high'],_0x195e34=_0x204f52['low'],_0x26b6f0=_0x47dab3['high'],_0xad6055=_0x47dab3['low'],_0x505435=_0x3c6354['high'],_0x42e53d=_0x3c6354['low'],_0x4c7bc5=_0x8ee83['high'],_0x47142e=_0x8ee83['low'],_0x4e8bca=_0x2becc0['high'],_0x413626=_0x2becc0['low'],_0x5060f0=_0x2e87b9['high'],_0x1b0392=_0x2e87b9['low'],_0x1505a4=_0x1fe87e,_0x2dbee7=_0x1c64cf,_0x191005=_0x57cab4,_0x13ea6a=_0x4e6f30,_0x3e7542=_0x42207d,_0x1a835f=_0x195e34,_0x70cf2f=_0x26b6f0,_0x1a12d7=_0xad6055,_0x37ee15=_0x505435,_0x3dd6e9=_0x42e53d,_0x1a8b7a=_0x4c7bc5,_0x3051fa=_0x47142e,_0x491ab5=_0x4e8bca,_0x4d9ca8=_0x413626,_0x274487=_0x5060f0,_0x5d7be7=_0x1b0392,_0x58ec48=0x0;_0x58ec48<0x50;_0x58ec48++){var _0x113abc=_0x2f7371[_0x58ec48];if(_0x58ec48<0x10)var _0x33f84a=_0x113abc['high']=0x0|_0x4a88c5[_0x4a9a8d+0x2*_0x58ec48],_0x211a3f=_0x113abc['low']=0x0|_0x4a88c5[_0x4a9a8d+0x2*_0x58ec48+0x1];else{var _0x51e458=_0x2f7371[_0x58ec48-0xf],_0x3e4496=_0x51e458['high'],_0x8dd5d6=_0x51e458['low'],_0x3a2fd6=(_0x3e4496>>>0x1|_0x8dd5d6<<0x1f)^(_0x3e4496>>>0x8|_0x8dd5d6<<0x18)^_0x3e4496>>>0x7,_0xce7972=(_0x8dd5d6>>>0x1|_0x3e4496<<0x1f)^(_0x8dd5d6>>>0x8|_0x3e4496<<0x18)^(_0x8dd5d6>>>0x7|_0x3e4496<<0x19),_0x2b537c=_0x2f7371[_0x58ec48-0x2],_0x3bf7bc=_0x2b537c['high'],_0x268441=_0x2b537c['low'],_0x21153c=(_0x3bf7bc>>>0x13|_0x268441<<0xd)^(_0x3bf7bc<<0x3|_0x268441>>>0x1d)^_0x3bf7bc>>>0x6,_0x413699=(_0x268441>>>0x13|_0x3bf7bc<<0xd)^(_0x268441<<0x3|_0x3bf7bc>>>0x1d)^(_0x268441>>>0x6|_0x3bf7bc<<0x1a),_0x4ecb40=_0x2f7371[_0x58ec48-0x7],_0xb0c2b7=_0x4ecb40['high'],_0x2a78d1=_0x4ecb40['low'],_0x3c1718=_0x2f7371[_0x58ec48-0x10],_0x530d37=_0x3c1718['high'],_0x8d6fe1=_0x3c1718['low'];_0x211a3f=_0xce7972+_0x2a78d1,_0x33f84a=_0x3a2fd6+_0xb0c2b7+(_0x211a3f>>>0x0<_0xce7972>>>0x0?0x1:0x0),_0x211a3f=_0x211a3f+_0x413699,_0x33f84a=_0x33f84a+_0x21153c+(_0x211a3f>>>0x0<_0x413699>>>0x0?0x1:0x0),_0x211a3f=_0x211a3f+_0x8d6fe1,_0x33f84a=_0x33f84a+_0x530d37+(_0x211a3f>>>0x0<_0x8d6fe1>>>0x0?0x1:0x0);_0x113abc['high']=_0x33f84a,_0x113abc['low']=_0x211a3f;}var _0x4e603b=_0x37ee15&_0x1a8b7a^~_0x37ee15&_0x491ab5,_0x316ebf=_0x3dd6e9&_0x3051fa^~_0x3dd6e9&_0x4d9ca8,_0x32fcea=_0x1505a4&_0x191005^_0x1505a4&_0x3e7542^_0x191005&_0x3e7542,_0x3d5144=_0x2dbee7&_0x13ea6a^_0x2dbee7&_0x1a835f^_0x13ea6a&_0x1a835f,_0x5e2666=(_0x1505a4>>>0x1c|_0x2dbee7<<0x4)^(_0x1505a4<<0x1e|_0x2dbee7>>>0x2)^(_0x1505a4<<0x19|_0x2dbee7>>>0x7),_0x220f34=(_0x2dbee7>>>0x1c|_0x1505a4<<0x4)^(_0x2dbee7<<0x1e|_0x1505a4>>>0x2)^(_0x2dbee7<<0x19|_0x1505a4>>>0x7),_0x3d466b=(_0x37ee15>>>0xe|_0x3dd6e9<<0x12)^(_0x37ee15>>>0x12|_0x3dd6e9<<0xe)^(_0x37ee15<<0x17|_0x3dd6e9>>>0x9),_0x2a0ab8=(_0x3dd6e9>>>0xe|_0x37ee15<<0x12)^(_0x3dd6e9>>>0x12|_0x37ee15<<0xe)^(_0x3dd6e9<<0x17|_0x37ee15>>>0x9),_0x4c2b3a=_0x2a5b30[_0x58ec48],_0x2d0aa4=_0x4c2b3a['high'],_0x4d9680=_0x4c2b3a['low'],_0x3db25a=_0x5d7be7+_0x2a0ab8,_0x27d09a=_0x274487+_0x3d466b+(_0x3db25a>>>0x0<_0x5d7be7>>>0x0?0x1:0x0),_0x1537e2=(_0x3db25a=_0x3db25a+_0x316ebf,_0x27d09a=_0x27d09a+_0x4e603b+(_0x3db25a>>>0x0<_0x316ebf>>>0x0?0x1:0x0),_0x3db25a=_0x3db25a+_0x4d9680,_0x27d09a=_0x27d09a+_0x2d0aa4+(_0x3db25a>>>0x0<_0x4d9680>>>0x0?0x1:0x0),_0x3db25a=_0x3db25a+_0x211a3f,_0x27d09a=_0x27d09a+_0x33f84a+(_0x3db25a>>>0x0<_0x211a3f>>>0x0?0x1:0x0),_0x220f34+_0x3d5144),_0x428e85=_0x5e2666+_0x32fcea+(_0x1537e2>>>0x0<_0x220f34>>>0x0?0x1:0x0);_0x274487=_0x491ab5,_0x5d7be7=_0x4d9ca8,_0x491ab5=_0x1a8b7a,_0x4d9ca8=_0x3051fa,_0x1a8b7a=_0x37ee15,_0x3051fa=_0x3dd6e9,_0x3dd6e9=_0x1a12d7+_0x3db25a|0x0,_0x37ee15=_0x70cf2f+_0x27d09a+(_0x3dd6e9>>>0x0<_0x1a12d7>>>0x0?0x1:0x0)|0x0,_0x70cf2f=_0x3e7542,_0x1a12d7=_0x1a835f,_0x3e7542=_0x191005,_0x1a835f=_0x13ea6a,_0x191005=_0x1505a4,_0x13ea6a=_0x2dbee7,_0x2dbee7=_0x3db25a+_0x1537e2|0x0,_0x1505a4=_0x27d09a+_0x428e85+(_0x2dbee7>>>0x0<_0x3db25a>>>0x0?0x1:0x0)|0x0;}_0x1c64cf=_0x15f96e['low']=_0x1c64cf+_0x2dbee7,_0x15f96e['high']=_0x1fe87e+_0x1505a4+(_0x1c64cf>>>0x0<_0x2dbee7>>>0x0?0x1:0x0),_0x4e6f30=_0x22e69b['low']=_0x4e6f30+_0x13ea6a,_0x22e69b['high']=_0x57cab4+_0x191005+(_0x4e6f30>>>0x0<_0x13ea6a>>>0x0?0x1:0x0),_0x195e34=_0x204f52['low']=_0x195e34+_0x1a835f,_0x204f52['high']=_0x42207d+_0x3e7542+(_0x195e34>>>0x0<_0x1a835f>>>0x0?0x1:0x0),_0xad6055=_0x47dab3['low']=_0xad6055+_0x1a12d7,_0x47dab3['high']=_0x26b6f0+_0x70cf2f+(_0xad6055>>>0x0<_0x1a12d7>>>0x0?0x1:0x0),_0x42e53d=_0x3c6354['low']=_0x42e53d+_0x3dd6e9,_0x3c6354['high']=_0x505435+_0x37ee15+(_0x42e53d>>>0x0<_0x3dd6e9>>>0x0?0x1:0x0),_0x47142e=_0x8ee83['low']=_0x47142e+_0x3051fa,_0x8ee83['high']=_0x4c7bc5+_0x1a8b7a+(_0x47142e>>>0x0<_0x3051fa>>>0x0?0x1:0x0),_0x413626=_0x2becc0['low']=_0x413626+_0x4d9ca8,_0x2becc0['high']=_0x4e8bca+_0x491ab5+(_0x413626>>>0x0<_0x4d9ca8>>>0x0?0x1:0x0),_0x1b0392=_0x2e87b9['low']=_0x1b0392+_0x5d7be7,_0x2e87b9['high']=_0x5060f0+_0x274487+(_0x1b0392>>>0x0<_0x5d7be7>>>0x0?0x1:0x0);},'_doFinalize':function(){var _0x37c1a0=this['_data'],_0x4688dd=_0x37c1a0['words'],_0x1c8662=0x8*this['_nDataBytes'],_0x200422=0x8*_0x37c1a0['sigBytes'];_0x4688dd[_0x200422>>>0x5]|=0x80<<0x18-_0x200422%0x20,_0x4688dd[0x1e+(_0x200422+0x80>>>0xa<<0x5)]=Math['floor'](_0x1c8662/0x100000000),_0x4688dd[0x1f+(_0x200422+0x80>>>0xa<<0x5)]=_0x1c8662,_0x37c1a0['sigBytes']=0x4*_0x4688dd['length'],this['_process']();var _0x2d391b=this['_hash']['toX32']();return _0x2d391b;},'clone':function(){var _0x2bc688=_0x33ed8a['clone']['call'](this);return _0x2bc688['_hash']=this['_hash']['clone'](),_0x2bc688;},'blockSize':0x20});_0x31055f['SHA512']=_0x33ed8a['_createHelper'](_0x5b5d51),_0x31055f['HmacSHA512']=_0x33ed8a['_createHmacHelper'](_0x5b5d51);}(),_0x556890['SHA512'];}));},'df2f':function(_0x28742d,_0x5e7690,_0x37f4f3){(function(_0x42536e,_0x4b3d29){_0x28742d['exports']=_0x4b3d29(_0x37f4f3('21bf'));}(0x0,function(_0x23e72c){return function(){var _0x1d0209=_0x23e72c,_0x5168c7=_0x1d0209['lib'],_0x1e94c4=_0x5168c7['WordArray'],_0x5e8caa=_0x5168c7['Hasher'],_0x48329c=_0x1d0209['algo'],_0x352ec6=[],_0x111211=_0x48329c['SHA1']=_0x5e8caa['extend']({'_doReset':function(){this['_hash']=new _0x1e94c4['init']([0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]);},'_doProcessBlock':function(_0x5bf1ea,_0x8534dc){for(var _0x3ce8f2=this['_hash']['words'],_0x261089=_0x3ce8f2[0x0],_0x1b02d9=_0x3ce8f2[0x1],_0xbe4c94=_0x3ce8f2[0x2],_0x5ea3f1=_0x3ce8f2[0x3],_0x134614=_0x3ce8f2[0x4],_0x2878b8=0x0;_0x2878b8<0x50;_0x2878b8++){if(_0x2878b8<0x10)_0x352ec6[_0x2878b8]=0x0|_0x5bf1ea[_0x8534dc+_0x2878b8];else{var _0x4cf16a=_0x352ec6[_0x2878b8-0x3]^_0x352ec6[_0x2878b8-0x8]^_0x352ec6[_0x2878b8-0xe]^_0x352ec6[_0x2878b8-0x10];_0x352ec6[_0x2878b8]=_0x4cf16a<<0x1|_0x4cf16a>>>0x1f;}var _0x3da440=(_0x261089<<0x5|_0x261089>>>0x1b)+_0x134614+_0x352ec6[_0x2878b8];_0x3da440+=_0x2878b8<0x14?0x5a827999+(_0x1b02d9&_0xbe4c94|~_0x1b02d9&_0x5ea3f1):_0x2878b8<0x28?0x6ed9eba1+(_0x1b02d9^_0xbe4c94^_0x5ea3f1):_0x2878b8<0x3c?(_0x1b02d9&_0xbe4c94|_0x1b02d9&_0x5ea3f1|_0xbe4c94&_0x5ea3f1)-0x70e44324:(_0x1b02d9^_0xbe4c94^_0x5ea3f1)-0x359d3e2a,_0x134614=_0x5ea3f1,_0x5ea3f1=_0xbe4c94,_0xbe4c94=_0x1b02d9<<0x1e|_0x1b02d9>>>0x2,_0x1b02d9=_0x261089,_0x261089=_0x3da440;}_0x3ce8f2[0x0]=_0x3ce8f2[0x0]+_0x261089|0x0,_0x3ce8f2[0x1]=_0x3ce8f2[0x1]+_0x1b02d9|0x0,_0x3ce8f2[0x2]=_0x3ce8f2[0x2]+_0xbe4c94|0x0,_0x3ce8f2[0x3]=_0x3ce8f2[0x3]+_0x5ea3f1|0x0,_0x3ce8f2[0x4]=_0x3ce8f2[0x4]+_0x134614|0x0;},'_doFinalize':function(){var _0x135766=this['_data'],_0x883405=_0x135766['words'],_0x3785b2=0x8*this['_nDataBytes'],_0x298e2b=0x8*_0x135766['sigBytes'];return _0x883405[_0x298e2b>>>0x5]|=0x80<<0x18-_0x298e2b%0x20,_0x883405[0xe+(_0x298e2b+0x40>>>0x9<<0x4)]=Math['floor'](_0x3785b2/0x100000000),_0x883405[0xf+(_0x298e2b+0x40>>>0x9<<0x4)]=_0x3785b2,_0x135766['sigBytes']=0x4*_0x883405['length'],this['_process'](),this['_hash'];},'clone':function(){var _0x3af03d=_0x5e8caa['clone']['call'](this);return _0x3af03d['_hash']=this['_hash']['clone'](),_0x3af03d;}});_0x1d0209['SHA1']=_0x5e8caa['_createHelper'](_0x111211),_0x1d0209['HmacSHA1']=_0x5e8caa['_createHmacHelper'](_0x111211);}(),_0x23e72c['SHA1'];}));},'e61b':function(_0x13d2dd,_0x567a3e,_0x57725a){(function(_0x20798a,_0x12326d,_0x413122){_0x13d2dd['exports']=_0x12326d(_0x57725a('21bf'),_0x57725a('3252'));}(0x0,function(_0x66d4bc){return function(_0x137268){var _0x3ed145=_0x66d4bc,_0x7cc5e7=_0x3ed145['lib'],_0x1476e5=_0x7cc5e7['WordArray'],_0x564448=_0x7cc5e7['Hasher'],_0x4d6fa2=_0x3ed145['x64'],_0x40e4d2=_0x4d6fa2['Word'],_0x42aef8=_0x3ed145['algo'],_0x9c7b92=[],_0x518ca1=[],_0x46c77e=[];(function(){for(var _0x4ebca2=0x1,_0x496333=0x0,_0x56e545=0x0;_0x56e545<0x18;_0x56e545++){_0x9c7b92[_0x4ebca2+0x5*_0x496333]=(_0x56e545+0x1)*(_0x56e545+0x2)/0x2%0x40;var _0x33d980=_0x496333%0x5,_0x450c02=(0x2*_0x4ebca2+0x3*_0x496333)%0x5;_0x4ebca2=_0x33d980,_0x496333=_0x450c02;}for(_0x4ebca2=0x0;_0x4ebca2<0x5;_0x4ebca2++)for(_0x496333=0x0;_0x496333<0x5;_0x496333++)_0x518ca1[_0x4ebca2+0x5*_0x496333]=_0x496333+(0x2*_0x4ebca2+0x3*_0x496333)%0x5*0x5;for(var _0x86d0e5=0x1,_0x1d69fa=0x0;_0x1d69fa<0x18;_0x1d69fa++){for(var _0x1f92b3=0x0,_0x4c625e=0x0,_0x3c0d9d=0x0;_0x3c0d9d<0x7;_0x3c0d9d++){if(0x1&_0x86d0e5){var _0x4db9b0=(0x1<<_0x3c0d9d)-0x1;_0x4db9b0<0x20?_0x4c625e^=0x1<<_0x4db9b0:_0x1f92b3^=0x1<<_0x4db9b0-0x20;}0x80&_0x86d0e5?_0x86d0e5=_0x86d0e5<<0x1^0x71:_0x86d0e5<<=0x1;}_0x46c77e[_0x1d69fa]=_0x40e4d2['create'](_0x1f92b3,_0x4c625e);}}());var _0x337b2b=[];(function(){for(var _0x530b02=0x0;_0x530b02<0x19;_0x530b02++)_0x337b2b[_0x530b02]=_0x40e4d2['create']();}());var _0x4427a8=_0x42aef8['SHA3']=_0x564448['extend']({'cfg':_0x564448['cfg']['extend']({'outputLength':0x200}),'_doReset':function(){for(var _0x2196da=this['_state']=[],_0x422166=0x0;_0x422166<0x19;_0x422166++)_0x2196da[_0x422166]=new _0x40e4d2['init']();this['blockSize']=(0x640-0x2*this['cfg']['outputLength'])/0x20;},'_doProcessBlock':function(_0x374f7b,_0x135244){for(var _0x35f975=this['_state'],_0x218810=this['blockSize']/0x2,_0x32619e=0x0;_0x32619e<_0x218810;_0x32619e++){var _0x8e9fb3=_0x374f7b[_0x135244+0x2*_0x32619e],_0x23d9d1=_0x374f7b[_0x135244+0x2*_0x32619e+0x1];_0x8e9fb3=0xff00ff&(_0x8e9fb3<<0x8|_0x8e9fb3>>>0x18)|0xff00ff00&(_0x8e9fb3<<0x18|_0x8e9fb3>>>0x8),_0x23d9d1=0xff00ff&(_0x23d9d1<<0x8|_0x23d9d1>>>0x18)|0xff00ff00&(_0x23d9d1<<0x18|_0x23d9d1>>>0x8);var _0x682ed3=_0x35f975[_0x32619e];_0x682ed3['high']^=_0x23d9d1,_0x682ed3['low']^=_0x8e9fb3;}for(var _0x343a4c=0x0;_0x343a4c<0x18;_0x343a4c++){for(var _0x4ab4ee=0x0;_0x4ab4ee<0x5;_0x4ab4ee++){for(var _0x7ba049=0x0,_0xb27982=0x0,_0x365666=0x0;_0x365666<0x5;_0x365666++){_0x682ed3=_0x35f975[_0x4ab4ee+0x5*_0x365666];_0x7ba049^=_0x682ed3['high'],_0xb27982^=_0x682ed3['low'];}var _0x410c19=_0x337b2b[_0x4ab4ee];_0x410c19['high']=_0x7ba049,_0x410c19['low']=_0xb27982;}for(_0x4ab4ee=0x0;_0x4ab4ee<0x5;_0x4ab4ee++){var _0x1abdd5=_0x337b2b[(_0x4ab4ee+0x4)%0x5],_0x53bf08=_0x337b2b[(_0x4ab4ee+0x1)%0x5],_0x329a06=_0x53bf08['high'],_0x1705b7=_0x53bf08['low'];for(_0x7ba049=_0x1abdd5['high']^(_0x329a06<<0x1|_0x1705b7>>>0x1f),_0xb27982=_0x1abdd5['low']^(_0x1705b7<<0x1|_0x329a06>>>0x1f),_0x365666=0x0;_0x365666<0x5;_0x365666++){_0x682ed3=_0x35f975[_0x4ab4ee+0x5*_0x365666];_0x682ed3['high']^=_0x7ba049,_0x682ed3['low']^=_0xb27982;}}for(var _0x190161=0x1;_0x190161<0x19;_0x190161++){_0x682ed3=_0x35f975[_0x190161];var _0x2a7a00=_0x682ed3['high'],_0x1bdb4a=_0x682ed3['low'],_0x43999f=_0x9c7b92[_0x190161];if(_0x43999f<0x20)_0x7ba049=_0x2a7a00<<_0x43999f|_0x1bdb4a>>>0x20-_0x43999f,_0xb27982=_0x1bdb4a<<_0x43999f|_0x2a7a00>>>0x20-_0x43999f;else _0x7ba049=_0x1bdb4a<<_0x43999f-0x20|_0x2a7a00>>>0x40-_0x43999f,_0xb27982=_0x2a7a00<<_0x43999f-0x20|_0x1bdb4a>>>0x40-_0x43999f;var _0x2fb713=_0x337b2b[_0x518ca1[_0x190161]];_0x2fb713['high']=_0x7ba049,_0x2fb713['low']=_0xb27982;}var _0x2fba77=_0x337b2b[0x0],_0x4a71fd=_0x35f975[0x0];_0x2fba77['high']=_0x4a71fd['high'],_0x2fba77['low']=_0x4a71fd['low'];for(_0x4ab4ee=0x0;_0x4ab4ee<0x5;_0x4ab4ee++)for(_0x365666=0x0;_0x365666<0x5;_0x365666++){_0x190161=_0x4ab4ee+0x5*_0x365666,_0x682ed3=_0x35f975[_0x190161];var _0xf3ea62=_0x337b2b[_0x190161],_0x2f5f34=_0x337b2b[(_0x4ab4ee+0x1)%0x5+0x5*_0x365666],_0x174314=_0x337b2b[(_0x4ab4ee+0x2)%0x5+0x5*_0x365666];_0x682ed3['high']=_0xf3ea62['high']^~_0x2f5f34['high']&_0x174314['high'],_0x682ed3['low']=_0xf3ea62['low']^~_0x2f5f34['low']&_0x174314['low'];}_0x682ed3=_0x35f975[0x0];var _0x388faf=_0x46c77e[_0x343a4c];_0x682ed3['high']^=_0x388faf['high'],_0x682ed3['low']^=_0x388faf['low'];}},'_doFinalize':function(){var _0x1a58c7=this['_data'],_0x3d4714=_0x1a58c7['words'],_0x1a8d7c=(this['_nDataBytes'],0x8*_0x1a58c7['sigBytes']),_0x4609f2=0x20*this['blockSize'];_0x3d4714[_0x1a8d7c>>>0x5]|=0x1<<0x18-_0x1a8d7c%0x20,_0x3d4714[(_0x137268['ceil']((_0x1a8d7c+0x1)/_0x4609f2)*_0x4609f2>>>0x5)-0x1]|=0x80,_0x1a58c7['sigBytes']=0x4*_0x3d4714['length'],this['_process']();for(var _0x45f1f7=this['_state'],_0x47fe05=this['cfg']['outputLength']/0x8,_0x7d05f3=_0x47fe05/0x8,_0x5117ab=[],_0x31f4b2=0x0;_0x31f4b2<_0x7d05f3;_0x31f4b2++){var _0x46636f=_0x45f1f7[_0x31f4b2],_0x4a7f2f=_0x46636f['high'],_0x207355=_0x46636f['low'];_0x4a7f2f=0xff00ff&(_0x4a7f2f<<0x8|_0x4a7f2f>>>0x18)|0xff00ff00&(_0x4a7f2f<<0x18|_0x4a7f2f>>>0x8),_0x207355=0xff00ff&(_0x207355<<0x8|_0x207355>>>0x18)|0xff00ff00&(_0x207355<<0x18|_0x207355>>>0x8),_0x5117ab['push'](_0x207355),_0x5117ab['push'](_0x4a7f2f);}return new _0x1476e5['init'](_0x5117ab,_0x47fe05);},'clone':function(){for(var _0x58ac6f=_0x564448['clone']['call'](this),_0x1dda57=_0x58ac6f['_state']=this['_state']['slice'](0x0),_0x4ae54f=0x0;_0x4ae54f<0x19;_0x4ae54f++)_0x1dda57[_0x4ae54f]=_0x1dda57[_0x4ae54f]['clone']();return _0x58ac6f;}});_0x3ed145['SHA3']=_0x564448['_createHelper'](_0x4427a8),_0x3ed145['HmacSHA3']=_0x564448['_createHmacHelper'](_0x4427a8);}(Math),_0x66d4bc['SHA3'];}));},'f4ea':function(_0x4db186,_0x39edda,_0x53d69e){(function(_0x329f1d,_0x950c5a,_0x366e3d){_0x4db186['exports']=_0x950c5a(_0x53d69e('21bf'),_0x53d69e('38ba'));}(0x0,function(_0x196b76){return _0x196b76['mode']['CTR']=function(){var _0x4011d5=_0x196b76['lib']['BlockCipherMode']['extend'](),_0x23c95c=_0x4011d5['Encryptor']=_0x4011d5['extend']({'processBlock':function(_0x14c80e,_0x2b0aa9){var _0x199e94=this['_cipher'],_0x5070a4=_0x199e94['blockSize'],_0x49cae6=this['_iv'],_0x4a3ff0=this['_counter'];_0x49cae6&&(_0x4a3ff0=this['_counter']=_0x49cae6['slice'](0x0),this['_iv']=void 0x0);var _0x3f8633=_0x4a3ff0['slice'](0x0);_0x199e94['encryptBlock'](_0x3f8633,0x0),_0x4a3ff0[_0x5070a4-0x1]=_0x4a3ff0[_0x5070a4-0x1]+0x1|0x0;for(var _0x14e209=0x0;_0x14e209<_0x5070a4;_0x14e209++)_0x14c80e[_0x2b0aa9+_0x14e209]^=_0x3f8633[_0x14e209];}});return _0x4011d5['Decryptor']=_0x23c95c,_0x4011d5;}(),_0x196b76['mode']['CTR'];}));}}]); 可以看到一些变量是十六进制字符串，而且代码全被压缩了。 我们就是要在这里找出token和id的构造逻辑。 要完全分析出整个网站的加密逻辑还是有一定难度的，不过不用担心，本节我们会一步一步地讲解逆向的思路、方法和技巧。 14.2 寻找列表页Ajax入口 两种方式： 全局搜索标志字符串 设置Ajax断点 我们之前讲过第二种，现在以第一种为例： 一些关键的字符串通常会被作为寻找JavaScript混淆入口的依据，我们可以通过全局搜索的方式来查找，然后根据搜索到的结果大体观察是否为我们想要找的入口。 重新打开列表页的Ajax接口，看一下请求的Ajax接口，如图： 这里Ajax接口的url为：https://spa6.scrape.center/api/movie/?limit=10&offset=0&token=YjdmMDNmMzMwMzYwOWFjZjE2ZDE2NGJhYTg3NmUxMGU3OWYxYjQ3OCwxNjY0OTcyMDk3 可以看到带有limit、offset、token三个参数，关键就是token，我们就全局搜索是否存在token，点击开发者工具右上角的“三个小竖点”选项，然后点击Search，如图： 这样我们就能进入全局搜索模式，搜索token，可以看到的确搜索到了几个结果，如图： 观察一下，下面的两个结果可能是我们想要的，点击第一个进入看看，此时定位到一个JavaScript文件，如图： 这时可以看到整个代码都是经过压缩的，只有一行，不好看，点击左下角的{}按钮，格式化JavaScript代码，格式化后的结果如图： 可以看到，这里弹出来一个新的选项卡，其名称是“JavaScript文件名 + :formatted”，代表格式化后的代码结果。这里我们再次定位到token观察一下。 可以看到，这里有limit、offset、token。然后观察其他的逻辑，基本上能够确定这就是构造Ajax请求的地方，如果不是的话，可以继续搜索其他的文件观察。 现在，我们就成功找到了混淆的入口，这是一个寻找入口的首先方法。 14.3 寻找列表页加密逻辑 我们已经找到token的位置了，可以观察这个token对应的变量，他叫作_0x263439，所以关键就是要看看这个变量是哪里来的。 这时添加断点就好了。 看下这个变量哪里产生的，然后我们在对应的行添加断点。如图： 这时我们就设置了一个新断点。由于只有一个断点，刷新网页后，我们会发现网页停在新的断点上，如图： 这是我们就可以观察正在运行的一些变量了，比如把鼠标放在各个变量上，可以看到变量的值和类型；把鼠标放在变量_0x2fa7bd上，会有一个浮窗显示，如图： 另外，还可以在右侧的Watch面板中添加想要查看的变量，如这行代码的内容为： , _0x263439 = Object(_0x2fa7bd[‘a’])(this[‘$store’][‘state’][‘url’][‘index’]); 我们比较感兴趣的是_0x2fa7bd，还有this里的$store属性。展开Watch面板。然后点击+号，把想看的变量添加到Watch面板里面，如图： 可以发现 ，_0x2fa7bd是一个对象，它具有属性a，其值是一个方法。this[‘$store’][‘state’][‘url’][‘index’]的值其实就是/api/movie，即Ajax请求的URL的path。__0x263439就是调用前者的方法传入/api/movie得到的。 下一步就是去寻找这个方法。我们可以把Watch面板的_0x2fa7bd展开，这里显示的FunctionLocation就是这个函数的代码位置，如图： 点击进入。 这时我们就进入一个新的名字为_0x456254的方法里，在这个方法中，应该就有token的生成逻辑，添加断点然后点击面板Resume script execution按钮，如图： 接下来，我们不断进行单步调试，观察一下这里的执行逻辑和每一步调试的结果都有什么变化，如图： 在每一步的执行过程中，我们可以发现一些运行值会被打到代码的右侧并高亮显示，同时在Watch面板下还能看到每一步的具体结果。 最后我们总结出这个token的构造逻辑，如下： 传入的/api/movie会构造一个初始化列表，将变量命名为_0x31a891。 获取当前时间戳，命名为_0x5da681，调用push方法将其添加到_0x31a891变量代表的列表中。 将_0x31a891变量用，拼接，然后进行SHA1编码，命名为_0xf7c3c7。 将_0xf7c3c7（SHA1编码的结果）和_0x5da681（时间戳）用逗号拼接，命名为_0x3c8435。 将_0x3c8435进行Base64编码，命名为_0x104b5b，得到最后的token。 经过反复的观察，以上逻辑可以比较轻松地总结出来，其中有些变量可以实时地查看，同时也可以自己输入到控制台上进行反复验证。 现在加密逻辑我们就分析出来了，基本思路就是： 将/api/movie放到一个列表里； 在列表中加入当前的时间戳； 将列表内容用逗号拼接； 将拼接的结果进行SHA1编码； 将编码的结果和时间戳再次拼接； 将拼接后的结果进行Base64编码。 验证一下，如果逻辑没问题，我们就可以用python来实现了。 14.4 使用python实现列表页的爬取 要用python实现这个逻辑，我们需要借助两个库：一个是hashlib，他提供了sha1的方法；另一个是base64库，他提供了b64encode方法对结果进行Base64编码，实现代码如下： import hashlib import time import base64 import requests INDEX_URL = "https://spa6.scrape.center/api/movie/?limit={}&offset={}&token={}" LIMIT = 10 OFFSET = 0 def get_token(args): # * 在列表中加入当前的时间戳； timestamp = str(int(time.time())) args.append(timestamp) # * 将列表内容用逗号拼接； # * 将拼接的结果进行SHA1编码； sign = hashlib.sha1(",".join(args).encode("utf-8")).hexdigest() # * 将编码的结果和时间戳再次拼接； # * 将拼接后的结果进行Base64编码。 return base64.b64encode(",".join([sign, timestamp]).encode("utf-8")).decode("utf-8") if __name__ == '__main__': # * 将/api/movie放到一个列表里； args = ['/api/movie'] token = get_token(args) for i in range(10): index_url = INDEX_URL.format(LIMIT, OFFSET + (i*10), token) # print(index_url) response = requests.get(index_url) print(response.json()) 我们根据上面的逻辑把加密流程实现了出来，并且爬取到了十页的内容。 14.5 寻找详情页加密id入口 观察上一步的输出结果，把结果格式化，这里看看部分结果： 这里我们看到有个id是1，另外还有一个一些其他的字段，如电影名称、封面、类别等，这里面一定有某个信息是用来唯一区分某个电影的。 但是当我们点击第一部电影的信息时，可以看到它跳转了URL为https://spa6.scrape.center/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx的页面，可以看到这里的URL里面有一个加密id为ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx他和电影这些信息有什么关系呢？ 如果你仔细观察，其实可以比较容易地找出规律来，但是这总归是观察出来的，如果遇到一些观察不出规律的，那就很麻烦了，因此，还需要靠技巧去找到它真正的加密位置。这时候该怎么办呢？ 分析一下，这个加密id到底是怎么生成的。 点击详情页的时候，我们就可以看到它访问的URL里面就带上了 ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx这个加密id了。而且不同详情页的加密id是不同的，这就说明这个加密id的构成依赖于列表页Ajax的返回结果。因此，可以确定这个加密id的生成发生在Ajax请求完成后或者点击详情页的一瞬间。 为了进一步确定这发生在何时，我们查看页面源码，可以看到在没有点击之前，详情页链接的href里面就已经带有加密id了，如图： 由此可以确定，这个加密id是在Ajax请求完成之后生成的，而且肯定也是由JavaScript生成的。 怎么去找Ajax完成之后的事件呢？是否应该去找Ajax完成之后的事件呢？ 可以试试。在Sources面板的右侧，有一个Event Listener Breakpoints，这里有一个XHR的监听，包括发起时、成功后、发生错误时的一些监听，这里我们勾选上readystatechange事件，代表Ajax得到响应时的事件，其他断点都可以删除，然后刷新页面看一下，如图： 可以看到，此时就停在Ajax得到响应时的位置了。我们怎么知道这个id时如何加密的呢？可以选择通过断点一步一步调试下去，但这个过程非常繁琐，因为这里可能会逐渐用到页面UI渲染的一些底层实现，甚至可能找着找着都不知道找到哪儿去了。 怎么办呢？这里我们又可以用上前文介绍的用于快速定位的方法，那就是Hook，这里就不再展开讲解原理了。 那么，这里怎么用Hook的方式来找到加密id的加密入口呢？ 想一下，这个加密id是一个Base64编码的字符串，那么生成过程中想必就调用了JavaScript的Base64编码的方法，这个方法名叫作btoa。当然，Base64也有其他的实现方式，比如利用crypto-js库实现，但可能底层调用的就不是btoa方法了。 现在，我们其实并不确定是不是通过调用btoa方法实现的Base64编码，那就先试试吧。 要实现Hook，关键在于将原来的方法改写，这里我们其实就是Hook btoa这个方法了，btoa这个方法属于window对象，这里直接改写成window对象的btoa方法即可。改写的逻辑如下： (function () { 'use strict' function hook(object, attr) { var func = object[attr] object[attr] = function () { console.log('hooked', object, attr, arguments) var ret = func.apply(object, arguments) debugger console.log('result', ret) return ret } } hook(window, 'btoa') }) () 这里我们定义了一个hook方法，给其传入object和attr参数，意思就是Hook object对象的attr参数。例如，如果我们想Hook alert方法，那就把object设置为window，把attr设置为字符串alert。这里我们想要Hook Base64的编码方法，所以只需要Hook window对象的btoa方法就好了。 hook方法的第一句话var func = object[attr]，相当于把他赋值为一个变量，我们调用func方法就可以实现和原来一样的功能。然后我们改写这个方法的定义，将其改写成一个新的方法，在新的方法中，通过func.apply方法又重新调用了原来的方法。这样我们可以保证前后方法的执行效果不受影响的前提下，在func方法执行的前后加入自己的代码，如使用console.log将信息输出到控制台，通过debugger进入断点等。在这个过程中，我们先临时保存func方法，然后定义一个新方法来接管程序控制权，在其中定义我们想要的实现，同时在新方法里重新调回func方法，保证前后的结果不受影响。因此，我们达到了在不影响原有方法效果的前提下，可以实现在方法的前后实现自定义的功能，就是Hook的完整实现过程。 最后我们调用Hook方法，传入window对象和btoa字符串即可。 怎么去注入这个代码呢？有三种注入方法。 控制台注入 重写JavaScript Tampermonkey注入 我们以重写JavaScript为例： 借助Chrome浏览器的Overrides功能，我们可以实现某些JavaScript文件的重写和保存。Overrides会在本地生成一个JavaScript的文件副本，以后每次刷新，都会使用副本的内容。 这里我们需要切换到Sources面板中的Overrides选项卡，然后选择一个文件夹，比如这里我自定义了一个ChromeOverrides文件夹，如图： 然后选一个JavaScript脚本，在后面贴上这段注入脚本，保存之后进行刷新，如图： 可以发现，现在浏览器加载的JavaScript文件就是我们修改过后的了，文件名左侧会有一个原点标识符。 同时我们还注意到，目前直接进入断点模式，并且成功Hook到btoa方法。 其实Overrides的功能非常有用，有了它，我们可以持久化保存任意修改的JavaScript代码，想在哪里改都可以了，甚至可以直接修改JavaScript的原始执行逻辑。 14.6 寻找详情页Ajax的token 现在我们已经找到详情页的加密id，但是还差一步，其Ajax请求也有一个token，如图： 因为这个是Ajax请求，其实这个token和列表页token的构造逻辑是一样的。 14.7 使用python实现详情页爬取 现在，我们已经成功把详情页的加密逻辑id和Ajax请求的token找出来了，下一步就是使用python完成爬取。这里我们只实现第一页的爬取，示例代码如下： import hashlib import time import base64 from typing import List, Any import requests INDEX_URL = 'https://spa6.scrape.center/api/movie?limit={limit}&offset={offset}&token={token}' DETAIL_URL = 'https://spa6.scrape.center/api/movie/{id}?token={token}' LIMIT = 10 OFFSET = 0 def get_token(args: List[Any]): timestamp = str(int(time.time())) args.append(timestamp) sign = hashlib.sha1(','.join(args).encode('utf-8')).hexdigest() return base64.b64encode(','.join([sign, timestamp]).encode('utf-8')).decode('utf-8') args = ['/api/movie'] token = get_token(args=args) index_url = INDEX_URL.format(limit=LIMIT, offset=OFFSET, token=token) response = requests.get(index_url) # print('response', response.json()) result = response.json() for item in result['results']: id = item['id'] encrypt_id = base64.b64encode(str(id).encode('utf-8')).decode('utf-8') args = [f'/api/movie/{encrypt_id}'] token = get_token(args=args) detail_url = DETAIL_URL.format(id=encrypt_id, token=token) response = requests.get(detail_url) print('response', response.json()) 这里模拟了详情页的加密id和token的构造过程，然后请求了详情页，获取到了响应。
什么是 Electron? Electron 是一个用于构建跨平台桌面应用的框架。它结合了 Chromium 和 Node.js，使开发者可以使用 Web 技术（HTML、CSS、JavaScript）来构建桌面应用。 如上图所示，Electron 由 Chromium、Node.js 和 Native API 组成。 Electron 的架构 Electron 的架构分为主进程和渲染进程。主进程负责管理应用的生命周期和窗口创建，而渲染进程负责显示网页内容。 主进程 Main Native API: 处理系统级别的操作。 Node.js: 提供服务器端功能。 渲染进程 Web 页面: 使用 HTML、CSS、JavaScript 构建。 Node.js: 提供服务器端功能。 Webkit: 渲染网页内容。 进程间通信 (IPC) 主进程和渲染进程之间通过 IPC (Inter-Process Communication) 进行通信。 渲染进程: 使用 ipcRenderer 进行通信。 主进程: 使用 ipcMain 进行通信。 使用 Electron-Vite 构建项目 Electron-Vite 是一个集成了 Vite 的工具，简化了 Electron 应用的构建过程。 步骤 安装 Electron-Vite 使用 pnpm 安装 Electron-Vite： pnpm create @quick-start/electron 创建项目 然后按照提示操作即可! ✔ Project name: … ✔ Select a framework: › vue ✔ Add TypeScript? … No / Yes ✔ Add Electron updater plugin? … No / Yes ✔ Enable Electron download mirror proxy? … No / Yes Scaffolding project in ./... Done. 3. **开发模式** 启动开发服务器： ```bash npm run dev 构建应用 构建生产版本： npm run build 进程间通信 1. 主进程与渲染进程的角色 主进程 (Main Process) 只有一个，是应用程序的入口点 可以访问原生 GUI 管理所有渲染进程 可以访问 Node.js API 控制应用程序的生命周期 渲染进程 (Renderer Process) 可以有多个，每个对应一个窗口 运行网页内容 无法直接访问原生 GUI 默认沙箱化环境 通过 IPC 与主进程通信 2. IPC 通信详解 从渲染进程到主进程 (Renderer to Main) 单向通信 (send) // 渲染进程发送 ipcRenderer.send('message-to-main', data) // 主进程接收 ipcMain.on('message-to-main', (event, data) => { console.log(data) }) 请求响应模式 (invoke/handle) // 渲染进程 const response = await ipcRenderer.invoke('async-message', data) // 主进程 ipcMain.handle('async-message', async (event, data) => { // 处理异步操作 return result }) 从主进程到渲染进程 (Main to Renderer) // 主进程发送 mainWindow.webContents.send('message-to-renderer', data) // 渲染进程接收 ipcRenderer.on('message-to-renderer', (event, data) => { console.log(data) }) 3. 页面间通信 方式一：通过主进程中转 // 页面A发送消息 ipcRenderer.send('message-to-page-b', data) // 主进程中转 ipcMain.on('message-to-page-b', (event, data) => { // 转发给页面B windowB.webContents.send('receive-from-page-a', data) }) // 页面B接收消息 ipcRenderer.on('receive-from-page-a', (event, data) => { console.log(data) }) 方式二：使用 contextBridge // 预加载脚本中设置 contextBridge.exposeInMainWorld('electronAPI', { sendMessage: (channel, data) => ipcRenderer.send(channel, data), onMessage: (channel, func) => ipcRenderer.on(channel, func) }) 4. 通信最佳实践 类型安全 interface IpcChannels { 'user-login': { username: string; password: string } 'login-result': { success: boolean; token?: string } } 错误处理 try { const result = await ipcRenderer.invoke('async-operation') } catch (error) { console.error('IPC 调用失败:', error) } 安全考虑 // 限制暴露的 API contextBridge.exposeInMainWorld('secureAPI', { // 只暴露安全的方法 sendMessage: (message) => { // 验证消息格式 if (isValidMessage(message)) { ipcRenderer.send('secure-channel', message) } } }) 性能优化 // 使用节流防止过多IPC调用 const throttledSend = _.throttle((data) => { ipcRenderer.send('frequent-update', data) }, 100) 5. 调试技巧 监听所有IPC通信 // 开发环境下监控所有IPC通信 if (process.env.NODE_ENV === 'development') { ipcMain.on('*', (event, channel, ...args) => { console.log(`IPC Channel: ${channel}`, args) }) } Demo案例：文件拖放查看器 这个案例展示了一个简单的文件拖放应用，包含了主进程和渲染进程的通信、文件系统操作等基本功能。 项目结构 file-viewer/ ├── src/ │ ├── main/ │ │ └── index.ts │ ├── renderer/ │ │ ├── index.html │ │ ├── App.vue │ │ └── style.css │ └── preload/ │ └── index.ts ├── electron.vite.config.ts └── package.json 1. 主进程代码 import { app, BrowserWindow, ipcMain } from 'electron' import { join } from 'path' import * as fs from 'fs/promises' function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: join(__dirname, '../preload/index.js'), nodeIntegration: false, contextIsolation: true } }) if (process.env.VITE_DEV_SERVER_URL) { win.loadURL(process.env.VITE_DEV_SERVER_URL) } else { win.loadFile(join(__dirname, '../renderer/index.html')) } // 处理文件读取请求 ipcMain.handle('read-file', async (_, filePath) => { try { const content = await fs.readFile(filePath, 'utf-8') return { success: true, content } } catch (error) { return { success: false, error: error.message } } }) } app.whenReady().then(createWindow) 2. 预加载脚本 import { contextBridge, ipcRenderer } from 'electron' contextBridge.exposeInMainWorld('electronAPI', { readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath), handleDrop: (callback: (files: string[]) => void) => { document.addEventListener('drop', (e) => { e.preventDefault() const files = Array.from(e.dataTransfer.files).map(f => f.path) callback(files) }) document.addEventListener('dragover', (e) => { e.preventDefault() }) } }) 3. 渲染进程代码 拖放文件到这里 {{ currentFile.name }} {{ currentFile.content }} 4. 类型声明 export interface ElectronAPI { readFile: (filePath: string) => Promise<{ success: boolean; content?: string; error?: string; }>; handleDrop: (callback: (files: string[]) => void) => void; } declare global { interface Window { electronAPI: ElectronAPI; } } 5. 运行应用 安装依赖： npm install 开发模式运行： npm run dev 构建应用： npm run build
# Docker常用命令 ## 镜像相关命令 ### 搜索镜像 ```bash docker search [镜像名称] # 示例：docker search ubuntu 用于在Docker Hub等镜像仓库查找包含指定关键词的镜像，展示镜像名称、描述、星级等相关信息，方便查找合适的镜像资源。 拉取镜像 docker pull [镜像名称:标签] # 示例：docker pull ubuntu:latest docker pull mysql:5.7 从镜像仓库下载指定的镜像到本地，若不指定标签，默认拉取 latest（最新版）镜像，以获取对应软件或服务的基础镜像。 查看本地镜像 docker images 此命令会列出本地已下载的所有镜像，展示的信息包含 REPOSITORY（仓库名）、TAG（标签）、IMAGE ID（镜像ID）、CREATED（创建时间）、SIZE（大小）等，方便了解本地镜像的存储情况。 删除镜像 docker rmi [镜像名称或镜像ID] # 示例：docker rmi ubuntu:latest docker rmi [具体镜像ID] 用于删除本地不再需要的镜像，若镜像正在被容器使用，需先停止并删除关联的容器后，方可成功删除该镜像。 构建镜像（基于Dockerfile） docker build -t [镜像名称:标签] [Dockerfile所在目录路径] # 示例：docker build -t myapp:v1 依据包含构建指令的Dockerfile所在目录来构建自定义镜像，-t 参数用于指定镜像名称和标签，便于创建符合自身业务需求的镜像。 批量删除镜像（根据条件） docker rmi $(docker images -q [筛选条件]) # 示例：docker rmi $(docker images -q -f "before=20240101") 可以通过结合 docker images -q（用于获取符合条件的镜像ID列表）与删除命令 docker rmi 来批量删除镜像。上述示例是删除在2024年1月1日之前创建的镜像，可根据具体的筛选条件（如名称、大小范围、创建时间等）灵活运用此方法进行批量清理。 容器相关命令 运行容器 docker run [参数] [镜像名称:标签] # 示例： docker run -it ubuntu:latest /bin/bash docker run -d -p 3306:3306 --name mymysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 常用参数解释： -d：使容器后台运行（守护式运行），常用于运行长期提供服务的容器，如数据库容器等。 -it：分配交互式终端进入容器内部，方便在容器内执行命令操作，常用于调试等场景。 -p：进行端口映射（宿主机端口:容器内部端口），使得容器内运行的服务能被外部访问。 --name：指定容器的名称，便于后续对容器进行识别和操作。 -e：设置环境变量，用于向容器内传递配置相关信息，比如数据库的初始密码等。 查看运行中的容器 docker ps 列出当前正在运行的容器信息，包含 CONTAINER ID（容器ID）、IMAGE（所使用的镜像）、COMMAND（启动容器时执行的命令）、CREATED（创建时间）、STATUS（状态）、PORTS（端口映射情况）和 NAMES（容器名称）等，帮助掌握容器运行状态。 查看所有容器（包括已停止的） docker ps -a 展示所有容器的详细信息，除了运行中的容器信息外，还能看到已停止容器的相关情况，用于全面了解容器的存在和过往运行记录。 停止容器 docker stop [容器名称或容器ID] # 示例：docker stop mymysql docker stop [具体容器ID] 正常停止正在运行的容器，让容器内的进程优雅地结束，避免数据丢失或异常情况。 启动容器 docker start [容器名称或容器ID] # 示例：docker start mymysql docker start [具体容器ID] 用于启动处于停止状态的容器，使其再次投入运行，恢复到之前配置的运行状态。 重启容器 docker restart [容器名称或容器ID] # 示例：docker restart mymysql docker restart [具体容器ID] 重启正在运行或已停止的容器，常用于更新配置后或者容器出现异常需要重新初始化的场景。 删除容器 docker rm [容器名称或容器ID] # 示例：docker rm mymysql docker rm [具体容器ID] 删除指定的容器，若容器正在运行，需先停止容器才能删除，清理不再使用的容器资源。 批量删除容器（根据条件） docker rm $(docker ps -aq [筛选条件]) # 示例：docker rm $(docker ps -aq -f "status=exited") 结合 docker ps -aq（获取符合条件的容器ID列表）与 docker rm 来批量删除容器。示例中是删除状态为已退出的容器，也可依据创建时间、名称等其他条件灵活运用此方法清理容器。 进入容器 docker exec -it [容器名称或容器ID] [命令] # 示例：docker exec -it mymysql bash 进入正在运行的容器内部执行指定命令，常用 bash 等命令进入交互式shell环境，便于在容器内操作文件、查看日志、调试程序等。 查看容器日志 docker logs [容器名称或容器ID] # 示例：docker logs mymysql 查看容器运行产生的日志信息，通过分析日志来排查容器运行过程中出现的问题，比如服务启动失败原因、运行时的报错等。 网络相关命令 查看Docker网络列表 docker network ls 列出Docker系统中创建的所有网络，展示网络的 NETWORK ID（网络ID）、NAME（网络名称）、DRIVER（网络驱动类型）等信息，便于了解网络配置情况。 创建自定义网络 docker network create [网络名称] # 示例：docker network create mynet 创建一个自定义的Docker网络，方便容器间按需求进行网络通信配置等，比如创建隔离的网络环境供特定的一组容器通信使用。 连接容器到网络 docker network connect [网络名称] [容器名称或容器ID] # 示例：docker network connect mynet mymysql 将指定容器连接到已有的网络中，使其能与该网络内的其他容器通信，实现容器间的互联互通。 断开容器与网络的连接 docker network disconnect [网络名称] [容器名称或容器ID] # 示例：docker network disconnect mynet mymysql 将容器从指定网络中断开连接，改变容器的网络通信范围，比如从某个测试网络中移除容器等。 数据卷相关命令 创建数据卷 docker volume create [数据卷名称] # 示例：docker volume create mydata 创建一个Docker数据卷，用于持久化存储容器数据或者在容器间共享数据，确保数据的独立性和可复用性。 查看数据卷列表 docker volume ls 列出Docker系统中已创建的所有数据卷，显示数据卷的 DRIVER（驱动类型）、VOLUME NAME（数据卷名称）等信息，便于管理和知晓已有数据卷资源。 挂载数据卷到容器 docker run -v [数据卷名称或宿主机路径:容器内部路径] [镜像名称:标签] # 示例： docker run -v mydata:/data myapp:v1 docker run -v /home/user/data:/data myapp:v1 在运行容器时，将指定的数据卷或者宿主机上的目录挂载到容器内部相应路径，实现数据的持久化或共享，比如保存数据库的数据文件等。 删除数据卷 docker volume rm [数据卷名称] # 示例：docker volume rm mydata 删除指定的数据卷，若数据卷正在被容器使用，则无法直接删除，要先解除关联后才能操作。