Files
my-botty/STEALTH.md

6.6 KiB

Botty Stealth - Deep Dive

What Warden (Battle.net Anti-Cheat) Can Detect

Warden is a kernel-level anti-cheat. It can:

  • Enumerate processes - see main.exe running, check the process name and parent
  • Scan loaded DLLs - look for known botting libraries (keyboard_io.dll, mousetool.dll, pyclick)
  • Monitor API calls - detect patterns in SendInput frequency and timing
  • Analyze input timing - bots have perfectly consistent timing; humans don't
  • Check mouse trajectory - bots move in straight lines; humans move in curves
  • Monitor memory - detect hooks, injected code, or unusual data patterns

Our Stealth Strategy (3 Tiers)

Tier 1: Input-Level Stealth (Already Working)

What we fixed:

  • Removed keyboard library (installed keyboard_io.dll kernel driver - instant detection)
  • Removed mouse/pyclick library (installed mousetool.dll kernel driver - instant detection)
  • Replaced with ctypes + user32.dll SendInput (standard Windows API, no drivers)
  • Hidden the console window (--noconsole) so no visible CMD window
  • Randomized the exe name from main.exe to something like whvitjz2.exe

Mouse movement stealth (in input_layer/mouse_impl.py):

  • Every mouse.move() generates a Bezier curve with random control points
  • Gaussian distortion applied to the curve (simulates hand tremor)
  • Endpoint wobble - final position is 2-5 pixels off, then corrected
  • Click variance - target position is randomized by +/-8px (configurable)
  • Arrival-to-click delay - 50-800ms pause between arriving and clicking (beta distribution)
  • Distance-based timing - closer clicks are faster, farther clicks take longer
  • Human curve complexity multiplier from config (default 1.0)

Keyboard stealth (in input_layer/__init__.py):

  • Every keyboard.send() includes micro-pauses before and after (20-120ms)
  • Key press duration varies (20-200ms, exponential distribution - most short, some linger)
  • Combo keys (e.g. shift + a) press modifiers individually with timing between

What's NOT yet wired: The stealth functions human_key_press() and human_keyboard_send() exist in utils/stealth.py but are called via the input layer shim automatically. Every keyboard.send() now gets stealth timing.

Tier 2: Behavior-Level Stealth (Partially Integrated)

Functions defined but NOT called from anywhere in char code:

Function What it does Where it SHOULD be used
should_wrong_waypoint() 2.5% chance of clicking wrong waypoint ui/waypoint.py when selecting TP portal
skill_rotation_hesitation() 80-300ms pause before casting Before every skill cast in char files
should_correct_skill_mistake() 1.5% chance of miscasting then correcting During skill rotation in combat
randomize_click_position() Gaussian click offset Before every mouse.click()

Current gap: These functions exist but are NOT called from the character combat code. The char files call keyboard.send("1") directly without going through a stealth wrapper. The input layer adds micro-pauses, but the behavior-level stealth (wrong skill, hesitation) is not wired in.

What IS wired:

  • mouse.stealth_move() calls randomize_click_position() and endpoint_wobble() automatically
  • mouse.click() calls apply_click_delay() automatically
  • keyboard.send() calls _stealth_before() / _stealth_after() (micro-pauses) and _stealth_duration() (variable press duration)

Tier 3: Session-Level Stealth (Fully Working)

Fully integrated in bot.py:

Function Config Behavior
should_skip_run() skip_run_chance = 10 10% chance to skip a run entirely (randomizes runtime)
maybe_afk_break() afk_break_chance = 5 5% chance after each run to take a 2-12 minute break
Run reshuffle reshuffle_each_rotation = 1 Re-shuffles run order after each full rotation
randomize_run_duration() run_duration_variance = 0.15 +/-15% variation on expected run time
Wait jitter wait_jitter_min = 0.85 / max = 1.20 Every wait() call is multiplied by 0.85x-1.20x

Current Stealth Summary

Vector Status Notes
Kernel drivers FIXED No keyboard_io.dll or mousetool.dll
Process name PARTIAL Randomized but still visible as a running process
Console window FIXED --noconsole hides the window
Mouse trajectory WORKING Bezier curves + Gaussian distortion + wobble
Click timing WORKING 50-800ms arrival-to-click delay + click variance
Key press timing WORKING 20-200ms variable duration + micro-pauses
Run timing WORKING 10% skip chance, 5% AFK break, +/-15% duration variance
Wait jitter WORKING 0.85x-1.20x on all waits
Wrong waypoint WORKING (2.5%) Wired in waypoint.py
Skill mistakes WORKING (1.5%) Wired in input_layer/init.py
Skill hesitation WORKING (80-300ms) Wired in input_layer/init.py

Remaining Risks

  1. Process visibility: Warden can still see a hidden process running alongside D2R. The exe name is random but the timing of when it starts (right when D2R launches) is suspicious.

  2. Click pattern analysis: Even with Bezier curves, Warden might detect that every "path click" follows a curve while a human sometimes double-clicks or moves in straight lines.

  3. No idle mouse movement: When the bot is fighting or waiting, the mouse is perfectly still. Humans constantly fidget with the mouse.

  4. Skill mistake only on 1-0 keys: The skill mistake feature only fires for skill hotkeys (number keys 1-0). It doesn't apply to inventory keys, stand_still, or other non-skill actions.

What Would Improve Stealth Further

High Impact (Warden likely checks these)

  1. Idle mouse movement: Add background thread that moves mouse 1-3 pixels randomly every 1-5 seconds (simulates hand resting on mouse)
  2. Occasional straight-line clicks: 10% of the time skip the Bezier curve and click directly (humans don't always move in curves)

Medium Impact

  1. Process parent spoofing: Launch the bot from a different process (e.g. from Explorer instead of directly) to hide the relationship with D2R
  2. Variable scroll speed: Add variance to mouse.wheel() calls (inventory scrolling)
  3. Right-click variation: Sometimes right-click to cancel actions before re-doing them

Low Impact (but good for completeness)

  1. Randomize AFK break more: Current range is 2-12 minutes, could be wider
  2. Add personality per character: Different timing distributions per character seed
  3. Typing delays: When using text chat (if implemented), add keystroke-by-keystroke delays