← 返回文章列表

深度解析:【验证码识别】OpenCV挑战网易易盾滑动验证码

使用java+selenium+OpenCV破解网易易盾滑动验证码戳这里→康康你手机号在过多少网站注册过!!!友情推荐:新一代安全短信谷歌图形验证码在AI面前已经形同虚设,所以谷歌宣布退出验证码服务,那么当所有的图形验证码都被破解时《腾讯防

使用java + selenium + OpenCV破解网易易盾滑动验证码

戳这里→

康康你手机号在过多少网站注册过!!!

友情推荐:

新一代安全短信

谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时

《腾讯防水墙滑动拼图验证码》

《百度旋转图片验证码》

《网易易盾滑动拼图验证码》

《顶象区域面积点选验证码》

《顶象滑动拼图验证码》

《极验滑动拼图验证码》

《使用深度学习来破解 captcha 验证码》

《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》

* 验证码地址:https://dun.163.com/trial/jigsaw

* 使用OpenCv模板匹配

* Java + Selenium + OpenCV

产品样例

接下来就是见证奇迹的时刻!

注意!!!

·

在模拟滑动时不能按照相同速度或者过快的速度滑动,需要向人滑动时一样先快后慢,这样才不容易被识别。

模拟滑动代码↓↓↓

/**

* 模拟人工移动

* @param driver

* @param element页面滑块

* @param distance需要移动距离

*/

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

(

)

;

}

}

/**

* 根据距离获取滑动轨迹

* @param distance需要移动的距离

* @return

*/

public

static

List

<

Integer

>

getMoveTrack

(

int

distance

)

{

List

<

Integer

>

track

=

new

ArrayList

<

>

(

)

;

// 移动轨迹

Random random

=

new

Random

(

)

;

int

current

=

0

;

// 已经移动的距离

int

mid

=

(

int

)

distance

*

4

/

5

;

// 减速阈值

int

a

=

0

;

int

move

=

0

;

// 每次循环移动的距离

while

(

true

)

{

a

=

random

.

nextInt

(

10

)

;

if

(

current

<=

mid

)

{

move

+=

a

;

// 不断加速

}

else

{

move

-=

a

;

}

if

(

(

current

+

move

)

<

distance

)

{

track

.

add

(

move

)

;

}

else

{

track

.

add

(

distance

-

current

)

;

break

;

}

current

+=

move

;

}

return

track

;

}

操作过程

/**

* 获取网易验证滑动距离

*

* @return

*/

public

static

String dllPath

=

"C://chrome//opencv_java440.dll"

;

public

double

getDistance

(

String bUrl

,

String sUrl

)

{

System

.

load

(

dllPath

)

;

File bFile

=

new

File

(

"C:/EasyDun_b.png"

)

;

File sFile

=

new

File

(

"C:/EasyDun_s.png"

)

;

try

{

FileUtils

.

copyURLToFile

(

new

URL

(

bUrl

)

,

bFile

)

;

FileUtils

.

copyURLToFile

(

new

URL

(

sUrl

)

,

sFile

)

;

BufferedImage bgBI

=

ImageIO

.

read

(

bFile

)

;

BufferedImage sBI

=

ImageIO

.

read

(

sFile

)

;

// 裁剪

cropImage

(

bgBI

,

sBI

,

bFile

,

sFile

)

;

Mat s_mat

=

Imgcodecs

.

imread

(

sFile

.

getPath

(

)

)

;

Mat b_mat

=

Imgcodecs

.

imread

(

bFile

.

getPath

(

)

)

;

//阴影部分为黑底时需要转灰度和二值化,为白底时不需要

// 转灰度图像

Mat s_newMat

=

new

Mat

(

)

;

Imgproc

.

cvtColor

(

s_mat

,

s_newMat

,

Imgproc

.

COLOR_BGR2GRAY

)

;

// 二值化图像

binaryzation

(

s_newMat

)

;

Imgcodecs

.

imwrite

(

sFile

.

getPath

(

)

,

s_newMat

)

;

int

result_rows

=

b_mat

.

rows

(

)

-

s_mat

.

rows

(

)

+

1

;

int

result_cols

=

b_mat

.

cols

(

)

-

s_mat

.

cols

(

)

+

1

;

Mat g_result

=

new

Mat

(

result_rows

,

result_cols

,

CvType

.

CV_32FC1

)

;

Imgproc

.

matchTemplate

(

b_mat

,

s_mat

,

g_result

,

Imgproc

.

TM_SQDIFF

)

;

// 归一化平方差匹配法TM_SQDIFF 相关系数匹配法TM_CCOEFF

Core

.

normalize

(

g_result

,

g_result

,

0

,

1

,

Core

.

NORM_MINMAX

,

-

1

,

new

Mat

(

)

)

;

Point matchLocation

=

new

Point

(

)

;

MinMaxLocResult mmlr

=

Core

.

minMaxLoc

(

g_result

)

;

matchLocation

=

mmlr

.

maxLoc

;

// 此处使用maxLoc还是minLoc取决于使用的匹配算法

Imgproc

.

rectangle

(

b_mat

,

matchLocation

,

new

Point

(

matchLocation

.

x

+

s_mat

.

cols

(

)

,

matchLocation

.

y

+

s_mat

.

rows

(

)

)

,

new

Scalar

(

0

,

255

,

0

,

0

)

)

;

Imgcodecs

.

imwrite

(

bFile

.

getPath

(

)

,

b_mat

)

;

return

matchLocation

.

x

+

s_mat

.

cols

(

)

-

sBI

.

getWidth

(

)

+

12

;

}

catch

(

Throwable

e

)

{

e

.

printStackTrace

(

)

;

return

0

;

}

finally

{

bFile

.

delete

(

)

;

sFile

.

delete

(

)

;

}

}

/**

* 图片亮度调整

*

* @param image

* @param param

* @throws IOException

*/

public

void

bloding

(

BufferedImage image

,

int

param

)

throws

IOException

{

if

(

image

==

null

)

{

return

;

}

else

{

int

rgb

,

R

,

G

,

B

;

for

(

int

i

=

0

;

i

<

image

.

getWidth

(

)

;

i

++

)

{

for

(

int

j

=

0

;

j

<

image

.

getHeight

(

)

;

j

++

)

{

rgb

=

image

.

getRGB

(

i

,

j

)

;

R

=

(

(

rgb

>>

16

)

&

0xff

)

-

param

;

G

=

(

(

rgb

>>

8

)

&

0xff

)

-

param

;

B

=

(

rgb

&

0xff

)

-

param

;

rgb

=

(

(

clamp

(

255

)

&

0xff

)

<<

24

)

|

(

(

clamp

(

R

)

&

0xff

)

<<

16

)

|

(

(

clamp

(

G

)

&

0xff

)

<<

8

)

|

(

(

clamp

(

B

)

&

0xff

)

)

;

image

.

setRGB

(

i

,

j

,

rgb

)

;

}

}

}

}

// 判断a,r,g,b值,大于256返回256,小于0则返回0,0到256之间则直接返回原始值

private

int

clamp

(

int

rgb

)

{

if

(

rgb

>

255

)

return

255

;

if

(

rgb

<

0

)

return

0

;

return

rgb

;

}

/**

* 生成半透明小图并裁剪

*

* @param image

* @return

*/

private

void

cropImage

(

BufferedImage bigImage

,

BufferedImage smallImage

,

File bigFile

,

File smallFile

)

{

int

y

=

0

;

int

h_

=

0

;

try

{

// 2 生成半透明图片

bloding

(

bigImage

,

75

)

;

for

(

int

w

=

0

;

w

<

smallImage

.

getWidth

(

)

;

w

++

)

{

for

(

int

h

=

smallImage

.

getHeight

(

)

-

2

;

h

>=

0

;

h

--

)

{

int

rgb

=

smallImage

.

getRGB

(

w

,

h

)

;

int

A

=

(

rgb

&

0xFF000000

)

>>>

24

;

if

(

A

>=

100

)

{

rgb

=

(

127

<<

24

)

|

(

rgb

&

0x00ffffff

)

;

smallImage

.

setRGB

(

w

,

h

,

rgb

)

;

}

}

}

for

(

int

h

=

1

;

h

<

smallImage

.

getHeight

(

)

;

h

++

)

{

for

(

int

w

=

1

;

w

<

smallImage

.

getWidth

(

)

;

w

++

)

{

int

rgb

=

smallImage

.

getRGB

(

w

,

h

)

;

int

A

=

(

rgb

&

0xFF000000

)

>>>

24

;

if

(

A

>

0

)

{

if

(

y

==

0

)

y

=

h

;

h_

=

h

-

y

;

break

;

}

}

}

smallImage

=

smallImage

.

getSubimage

(

0

,

y

,

smallImage

.

getWidth

(

)

,

h_

)

;

bigImage

=

bigImage

.

getSubimage

(

0

,

y

,

bigImage

.

getWidth

(

)

,

h_

)

;

ImageIO

.

write

(

bigImage

,

"png"

,

bigFile

)

;

ImageIO

.

write

(

smallImage

,

"png"

,

smallFile

)

;

}

catch

(

Throwable

e

)

{

System

.

out

.

println

(

e

.

toString

(

)

)

;

}

}

/**

*

* @param mat

* 二值化图像

*/

public

static

void

binaryzation

(

Mat mat

)

{

int

BLACK

=

0

;

int

WHITE

=

255

;

int

ucThre

=

0

,

ucThre_new

=

127

;

int

nBack_count

,

nData_count

;

int

nBack_sum

,

nData_sum

;

int

nValue

;

int

i

,

j

;

int

width

=

mat

.

width

(

)

,

height

=

mat

.

height

(

)

;

// 寻找最佳的阙值

while

(

ucThre

!=

ucThre_new

)

{

nBack_sum

=

nData_sum

=

0

;

nBack_count

=

nData_count

=

0

;

for

(

j

=

0

;

j

<

height

;

++

j

)

{

for

(

i

=

0

;

i

<

width

;

i

++

)

{

nValue

=

(

int

)

mat

.

get

(

j

,

i

)

[

0

]

;

if

(

nValue

>

ucThre_new

)

{

nBack_sum

+=

nValue

;

nBack_count

++

;

}

else

{

nData_sum

+=

nValue

;

nData_count

++

;

}

}

}

nBack_sum

=

nBack_sum

/

nBack_count

;

nData_sum

=

nData_sum

/

nData_count

;

ucThre

=

ucThre_new

;

ucThre_new

=

(

nBack_sum

+

nData_sum

)

/

2

;

}

// 二值化处理

int

nBlack

=

0

;

int

nWhite

=

0

;

for

(

j

=

0

;

j

<

height

;

++

j

)

{

for

(

i

=

0

;

i

<

width

;

++

i

)

{

nValue

=

(

int

)

mat

.

get

(

j

,

i

)

[

0

]

;

if

(

nValue

>

ucThre_new

)

{

mat

.

put

(

j

,

i

,

WHITE

)

;

nWhite

++

;

}

else

{

mat

.

put

(

j

,

i

,

BLACK

)

;

nBlack

++

;

}

}

}

// 确保白底黑字

if

(

nBlack

>

nWhite

)

{

for

(

j

=

0

;

j

<

height

;

++

j

)

{

for

(

i

=

0

;

i

<

width

;

++

i

)

{

nValue

=

(

int

)

(

mat

.

get

(

j

,

i

)

[

0

]

)

;

if

(

nValue

==

0

)

{

mat

.

put

(

j

,

i

,

WHITE

)

;

}

else

{

mat

.

put

(

j

,

i

,

BLACK

)

;

}

}

}

}

}

// 延时加载

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

;

}

注意:有一个问题还没有解决,还无法区分阴影部分是黑色还是白色。 因为两种的情况不同,所以处理方式也不同。阴影部分为黑底时需要转灰度和二值化,为白底时不需要。黑底使用归一化平方差匹配算法 TM_SQDIFF ,而白底使用相关系数匹配算法 TM_CCOEFF。

有找到区分方法的大佬可以私信作者或者在评论区留言哦。

戳这里→

康康你手机号在过多少网站注册过!!!

友情推荐:

新一代安全短信