Files
CLOAK/resources/virtual_device_detect.inc.php
T
2026-06-16 04:58:56 +08:00

242 lines
7.0 KiB
PHP
Executable File

<?php
/** 虚拟设备检测规则引擎(IS_VIRTUAL 1=简单 2=标准 3=严格) */
?>
(function (window) {
'use strict';
var BOT_UA = /googlebot|bingbot|yandexbot|baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|slackbot|vkshare|w3c_validator|whatsapp|telegrambot|applebot|petalbot|semrushbot|ahrefsbot|mj12bot|dotbot|bytespider|gptbot|claudebot|amazonbot/i;
var AUTO_UA = /HeadlessChrome|PhantomJS|Puppeteer|Selenium|WebDriver|playwright|AutomationControlled/i;
var VM_UA = /virtualbox|vmware|qemu|genymotion|\bemulator\b|sdk_gphone|android sdk built for x86|simulator/i;
var _glCache = null;
function getWebglRenderer() {
if (_glCache !== null) {
return _glCache;
}
try {
var canvas = document.createElement('canvas');
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
_glCache = '';
return _glCache;
}
var dbg = gl.getExtension('WEBGL_debug_renderer_info');
_glCache = dbg ? String(gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) || '') : '';
} catch (e) {
_glCache = '';
}
return _glCache;
}
function rule(scope, id, mmr, testFn) {
return { scope: scope, id: id, mmr: mmr, test: testFn };
}
var SIMPLE_RULES = [
rule('all', 'V01', 12, function (ctx) {
return !!navigator.webdriver;
}),
rule('all', 'V02', 12, function (ctx) {
return AUTO_UA.test(ctx.ua);
}),
rule('all', 'V03', 12, function (ctx) {
return !!(
window.callPhantom ||
window._phantom ||
window.__nightmare ||
document.__selenium_unwrapped ||
document.__webdriver_evaluate ||
window.domAutomation ||
window.domAutomationController
);
}),
rule('all', 'V04', 13, function (ctx) {
return VM_UA.test(ctx.ua);
}),
rule('all', 'V05', 14, function (ctx) {
return BOT_UA.test(ctx.ua);
}),
];
var STANDARD_RULES = [
rule('all', 'V10', 15, function (ctx) {
var r = getWebglRenderer();
return r !== '' && /llvmpipe|swiftshader|software renderer|mesa offscreen/i.test(r);
}),
rule('all', 'V11', 16, function (ctx) {
return window.outerWidth === 0 || window.outerHeight === 0;
}),
rule('all', 'V12', 16, function (ctx) {
var plat = ctx.platform;
if (ctx.isMobile) {
return plat === 'Win32' || plat === 'Win64';
}
return /iPhone|iPod|Android/i.test(plat);
}),
rule('mobileOnly', 'V13', 16, function (ctx) {
var tp = typeof navigator.maxTouchPoints === 'number' ? navigator.maxTouchPoints : 0;
return tp === 0 && !('ontouchstart' in window);
}),
];
var STRICT_RULES = [
rule('mobileOnly', 'V24', 17, function (ctx) {
var r = getWebglRenderer().toLowerCase();
if (!r) {
return false;
}
var mobileUa = /iPhone|iPad|iPod|Android/i.test(ctx.ua);
if (!mobileUa) {
return false;
}
return /angle|nvidia|amd|radeon|intel hd|intel\(r\)|llvmpipe|mesa|swiftshader|google swiftshader|microsoft basic render|vmware|virtualbox|citrix/i.test(r);
}),
];
function shouldRunRule(ruleDef, ctx) {
if (ruleDef.scope === 'mobileOnly' && !ctx.isMobile) {
return false;
}
if (ruleDef.scope === 'desktopOnly' && ctx.isMobile) {
return false;
}
return true;
}
function evaluateRules(rules, ctx) {
for (var i = 0; i < rules.length; i++) {
var rd = rules[i];
if (!shouldRunRule(rd, ctx)) {
continue;
}
try {
if (rd.test(ctx)) {
return { blocked: true, ruleId: rd.id, mmr: rd.mmr };
}
} catch (e) {}
}
return { blocked: false };
}
function collectRules(level) {
var list = SIMPLE_RULES.slice();
if (level >= 2) {
list = list.concat(STANDARD_RULES);
}
if (level >= 3) {
list = list.concat(STRICT_RULES);
}
return list;
}
function hasAccelSample(event) {
var a = event && event.accelerationIncludingGravity;
if (!a) {
return false;
}
if (a.x == null && a.y == null && a.z == null) {
return false;
}
var mag = Math.abs(a.x || 0) + Math.abs(a.y || 0) + Math.abs(a.z || 0);
return mag > 0.5;
}
function hasOrientSample(event) {
if (!event) {
return false;
}
return event.alpha !== null || event.beta !== null || event.gamma !== null;
}
function checkMotionSensors(timeoutMs) {
timeoutMs = timeoutMs || 180;
return new Promise(function (resolve) {
if (typeof window.DeviceMotionEvent === 'undefined' && typeof window.DeviceOrientationEvent === 'undefined') {
resolve({ blocked: true, ruleId: 'V27', mmr: 18, detail: 'no_sensor_api' });
return;
}
var settled = false;
var gotAccel = false;
var gotOrient = false;
function finish(result) {
if (settled) {
return;
}
settled = true;
window.removeEventListener('devicemotion', onMotion);
window.removeEventListener('deviceorientation', onOrient);
resolve(result);
}
function onMotion(e) {
if (hasAccelSample(e)) {
gotAccel = true;
}
}
function onOrient(e) {
if (hasOrientSample(e)) {
gotOrient = true;
}
}
function listenAndWait() {
window.addEventListener('devicemotion', onMotion, { passive: true });
window.addEventListener('deviceorientation', onOrient, { passive: true });
setTimeout(function () {
if (gotAccel || gotOrient) {
finish({ blocked: false });
} else {
finish({ blocked: true, ruleId: 'V27', mmr: 18, detail: 'no_sensor_data' });
}
}, timeoutMs);
}
if (typeof DeviceMotionEvent !== 'undefined' && typeof DeviceMotionEvent.requestPermission === 'function') {
DeviceMotionEvent.requestPermission()
.then(function (state) {
if (state === 'granted') {
listenAndWait();
} else {
finish({ blocked: true, ruleId: 'V28', mmr: 18, detail: 'sensor_permission_denied' });
}
})
.catch(function () {
finish({ blocked: true, ruleId: 'V28', mmr: 18, detail: 'sensor_permission_error' });
});
return;
}
listenAndWait();
});
}
window.CloakVirtualDetect = {
run: function (level, ctx) {
level = parseInt(level, 10) || 0;
if (level <= 0) {
return Promise.resolve({ blocked: false });
}
ctx = ctx || {};
ctx.ua = ctx.ua || navigator.userAgent || '';
ctx.platform = ctx.platform || navigator.platform || '';
var syncResult = evaluateRules(collectRules(level), ctx);
if (syncResult.blocked) {
return Promise.resolve(syncResult);
}
if (level >= 3 && ctx.isMobile) {
return checkMotionSensors(180);
}
return Promise.resolve({ blocked: false });
},
};
})(window);