frist
This commit is contained in:
156
src/generator/motion.js
Normal file
156
src/generator/motion.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Motion Generator - Drawing the Soul
|
||||
*
|
||||
* hCaptcha uses mouse trajectory analysis to detect bots.
|
||||
* Straight lines = robot = death.
|
||||
*
|
||||
* We generate human-like mouse movements using:
|
||||
* - Bezier curves for smooth paths
|
||||
* - Perlin noise for natural jitter
|
||||
* - Realistic velocity profiles (slow start, fast middle, slow end)
|
||||
*/
|
||||
|
||||
export class MotionGenerator {
|
||||
constructor(options = {}) {
|
||||
this.screenWidth = options.screenWidth || 1920;
|
||||
this.screenHeight = options.screenHeight || 1080;
|
||||
this.checkboxPos = options.checkboxPos || { x: 200, y: 300 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate complete motion data matching hCaptcha's expected format
|
||||
*/
|
||||
generate() {
|
||||
const startTime = Date.now();
|
||||
const duration = this._randomBetween(800, 2000); // Human reaction time
|
||||
|
||||
// Starting point (off-screen or edge)
|
||||
const start = {
|
||||
x: this._randomBetween(-50, 50),
|
||||
y: this._randomBetween(this.screenHeight / 2, this.screenHeight),
|
||||
};
|
||||
|
||||
// Target: the checkbox
|
||||
const end = {
|
||||
x: this.checkboxPos.x + this._randomBetween(-5, 5),
|
||||
y: this.checkboxPos.y + this._randomBetween(-5, 5),
|
||||
};
|
||||
|
||||
// Generate movement points
|
||||
const mm = this._generateMouseMoves(start, end, startTime, duration);
|
||||
|
||||
// Mouse down/up at the end
|
||||
const clickTime = startTime + duration + this._randomBetween(50, 150);
|
||||
const md = [[end.x, end.y, clickTime]];
|
||||
const mu = [[end.x, end.y, clickTime + this._randomBetween(80, 150)]];
|
||||
|
||||
return {
|
||||
st: startTime, // Start timestamp
|
||||
dct: startTime, // Document creation time
|
||||
mm, // Mouse moves: [[x, y, timestamp], ...]
|
||||
md, // Mouse down
|
||||
mu, // Mouse up
|
||||
topLevel: {
|
||||
st: startTime - this._randomBetween(1000, 3000),
|
||||
sc: {
|
||||
availWidth: this.screenWidth,
|
||||
availHeight: this.screenHeight - 40,
|
||||
width: this.screenWidth,
|
||||
height: this.screenHeight,
|
||||
colorDepth: 24,
|
||||
pixelDepth: 24,
|
||||
},
|
||||
nv: {
|
||||
vendorSub: '',
|
||||
productSub: '20030107',
|
||||
vendor: 'Google Inc.',
|
||||
maxTouchPoints: 0,
|
||||
hardwareConcurrency: 8,
|
||||
cookieEnabled: true,
|
||||
appCodeName: 'Mozilla',
|
||||
appName: 'Netscape',
|
||||
appVersion: '5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
platform: 'Win32',
|
||||
product: 'Gecko',
|
||||
language: 'en-US',
|
||||
onLine: true,
|
||||
deviceMemory: 8,
|
||||
},
|
||||
dr: '',
|
||||
inv: false,
|
||||
exec: false,
|
||||
},
|
||||
v: 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate mouse movement points using Bezier curves
|
||||
*/
|
||||
_generateMouseMoves(start, end, startTime, duration) {
|
||||
const points = [];
|
||||
const numPoints = this._randomBetween(30, 60);
|
||||
|
||||
// Control points for cubic Bezier
|
||||
const cp1 = {
|
||||
x: start.x + (end.x - start.x) * 0.3 + this._randomBetween(-100, 100),
|
||||
y: start.y + (end.y - start.y) * 0.1 + this._randomBetween(-50, 50),
|
||||
};
|
||||
const cp2 = {
|
||||
x: start.x + (end.x - start.x) * 0.7 + this._randomBetween(-50, 50),
|
||||
y: start.y + (end.y - start.y) * 0.9 + this._randomBetween(-30, 30),
|
||||
};
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
// Non-linear time distribution (ease-in-out)
|
||||
const rawT = i / (numPoints - 1);
|
||||
const t = this._easeInOutCubic(rawT);
|
||||
|
||||
// Bezier interpolation
|
||||
const pos = this._cubicBezier(start, cp1, cp2, end, t);
|
||||
|
||||
// Add micro-jitter (human hands shake)
|
||||
pos.x += this._randomBetween(-2, 2);
|
||||
pos.y += this._randomBetween(-2, 2);
|
||||
|
||||
// Timestamp with slight randomness
|
||||
const timestamp = startTime + Math.floor(duration * rawT) + this._randomBetween(-5, 5);
|
||||
|
||||
points.push([Math.round(pos.x), Math.round(pos.y), timestamp]);
|
||||
}
|
||||
|
||||
// Sort by timestamp
|
||||
points.sort((a, b) => a[2] - b[2]);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cubic Bezier interpolation
|
||||
*/
|
||||
_cubicBezier(p0, p1, p2, p3, t) {
|
||||
const t2 = t * t;
|
||||
const t3 = t2 * t;
|
||||
const mt = 1 - t;
|
||||
const mt2 = mt * mt;
|
||||
const mt3 = mt2 * mt;
|
||||
|
||||
return {
|
||||
x: mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x,
|
||||
y: mt3 * p0.y + 3 * mt2 * t * p1.y + 3 * mt * t2 * p2.y + t3 * p3.y,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Easing function for natural movement
|
||||
*/
|
||||
_easeInOutCubic(t) {
|
||||
return t < 0.5
|
||||
? 4 * t * t * t
|
||||
: 1 - Math.pow(-2 * t + 2, 3) / 2;
|
||||
}
|
||||
|
||||
_randomBetween(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user