ksjsb/tools/kws_probe.js
2026-07-30 20:25:56 +08:00

233 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 离线 instrument 沙箱 v2: 全容错 Proxy 桩, 让 kwpsec(kws)+kwf 的 Jimbei VM 跑到底。
// 记录它读了哪些指纹 / 监听了哪些事件 / 采了什么触摸保真 / 有没有自动化检测。
// 决定性: 真手指相对 adb 是否存在不可伪造信号。
// 有 force/pressure/radius/coalesced/精确时序 → 行为墙 → 人工路径
// 只可合成轨迹 + 真机已真指纹 → 真手指无优势 → 号码信誉墙 → 换号
const fs = require("fs"), vm = require("vm"), path = require("path");
const ROOT = path.join(__dirname, "..");
const H5 = path.join(ROOT, "out", "h5_security_js");
const KWS = fs.readFileSync(path.join(H5, "kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js"), "utf-8");
const KWF = fs.readFileSync(path.join(H5, "kwf-0.0.2.2cee19b4b7dec496.js"), "utf-8");
const ctx = vm.createContext({
console, setTimeout, clearTimeout, setImmediate, setInterval, clearInterval, queueMicrotask,
// 注意: 不注入 Buffer/process (Node 全局) —— kwpsec 会探测它们判定 Node 环境 (自动化), 真浏览器没有。
btoa: (s) => Buffer.from(s, "binary").toString("base64"),
atob: (s) => Buffer.from(s, "base64").toString("binary"),
});
vm.runInContext(`
globalThis.__reads = {}; globalThis.__events = [];
globalThis.__canvas = []; globalThis.__audio = []; globalThis.__errs = [];
var __win; // window 自指
// 全容错日志 Proxy: 任何属性都不返回 undefined (缺的返回可调用代理), 永不崩。
// 自动化/Node 环境标记黑名单: 真浏览器/WebView 里全是 undefined。强制返回 undefined (否则代理返回 truthy → 被误判自动化)。
var MARKERS = new Set(["_phantom","callPhantom","__nightmare","phantom","_selenium","callSelenium","_Selenium_IDE_Recorder","selenium","domAutomation","domAutomationController","Buffer","process","fbejkbakrbadskfe","kwscb","webdriver","__webdriver_evaluate","__webdriver_script_function","__webdriver_script_func","__webdriver_script_fn","__webdriver_unwrapped","__selenium_evaluate","__selenium_unwrapped","__fxdriver_evaluate","__fxdriver_unwrapped","__driver_evaluate","__driver_unwrapped","spawn","_WEBDRIVER_ELEM_CACHE","ChromeDriverw","require","module","__core-js_shared__","awesomplete","callPhantom"]);
// 全容错日志 Proxy。callable=true 时代理本身是函数 (target=noop), 这样未知方法 .apply/.call/() 不崩。
// 容器(navigator/document/window)用 callable=false (typeof object, 准确)。
function mkTotal(name, real, strict, callable){
real = real || {};
var target = callable ? function(){} : {};
function sub(n){ return mkTotal(n, {}, false, true); } // 未知子值一律=可调用代理 (永远不崩)
return new Proxy(target, {
get: function(t,k){
if (typeof k === "symbol") return undefined;
if (k === "then" || k === "toJSON") return undefined;
var key = String(k);
if (__win && (key==="window"||key==="self"||key==="globalThis"||key==="top"||key==="parent"||key==="frames")) return __win;
var lk = name+"."+key; __reads[lk]=(__reads[lk]||0)+1;
if (key in real && real[key] !== undefined){
var v = real[key];
if (typeof v === "function") return v.bind(real);
if (typeof v === "object" && v !== null) return v;
return v;
}
// 内置全局 (Array/Object/Function/encodeURIComponent...) 真返回
if (key!=="window"&&key!=="self"&&key!=="globalThis"&&typeof globalThis[key]!=="undefined"){ return globalThis[key]; }
// 自动化/Node 标记: 真浏览器里 undefined, 强制返回 undefined (否则 truthy 代理被误判自动化)
if (MARKERS.has(key)){ return undefined; }
if (strict){ return undefined; } // window 精确模式保留
return sub(lk);
},
apply: callable ? function(t,thisArg,args){ var k=name+"()"; __reads[k]=(__reads[k]||0)+1; return sub(k); } : undefined,
construct: callable ? function(){ return {}; } : undefined,
has: function(){ return true; },
set: function(){ return true; },
getOwnPropertyDescriptor: function(t,k){ if(k in real) return Object.getOwnPropertyDescriptor(real,k); return {configurable:true,enumerable:false,value:undefined}; },
ownKeys: function(){ return Object.keys(real); }
});
}
// ---- 真实 canvas/webgl 实现 (关键信号要真跑) ----
function mkCanvasReal(){
return {
width:300, height:150, style:{},
getContext: function(type){
__canvas.push("getContext:"+(type||"?"));
var ctx2d = {
fillRect:function(){}, clearRect:function(){}, strokeRect:function(){},
fillText:function(t){ __canvas.push("2d.fillText:"+(t||"").slice(0,8)); },
strokeText:function(){ __canvas.push("2d.strokeText"); },
measureText:function(s){ __canvas.push("2d.measureText"); return { width:(s?String(s).length:0)*7, actualBoundingBoxAscent:8, actualBoundingBoxDescent:2 }; },
getImageData:function(x,y,w,h){ __canvas.push("2d.getImageData"); return { width:w, height:h, data: new Uint8ClampedArray((w||1)*(h||1)*4) }; },
createImageData:function(w,h){ return { width:w, height:h, data: new Uint8ClampedArray((w||1)*(h||1)*4) }; },
putImageData:function(){}, beginPath:function(){}, closePath:function(){}, moveTo:function(){}, lineTo:function(){}, arc:function(){}, rect:function(){}, fill:function(){}, stroke:function(){},
save:function(){}, restore:function(){}, translate:function(){}, rotate:function(){}, scale:function(){}, setTransform:function(){}, transform:function(){}, drawImage:function(){}, clip:function(){},
arcTo:function(){}, quadraticCurveTo:function(){}, bezierCurveTo:function(){}, isPointInPath:function(){return false;}, createLinearGradient:function(){return {addColorStop:function(){}};}, createRadialGradient:function(){return {addColorStop:function(){}};},
fillStyle:"#000", strokeStyle:"#000", font:"16px Arial", textBaseline:"alphabetic", textAlign:"start",
globalAlpha:1, lineWidth:1, lineCap:"butt", lineJoin:"miter", shadowBlur:0, shadowColor:"rgba(0,0,0,0)", shadowOffsetX:0, shadowOffsetY:0,
};
var webgl = {
getParameter:function(p){ __canvas.push("webgl.getParameter:"+p); return {"37445":"Qualcomm","37446":"Adreno (TM) 740","7936":"WebKit WebGL","7937":"WebKit WebGL","34921":16384,"33902":16384,"33901":16384,"35724":"WebGL GLSL ES 1.00 (OpenGL ES)"}[p] || "WebKit WebGL"; },
getExtension:function(n){ __canvas.push("webgl.getExtension:"+n); return n==="WEBGL_debug_renderer_info"?{UNMASKED_VENDOR_WEBGL:37445,UNMASKED_RENDERER_WEBGL:37446}:{}; },
getSupportedExtensions:function(){ return ["EXT_blend_minmax","ANGLE_instanced_arrays","WEBGL_debug_renderer_info","OES_texture_float"]; },
getShaderPrecisionFormat:function(){ return {rangeMin:127,rangeMax:127,precision:23}; },
createBuffer:function(){return{};},bindBuffer:function(){},bufferData:function(){},createShader:function(){return{};},shaderSource:function(){},compileShader:function(){},createProgram:function(){return{};},attachShader:function(){},linkProgram:function(){},useProgram:function(){},enableVertexAttribArray:function(){},vertexAttribPointer:function(){},drawArrays:function(){},
drawingBufferWidth:300, drawingBufferHeight:150, UNMASKED_VENDOR_WEBGL:37445, UNMASKED_RENDERER_WEBGL:37446,
};
return mkTotal("canvas.ctx("+(type||"")+")", type==="webgl"||type==="experimental-webgl" ? webgl : ctx2d);
},
toDataURL: function(type){ __canvas.push("toDataURL:"+(type||"png")); return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg=="; },
toBlob: function(cb){ __canvas.push("toBlob"); if(cb) setTimeout(function(){cb({size:100});},0); },
addEventListener: function(t){ __events.push("canvas:"+t); },
getBoundingClientRect: function(){ return {left:0,top:0,right:300,bottom:150,width:300,height:150,x:0,y:0}; },
captureStream: function(){ return { getTracks:function(){return [];} }; },
};
}
// ---- 真实 audio 实现 ----
function mkAudioReal(label){
var dest={connect:function(){return null;},disconnect:function(){}};
function node(){ return { connect:function(){return dest;}, disconnect:function(){}, frequency:{value:440}, type:"sine", value:1, start:function(){}, stop:function(){}, gain:{value:1}, threshold:-24, knee:30, ratio:12, attack:0.003, release:0.25, reduction:0 }; }
return {
sampleRate:44100, currentTime:0, destination:dest, state:"running", listener:{},
createOscillator:function(){ __audio.push(label+":createOscillator"); return node(); },
createAnalyser:function(){ __audio.push(label+":createAnalyser"); return node(); },
createDynamicsCompressor:function(){ __audio.push(label+":createDynamicsCompressor"); return node(); },
createGain:function(){ __audio.push(label+":createGain"); return node(); },
createBiquadFilter:function(){ return node(); }, createScriptProcessor:function(){ return node(); },
createBuffer:function(ch,len,sr){ return { getChannelData:function(){ return new Float32Array(len); }, length:len, numberOfChannels:ch, sampleRate:sr }; },
createBufferSource:function(){ var n=node(); n.buffer=null; return n; },
decodeAudioData:function(b,ok){ if(ok) ok({getChannelData:function(){return new Float32Array(100);}}); },
startRendering:function(){ __audio.push(label+":startRendering"); return Promise.resolve({ getChannelData:function(){ return new Float32Array(100); }, length:100 }); },
close:function(){ return Promise.resolve(); }, resume:function(){ return Promise.resolve(); }
};
}
// ---- navigator / screen / document / window ----
var nav = mkTotal("navigator", {
userAgent:"Mozilla/5.0 (Linux; Android 13; PJZ110) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.0.0 Mobile Safari/537.36",
appVersion:"5.0 (Linux; Android 13; PJZ110)", appName:"Netscape", appCodeName:"Mozilla",
platform:"Linux armv8l", oscpu:"Linux armv8l", product:"Gecko", productSub:"20030107", vendor:"Google Inc.", vendorSub:"",
language:"zh-CN", languages:["zh-CN","en-US"], cookieEnabled:true, doNotTrack:null, onLine:true,
webdriver:false, hardwareConcurrency:8, deviceMemory:8, maxTouchPoints:5,
plugins:{length:0}, mimeTypes:{length:0}, javaEnabled:function(){return false;},
connection:{effectiveType:"4g",rtt:50,downlink:10,saveData:false},
permissions:{query:function(){return Promise.resolve({state:"prompt"});}},
mediaDevices:{enumerateDevices:function(){return Promise.resolve([]);}},
userAgentData:{mobile:true,brands:[{brand:"Chromium",version:"114"}]},
geolocation:{getCurrentPosition:function(){},watchPosition:function(){}},
storage:{estimate:function(){return Promise.resolve({quota:1e9,usage:1e6});}},
sendBeacon:function(){return true;}
});
globalThis.navigator = nav;
globalThis.screen = mkTotal("screen", { width:1080, height:2376, availWidth:1080, availHeight:2256, availLeft:0, availTop:0, colorDepth:24, pixelDepth:24, orientation:{type:"portrait-primary",angle:0} });
var docEl={style:{},clientWidth:1080,clientHeight:2256,getBoundingClientRect:function(){return {left:0,top:0,width:1080,height:2256};},addEventListener:function(t){__events.push("docEl:"+t);}};
var bodyEl={style:{},clientWidth:1080,clientHeight:2256,appendChild:function(){},addEventListener:function(t){__events.push("body:"+t);}};
var headEl={appendChild:function(){},getElementsByTagName:function(){return [];}};
var doc = mkTotal("document", {
nodeType:9, documentElement:docEl, body:bodyEl, head:headEl, cookie:"", referrer:"https://app.m.kuaishou.com/",
URL:"https://app.m.kuaishou.com/verify/captcha.html", domain:"app.m.kuaishou.com", title:"安全验证", readyState:"complete",
visibilityState:"visible", hidden:false, hasFocus:function(){return true;},
createElement:function(tag){ if(String(tag).toLowerCase()==="canvas") return mkTotal("canvas", mkCanvasReal());
return mkTotal("el("+tag+")", { tagName:String(tag).toUpperCase(), style:{}, setAttribute:function(){}, getAttribute:function(){return null;}, appendChild:function(c){return c;}, addEventListener:function(t){__events.push("el("+tag+"):"+t);}, getBoundingClientRect:function(){return {left:0,top:0,width:100,height:20};}, width:0, height:0, innerHTML:"", textContent:"" }); },
createElementNS:function(ns,tag){ return this.createElement(tag); }, createTextNode:function(t){return {nodeValue:t};},
getElementsByTagName:function(t){ return t==="html"?[docEl]:t==="body"?[bodyEl]:t==="head"?[headEl]:[]; },
getElementById:function(){return null;}, querySelector:function(){return null;}, querySelectorAll:function(){return [];},
addEventListener:function(type,fn){ __events.push("document:"+type); if(type==="DOMContentLoaded"||type==="load"||type==="readystatechange"){ try{fn&&fn();}catch(e){__errs.push("docEvh:"+e.message);} } },
removeEventListener:function(){}, dispatchEvent:function(){return true;},
fonts:{ready:Promise.resolve(),check:function(){return true;},load:function(){return Promise.resolve([]);}},
createEvent:function(){return {initEvent:function(){}};}, queryCommandSupported:function(){return false;}, adoptNode:function(n){return n;}, importNode:function(n){return n;}
});
globalThis.document = doc;
globalThis.AudioContext=function(){ return mkTotal("AudioContext", mkAudioReal("AC")); };
globalThis.webkitAudioContext=globalThis.AudioContext;
globalThis.OfflineAudioContext=function(){ return mkTotal("OfflineAudioContext", mkAudioReal("OAC")); };
globalThis.webkitOfflineAudioContext=globalThis.OfflineAudioContext;
globalThis.MessageChannel=function(){ this.port1={postMessage:function(){},onmessage:null,close:function(){}}; this.port2=this.port1; };
globalThis.Worker=function(){ this.postMessage=function(){}; this.terminate=function(){}; };
globalThis.Blob=function(a,o){ this.type=(o&&o.type)||""; this.size=10; this.arrayBuffer=function(){return Promise.resolve(new ArrayBuffer(10));}; };
globalThis.URL=createURL();
function createURL(){ return {createObjectURL:function(){return "blob:x";},revokeObjectURL:function(){}}; }
// window 级属性: 用 mkTotal 包一个真 backing, 再绑定到 globalThis
var winBacking = {
devicePixelRatio:3, innerWidth:1080, innerHeight:2256, outerWidth:1080, outerHeight:2376,
screenX:0, screenY:0, scrollX:0, scrollY:0, pageXOffset:0, pageYOffset:0,
localStorage:{getItem:function(){return null;},setItem:function(){},removeItem:function(){}},
sessionStorage:{getItem:function(){return null;},setItem:function(){}},
indexedDB:{}, name:"", status:"", closed:false,
ontouchstart:null, ondevicemotion:null, ondeviceorientation:null,
DeviceMotionEvent:function(){}, DeviceOrientationEvent:function(){},
addEventListener:function(type,fn){ __events.push("window:"+type); if(type==="load"||type==="DOMContentLoaded"){ try{fn&&fn();}catch(e){__errs.push("winEvh:"+e.message);} } },
removeEventListener:function(){}, postMessage:function(){},
WebSocket:function(){ this.send=function(){}; this.close=function(){}; },
XMLHttpRequest:function(){ this.open=function(){}; this.send=function(){}; this.setRequestHeader=function(){}; },
fetch:function(){ return Promise.resolve({json:function(){return Promise.resolve({});},text:function(){return Promise.resolve("");},arrayBuffer:function(){return Promise.resolve(new ArrayBuffer(8));}}); },
requestAnimationFrame:function(cb){ return 1; }, cancelAnimationFrame:function(){},
history:{length:1,pushState:function(){},replaceState:function(){}},
location:{href:"https://app.m.kuaishou.com/verify/captcha.html?key=-123&type=7",protocol:"https:",host:"app.m.kuaishou.com",hostname:"app.m.kuaishou.com",pathname:"/verify/captcha.html",search:"?key=-123&type=7",hash:"",origin:"https://app.m.kuaishou.com"},
crypto:{getRandomValues:function(b){for(var i=0;i<b.length;i++)b[i]=(Math.random()*256)|0;return b;},subtle:{},randomUUID:function(){return "00000000-0000-4000-8000-000000000000";}},
performance:{now:function(){return 1234.5;},timeOrigin:1700000000000,timing:{navigationStart:1700000000000,loadEventEnd:1700000002000},navigation:{type:0,redirectCount:0},getEntries:function(){return [];},getEntriesByType:function(){return [];},getEntriesByName:function(){return [];},memory:{jsHeapSizeLimit:4294705156,totalJSHeapSize:25000000,usedJSHeapSize:15000000},mark:function(){},measure:function(){}}
};
__win = mkTotal("window", winBacking, true);
// 把 winBacking 的键也挂到 globalThis (裸标识符访问), 但经 __win 代理
["devicePixelRatio","innerWidth","innerHeight","outerWidth","outerHeight","screenX","screenY","scrollX","scrollY","pageXOffset","pageYOffset","ontouchstart","ondevicemotion","ondeviceorientation","DeviceMotionEvent","DeviceOrientationEvent","WebSocket","XMLHttpRequest","fetch","requestAnimationFrame","cancelAnimationFrame","history","location","crypto","performance","localStorage","sessionStorage","indexedDB","name","status","closed"].forEach(function(k){
Object.defineProperty(globalThis, k, { configurable:true, get:function(){ return __win[k]; }, set:function(v){ winBacking[k]=v; } });
});
globalThis.addEventListener = function(t,fn){ __win.addEventListener(t,fn); };
globalThis.removeEventListener = function(){};
globalThis.window = __win; globalThis.self = __win; globalThis.global = __win;
`, ctx, { filename: "bootstrap", timeout: 15000 });
function run(label, src) {
try { vm.runInContext(src, ctx, { filename: label, timeout: 15000 }); console.log("[" + label + "] loaded OK"); }
catch (e) {
const msg = (e && e.message) || e;
const m = (e && e.stack && e.stack.match(new RegExp(label.replace(".","\\.") + ":1:(\\d+)")));
let ctx_ = "";
if (m) { const col = +m[1]; ctx_ = "\n └─ 崩溃处源码: " + src.substring(col-80, col+60).replace(/\n/g," "); }
console.log("[" + label + "] threw: " + msg + (m? " @col "+m[1] : "") + ctx_);
ctx.__errs.push(label + ":" + msg + (m? "@col"+m[1]:""));
}
}
run("kws.js", KWS);
run("kwf.js", KWF);
setTimeout(() => {
const reads = ctx.__reads, events = ctx.__events, canvas = ctx.__canvas, audio = ctx.__audio;
const FIDELITY = ["force","pressure","radiusX","radiusY","webkitForce","mozPressure","tiltX","tiltY","coalesced","getCoalescedEvents","predicted","twist","tangentialPressure","width","height"];
const fidHit = []; for (const f of FIDELITY) for (const k in reads) if (k.toLowerCase().endsWith("."+f.toLowerCase()) || k.toLowerCase().includes("."+f.toLowerCase())) fidHit.push(k+"="+reads[k]);
const AUTO = ["webdriver","phantom","nightmare","selenium","domAutomation","callPhantom","_phantom","__nightmare","cdc_","driver","headless","puppeteter","spawn","Buffer","process"];
const autoHit = []; for (const a of AUTO) for (const k in reads) if (k.toLowerCase().includes(a.toLowerCase())) autoHit.push(k+"="+reads[k]);
const behUniq = [...new Set(events.filter(e=>/mouse|touch|pointer|click|scroll|wheel|key|focus|blur|visibility|devicemotion|orientation|resize|input|drag|transition|copy|paste/i.test(e)))].sort();
console.log("\n================ kwpsec 信号采集面报告 ================");
console.log("\n[A] 行为事件监听 ("+behUniq.length+"): " + (behUniq.join(", ") || "(无)"));
console.log("\n[B] 触摸保真字段 (有命中=真手指不可伪造→人工路径): " + (fidHit.join(", ") || "(无)"));
console.log("\n[C] 自动化检测读取: " + (autoHit.join(", ") || "(无明文命中)"));
console.log("\n[D] Canvas/WebGL 探针 ("+canvas.length+"): " + ([...new Set(canvas)].slice(0,40).join(", ") || "(无)"));
console.log("\n[E] Audio 探针 ("+audio.length+"): " + ([...new Set(audio)].slice(0,20).join(", ") || "(无)"));
console.log("\n[F] 全部属性探针 (top 60 by 次数):");
console.log(Object.entries(reads).sort((a,b)=>b[1]-a[1]).slice(0,60).map(([k,v])=>" "+k+" ×"+v).join("\n"));
console.log("\n[G] 全部事件 (unique): " + ([...new Set(events)].sort().join(", ") || "(无)"));
console.log("\n[H] 运行期错误 ("+ctx.__errs.length+"): " + (ctx.__errs.slice(0,12).join(" | ") || "(无)"));
fs.writeFileSync(path.join(ROOT,"out","kws_signal_report.json"), JSON.stringify({reads,events:[...new Set(events)],canvas:[...new Set(canvas)],audio:[...new Set(audio)],errs:ctx.__errs},null,2));
console.log("\n[saved] out/kws_signal_report.json");
}, 800);