yggm/sources/main.cpp
2025-02-28 11:37:30 +05:00

535 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Address miner for Yggdrsail Network 0.4.x and higher.
*
* developers: Vort, acetone, R4SAS, lialh4, filarius, orignal
* developers team, 2021 (c) GPLv3
*
*/
#include <sodium.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <array>
#include <string.h>
#include <memory.h>
#include <thread>
#include <regex>
#include <mutex>
#include <arpa/inet.h>
#include "cppcodec/base32_rfc4648.hpp"
//#include "miner.cu"
//std::time_t sygstartedin = std::time(NULL);
int countsize = 0;
uint64_t totalcount = 0;
uint64_t countfortune = 0;
bool newline = false;
std::mutex mtx;
const size_t KEYSIZE = 32;
const size_t ADDRIPV6 = 16;
typedef std::array<uint8_t, KEYSIZE> Key;
typedef std::array<uint8_t, ADDRIPV6> Address;
struct KeysBox {
Key PublicKey;
Key PrivateKey;
};
struct option {
unsigned int proc = 0; // количество потоков
int mode = 1; // режим майнинга
bool log = true; // логгирование
int high = 20; // начальная высота при майнинге: dec(20) == hex(14)
bool letsup = true; // повышение высоты при нахождении
bool mesh = false; // отображение meshname-доменов
bool fullkeys = true; // отображение секретного ключа в консоли в полном формате
std::string str = "aaaa";
std::string outputfile;
int sbt_size = 7; // 64b/8 = 8B, нумерация с нуля
bool sbt_alarm = false; // для симпатичного вывода предупреждения
};
static option conf;
int parameters(option& conf, std::string arg)
{
if (arg.find(" ") != std::string::npos) // Строка с пробелом, значит, ключ_значение
{
const size_t npos = -1; // std::string::npos с блэкджэком и путанами
int position = arg.find(" ");
std::istringstream ss(arg.substr(position + 1)); // Поток нужен для проверки корректности и конвертации
if (arg.find("--threads") != npos || arg.find("-t") != npos) {
ss >> conf.proc;
if (ss.fail()) return 1;
return 0;
}
if (arg.find("--pattern") != npos || arg.find("-p") != npos) {
ss >> conf.str;
if (ss.fail()) return 1;
return 0;
}
if (arg.find("--altitude") != npos || arg.find("-a") != npos) {
ss >> std::hex >> conf.high;
if (ss.fail()) return 1;
return 0;
}
}
else if (arg == "--ip" || arg == "-i") conf.mode = 0;
else if (arg == "--ip-high" || arg == "-ih") conf.mode = 2;
else if (arg == "--regexp" || arg == "-r") conf.mode = 3;
else if (arg == "--regexp-high" || arg == "-rh") conf.mode = 4;
else if (arg == "--mesh" || arg == "-m") conf.mode = 5;
else if (arg == "--mesh-regexp" || arg == "-mr") conf.mode = 6;
else if (arg == "--brute-force" || arg == "-b") conf.mode = 7;
else if (arg == "--increase-none" || arg == "-in") conf.letsup = false;
else if (arg == "--logging-none" || arg == "-ln") conf.log = false;
else if (arg == "--display-mesh" || arg == "-dm") conf.mesh = true;
else if (arg == "--full-pk" || arg == "-fp") conf.fullkeys = true;
else if (arg == "--threads" || arg == "-t") return 777; // Параметры, требующие значение
else if (arg == "--pattern" || arg == "-p") return 777;
else if (arg == "--altitude" || arg == "-a") return 777;
return 0;
}
void displayConfig() {
// из-за регулирования количества потоков и countsize вызов функции обязателен
unsigned short processor_count = std::thread::hardware_concurrency(); // кол-во процессоров
if (conf.proc == 0 || conf.proc > static_cast<unsigned int>(processor_count))
conf.proc = static_cast<unsigned int>(processor_count);
countsize = 80000 * conf.proc;
std::cout << " Threads: " << conf.proc << ", ";
if (conf.mode == 0)
std::cout << "IPv6 pattern (" << conf.str << "), ";
else if (conf.mode == 1)
{
std::cout << "high addresses (2" << std::setw(2) << std::setfill('0') <<
std::hex << conf.high << std::dec;
(conf.letsup != 0) ? std::cout << "++), " : std::cout << "+), ";
} else if (conf.mode == 2)
{
std::cout << "by pattern (" << conf.str << ") & high (2" <<
std::setw(2) << std::setfill('0') << std::hex << conf.high << std::dec;
(conf.letsup != 0) ? std::cout << "++), " : std::cout << "+), ";
} else if (conf.mode == 3)
std::cout << "IPv6 regexp (" << conf.str << "), ";
else if (conf.mode == 4)
{
std::cout << "IPv6 regexp (" << conf.str << ") & high (2" <<
std::setw(2) << std::setfill('0') << std::hex << conf.high << std::dec;
(conf.letsup != 0) ? std::cout << "++), " : std::cout << "+), ";
} else if (conf.mode == 5)
std::cout << "meshname pattern (" << conf.str << "), ";
else if (conf.mode == 6)
std::cout << "meshname regexp (" << conf.str << "), ";
else if (conf.mode == 7)
std::cout << "subnet brute force (" << conf.str << "/" << (conf.sbt_size + 1) * 8 << "), ";
if (conf.log)
std::cout << "logging to text file.";
else
std::cout << "console log only.";
if ((conf.mode == 5 || conf.mode == 6) && conf.mesh == 0)
conf.mesh = 1; // принудительно включаем отображение мешнейм-доменов при их майнинге
std::cout << std::endl << std::endl;
}
void testOutput()
{
if (conf.log)
{
if (conf.mode == 0)
conf.outputfile = "syg-ipv6-pattern.txt";
else if (conf.mode == 1)
conf.outputfile = "syg-ipv6-high.txt";
else if (conf.mode == 2)
conf.outputfile = "syg-ipv6-pattern-high.txt";
else if (conf.mode == 3)
conf.outputfile = "syg-ipv6-regexp.txt";
else if (conf.mode == 4)
conf.outputfile = "syg-ipv6-regexp-high.txt";
else if (conf.mode == 5)
conf.outputfile = "syg-meshname-pattern.txt";
else if (conf.mode == 6)
conf.outputfile = "syg-meshname-regexp.txt";
else if (conf.mode == 7)
conf.outputfile = "syg-subnet-brute-force.txt";
std::ifstream test(conf.outputfile);
if (!test)
{
test.close();
std::ofstream output(conf.outputfile);
output << "******************************************************\n"
<< "Change PublicKey and PrivateKey to your yggdrasil.conf\n"
<< "Windows: C:\\ProgramData\\Yggdrasil\\yggdrasil.conf\n"
<< "Debian: /etc/yggdrasil.conf\n"
<< "******************************************************\n";
output.close();
} else test.close();
}
}
std::string getBase32(const Address& rawAddr)
{
return static_cast<std::string>(cppcodec::base32_rfc4648::encode(rawAddr.data(), 16));
}
/**
* pickupStringForMeshname получает человекочитаемую строку
* типа fsdasdaklasdgdas.meship и возвращает значение, пригодное
* для поиска по meshname-строке: удаляет возможную доменную зону
* (всё после точки и саму точку), а также делает все буквы
* заглавными.
*/
std::string pickupStringForMeshname(std::string str) {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
size_t dotPos = str.find('.');
if (dotPos != std::string::npos) {
// Удаляем доменную зону
str.resize(dotPos);
}
return str;
}
/**
* pickupMeshnameForOutput получает сырое base32 значение
* типа KLASJFHASSA7979====== и возвращает meshname-домен:
* делает все символы строчными и удаляет паддинги ('='),
* а также добавляет доменную зону ".meship".
*/
std::string pickupMeshnameForOutput(std::string str)
{
for (auto it = str.begin(); it != str.end(); it++) // делаем все буквы строчными для вывода
*it = tolower(*it);
for (auto it = str.end(); *(it - 1) == '='; it--)
str.pop_back(); // удаляем символы '=' в конце адреса
return str + ".meship";
}
/**
* decodeMeshToIP получает строковое значение сырого base32
* кода типа KLASJFHASSA7979====== и возвращает IPv6-стринг.
*/
bool subnetCheck() {
if (conf.str[0] == '3') {
conf.str[0] = '2';
return true;
}
return false;
}
bool convertStrToRaw(const std::string& str, Address& array) {
return inet_pton(AF_INET6, str.c_str(), (void*)array.data());
}
KeysBox getKeyPair() {
KeysBox keys;
uint8_t sk[64];
crypto_sign_ed25519_keypair(keys.PublicKey.data(), sk);
memcpy(keys.PrivateKey.data(), sk, 32);
return keys;
}
std::string getAddress(const Address& rawAddr) {
char ipStrBuf[46];
inet_ntop(AF_INET6, rawAddr.data(), ipStrBuf, 46);
return std::string(ipStrBuf);
}
std::string decodeMeshToIP(const std::string& str) {
std::string mesh = pickupStringForMeshname(str);
mesh.append("======");
std::vector<uint8_t> raw = cppcodec::base32_rfc4648::decode(mesh);
if (raw.size() != 16) {
throw std::runtime_error("Invalid mesh string for IPv6 address.");
}
std::array<uint8_t, 16> rawAddr;
std::copy(raw.begin(), raw.end(), rawAddr.begin());
return std::string(getAddress(rawAddr));
}
std::string hexArrayToString(const uint8_t* bytes, int length) {
std::stringstream ss;
for (int i = 0; i < length; i++)
ss << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(bytes[i]);
return ss.str();
}
std::string keyToString(const Key& key) {
return hexArrayToString(key.data(), KEYSIZE);
}
void getRawAddress(int lErase, Key InvertedPublicKey, Address& rawAddr) {
++lErase;
const int bitsToShift = lErase % 8;
const int start = lErase / 8;
const int invSize = start + 15;
if (bitsToShift == 0) {
for (int i = start; i < invSize; ++i)
InvertedPublicKey[i] = InvertedPublicKey[i];
} else {
const int complementary = 8 - bitsToShift;
for (int i = start; i < start + 15; ++i) {
InvertedPublicKey[i] = static_cast<unsigned char>(
(InvertedPublicKey[i] << bitsToShift) |
(InvertedPublicKey[i + 1] >> complementary)
);
}
}
rawAddr[0] = 0x02;
rawAddr[1] = static_cast<unsigned char>(lErase - 1);
memcpy(&rawAddr[2], &InvertedPublicKey[start], 14);
}
Key bitwiseInverse(const Key& key) {
Key inverted{};
for (size_t i = 0; i < key.size(); ++i) inverted[i] = static_cast<unsigned char>(~key[i]);
return inverted;
}
int getOnes(const Key& value) {
int leadOnes = 0;
constexpr int nBytes = 17;
for (int i = 0; i < nBytes; ++i) {
if (value[i] == 0xFF) {
leadOnes += 8;
} else {
for (int j = 7; j >= 0; --j) {
if (value[i] & (1 << j))
++leadOnes;
else
return leadOnes;
}
}
}
return leadOnes;
}
void logKeys(const Address& raw, const KeysBox& keys) {
mtx.lock();
std::cout << std::endl;
if (conf.mesh) {
std::string base32 = getBase32(raw);
std::cout << " Domain: " << pickupMeshnameForOutput(base32) << std::endl;
}
std::cout << " Address: " << getAddress(raw) << std::endl;
std::cout << " PublicKey: " << keyToString(keys.PublicKey) << std::endl;
std::cout << " PrivateKey: " << keyToString(keys.PrivateKey);
if (!conf.log || conf.fullkeys) std::cout << keyToString(keys.PublicKey);
std::cout << std::endl << std::endl;
if (conf.log) {
std::ofstream output(conf.outputfile, std::ios::app);
output << std::endl;
if (conf.mesh) {
std::string base32 = getBase32(raw);
output << "Domain: " << pickupMeshnameForOutput(base32) << std::endl;
}
output << "Address: " << getAddress(raw) << std::endl;
output << "PublicKey: " << keyToString(keys.PublicKey) << std::endl;
output << "PrivateKey: " << keyToString(keys.PrivateKey) << keyToString(keys.PublicKey) << std::endl;
output.close();
}
mtx.unlock();
}
void process_fortune_key(const KeysBox& keys) {
Key invKey = bitwiseInverse(keys.PublicKey);
int ones = getOnes(invKey);
Address rawAddr;
getRawAddress(ones, invKey, rawAddr);
logKeys(rawAddr, keys);
++countfortune;
}
template <int T>
void miner_thread() {
Address rawForBrute;
Address rawAddr;
KeysBox keys;
Key invKey;
int ones;
std::string addressStr, base32Str;
bool shouldProcess;
if constexpr (T == 5) {
conf.str = pickupStringForMeshname(conf.str);
}
if constexpr (T == 7) {
{
std::lock_guard<std::mutex> lock(mtx);
std::string oldString = conf.str;
if (!convertStrToRaw(conf.str, rawForBrute) || subnetCheck() || conf.str != getAddress(rawForBrute)) {
if (!conf.sbt_alarm) {
std::cerr << " WARNING: Your string [" << oldString << "] converted to IP ["
<< getAddress(rawForBrute) << "]" << std::endl << std::endl;
}
conf.sbt_alarm = true;
}
}
}
std::regex regx(conf.str, std::regex_constants::egrep | std::regex_constants::icase);
while (true) {
keys = getKeyPair();
invKey = bitwiseInverse(keys.PublicKey);
ones = getOnes(invKey);
getRawAddress(ones, invKey, rawAddr);
addressStr = getAddress(rawAddr);
base32Str = getBase32(rawAddr);
shouldProcess = false;
if constexpr (T == 0) {
shouldProcess = (addressStr.find(conf.str) != std::string::npos);
} else if constexpr (T == 1) {
if (ones > conf.high) {
if (conf.letsup != 0)
conf.high = ones;
shouldProcess = true;
}
} else if constexpr (T == 2) {
if (ones > conf.high && addressStr.find(conf.str) != std::string::npos) {
if (conf.letsup != 0)
conf.high = ones;
shouldProcess = true;
}
} else if constexpr (T == 3) {
shouldProcess = std::regex_search(addressStr, regx);
} else if constexpr (T == 4) {
if (ones > conf.high && std::regex_search(addressStr, regx)) {
if (conf.letsup != 0)
conf.high = ones;
shouldProcess = true;
}
} else if constexpr (T == 5) {
shouldProcess = (base32Str.find(conf.str) != std::string::npos);
} else if constexpr (T == 6) {
shouldProcess = std::regex_search(base32Str, regx);
} else if constexpr (T == 7) {
for (size_t z = 0; z < rawForBrute.size() && rawForBrute[z] == rawAddr[z]; ++z) {
if (z > 4) {
if (z == static_cast<size_t>(conf.sbt_size)) {
shouldProcess = true;
break;
} else {
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << " So close! Bruted bytes: " << (z + 1) << "/" << (conf.sbt_size + 1) << std::endl;
}
}
}
}
}
if (shouldProcess) process_fortune_key(keys);
}
}
void startThreads() {
for (unsigned int i = 0; i < conf.proc; ++i) {
std::thread* thread = new std::thread(
conf.mode == 0 ? miner_thread<0> :
conf.mode == 1 ? miner_thread<1> :
conf.mode == 2 ? miner_thread<2> :
conf.mode == 3 ? miner_thread<3> :
conf.mode == 4 ? miner_thread<4> :
conf.mode == 5 ? miner_thread<5> :
conf.mode == 6 ? miner_thread<6> :
miner_thread<7>
);
if (i + 1 < conf.proc) thread->detach();
else thread->join();
}
}
void error(int code)
{
std::cerr << std::endl << "\
+--------------------------------------------------------------------------+\n\
| Incorrect input, my dear friend. Use --help for usage information. |\n\
+--------------------------------------------------------------------------+\n\
Error code: " << code << std::endl;
}
int main(int argc, char* argv[]) {
if (argc >= 2) {
std::string p1;
p1 = argv[1];
if (p1 == "--help" || p1 == "-help" || p1 == "-h") {
//help();
return 0;
} else if (p1 == "--tomesh" || p1 == "-tm") {
if (argc >= 3) {
Address rawAddr;
convertStrToRaw(argv[2], rawAddr);
std::string base32 = getBase32(rawAddr);
std::cout << std::endl << pickupMeshnameForOutput(base32) << std::endl;
return 0;
} else { error(-501); return -501; }
} else if (p1 == "--toip" || p1 == "-ti") {
if (argc >= 3) {
std::cout << std::endl << decodeMeshToIP(argv[2]) << std::endl;
return 0;
} else { error(-502); return -502; }
} else {
int res = -1;
for (int i = 1;; ++i) {
if (argv[i] == nullptr) break;
res = parameters(conf, std::string(argv[i]));
if (res == 777) {
++i;
if (argv[i] == nullptr) {
error(776);
std::cerr << " Empty value for parameter \"" << argv[i - 1] << "\"" << std::endl;
return 776;
}
int res2 = parameters(conf, std::string(std::string(argv[i - 1]) + " " + std::string(argv[i])));
if (res2 != 0) {
error(res);
std::cerr << " Wrong value \"" << argv[i] << "\" for parameter \"" << argv[i - 1] << "\"" << std::endl;
return res;
}
}
}
}
}
//else { without(); std::this_thread::sleep_for(std::chrono::seconds(1)); }
displayConfig();
//testOutput();
startThreads();
}
/*
void help()
{
std::cout << std::endl << "\
+--------------------------------------------------------------------------+\n\
| Simple Yggdrasil address miner usage: --help or -h |\n\
+--------------------------------------------------------------------------+\n\
[Mining modes] \n\
High addresses BY DEFAULT | \n\
IPv6 by pattern --ip | -i \n\
IPv6 by pattern + height --ip-high | -ih \n\
IPv6 by regular expression --regexp | -r \n\
IPv6 by regular expression + height --regexp-high | -rh \n\
Meshname by pattern --mesh | -m \n\
Meshname by regular expression --mesh-regexp | -mr \n\
Subnet brute force (300::/64) --brute-force | -b \n\
[Main parameters] \n\
Threads count (maximum by default) --threads | -t <value>\n\
String for pattern or regular expression --pattern | -p <value>\n\
Start position for high addresses (14 by default) --altitude | -a <value>\n\
[Extra options] \n\
Disable auto-increase in high mode --increase-none | -in \n\
Disable logging to text file, stdout only --logging-none | -ln \n\
Force display meshname domains --display-mesh | -dm \n\
Show PrivateKeys in full format in console --full-pk | -fp \n\
Show the version of the miner --version | -v \n\
[Meshname convertation] \n\
Convert IP to Meshname --tomesh | -tm <value>\n\
Convert Meshname to IP --toip | -ti <value>\n\
[Notes] \n\
Meshname domains use base32 (RFC4648) alphabet symbols. \n\
In meshname domain mining should use \"=\" instead \".meship\" or \".meshname\".\n\
Subnet brute force mode understand \"3xx:\" and \"2xx:\" patterns. \n\
+--------------------------------------------------------------------------+\n";
}
void without()
{
std::cout << "\
SimpleYggGen was started without parameters.\n\
The mining mode for high addresses will be launched automatically.\n\
Use --help for usage information."
<< std::flush;
}
*/