免責宣告
本文章所提到的技術僅用於學習用途,禁止使用本文章的任何技術進行發起網路攻擊、非法利用等網路犯罪行為,一切資訊禁止用於任何非法用途。若讀者利用文章所提到的技術實施違法犯罪行為,其責任一概由讀者自行承擔,與作者無關。
0x01 前言
滑塊驗證碼是我們在網際網路上經常遇見的校驗是否人類操作行為的一種檢測方式,大概流程就是生成一張圖片,然後隨機挖去一塊,在頁面展示被挖去部分的圖片,再通過js獲取使用者滑動距離,以及座標等資訊到後臺進行校驗。只要使用者移動的距離符合,以及移動的軌跡行為檢測通過即可視為驗證通過。
而大部分開發者做爬蟲時經常需要繞過該此類驗證碼進行下一步的爬蟲,本文僅對極驗geetest平臺下的滑塊驗證碼的圖片還原進行分析與研究,試圖找到打亂圖片到正常圖片之間的還原函式以及解析它還原演算法的原理。
0x02 截獲驗證碼原圖
訪問極驗geetest滑動模組demo頁面後請求驗證碼圖片,並使用瀏覽器F12開發者工具
截獲從極驗geetest伺服器響應回來的圖片。
響應回來的一張是原圖,另一張是帶有拼圖缺口的圖,但它們均是亂序的,介紹來我們得逐步分析js的原始碼是如何將亂序圖片恢復至正常圖片的。
0x03 找突破口
尋找思路
思路是需要找到還原驗證碼圖片的方法,首先我們得找到驗證碼被繪製時的方法呼叫,開啟Elements(元素)
選項卡,檢視頁面上驗證碼圖片框內的元素。
顯然易見,該圖片是使用canvas畫出來的,這時候我們第一想到的就是使用事件監聽斷點,將canvas要被建立時的動作斷下來。開啟Sources(原始碼)
選項卡,將右側Event Listener Breakpoints(事件監聽斷點)
內的Canvas
->Create canvas context
,勾選上,意為在canvas建立context之時將斷點停在該方法上。重新點選重新整理驗證碼圖片後,即可捕獲到該事件。
捕獲CanvasContext建立事件
捕獲到canvas建立context事件,由下圖不難看出,第280行程式碼var o = i[$_CJDQ(76)]($_CJDQ(28));
以及第282程式碼var s = e[$_CJET(76)]($_CJET(28));
均是建立了一個CanvasRenderingContext2D物件,並分別賦值給了變數o
與變數s
。
接下來直接斷點至第291行,並放行至該行程式碼,忽略for迴圈的運算邏輯以及他的意義,我們需要先梳理被混淆後的js原始碼的實際含義是什麼。
恢復原始碼被混淆前的含義
經過斷點後,可以將游標放置在被混淆的掩碼上,會自動提示該掩碼的真正含義。以下是作者整理出來的含義以及恢復後的原始碼。
掩碼 | 實際含義 |
---|---|
$_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[]
進行還原順序的檢視。
0x04 還原演算法的原理
首先需要知道的是亂碼圖片其實是被分成了上下各26塊小圖塊,並且接收的圖片一定被分為上下兩大塊,原因是還原演算法的for迴圈中,定義了var a = r / 2
,意思是a為圖片座標的一半,並且是固定的一半。
var c = Ut[_] % 26 * 12 + 1
和u = 25 < Ut[_] ? a: 0
,這兩條for迴圈體中的語句對應的變數c
與變數u
的含義分別是第Ut[_]個
亂圖中小方塊的左上角x
與y
的畫素位置。
經過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
中。
由此分析,可得到以下圖片的還原順序。
0x05 作者的一些話
該篇講述如何使用瀏覽器F12開發者工具
對極驗geetest滑動驗證碼圖片的還原函式的定位、尋找思路,以及解析了還原函式的實現原理,作者強烈反對大家使用該技術進行實質性地爬蟲以及其他形式的利用。
請時刻牢記:天網恢恢,疏而不漏。