Print This Post Генератор аккаунтов

Воскресенье, 6. Февраль 2011
Раздел: C/C++, Софт, автор:

Пару дней назад в асю стукнул человек с просьбой написать программку для генерации списков невалидных аккаунтов, составляя их из паролей, логинов и доменов, которые он предоставит. Узнав, что дальнейшая судьба софта его не интересует, я решил выложить исходники в блог и немного прокомментировать их.
Для начала, возможности софта:
[+] Генерация заданного количества 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.

Также рекомендую почитать

 Обсудить на форуме


Получать обновления на почту:     

Метки: , , , , , , .

Комментариев: 10 к “Генератор аккаунтов”

  1. А ты спросил на кой ему такой софт?

    [Ответить]

    dx:

    Видимо, продавать аккаунты скупщикам невалида, зачем же еще)

    [Ответить]

    anonim:

    и накой оно нужно этим скупщикам?

    [Ответить]


  2. Martin :

    Мдя, фанаты C# и VB негодуют(впрочем как и фанаты С++) - такое они делают за пол часика, зачем сей кусок... мастерства выкладывать непонятно, все одно что Hello World написать использую для этого 3рд пати либы.

    [Ответить]

    dx:

    буст это уже не 3рд пати либа, много чего из него включено в stl c++0x. Продолжайте негодовать, наркоманы .нетов)

    [Ответить]

  3. может это както связанно с инвайтами

    [Ответить]

  4. DX, очень тонко подмечено)))

    [Ответить]

  5. =)
    FaS тут как-то купил 1кк невалида из которого было нереально ничего выжать.
    Я до этого также натыкался на 10к такого, взятого на пробу.
    не удивлюсь, если знаю, кто заказчик.

    [Ответить]


  6. dr_vice :

    Подозреваю что данный софт заказчику нужен для генерации списка для брута..

    [Ответить]


  7. k4 :

    Данный софт нужен рипперам ,чтобы кидать всевозможных скупщиков дампов(

    [Ответить]


Оставьте ваш комментарий