極驗驗證可以被破解嗎?

極驗驗證


人機識別是一個不斷攻防對抗的過程,極驗驗證碼是一種新的驗證碼嘗試。在國內出現此類創新很贊,但從破解角度分析,極驗有如下優劣之處:

優勢:

  • 服務端無監督機器學習實時發現異常聚類的能力,黑灰產破解極驗主要的門檻也在此,這是極驗和黑灰產攻防對抗的依仗;

劣勢:

  • 前端信息採集加密對抗較弱,這也間接的導致黑灰產可以簡單逆向,不斷的fuzz後端模型進行bypass;

  • 缺乏二次驗證機制,bypass滑塊拼圖後對抗無縱深;

比如有人在github上將破解程序開源豆瓣登陸驗證碼的識別腳本 ,從代碼中可以看出極驗的前端加密的確很弱,這一點可以借鑒Google 的recaptcha和阿里的滑動驗證的前端加密實踐騰訊的點擊驗證碼是什麼原理? - 網路安全 - 知乎。


我的小站到現在用極驗一年左右,對極驗驗證做了比較多的功課,我先說一下我對極驗驗證功能模式的理解(以下僅限於我的專業知識,如有不妥各位大神勿噴):它主要是以行為來作為對終端交互對象的判別標準,但是整體又依賴了一小部分圖片識別的驗證技術。所以如果想去破解它,有三個技術門檻,首先是圖像識別、其次是拖動滑塊的行為模擬、最後是數據編解碼。

所以想破解極驗驗證主要的步驟如下:

  1. 先獲取驗證模塊相關數據和圖片資源。

  2. 識別圖像,得到答案的位置。

  3. 根據答案位置模擬出一段拖動軌跡。

  4. 對行為和答案位置數據進行編碼,提交驗證

  5. 得到極驗伺服器的返回結果,提交客戶伺服器。

  6. 完成一次驗證,並在客戶伺服器上產生一個想要的動作或一次破壞行為。

只有完成上述步驟才有希望破解,而其中每一步的難度都相當大,使得破解軟體很難編寫(至少我現在寫不出來,以後也.....)。

作為網站主來說現在很難找到一個兼顧防刷和用戶體驗的驗證了,我以前是用比較難識別的圖文驗證碼,每次被刷後我就繼續去換更難識別的驗證,其實就是一個惡性循環。後來在discuz上集成的極驗驗證,當時集成它的主要原因是因為它的畫風跟我小清新的站放在一起比較和諧.......自己建過站的筒子們應該都了解被爬蟲爬到的後果,尼瑪到處留廣告......(在這裡我對做爬蟲的大神們膜拜三分鐘,求把這技術拿來做點正經事).....後來我對它的功能模式補了很多功課,破解軟體什麼的我基本都抱著學術的態度試過,用網上流傳的破解版本去嘗試攻擊自己的站,最後都是被forbidden掉了......

做安全的行業,永遠是會存在黑客和白客之間的相互技術科技競賽。天下沒有永遠不透風的牆,黑客不斷進步,那麼白客就必須不斷的升級防禦手段效果和增加防禦方案來提升自己的安全技術水平,以一個網站主的角度來說還是希望以後極驗驗證可以越做越好,不斷進行特徵的動態更新來防護我們的小站吧~


分享一下滑動驗證碼的模擬滑動攻克

源碼分享 darbra/geetest

## 瞬間移動 直接在1s內移動到目標位置 結果就是」被吃了「

## 勻速直線運動 使用勻速直線運動大法, 果不其然,被吃了

## 隨機亂速直線運動 使用亂速運動法, 有一次竟然沒有被吃了

## 模模擬人運動 停停頓頓,偶爾後撤,效果不錯

## 模仿抖抖病患者運動 顫顫巍巍,如履薄冰,估計geetest伺服器認為是我外婆在操作吧

源碼分享 darbra/geetest

僅僅只是學習美好,領略有趣,分享快樂。


今天看到CSDN上面對於這個驗證碼的介紹,簡直是驗證的未來。容我說一句:凈他媽扯淡(笑)

既然CSDN上吹的這麼厲害我就來說一說這個驗證碼幹了什麼。打開在線Demo,F12大法分析代碼。

  • 首先發現原始圖片是亂序的,沒關係,只要能拼接成功就可以。實際上我發現每次圖片打亂的規律是相同的。所以簡單的固定的圖片裁剪拼接就可以

  • 每一次拖動完成驗證碼之後會發送一個AJAX請求,主要包含了密碼(來源驗證用),時間,用戶移動的長度,一個奇怪的字元串。這些所有的都是加密的,那就調試唄
  • 調試的時候就不管那些有的沒了,這裡頂多也就是和滑鼠拖動的軌跡相關。那麼破解原理就很明白嘍

既然這樣,讓我們來一個一勞永逸的破解方法

  • 寫一個程序包含一個瀏覽器內核(一下方便好多了呢,不用看他們噁心的混淆後的代碼啦)
  • 通過某種方式還原出圖像(Easy)
  • 找到圖形中的滑塊坐標(難度中等以下,OpenCV直接搞定)
  • 生成一個軌跡(難度中等)
  • 模擬滑鼠拖動(Easy)

這個最關鍵的軌跡怎麼生成呢?

有一種神奇的東西叫做神經網路。我們現在只需要寫一個程序隨機的生成滑塊拖動任務,然後讓用戶拖動,構建一個軌跡的資料庫(定時採樣XY坐標),於是就構成了一個二維到n*2維的一個映射。如果直接擬合這個的話根本不像是人類的行為。我個人認為比較合理的做法是用類似時間序列的處理方式來處理。由於具體的還沒有做,所以我這面也不好說。到最後成了智能演算法的比拼呢。

但是神經網路那種東西作為人類的愚蠢的模仿者還是應該能不錯的完成任務的。


這個東西比較難的就是軌跡,滑動到的位置根據對比兩圖像素點可以找到,後續的js用phantomjs分分鐘解決,這些東西一個小時不到就解決了,滑塊的軌跡昨天弄了一天,就通過了一次,今天還要繼續弄=_=

__________________________________________________________________________________

採集了自己的三十組滑鼠移動數據做個回歸,沒想到拿下了,哈哈

____________________________________________________________________________________

最近總有人私信問怎麼破解的。不打算做出太詳細地回答,畢竟別人的蛋糕自己偷吃了再呼朋喚友一起偷吃就有些不地道了。下面簡單講下我的思路,滑鼠滑動數據可以通過一些軌跡記錄軟體得到,數據的採集就是本人自己劃,成功的話就把這組數據記錄下來,之後把數據整理一下,得到滑鼠的時間和位移數據,隨便找幾組數據做折線圖,就能發現大體的運動速度變化。有機器學習基礎的可以根據這些數據線性回歸,沒有機器學習基礎的話根據軌跡去碰數估計也能通過。大概就是這個思路。

成功率的話。開始時很高大概十次有九次可以通過,現在可能極驗升級演算法了十次有五六次可以通過,但發現現在自己手動滑的話也有很多時候過不去。


提供一種效率不是很高的破解方式selenium+java

完整代碼已上傳至github,地址:https://github.com/wycm/selenium-geetest-crack

selenium+java破解極驗滑動驗證碼

摘要

分析驗證碼素材圖片混淆原理,並採用selenium模擬人拖動滑塊過程,進而破解驗證碼。

人工驗證的過程

  • 打開威鋒網註冊頁面(https://passport.feng.com/?r=user/register
  • 移動滑鼠至小滑塊,一張完整的圖片會出現(如下圖1)

  • 點擊滑鼠左鍵,圖片中間會出現一個缺塊(如下圖2)

  • 移動小滑塊正上方圖案至缺塊處
  • 驗證通過

selenium模擬驗證的過程

  1. 載入威鋒網註冊頁面(https://passport.feng.com/?r=user/register
  2. 下載圖片1和缺塊圖片2
  3. 根據兩張圖片的差異計算平移的距離x
  4. 模擬滑鼠點擊事件,點擊小滑塊向右移動x
  5. 驗證通過

詳細分析

  • 打開chrome瀏覽器控制台,會發現圖1所示的驗證碼圖片並不是極驗後台返回的原圖。而是由多個div拼接而成(如下圖3)

通過圖片顯示div的style屬性可知,極驗後台把圖片進行切割加錯位處理。把素材圖片切割成10 * 58大小的52張小圖,再進行錯位處理。在網頁上顯示的時候,再通過css的background-position屬性對圖片進行還原。以上的圖1和圖2都是經過了這種處理。在這種情況下,使用selenium模擬驗證是需要對下載的驗證碼圖片進行還原。如上圖3的第一個div.gt_cut_fullbg_slice標籤,它的大小為10px * 58px,其中style屬性為background-image: url("http://static.geetest.com/pictures/gt/969ffa43c/969ffa43c.webp"); background-position: -157px -58px;會把該屬性對應url的圖片進行一個平移操作,以左上角為參考,向左平移157px,向上平移58px,圖片超出部分不會顯示。所以上圖1所示圖片是由26 * 2個10px * 58px大小的div組成(如下圖4)。每一個小方塊的大小58 * 10

  • 下載圖片並還原,上一步驟分析了圖片具體的混淆邏輯,具體還原圖片的代碼實現如下,主要邏輯是把原圖裁剪為52張小圖,然後拼接成一張完整的圖。

/**
*還原圖片
* @param type
*/
private static void restoreImage(String type) throws IOException {
//把圖片裁剪為2 * 26份
for(int i = 0; i &< 52; i++){ cutPic(basePath + type +".jpg" ,basePath + "result/" + type + i + ".jpg", -moveArray[i][0], -moveArray[i][1], 10, 58); } //拼接圖片 String[] b = new String[26]; for(int i = 0; i &< 26; i++){ b[i] = String.format(basePath + "result/" + type + "%d.jpg", i); } mergeImage(b, 1, basePath + "result/" + type + "result1.jpg"); //拼接圖片 String[] c = new String[26]; for(int i = 0; i &< 26; i++){ c[i] = String.format(basePath + "result/" + type + "%d.jpg", i + 26); } mergeImage(c, 1, basePath + "result/" + type + "result2.jpg"); mergeImage(new String[]{basePath + "result/" + type + "result1.jpg", basePath + "result/" + type + "result2.jpg"}, 2, basePath + "result/" + type + "result3.jpg"); //刪除產生的中間圖片 for(int i = 0; i &< 52; i++){ new File(basePath + "result/" + type + i + ".jpg").deleteOnExit(); } new File(basePath + "result/" + type + "result1.jpg").deleteOnExit(); new File(basePath + "result/" + type + "result2.jpg").deleteOnExit();

  • 模擬滑鼠移動事件,這一步驟是最關鍵的步驟,極驗驗證碼後台正是通過移動滑塊的軌跡來判斷是否為機器所為。整個移動軌跡的過程越隨機越好,我這裡提供一種成功率較高的移動演算法,代碼如下。

BufferedImage fullBI = ImageIO.read(new File(basePath + "result/" + FULL_IMAGE_NAME + "result3.jpg"));
BufferedImage bgBI = ImageIO.read(new File(basePath + "result/" + BG_IMAGE_NAME + "result3.jpg"));
for (int i = 0; i &< bgBI.getWidth(); i++){ for (int j = 0; j &< bgBI.getHeight(); j++) { int[] fullRgb = new int[3]; fullRgb[0] = (fullBI.getRGB(i, j) 0xff0000) &>&> 16;
fullRgb[1] = (fullBI.getRGB(i, j) 0xff00) &>&> 8;
fullRgb[2] = (fullBI.getRGB(i, j) 0xff);

int[] bgRgb = new int[3];
bgRgb[0] = (bgBI.getRGB(i, j) 0xff0000) &>&> 16;
bgRgb[1] = (bgBI.getRGB(i, j) 0xff00) &>&> 8;
bgRgb[2] = (bgBI.getRGB(i, j) 0xff);
if(difference(fullRgb, bgRgb) &> 255){
return i;
}
}
}

完整代碼如下

package com.github.wycm;

import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.By;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GeettestCrawler {
private static String basePath = "src/main/resources/";
private static String FULL_IMAGE_NAME = "full-image";
private static String BG_IMAGE_NAME = "bg-image";
private static int[][] moveArray = new int[52][2];
private static boolean moveArrayInit = false;
private static String INDEX_URL = "https://passport.feng.com/?r=user/register";
private static WebDriver driver;

static {
System.setProperty("webdriver.chrome.driver", "D:/dev/selenium/chromedriver_V2.30/chromedriver_win32/chromedriver.exe");
if (!System.getProperty("os.name").toLowerCase().contains("windows")){
System.setProperty("webdriver.chrome.driver", "/Users/wangyang/workspace/selenium/chromedriver_V2.30/chromedriver");
}
driver = new ChromeDriver();
}

public static void main(String[] args) throws InterruptedException {
for (int i = 0; i &< 10; i++){ try { invoke(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } driver.quit(); } private static void invoke() throws IOException, InterruptedException { //設置input參數 driver.get(INDEX_URL); //通過[class=gt_slider_knob gt_show] By moveBtn = By.cssSelector(".gt_slider_knob.gt_show"); waitForLoad(driver, moveBtn); WebElement moveElemet = driver.findElement(moveBtn); int i = 0; while (i++ &< 15){ int distance = getMoveDistance(driver); move(driver, moveElemet, distance - 6); By gtTypeBy = By.cssSelector(".gt_info_type"); By gtInfoBy = By.cssSelector(".gt_info_content"); waitForLoad(driver, gtTypeBy); waitForLoad(driver, gtInfoBy); String gtType = driver.findElement(gtTypeBy).getText(); String gtInfo = driver.findElement(gtInfoBy).getText(); System.out.println(gtType + "---" + gtInfo); /** * 再來一次: * 驗證失敗: */ if(!gtType.equals("再來一次:") !gtType.equals("驗證失敗:")){ Thread.sleep(4000); System.out.println(driver); break; } Thread.sleep(4000); } } /** * 移動 * @param driver * @param element * @param distance * @throws InterruptedException */ public static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException { int xDis = distance + 11; System.out.println("應平移距離:" + xDis); int moveX = new Random().nextInt(8) - 5; int moveY = 1; Actions actions = new Actions(driver); new Actions(driver).clickAndHold(element).perform(); Thread.sleep(200); printLocation(element); actions.moveToElement(element, moveX, moveY).perform(); System.out.println(moveX + "--" + moveY); printLocation(element); for (int i = 0; i &< 22; i++){ int s = 10; if (i % 2 == 0){ s = -10; } actions.moveToElement(element, s, 1).perform(); // printLocation(element); Thread.sleep(new Random().nextInt(100) + 150); } System.out.println(xDis + "--" + 1); actions.moveByOffset(xDis, 1).perform(); printLocation(element); Thread.sleep(200); actions.release(element).perform(); } private static void printLocation(WebElement element){ Point point = element.getLocation(); System.out.println(point.toString()); } /** * 等待元素載入,10s超時 * @param driver * @param by */ public static void waitForLoad(final WebDriver driver, final By by){ new WebDriverWait(driver, 10).until(new ExpectedCondition&() {
public Boolean apply(WebDriver d) {
WebElement element = driver.findElement(by);
if (element != null){
return true;
}
return false;
}
});
}

/**
* 計算需要平移的距離
* @param driver
* @return
* @throws IOException
*/
public static int getMoveDistance(WebDriver driver) throws IOException {
String pageSource = driver.getPageSource();
String fullImageUrl = getFullImageUrl(pageSource);
FileUtils.copyURLToFile(new URL(fullImageUrl), new File(basePath + FULL_IMAGE_NAME + ".jpg"));
String getBgImageUrl = getBgImageUrl(pageSource);
FileUtils.copyURLToFile(new URL(getBgImageUrl), new File(basePath + BG_IMAGE_NAME + ".jpg"));
initMoveArray(driver);
restoreImage(FULL_IMAGE_NAME);
restoreImage(BG_IMAGE_NAME);
BufferedImage fullBI = ImageIO.read(new File(basePath + "result/" + FULL_IMAGE_NAME + "result3.jpg"));
BufferedImage bgBI = ImageIO.read(new File(basePath + "result/" + BG_IMAGE_NAME + "result3.jpg"));
for (int i = 0; i &< bgBI.getWidth(); i++){ for (int j = 0; j &< bgBI.getHeight(); j++) { int[] fullRgb = new int[3]; fullRgb[0] = (fullBI.getRGB(i, j) 0xff0000) &>&> 16;
fullRgb[1] = (fullBI.getRGB(i, j) 0xff00) &>&> 8;
fullRgb[2] = (fullBI.getRGB(i, j) 0xff);

int[] bgRgb = new int[3];
bgRgb[0] = (bgBI.getRGB(i, j) 0xff0000) &>&> 16;
bgRgb[1] = (bgBI.getRGB(i, j) 0xff00) &>&> 8;
bgRgb[2] = (bgBI.getRGB(i, j) 0xff);
if(difference(fullRgb, bgRgb) &> 255){
return i;
}
}
}
throw new RuntimeException("未找到需要平移的位置");
}
private static int difference(int[] a, int[] b){
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]) + Math.abs(a[2] - b[2]);
}
/**
* 獲取move數組
* @param driver
*/
private static void initMoveArray(WebDriver driver){
if (moveArrayInit){
return;
}
Document document = Jsoup.parse(driver.getPageSource());
Elements elements = document.select("[class=gt_cut_bg gt_show]").first().children();
int i = 0;
for(Element element : elements){
Pattern pattern = Pattern.compile(".*background-position: (.*?)px (.*?)px.*");
Matcher matcher = pattern.matcher(element.toString());
if (matcher.find()){
String width = matcher.group(1);
String height = matcher.group(2);
moveArray[i][0] = Integer.parseInt(width);
moveArray[i++][1] = Integer.parseInt(height);
} else {
throw new RuntimeException("解析異常");
}
}
moveArrayInit = true;
}
/**
*還原圖片
* @param type
*/
private static void restoreImage(String type) throws IOException {
//把圖片裁剪為2 * 26份
for(int i = 0; i &< 52; i++){ cutPic(basePath + type +".jpg" ,basePath + "result/" + type + i + ".jpg", -moveArray[i][0], -moveArray[i][1], 10, 58); } //拼接圖片 String[] b = new String[26]; for(int i = 0; i &< 26; i++){ b[i] = String.format(basePath + "result/" + type + "%d.jpg", i); } mergeImage(b, 1, basePath + "result/" + type + "result1.jpg"); //拼接圖片 String[] c = new String[26]; for(int i = 0; i &< 26; i++){ c[i] = String.format(basePath + "result/" + type + "%d.jpg", i + 26); } mergeImage(c, 1, basePath + "result/" + type + "result2.jpg"); mergeImage(new String[]{basePath + "result/" + type + "result1.jpg", basePath + "result/" + type + "result2.jpg"}, 2, basePath + "result/" + type + "result3.jpg"); //刪除產生的中間圖片 for(int i = 0; i &< 52; i++){ new File(basePath + "result/" + type + i + ".jpg").deleteOnExit(); } new File(basePath + "result/" + type + "result1.jpg").deleteOnExit(); new File(basePath + "result/" + type + "result2.jpg").deleteOnExit(); } /** * 獲取原始圖url * @param pageSource * @return */ private static String getFullImageUrl(String pageSource){ String url = null; Document document = Jsoup.parse(pageSource); String style = document.select("[class=gt_cut_fullbg_slice]").first().attr("style"); Pattern pattern = Pattern.compile("url\("(.*)"\)"); Matcher matcher = pattern.matcher(style); if (matcher.find()){ url = matcher.group(1); } url = url.replace(".webp", ".jpg"); System.out.println(url); return url; } /** * 獲取帶背景的url * @param pageSource * @return */ private static String getBgImageUrl(String pageSource){ String url = null; Document document = Jsoup.parse(pageSource); String style = document.select(".gt_cut_bg_slice").first().attr("style"); Pattern pattern = Pattern.compile("url\("(.*)"\)"); Matcher matcher = pattern.matcher(style); if (matcher.find()){ url = matcher.group(1); } url = url.replace(".webp", ".jpg"); System.out.println(url); return url; } public static boolean cutPic(String srcFile, String outFile, int x, int y, int width, int height) { FileInputStream is = null; ImageInputStream iis = null; try { if (!new File(srcFile).exists()) { return false; } is = new FileInputStream(srcFile); String ext = srcFile.substring(srcFile.lastIndexOf(".") + 1); Iterator& it = ImageIO.getImageReadersByFormatName(ext);
ImageReader reader = it.next();
iis = ImageIO.createImageInputStream(is);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Rectangle rect = new Rectangle(x, y, width, height);
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0, param);
File tempOutFile = new File(outFile);
if (!tempOutFile.exists()) {
tempOutFile.mkdirs();
}
ImageIO.write(bi, ext, new File(outFile));
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (is != null) {
is.close();
}
if (iis != null) {
iis.close();
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
/**
* 圖片拼接 (注意:必須兩張圖片長寬一致哦)
* @param files 要拼接的文件列表
* @param type 1橫向拼接,2 縱向拼接
* @param targetFile 輸出文件
*/
private static void mergeImage(String[] files, int type, String targetFile) {
int length = files.length;
File[] src = new File[length];
BufferedImage[] images = new BufferedImage[length];
int[][] ImageArrays = new int[length][];
for (int i = 0; i &< length; i++) { try { src[i] = new File(files[i]); images[i] = ImageIO.read(src[i]); } catch (Exception e) { throw new RuntimeException(e); } int width = images[i].getWidth(); int height = images[i].getHeight(); ImageArrays[i] = new int[width * height]; ImageArrays[i] = images[i].getRGB(0, 0, width, height, ImageArrays[i], 0, width); } int newHeight = 0; int newWidth = 0; for (int i = 0; i &< images.length; i++) { // 橫向 if (type == 1) { newHeight = newHeight &> images[i].getHeight() ? newHeight : images[i].getHeight();
newWidth += images[i].getWidth();
} else if (type == 2) {// 縱向
newWidth = newWidth &> images[i].getWidth() ? newWidth : images[i].getWidth();
newHeight += images[i].getHeight();
}
}
if (type == 1 newWidth &< 1) { return; } if (type == 2 newHeight &< 1) { return; } // 生成新圖片 try { BufferedImage ImageNew = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); int height_i = 0; int width_i = 0; for (int i = 0; i &< images.length; i++) { if (type == 1) { ImageNew.setRGB(width_i, 0, images[i].getWidth(), newHeight, ImageArrays[i], 0, images[i].getWidth()); width_i += images[i].getWidth(); } else if (type == 2) { ImageNew.setRGB(0, height_i, newWidth, images[i].getHeight(), ImageArrays[i], 0, newWidth); height_i += images[i].getHeight(); } } //輸出想要的圖片 ImageIO.write(ImageNew, targetFile.split("\.")[1], new File(targetFile)); } catch (Exception e) { throw new RuntimeException(e); } } }

  • pom文件依賴如下

& &org.seleniumhq.selenium& &selenium-server& &3.0.1& & & & &org.jsoup& &jsoup& &1.7.2& &

最後

  1. 完整代碼已上傳至github,地址:https://github.com/wycm/selenium-geetest-crack
  2. 附上一張滑動效果圖


開源參考:GitHub - wsguest/geetest: Crack geetest verify code in C#


沒有什麼網路驗證能徹底防破解……


我司正在用極驗驗證碼。不討論任何技術方面的問題,僅僅說效果

從效果來看只能說是一般,仍需要慢慢迭代,而且也不排除人肉打碼的情況存在,所以極驗的路還很長,未來的趨勢肯定是對用戶越來越輕,對機器越來越難,所以樓上說easy的,很難說是不是在安全領域有過研究,軌跡肯定是一種維度罷了,全依賴於軌跡判斷肯定不靠譜,極驗肯定使用了很多種維度判斷,因為模擬一段隨機的軌跡很容易。

極驗整整一個公司在做安全驗證,樓上幾個匿名用戶也就能吹吹牛逼,黑產也不是那麼簡單的,希望極驗能越走越遠,雖然說沒有達到我司預想的安全驗證的目標,但是還是挺相信這種獨出心裁的驗證碼能成長起來。


近來也是工作需要, 在研究滑動驗證碼這塊,而比較知名的滑動驗證碼據我了解的只有極驗和天御。那麼我就先選擇了極驗。

今天呢大概花了一天時間搭建一個簡單的運行環境,模擬crack它。整的過程還是挺有意思的,比如selenium這個軟體就很有趣。

結果見錄製的短視頻。

可能有些朋友看完想知道些細節,我在這裡簡單說下。

先說運行環境:java+springboot+springmvc+selenium+geetest

1、使用selenium模擬操作瀏覽器

2、crack的過程就五步:

(1)模擬點擊動作 觸發滑動驗證碼圖片出現

(2)此時的圖片並沒有偏移塊 將該圖片保存下來

(3)模擬稍微移動一下滑動按鈕 觸發偏移塊的出現

(4)將帶有偏移塊的圖片保存下來 並比較兩張圖片的像素差異

(5)獲取水平偏移 模擬移動即可 但可惜最後顯示圖片被怪物吃了 估計是速度太快導致。有沒有朋友能告知selenium在移動的過程中如何控制速度呀。Google了一番 沒找到好的解決辦法,明天再看。

這裡有幾個注意事項:

某些操作需要等待1-2秒 注意控制


再洗地也已經是破解爛大街。。。我就隨手百度一下貼個鏈接不說話。。。

極驗驗證碼識別,極驗識別平台

極驗驗證碼自動識別介面(極驗驗證geetest滑動驗證碼破 解)

wsguest/geetest

再附上高票的原回答:

http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763105392230e54f72b668c8a5268d4e419ce3b46020031a2fd7c7f4c19d3cf766600a4435bfeb56b32610c37c7eadffb3acabae23f598b30437b0b873105a51ab8bc4d32b7248729efb81897ad813284d9a2c4de2444bb20120a84e7fb591760c8

就煩你們這樣在知乎上還軟文還刷評論還洗地的(傲嬌臉)

來吧留言吧那幾位


關鍵是極驗的那段移動軌跡,極驗有太多的用戶數據做支撐了,目前移動那段演算法沒有仔細研究,導致程序每次都是geetest_1486533987181({"success": 0,"message":"forbidden"});其餘的不難,只是js分析頭疼而已。


推薦閱讀:

scalers是誰?
rss在中國是否足夠普及?有多少人會使用rss功能?rss是否是現在網站一個必不可少的功能?
明明知道獎學金不能代表什麼,還在爭取一等、國獎的計算機專業學生,是不是被應試教育徹底毒害了?
懂fpga如何進互聯網公司?
為什麼很多人看今日頭條會看上癮?

TAG:互聯網 | 破解 | 計算機 | 驗證碼 |