Генератор аккаунтов
Воскресенье, 6. Февраль 2011
Раздел: C/C++, Софт, автор: dx
Пару дней назад в асю стукнул человек с просьбой написать программку для генерации списков невалидных аккаунтов, составляя их из паролей, логинов и доменов, которые он предоставит. Узнав, что дальнейшая судьба софта его не интересует, я решил выложить исходники в блог и немного прокомментировать их.
Для начала, возможности софта:
[+] Генерация заданного количества login@domain:password из заданных списков логинов, доменов и паролей.
[+] Возможность задать файл с префиксами и постфиксами для логинов и паролей и указать вероятности их использования в логине и пароле.
[+] Возможность задать разделитель логина и пароля.
[+] Фильтрация списков логинов и паролей по заданным регулярным выражениям.
[+] Возможность переводить логины в нижний регистр.
[+] Возможность включить проверку, чтобы повторные логины@домены не генерировались.
Все это написано на C++ с использованием замечательной бесплатной библиотеки boost.
Чтобы фанаты таких языков, как C# или VB, не сильно зазнавались, я привожу код этого софта с комментариями - всего 260 строчек. Он, к тому же, является переносимым и может быть скомпилирован под практически любую операционную систему.
Сначала подключаем необходимые файлы:
#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>
Теперь немного вспомогательных функций:
//функция, загружающая строки из потока в вектор 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; }
Создадим собственный класс исключений, чтобы кидать их в случае какой-либо ошибки:
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); }
Главная функция программы:
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; //проверять ли логины на повторы
Переходим к загрузке опций программы и списков логинов, паролей, префиксов, постфиксов и доменов:
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; }
Обработка исключений:
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; }
Теперь непосредственно подготовка к генерированию аккаунтов:
/* 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;
Основной цикл генерации:
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; }
Вот и всё. Осталось лишь разобрать формат конфигурационного файла. Вот пример:
#Список логинов 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.

GlooK :
А ты спросил на кой ему такой софт?
[Ответить]
dx:
Февраль 8th, 2011 at 22:51
Видимо, продавать аккаунты скупщикам невалида, зачем же еще)
[Ответить]
anonim:
Февраль 11th, 2011 at 14:26
и накой оно нужно этим скупщикам?
[Ответить]
Martin :
Мдя, фанаты C# и VB негодуют(впрочем как и фанаты С++) - такое они делают за пол часика, зачем сей кусок... мастерства выкладывать непонятно, все одно что Hello World написать использую для этого 3рд пати либы.
[Ответить]
dx:
Февраль 24th, 2011 at 15:00
буст это уже не 3рд пати либа, много чего из него включено в stl c++0x. Продолжайте негодовать, наркоманы .нетов)
[Ответить]
Origina1 :
может это както связанно с инвайтами
[Ответить]
AlexV :
DX, очень тонко подмечено)))
[Ответить]
megalodon :
=)
FaS тут как-то купил 1кк невалида из которого было нереально ничего выжать.
Я до этого также натыкался на 10к такого, взятого на пробу.
не удивлюсь, если знаю, кто заказчик.
[Ответить]
dr_vice :
Подозреваю что данный софт заказчику нужен для генерации списка для брута..
[Ответить]
k4 :
Данный софт нужен рипперам ,чтобы кидать всевозможных скупщиков дампов(
[Ответить]