基於YOLO實現滑塊驗證碼破解

chyun2011發表於2024-11-16

申明:本案例中的思路和技術僅用於學習交流。請勿用於非法行為。

一、訓練模型

詳細訓練步驟和匯出模型參考 滑塊驗證碼識別模型訓練

二、模型試用

透過YoloDotNet執行模型,計算出滑塊缺口位置後用RESTful格式的介面返回座標給其它應用呼叫。YoloDotNet案例參考 物體檢測框架YoloDotNet初體驗

主要步驟:

1. 建立webapi(c#)專案

# -n:指定專案名稱為slider_api
dotnet new webapi -n slider_api
# 切換到專案目錄下
cd slider_api
# 新增兩項依賴
dotnet add package System.Drawing.Common
dotnet add package YoloDotNet

2. 建立控制器,用於計算滑塊位置並返回。幾個需要注意的點:

  • 建立Yolo模型時OnnxModel引數應設定為第一步中訓練好的模型檔案。
  • 模型識別結果是一個BoundingBox物件,不是座標位置,需要手動處理。BoundingBox型別為SKRectI,可以理解為一個矩形。觀察BoundingBox中的屬性值有Left、Right、Top、Bottom、Width等。那麼缺口在背景圖中的中心點的X座標軸計算方式應該為Left+(Width/2)。
  • 實驗中計算出的X軸座標與手動拖動滑塊的真實座標可能會有一個誤差,但若干次測試後觀察到這個誤差波動不大,在一個比較固定的範圍內。因此計算出的座標值需要調整,調整值是多少?用相同的驗證碼圖片分別在真實環境和模型中測試,得到一組資料計算出平均值,本例只取了10來組資料。
  • 模型會返回一對座標資料,本案例中只有X軸座標的位置會用於驗證,因為驗證碼的背景圖和滑塊圖的高度是完全一樣的,滑鼠拖過中滑塊只會橫向移動。

核心程式碼:

    [ApiController]
    [Route("api/yolo")]
    public class YoloController : ControllerBase
    {

        private Yolo yolo;

        public YoloController()
        {
            yolo = new Yolo(new YoloOptions
            {
                OnnxModel = $"e:/4-code-space/82.yolo/models/slider.onnx",
                ModelType = ModelType.ObjectDetection,
                Cuda = false,
                GpuId = 0,
                PrimeGpu = false,
            });
        }

        [HttpPost]
        public IActionResult Detect([FromForm] IFormFile image)
        {
            try
            {
                using var memoryStream = new MemoryStream();
                image.CopyTo(memoryStream);
                var imageBytes = memoryStream.ToArray();
                using var imageStream = new MemoryStream(imageBytes);
                using var skBitmap = SKBitmap.Decode(imageStream);
                using var skImage = SKImage.FromBitmap(skBitmap);


                var results = yolo.RunObjectDetection(skImage, confidence: 0.25, iou: 0.7);
                int x = results[0].BoundingBox.Left + (results[0].BoundingBox.Width / 2);
                x-=20;//同圖片相比wq yolov5的結果對比,平均需要減20
                Console.WriteLine($"識別結果:{x}");
                return Ok(x);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"識別錯誤:{ex.Message}");
                return BadRequest(ex.Message);
            }
        }
    }

3. 將http請求路由到控制器,修改Program.cs,在app.Run()前面加一句app.MapControllers()。本專案sdk版本為8.0.403。

三、驗證模型計算結果

1. 根據第二步計算出的結果模擬生成驗證資料,按照介面要求的資料格式拼接http報文發往服務端驗證識別結果是否正確。開啟chrome開發者工具觀察真實驗證資料,多次實驗踩坑後總結驗證資料有以下特徵:

  • 滑塊驗證碼的驗證是在服務端進行的,滑鼠拖動滑塊過程中會產生一系列軌跡資料,這個軌跡資料記錄是js實現的,驗證過程中將驗證碼id和軌跡資料一併發往服務端,服務端驗證透過返回目標資料。

  • 軌跡資料有4個屬性x、y、t、type,分別表示橫座標、縱座標、時間、滑鼠動作(down、move、up)。

  • y座標值不會參與驗證,但軌跡資料中y值不是恆定不變的。因為橫向拖動滑鼠形成的不是絕對的橫線而是近似橫線,最終的y值是圍繞0上下波動的,但波動範圍不能太大。

  • 軌跡資料的t值的間距由小變大,再由大變小。因為拖動滑塊是由慢到快,再由快到慢最終停止的過程。最後一個軌跡點的x值只需要接近真實值即可。

  • 軌跡資料的x值由0逐漸增大到接近實際值(模型返回的缺口位置),接近後還可以再小距離反向遠離實際值。因為真實環境中滑塊可能拖動過度再回撥。

按上以分析生成模擬資料,java實現程式碼如下:

private List<MouseTrajectory> generateTrajectory(Integer width) {
    List<MouseTrajectory> trajectories = new ArrayList<>();
    Random random = new Random();
    int x = 0, y = 0, tmp = 0, t = 0;
    t = random.nextInt(50) + 50;
    // 按下滑鼠
    trajectories.add(new MouseTrajectory(x, y, "down", t));
    // 拖動滑鼠
    while (x < width) {
        x += random.nextInt(5) + 2;
        tmp = random.nextInt(100) % 4;
        if (tmp == 0) {
            y = random.nextInt(7) - 3;
        }
        t += random.nextInt(60) + 20;
        if (x > width) {
            x = width;
        }
        trajectories.add(new MouseTrajectory(x, y, "move", t));
    }
    // 釋放滑鼠
    trajectories.add(new MouseTrajectory(x, y, "up", t));
    return trajectories;
}

2. 將生成的軌跡資料和其它查詢引數按照目標介面格式組裝後發往服務端。http報文的header值保持跟chrome開發者工具中的資料完全一致,儘量模擬真實環境。最終得到正確的服務端響應,這個案例中的返回資料是json格式字串的base64編碼值,需要再解碼。

為了不暴露案例中的系統介面地址,只截圖小部分返回資料和模擬生成的軌跡資料:

總結實驗成功的兩個關鍵點:

1. 模型識別準確。訓練模型用了真實環境驗證碼圖片約300張。

2. 軌跡驗證資料儘量接近真實資料。

相關文章