Пару дней назад в асю стукнул человек с просьбой написать программку для генерации списков невалидных аккаунтов, составляя их из паролей, логинов и доменов, которые он предоставит. Узнав, что дальнейшая судьба софта его не интересует, я решил выложить исходники в блог и немного прокомментировать их.
Для начала, возможности софта:
[+] Генерация заданного количества login@domain:password из заданных списков логинов, доменов и паролей.
[+] Возможность задать файл с префиксами и постфиксами для логинов и паролей и указать вероятности их использования в логине и пароле.
[+] Возможность задать разделитель логина и пароля.
[+] Фильтрация списков логинов и паролей по заданным регулярным выражениям.
[+] Возможность переводить логины в нижний регистр.
[+] Возможность включить проверку, чтобы повторные логины@домены не генерировались.
Все это написано на C++ с использованием замечательной бесплатной библиотеки boost.
Чтобы фанаты таких языков, как C# или VB, не сильно зазнавались, я привожу код этого софта с комментариями - всего 260 строчек. Он, к тому же, является переносимым и может быть скомпилирован под практически любую операционную систему.
Сначала подключаем необходимые файлы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> #include <fstream> #include <string> #include <vector> #include <set> #include <exception> //будем генерировать качественный рандом с равномерным распределением #include <boost/random.hpp> //для чтения файла настроек #include <boost/program_options.hpp> //для измерения времени генерации #include <boost/progress.hpp> //регулярные выражения #include <boost/regex.hpp> //для перевода строк в нижний регистр #include <boost/algorithm/string.hpp> |
Теперь немного вспомогательных функций:
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 |
//функция, загружающая строки из потока в вектор void stream_to_vector(std::ifstream& stream, std::vector<std::string>& vec) { std::string temp; while(std::getline(stream, temp)) { if(!temp.empty()) vec.push_back(temp); } } //перегруженная функция для проверки строк по регулярному выражению и с возможностью перевода их в нижний регистр void stream_to_vector(std::ifstream& stream, std::vector<std::string>& vec, const boost::regex& regex, bool to_lower = false) { std::string temp; while(std::getline(stream, temp)) { if(!temp.empty() && boost::regex_match(temp, regex)) { if(to_lower) boost::to_lower(temp); vec.push_back(temp); } } } //функция загрузки контента из файла в вектор bool load_whatever(const std::string& file_name, std::vector<std::string>& load_to) { std::ifstream file; file.open(file_name.c_str()); if(!file.is_open()) return false; stream_to_vector(file, load_to); return true; } |
Создадим собственный класс исключений, чтобы кидать их в случае какой-либо ошибки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class common_exception : public std::runtime_error { public: explicit common_exception(const std::string& what) :std::runtime_error(what) {} virtual ~common_exception() throw() {} }; //и функцию, которая будет кидать заданное исключение void throw_if_true(const std::string& text, bool check = true) { if(check) throw common_exception(text); } |
Главная функция программы:
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 |
int main() { namespace po = boost::program_options; //с помощью boost::program_options можно удобно загрузить все настройки из конфиг-файла //(ниже я проведу пример, каким он должен быть) //добавляем список опций, их типы и описания po::options_description desc("Allowed options"); desc.add_options() ("logins", po::value<std::string>(), "Login list file") ("domains", po::value<std::string>(), "Domain list file") ("prefixes", po::value<std::string>(), "Login prefixes list file") ("postfixes", po::value<std::string>(), "Login postfixes list file") ("passwords", po::value<std::string>(), "Passwords list file") ("prefix_postfix_prob", po::value<unsigned int>(), "Login prefix/postfix usage probability") ("pass_prefix_postfix_prob", po::value<unsigned int>(), "Login prefix/postfix usage probability in passwords") ("delimiter", po::value<std::string>(), "Login-password delimiter") ("count", po::value<unsigned int>(), "Generate count") ("output", po::value<std::string>(), "Output file") ("login_regex", po::value<std::string>(), "Login validation regex") ("login_lower", po::value<bool>(), "Make logins lowercase") ("login_check", po::value<bool>(), "Skip similar logins@domains, if any") ("password_regex", po::value<std::string>(), "Password validation regex") ; po::variables_map vm; //загруженные опции std::vector<std::string> logins, passwords, prefixes, postfixes, domains; //списки std::string delimiter, output; //разделитель логина-пароля и имя файла результата unsigned int count, prefix_postfix_prob, pass_prefix_postfix_prob; //вероятности, число аккаунтов для генерации boost::regex login_regex, password_regex; //регулярные выражения для проверки логинов и паролей при их загрузке bool login_check; //проверять ли логины на повторы |
Переходим к загрузке опций программы и списков логинов, паролей, префиксов, постфиксов и доменов:
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 |
try { //загружаем опции из файла конфигурации po::store(po::parse_config_file<char>("./generator.conf", desc), vm); po::notify(vm); //конвертируем некоторые опции и сохраняем их в программе login_check = vm["login_check"].as<bool>(); delimiter = vm["delimiter"].as<std::string>(); output = vm["output"].as<std::string>(); count = vm["count"].as<unsigned int>(); prefix_postfix_prob = vm["prefix_postfix_prob"].as<unsigned int>(); pass_prefix_postfix_prob = vm["pass_prefix_postfix_prob"].as<unsigned int>(); //загружаем регулярные выражения и проверяем их корректность try { password_regex = boost::regex(vm["password_regex"].as<std::string>()); } catch(const boost::bad_expression&) { throw_if_true("Invalid login regex"); } try { login_regex = boost::regex(vm["login_regex"].as<std::string>()); } catch(const boost::bad_expression&) { throw_if_true("Invalid password regex"); } //Проверяем, не 0 ли аккаунтов генерировать, и верные ли вероятности использования префиксов и постфиксов указаны throw_if_true("Zero generate count specified", count == 0); throw_if_true("Incorrect probabilities (needed: 0-100, int)", prefix_postfix_prob > 100 || pass_prefix_postfix_prob > 100); //Загружаем логины std::cout << "Loading logins... "; { std::ifstream loginsf; loginsf.open(vm["logins"].as<std::string>().c_str()); throw_if_true("Cannot open login list", !loginsf.is_open()); stream_to_vector(loginsf, logins, login_regex, vm["login_lower"].as<bool>()); //Проверяем, не 0 ли логинов загрузилось throw_if_true("Login list is empty", logins.empty()); } //Загружаем список доменов и проверяем их количество std::cout << logins.size() << " OK" << std::endl << "Loading domains... "; throw_if_true("Cannot open domain list", !load_whatever(vm["domains"].as<std::string>(), domains)); throw_if_true("Domain list is empty", domains.empty()); std::cout << domains.size() << " OK" << std::endl << "Loading passwords... "; //Загружаем список паролей и проверяем их количество { std::ifstream passwordsf; passwordsf.open(vm["passwords"].as<std::string>().c_str()); throw_if_true("Cannot open password list", !passwordsf.is_open()); stream_to_vector(passwordsf, passwords, password_regex); throw_if_true("Password list is empty", passwords.empty()); } std::cout << passwords.size() << " OK" << std::endl << "Loading postfixes... "; //Загружаем список постфиксов и проверяем их количество //Если файл постфиксов пуст, добавляем один постфикс, являющийся пустой строкой. //Это позволит избежать лишних проверок далее throw_if_true("Cannot open postfix list", !load_whatever(vm["postfixes"].as<std::string>(), postfixes)); if(postfixes.empty()) postfixes.push_back(""); std::cout << "OK" << std::endl << "Loading prefixes... "; //С префиксами - аналогично throw_if_true("Cannot open prefix list", !load_whatever(vm["prefixes"].as<std::string>(), prefixes)); if(prefixes.empty()) prefixes.push_back(""); std::cout << "OK" << std::endl; } |
Обработка исключений:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
catch(const boost::program_options::error& e) { //Если не удалось прочитать файл настроек, выведем ошибку и помощь по настройкам std::cout << e.what() << std::endl; desc.print(std::cout); return 0; } catch(const std::bad_cast&) { //Если в файле настроек найдены параметры неверных типов, выведем помощь по настройкам desc.print(std::cout); return 0; } catch(const common_exception& e) { //Ошибка во время выполнения - выведем ее и выйдем из программы std::cout << e.what() << std::endl; return 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 |
/* The specializations mt11213b and mt19937 are from "Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator", Makoto Matsumoto and Takuji Nishimura, ACM Transactions on Modeling and Computer Simulation: Special Issue on Uniform Random Number Generation, Vol. 8, No. 1, January 1998, pp. 3-30. */ boost::mt19937 rng; rng.seed(static_cast<unsigned int>(std::time(0))); //рандомизируем //Подготавливаем генераторы рандомов для каждого вектора и также генератор от 1 до 100 boost::uniform_int<unsigned int> login_range(0, logins.size() - 1), password_range(0, passwords.size() - 1), prefix_range(0, prefixes.size() - 1), postfix_range(0, postfixes.size() - 1), domain_range(0, domains.size() - 1), probability_range(1, 100); boost::variate_generator<boost::mt19937&, boost::uniform_int<unsigned int> > get_random_login(rng, login_range), get_random_password(rng, password_range), get_random_prefix(rng, prefix_range), get_random_postfix(rng, postfix_range), get_random_domain(rng, domain_range), get_percent(rng, probability_range); //Создаем или открываем файл для сгенерированных аккаунтов std::ofstream result; result.open(output.c_str(), std::ios::out | std::ios::app); if(!result.is_open()) { std::cout << "Cannot open/create output file" << std::endl; return 0; } //Начало генерации std::cout << "Working..." << std::endl; boost::timer timer; std::set<std::string> generated_logins; std::string current_login; unsigned int generated = 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 |
for(unsigned int i = 0; i < count; i++) { current_login.clear(); //Генерируем новый логин и сохраняем его во временную строку //Если вероятность сработала, добавляем к логину префикс if(prefix_postfix_prob >= get_percent()) current_login.assign(prefixes[get_random_prefix()]); current_login.append(logins[get_random_login()]); //Если вероятность сработала, добавляем к логину постфикс if(prefix_postfix_prob >= get_percent()) current_login.append(postfixes[get_random_postfix()]); //добавляем собаку и домен current_login.push_back('@'); current_login.append(domains[get_random_domain()]); if(login_check) { //Если включена проверка на повторные логины if(!generated_logins.insert(current_login).second) continue; } //Увеличиваем счетчик сгенерированных аккаунтов на 1 generated++; //Добавляем разделитель result << current_login << delimiter; //Аналогичные логину манипуляции с паролем if(pass_prefix_postfix_prob >= get_percent()) result << prefixes[get_random_prefix()]; result << passwords[get_random_password()]; if(pass_prefix_postfix_prob >= get_percent()) result << postfixes[get_random_postfix()]; result << std::endl; } //Выводим количество сгенерированных аккаунтов, время генерации и выходим std::cout << "Done in " << timer.elapsed() << " secs, generated: " << generated << "." << std::endl; return 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 |
#Список логинов logins = logins.txt #Список префиксов prefixes = prefixes.txt #Список постфиксов postfixes = postfixes.txt #Список паролей passwords = passwords.txt #Список доменов domains = domains.txt #Вероятность использования префиксов и постфиксов в логинах prefix_postfix_prob = 30 #Вероятность использования префиксов и постфиксов в паролях pass_prefix_postfix_prob = 20 #Разделитель логина и пароля delimiter = : #Регулярное выражение для проверки логина login_regex = ^[a-z0-9A-Z_\.]+$ #Пропускать ли повторные логины@домены, если такие сгенерируются login_check = true #Регулярное выражение для проверки пароля password_regex = ^.+$ #Переводить логины в нижний регистр login_lower = true #Количество аккаунтов для генерации count = 100000 #Файл результата output = result.txt |
Замечу, что префиксы и постфиксы не проверяются.
Скачать программу (exe, код, примеры списков и конфига): ZIP
Upd: Несколько дней назад были обновлены библиотека для работы с http на masm32, универсальный конвертер и People search.
А ты спросил на кой ему такой софт?
Видимо, продавать аккаунты скупщикам невалида, зачем же еще)
и накой оно нужно этим скупщикам?
Мдя, фанаты C# и VB негодуют(впрочем как и фанаты С++) - такое они делают за пол часика, зачем сей кусок... мастерства выкладывать непонятно, все одно что Hello World написать использую для этого 3рд пати либы.
буст это уже не 3рд пати либа, много чего из него включено в stl c++0x. Продолжайте негодовать, наркоманы .нетов)
может это както связанно с инвайтами
DX, очень тонко подмечено)))
=)
FaS тут как-то купил 1кк невалида из которого было нереально ничего выжать.
Я до этого также натыкался на 10к такого, взятого на пробу.
не удивлюсь, если знаю, кто заказчик.
Подозреваю что данный софт заказчику нужен для генерации списка для брута..
Данный софт нужен рипперам ,чтобы кидать всевозможных скупщиков дампов(