По просьбам некоторых людей, да и просто для себя, решил написать функции для работы с антикапчей (anti-captcha.com) на ассемблере.
Если кто не знает, anti-captcha – это сервис, позволяющий распознавать капчи за небольшую плату (1$ за 1000 штук). Качество распознавания, как правило, 90%, время ожидания обычно не превышает 20 секунд.
Функции для работы с сервисом сразу решил проектировать таким образом, чтобы они могли нормально без конфликтов работать во много потоков одновременно.
Для этого необходимо использовать локальные переменные для чтения и записи, а глобальные переменные (из секции данных) можно только читать, но не изменять.
Кроме того, при работе с общими переменными или ресурсами необходимо применять какой-нибудь механизм, помогающий избегать конфликтов. Я использовал мьютексы (что это такое, объясню дальше).
Программу для тестирования я сделал таким образом – можно открыть несколько файлов (от 1 до 40) с картинками и одновременно послать их на распознавание (по одной картинке на поток).
Получилось следующее:
Кроме того, я создал dll с процедурами для работы с антикапчей, которую может импортировать любая программа.
Итак, начнем писать программу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
.486 ;32-разрядный код для всех от 486 .model flat, stdcall ;плоская подель памяти, stdcall option casemap :none ;все имена регистрозависимы ;подключаем разные include-файлы include \masm32\include\windows.inc ;некоторые функции и структуры windows include \masm32\include\masm32.inc ;много полезных функций masm32 include \masm32\include\user32.inc ;функции user32.dll include \masm32\include\kernel32.inc ;функции kernel32.dll include \masm32\include\Comctl32.inc ;common controls - всякие контролы для форм include \masm32\include\comdlg32.inc ;диалоги сохранения и открытия файлов include \masm32\include\DIALOGS.INC ;удобные макросы от masm32 для создания диалоговых окон include \masm32\macros\macros.asm ;еще макросы масма include \masm32\include\ws2_32.inc ;работа с сокетами - winsock ;подключаем соответствующие им библиотеки includelib \masm32\lib\masm32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\Comctl32.lib includelib \masm32\lib\comdlg32.lib includelib \masm32\lib\ws2_32.lib DialogProc PROTO :DWORD,:DWORD,:DWORD,:DWORD ;прототип процедуры обработки сообщений, посылаемых окну программы CaptchaThread PROTO :DWORD ;прототип процедуры распознавания капчи BUFFER_SIZE equ 50000 ;размер буфера для хранения одной капчи MAX_THREADS equ 45 ;максимум потоков (45-1) WM_TCLEANUP equ WM_APP+1 ;определяем собственное сообщение, будем его использовать ;секция данных .data WSAStruct WSADATA <> ;структура для инициализации сокета ;тут - разные тексты ошибок SockErr db "Ошибка инициализации сокета",0 CapchaInit db "Ошибка инициализации anti-captcha",0 ErrOpen db "Ошибка открытия файла",0 TooLarge db "Слишком большой файл",0 noKey db "Введите ключ антикапчи",0 MutexErr db "Не удалось создать мьютекс",0 MaxThreads db "Открыто максимальное число картинок",0 GetHostFail db "Не удалось получить IP-адрес. Проверьте подключение по сети и попробуйте еще раз",0 MemErr db "Не удалось выделить память",0 Caption db "Сообщение",0 BadKey db "Недопустимый ключ антикапчи",0 fRead dd 0 ;переменная, необходимая при чтении файла ;некоторые данные отправляемого файла. Вообще, формат не анализируется ;на anti-captcha.com, поэтому всегда отсылаем jpeg contentType db "image/jpeg",0 extension db "jpg",0 ;максимальная и минимальная длина капчи - сделал статические переменные ;если необходимо, всегда можно сделать поля для ввода max_len dd 15 min_len dd 2 bAllocated db 0;выделена ли память ThreadCount dd 1 ;количество потоков Threads dd 1 ;количество потоков - еще один счетчик lineend db 13,10,0 ;конец строки text1 db "Got ID: ",0 ;сообщение для лога text2 db "Начинаем...",13,10,0 ;сообщение для лога ;разные форматы вывода (см. документацию по printf) msg2 db "ERR %u",0 msg3 db "THR%u: ",0 msg4 db "Капча №%u",0 ;секция неопределенных данных. Отличается от .data тем, что данные в ней не определены ;и поэтому не занимают места в exe-файле .data? apikey db 50 dup(?) ;ключ антикапчи iFile dd ? ;хендл открытого файла FileSize dd ? ;размер файла myFile dd 50 dup(?) ;массив для хранения указателей на содержимое файлов hWnd dd ? ;хендл нашего окна hInstance dd ? ;хендл текущего процесса ThreadId dd 50 dup(?) ;массив хендлов потоков ImageSize dd 50 dup(?) ;массив размеров картинок btnname db 30 dup(?) ;имя кнопки для открытия новых картинок ;хендлы мьютексов - нам потребуется два hMutex dd ? hMutex2 dd ? ;капча: состоит ли только из цифр, чувствительна ли к регистру, состоит ли из нескольких слов is_numeric dd ? is_regsense dd ? is_phrase dd ? ;секция кода .code ;точка входа, определена в самом низу кода start: mov hInstance, FUNC(GetModuleHandle,NULL) ;получим хендл процесса call main ;вызываем главную процедуру invoke ExitProcess,eax ;выходим ;главная процедура main PROC invoke WSAStartup,0101h,offset WSAStruct ;инициализируем сокеты версии 2.2 test eax,eax je @F mov eax,offset SockErr ;если не удалось - выведем ошибку jmp ErrBlock @@: mov ecx,MAX_THREADS ;начинаем выделять по 50000 байт памяти на каждый процесс в цикле ALLOC: push ecx invoke VirtualAlloc,0,BUFFER_SIZE,MEM_COMMIT,PAGE_READWRITE pop ecx test eax,eax jne goodmem ;если выделить не удалось - очистим ту память, что уже выделили до этого mov ebx,MAX_THREADS cmp ebx,ecx je next1 freemem: push ebx push ecx invoke VirtualFree,myFile[ebx*4],0,MEM_RELEASE ;освобождаем pop ecx pop ebx dec ebx cmp ebx,ecx jne freemem next1: mov eax,offset MemErr jmp ErrBlock goodmem: mov myFile[ecx*4],eax ;сохраняем указатель на память в массиве ;почему ecx*4, а не просто ecx? потому что myFile имеет тип DWORD = 4 байта loop ALLOC mov bAllocated,1 ;память выделена |
Теперь рассмотрим следующий код, в котором создаются мьютексы. Что же такое мьютекс? Представим, что у нас есть программа, в которой работает два потока одновременно.
Пусть 1й поток считал переменную, увеличил ее на единицу и хочет записать ее новое значение. В это время второй поток тоже считал переменную, до того, как 1й успел ее записать, и тоже увеличил ее значение на 1.
Тогда после того, как 1й и 2й поток запишут изменения, учтено будет только одно.
Можно взглянуть на такой псевдокод:
1: read A
1: A+1
2: read A //тут еще старое значение A!
2: A+1
1: write A //стало A+1
2: write A //стало A+1, а не A+2, как должно было быть!
С помощью мьютекса можно избежать такого конфликта. Можно заблокировать переменную A и освободить ее только после того, как первый поток закончит с ней работать:
block A <-теперь A доступна только первому потоку 1: read A 1: A+1 1: write A //стало A+1 unblock A <-теперь A доступна всем, и она уже инкрементирована на 1 2: read A //тут уже новое значение A 2: A+1 2: write A //стало A+2, как и должно было быть.
В Windows существует еще несколько способов для зщиты от конфликтов - семафоры, критические секции, события, но в нашем случае будет достаточно самого простого - мьютексов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
call AntiCaptchaInit ;инициализируем работу с антикапчей. Эта функция дальше подробно описывается test eax,eax je @F mov eax,offset CapchaInit ;если не удалось - выведем ошибку jmp ErrBlock @@: ;далее - создаем два мьютекса и проверяем, не возникло ли ошибок при создании mov hMutex,FUNC(CreateMutex,NULL,FALSE,NULL) test eax,eax jne @F mov eax,offset MutexErr jmp ErrBlock @@: mov hMutex2,FUNC(CreateMutex,NULL,FALSE,NULL) test eax,eax jne @F invoke CloseHandle,hMutex mov eax,offset MutexErr jmp ErrBlock @@: ;создаем диалоговое окно и элементы в нем с помощью удобных макросов masm32 Dialog "АнтиКапча test (c) dx", \ "MS Sans Serif",8, \ WS_OVERLAPPED or \ WS_SYSMENU or DS_CENTER, \ 9, \ 50,50,230,125, \ 1024 DlgButton "Капча №1",WS_TABSTOP,5,5,60,13,200 DlgButton "Распознать",WS_TABSTOP+WS_DISABLED,5,20,60,13,201 DlgEdit ES_READONLY+ES_MULTILINE+WS_BORDER+WS_TABSTOP+ES_AUTOVSCROLL+WS_VSCROLL,70,5,150,45,101 DlgStatic "Ключ: ",0,5,55,28,12,102 DlgEdit WS_BORDER+WS_TABSTOP,30,53,190,12,103 DlgButton "Закрыть",WS_TABSTOP,5,35,60,13,202 DlgCheck "Капчи состоят только из цифр",WS_TABSTOP,5,70,135,12,90 DlgCheck "Капчи зависят от регистра",WS_TABSTOP,5,82,125,12,91 DlgCheck "Капчи из нескольких слов",WS_TABSTOP,5,94,125,12,92 CallModalDialog hInstance,0,DialogProc,NULL ;устанавливаем процедуру для обработки сообщений и создаем окно ret ErrBlock: ;блок обработки ошибок .if bAllocated==1 ;если была выделена память - освободим ее mov ecx,MAX_THREADS FREEALLOC: push ecx invoke VirtualFree,myFile[ecx*4],0,MEM_RELEASE pop ecx loop FREEALLOC .endif invoke MessageBox,0,eax,offset Caption,0 invoke ExitProcess,0 ret main ENDP |
Инициализация произведена, форма создана. Остается теперь обрабатывать разные сообщения, посылаемые окну, в том числе и нажатия клавиш, и обрабатывать их.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
DialogProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD LOCAL path :DWORD ;несколько локальных переменных - путь к файлу, опции файлового диалога и номер текущего потока при запуске LOCAL ThreadNum :DWORD .if uMsg == WM_INITDIALOG ;инициализация окна invoke SendMessage,hWin,WM_SETICON,1, FUNC(LoadIcon,NULL,IDI_ASTERISK) ;установим иконку m2m hWnd, hWin ;поместим значение hWin в hWnd - глобальную переменную ;макрос m2m используется для пересылки данных из одной переменной в другую ;мы же не можем написать mov hWnd,hWin invoke SendDlgItemMessage,hWin,103,EM_SETLIMITTEXT,35,0 ;ограничиваем длину текста в поле "ключ" invoke SendDlgItemMessage,hWin,91,BM_SETCHECK,BST_CHECKED,0 ;устанавливаем чекбокс "капча чувствительна к регистру" |
Далее идет основная часть - обработка нажатия кнопок:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
.elseif uMsg == WM_COMMAND .if wParam==202 ;кнопка "Закрыть" jmp quit_dialog ;освободим ресурсы и выйдем .elseif wParam==200 ;кнопка "открыть капчу" .if ThreadCount==MAX_THREADS ;если уже открыто максимальное число - выведем ошибку и выйдем mov eax,offset MaxThreads jmp ErrBlock .endif mov path,chr$("Картинки bmp, jpeg, gif, png",0,"*.bmp;*.gif;*.jpeg;*.jpg;*.jpe;*.png",0,0) mov fname,OpenFileDlg(hWnd,hInstance,"Открыть капчу",path) ;предлагаем открыть картинки cmp BYTE PTR [eax], 0 ;если нажали "Отмена", то выходим jne @F xor eax,eax ret @@: mov iFile,FUNC(CreateFile,fname,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL) ;откроем файл для чтения cmp iFile,INVALID_HANDLE_VALUE ;если не удалось - выведем ошибку и выйдем из процедуры jne @F mov eax,offset ErrOpen jmp ErrBlock @@: mov FileSize,FUNC(GetFileSize,iFile,NULL) ;узнаем размер файла cmp eax,BUFFER_SIZE ;если он больше размера буфера jb @F ;то выйдем invoke CloseHandle,iFile mov eax,offset TooLarge jmp ErrBlock @@: mov ecx,ThreadCount ;запишем размер картинки в массив m2m ImageSize[ecx*4],FileSize mov fRead,0 invoke ReadFile,iFile,myFile[ecx*4],eax,offset fRead,NULL ;считаем содержимое файла и поместим указатель на память в массив invoke CloseHandle,iFile ;закроем файл inc ThreadCount ;увеличим число потоков на 1 invoke wsprintf,offset btnname,offset msg4,ThreadCount ;изменим название кнопки "открыть капчу" invoke SetDlgItemText,hWin,200,offset btnname invoke GetDlgItem,hWnd,201 ;разблокируем кнопку "Распознать" invoke EnableWindow,eax,TRUE .elseif wParam==201 ;кнопка "Распознать" invoke GetDlgItemText,hWin,103,offset apikey,45; считаем ключ антикапчи test eax,eax jne @F mov eax,offset noKey ;если не введен - выйдем jmp ErrBlock @@: ;проверим, не содержит ли ключ недопустимых символов... mov edi,offset apikey keycheck: mov al,byte ptr [edi] .if (al<'0' || al>'9') && (al<'a' || al>'f') mov eax,offset BadKey ;если мы нашли такой - выведем ошибку и выйдем из процедуры jmp ErrBlock .endif inc edi cmp byte ptr [edi],0 jne keycheck ;далее - читаем значения флажков и записываем 1 или 0 в соответствующие переменные invoke IsDlgButtonChecked,hWnd,90 mov is_numeric,0 .if eax==BST_CHECKED mov is_numeric,1 .endif invoke IsDlgButtonChecked,hWnd,91 mov is_regsense,0 .if eax==BST_CHECKED mov is_regsense,1 .endif invoke IsDlgButtonChecked,hWnd,92 mov is_phrase,0 .if eax==BST_CHECKED mov is_phrase,1 .endif invoke GetDlgItem,hWnd,200 ;заблокируем кнопку "Открыть капчу" invoke EnableWindow,eax,FALSE invoke GetDlgItem,hWnd,201 ;заблокируем кнопку "Распознать" invoke EnableWindow,eax,FALSE invoke SetDlgItemText,hWnd,101,offset text2 ;сообщим о начале работы m2m Threads,ThreadCount ;сохраним число потоков mov ecx,1 ;в цикле запустим потоки Launch: mov ThreadNum,ecx push ecx invoke CreateThread,0,0,addr CaptchaThread,ThreadNum,0,NULL ;процедура CaptchaThread описана далее pop ecx mov ThreadId[ecx*4],eax ;запишем хендл свежесозданного потока inc ecx cmp ecx,ThreadCount jne Launch .endif ret .elseif uMsg == WM_CLOSE ;закрытие окна quit_dialog: ;кнопка "закрыть" call WSACleanup ;очистим ресурсы сокетов mov ecx,MAX_THREADS ;освободим выделенную память FREEALLOC: push ecx invoke VirtualFree,myFile[ecx*4],0,MEM_RELEASE pop ecx loop FREEALLOC .if Threads!=1 ;освободим хендлы открытых потоков (если такие были) mov ecx,1 cleanupthr2: push ecx invoke CloseHandle,ThreadId[ecx*4] pop ecx inc ecx cmp ecx,Threads jne cleanupthr2 .endif invoke CloseHandle,hMutex ;освободим хендлы мьютексов invoke CloseHandle,hMutex2 invoke EndDialog,hWin,0 ;закончим работу .elseif uMsg==WM_TCLEANUP ;наше собственное сообщение, мы будем его посылать из тела потока для того, чтобы закрыть хендлы открытых потоков mov ecx,1 cleanupthr: push ecx invoke CloseHandle,ThreadId[ecx*4] ;закрываем хендлы всех созданных потоков pop ecx inc ecx cmp ecx,Threads jne cleanupthr mov Threads,1 ;ни одного потока не создано .endif xor eax,eax ret ErrBlock: ;блок обработки ошибок invoke MessageBox,hWnd,eax,offset Caption,0 xor eax,eax ret DialogProc ENDP |
Процедура обработки сообщений написана, теперь остается разобрать остальные процедуры.
Начнем с процедуры, которая вызывается в теле потоков, создаваемых основной программой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
CaptchaThread PROC ThreadNum:DWORD ;этой процедуре передается номер потока LOCAL cID [30] :BYTE ;используем локальные переменные: LOCAL cANS [30] :BYTE ;ID капчи на антикапче, ответ и временная переменная для вывода логов LOCAL errnum [50] :BYTE mov eax,ThreadNum ;запишем все необходимые данные в стек push myFile[eax*4] ;указатель на память с картинкой push ImageSize[eax*4] ;размер картинки push offset contentType ;тип картинки push offset extension ;расширение файла push max_len ;настройки всякие push min_len push is_numeric push is_regsense push is_phrase push offset apikey ;ключ антикапчи lea eax,cID ;указатель на переменную, куда будет помещен ID загруженной капчи push eax call AntiCaptcha ;вызываем процедуру загрузки капчи на сервер и получения ее ID ;эта процедура описана далее push ebx ;проыеряе , не возникло ли ошибок .if ebx!=0 invoke wsprintf,addr errnum,offset msg2,ebx ;если возникло (ebx!=0), то выводим ее номер .else invoke lstrcpy,addr errnum,offset text1 ;если все нормально, то выводим ID капчи .if eax!=0 lea ecx,cID invoke lstrcat,eax,ecx .endif .endif push ThreadNum lea eax,errnum push eax call AddText ;выведем в поле логов pop ebx .if ebx!=0 ;если возникла ошибка, то помимо ее вывода: call GetCaptchaError ;получим ее текст push ThreadNum push ebx call AddText ;выведем ее текст invoke WaitForSingleObject,hMutex2,INFINITE ;ждем, пока мьютекс освободится cmp eax,WAIT_OBJECT_0 ;проверяем, нам ли досталось управление им? je @F ret @@: dec ThreadCount ;уменьшим количество работающих потоков ;это и есть общая переменная для всех потоков cmp ThreadCount,1 ;если остался 1 jne @F invoke GetDlgItem,hWnd,200 ;то разблокируем кнопку "Открыть капчу" invoke EnableWindow,eax,TRUE invoke SetDlgItemText,hWnd,200,chr$("Капча №1") ;и меняем ее имя на первоначальное invoke SendMessage,hWnd,WM_TCLEANUP,0,0 ;ну и освобождаем хендлы потоков (помните наше пользовательское сообщение форме?) @@: invoke ReleaseMutex,hMutex2 ;освобождаем мьютекс ret .endif push ThreadNum push chr$("Распознавание...") call AddText ;все нормально - распознаем капчу get_answer: push offset apikey ;ключ lea eax,cID ;ID капчи push eax lea eax,cANS ;указатель на переменную, куда будет записан ответ push eax call AntiCaptchaAnswer ;эта процедура пытается получить ответ сервера о ходе распознавания test ebx,ebx je @F call GetCaptchaError ;если капча не распознана - выведем текст ошибки push ThreadNum push ebx call AddText invoke Sleep,1000 ;подождем 1 сек jmp get_answer ;попробуем еще раз @@: ;если капча распознана (ebx==0) invoke lstrcpy,addr errnum,chr$("Готово: ") ;выведем результат распознавания .if eax!=0 lea ecx,cANS invoke lstrcat,eax,ecx .endif push ThreadNum lea eax,errnum push eax call AddText invoke WaitForSingleObject,hMutex2,INFINITE ;ждем освобождения мьютекса cmp eax,WAIT_OBJECT_0 ;проверяем, нам ли он достался je @F ret @@: dec ThreadCount ;уменьшаем количество потоков cmp ThreadCount,1 ;если остался 1 jne @F invoke GetDlgItem,hWnd,200 ;то разблокируем кнопку "Открыть капчу" invoke EnableWindow,eax,TRUE invoke SetDlgItemText,hWnd,200,chr$("Капча №1") ;и меняем ее имя на первоначальное invoke SendMessage,hWnd,WM_TCLEANUP,0,0 ;ну и освобождаем хендлы потоков (помните наше пользовательское сообщение форме?) @@: invoke ReleaseMutex,hMutex2 ;освобожим мьютекс ret CaptchaThread ENDP |
Теперь я опищу процедуру GetCaptchaError - эта процедура по номеру ошибки, возвращенному процедурами распознавания капчи, выдаст текст ошибки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
GetCaptchaError PROC ;тут мы просто в зависимости от номера ошибки возвращаем указатель на соответствующую строку cmp ebx,1 jne @F mov ebx,chr$("Не удалось создать сокет") @@: cmp ebx,2 jne @F mov ebx,chr$("Не удалось подключиться") @@: cmp ebx,4 jne @F mov ebx,chr$("Ошибка при объединении строк") @@: cmp ebx,5 jne @F mov ebx,chr$("Не удалось отправить данные") @@: cmp ebx,6 jne @F mov ebx,chr$("Не удалось получить данные") @@: cmp ebx,7 jne @F mov ebx,chr$("Предотвращено переполнение буфера") @@: cmp ebx,10 jne @F mov ebx,chr$("Капча пока не готова") @@: cmp ebx,11 jne @F mov ebx,chr$("Неверный ключ") @@: cmp ebx,13 jne @F mov ebx,chr$("Нулевой баланс аккаунта") @@: cmp ebx,14 jne @F mov ebx,chr$("Несуществующий ID капчи") @@: cmp ebx,12 jne @F mov ebx,chr$("Все работники пока заняты") @@: cmp ebx,20 jne @F mov ebx,chr$("Неопределенная ошибка") @@: ret GetCaptchaError ENDP |
Теперь - описание процедуры вывода нового сообщения в лог. Здесь тоже используется мьютекс, так как поле - общее.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
AddText PROC, text:DWORD, tnum:DWORD ;процедуре передается указатель на текст и номер потока LOCAL tmptxt [20] :BYTE invoke WaitForSingleObject,hMutex,INFINITE ;ждем освобождения мьютекса cmp eax,WAIT_OBJECT_0 ;нам ли он достался? je @F ret @@: invoke wsprintf,addr tmptxt,offset msg3,tnum ;печатаем инфо о номере потока, который выводит сообщение mov ebx,tnum invoke GetDlgItemText,hWnd,101,myFile[ebx*4],BUFFER_SIZE-50 ;берем текст, который уже есть в логе mov ebx,tnum add eax,myFile[ebx*4] lea ecx,tmptxt invoke lstrcat,eax,ecx ;добавляем к нему номер потока .if eax!=0 invoke lstrcat,eax,text ;и наш текст .endif .if eax!=0 invoke lstrcat,eax,offset lineend ;и конец строки .endif mov ebx,tnum invoke SetDlgItemText,hWnd,101,myFile[ebx*4] ;записываем все это в поле лога invoke SendDlgItemMessage,hWnd,101,EM_LINESCROLL,0,10000 ;и скроллим его вниз invoke ReleaseMutex,hMutex ;освобождаем мьютекс ret AddText ENDP |
Еще одна вспомогательная процедура - процедура для объединения любого количества строк в одну:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
;подать: ;push strN ;push strN-1 ;... ;push str1 ;push N-1 ;(количество строк)-1 ;push memory address ;указатель на память, куда записать результат ConcatStrings PROC pop edi ;адрес возврата pop ebx pop ecx pop edx push ecx invoke lstrcpy,ebx,edx ;копируем первую строку pop ecx concatx: pop edx push ecx .if eax!=0 ;если на каком-то этапе не возникла ошибка invoke lstrcat,eax,edx ;объединяем оставшиеся строки с буфером .endif pop ecx loop concatx push edi ;адрес возврата ret ConcatStrings ENDP |
Наконец, осталось разобрать самые важные процедуры - те, которые я написал для работы с сервисом антикапчи.
Начнем с процедуры, которая осуществляет закачку картинки на сервер и получает ID закачанной картинки.
В случае ошибки будет возвращен код ошибки в ebx, а если все нормально, то ebx будет равен 0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
;в стек передать: ;push указатель_на_память_с_картинкой - dword ;push размер_картинки - dword ;push content-type - указатель на строку - dword ;push extension - указатель на строку - dword ;push max_len - dword ;push min_len - dword ;push is_numeric - dword ;push is_regsense - dword ;push is_phrase - dword ;push apikey - указатель на строку - dword ;push cID - указатель на строку - dword - куда будет записан id, буфер не менее 30 байт ; ;вернет указатель на строку с капчей или 0 при ошибке AntiCaptcha PROC, AC_cID:DWORD, AC_apikey:DWORD, AC_is_phrase:DWORD, AC_is_regsense:DWORD, AC_is_numeric:DWORD, AC_min_len:DWORD, AC_max_len:DWORD, AC_extension:DWORD, AC_contentType:DWORD, AC_imageSize:DWORD, AC_imagePtr:DWORD AC_BUFFER_SIZE equ 2048 ;буфер для чтения ответа. 2 кб вполне достаточно, и выделять дополнительную память не требуется - мы возьмем эти 2кб из стека ;все переменные локальные, чтобы можно было использовать эту процедуру многопоточно LOCAL AC_bConnected:BYTE ;подключен ли сокет LOCAL AC_iSock:DWORD ;хендл сокета ;настройки антикапчи LOCAL AC_max_len_str [5] :BYTE LOCAL AC_min_len_str [5] :BYTE LOCAL AC_is_numeric_str [5] :BYTE LOCAL AC_is_regsense_str [5] :BYTE LOCAL AC_is_phrase_str [5] :BYTE LOCAL AC_DataLen:DWORD ;длины строк, отправляемых LOCAL AC_Data1Len:DWORD ;на сервер антикапчи LOCAL AC_mem:DWORD ;указатель на память LOCAL AC_Header [512] :BYTE ;заголовок запроса LOCAL AC_Data1 [AC_BUFFER_SIZE] :BYTE ;данные запроса LOCAL AC_querylen [10] :BYTE ;переменная для хранения Content-Length AC_boundary equ "---------FGf4Fh3fdjGQ148fdh" ;разделитель ;секция данных - все данные отсюда будут только читаться ;здесь - строки запросов для отправки на сервер .data AC_SocketAddress sockaddr_in <> ;структура для подключения сокета AC_content1 db '--',AC_boundary,13,10,'Content-Disposition: form-data; name="method"',13,10,13,10, \ 'post',13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="key"',13,10,13,10,0 AC_content2 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="phrase"',13,10,13,10,0 AC_content3 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="regsense"',13,10,13,10,0 AC_content4 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="numeric"',13,10,13,10,0 AC_content5 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="min_len"',13,10,13,10,0 AC_content6 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="max_len"',13,10,13,10,0 AC_content7 db 13,10,'--',AC_boundary,13,10, \ 'Content-Disposition: form-data; name="file"; filename="capcha.',0 AC_content8 db '"',13,10,'Content-Type: ',0 AC_content9 db 13,10,13,10,0 AC_content10 db 13,10,'--',AC_boundary,'--',0 AC_content10len=($-AC_content10) AC_poststr1 db "POST /in.php HTTP/1.0",13,10, \ "Content-Type: multipart/form-data; boundary=",AC_boundary,13,10, \ "Host: www.anti-captcha.com",13,10, \ "Content-Length: ",0 AC_poststr2 db 13,10,13,10,0 AC_msg1 db "%u",0 ;для формирования Content-Length в ASCII AC_substr1 db "OK|",0 ;для парсинга ответа ;секция кода .code mov AC_bConnected,0 ;мы пока не подключены lea eax,AC_Data1 ;устанавливаем указатель на память mov AC_mem,eax invoke socket,AF_INET,SOCK_STREAM,IPPROTO_TCP ;создаем сокет CMP EAX,INVALID_SOCKET ;если не удалось - запишем номер ошибки в ebx и выйдем jne @F mov ebx,2 jmp AC_CaptchaErr @@: mov AC_iSock,eax ;создали сокет invoke connect,AC_iSock,offset AC_SocketAddress,sizeof AC_SocketAddress ;подключаемся test eax,eax je @F mov ebx,3 jmp AC_CaptchaErr @@: mov AC_bConnected,1 ;подключились ;дальше мы объединяем все строки запроса в одну push offset AC_content9 push AC_contentType push offset AC_content8 push AC_extension push offset AC_content7 invoke wsprintf,addr AC_max_len_str,offset AC_msg1,AC_max_len lea eax, AC_max_len_str push eax push offset AC_content6 invoke wsprintf,addr AC_min_len_str,offset AC_msg1,AC_min_len lea eax, AC_min_len_str push eax push offset AC_content5 invoke wsprintf,addr AC_is_numeric_str,offset AC_msg1,AC_is_numeric lea eax, AC_is_numeric_str push eax push offset AC_content4 invoke wsprintf,addr AC_is_regsense_str,offset AC_msg1,AC_is_regsense lea eax, AC_is_regsense_str push eax push offset AC_content3 invoke wsprintf,addr AC_is_phrase_str,offset AC_msg1,AC_is_phrase lea eax, AC_is_phrase_str push eax push offset AC_content2 push AC_apikey push offset AC_content1 push 16 lea eax,AC_Data1 push eax call ConcatStrings test eax,eax ;если ошибка - выйдем и запишем код в ebx jne @F mov ebx,4 jmp AC_CaptchaErr @@: ;теперь посчитаем Content-Length и длины отправляемых на сервер кусков запроса mov AC_DataLen,FUNC(lstrlen,addr AC_Data1) mov AC_Data1Len,eax mov eax,AC_imageSize add AC_DataLen,eax add AC_DataLen,AC_content10len invoke wsprintf,addr AC_querylen,offset AC_msg1,AC_DataLen push offset AC_poststr2 lea eax,AC_querylen push eax push offset AC_poststr1 push 2 lea eax,AC_Header push eax call ConcatStrings ;делаем заголовок запроса test eax,eax jne @F mov ebx,4 jmp AC_CaptchaErr @@: ;теперь будем отправлять данные по частям mov ebx,FUNC(lstrlen,addr AC_Header) push ebx invoke send,AC_iSock,addr AC_Header,ebx,0 ;шлем заголовок pop ebx cmp eax,ebx ;всё ли отправилось? je @F mov ebx,5 jmp AC_CaptchaErr @@: invoke send,AC_iSock,addr AC_Data1,AC_Data1Len,0 ;кусок запроса до картинки cmp eax,AC_Data1Len ;полностью ли отправилось? je @F mov ebx,5 jmp AC_CaptchaErr @@: invoke send,AC_iSock,AC_imagePtr,AC_imageSize,0 ;отправляем картинку в бинарном виде cmp eax,AC_imageSize ;полностью ли отправлена? je @F mov ebx,5 jmp AC_CaptchaErr @@: invoke send,AC_iSock,offset AC_content10,AC_content10len,0 ;последний кусок запроса cmp eax,AC_content10len ;полностью ли отправлен? je @F mov ebx,5 jmp AC_CaptchaErr @@: mov edi,AC_mem ;теперь читаем ответ в память AC_sread: invoke recv,AC_iSock,edi,256,0 ;по 256 байт cmp eax,-1 ;не возникла ли ошибка чтения? jne @F mov ebx,6 jmp AC_CaptchaErr @@: add edi,eax ;перемещаем указатель в памяти mov edx,edi sub edx,AC_mem cmp edx,AC_BUFFER_SIZE-257 ;не возникнет ли переполенние буфера на следующем шаге чтения? jb @F mov ebx,7 jmp AC_CaptchaErr @@: cmp eax,0 jne AC_sread mov byte ptr [edi],0 ;завершающий 0 в считанных данных invoke closesocket,AC_iSock ;закрываем соединение mov AC_bConnected,0 ;дальше - блок обработки ошибок. ;функция проверяет наличие подстроки в полученных из сокета данных, ;и если она есть - возвращает 0 в eax и код ошибки в ebx. mov edx,istring(50,AC_mem,"ERROR_KEY_DOES_NOT_EXIST") ;ключ невалидный test edx,edx je @F mov ebx,11 jmp AC_CaptchaErr @@: mov edx,istring(50,AC_mem,"ERROR_WRONG_USER_KEY") ;ключ неправильный test edx,edx je @F mov ebx,11 jmp AC_CaptchaErr @@: mov edx,istring(50,AC_mem,"ERROR_NO_SLOT_AVAILABLE") ;все работники заняты test edx,edx je @F mov ebx,12 jmp AC_CaptchaErr @@: mov edx,istring(50,AC_mem,"ERROR_ZERO_BALANCE") ;деньги кончились test edx,edx je @F mov ebx,13 jmp AC_CaptchaErr @@: mov edx,istring(100,AC_mem,offset AC_substr1) ;ищем строку OK|ID_капчи test edx,edx jne @F mov ebx,20 ;если такой нет - неопределенная ошибка jmp AC_CaptchaErr @@: add edx,AC_mem ;получаем ID капчи add edx,2 push edx invoke lstrlen,edx pop edx cmp eax,29 ;если он слишком длинный - ошибка jbe @F mov ebx,20 jmp AC_CaptchaErr @@: invoke lstrcpy,AC_cID,edx ;копируем его в данную нам память mov eax,AC_cID ;и возвращаем указатель на нее xor ebx,ebx ;обнуляем ошибку ret AC_CaptchaErr: ;блок обработки ошибок push ebx cmp AC_bConnected,0 ;если мы были подключены - закроем соединение je @F invoke closesocket,AC_iSock @@: pop ebx xor eax,eax ;возвратим 0 в eax и код ошибки в ebx ret AntiCaptcha ENDP |
Картинку с капчей на сервер отправили, ID её получили, теперь остается только делать регулярные запросы на сервер в ожидании ответа.
Для этого я написал процедуру AntiCaptchaAnswer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
;передать: ;push ptr apikey - dword ;push указатель_на_память_с_ID_капчи - dword ;push answer ptr - dword - куда будет записан ответ, буфер не менее 30 байт ; ;вернет ответ AntiCaptchaAnswer PROC, ACA_answer:DWORD, ACA_cID:DWORD, ACA_apikey:DWORD ACA_BUFFER_SIZE equ 2048 ;размер буфера для чтения ответа LOCAL ACA_bConnected:BYTE ;те же переменные, что и в предыдущей процедуре LOCAL ACA_iSock:DWORD LOCAL ACA_DataLen:DWORD LOCAL ACA_Data [ACA_BUFFER_SIZE] :BYTE LOCAL ACA_mem :DWORD LOCAL ACA_querylen [10] :BYTE ;секция данных - они снова только читаются отсюда, но не перезаписываются. .data ACA_query1 db "GET /res.php?key=",0 ACA_query2 db "&action=get&id=",0 ACA_query3 db " HTTP/1.0",13,10, \ "Host: www.anti-captcha.com",13,10, \ "Connection: keep-alive",13,10,"Keep-alive: 300",13,10,"Content-Length: 0",13,10,13,10,0 ;секция кода .code mov ACA_bConnected,0 ;мы не подключены lea eax,ACA_Data mov ACA_mem,eax invoke socket,AF_INET,SOCK_STREAM,IPPROTO_TCP ;создаем сокет CMP EAX,INVALID_SOCKET ;если не удалось - вернем ошибку в ebx jne @F mov ebx,1 jmp ACA_CaptchaErr @@: mov ACA_iSock,eax invoke connect,ACA_iSock,offset AC_SocketAddress,sizeof AC_SocketAddress ;подключаемся test eax,eax je @F mov ebx,2 jmp ACA_CaptchaErr @@: mov ACA_bConnected,1 ;подключились ;объединяем строки для запроса с переданным нам ID капчи push offset ACA_query3 push ACA_cID push offset ACA_query2 push ACA_apikey push offset ACA_query1 push 4 lea eax,ACA_Data push eax call ConcatStrings test eax,eax ;если не удалось - вернем ошибку в ebx jne @F mov ebx,4 jmp ACA_CaptchaErr @@: mov ebx,FUNC(lstrlen,addr ACA_Data) push ebx invoke send,ACA_iSock,addr ACA_Data,ebx,0 ;отсылаем данные pop ebx cmp eax,ebx ;полностью ли отослалось? je @F mov ebx,5 jmp ACA_CaptchaErr @@: mov edi,ACA_mem ;начинаем чтение ACA_sread: invoke recv,ACA_iSock,edi,256,0 ;по 256 байт cmp eax,-1 ;не возникло ли ошибок? jne @F mov ebx,6 jmp ACA_CaptchaErr @@: add edi,eax mov edx,edi sub edx,ACA_mem cmp edx,ACA_BUFFER_SIZE-257 ;не будет ли переполнения на следующем шаге? jb @F mov ebx,7 jmp ACA_CaptchaErr @@: cmp eax,0 jne ACA_sread mov byte ptr [edi],0 ;завершающий ноль invoke closesocket,ACA_iSock mov ACA_bConnected,0 ;тут снова обработка возможных ошибок, как и в предыдущей функции mov edx,istring(50,ACA_mem,"CAPCHA_NOT_READY") ;капча пока не распознана test edx,edx je @F mov ebx,10 jmp ACA_CaptchaErr @@: mov edx,istring(50,ACA_mem,"ERROR_KEY_DOES_NOT_EXIST") test edx,edx je @F mov ebx,11 jmp ACA_CaptchaErr @@: mov edx,istring(50,ACA_mem,"ERROR_WRONG_USER_KEY") test edx,edx je @F mov ebx,11 jmp ACA_CaptchaErr @@: mov edx,istring(50,ACA_mem,"ERROR_ZERO_BALANCE") test edx,edx je @F mov ebx,13 jmp ACA_CaptchaErr @@: mov edx,istring(50,ACA_mem,"ERROR_NO_SUC_CAPCHA_ID") ;неверный ID капчи test edx,edx je @F mov ebx,14 jmp ACA_CaptchaErr @@: mov edx,istring(50,ACA_mem,offset AC_substr1) ;ищем подстроку OK|ответ test edx,edx jne @F mov ebx,20 ;если не нашли - неопределенная ошибка jmp ACA_CaptchaErr @@: add edx,ACA_mem ;получаем ответ add edx,2 push edx invoke lstrlen,edx pop edx cmp eax,29 ;если он слишком длинный - ошибка jbe @F mov ebx,20 jmp ACA_CaptchaErr @@: invoke lstrcpy,ACA_answer,edx ;копируем ответ в отведенную нам переменную mov eax,ACA_answer ;возвращаем указатель на нее в eax и 0 в ebx xor ebx,ebx ret ACA_CaptchaErr: ;если возникали ошибки push ebx cmp ACA_bConnected,0 ;если мы были подключены - отключаемся je @F invoke closesocket,ACA_iSock @@: pop ebx ;вернем код ошибки в ebx и 0 в eax xor eax,eax ret AntiCaptchaAnswer ENDP |
Ну и наконец последнее - процедура инициализации для работы с антикапчей. Её нужно вызывать всего один раз после инициализации сокетов.
Она вернет в eax 0 в случае успеха или 2 в случае ошибки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
AntiCaptchaInit PROC .data AC_anticaptchaURL db "anti-captcha.com",0 ;адрес антикапчи .code invoke gethostbyname,offset AC_anticaptchaURL ;получаем IP test eax,eax jne @F mov eax,2 ;если не удалось - ошибка ret @@: mov eax,[eax+12] mov eax,[eax] mov eax,[eax] MOV AC_SocketAddress.sin_addr,EAX ;записываем ip в структуру invoke htons,80 mov AC_SocketAddress.sin_port,AX ;записываем порт в структуру MOV AC_SocketAddress.sin_family,AF_INET xor eax,eax ;все ОК ret AntiCaptchaInit ENDP end start |
Всё. Программа написана, можно ее скомпилировать и использовать. Как можно компилировать программы в MASM32, описано в предыдущих статьях.
Напоследок приведу лог теста программы - я открывал 5 капч и одновременно их распознавал. Программа выдала следующее:
Начинаем...
THR4: Got ID: 2117276
THR4: Распознавание...
THR3: Got ID: 2117279
THR3: Распознавание...
THR5: Got ID: 2117280
THR5: Распознавание...
THR2: Got ID: 2117283
THR2: Распознавание...
THR1: Got ID: 2117277
THR1: Распознавание...
THR4: Капча пока не готова
THR3: Капча пока не готова
THR5: Капча пока не готова
THR2: Капча пока не готова
THR1: Капча пока не готова
THR4: Готово: 8031
THR3: Капча пока не готова
THR5: Капча пока не готова
THR2: Готово: qwsdv
THR1: Капча пока не готова
THR3: Готово: dqgs5
THR5: Готово: 770857
THR1: Готово: 905955
THR и номер - это номер потока, который выводит сообщение в лог.
Теперь создадим dll - библиотеку с функциями для работы с антикапчей. В исходном коде библиотеки поместим три процедуры - AntiCaptcha, AntiCaptchaInit и AntiCaptchaAnswer. Кроме того, создадим функцию инициализации dll:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
LibMain proc instance:DWORD,reason:DWORD,reserved:DWORD .if reason == DLL_PROCESS_ATTACH mov eax, 1 ;тут необходимо вернуть 1 для успешной инициализации .elseif reason == DLL_THREAD_ATTACH ; unused .elseif reason == DLL_THREAD_DETACH ; unused .elseif reason == DLL_PROCESS_DETACH ; unused .endif ret LibMain ENDP |
Соответственно, точка входа в dll будет LibMain. (Я несколько видоизменил функции в самой dll, чтобы они занимали меньше места в exe-файле, но в любом случае - суть не изменилась и исходный код прилагается).
В основной программе, чтобы использовать эту dll, определим переменные для хранения адресов загруженной dll и функций в ней:
1 2 3 4 |
aclib dd ? AntiCaptchaInit dd ? AntiCaptcha dd ? AntiCaptchaAnswer dd ? |
Потом загрузим библиотеку и получим адреса функций в длл сразу после запуска программы:
1 2 3 4 5 6 7 8 9 10 |
mov aclib,FUNC(LoadLibrary,chr$("anticaptcha.dll")) cmp aclib,NULL jne @F invoke MessageBox,0,offset DllErr,offset Caption,0 ;если длл не удалось загрузить invoke ExitProcess,0 @@: mov AntiCaptchaInit,FUNC(GetProcAddress,aclib,chr$("AntiCaptchaInit")) mov AntiCaptcha,FUNC(GetProcAddress,aclib,chr$("AntiCaptcha")) mov AntiCaptchaAnswer,FUNC(GetProcAddress,aclib,chr$("AntiCaptchaAnswer")) |
В остальном программа не изменилась, кроме того, что при выходе необходимо освободить библиотеку:
1 |
invoke FreeLibrary,aclib |
В саму dll добавил на всякий случай еще две вспомогательные функции - SocketInit и SocketCleanup - это просто переходники к WinApi-функциям WSAStartup и WSACleanup.
Описание функций, экспортируемых dll:
AntiCaptchaInit - вызывается для инициализации работы с антикапчей, ей не надо передавать параметры, вернет 0 в случае успеха.
AntiCaptcha - вызывается для отправки изображения на сервер. Вернет 0 в случае успеха или код ошибки.
Параметры:
AntiCaptcha PROC,
[указатель на память, куда записывать ID капчи - буфер не менее 30 байт,
туда будет записана строка],
[указатель на строку с ключом антикапчи],
[1 или 0 - состоит ли капча из нескольких слов],
[1 или 0 - регистрозависима ли капча],
[1 или 0 - состоит ли капча только из цифр],
[минимальная длина капчи],
[максимальная длина капчи],
[указатель на строку с расширением, например, "jpg"],
[указатель на строку с типом, например "image/jpeg"],
[размер картинки в байтах],
[указатель на память, где загружена картинка]
Все параметры - DWORD.
AntiCaptchaAnswer - вызывается для получения распознанного варианта или статуса распознавания. Вернет 0 в случае успеха или код ошибки.
Параметры:
AntiCaptchaAnswer PROC,
[указатель на память, куда записать ответ],
[указатель на память с ID капчи, полученный с помощью предыдущей функции],
[указатель на строку с ключом антикапчи]
Все параметры - DWORD.
Коды возвращаемых ошибок:
1 - ошибка при создании сокета
2 - ошибка при подключении
4 - ошибка при создании запроса
5 - ошибка при отправке данных
6 - ошибка при получении данных
7 - слишком большой ответ сервера
10 - капча пока не распознана
11 - неверный ключ
12 - все работники пока заняты
13 - нулевой баланс на аккаунте
14 - несуществующий ID капчи
20 - неопределенная ошибка
Пример прототипа функции AntiCaptcha для c++:
1 2 3 4 5 6 7 8 |
typedef UINT (CALLBACK* func2)(char *id, char *key, bool a, bool b, bool c, int min, int max, char *type, char *type2, long size, char *pic); //... func2 AntiCaptcha; AntiCaptcha = (func2)GetProcAddress(hLib, "AntiCaptcha"); //hLib - наша длл |
Скачать исходники программы, саму программу в собранном виде, а также ее версию с dll можно тут: ZIP
хочу спросить программа работает по распознования капча ? или как её настроить ? добавляйся в скайп мой ник usausa629 есть пару вопросиков по программе . Спасибо
Это просто пример для работы с сервисом antigate.