🛠 A redesigned CLI and numerous bug fixes

This commit is contained in:
bitcookies
2026-03-21 22:14:49 +08:00
parent 6abeedb5b4
commit 38f15bc187
8 changed files with 310 additions and 84 deletions
+6 -3
View File
@@ -60,6 +60,10 @@ public:
mpz_swap(_Value, Other._Value); mpz_swap(_Value, Other._Value);
} }
~BigInteger() noexcept {
mpz_clear(_Value);
}
BigInteger& operator=(const BigInteger& Other) noexcept { BigInteger& operator=(const BigInteger& Other) noexcept {
if (this != &Other) { if (this != &Other) {
mpz_set(_Value, Other._Value); mpz_set(_Value, Other._Value);
@@ -70,7 +74,6 @@ public:
BigInteger& operator=(BigInteger&& Other) noexcept { BigInteger& operator=(BigInteger&& Other) noexcept {
if (this != &Other) { if (this != &Other) {
mpz_swap(_Value, Other._Value); mpz_swap(_Value, Other._Value);
mpz_clear(Other._Value);
} }
return *this; return *this;
} }
@@ -90,7 +93,7 @@ public:
} }
BigInteger& operator=(const char* lpszValue) noexcept { BigInteger& operator=(const char* lpszValue) noexcept {
mpz_init_set_str(_Value, lpszValue, 0); mpz_set_str(_Value, lpszValue, 0);
return *this; return *this;
} }
@@ -265,7 +268,7 @@ public:
return *this; return *this;
} }
BigInteger& Load(bool IsNegative, const std::vector<uint8_t> Buffer, bool UseLittleEndian) noexcept { BigInteger& Load(bool IsNegative, const std::vector<uint8_t>& Buffer, bool UseLittleEndian) noexcept {
mpz_import(_Value, Buffer.size(), UseLittleEndian ? -1 : 1, sizeof(uint8_t), 0, 0, Buffer.data()); mpz_import(_Value, Buffer.size(), UseLittleEndian ? -1 : 1, sizeof(uint8_t), 0, 0, Buffer.data());
if (IsNegative) if (IsNegative)
mpz_neg(_Value, _Value); mpz_neg(_Value, _Value);
+21 -10
View File
@@ -54,7 +54,10 @@ public:
} }
Point operator-() const noexcept { 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) { Point& operator=(const Point& Other) {
@@ -139,9 +142,14 @@ public:
if (&_Curve == &Other._Curve || _Curve == Other._Curve) { if (&_Curve == &Other._Curve || _Curve == Other._Curve) {
if (IsAtInfinity()) { if (IsAtInfinity()) {
return Other; return Other;
} else { } else if (Other.IsAtInfinity()) {
if (this == &Other || _X == Other._X) { return *this;
} else if (_X == Other._X) {
if (this == &Other || _Y == Other._Y) {
return ValueOfDouble(); return ValueOfDouble();
} else {
return Point(_Curve);
}
} else { } else {
Point Result(_Curve); Point Result(_Curve);
@@ -163,7 +171,6 @@ public:
return Result; return Result;
} }
}
} else { } else {
throw std::invalid_argument("Not on the same curve."); throw std::invalid_argument("Not on the same curve.");
} }
@@ -174,12 +181,16 @@ public:
if (IsAtInfinity()) { if (IsAtInfinity()) {
_X = Other._X; _X = Other._X;
_Y = Other._Y; _Y = Other._Y;
} else { } else if (Other.IsAtInfinity()) {
if (this == &Other || _X == Other._X) { // *this unchanged
} else if (_X == Other._X) {
if (this == &Other || _Y == Other._Y) {
Double(); Double();
} else { } else {
Point Result(_Curve); _X = __FieldType();
_Y = __FieldType();
}
} else {
// m = (Y0 + Y1) / (X0 + X1) // m = (Y0 + Y1) / (X0 + X1)
auto m = (_Y + Other._Y) / (_X + Other._X); auto m = (_Y + Other._Y) / (_X + Other._X);
@@ -198,7 +209,6 @@ public:
_X = NewX; _X = NewX;
} }
}
return *this; return *this;
} else { } else {
throw std::invalid_argument("Not on the same curve."); throw std::invalid_argument("Not on the same curve.");
@@ -229,7 +239,7 @@ public:
return Result; return Result;
} }
Point operator*=(const BigInteger N) noexcept { Point& operator*=(const BigInteger N) noexcept {
Point Result(_Curve); Point Result(_Curve);
size_t bit_length = N.BitLength(); size_t bit_length = N.BitLength();
@@ -240,6 +250,7 @@ public:
} }
*this = Result; *this = Result;
return *this;
} }
// SEC 1: Elliptic Curve Cryptography // SEC 1: Elliptic Curve Cryptography
+5 -1
View File
@@ -94,7 +94,11 @@ private:
static BigInteger GenerateRandomInteger() { static BigInteger GenerateRandomInteger() {
uint16_t RawRandomInteger[15]; uint16_t RawRandomInteger[15];
static bool seeded = false;
if (!seeded) {
srand(static_cast<unsigned int>(time(nullptr))); srand(static_cast<unsigned int>(time(nullptr)));
seeded = true;
}
for (size_t i = 0; i < 15; ++i) { for (size_t i = 0; i < 15; ++i) {
RawRandomInteger[i] = static_cast<uint16_t>(rand()); RawRandomInteger[i] = static_cast<uint16_t>(rand());
} }
@@ -226,7 +230,7 @@ public:
CalculateChecksum(RegInfo); CalculateChecksum(RegInfo);
RegInfo.HexData = HelperStringFormat( 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[0].length(),
RegInfo.Items[1].length(), RegInfo.Items[1].length(),
RegInfo.Items[2].length(), RegInfo.Items[2].length(),
+238 -30
View File
@@ -28,12 +28,113 @@ std::wstring Utf8ToWide(const std::string& str) {
return result; return result;
} }
void Help() { std::string WideToAnsi(const std::wstring& wstr) {
std::wcout << L"Please use the following command:\n"; 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<std::wstring> 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"Usage:\n";
std::wcout << L" winrar-keygen.exe <Username> <License Name>\n\n"; std::wcout << L" winrar-keygen.exe <Username> <LicenseName> [options]\n";
std::wcout << L"Example:\n"; std::wcout << L" winrar-keygen.exe -v | --version\n";
std::wcout << L" winrar-keygen.exe \"GitHub\" \"Single PC usage license\"\n"; std::wcout << L" winrar-keygen.exe -h | --help\n\n";
std::wcout << L"Options:\n";
std::wcout << L" -e, --encoding <enc> utf8 (default), ascii, ansi\n";
std::wcout << L" -o, --output <file> 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() { std::wstring GetExecutableVersion() {
@@ -69,15 +170,14 @@ std::wstring GetExecutableVersion() {
std::to_wstring(LOWORD(fixedInfo->dwFileVersionLS)); std::to_wstring(LOWORD(fixedInfo->dwFileVersionLS));
} }
void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info) { void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info,
std::wstring user = Utf8ToWide(Info.UserName); const std::wstring& wUser, const std::wstring& wLicense) {
std::wstring license = Utf8ToWide(Info.LicenseType);
std::wstring uid = Utf8ToWide(Info.UID); std::wstring uid = Utf8ToWide(Info.UID);
std::wstring data = Utf8ToWide(Info.HexData); std::wstring data = Utf8ToWide(Info.HexData);
std::wcout << L"RAR registration data\n"; std::wcout << L"RAR registration data\n";
std::wcout << user << L"\n"; std::wcout << wUser << L"\n";
std::wcout << license << L"\n"; std::wcout << wLicense << L"\n";
std::wcout << L"UID=" << uid << L"\n"; std::wcout << L"UID=" << uid << L"\n";
for (size_t i = 0; i < data.length(); i += 54) { for (size_t i = 0; i < data.length(); i += 54) {
@@ -85,6 +185,27 @@ void PrintRegisterInfo(const WinRarKeygen<WinRarConfig>::RegisterInfo& Info) {
} }
} }
std::string BuildRegFileContent(const WinRarKeygen<WinRarConfig>::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) { bool IsConsoleHandle(HANDLE handle) {
if (handle == nullptr || handle == INVALID_HANDLE_VALUE) { if (handle == nullptr || handle == INVALID_HANDLE_VALUE) {
return false; return false;
@@ -97,25 +218,23 @@ void ConfigureConsoleOutput() {
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE stderrHandle = GetStdHandle(STD_ERROR_HANDLE); HANDLE stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
int mode;
// console use UTF-16, file use UTF-8 // console use UTF-16, file use UTF-8
if (IsConsoleHandle(stdoutHandle)) { 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"; std::wcerr << L"Failed to set stdout _O_WTEXT\n";
} }
else { else {
// stdout is redirected to a file, use UTF-8 // 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"; std::wcerr << L"Failed to set stdout _O_U8TEXT\n";
} }
if (IsConsoleHandle(stderrHandle)) { 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"; std::wcerr << L"Failed to set stderr _O_WTEXT\n";
} }
else { 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"; std::wcerr << L"Failed to set stderr _O_U8TEXT\n";
} }
} }
@@ -123,27 +242,116 @@ void ConfigureConsoleOutput() {
int wmain(int argc, wchar_t* argv[]) { int wmain(int argc, wchar_t* argv[]) {
ConfigureConsoleOutput(); ConfigureConsoleOutput();
if (argc == 2 && Options opts;
(_wcsicmp(argv[1], L"ver") == 0 || _wcsicmp(argv[1], L"--version") == 0 || _wcsicmp(argv[1], L"-v") == 0)) { if (!ParseArguments(argc, argv, opts)) {
std::wcout << GetExecutableVersion() << L"\n"; return -1;
} }
else if (argc == 3) {
try {
std::string user = WideToUtf8(argv[1]);
std::string license = WideToUtf8(argv[2]);
PrintRegisterInfo( std::wstring version = GetExecutableVersion();
WinRarKeygen<WinRarConfig>::GenerateRegisterInfo(user.c_str(), license.c_str())
); 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";
}
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<unsigned>(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<WinRarConfig>::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) { catch (std::exception& e) {
std::wcerr << L"Error: " << Utf8ToWide(e.what()) << L"\n"; std::wcerr << L"Error: " << Utf8ToWide(e.what()) << L"\n";
return -1; return -1;
} }
}
else {
Help();
}
return 0; return 0;
} }
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -51,8 +51,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,1,1,0 FILEVERSION 4,0,0,0
PRODUCTVERSION 3,1,1,0 PRODUCTVERSION 4,0,0,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@@ -69,12 +69,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "WinRAR Keygen" VALUE "CompanyName", "WinRAR Keygen"
VALUE "FileDescription", "WinRAR Key Generation Tool" VALUE "FileDescription", "WinRAR Key Generation Tool"
VALUE "FileVersion", "3.1.1.0" VALUE "FileVersion", "4.0.0.0"
VALUE "InternalName", "winrar-keygen.exe" VALUE "InternalName", "winrar-keygen.exe"
VALUE "LegalCopyright", "Copyright (C) 2021 Bitcookies" VALUE "LegalCopyright", "Copyright (C) 2021 Bitcookies"
VALUE "OriginalFilename", "winrar-keygen.exe" VALUE "OriginalFilename", "winrar-keygen.exe"
VALUE "ProductName", "WinRAR Keygen" VALUE "ProductName", "WinRAR Keygen"
VALUE "ProductVersion", "3.1.1.0" VALUE "ProductVersion", "4.0.0.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"