From 7b64aba9dad147a68a437280503d26bf7c22c5ce Mon Sep 17 00:00:00 2001 From: R4SAS Date: Mon, 17 Aug 2020 11:38:24 +0300 Subject: [PATCH] init restructured 1.1 release Signed-off-by: R4SAS --- .gitignore | 4 + Makefile | 32 +++ Makefile.mingw | 11 + README.md | 34 +++ sygcpp.cpp | 459 +++++++++++++++++++++++++++++++++++++++ windows/resource.rc | 36 +++ windows/syg-cpp-logo.ico | Bin 0 -> 15086 bytes 7 files changed, 576 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Makefile.mingw create mode 100644 README.md create mode 100644 sygcpp.cpp create mode 100644 windows/resource.rc create mode 100644 windows/syg-cpp-logo.ico diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eae497d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/obj +/sygcpp* +/syg-*.txt +/sygcpp.conf \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..212153e --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +SYS := $(shell $(CXX) -dumpmachine) +CXX = g++ + +SYG_SRC = sygcpp.cpp +SYGCPP = sygcpp + +LDFLAGS = -O3 -s +LDLIBS = -lcrypto -lpthread + +SYG_OBJS += $(patsubst %.cpp,obj/%.o,$(SYG_SRC)) + +ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) + include Makefile.mingw +endif + +all: mk_obj_dir $(SYGCPP) + +mk_obj_dir: + @mkdir -p obj/windows + +clean: + $(RM) -r obj $(SYGCPP) + +obj/%.o: %.cpp + $(CXX) -c -o $@ $< + +$(SYGCPP): $(SYG_OBJS) + $(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +.PHONY: all +.PHONY: clean +.PHONY: mk_obj_dir diff --git a/Makefile.mingw b/Makefile.mingw new file mode 100644 index 0000000..3cedfc1 --- /dev/null +++ b/Makefile.mingw @@ -0,0 +1,11 @@ +WINDRES = windres + +SYG_RC = windows/resource.rc + +LDFLAGS = -O3 -s -Wl,-Bstatic +LDLIBS = -static-libgcc -lcrypto -lws2_32 -lpthread + +SYG_OBJS += $(patsubst %.rc,obj/%.o,$(SYG_RC)) + +obj/%.o: %.rc + $(WINDRES) -i $< -o $@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..aad236b --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# SYGCPP + +### How build on windows under MSYS2 shell + +* Run MSYS2 MinGW 64-bit shell +* Install required packages + +```bash +pacman -S make mingw-w64-x86_64-gcc mingw-w64-x86_64-openssl +``` + +* Compile application + +```bash +make -f Makefile.mingw +``` + +* Run it using `sygcpp.exe` + +### How build on Linux + +* Install required packages + +``` +sudo apt-get install make g++ libssl-dev +``` + +* Compile application + +``` +make -f Makefile.linux +``` + +* Run it using `./sygcpp` diff --git a/sygcpp.cpp b/sygcpp.cpp new file mode 100644 index 0000000..fe9fe61 --- /dev/null +++ b/sygcpp.cpp @@ -0,0 +1,459 @@ +/** + * Thanks PurpleI2P Project for support to writing that code. + * + * IRC: irc.ilita.i2p port 6667 || 303:60d4:3d32:a2b9::3 port 16667 + * general channels: #ru and #howtoygg + * + * git: notabug.org/acetone/SimpleYggGen-CPP + * + * acetone, 2020 (c) GPLv3 + * + */ + +#include // библиотека OpenSSL +#include +#include +#include // вывод на экран +#include +#include +#include // файловые потоки +#include // форматированный вывод строк +#include // побитовое чтение +#include +#include // многопоточность +#include +#include // для паузы в заставке +#include + +////////////////////////////////////////////////// Заставка + +void intro() +{ + std::cout << std::endl + << " +----------------------------------------------------------------------------+" << std::endl + << " | SimpleYggGen C++ 1.1-train |" << std::endl + << " | OpenSSL inside: x25519 -> sha512 |" << std::endl + << " | notabug.org/acetone/SimpleYggGen-CPP |" << std::endl + << " | |" << std::endl + << " | developers: acetone, lialh4, orignal, R4SAS |" << std::endl + << " | GPLv3 (c) 2020 |" << std::endl + << " +"; + for(int i = 0; i < 76; ++i) + { + std::cout << "-"; + std::cout.flush(); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + std::cout << "+" << std::endl; +} + +////////////////////////////////////////////////// Суть вопроса + +#define KEYSIZE 32 +std::mutex mtx; + +int conf_proc = 0; +int conf_mode = 0; +int conf_log = 0; +int conf_high = 0; +std::string conf_search; +std::string log_file; + +uint64_t totalcount = 0; // счетчик основного цикла +uint64_t totalcountfortune = 0; // счетчик нахождений +bool newline = true; // используется для вывода счетчика + +int config() +{ + std::ifstream conffile ("sygcpp.conf"); + + if(!conffile) // проверка наличия конфига + { + std::cout << " Configuration file not found..." << std::endl; + conffile.close(); + std::ofstream newconf ("sygcpp.conf"); // создание конфига + if(!newconf) + { + std::cerr << " Config (sygcpp.conf) creation failed :(" << std::endl; + return -1; + } + newconf << "1 0 1 9 ::\n" + << "| | | | ^Pattern for search by name.\n" + << "| | | ^Start position for high addresses search.\n" + << "| | ^Logging mode (0 - console output only, 1 - log to file).\n" + << "| ^Mining mode (0 - by name, 1 - high address).\n" + << "^Count of thread (mining streams).\n\n" + << "Parameters are separated by spaces."; + newconf.close(); + std::ifstream conffile ("sygcpp.conf"); + if(conffile) + std::cout << " Config successfully created :)" << std::endl; + config(); + return 0; + } else { + conffile >> conf_proc >> conf_mode >> conf_log >> conf_high >> conf_search; + conffile.close(); + if(conf_mode > 1 || conf_mode < 0 || conf_log > 1 || conf_log < 0 || conf_high < 0) // проверка полученных значений + { + std::cerr << " Invalid config found!\n" + << " Check it:\n" + << " - 2 field - mining mode: 0 or 1 only\n" + << " - 3 field - logging mode: 0 or 1 only\n" + << " - 5 field - string to search by name: a-z, 0-9 and ':' symbols only\n" + << " Remove or correct sygcpp.conf and run SYG again."<< std::endl; + return -2; + } + + unsigned int processor_count = std::thread::hardware_concurrency(); // кол-во процессоров + if(conf_proc > (int)processor_count) + conf_proc = (int)processor_count; + } + + // вывод конфигурации на экран + std::cout << " Threads: " << conf_proc << ", "; + + if(conf_mode) + std::cout << "search high addresses (" << conf_high << "), "; + else + std::cout << "search by name (" << conf_search << "), "; + + if(conf_log) + std::cout << "logging to text file." << std::endl; + else + std::cout << "console log only." << std::endl; + + return 0; +} + +void testoutput() +{ + if(conf_log) // проверка включено ли логирование + { + if(conf_mode) + log_file = "syg-high.txt"; + else + log_file = "syg-byname.txt"; + + std::ifstream test(log_file); + if(!test) // проверка наличия выходного файла + { + test.close(); + std::ofstream output(log_file); + output << "**************************************************************************\n" + << "Change EncryptionPublicKey and EncryptionPrivateKey to your yggdrasil.conf\n" + << "Windows: C:\\ProgramData\\Yggdrasil\\yggdrasil.conf\n" + << "Debian: /etc/yggdrasil.conf\n\n" + << "Visit HowTo.Ygg wiki for more information (russian language page):\n" + << "http://[300:529f:150c:eafe::6]/doku.php?id=yggdrasil:simpleygggen_cpp\n" + << "**************************************************************************\n"; + output.close(); + } else test.close(); + } +} + +struct BoxKeys +{ + uint8_t PublicKey[KEYSIZE]; + uint8_t PrivateKey[KEYSIZE]; +}; + +BoxKeys getKeyPair() +{ + BoxKeys keys; + size_t len = KEYSIZE; + + EVP_PKEY_CTX * Ctx; + EVP_PKEY * Pkey = nullptr; + Ctx = EVP_PKEY_CTX_new_id (NID_X25519, NULL); + + EVP_PKEY_keygen_init (Ctx); + EVP_PKEY_keygen (Ctx, &Pkey); + + EVP_PKEY_get_raw_public_key (Pkey, keys.PublicKey, &len); + EVP_PKEY_get_raw_private_key (Pkey, keys.PrivateKey, &len); + + EVP_PKEY_CTX_free(Ctx); + EVP_PKEY_free(Pkey); + + return keys; +} + +int getOnes(const unsigned char HashValue[SHA512_DIGEST_LENGTH]) +{ + bool done = false; + int lOnes = 0; // кол-во лидирующих единиц + + std::vector> bytes; // вектор с однобайтовыми битсетами (двумерный массив) + for(int i = 0; i < 32; ++i) // всего 32 байта, т.к. лидирующих единиц больше быть не может (32*8 = 256 бит, а ff = 255) + bytes.push_back(HashValue[i]); // вставка в вектор с битсетами одного i-того байта хэша + + for(auto vector_count = bytes.begin(); vector_count != bytes.end() && !done; vector_count++) + { + for(int i = 7; i >= 0 && !done; --i) + { + if((*vector_count)[i] == 1) // обращение к i-тому элементу битсета + ++lOnes; + if((*vector_count)[i] == 0) + done = true; + } + } + return lOnes; +} + +std::string getAddress(unsigned char HashValue[SHA512_DIGEST_LENGTH]) +{ + // функция "портит" массив хэша, т.к. копирование массива не происходит + int lErase = getOnes(HashValue) + 1; // лидирующие единицы и первый ноль + + bool changeit = false; + int bigbyte = 0; + + for(int j = 0; j < lErase; ++j) // побитовое смещение + { + for(int i = 63; i >= 0; --i) + { + if(bigbyte == i+1) // предыдущий байт требует переноса + changeit = true; + + if(HashValue[i] & 0x80) + bigbyte = i; + + HashValue[i] <<= 1; + + if(changeit) + { + HashValue[i] |= 0x01; + changeit = false; + } + } + } + + std::string address; + bool shortadd = false; + std::stringstream ss(address); + ss << 0x02 << std::setw(2) << std::setfill('0') << std::hex << lErase - 1 << ":"; + // 2 - константа подсети Yggdrasil, второй байт - кол-во лидирующих единиц в хешэ + + for(int i = 0; i < 14; ++i) + { + if(i % 2 == 0) // если работаем с первым байтом секции + { + if(HashValue[i] == 0) // если байт нулевой + { + if(HashValue[i+1] == 0) // если следующий байт нулевой + { + if(HashValue[i+2] == 0 && i+2 < 13 && HashValue[i+3] == 0 && i+3 <= 13 && !shortadd) + { + ss << ":"; + i += 3; + shortadd = true; + continue; + } else { + ss << "0"; + ++i; + } + } + } else { + ss << std::hex << (int)HashValue[i]; + } + } else { // если работаем со вторым байтом секции + if(HashValue[i-1] == 0) // если предыдущий первый байт был нулевой, нули сокращаем + ss << std::hex << (int)HashValue[i]; + else + ss << std::setw(2) << std::setfill('0') << std::hex << (int)HashValue[i]; + } + if(i != 13 && i % 2 != 0) // не выводим двоеточие в конце адреса и после первого байта секции + ss << ":"; + } + return ss.str(); +} + +void getConsoleLog() +{ + mtx.lock(); + ++totalcount; + if(totalcount % 250000 == 0) + { + if(newline) + { + std::cout << std::endl; + newline = false; + } + std::time_t realtime = std::time(NULL); + + std::cout << " # count [ " << std::dec << std::setfill('.') << std::setw(19) << totalcount << " ] [ " + << std::setw(15) << totalcountfortune << " ] " << std::asctime(std::localtime(&realtime)); + std::cout.flush(); + } + mtx.unlock(); +} + +void highminer() +{ + unsigned char HashValue[SHA512_DIGEST_LENGTH]; + + uint8_t PublicKeyBest[KEYSIZE]; + uint8_t PrivateKeyBest[KEYSIZE]; + + while(true) + { + BoxKeys myKeys = getKeyPair(); + SHA512(myKeys.PublicKey, KEYSIZE, HashValue); + int newones = getOnes(HashValue); + + if(newones > conf_high) // сохранение лучших ключей + { + conf_high = newones; + for(int i = 0; i < KEYSIZE; ++i) + { + PublicKeyBest[i] = myKeys.PublicKey[i]; + PrivateKeyBest[i] = myKeys.PrivateKey[i]; + } + + std::string address = getAddress(HashValue); + mtx.lock(); + std::cout << "\n Address: " << address << std::endl; + std::cout << " PublicKey: "; + for(int i = 0; i < 32; ++i) + { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)PublicKeyBest[i]; + } + std::cout << std::endl; + + std::cout << " PrivateKey: "; + for(int i = 0; i < 32; ++i) + { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)PrivateKeyBest[i]; + } + std::cout << std::endl; + + if(conf_log) // запись в файл + { + std::ofstream output(log_file, std::ios::app); + output << "\nAddress: " << address << std::endl; + output << "EncryptionPublicKey: "; + for(int i = 0; i < 32; ++i) + { + output << std::setw(2) << std::setfill('0') << std::hex << (int)PublicKeyBest[i]; + } + output << std::endl; + + output << "EncryptionPrivateKey: "; + for(int i = 0; i < 32; ++i) + { + output << std::setw(2) << std::setfill('0') << std::hex << (int)PrivateKeyBest[i]; + } + output << std::endl; + output.close(); + } + ++totalcountfortune; + newline = true; + mtx.unlock(); + } + getConsoleLog(); + } // while(true) +} + +void nameminer() +{ + unsigned char HashValue[SHA512_DIGEST_LENGTH]; + + uint8_t PublicKeyBest[KEYSIZE]; + uint8_t PrivateKeyBest[KEYSIZE]; + + while(true) + { + BoxKeys myKeys = getKeyPair(); + SHA512(myKeys.PublicKey, KEYSIZE, HashValue); + std::string tempstr = getAddress(HashValue); + + if(tempstr.find(conf_search.c_str()) != std::string::npos) // сохранение найденных ключей + { + for(int i = 0; i < KEYSIZE; ++i) + { + PublicKeyBest[i] = myKeys.PublicKey[i]; + PrivateKeyBest[i] = myKeys.PrivateKey[i]; + } + mtx.lock(); + std::cout << "\n Address: " << tempstr << std::endl; + std::cout << " PublicKey: "; + for(int i = 0; i < 32; ++i) + { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)PublicKeyBest[i]; + } + std::cout << std::endl; + + std::cout << " PrivateKey: "; + for(int i = 0; i < 32; ++i) + { + std::cout << std::setw(2) << std::setfill('0') << std::hex << (int)PrivateKeyBest[i]; + } + std::cout << std::endl; + + if(conf_log) // запись в файл + { + std::ofstream output(log_file, std::ios::app); + output << "\nAddress: " << tempstr << std::endl; + output << "EncryptionPublicKey: "; + for(int i = 0; i < 32; ++i) + { + output << std::setw(2) << std::setfill('0') << std::hex << (int)PublicKeyBest[i]; + } + output << std::endl; + output << "EncryptionPrivateKey: "; + for(int i = 0; i < 32; ++i) + { + output << std::setw(2) << std::setfill('0') << std::hex << (int)PrivateKeyBest[i]; + } + output << std::endl; + output.close(); + } + ++totalcountfortune; + newline = true; + mtx.unlock(); + } + getConsoleLog(); + } +} + +// ------------------------------------------------------ +int main() +{ + intro(); + + int configcheck = config(); + if(configcheck < 0) // функция получения конфигурации + { + std::cerr << "Error code: " << configcheck << std::endl; + system("PAUSE"); + return configcheck; + } + + testoutput(); + if(conf_mode) // запуск соответствующего режима майнинга + { + std::thread * threads[conf_proc]; + for(int i = 0; i < conf_proc; ++i) + threads[i] = new std::thread(highminer); + + for(int i = 0; i < conf_proc - 1; ++i) + threads[i]->detach(); + + threads[conf_proc-1]->join(); // "ждем" последний трэд, бесконечное ожидание + } + else + { + std::thread * threads[conf_proc]; + for(int i = 0; i < conf_proc; ++i) + threads[i] = new std::thread(nameminer); + + for(int i = 0; i < conf_proc - 1; ++i) + threads[i]->detach(); + + threads[conf_proc-1]->join(); + } + + std::cerr << "SYG has stopped working unexpectedly! Please, report about this." << std::endl; + system("PAUSE"); + return -420; +} diff --git a/windows/resource.rc b/windows/resource.rc new file mode 100644 index 0000000..5fe1680 --- /dev/null +++ b/windows/resource.rc @@ -0,0 +1,36 @@ +#include + +#define MAINICON 101 + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +MAINICON ICON "syg-cpp-logo.ico" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,1,0,0 +PRODUCTVERSION 1,1,0,0 + +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "acetone" + VALUE "FileDescription", "SimpleYggGen" + VALUE "FileVersion", "1.1.0.0" + VALUE "LegalCopyright", "Copyright (C) 2020, acetone" + VALUE "OriginalFilename", "sygcpp" + VALUE "ProductName", "SimpleYggGen" + VALUE "ProductVersion", "1.1-train" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/windows/syg-cpp-logo.ico b/windows/syg-cpp-logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..56f16a5b2461c24f30e3906fc7cb2cbfee459b76 GIT binary patch literal 15086 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 at)_)yQ=rv6U%epybM43gbk5Zdg8u^HBPQtp literal 0 HcmV?d00001