edx Опубликовано 13 мая, 2020 Поделиться Опубликовано 13 мая, 2020 Всем доброго времени суток. Решил поботоводить известным ботом в известной игре:). Бот умеет решать капчу, но на данном сервере возникла проблема) Байпасы не перехватываются, а при попытке отправить какой-то из них (при чем верный), спустя пару попыток клиент закрывается, либо дисконнектит с сервера. Чтобы сильно не заморачиваться и не влезать в клиент, решил накидать прототип "решалки" на OpenCV. Применять можно абсолютно везде (в любой игре). Вид капчи: Спойлер Софт: - VisualStudio 2019 - OpenCV 3.4.10 Итак, сначала нам надо получить handle окна, с которого мы будем снимать скрин: Для этого у нас будет 2 функции: GetPID и EnumWindowsProc Спойлер HWND hWindow = NULL; int GetPID() { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);; PROCESSENTRY32 pentry32; if (snap == INVALID_HANDLE_VALUE) return 0; pentry32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(snap, &pentry32)) { CloseHandle(snap); return 0; } do { if (!lstrcmpi("L2.exe", &pentry32.szExeFile[0])) { CloseHandle(snap); return pentry32.th32ProcessID; } } while (Process32Next(snap, &pentry32)); CloseHandle(snap); return 0; } BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { DWORD lpdwProcessId; GetWindowThreadProcessId(hwnd, &lpdwProcessId); if (lpdwProcessId == lParam) { hWindow = hwnd; return FALSE; } return TRUE; } Далее описание в коде: Спойлер BOOL antibot_state = false; EnumWindows(EnumWindowsProc, GetPID()); if (hWindow) { RECT windowRect; GetWindowRect(hWindow, &windowRect); // Получаем размеры и позицию окна int left = windowRect.left; int top = windowRect.top; int right = windowRect.right; int bot = windowRect.bottom; int width = right - left; int height = bot - top; HDC hwndDC = GetWindowDC(hWindow); HDC hdcMem = CreateCompatibleDC(hwndDC); // Полный путь к папке в которой лежин паттерн с помощью которого мы определяем, что вылезло окно с капчей // здесь - текущая папка с проектом TCHAR dirBuffer[256]; DWORD ProjectPath = GetCurrentDirectory(256, dirBuffer); const char* FILE_NAME = "\\antibot.jpg\0"; const char* FILE_PATH = strcat((char*)dirBuffer, FILE_NAME); // Считываем паттерн из файла и прифодим к формату цвета RGB Mat antibot = imread(FILE_PATH); cvtColor(antibot, antibot, BI_RGB); if (!antibot.data) { std::cout << "Image not loaded"; return -1; } // Далее сам цикл отработки while (true) { HBITMAP hBitmap = CreateCompatibleBitmap(hwndDC, width, height); SelectObject(hdcMem, hBitmap); BitBlt(hdcMem, 0, 0, width, height, hwndDC, 0, 0, SRCCOPY); BITMAPINFO BMI; BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); BMI.bmiHeader.biWidth = width; BMI.bmiHeader.biHeight = -height; BMI.bmiHeader.biPlanes = 1; BMI.bmiHeader.biBitCount = 32; BMI.bmiHeader.biCompression = BI_RGB; Mat img = Mat(height, width, CV_8UC4); // Сам скриншот Mat result = Mat(height, width, CV_8UC4); // Переменная для записи результата сравнения Mat antibot_result = Mat(544, 60, CV_8UC4); // Для записи результата сравнения паттерна окна капчи Mat cropped_img, pattern, find_field; // Обрезанное изображение без рамок Rect cropp, roi, find_cropp; // Rect обрезанного изображения. Здесь мы корректируем смещение координат по толщине рамки // width - 16 это толщина рамки в 8 пикселей слева и справа // height - 39 толщина рамки 8 пикселей снизу и 31 пиксель сверху cropp.x = 8; cropp.y = 31; cropp.width = width - 16; cropp.height = height - 39; // Для паттерна "Примера" картинки которую нужно найти и кликнуть (чтобы не искать по всему скриншоту) roi.x = 561; roi.y = 280; roi.width = 25; roi.height = 25; // Область поиска в которой мы ищем нужную картинку (поле "Выбор") find_cropp.x = 400; find_cropp.y = 348; find_cropp.width = 340; find_cropp.height = 340; cropped_img = img(cropp); // Обрезаем скриншот (не знаю зачем конечно, но мне так больше нравится)) pattern = cropped_img(roi); // Выделяем область из скриншота, в которой находится наш пример find_field = cropped_img(find_cropp); // Выделяем область поиска GetDIBits(hwndDC, hBitmap, 0, height, img.data, &BMI, DIB_RGB_COLORS); // Сравнение скриншота и паттерна антибота matchTemplate(cropped_img, antibot, antibot_result, CV_TM_SQDIFF); double minval_a, maxval_a; Point minloc_a, maxloc_a; minMaxLoc(antibot_result, &minval_a, &maxval_a, &minloc_a, &maxloc_a); // Далее проверка. Необходимо вывести в консоль значения minval и maxval и посмотреть в каком диапазоне // находятся значения когда появляется наш паттерн на скриншоте. Я брал только по minval. // Также для удобства можно обводить область в которой нашелся наш паттерн if (minval_a >= 6.77e7 && minval_a <= 6.84e7) { char timeBuffer[80]; time_t seconds = time(NULL); tm* timeinfo = localtime(&seconds); const char* format = "%H:%M:%S"; strftime(timeBuffer, 80, format, timeinfo); cout << timeBuffer << " Finded ANTIBOT!" << endl; antibot_state = TRUE; } else { antibot_state = FALSE; } // Поиск паттерна "Примера" в нашей заданной области поиска и выполнение обводки первого попавшегося паттерна matchTemplate(find_field, pattern, result, CV_TM_SQDIFF); double minval, maxval; Point minloc, maxloc; minMaxLoc(result, &minval, &maxval, &minloc, &maxloc); cv::rectangle(find_field, cvPoint(minloc.x, minloc.y), cvPoint(minloc.x + roi.width - 1, minloc.y + roi.height - 1), CV_RGB(255, 0, 0), 1, 8); if (antibot_state) { // Сохраняем координаты курсора POINT cur; GetCursorPos(&cur); // Центр квадрата найденной картинки int center_x = (minloc.x + minloc.x + roi.width - 1) / 2; int center_y = (minloc.y + minloc.y + roi.height - 1) / 2; // handle текущего активного окна HWND current_window = GetForegroundWindow(); // Выбираем окно с игрой и совершаем клик в центре найденного квадрата с картинкой SetForegroundWindow(hWindow); Sleep(200); SetCursorPos(windowRect.left + find_cropp.x + center_x, windowRect.top + find_cropp.y + center_y + 29); Sleep(200); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); Sleep(200); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); Sleep(200); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); // Возвращаем окно с которого ушли SetForegroundWindow(current_window); // Восстановливаем координаты курсора SetCursorPos(cur.x, cur.y); ShowCursor(TRUE); Sleep(2000); } // Для удобства подгона зоны в которой нужно искать паттерны можно вывести картинку imshow("window", resized); waitKey(1); DeleteObject(hBitmap); } ReleaseDC(NULL, hwndDC); DeleteDC(hdcMem); Скрины и описание: Спойлер Тут видим зеленый квадрат и красный. Подгоняем окно внутри игры так, чтобы "Пример" был в зеленом квадрате Подогнали и видим, что программа нашла такую-же картинку. Кстати, несмотря на то, что наш пример темнее чем остальные картинки и там заштрихован фон. В зависимости от позиции окна капчи внутри окна игры будет меняться и minval\maxval. Поэтому нужно следить за этим) Как только программа найдет картинку, выведет в лог сообщение и произведет необходимые действия Пока что быстродействие сего чуда хромает. Программа является лишь прототипом для облегчения жизни себе любимому). Код "грязноват", есть лишние моменты, можно было упростить, перевести в GRAY, уменьшить картинку, возможно даже привести к сравнению хэшей, и так далее. Жду критики\предложений. Возможно буду дорабатывать программу под разные виды капч и сделаю GUI к ней (это не точно). Может кому пригодится) Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 13 мая, 2020 Поделиться Опубликовано 13 мая, 2020 Посмотри в сторону Sikuli IDE. Там и без OpenCV такая решалка есть. Причем быстрая. Точнее, не решалка, а поисковик картинки по паттерну. И даже можно задать уровень точности совпадения. Ссылка на комментарий Поделиться на другие сайты Поделиться
edx Опубликовано 13 мая, 2020 Автор Поделиться Опубликовано 13 мая, 2020 12 минут назад, Xipho сказал: Посмотри в сторону Sikuli IDE. Там и без OpenCV такая решалка есть. Причем быстрая. Точнее, не решалка, а поисковик картинки по паттерну. И даже можно задать уровень точности совпадения. Хм, спасибо очень даже интересно и код при чем пишется на Python'е, что много проще, возьму в копилку. upd: Видео, и статейка на хабре: Спойлер https://habr.com/ru/post/163883/ Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 14 мая, 2020 Поделиться Опубликовано 14 мая, 2020 Помню, для жены делал на Sikuli бота для игры браузерной ВКшной. В ней каждую минуту нужно было собирать ресурсы с домов. Фармить. Вот я и сделал бота, который ждал появления определенной картинки на экране и по ней кликал. Нормально она в сутки ресурсов поднимала )) Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения