[verification code identification] OpenCV challenge polar sliding puzzle verification code

preface

Poke here → Kangkang, how many websites have you registered your mobile phone number!!!
Friendly recommendation: New generation secure SMS

This paper introduces in detail all the processes of cracking the pole test sliding verification code, including the cracking idea, implementation steps and test results. I believe you can easily crack the sliding verification code after reading it; #SMS flood control bombing#

I. composition of analytical verification code

From the above three pictures, the polar sliding puzzle verification code is composed of a small puzzle and a large background picture. The shapes of the puzzle are various, and there is a shadow gap in the background picture, which is consistent with the shape of the puzzle.

Here we use the F12 method to open the browser console and observe the page structure of the verification code.

Through observation, we can see that the pictures contained in the verification code are presented on the page in the form of < canves > canvas, and there are three pictures, and the third picture is added with the attribute style = "display: none;", That is, it is hidden and not displayed. So let's modify the page code to see what this picture is.


After modifying the code, I found that this is the complete background picture. According to the above naming, we can basically determine what these three figures are.

  • The first class is geetest_canvas_bg geetest_absolute, which can be determined as the background image with notch.

  • The second class is geetest_canvas_slice geetest_absolute, which can be determined as a puzzle.

  • The third is the complete picture.

II. Analyze the crack ideas

  1. First, analyze what we should do according to the composition of the verification code:

According to the normal manual operation process, we need to see the position of the shadow gap corresponding to the puzzle in the background image, and then press and hold the lower slider to align the puzzle to the gap position to complete the verification.

  1. Then analyze what the program needs to do according to what people want to do:

According to the analysis, the following steps are obtained:
1. Obtain two pictures (with gap background and complete background)
2. Process the picture, get the shadow position and calculate the sliding distance
3. Simulate sliding according to sliding distance

III. specific operation steps

1. Get two pictures

Since the pictures here are rendered through canvas canvas, we can generate pictures by executing js code.
Can refer to How to capture pictures in canvas.

2. Process pictures and calculate the sliding distance

From the two pictures obtained in the first step, we can see that there are two different places in the two pictures, one is small and the other is large. We can determine the position of the shadow gap by comparing the difference of each pixel. The distance between the abscissa of the notch position minus the distance between the small drawing and the frame is the sliding distance.

The following is the key part of the code:

private final String INDEX_URL = "https://www.geetest.com/Register";
	// Lazy loading 
	private static WebElement waitWebElement(WebDriver driver, By by, int count) throws Exception {
		WebElement webElement = null;
		boolean isWait = false;
		for (int k = 0; k < count; k++) {
			try {
				webElement = driver.findElement(by);
				if (isWait)
					System.out.println(" ok!");
				return webElement;
			} catch (org.openqa.selenium.NoSuchElementException ex) {
				isWait = true;
				if (k == 0)
					System.out.print("waitWebElement(" + by.toString() + ")");
				else
					System.out.print(".");
				Thread.sleep(50);
			}
		}
		if (isWait)
			System.out.println(" outTime!");
		return null;
	}
	/**
	 * Calculate the distance to be translated
	 * 
	 * @param driver
	 * @param fullImgPath Full background picture file name
	 * @param bgImgPath File name with gap background picture
	 * @return
	 * @throws IOException
	 */
	public static int getMoveDistance(WebDriver driver, String fullImgPath, String bgImgPath) throws IOException {
		File fullFile = new File(fullImgPath);
		File bgFile = new File(bgImgPath);
		try {
			BufferedImage fullBI = ImageIO.read(fullFile);
			BufferedImage bgBI = ImageIO.read(bgFile);
			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;
					}
				}
			}
		} catch (Exception e) {
			return 0;
		} finally {
			fullFile.delete();
			bgFile.delete();
		}
		return 0;
	}
	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]);
	}
	/**
	 * // Execute JS code and generate pictures
	 * 
	 * @param driver
	 * @param jsString
	 * @param input
	 * @return Picture path
	 */
	public static String getImgByJs(WebDriver driver, String jsString) {
		try {
			String imgFilePath = "c://GeeTest_" + System.currentTimeMillis() + "_" + (Math.random() * 9 + 1) * 100000 + ".jpg";
			String imgInfo = ((JavascriptExecutor) driver).executeScript(jsString).toString();
			if (imgInfo != null && imgInfo.contains("data")) {
				imgInfo = imgInfo.substring(imgInfo.indexOf(",") + 1);
				ByteArrayOutputStream outputStream = imgStrToFile(imgInfo);
				if (outputStream != null) {
					byte[] picBytes = outputStream.toByteArray();
					outPicToFile(picBytes, imgFilePath);
					return imgFilePath;
				}
			}
			return null;
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 * Convert base64 byte code to byte output stream
	 * 
	 * @param imgBase64Str
	 * @return
	 */
	private static ByteArrayOutputStream imgStrToFile(String imgBase64Str) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			if (imgBase64Str != null) {
				BASE64Decoder decoder = new BASE64Decoder();
				byte[] data = decoder.decodeBuffer(imgBase64Str);
				outputStream.write(data);
				outputStream.flush();
			}
			return outputStream;
		} catch (Exception e) {
			return null;
		}
	}
	/**
	 * Picture circulation picture
	 * 
	 * @param o
	 * @param imgFilePath
	 */
	private static void outPicToFile(Object o, String imgFilePath) {
		if (o == null)
			return;
		try {
			if (o instanceof byte[]) { // Convert to picture
				if (((byte[]) o).length == 0)
					return;
				File imgFile = new File(imgFilePath);
				// byte array to picture
				FileImageOutputStream imageOutput = new FileImageOutputStream(imgFile);
				imageOutput.write((byte[]) o, 0, ((byte[]) o).length);
				imageOutput.close();
			} else {
				return;
			}
		} catch (Exception e) {
		}
	}
	/**
	 * Simulated manual movement
	 * 
	 * @param driver
	 * @param element Page slider
	 * @param distance Need to move distance
	 * @throws InterruptedException
	 */
	public static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException {
		int randomTime = 0;
		if (distance > 90) {
			randomTime = 250;
		} else if (distance > 80 && distance <= 90) {
			randomTime = 150;
		}
		List<Integer> track = getMoveTrack(distance - 2);
		int moveY = 1;
		try {
			Actions actions = new Actions(driver);
			actions.clickAndHold(element).perform();
			Thread.sleep(200);
			for (int i = 0; i < track.size(); i++) {
				actions.moveByOffset(track.get(i), moveY).perform();
				Thread.sleep(new Random().nextInt(300) + randomTime);
			}
			Thread.sleep(200);
			actions.release(element).perform();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Obtain the sliding track according to the distance
	 * 
	 * @param distance Distance to move
	 * @return
	 */
	public static List<Integer> getMoveTrack(int distance) {
		List<Integer> track = new ArrayList<>();// Moving track
		Random random = new Random();
		int current = 0;// Distance moved
		int mid = (int) distance * 4 / 5;// Deceleration threshold
		int a = 0;
		int move = 0;// Distance per cycle
		while (true) {
			a = random.nextInt(10);
			if (current <= mid) {
				move += a;// Accelerating
			} else {
				move -= a;
			}
			if ((current + move) < distance) {
				track.add(move);
			} else {
				track.add(distance - current);
				break;
			}
			current += move;
		}
		return track;
	}

	private void seleniumTest() {
		ChromeDriverManager manager = ChromeDriverManager.getInstance();
		int status = -1;
		String phone = "13814389438";
		try {
			WebDriver driver = manager.getDriver();
			driver.get(INDEX_URL);
			driver.manage().window().maximize(); // Set browser window maximization
			Thread.sleep(2000);

			// Enter mobile number
			WebElement phoneElemet = waitWebElement(driver, By.xpath("//input[@placeholder = 'mobile number'] "), 20);
			phoneElemet.clear();
			for (int i = 0; i < phone.length(); i++) {
				char c = phone.charAt(i);
				phoneElemet.sendKeys(c + "");
				phoneElemet.click();
			}
			sleep(50);
			// Click to get the verification code
			waitWebElement(driver, By.className("sendCode"), 20).click();
			sleep(2000);
			// Complete background image geetest_canvas_fullbg geetest_fade geetest_absolute
			String fullImgJs = "return document.getElementsByClassName(\"geetest_canvas_fullbg geetest_fade geetest_absolute\")[0].toDataURL(\"image/png\");";
			String fullImgPath = getImgByJs(driver, fullImgJs);
			// Background image with gap geetest_canvas_bg geetest_absolute
			String bgImgJs = "return document.getElementsByClassName(\"geetest_canvas_bg geetest_absolute\")[0].toDataURL(\"image/png\");";
			String bgImgPath = getImgByJs(driver, bgImgJs);
			// Get slide button
			WebElement moveElemet = waitWebElement(driver, By.className("geetest_slider_button"), 20);
			// Get the sliding distance and delete the picture
			int distance = getMoveDistance(driver, fullImgPath, bgImgPath);
			if (distance == 0) {
			}
			// slide
			move(driver, moveElemet, distance - 6);
			// Sliding result
			sleep(2 * 1000);
			String gtInfo = waitWebElement(driver, By.className("sendCode"), 20).getAttribute("innerHTML");
			System.out.println(gtInfo);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			manager.closeDriver(status);
		}
	}

	protected static void sleep(long time) {
		try {
			Thread.sleep(time);
		} catch (InterruptedException e) {
		}
	}

3. Simulate sliding according to sliding distance

After getting the sliding distance, let's look at the sliding track. If the sliding track is too regular, it is easy to be recognized. Therefore, we can close the sliding track to the normal operation track of human beings.
For example: first fast and then slow, and slowly align with the gap. Shake left and right at the notch. Stay at the gap, appreciate the results, etc.

IV. result display

V. result analysis

Objectives:

Identify the shadow position, calculate the corresponding sliding distance and simulate sliding.

Realization idea:

1. Obtain two pictures (complete picture and gap picture)
2. Process the picture, get the shadow position and calculate the sliding distance
3. Simulate sliding according to sliding distance

Identification time:

15 - 50 ms

Pass rate:

>95%

Vi. conclusion

This article ends here. Thank you for stopping to watch. Pay attention and praise~

Thank you, boss~

Poke here → Kangkang, how many websites have you registered your mobile phone number!!!

Google has announced its withdrawal from the graphic verification code service. Why are all kinds of wonderful verification methods emerging in China? How is the security?
Tencent waterproof wall sliding puzzle verification code
Baidu rotating picture verification code
Netease shield Slide Puzzle verification code
Verification code for point selection of top image area
Top image sliding puzzle verification code
Polar sliding puzzle verification code
Using deep learning to crack captcha verification code

Posted by lentin64 on Mon, 18 Apr 2022 23:07:24 +0930