최근 인터넷에 인증코드를 해독할 수 있는 JavaScript 스크립트가 등장했습니다. GreaseMonkey! "Shaun Friedle"이 개발한 이 스크립트는 Megaupload 사이트의 CAPTCHA를 쉽게 해결할 수 있습니다. 믿을 수 없다면 http://herecomethelizards.co.uk/mu_captcha/에 가셔서 직접 해보세요!
이제 Megaupload 사이트에서 제공하는 CAPTCHA는 위의 내용으로 패배했습니다. 솔직히 말해서 여기의 인증 코드는 잘 설계되지 않았습니다. 하지만 더 흥미로운 점은 다음과 같습니다.
1. HTML 5의 Canvas 애플리케이션 인터페이스 getImageData를 사용하여 인증 코드 이미지에서 픽셀 데이터를 얻을 수 있습니다. Canvas를 사용하면 이미지를 캔버스에 삽입할 수 있을 뿐만 아니라 나중에 이미지를 다시 추출할 수도 있습니다.
2. 위 스크립트에는 전적으로 JavaScript로 구현된 신경망이 포함되어 있습니다.
3. Canvas를 이용해 이미지에서 픽셀 데이터를 추출한 뒤 신경망으로 전송하고, 간단한 광학 문자 인식 기술을 이용해 인증코드에 어떤 문자가 사용됐는지 추론한다.
소스 코드를 읽으면 작동 원리를 더 잘 이해할 수 있을 뿐만 아니라 이 인증 코드가 어떻게 구현되는지 이해할 수 있습니다. 앞에서 본 것처럼 여기에 사용된 CAPTCHA는 그다지 복잡하지 않습니다. 각 CAPTCHA는 3개의 문자로 구성되며, 각 문자는 서로 다른 색상을 사용하고 26자 알파벳의 문자만 사용하지만 모든 문자는 모두 동일한 글꼴을 사용합니다.
첫 번째 단계의 목적은 분명합니다. 즉, 인증 코드를 캔버스에 복사하고 이를 회색조 이미지로 변환하는 것입니다.
함수 변환_회색(이미지_데이터){
for (var x = 0; x < image_data.width; x++){
for (var y = 0; y < image_data.height; y++ ){
var i = x*4+y*4*image_data.width;
var luma = Math.floor(image_data.data[i] * 299/1000 +
image_data.data[i+ 1 ] * 587/1000 +
Image_data.data[i+2] * 114/1000)
image_data.data[i] = luma;
image_data.data[i+1] = luma; 🎜> image_data.data[i+2] = luma;
image_data.data[i+3] = 255;
는 캔버스를 각각 하나의 문자를 포함하는 세 개의 별도 픽셀 행렬로 나눕니다. 이 단계는 각 캐릭터가 별도의 색상을 사용하므로 색상별로 구분이 가능하기 때문에 매우 쉽게 달성할 수 있습니다.
필터(이미지_데이터[0], 105);
필터(이미지_데이터[1], 120)
함수 필터(이미지_데이터, color){
for (var x = 0; x < image_data.width; *4+y*4*image_data.width;
if (image_data.data[i] == 색상) {
image_data.data[i ] = 255;
image_data.data[i+1] = 255
image_data.data[i+2] = 255;
// 나머지는 모두 검정색으로
} else { image_data.data[i] = 0
image_data.data[i+1] = 0; ] = 0; 🎜> }
마지막으로 관련 없는 간섭 픽셀이 모두 제거됩니다. 이렇게 하려면 먼저 앞이나 뒤의 검은색(일치하지 않는) 픽셀로 둘러싸인 흰색(일치하는) 픽셀을 찾은 다음 일치하는 픽셀을 삭제하면 됩니다.
var i = x*4+y*4*image_data.width;
var 위 = x*4+(y-1)*4*image_data.width
var 아래 = x *4+(y+1)*4*image_data.width;
if (image_data.data[i] == 255 &&
image_data.data[위] == 0 &&
image_data.data[ 아래] == 0) {
image_data.data[i] = 0;
image_data.data[i+1] = 0
image_data.data[i+2] = 0; }
이제 캐릭터의 대략적인 모양이 완성되었지만 이를 신경망에 로드하기 전에 스크립트는 캐릭터에 대해 필요한 가장자리 감지를 추가로 수행합니다. 스크립트는 그래픽의 가장 왼쪽, 오른쪽, 위쪽 및 아래쪽 픽셀을 찾아 직사각형으로 변환한 다음 직사각형을 20*25 픽셀 매트릭스로 다시 변환합니다.
var edge = find_edges(image_data[i])
Cropped_canvas.getContext("2d" ).drawImage(canvas, edge[0], edge[1],
edge[2]-edges[0], edge[3]-edges[1], 0, 0,
edge[2] -edges[0], edges[3]-edges[1]);
image_data[i] = Cropped_canvas.getContext("2d").getImageData(0, 0,
Cropped_canvas.width, Cropped_canvas.height );
위의 처리 후에 우리는 무엇을 얻나요? 흑백으로 채워진 단일 직사각형을 포함하는 20*25 행렬입니다. 훌륭해요!
그러면 이 직사각형이 더욱 단순화됩니다. 우리는 신경망에 공급될 "광수용체"로서 매트릭스에서 점을 전략적으로 추출합니다. 예를 들어, 어떤 광수용체는 픽셀이 있거나 없는 9*6에 위치한 픽셀에 해당할 수 있습니다. 스크립트는 이러한 일련의 상태(전체 20x25 행렬 계산보다 훨씬 적음 - 64개 상태만 추출됨)를 추출하고 이러한 상태를 신경망에 공급합니다.
픽셀을 직접 비교하면 어떨까? 신경망을 사용해야 하는가? 문제의 핵심은 이러한 모호한 상황을 제거해야 한다는 것입니다. 이전 데모를 시도해 본 적이 있다면 픽셀을 직접 비교하는 것이 신경망을 통해 비교하는 것보다 오류가 더 많이 발생한다는 것을 알 수 있습니다. 그러나 대부분의 사용자에게는 직접적인 픽셀 비교만으로 충분하다는 점을 인정해야 합니다.
다음 단계는 글자를 추측하는 것입니다. 64개의 부울 값(문자 이미지 중 하나에서 얻은)과 사전 계산된 일련의 데이터를 신경망으로 가져옵니다. 신경망의 개념 중 하나는 우리가 얻고자 하는 결과가 미리 알려져 있으므로 결과에 따라 그에 따라 신경망을 훈련할 수 있다는 것입니다. 스크립트 작성자는 스크립트를 여러 번 실행하고 일련의 최고 점수를 수집할 수 있으며, 이는 신경망이 이를 생성한 값에서 거꾸로 작업하여 답을 추측하는 데 도움이 될 수 있지만 이러한 점수는 특별한 의미가 없습니다.
신경망이 인증코드의 한 문자에 해당하는 64개의 부울 값을 계산하면 미리 계산된 알파벳과 비교한 후 각 문자와 일치하는 점수를 부여합니다. (최종 결과는 유사할 수 있습니다. 98%는 A, 36%는 B일 수 있습니다.)
인증 코드 세 글자가 모두 처리되면 최종 결과가 나옵니다. 밖으로. 이 스크립트가 100% 정확하지는 않다는 점에 유의해야 합니다(처음에 문자를 직사각형으로 변환하지 않으면 채점의 정확도가 향상될 수 있는지 궁금합니다). 그러나 적어도 현재 목적으로는 꽤 좋습니다. 그렇게 말해보세요. 그리고 모든 작업은 표준 클라이언트 기술을 기반으로 브라우저에서 완료됩니다.
이 스크립트는 다른 간단한 인증 코드에서는 잘 작동할 수 있지만, 복잡한 경우에는 이 스크립트가 작동할 수 있다는 점을 덧붙이고 싶습니다. 인증 코드는 도달 범위를 조금 넘어서는 것입니다(특히 이러한 종류의 클라이언트 기반 분석). 이 프로젝트를 통해 더 많은 사람들이 영감을 받고 더 멋진 것들을 개발할 수 있기를 바랍니다. 잠재력이 너무 크기 때문입니다