對極驗geetest滑塊驗證碼圖片還原算法的研究

免責聲明
本文章所提到的技術僅用於學習用途,禁止使用本文章的任何技術進行發起網絡攻擊、非法利用等網絡犯罪行為,一切信息禁止用於任何非法用途。若讀者利用文章所提到的技術實施違法犯罪行為,其責任一概由讀者自行承擔,與作者無關。

0x01 前言

滑塊驗證碼是我們在互聯網上經常遇見的校驗是否人類操作行為的一種檢測方式,大概流程就是生成一張圖片,然後隨機挖去一塊,在頁面展示被挖去部分的圖片,再通過js獲取用戶滑動距離,以及坐標等信息到後台進行校驗。只要用戶移動的距離符合,以及移動的軌跡行為檢測通過即可視為驗證通過。

而大部分開發者做爬蟲時經常需要繞過該此類驗證碼進行下一步的爬蟲,本文僅對極驗geetest平台下的滑塊驗證碼的圖片還原進行分析與研究,試圖找到打亂圖片到正常圖片之間的還原函數以及解析它還原算法的原理。

0x02 截獲驗證碼原圖

訪問極驗geetest滑動模塊demo頁面後請求驗證碼圖片,並使用瀏覽器F12開發者工具截獲從極驗geetest服務器響應回來的圖片。

image

image

響應回來的一張是原圖,另一張是帶有拼圖缺口的圖,但它們均是亂序的,介紹來我們得逐步分析js的源代碼是如何將亂序圖片恢復至正常圖片的。

image

0x03 找突破口

尋找思路

思路是需要找到還原驗證碼圖片的方法,首先我們得找到驗證碼被繪製時的方法調用,打開Elements(元素)選項卡,查看頁面上驗證碼圖片框內的元素。

image

顯然易見,該圖片是使用canvas畫出來的,這時候我們第一想到的就是使用事件監聽斷點,將canvas要被創建時的動作斷下來。打開Sources(源代碼)選項卡,將右側Event Listener Breakpoints(事件監聽斷點)內的Canvas->Create canvas context,勾選上,意為在canvas創建context之時將斷點停在該方法上。重新點擊刷新驗證碼圖片後,即可捕獲到該事件。

image

捕獲CanvasContext創建事件

捕獲到canvas創建context事件,由下圖不難看出,第280行代碼var o = i[$_CJDQ(76)]($_CJDQ(28));以及第282代碼var s = e[$_CJET(76)]($_CJET(28));均是創建了一個CanvasRenderingContext2D對象,並分別賦值給了變量o變量s

image

接下來直接斷點至第291行,並放行至該行代碼,忽略for循環的運算邏輯以及他的意義,我們需要先梳理被混淆後的js源代碼的實際含義是什麼。

image

恢復源代碼被混淆前的含義

經過斷點後,可以將光標放置在被混淆的掩碼上,會自動提示該掩碼的真正含義。以下是作者整理出來的含義以及恢復後的源代碼。

掩碼 實際含義
$_CJDQ(76) “getContext”
$_CJDQ(28) “2d”
$_CJET(12) “drawImage”
$_CJET(72) “height”
$_CJDQ(39) “width”
$_CJET(69) “getImageData”
$_CJET(66) “putImageData”
// slide.7.8.6.js的第280行至第290行代碼
var o = i[$_CJDQ(76)]($_CJDQ(28));
o[$_CJET(12)](t, 0, 0);
var s = e[$_CJET(76)]($_CJET(28));
e[$_CJET(72)] = r,
e[$_CJDQ(39)] = 260;
for (var a = r / 2, _ = 0; _ < 52; _ += 1) {
	var c = Ut[_] % 26 * 12 + 1,
	u = 25 < Ut[_] ? a: 0,
	l = o[$_CJET(69)](c, u, 10, a);
	s[$_CJET(66)](l, _ % 26 * 10, 25 < _ ? a: 0);
}

經過對掩碼的替換,執行含義也非常的清晰。

// 被還原後的代碼含義(變量名也經過了修改)
var CRC2D_1 = i.getContext("2d"); 	//創建一個CanvasRenderingContext2D對象
CRC2D_1.drawImage(t, 0, 0); 		//將t變量畫到CRC2D_1中,該t為驗證碼亂碼原圖
var CRC2D_2 = e.getContext("2d"); 	//創建另一個CanvasRenderingContext2D對象
CRC2D_2.height = r, 			//設置高度
CRC2D_2.width = 260;			//設置寬度
// 該循環必定是還原亂碼原圖的算法
for (var a = r / 2, _ = 0; _ < 52; _ += 1) {
	var c = Ut[_] % 26 * 12 + 1,
	u = 25 < Ut[_] ? a: 0,
	l = CRC2D_1.getImageData(c, u, 10, a);			// 將某個小區塊的亂碼原圖賦值給變量l
	CRC2D_2.putImageData(l, _ % 26 * 10, 25 < _ ? a: 0);	// 將在亂碼原圖扣下來的小部分按順序拼接到CRC2D_2中
}

顯然for循環體內的數組Ut[]必定就是該亂碼圖片還原的關鍵序列,通過調試可以得出數組Ut[]並不會隨着運行次數的改變而改變,它是固定的一個還原序列,通過Console(控制台)選項卡可直接打印數組Ut[]進行還原順序的查看。

image

0x04 還原算法的原理

首先需要知道的是亂碼圖片其實是被分成了上下各26塊小圖塊,並且接收的圖片一定被分為上下兩大塊,原因是還原算法的for循環中,定義了var a = r / 2,意思是a為圖片坐標的一半,並且是固定的一半。

image

var c = Ut[_] % 26 * 12 + 1u = 25 < Ut[_] ? a: 0,這兩條for循環體中的語句對應的變量c變量u的含義分別是第Ut[_]個亂圖中小方塊的左上角xy的像素位置。

經過l = CRC2D_1.getImageData(c, u, 10, a)方法執行,將小圖塊從(c, u)坐標開始,寬度為10,高度為a(指的就是小圖塊的高度)扣下來,保存到變量l中。

然後執行CRC2D_2.putImageData(l, _ % 26 * 10, 25 < _ ? a: 0);,將變量l保存的小圖塊,把它按從左往後,從上往下的順序,依次寫入到對象CRC2D_2中。

由此分析,可得到以下圖片的還原順序。

image

0x05 作者的一些話

該篇講述如何使用瀏覽器F12開發者工具對極驗geetest滑動驗證碼圖片的還原函數的定位、尋找思路,以及解析了還原函數的實現原理,作者強烈反對大家使用該技術進行實質性地爬蟲以及其他形式的利用。

請時刻牢記:天網恢恢,疏而不漏。