1.4.5.2
This commit is contained in:
211
EdgeNode/internal/nodes/http_request_loader.go
Normal file
211
EdgeNode/internal/nodes/http_request_loader.go
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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()
|
||||
}
|
||||
Reference in New Issue
Block a user