chaser-gt 0.1.0

High-performance Geetest v4 captcha solver with automatic deobfuscation
Documentation

chaser-gt

A high-performance Rust port of GeekedTest - a Geetest v4 captcha solver.

Features

  • Automatic Deobfuscation: Constants are automatically updated when Geetest releases new versions - no manual intervention required!
  • Multiple Captcha Types: Supports Slide, Gobang, Icon, and AI/Invisible captchas
  • TLS Fingerprinting: Uses rquest for Chrome-like TLS fingerprinting
  • Proxy Support: HTTP and SOCKS5 proxy support with authentication
  • Async/Await: Built on Tokio for efficient concurrent captcha solving
  • Cross-Platform: Works on Windows, macOS, and Linux

Installation

Add to your Cargo.toml:

[dependencies]
chaser-gt = { git = "https://github.com/ccheshirecat/chaser-gt" }
tokio = { version = "1", features = ["full"] }

Quick Start

Basic usage:

use chaser_gt::{Geeked, RiskType};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create a solver for slide captcha
    let solver = Geeked::builder("your_captcha_id", RiskType::Slide)
        .build()
        .await?;

    // Solve the captcha
    let result = solver.solve().await?;

    println!("captcha_id: {}", result.captcha_id);
    println!("lot_number: {}", result.lot_number);
    println!("pass_token: {}", result.pass_token);
    println!("gen_time: {}", result.gen_time);
    println!("captcha_output: {}", result.captcha_output);

    Ok(())
}

With Proxy

let solver = Geeked::builder("captcha_id", RiskType::Slide)
    .proxy("http://user:pass@proxy.example.com:8080")  // HTTP proxy
    // or: .proxy("socks5://127.0.0.1:1080")           // SOCKS5 proxy
    .build()
    .await?;

With User Info (Site-Specific Binding)

Some sites require a user_info parameter to bind captcha verification to a specific user/session:

let solver = Geeked::builder("captcha_id", RiskType::Ai)
    .user_info("account_id=12345")  // Site-specific data
    .proxy("http://proxy:8080")
    .build()
    .await?;

With IPv6/Local Address Binding

For scenarios where you need to route captcha solving through a specific network interface or IPv6 address (e.g., BGP exit nodes):

use std::net::IpAddr;

let ipv6: IpAddr = "2a11:29c0:4f50::1".parse().unwrap();

let solver = Geeked::builder("captcha_id", RiskType::Ai)
    .local_address(ipv6)  // Bind to specific IPv6
    .build()
    .await?;

This is useful for:

  • BGP exit nodes: Route each account through a unique /128 from your subnet
  • Multi-IP setups: Distribute captcha solving across multiple IPs
  • IP consistency: Ensure captcha and subsequent requests use the same IP

Supported Captcha Types

Type Enum Description
Slide RiskType::Slide Slide puzzle captcha
Gobang RiskType::Gobang Five-in-a-row puzzle
Icon RiskType::Icon Icon selection captcha (requires icon feature)
AI RiskType::Ai AI/Invisible captcha

Icon Captcha Support

To enable icon captcha support, add the icon feature:

[dependencies]
chaser-gt = { git = "https://github.com/ccheshirecat/chaser-gt", features = ["icon"] }

The icon solver uses:

  • ONNX Runtime for neural network inference
  • Image processing to detect icon regions
  • A bundled classification model to identify arrow directions

The ONNX model (geetest_v4_icon.onnx) is embedded in the binary for easy distribution.

Key Improvements

Automatic Constant Updates

Unlike the Python version which requires manually running deobfuscate.py when Geetest updates, this Rust implementation:

  1. Checks for new versions on startup
  2. Automatically deobfuscates the Geetest script
  3. Caches constants to ~/.cache/chaser-gt/constants.json
  4. Refreshes automatically when Geetest updates

This means the solver stays functional without any manual intervention!

Multi-Round Verification Support

Some sites use multi-round verification where Geetest returns result: "continue" with updated payload. This library automatically handles the retry loop, making it compatible with sites like shuffle.com that require multiple verification rounds.

Architecture

chaser-gt/
├── src/
│   ├── lib.rs           # Public API exports
│   ├── client.rs        # Main Geeked client
│   ├── deobfuscate.rs   # Auto-deobfuscation system
│   ├── sign.rs          # W parameter generation
│   ├── error.rs         # Error types
│   ├── models.rs        # Data structures
│   ├── crypto/
│   │   ├── aes_enc.rs   # AES-CBC encryption
│   │   ├── rsa_enc.rs   # RSA PKCS1v1.5
│   │   └── pow.rs       # Proof of Work
│   └── solvers/
│       ├── slide.rs     # Slide captcha solver
│       ├── gobang.rs    # Gobang puzzle solver
│       └── icon.rs      # Icon captcha solver
└── models/
    └── geetest_v4_icon.onnx  # ONNX model for icon detection

Building

cd chaser-gt

# Without icon support
cargo build --release

# With icon support (includes ONNX runtime)
cargo build --release --features icon

# With C FFI bindings
cargo build --release --features ffi

C FFI Bindings

chaser-gt provides C FFI bindings for use from Python, Go, Node.js, C/C++, etc.

Building FFI Library

cargo build --release --features ffi

# Library outputs:
# - target/release/libchaser_gt.so (Linux)
# - target/release/libchaser_gt.dylib (macOS)
# - target/release/chaser_gt.dll (Windows)

# C header generated at:
# - include/chaser_gt.h

C Example

#include "chaser_gt.h"
#include <stdio.h>

int main() {
    // Solve a slide captcha (blocking call)
    GeekedResult result = geeked_solve(
        "your_captcha_id",  // captcha_id
        "slide",            // risk_type: slide, gobang, icon, ai
        NULL,               // proxy (optional): "http://user:pass@host:port"
        NULL                // user_info (optional)
    );

    if (result.error_code == 0) {
        printf("lot_number: %s\n", result.lot_number);
        printf("pass_token: %s\n", result.pass_token);
        printf("captcha_output: %s\n", result.captcha_output);
    } else {
        printf("Error: %s\n", result.error_message);
    }

    geeked_free_result(result);
    return 0;
}

Compile with:

gcc -o example example.c -L./target/release -lchaser_gt -lpthread -ldl -lm

Python Example (ctypes)

import ctypes
import json
from ctypes import c_char_p, c_int, Structure, POINTER

# Load library
lib = ctypes.CDLL('./target/release/libchaser_gt.so')  # or .dylib on macOS

# Define result structure
class GeekedResult(Structure):
    _fields_ = [
        ('error_code', c_int),
        ('error_message', c_char_p),
        ('captcha_id', c_char_p),
        ('lot_number', c_char_p),
        ('pass_token', c_char_p),
        ('gen_time', c_char_p),
        ('captcha_output', c_char_p),
    ]

# Or use the simpler JSON API
lib.geeked_solve_json.restype = c_char_p

result_json = lib.geeked_solve_json(
    b"your_captcha_id",
    b"slide",
    None,  # proxy
    None   # user_info
)

result = json.loads(result_json.decode())
lib.geeked_free_string(result_json)

if result['success']:
    print(f"pass_token: {result['pass_token']}")
else:
    print(f"Error: {result['error']}")

FFI Functions

// Solve captcha, returns struct with all fields
GeekedResult geeked_solve(
    const char* captcha_id,   // Required
    const char* risk_type,    // Required: "slide", "gobang", "icon", "ai"
    const char* proxy,        // Optional: "http://host:port" or "socks5://host:port"
    const char* user_info     // Optional: site-specific binding
);

// Solve captcha, returns JSON string
char* geeked_solve_json(
    const char* captcha_id,
    const char* risk_type,
    const char* proxy,
    const char* user_info
);

// Free result struct
void geeked_free_result(GeekedResult result);

// Free string
void geeked_free_string(char* s);

// Get library version
const char* geeked_version();

Running Tests

# Test without icon feature
cargo test

# Test with icon feature
cargo test --features icon

Running Example

cargo run --example solve_captcha

Dependencies

Key dependencies:

  • rquest - HTTP client with TLS fingerprinting
  • tokio - Async runtime
  • aes, rsa, sha2 - Cryptography (RustCrypto)
  • image, imageproc - Image processing for slide captcha
  • ort - ONNX Runtime for icon captcha (optional)

API Reference

GeekedResult

The solve() method returns a GeekedResult containing all the fields needed for verification:

pub struct GeekedResult {
    pub captcha_id: String,      // The captcha ID used
    pub lot_number: String,      // Unique lot number for this solve
    pub pass_token: String,      // Token to submit to the site
    pub gen_time: String,        // Generation timestamp
    pub captcha_output: String,  // Encrypted captcha output
}

Error Handling

use chaser_gt::{Geeked, RiskType, GeekedError};

match solver.solve().await {
    Ok(result) => println!("Token: {}", result.pass_token),
    Err(GeekedError::NetworkError(e)) => eprintln!("Network failed: {}", e),
    Err(GeekedError::CaptchaFailed(msg)) => eprintln!("Captcha failed: {}", msg),
    Err(GeekedError::DeobfuscationFailed) => eprintln!("Script deobfuscation failed"),
    Err(e) => eprintln!("Other error: {}", e),
}

Requirements

  • Rust 1.70+ (for async traits)
  • Internet connection (fetches Geetest scripts for deobfuscation)
  • Optional: Proxy for production use

License

MIT License - same as the original GeekedTest project.

Acknowledgements

  • GeekedTest - Original Python implementation by xKiian