[MF]实验室对应添加fetch劫持

This commit is contained in:
51hhh 2025-08-26 11:09:32 +08:00
parent a746d54993
commit 54d3e5798f
9 changed files with 372 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
{"words": ["_background_noise_", "_unknown_", "down", "eight", "five", "four", "go", "left", "nine", "no", "one", "right", "seven", "six", "stop", "three", "two", "up", "yes", "zero"], "frameSize": 232}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1 @@
{"dataset":{"0":{"start":0,"length":38400},"1":{"start":38400,"length":38400}},"classList":[{"id":"class-1","name":"青橘子","images":[]},{"id":"class-2","name":"黄橘子","images":[]}],"k":3,"featureDim":1280,"date":"2025-08-25T09:39:56.187Z","dataFile":"knn-model-juzi.bin"}

View File

@ -64,6 +64,134 @@
</div>
</main>
<!-- !!!!!! 核心劫持代码:确保在任何 TF.js 库之前加载 !!!!!! -->
<script>
(function() {
// 定义你的镜像服务器的公共前缀,用于存放 MoveNet 模型文件
// 例如:'https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/movenet-mirror/'
// 重要:确保你的镜像服务器的目录结构与原始模型文件的路径部分匹配。
// 举例:
// 如果原始是 https://tfhub.dev/deepmind/movenet/singlepose-lightning/4/model.json
// 那么在你的CDN上你需要部署为
// https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/movenet-mirror/tfhub.dev/deepmind/movenet/singlepose-lightning/4/model.json
//
// 如果你的CDN就是直接放了 model.json, group1-shard*of*.bin那么 MIRROR_BASE_URL 将不含后续路径。
//
// **** 根据你前一个回复的镜像路径,我们假设你的镜像结构是: ****
// https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/snake_game/model.json
// https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/snake_game/group1-shard1of2.bin
// https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/snake_game/group1-shard2of2.bin
//
// 那么我们需要将匹配的原始URL路径重写为 MIRROR_SPECIFIC_FILENAME_PREFIX
// 也就是将类似 "https://tfhub.dev/.../model.json..."
// 替换为 "https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/snake_game/model.json"
const MIRROR_SPECIFIC_FILENAME_PREFIX = 'https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/snake_game/';
// 定义需要被劫持的原始 URL 的域名模式
const INTERCEPT_DOMAINS = [
'https://tfhub.dev/',
// 如果实际的最终模型文件仍然解析到 storage.googleapis.com也需要包含
// 例如:'https://storage.googleapis.com/tfjs-models/'
// 或者你观察到的实际的最终 Google Storage 域名
];
// 备份原始的 fetch 函数
const originalFetch = window.fetch;
window.fetch = function(input, init) {
let url = input;
if (input instanceof Request) {
url = input.url;
}
let newUrl = url;
let isIntercepted = false;
// 检查 URL 是否以我们关注的域名开头
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
// 尝试从 URL 中提取文件名 (不包含查询参数)
// 匹配 model.json 或 group1-shardXofY.bin
const fileNameMatch = url.match(/(model\.json|group1-shard\dof\d\.bin)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0]; // 获取匹配到的文件名
newUrl = MIRROR_SPECIFIC_FILENAME_PREFIX + fileName; // 拼接新的镜像 URL
isIntercepted = true;
break; // 找到匹配的域名和文件,停止循环
}
}
}
if (isIntercepted) {
console.warn(`[TFJS Fetch Intercepted] Original: ${url}`);
console.warn(`[TFJS Fetch Intercepted] Redirecting to: ${newUrl}`);
if (input instanceof Request) {
input = new Request(newUrl, {
method: input.method,
headers: input.headers,
body: input.body,
referrer: input.referrer,
referrerPolicy: input.referrerPolicy,
mode: 'cors',
credentials: input.credentials,
cache: 'default',
redirect: 'follow',
integrity: undefined, // 移除 integrity 属性以避免校验失败
signal: input.signal,
});
} else {
input = newUrl;
}
}
return originalFetch(input, init).catch(error => {
console.error(`[TFJS Fetch Intercepted Error] Failed to load ${url} (redirected to ${newUrl || url || input}):`, error);
throw error;
});
};
// -------------------- 劫持 XMLHttpRequest API (备用安全网) --------------------
// 尽管 TF.js 主要用 fetch但安全起见保留 XHR 劫持
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
xhr.open = function(method, url, async = true, user = null, password = null) {
let newUrl = url;
let isIntercepted = false;
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
const fileNameMatch = url.match(/(model\.json|group1-shard\dof\d\.bin)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0];
newUrl = MIRROR_SPECIFIC_FILENAME_PREFIX + fileName;
isIntercepted = true;
break;
}
}
}
if (isIntercepted) {
console.warn(`[TFJS XHR Intercepted] Original: ${url}`);
console.warn(`[TFJS XHR Intercepted] Redirecting to: ${newUrl}`);
url = newUrl; // 修改传入 open 的 URL
}
return originalOpen.apply(this, arguments);
};
for (const key in originalXHR) {
if (originalXHR.hasOwnProperty(key)) {
window.XMLHttpRequest[key] = originalXHR[key];
}
}
return xhr;
};
})();
</script>
<!-- 引入所有依赖库 -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.20.0/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/pose-detection@2.1.3/dist/pose-detection.min.js"></script>

View File

@ -4,6 +4,130 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KNN 图像分类器 - TensorFlow.js</title>
<!-- !!!!!! 核心劫持代码:确保在任何 TF.js 库之前加载 !!!!!! -->
<script>
(function() {
// 定义你的镜像服务器的公共前缀,用于存放 MobileNet 模型文件
// 根据你提供的最新路径进行更新。
// 确保这些文件就命名为 model.json, group1-shardXof4.bin并直接在此目录下
const MOBILENET_MIRROR_BASE_URL = 'https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/mobilenet/';
// 定义需要被劫持的原始 URL 的域名模式
// 我们观察到最终请求来自 storage.googleapis.com
const INTERCEPT_DOMAINS = [
'https://storage.googleapis.com/tfjs-models/', // tfjs 官方模型常用的 CDN
'https://storage.googleapis.com/', // 更宽泛的匹配 Google Storage
'https://tfhub.dev/', // 如果 MobileNet 也会通过 tfhub.dev 间接加载
];
// 备份原始的 fetch 函数
const originalFetch = window.fetch;
window.fetch = function(input, init) {
let url = input;
if (input instanceof Request) {
url = input.url;
}
let newUrl = url;
let isIntercepted = false;
// 检查 URL 是否以我们关注的域名开头
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
// 尝试从 URL 中提取文件名 (不包含查询参数)
// 匹配 model.json 或 group1-shardXofY.bin
const fileNameMatch = url.match(/(model\.json|group1-shard\dof\d\.bin)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0]; // 获取匹配到的文件名
newUrl = MOBILENET_MIRROR_BASE_URL + fileName; // 拼接新的镜像 URL
isIntercepted = true;
break; // 找到匹配的域名和文件,停止循环
}
}
}
if (isIntercepted) {
console.warn(`[TFJS Fetch Intercepted] Original: ${url}`);
console.warn(`[TFJS Fetch Intercepted] Redirecting to: ${newUrl}`);
// 如果 input 是 Request 对象,需要创建新的 Request 对象来修改 URL
if (input instanceof Request) {
try {
input = new Request(newUrl, {
method: input.method,
headers: input.headers,
body: input.body,
referrer: input.referrer,
referrerPolicy: input.referrerPolicy,
mode: 'cors', // 总是使用 CORS 模式
credentials: input.credentials,
cache: 'default',
redirect: 'follow',
integrity: undefined, // 移除 integrity 属性以避免校验失败
signal: input.signal,
});
} catch (e) {
console.error(`[TFJS Fetch Intercepted Error] Failed to create new Request object: ${e.message}. Falling back to URL string.`, input);
// 如果创建 Request 对象失败,回退到直接使用 URL 字符串
input = newUrl;
}
} else {
// 如果 input 是 URL 字符串,直接替换
input = newUrl;
}
}
return originalFetch(input, init).catch(error => {
console.error(`[TFJS Fetch Intercepted Error] Failed to load ${url} (redirected to ${newUrl || url || input}):`, error);
throw error;
});
};
// -------------------- 劫持 XMLHttpRequest API (备用安全网) --------------------
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
xhr.open = function(method, url, async = true, user = null, password = null) {
let newUrl = url;
let isIntercepted = false;
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
const fileNameMatch = url.match(/(model\.json|group1-shard\dof\d\.bin)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0];
newUrl = MOBILENET_MIRROR_BASE_URL + fileName;
isIntercepted = true;
break;
}
}
}
if (isIntercepted) {
console.warn(`[TFJS XHR Intercepted] Original: ${url}`);
console.warn(`[TFJS XHR Intercepted] Redirecting to: ${newUrl}`);
url = newUrl; // 修改传入 open 的 URL
}
// 调用原始的 open 方法
return originalOpen.apply(this, arguments);
};
// 将原始 XMLHttpRequest 的所有静态属性和方法复制到劫持后的 XMLHttpRequest 构造函数
// 这样像 XHR.UNSENT 等常量仍然可用
for (const key in originalXHR) {
if (typeof originalXHR[key] !== 'function' && originalXHR.hasOwnProperty(key)) {
window.XMLHttpRequest[key] = originalXHR[key];
}
}
return xhr;
};
})();
</script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier@latest"></script>

View File

@ -181,7 +181,124 @@
<div id="predictionResult">
等待模型训练完成并开始识别...
</div>
<!-- !!!!!! 核心劫持代码:确保在任何 TF.js 库之前加载 !!!!!! -->
<!-- 如果你有其他模型的劫持代码,请确保它们都先于 TF.js 库 -->
<script>
(function() {
// 定义你的镜像服务器的公共前缀,用于存放 Speech Commands 模型文件
// 根据你提供的最新路径进行更新。
const SPEECH_COMMANDS_MIRROR_BASE_URL = 'https://goood-space-assets.oss-cn-beijing.aliyuncs.com/public/fetch/speech-commands/';
// 定义需要被劫持的原始 URL 的域名模式
// 明确指出是 tfjs-models 下的 speech-commands 模型
const INTERCEPT_DOMAINS = [
'https://storage.googleapis.com/tfjs-models/tfjs/speech-commands/',
// 如果实际请求路径更短,也可以包含更宽泛的匹配
// 'https://storage.googleapis.com/tfjs-models/',
// 'https://storage.googleapis.com/',
];
// 备份原始的 fetch 函数
const originalFetch = window.fetch;
window.fetch = function(input, init) {
let url = input;
if (input instanceof Request) {
url = input.url;
}
let newUrl = url;
let isIntercepted = false;
// 检查 URL 是否以我们关注的域名开头
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
// 尝试从 URL 中提取文件名 (不包含查询参数)
// 匹配 metadata.json, model.json, group1-shardXofY (注意这里没有.bin后缀)
const fileNameMatch = url.match(/(metadata\.json|model\.json|group1-shard\dof\d)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0]; // 获取匹配到的文件名
newUrl = SPEECH_COMMANDS_MIRROR_BASE_URL + fileName; // 拼接新的镜像 URL
isIntercepted = true;
break; // 找到匹配的域名和文件,停止循环
}
}
}
if (isIntercepted) {
console.warn(`[TFJS Fetch Intercepted] Speech Commands - Original: ${url}`);
console.warn(`[TFJS Fetch Intercepted] Speech Commands - Redirecting to: ${newUrl}`);
if (input instanceof Request) {
try {
input = new Request(newUrl, {
method: input.method,
headers: input.headers,
body: input.body,
referrer: input.referrer,
referrerPolicy: input.referrerPolicy,
mode: 'cors',
credentials: input.credentials,
cache: 'default',
redirect: 'follow',
integrity: undefined, // 移除 integrity 属性以避免校验失败
signal: input.signal,
});
} catch (e) {
console.error(`[TFJS Fetch Intercepted Error] Speech Commands - Failed to create new Request object: ${e.message}. Falling back to URL string.`, input);
input = newUrl;
}
} else {
input = newUrl;
}
}
return originalFetch(input, init).catch(error => {
console.error(`[TFJS Fetch Intercepted Error] Speech Commands - Failed to load ${url} (redirected to ${newUrl || url || input}):`, error);
throw error;
});
};
// -------------------- 劫持 XMLHttpRequest API (备用安全网) --------------------
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
const originalOpen = xhr.open;
xhr.open = function(method, url, async = true, user = null, password = null) {
let newUrl = url;
let isIntercepted = false;
for (const domain of INTERCEPT_DOMAINS) {
if (url.startsWith(domain)) {
const fileNameMatch = url.match(/(metadata\.json|model\.json|group1-shard\dof\d)/);
if (fileNameMatch) {
const fileName = fileNameMatch[0];
newUrl = SPEECH_COMMANDS_MIRROR_BASE_URL + fileName;
isIntercepted = true;
break;
}
}
}
if (isIntercepted) {
console.warn(`[TFJS XHR Intercepted] Speech Commands - Original: ${url}`);
console.warn(`[TFJS XHR Intercepted] Speech Commands - Redirecting to: ${newUrl}`);
url = newUrl;
}
return originalOpen.apply(this, arguments);
};
for (const key in originalXHR) {
if (typeof originalXHR[key] !== 'function' && originalXHR.hasOwnProperty(key)) {
window.XMLHttpRequest[key] = originalXHR[key];
}
}
return xhr;
};
})();
</script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/speech-commands@latest/dist/speech-commands.min.js"></script>