mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-06-01 04:11:35 +02:00
@@ -9,6 +9,7 @@ import (
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -1292,30 +1293,63 @@ func (m *RemoteBrowserController) dispatchInput(page *rod.Page, msg []byte) {
|
||||
btn = proto.InputMouseButtonRight
|
||||
}
|
||||
mods := int(cmd.Modifiers)
|
||||
// Shared Buttons bitmask values for pointer events.
|
||||
zeroButtons := 0
|
||||
oneButton := 1
|
||||
nowTs := func() proto.TimeSinceEpoch {
|
||||
return proto.TimeSinceEpoch(float64(time.Now().UnixNano()) / 1e9)
|
||||
}
|
||||
switch cmd.Type {
|
||||
case "mousemove":
|
||||
// Add ±0.5 px uniform noise so canvas-quantized coordinates have
|
||||
// subpixel variation, matching natural pointer imprecision.
|
||||
jx := cmd.X + (rand.Float64()*2-1)*0.5
|
||||
jy := cmd.Y + (rand.Float64()*2-1)*0.5
|
||||
// Add ±1 px integer noise then round: keeps movementX == clientX-prevClientX
|
||||
// consistent (subpixel CDP coordinates create a float/int mismatch detectors
|
||||
// check), while still adding the ±1 px variation that breaks exact-integer paths.
|
||||
jx := math.Round(cmd.X + (rand.Float64()*2-1)*0.5)
|
||||
jy := math.Round(cmd.Y + (rand.Float64()*2-1)*0.5)
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMouseMoved,
|
||||
X: jx, Y: jy, Modifiers: mods,
|
||||
Type: proto.InputDispatchMouseEventTypeMouseMoved,
|
||||
X: jx,
|
||||
Y: jy,
|
||||
Modifiers: mods,
|
||||
Timestamp: nowTs(),
|
||||
Button: proto.InputMouseButtonNone,
|
||||
Buttons: &zeroButtons,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
case "mousedown":
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMousePressed,
|
||||
X: cmd.X, Y: cmd.Y, Button: btn, ClickCount: 1, Modifiers: mods,
|
||||
Type: proto.InputDispatchMouseEventTypeMousePressed,
|
||||
X: cmd.X,
|
||||
Y: cmd.Y,
|
||||
Modifiers: mods,
|
||||
Timestamp: nowTs(),
|
||||
Button: btn,
|
||||
Buttons: &oneButton,
|
||||
ClickCount: 1,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
case "mouseup":
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMouseReleased,
|
||||
X: cmd.X, Y: cmd.Y, Button: btn, ClickCount: 1, Modifiers: mods,
|
||||
Type: proto.InputDispatchMouseEventTypeMouseReleased,
|
||||
X: cmd.X,
|
||||
Y: cmd.Y,
|
||||
Modifiers: mods,
|
||||
Timestamp: nowTs(),
|
||||
Button: btn,
|
||||
Buttons: &zeroButtons,
|
||||
ClickCount: 1,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
case "scroll":
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMouseWheel,
|
||||
X: cmd.X, Y: cmd.Y, DeltaX: cmd.DeltaX, DeltaY: cmd.DeltaY, Modifiers: mods,
|
||||
Type: proto.InputDispatchMouseEventTypeMouseWheel,
|
||||
X: cmd.X,
|
||||
Y: cmd.Y,
|
||||
DeltaX: cmd.DeltaX,
|
||||
DeltaY: cmd.DeltaY,
|
||||
Modifiers: mods,
|
||||
Timestamp: nowTs(),
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
case "keydown":
|
||||
proto.InputDispatchKeyEvent{
|
||||
|
||||
@@ -664,6 +664,27 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page,
|
||||
// consecutive moveMouse and clickXY calls produce a continuous path.
|
||||
var mouseX, mouseY float64
|
||||
|
||||
// cdpMouseMove dispatches a MouseMoved event with all required fields set.
|
||||
// Using proto.InputDispatchMouseEvent directly (instead of page.Mouse.MoveTo)
|
||||
// lets us set Timestamp, PointerType, and Buttons - fields that rod omits
|
||||
// and that detectors use to distinguish CDP-injected events from hardware input.
|
||||
zeroButtons := 0
|
||||
cdpMouseMove := func(x, y float64) {
|
||||
// Round to integers: Chrome stores cursor position as float internally and
|
||||
// computes movementX/Y from the float delta, but event.clientX/Y are integers.
|
||||
// Subpixel coordinates create a movementX != clientX-prevClientX mismatch
|
||||
// that detectors use as a CDP fingerprint.
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMouseMoved,
|
||||
X: math.Round(x),
|
||||
Y: math.Round(y),
|
||||
Timestamp: proto.TimeSinceEpoch(float64(time.Now().UnixNano()) / 1e9),
|
||||
Button: proto.InputMouseButtonNone,
|
||||
Buttons: &zeroButtons,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
}
|
||||
|
||||
// humanMoveTo moves the CDP cursor from the last tracked position to
|
||||
// (targetX, targetY) along a cubic Bezier curve with ease-in-out timing
|
||||
// and optional micro-jitter, mimicking natural hand movement.
|
||||
@@ -680,7 +701,7 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page,
|
||||
dx, dy := targetX-startX, targetY-startY
|
||||
dist := math.Sqrt(dx*dx + dy*dy)
|
||||
if dist < 2 {
|
||||
page.Mouse.MoveTo(proto.Point{X: targetX, Y: targetY}) //nolint:errcheck
|
||||
cdpMouseMove(targetX, targetY)
|
||||
mouseX, mouseY = targetX, targetY
|
||||
return
|
||||
}
|
||||
@@ -719,12 +740,12 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page,
|
||||
bx += (rand.Float64()*2 - 1) * jitterPx
|
||||
by += (rand.Float64()*2 - 1) * jitterPx
|
||||
}
|
||||
page.Mouse.MoveTo(proto.Point{X: bx, Y: by}) //nolint:errcheck
|
||||
cdpMouseMove(bx, by)
|
||||
if i < steps {
|
||||
time.Sleep(stepDur)
|
||||
}
|
||||
}
|
||||
page.Mouse.MoveTo(proto.Point{X: targetX, Y: targetY}) //nolint:errcheck
|
||||
cdpMouseMove(targetX, targetY)
|
||||
mouseX, mouseY = targetX, targetY
|
||||
}
|
||||
|
||||
@@ -750,11 +771,39 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page,
|
||||
})
|
||||
|
||||
pc.Set("clickXY", func(call goja.FunctionCall) goja.Value {
|
||||
x := call.Argument(0).ToFloat()
|
||||
y := call.Argument(1).ToFloat()
|
||||
x := math.Round(call.Argument(0).ToFloat())
|
||||
y := math.Round(call.Argument(1).ToFloat())
|
||||
dbg(fmt.Sprintf("→ clickXY %.0f,%.0f", x, y))
|
||||
humanMoveTo(x, y, -1, -1)
|
||||
must(page.Mouse.Click(proto.InputMouseButtonLeft, 1))
|
||||
// Dispatch mousedown and mouseup directly so we can set Timestamp,
|
||||
// PointerType, and Buttons - and add a realistic hold duration.
|
||||
// page.Mouse.Click omits these fields and has 0 ms hold time.
|
||||
oneButton := 1
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMousePressed,
|
||||
X: x,
|
||||
Y: y,
|
||||
Timestamp: proto.TimeSinceEpoch(float64(time.Now().UnixNano()) / 1e9),
|
||||
Button: proto.InputMouseButtonLeft,
|
||||
Buttons: &oneButton,
|
||||
ClickCount: 1,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
// Human click hold: 80-150 ms between press and release.
|
||||
time.Sleep(time.Duration(80+rand.Intn(70)) * time.Millisecond)
|
||||
proto.InputDispatchMouseEvent{
|
||||
Type: proto.InputDispatchMouseEventTypeMouseReleased,
|
||||
X: x,
|
||||
Y: y,
|
||||
Timestamp: proto.TimeSinceEpoch(float64(time.Now().UnixNano()) / 1e9),
|
||||
Button: proto.InputMouseButtonLeft,
|
||||
Buttons: &zeroButtons,
|
||||
ClickCount: 1,
|
||||
PointerType: proto.InputDispatchMouseEventPointerTypeMouse,
|
||||
}.Call(page) //nolint:errcheck
|
||||
// Sync rod's internal position tracker so any subsequent rod operations
|
||||
// (e.g. el.Click) see the correct cursor location.
|
||||
page.Mouse.MoveTo(proto.Point{X: x, Y: y}) //nolint:errcheck
|
||||
dbg(fmt.Sprintf("✓ clickXY %.0f,%.0f", x, y))
|
||||
return goja.Undefined()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user