212 lines
7.2 KiB
Go
212 lines
7.2 KiB
Go
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||
|
||
package nodes
|
||
|
||
import (
|
||
"net/http"
|
||
)
|
||
|
||
// serveWAFLoader 提供 WAF Loader JavaScript 文件
|
||
func (this *HTTPRequest) serveWAFLoader() {
|
||
loaderJS := `(function() {
|
||
'use strict';
|
||
|
||
// 全局队列与执行器
|
||
window.__WAF_Q__ = window.__WAF_Q__ || [];
|
||
if (!window.__WAF_LOADER__) {
|
||
window.__WAF_LOADER__ = {
|
||
executing: false,
|
||
execute: function() {
|
||
if (this.executing) {
|
||
return;
|
||
}
|
||
this.executing = true;
|
||
var self = this;
|
||
var queue = window.__WAF_Q__ || [];
|
||
var runNext = function() {
|
||
if (!queue.length) {
|
||
self.executing = false;
|
||
return;
|
||
}
|
||
var item = queue.shift();
|
||
executeDecryptedCode(item.p, item.m, runNext);
|
||
};
|
||
runNext();
|
||
}
|
||
};
|
||
}
|
||
|
||
// 1. XOR 解码为字符串(不压缩,避免顺序问题)
|
||
function xorDecodeToString(b64, key) {
|
||
try {
|
||
var bin = atob(b64);
|
||
var out = new Uint8Array(bin.length);
|
||
for (var i = 0; i < bin.length; i++) {
|
||
out[i] = bin.charCodeAt(i) ^ key.charCodeAt(i % key.length);
|
||
}
|
||
if (typeof TextDecoder !== 'undefined') {
|
||
return new TextDecoder().decode(out);
|
||
}
|
||
var s = '';
|
||
for (var j = 0; j < out.length; j++) {
|
||
s += String.fromCharCode(out[j]);
|
||
}
|
||
return s;
|
||
} catch (e) {
|
||
console.error('WAF Loader: xor decode failed', e);
|
||
return '';
|
||
}
|
||
}
|
||
|
||
// 2. XOR 解密
|
||
function decryptXOR(payload, key) {
|
||
try {
|
||
var binary = atob(payload);
|
||
var output = [];
|
||
var keyLen = key.length;
|
||
if (keyLen === 0) {
|
||
return '';
|
||
}
|
||
for (var i = 0; i < binary.length; i++) {
|
||
var charCode = binary.charCodeAt(i) ^ key.charCodeAt(i % keyLen);
|
||
output.push(String.fromCharCode(charCode));
|
||
}
|
||
return output.join('');
|
||
} catch (e) {
|
||
console.error('WAF Loader: Decrypt failed', e);
|
||
return '';
|
||
}
|
||
}
|
||
|
||
// 3. 执行解密后的代码
|
||
function executeDecryptedCode(cipher, meta, done) {
|
||
var finish = function() {
|
||
if (typeof done === 'function') {
|
||
done();
|
||
}
|
||
};
|
||
try {
|
||
if (!cipher || !meta || !meta.key) {
|
||
console.error('WAF Loader: Missing cipher or meta.key');
|
||
finish();
|
||
return;
|
||
}
|
||
if (meta.alg !== 'xor') {
|
||
console.error('WAF Loader: Unsupported alg', meta.alg);
|
||
finish();
|
||
return;
|
||
}
|
||
|
||
// 1. XOR 解码为字符串
|
||
var plainJS = xorDecodeToString(cipher, meta.key);
|
||
if (!plainJS) {
|
||
console.error('WAF Loader: XOR decode failed');
|
||
finish();
|
||
return;
|
||
}
|
||
|
||
// 2. 执行解密后的代码(同步)
|
||
try {
|
||
// 使用全局 eval,尽量保持和 <script> 一致的作用域
|
||
window.eval(plainJS);
|
||
|
||
// 3. 计算 Token 并握手
|
||
calculateAndHandshake(plainJS);
|
||
} catch (e) {
|
||
console.error('WAF Loader: Execute failed', e);
|
||
}
|
||
finish();
|
||
} catch (e) {
|
||
console.error('WAF Loader: Security check failed', e);
|
||
finish();
|
||
}
|
||
}
|
||
|
||
// 7. Token 计算和握手
|
||
function base64EncodeUtf8(str) {
|
||
try {
|
||
return btoa(unescape(encodeURIComponent(str)));
|
||
} catch (e) {
|
||
console.error('WAF Loader: base64 encode failed', e);
|
||
return '';
|
||
}
|
||
}
|
||
|
||
function calculateAndHandshake(decryptedJS) {
|
||
try {
|
||
// 计算 Token(简化示例)
|
||
var tokenData = decryptedJS.substring(0, Math.min(100, decryptedJS.length)) + Date.now();
|
||
var token = base64EncodeUtf8(tokenData).substring(0, 64); // 限制长度
|
||
|
||
// 发送握手请求
|
||
fetch('/waf/handshake', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ token: token })
|
||
}).then(function(resp) {
|
||
if (resp.ok) {
|
||
// 握手成功,设置全局 Token
|
||
window.__WAF_TOKEN__ = token;
|
||
|
||
// 为后续请求设置 Header
|
||
if (window.XMLHttpRequest) {
|
||
var originalOpen = XMLHttpRequest.prototype.open;
|
||
XMLHttpRequest.prototype.open = function() {
|
||
originalOpen.apply(this, arguments);
|
||
if (window.__WAF_TOKEN__) {
|
||
this.setRequestHeader('X-WAF-TOKEN', window.__WAF_TOKEN__);
|
||
}
|
||
};
|
||
}
|
||
|
||
// 为 fetch 设置 Header
|
||
var originalFetch = window.fetch;
|
||
window.fetch = function() {
|
||
var args = Array.prototype.slice.call(arguments);
|
||
if (args.length > 1 && typeof args[1] === 'object') {
|
||
if (!args[1].headers) {
|
||
args[1].headers = {};
|
||
}
|
||
if (window.__WAF_TOKEN__) {
|
||
args[1].headers['X-WAF-TOKEN'] = window.__WAF_TOKEN__;
|
||
}
|
||
} else {
|
||
args[1] = {
|
||
headers: {
|
||
'X-WAF-TOKEN': window.__WAF_TOKEN__ || ''
|
||
}
|
||
};
|
||
}
|
||
return originalFetch.apply(this, args);
|
||
};
|
||
}
|
||
}).catch(function(err) {
|
||
console.error('WAF Loader: Handshake failed', err);
|
||
});
|
||
} catch (e) {
|
||
console.error('WAF Loader: Calculate token failed', e);
|
||
}
|
||
}
|
||
|
||
// 8. 主逻辑
|
||
if (window.__WAF_Q__ && window.__WAF_Q__.length) {
|
||
window.__WAF_LOADER__.execute();
|
||
} else {
|
||
// 如果没有加密内容,等待一下再检查(可能是异步加载)
|
||
setTimeout(function() {
|
||
if (window.__WAF_Q__ && window.__WAF_Q__.length) {
|
||
window.__WAF_LOADER__.execute();
|
||
}
|
||
}, 100);
|
||
}
|
||
})();`
|
||
|
||
this.writer.Header().Set("Content-Type", "application/javascript; charset=utf-8")
|
||
this.writer.Header().Set("Cache-Control", "public, max-age=3600")
|
||
this.writer.WriteHeader(http.StatusOK)
|
||
_, _ = this.writer.WriteString(loaderJS)
|
||
this.writer.SetOk()
|
||
}
|