diff --git a/BigInteger.hpp b/BigInteger.hpp index 11efac2..b5d3b70 100644 --- a/BigInteger.hpp +++ b/BigInteger.hpp @@ -60,6 +60,10 @@ public: mpz_swap(_Value, Other._Value); } + ~BigInteger() noexcept { + mpz_clear(_Value); + } + BigInteger& operator=(const BigInteger& Other) noexcept { if (this != &Other) { mpz_set(_Value, Other._Value); @@ -70,7 +74,6 @@ public: BigInteger& operator=(BigInteger&& Other) noexcept { if (this != &Other) { mpz_swap(_Value, Other._Value); - mpz_clear(Other._Value); } return *this; } @@ -90,7 +93,7 @@ public: } BigInteger& operator=(const char* lpszValue) noexcept { - mpz_init_set_str(_Value, lpszValue, 0); + mpz_set_str(_Value, lpszValue, 0); return *this; } @@ -265,7 +268,7 @@ public: return *this; } - BigInteger& Load(bool IsNegative, const std::vector Buffer, bool UseLittleEndian) noexcept { + BigInteger& Load(bool IsNegative, const std::vector& Buffer, bool UseLittleEndian) noexcept { mpz_import(_Value, Buffer.size(), UseLittleEndian ? -1 : 1, sizeof(uint8_t), 0, 0, Buffer.data()); if (IsNegative) mpz_neg(_Value, _Value); diff --git a/EllipticCurveGF2m.hpp b/EllipticCurveGF2m.hpp index 9542786..7c9dcb6 100644 --- a/EllipticCurveGF2m.hpp +++ b/EllipticCurveGF2m.hpp @@ -54,7 +54,10 @@ public: } Point operator-() const noexcept { - return Point(_X, _X + _Y); + Point Result(_Curve); + Result._X = _X; + Result._Y = _X + _Y; + return Result; } Point& operator=(const Point& Other) { @@ -139,30 +142,34 @@ public: if (&_Curve == &Other._Curve || _Curve == Other._Curve) { if (IsAtInfinity()) { return Other; - } else { - if (this == &Other || _X == Other._X) { + } else if (Other.IsAtInfinity()) { + return *this; + } else if (_X == Other._X) { + if (this == &Other || _Y == Other._Y) { return ValueOfDouble(); } else { - Point Result(_Curve); - - // m = (Y0 + Y1) / (X0 + X1) - auto m = (_Y + Other._Y) / (_X + Other._X); - - // NewX = m ^ 2 + m + X0 + X1 + a - Result._X = m.SquareValue(); - Result._X += m; - Result._X += _X; - Result._X += Other._X; - Result._X += _Curve._A; - - // NewY = m * (X0 + NewX) + NewX + Y0 - Result._Y = _X + Result._X; - Result._Y *= m; - Result._Y += Result._X; - Result._Y += _Y; - - return Result; + return Point(_Curve); } + } else { + Point Result(_Curve); + + // m = (Y0 + Y1) / (X0 + X1) + auto m = (_Y + Other._Y) / (_X + Other._X); + + // NewX = m ^ 2 + m + X0 + X1 + a + Result._X = m.SquareValue(); + Result._X += m; + Result._X += _X; + Result._X += Other._X; + Result._X += _Curve._A; + + // NewY = m * (X0 + NewX) + NewX + Y0 + Result._Y = _X + Result._X; + Result._Y *= m; + Result._Y += Result._X; + Result._Y += _Y; + + return Result; } } else { throw std::invalid_argument("Not on the same curve."); @@ -174,30 +181,33 @@ public: if (IsAtInfinity()) { _X = Other._X; _Y = Other._Y; - } else { - if (this == &Other || _X == Other._X) { + } else if (Other.IsAtInfinity()) { + // *this unchanged + } else if (_X == Other._X) { + if (this == &Other || _Y == Other._Y) { Double(); } else { - Point Result(_Curve); - - // m = (Y0 + Y1) / (X0 + X1) - auto m = (_Y + Other._Y) / (_X + Other._X); - - // NewX = m ^ 2 + m + X0 + X1 + a - __FieldType NewX = m.SquareValue(); - NewX += m; - NewX += _X; - NewX += Other._X; - NewX += _Curve._A; - - // NewY = m * (X0 + NewX) + NewX + Y0 - _X += NewX; - _X *= m; - _X += NewX; - _Y += _X; - - _X = NewX; + _X = __FieldType(); + _Y = __FieldType(); } + } else { + // m = (Y0 + Y1) / (X0 + X1) + auto m = (_Y + Other._Y) / (_X + Other._X); + + // NewX = m ^ 2 + m + X0 + X1 + a + __FieldType NewX = m.SquareValue(); + NewX += m; + NewX += _X; + NewX += Other._X; + NewX += _Curve._A; + + // NewY = m * (X0 + NewX) + NewX + Y0 + _X += NewX; + _X *= m; + _X += NewX; + _Y += _X; + + _X = NewX; } return *this; } else { @@ -229,7 +239,7 @@ public: return Result; } - Point operator*=(const BigInteger N) noexcept { + Point& operator*=(const BigInteger N) noexcept { Point Result(_Curve); size_t bit_length = N.BitLength(); @@ -240,6 +250,7 @@ public: } *this = Result; + return *this; } // SEC 1: Elliptic Curve Cryptography diff --git a/WinRarKeygen.hpp b/WinRarKeygen.hpp index 4500ea9..e8aa1ab 100644 --- a/WinRarKeygen.hpp +++ b/WinRarKeygen.hpp @@ -94,7 +94,11 @@ private: static BigInteger GenerateRandomInteger() { uint16_t RawRandomInteger[15]; - srand(static_cast(time(nullptr))); + static bool seeded = false; + if (!seeded) { + srand(static_cast(time(nullptr))); + seeded = true; + } for (size_t i = 0; i < 15; ++i) { RawRandomInteger[i] = static_cast(rand()); } @@ -226,7 +230,7 @@ public: CalculateChecksum(RegInfo); RegInfo.HexData = HelperStringFormat( - "%zd%zd%zd%zd%s%s%s%s%010lu", + "%zu%zu%zu%zu%s%s%s%s%010lu", RegInfo.Items[0].length(), RegInfo.Items[1].length(), RegInfo.Items[2].length(), diff --git a/_tmain.cpp b/_tmain.cpp index 636df38..14b2cb6 100644 --- a/_tmain.cpp +++ b/_tmain.cpp @@ -28,12 +28,113 @@ std::wstring Utf8ToWide(const std::string& str) { return result; } -void Help() { - std::wcout << L"Please use the following command:\n"; +std::string WideToAnsi(const std::wstring& wstr) { + if (wstr.empty()) return {}; + BOOL usedDefaultChar = FALSE; + int size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr.data(), (int)wstr.size(), + nullptr, 0, nullptr, &usedDefaultChar); + if (size == 0 || usedDefaultChar) { + throw std::runtime_error( + "Input contains characters not representable in the current ANSI code page. " + "Use '-e utf8' for Unicode characters."); + } + std::string result(size, 0); + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr.data(), (int)wstr.size(), + &result[0], size, nullptr, nullptr); + return result; +} + +enum class Encoding { ASCII, ANSI, UTF8 }; + +struct Options { + std::wstring username; + std::wstring license; + Encoding encoding = Encoding::UTF8; + std::wstring outputFile = L"rarreg.key"; + bool textOnly = false; + bool activate = false; + bool showVersion = false; + bool showHelp = false; +}; + +bool ParseArguments(int argc, wchar_t* argv[], Options& opts) { + std::vector positional; + for (int i = 1; i < argc; ++i) { + std::wstring arg = argv[i]; + if (arg == L"-v" || arg == L"--version" || _wcsicmp(arg.c_str(), L"ver") == 0) { + opts.showVersion = true; return true; + } + if (arg == L"-h" || arg == L"--help" || _wcsicmp(arg.c_str(), L"help") == 0) { + opts.showHelp = true; return true; + } + if (arg == L"-t" || arg == L"--text") { + opts.textOnly = true; continue; + } + if (arg == L"-a" || arg == L"--activate") { + opts.activate = true; continue; + } + if (arg == L"-e" || arg == L"--encoding") { + if (++i >= argc) { + std::wcerr << L"Error: Missing value for " << arg << L"\n"; + return false; + } + std::wstring val = argv[i]; + if (_wcsicmp(val.c_str(), L"ascii") == 0) opts.encoding = Encoding::ASCII; + else if (_wcsicmp(val.c_str(), L"ansi") == 0) opts.encoding = Encoding::ANSI; + else if (_wcsicmp(val.c_str(), L"utf8") == 0 || _wcsicmp(val.c_str(), L"utf-8") == 0) + opts.encoding = Encoding::UTF8; + else { + std::wcerr << L"Error: Unknown encoding '" << val << L"'. Use: ascii, ansi, utf8\n"; + return false; + } + continue; + } + if (arg == L"-o" || arg == L"--output") { + if (++i >= argc) { + std::wcerr << L"Error: Missing value for " << arg << L"\n"; + return false; + } + opts.outputFile = argv[i]; continue; + } + if (!arg.empty() && arg[0] == L'-') { + std::wcerr << L"Error: Unknown option '" << arg << L"'\n"; + return false; + } + positional.push_back(arg); + } + if (positional.size() == 2) { + opts.username = positional[0]; + opts.license = positional[1]; + return true; + } + if (positional.empty()) { + opts.showHelp = true; return true; + } + std::wcerr << L"Error: Expected 2 arguments (Username, LicenseName), got " + << positional.size() << L"\n"; + return false; +} + +void ShowHelp(const std::wstring& version) { + std::wcout << L"WinRAR Keygen v" << version << L"\n\n"; std::wcout << L"Usage:\n"; - std::wcout << L" winrar-keygen.exe \n\n"; - std::wcout << L"Example:\n"; - std::wcout << L" winrar-keygen.exe \"GitHub\" \"Single PC usage license\"\n"; + std::wcout << L" winrar-keygen.exe [options]\n"; + std::wcout << L" winrar-keygen.exe -v | --version\n"; + std::wcout << L" winrar-keygen.exe -h | --help\n\n"; + std::wcout << L"Options:\n"; + std::wcout << L" -e, --encoding utf8 (default), ascii, ansi\n"; + std::wcout << L" -o, --output Output file (default: rarreg.key)\n"; + std::wcout << L" -a, --activate Write to %%APPDATA%%\\WinRAR\\rarreg.key\n"; + std::wcout << L" -t, --text Print to console only, don't write file\n"; + std::wcout << L" -v, --version Show version\n"; + std::wcout << L" -h, --help Show this help\n\n"; + std::wcout << L"Examples:\n"; + std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\"\n"; + std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -e ansi\n"; + std::wcout << L" winrar-keygen.exe \"YourName\" \"Single PC usage license\" -e utf8\n"; + std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -o \"D:\\keys\\rarreg.key\"\n"; + std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" --activate\n"; + std::wcout << L" winrar-keygen.exe \"Github\" \"Single PC usage license\" -t\n"; } std::wstring GetExecutableVersion() { @@ -69,15 +170,14 @@ std::wstring GetExecutableVersion() { std::to_wstring(LOWORD(fixedInfo->dwFileVersionLS)); } -void PrintRegisterInfo(const WinRarKeygen::RegisterInfo& Info) { - std::wstring user = Utf8ToWide(Info.UserName); - std::wstring license = Utf8ToWide(Info.LicenseType); +void PrintRegisterInfo(const WinRarKeygen::RegisterInfo& Info, + const std::wstring& wUser, const std::wstring& wLicense) { std::wstring uid = Utf8ToWide(Info.UID); std::wstring data = Utf8ToWide(Info.HexData); std::wcout << L"RAR registration data\n"; - std::wcout << user << L"\n"; - std::wcout << license << L"\n"; + std::wcout << wUser << L"\n"; + std::wcout << wLicense << L"\n"; std::wcout << L"UID=" << uid << L"\n"; for (size_t i = 0; i < data.length(); i += 54) { @@ -85,6 +185,27 @@ void PrintRegisterInfo(const WinRarKeygen::RegisterInfo& Info) { } } +std::string BuildRegFileContent(const WinRarKeygen::RegisterInfo& Info) { + std::string s; + s += "RAR registration data\r\n"; + s += Info.UserName + "\r\n"; + s += Info.LicenseType + "\r\n"; + s += "UID=" + Info.UID + "\r\n"; + for (size_t i = 0; i < Info.HexData.length(); i += 54) { + s += Info.HexData.substr(i, 54) + "\r\n"; + } + return s; +} + +bool WriteRegFile(const std::wstring& filePath, const std::string& content) { + FILE* fp = nullptr; + if (_wfopen_s(&fp, filePath.c_str(), L"wb") != 0 || !fp) + return false; + size_t written = fwrite(content.data(), 1, content.size(), fp); + fclose(fp); + return written == content.size(); +} + bool IsConsoleHandle(HANDLE handle) { if (handle == nullptr || handle == INVALID_HANDLE_VALUE) { return false; @@ -97,25 +218,23 @@ void ConfigureConsoleOutput() { HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE stderrHandle = GetStdHandle(STD_ERROR_HANDLE); - int mode; - // console use UTF-16, file use UTF-8 if (IsConsoleHandle(stdoutHandle)) { - if ((mode = _setmode(_fileno(stdout), _O_WTEXT)) == -1) + if (_setmode(_fileno(stdout), _O_WTEXT) == -1) std::wcerr << L"Failed to set stdout _O_WTEXT\n"; } else { // stdout is redirected to a file, use UTF-8 - if ((mode = _setmode(_fileno(stdout), _O_U8TEXT)) == -1) + if (_setmode(_fileno(stdout), _O_U8TEXT) == -1) std::wcerr << L"Failed to set stdout _O_U8TEXT\n"; } if (IsConsoleHandle(stderrHandle)) { - if ((mode = _setmode(_fileno(stderr), _O_WTEXT)) == -1) + if (_setmode(_fileno(stderr), _O_WTEXT) == -1) std::wcerr << L"Failed to set stderr _O_WTEXT\n"; } else { - if ((mode = _setmode(_fileno(stderr), _O_U8TEXT)) == -1) + if (_setmode(_fileno(stderr), _O_U8TEXT) == -1) std::wcerr << L"Failed to set stderr _O_U8TEXT\n"; } } @@ -123,26 +242,115 @@ void ConfigureConsoleOutput() { int wmain(int argc, wchar_t* argv[]) { ConfigureConsoleOutput(); - if (argc == 2 && - (_wcsicmp(argv[1], L"ver") == 0 || _wcsicmp(argv[1], L"--version") == 0 || _wcsicmp(argv[1], L"-v") == 0)) { - std::wcout << GetExecutableVersion() << L"\n"; + Options opts; + if (!ParseArguments(argc, argv, opts)) { + return -1; } - else if (argc == 3) { - try { - std::string user = WideToUtf8(argv[1]); - std::string license = WideToUtf8(argv[2]); - PrintRegisterInfo( - WinRarKeygen::GenerateRegisterInfo(user.c_str(), license.c_str()) - ); - } - catch (std::exception& e) { - std::wcerr << L"Error: " << Utf8ToWide(e.what()) << L"\n"; + std::wstring version = GetExecutableVersion(); + + if (opts.showVersion) { + std::wcout << L"winrar-keygen v" << version << L"\n"; + return 0; + } + if (opts.showHelp) { + ShowHelp(version); + return 0; + } + + if (opts.activate && opts.outputFile != L"rarreg.key") { + std::wcerr << L"Error: --activate and -o cannot be used together.\n"; + return -1; + } + if (opts.activate && opts.textOnly) { + std::wcerr << L"Error: --activate and -t cannot be used together.\n"; + return -1; + } + + if (opts.activate) { + wchar_t appdata[MAX_PATH] = {}; + DWORD len = ExpandEnvironmentStringsW(L"%APPDATA%\\WinRAR", appdata, MAX_PATH); + if (len == 0 || len > MAX_PATH) { + std::wcerr << L"Error: Failed to resolve %%APPDATA%% path.\n"; return -1; } + CreateDirectoryW(appdata, nullptr); + opts.outputFile = std::wstring(appdata) + L"\\rarreg.key"; } - else { - Help(); + + try { + if (opts.username.empty() || opts.license.empty()) { + std::wcerr << L"Error: Username and License Name must not be empty.\n"; + return -1; + } + if (opts.username.length() > 200 || opts.license.length() > 200) { + std::wcerr << L"Error: Username and License Name must not exceed 200 characters.\n"; + return -1; + } + + std::wstring wDisplayUser = opts.username; + std::wstring wDisplayLicense = opts.license; + std::string user, license; + + switch (opts.encoding) { + case Encoding::UTF8: { + auto hasNonAscii = [](const std::wstring& s) { + for (wchar_t c : s) + if (static_cast(c) > 127) return true; + return false; + }; + if (hasNonAscii(wDisplayUser) && + (wDisplayUser.length() < 5 || wDisplayUser.substr(0, 5) != L"utf8:")) + wDisplayUser = L"utf8:" + wDisplayUser; + if (hasNonAscii(wDisplayLicense) && + (wDisplayLicense.length() < 5 || wDisplayLicense.substr(0, 5) != L"utf8:")) + wDisplayLicense = L"utf8:" + wDisplayLicense; + user = WideToUtf8(wDisplayUser); + license = WideToUtf8(wDisplayLicense); + break; + } + case Encoding::ANSI: + user = WideToAnsi(wDisplayUser); + license = WideToAnsi(wDisplayLicense); + break; + case Encoding::ASCII: + default: + user = WideToAnsi(wDisplayUser); + license = WideToAnsi(wDisplayLicense); + for (unsigned char c : user) + if (c > 127) + throw std::runtime_error( + "Username contains non-ASCII characters. Use '-e ansi' or '-e utf8'."); + for (unsigned char c : license) + if (c > 127) + throw std::runtime_error( + "License name contains non-ASCII characters. Use '-e ansi' or '-e utf8'."); + break; + } + + auto Info = WinRarKeygen::GenerateRegisterInfo(user.c_str(), license.c_str()); + + if (opts.textOnly) { + PrintRegisterInfo(Info, wDisplayUser, wDisplayLicense); + } else { + std::string content = BuildRegFileContent(Info); + if (!WriteRegFile(opts.outputFile, content)) { + std::wcerr << L"Error: Failed to write file: " << opts.outputFile << L"\n"; + return -1; + } + + const wchar_t* encName = (opts.encoding == Encoding::UTF8) ? L"UTF-8" : + (opts.encoding == Encoding::ANSI) ? L"ANSI" : L"ASCII"; + + std::wcout << L"\n"; + PrintRegisterInfo(Info, wDisplayUser, wDisplayLicense); + std::wcout << L"\nDone! " << opts.outputFile << L" has been generated. (" + << encName << L")\n"; + } + } + catch (std::exception& e) { + std::wcerr << L"Error: " << Utf8ToWide(e.what()) << L"\n"; + return -1; } return 0; diff --git a/bin/Win32-Release/winrar-keygen.exe b/bin/Win32-Release/winrar-keygen.exe index efac96a..8f5fcfd 100644 Binary files a/bin/Win32-Release/winrar-keygen.exe and b/bin/Win32-Release/winrar-keygen.exe differ diff --git a/bin/x64-Release/winrar-keygen.exe b/bin/x64-Release/winrar-keygen.exe index 1b16ace..8e81fe8 100644 Binary files a/bin/x64-Release/winrar-keygen.exe and b/bin/x64-Release/winrar-keygen.exe differ diff --git a/winrar-keygen.aps b/winrar-keygen.aps index 54fd58a..323c754 100644 Binary files a/winrar-keygen.aps and b/winrar-keygen.aps differ diff --git a/winrar-keygen.rc b/winrar-keygen.rc index 96a0e64..b72edd9 100644 --- a/winrar-keygen.rc +++ b/winrar-keygen.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,1,1,0 - PRODUCTVERSION 3,1,1,0 + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "WinRAR Keygen" VALUE "FileDescription", "WinRAR Key Generation Tool" - VALUE "FileVersion", "3.1.1.0" + VALUE "FileVersion", "4.0.0.0" VALUE "InternalName", "winrar-keygen.exe" VALUE "LegalCopyright", "Copyright (C) 2021 Bitcookies" VALUE "OriginalFilename", "winrar-keygen.exe" VALUE "ProductName", "WinRAR Keygen" - VALUE "ProductVersion", "3.1.1.0" + VALUE "ProductVersion", "4.0.0.0" END END BLOCK "VarFileInfo"