diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b338ab4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,49 @@ +# CMake最低版本要求 +cmake_minimum_required(VERSION 3.10) + +# 项目名称 +project(SoftwareRegistration CXX) + +# 设置C++标准 +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 添加头文件目录 +include_directories(include lib) + +# 添加源文件 +aux_source_directory(src SRC_FILES) + +# --- 生成主程序 (用于生成密钥) --- +add_executable(machine_code_generator WIN32 src/generator_gui.cpp) +target_link_libraries(machine_code_generator PRIVATE RegistrationDLL) + +# --- 生成DLL库 --- +add_library(RegistrationDLL SHARED src/registration_api.cpp src/machine_code.cpp) +target_include_directories(RegistrationDLL PRIVATE include lib) +if(WIN32) + target_link_libraries(RegistrationDLL PRIVATE wbemuuid ole32 oleaut32) +endif() +# 定义导出宏 +target_compile_definitions(RegistrationDLL PRIVATE REGISTRATION_EXPORTS) + +# --- 生成GUI应用程序 --- +add_executable(RegistrationApp WIN32 src/gui.cpp) +target_link_libraries(RegistrationApp PRIVATE RegistrationDLL) + +# --- 生成测试程序 --- +add_executable(test_license src/test_license.cpp) +target_link_libraries(test_license PRIVATE RegistrationDLL) + +# --- 生成仅验证程序 --- +add_executable(validate_only src/validate_only.cpp) +target_link_libraries(validate_only PRIVATE RegistrationDLL) + +# --- 生成有效许可证文件程序 --- +add_executable(generate_valid src/generate_valid.cpp) +target_link_libraries(generate_valid PRIVATE RegistrationDLL) + +# 将生成的文件输出到 build/bin 目录 +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) diff --git a/include/machine_code.h b/include/machine_code.h new file mode 100644 index 0000000..e79111f --- /dev/null +++ b/include/machine_code.h @@ -0,0 +1,9 @@ +#ifndef MACHINE_CODE_H +#define MACHINE_CODE_H + +#include + +// 获取机器码 +std::string getMachineCode(); + +#endif // MACHINE_CODE_H diff --git a/include/registration_api.h b/include/registration_api.h new file mode 100644 index 0000000..16d2594 --- /dev/null +++ b/include/registration_api.h @@ -0,0 +1,49 @@ +#ifndef REGISTRATION_API_H +#define REGISTRATION_API_H + +#ifdef REGISTRATION_EXPORTS +#define REGISTRATION_API __declspec(dllexport) +#else +#define REGISTRATION_API __declspec(dllimport) +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + // 获取原始机器码 + REGISTRATION_API const char* GetMachineCode(); + + // 获取加密后的许可证密钥 + REGISTRATION_API const char* GetLicenseKey(); + + // 验证许可证密钥 (离线) + // @param key: 要验证的密钥 + // @return: 0=成功, 1=失败, 2=过期 + REGISTRATION_API int ValidateLicenseOffline(const char* key); + + // 设置离线许可证的有效期 + // @param expiration_date: "YYYY-MM-DD" 格式的日期字符串 + REGISTRATION_API void SetOfflineExpiration(const char* expiration_date); + + // 生成许可证文件 + // @param expiration_date: "YYYY-MM-DD" 格式的日期字符串 + // @return: 0=成功, 1=失败 + REGISTRATION_API int GenerateLicenseFile(const char* expiration_date); + + // 从提供的机器码生成许可证文件 + // @param machine_code: 要使用的机器码 + // @param expiration_date: "YYYY-MM-DD" 格式的日期字符串 + // @return: 0=成功, 1=失败 + REGISTRATION_API int GenerateLicenseFileFromCode(const char* machine_code, const char* expiration_date); + + // 验证许可证文件 + // @return: 0=成功, 1=失败, 2=过期 + REGISTRATION_API int ValidateLicenseFile(); + +#ifdef __cplusplus +} +#endif + +#endif // REGISTRATION_API_H diff --git a/lib/sha256.h b/lib/sha256.h new file mode 100644 index 0000000..628594c --- /dev/null +++ b/lib/sha256.h @@ -0,0 +1,340 @@ +/* + * MIT License + * + * Copyright (c) 2017-2024 nagaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#ifndef PICOSHA2_H +#define PICOSHA2_H +//picosha2:20140213 + +#include +#include +#include +#include +#include +#include + +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#include +#else +#include +#endif + +namespace picosha2 +{ + typedef unsigned char byte_t; + typedef unsigned int word_t; +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) + typedef uint64_t bits_t; +#else + typedef uint64_t bits_t; +#endif + + namespace detail + { + inline byte_t rotr(byte_t x, byte_t n) + { + return (x >> n) | (x << (8 - n)); + } + + inline word_t rotr(word_t x, word_t n) + { + return (x >> n) | (x << (32 - n)); + } + + inline word_t add(word_t i, word_t j) + { + return i + j; + } + + template + void fill_block(InIter first, InIter last, std::vector& block) + { + block.clear(); + block.reserve(std::distance(first, last)); + std::copy(first, last, std::back_inserter(block)); + } + +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) + template::iterator_category, + std::input_iterator_tag>::value>::type* = nullptr> +#else + template +#endif + void fill_block(const InContainer& container, std::vector& block) + { + block.clear(); + block.reserve(container.size()); + std::copy(container.begin(), container.end(), std::back_inserter(block)); + } + + class hash256_one_by_one + { + public: + hash256_one_by_one() + { + init(); + } + + void init() + { + h_[0] = 0x6a09e667; + h_[1] = 0xbb67ae85; + h_[2] = 0x3c6ef372; + h_[3] = 0xa54ff53a; + h_[4] = 0x510e527f; + h_[5] = 0x9b05688c; + h_[6] = 0x1f83d9ab; + h_[7] = 0x5be0cd19; + + add_len_ = 0; + buffer_idx_ = 0; + } + + template + void process(InIter first, InIter last) + { + add_len_ += std::distance(first, last); + for (; first != last; ++first) + { + buffer_[buffer_idx_++] = *first; + if (buffer_idx_ == 64) + { + calc_chunk(); + buffer_idx_ = 0; + } + } + } + + void finish() + { + bits_t len_bit_ = add_len_ * 8; + word_t len_bit_upper = static_cast(len_bit_ >> 32); + word_t len_bit_lower = static_cast(len_bit_ & 0xffffffff); + + buffer_[buffer_idx_++] = 0x80; + + if (buffer_idx_ > 56) + { + while (buffer_idx_ < 64) + { + buffer_[buffer_idx_++] = 0; + } + calc_chunk(); + buffer_idx_ = 0; + } + + while (buffer_idx_ < 56) + { + buffer_[buffer_idx_++] = 0; + } + + buffer_[56] = static_cast(len_bit_upper >> 24); + buffer_[57] = static_cast(len_bit_upper >> 16); + buffer_[58] = static_cast(len_bit_upper >> 8); + buffer_[59] = static_cast(len_bit_upper); + buffer_[60] = static_cast(len_bit_lower >> 24); + buffer_[61] = static_cast(len_bit_lower >> 16); + buffer_[62] = static_cast(len_bit_lower >> 8); + buffer_[63] = static_cast(len_bit_lower); + + calc_chunk(); + } + + word_t const* get_hash() const + { + return h_; + } + + private: + void calc_chunk() + { + word_t w[64]; + for (int i = 0; i < 16; ++i) + { + w[i] = (static_cast(buffer_[i * 4]) << 24) | + (static_cast(buffer_[i * 4 + 1]) << 16) | + (static_cast(buffer_[i * 4 + 2]) << 8) | + (static_cast(buffer_[i * 4 + 3])); + } + + for (int i = 16; i < 64; ++i) + { + word_t s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >> 3); + word_t s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >> 10); + w[i] = add(add(w[i - 16], s0), add(w[i - 7], s1)); + } + + word_t a = h_[0]; + word_t b = h_[1]; + word_t c = h_[2]; + word_t d = h_[3]; + word_t e = h_[4]; + word_t f = h_[5]; + word_t g = h_[6]; + word_t h = h_[7]; + + static const word_t k[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + for (int i = 0; i < 64; ++i) + { + word_t s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); + word_t ch = (e & f) ^ ((~e) & g); + word_t temp1 = add(add(h, s1), add(ch, add(k[i], w[i]))); + word_t s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); + word_t maj = (a & b) ^ (a & c) ^ (b & c); + word_t temp2 = add(s0, maj); + + h = g; + g = f; + f = e; + e = add(d, temp1); + d = c; + c = b; + b = a; + a = add(temp1, temp2); + } + + h_[0] = add(h_[0], a); + h_[1] = add(h_[1], b); + h_[2] = add(h_[2], c); + h_[3] = add(h_[3], d); + h_[4] = add(h_[4], e); + h_[5] = add(h_[5], f); + h_[6] = add(h_[6], g); + h_[7] = add(h_[7], h); + } + + word_t h_[8]; + byte_t buffer_[64]; + size_t buffer_idx_; + uint64_t add_len_; + }; + + } // namespace detail + + template + void hash256(InIter first, InIter last, std::vector& hash) + { + detail::hash256_one_by_one hasher; + hasher.process(first, last); + hasher.finish(); + hash.assign(32, 0); + word_t const* digest = hasher.get_hash(); + for (int i = 0; i < 8; ++i) + { + hash[i * 4] = static_cast(digest[i] >> 24); + hash[i * 4 + 1] = static_cast(digest[i] >> 16); + hash[i * 4 + 2] = static_cast(digest[i] >> 8); + hash[i * 4 + 3] = static_cast(digest[i]); + } + } + + template + void hash256(InIter first, InIter last, std::string& hex_str) + { + std::vector hash(32); + hash256(first, last, hash); + hex_str = bytes_to_hex_string(hash.begin(), hash.end()); + } + + template + std::string hash256_hex_string(InIter first, InIter last) + { + std::string hex_str; + hash256(first, last, hex_str); + return hex_str; + } + + template + void hash256(const In& in, std::vector& hash) + { + hash256(std::begin(in), std::end(in), hash); + } + + template + void hash256(const In& in, std::string& hex_str) + { + hash256(std::begin(in), std::end(in), hex_str); + } + + template + std::string hash256_hex_string(const In& in) + { + return hash256_hex_string(std::begin(in), std::end(in)); + } + + template + std::string bytes_to_hex_string(InIter first, InIter last) + { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + while (first != last) + { + ss << std::setw(2) << static_cast(*first); + ++first; + } + return ss.str(); + } + + template + std::string bytes_to_hex_string(const In& bytes) + { + return bytes_to_hex_string(std::begin(bytes), std::end(bytes)); + } + + template + void hex_string_to_bytes(const std::string& hex_str, OutIter out) + { + assert(hex_str.length() % 2 == 0); + for (size_t i = 0; i < hex_str.length(); i += 2) + { + int val; + std::stringstream ss; + ss << std::hex << hex_str.substr(i, 2); + ss >> val; + *out = static_cast(val); + ++out; + } + } + + template + void hex_string_to_bytes(const std::string& hex_str, OutContainer& bytes) + { + hex_string_to_bytes(hex_str, std::back_inserter(bytes)); + } + +} // namespace picosha2 + +#endif //PICOSHA2_H diff --git a/src/generate_valid.cpp b/src/generate_valid.cpp new file mode 100644 index 0000000..311913f --- /dev/null +++ b/src/generate_valid.cpp @@ -0,0 +1,28 @@ +#include +#include "registration_api.h" +#include "machine_code.h" + +int main() { + std::cout << "--- Generating a valid License.data file for this machine ---" << std::endl; + + const char* machine_code = GetMachineCode(); + if (!machine_code || std::string(machine_code).empty()) { + std::cerr << "!! ERROR: Could not get machine code." << std::endl; + return 1; + } + std::cout << "Machine Code: " << machine_code << std::endl; + + const char* future_date = "2099-12-31"; + std::cout << "Expiration Date: " << future_date << std::endl; + + int gen_result = GenerateLicenseFileFromCode(machine_code, future_date); + + if (gen_result == 0) { + std::cout << ">> SUCCESS: License.data generated successfully." << std::endl; + } else { + std::cerr << ">> FAILURE: Failed to generate License.data." << std::endl; + return 1; + } + + return 0; +} diff --git a/src/generator_gui.cpp b/src/generator_gui.cpp new file mode 100644 index 0000000..69274f2 --- /dev/null +++ b/src/generator_gui.cpp @@ -0,0 +1,115 @@ +#include +#include "registration_api.h" +#include +#include +#include + +#define IDC_MACHINE_CODE_LABEL 201 +#define IDC_MACHINE_CODE_EDIT 202 +#define IDC_COPY_BUTTON 203 + +LRESULT CALLBACK GenWndProc(HWND, UINT, WPARAM, LPARAM); + +void CopyToClipboard(HWND hwnd, const std::string& text) { + if (!OpenClipboard(hwnd)) { + return; + } + EmptyClipboard(); + HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1); + if (!hg) { + CloseClipboard(); + return; + } + memcpy(GlobalLock(hg), text.c_str(), text.size() + 1); + GlobalUnlock(hg); + SetClipboardData(CF_TEXT, hg); + CloseClipboard(); + GlobalFree(hg); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = 0; + wc.lpfnWndProc = GenWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = "GeneratorWindowClass"; + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + if (!RegisterClassEx(&wc)) { + MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); + return 0; + } + + HWND hwnd = CreateWindowEx( + WS_EX_CLIENTEDGE, + "GeneratorWindowClass", + "Machine Code Generator", + (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX), + CW_USEDEFAULT, CW_USEDEFAULT, 540, 150, + NULL, NULL, hInstance, NULL); + + if (hwnd == NULL) { + MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); + return 0; + } + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + MSG Msg; + while (GetMessage(&Msg, NULL, 0, 0) > 0) { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + return Msg.wParam; +} + +LRESULT CALLBACK GenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + static HWND hMachineCodeEdit; + switch (msg) { + case WM_CREATE: { + // Labels + CreateWindow("STATIC", "Your Machine Code:", WS_VISIBLE | WS_CHILD, 20, 20, 150, 20, hwnd, (HMENU)IDC_MACHINE_CODE_LABEL, NULL, NULL); + + // Edit Controls + hMachineCodeEdit = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_READONLY | ES_CENTER, 180, 20, 330, 20, hwnd, (HMENU)IDC_MACHINE_CODE_EDIT, NULL, NULL); + + // Buttons + CreateWindow("BUTTON", "Copy to Clipboard", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 180, 60, 150, 30, hwnd, (HMENU)IDC_COPY_BUTTON, NULL, NULL); + + // Set machine code + std::string machineCode = GetMachineCode(); + SetWindowText(hMachineCodeEdit, machineCode.c_str()); + + break; + } + case WM_COMMAND: { + switch (LOWORD(wParam)) { + case IDC_COPY_BUTTON: { + char formattedMachineCode[256]; + GetWindowText(hMachineCodeEdit, formattedMachineCode, 256); + CopyToClipboard(hwnd, formattedMachineCode); + MessageBox(hwnd, "Machine code copied to clipboard!", "Success", MB_OK); + break; + } + } + break; + } + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} diff --git a/src/gui.cpp b/src/gui.cpp new file mode 100644 index 0000000..62b310e --- /dev/null +++ b/src/gui.cpp @@ -0,0 +1,135 @@ +#include +#include "registration_api.h" +#include +#include +#include + +#define IDC_STATUS_LABEL 101 +#define IDC_MACHINE_CODE_LABEL 102 +#define IDC_MACHINE_CODE_EDIT 103 +#define IDC_EXPIRATION_LABEL 104 +#define IDC_EXPIRATION_EDIT 105 +#define IDC_GENERATE_BUTTON 106 + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +static HWND hMachineCodeEdit, hExpirationEdit, hStatusLabel; + +void UpdateValidationStatus(HWND hwnd) { + int result = ValidateLicenseFile(); + std::string statusText; + switch (result) { + case 0: + statusText = "License Status: VALID"; + break; + case 1: + statusText = "License Status: INVALID or NOT FOUND"; + break; + case 2: + statusText = "License Status: EXPIRED"; + break; + } + SetWindowText(hStatusLabel, statusText.c_str()); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = 0; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = "RegistrationWindowClass"; + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + if (!RegisterClassEx(&wc)) { + MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); + return 0; + } + + HWND hwnd = CreateWindowEx( + WS_EX_CLIENTEDGE, + "RegistrationWindowClass", + "Software Registration", + (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX), + CW_USEDEFAULT, CW_USEDEFAULT, 480, 250, + NULL, NULL, hInstance, NULL); + + if (hwnd == NULL) { + MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); + return 0; + } + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + MSG Msg; + while (GetMessage(&Msg, NULL, 0, 0) > 0) { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } + return Msg.wParam; +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + case WM_CREATE: { + // --- Top Part: Validation Status --- + hStatusLabel = CreateWindow("STATIC", "Checking license...", WS_VISIBLE | WS_CHILD | SS_CENTER, 20, 20, 420, 20, hwnd, (HMENU)IDC_STATUS_LABEL, NULL, NULL); + UpdateValidationStatus(hwnd); + + // --- Bottom Part: License Generation --- + CreateWindow("STATIC", "--- License Generation ---", WS_VISIBLE | WS_CHILD | SS_CENTER, 20, 60, 420, 20, hwnd, NULL, NULL, NULL); + CreateWindow("STATIC", "Machine Code:", WS_VISIBLE | WS_CHILD, 20, 90, 150, 20, hwnd, (HMENU)IDC_MACHINE_CODE_LABEL, NULL, NULL); + hMachineCodeEdit = CreateWindow("EDIT", "", WS_VISIBLE | WS_CHILD | WS_BORDER, 180, 90, 260, 20, hwnd, (HMENU)IDC_MACHINE_CODE_EDIT, NULL, NULL); + + CreateWindow("STATIC", "Expiration (YYYY-MM-DD):", WS_VISIBLE | WS_CHILD, 20, 120, 150, 20, hwnd, (HMENU)IDC_EXPIRATION_LABEL, NULL, NULL); + hExpirationEdit = CreateWindow("EDIT", "2099-12-31", WS_VISIBLE | WS_CHILD | WS_BORDER, 180, 120, 120, 20, hwnd, (HMENU)IDC_EXPIRATION_EDIT, NULL, NULL); + + CreateWindow("BUTTON", "Generate License File", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 180, 160, 180, 30, hwnd, (HMENU)IDC_GENERATE_BUTTON, NULL, NULL); + + break; + } + case WM_COMMAND: { + switch (LOWORD(wParam)) { + case IDC_GENERATE_BUTTON: { + char machineCode[256]; + char expirationDate[11]; + GetWindowText(hMachineCodeEdit, machineCode, 256); + GetWindowText(hExpirationEdit, expirationDate, 11); + + if (strlen(machineCode) == 0) { + MessageBox(hwnd, "Please enter a machine code.", "Error", MB_ICONERROR); + break; + } + + // Note: We are now using the function that takes the machine code as an argument + int result = GenerateLicenseFileFromCode(machineCode, expirationDate); + if (result == 0) { + MessageBox(hwnd, "License.data generated successfully!", "Success", MB_OK); + // Re-validate and update the status label + UpdateValidationStatus(hwnd); + } else { + MessageBox(hwnd, "Failed to generate license file!", "Error", MB_ICONERROR); + } + break; + } + } + break; + } + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} diff --git a/src/machine_code.cpp b/src/machine_code.cpp new file mode 100644 index 0000000..777e1be --- /dev/null +++ b/src/machine_code.cpp @@ -0,0 +1,158 @@ +#include "machine_code.h" +#include +#include +#include +#include +#include + +#pragma comment(lib, "wbemuuid.lib") + +// Helper function to query a WMI property +std::string getWMIProperty(const wchar_t* wmiClass, const wchar_t* propertyName) { + std::string result = ""; + HRESULT hres; + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) { + return "Failed to initialize COM library."; + } + + hres = CoInitializeSecurity( + NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + NULL + ); + + if (FAILED(hres)) { + CoUninitialize(); + return "Failed to initialize security."; + } + + IWbemLocator* pLoc = NULL; + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID*)&pLoc); + + if (FAILED(hres)) { + CoUninitialize(); + return "Failed to create IWbemLocator object."; + } + + IWbemServices* pSvc = NULL; + hres = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), + NULL, + NULL, + 0, + NULL, + 0, + 0, + &pSvc + ); + + if (FAILED(hres)) { + pLoc->Release(); + CoUninitialize(); + return "Could not connect to WMI server."; + } + + hres = CoSetProxyBlanket( + pSvc, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE + ); + + if (FAILED(hres)) { + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return "Could not set proxy blanket."; + } + + IEnumWbemClassObject* pEnumerator = NULL; + std::wstring query = L"SELECT * FROM "; + query += wmiClass; + + hres = pSvc->ExecQuery( + bstr_t("WQL"), + bstr_t(query.c_str()), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator); + + if (FAILED(hres)) { + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return "Query for hardware info failed."; + } + + IWbemClassObject* pclsObj = NULL; + ULONG uReturn = 0; + + while (pEnumerator) { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + if (0 == uReturn) { + break; + } + + VARIANT vtProp; + hr = pclsObj->Get(propertyName, 0, &vtProp, 0, 0); + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) { + _bstr_t bstr(vtProp.bstrVal, false); + result = (char*)bstr; + VariantClear(&vtProp); + pclsObj->Release(); + // We only need the first one + break; + } + VariantClear(&vtProp); + pclsObj->Release(); + } + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + CoUninitialize(); + + return result; +} + +std::string getCpuId() { + return getWMIProperty(L"Win32_Processor", L"ProcessorId"); +} + +std::string getDiskSerial() { + return getWMIProperty(L"Win32_DiskDrive", L"SerialNumber"); +} + +std::string getMacAddress() { + return getWMIProperty(L"Win32_NetworkAdapterConfiguration", L"MACAddress"); +} + +std::string getMachineCode() { + std::string cpuId = getCpuId(); + std::string diskSerial = getDiskSerial(); + std::string macAddress = getMacAddress(); + + // Clean up strings + cpuId.erase(std::remove_if(cpuId.begin(), cpuId.end(), isspace), cpuId.end()); + diskSerial.erase(std::remove_if(diskSerial.begin(), diskSerial.end(), isspace), diskSerial.end()); + macAddress.erase(std::remove_if(macAddress.begin(), macAddress.end(), isspace), macAddress.end()); + macAddress.erase(std::remove(macAddress.begin(), macAddress.end(), ':'), macAddress.end()); + + return cpuId + "-" + diskSerial + "-" + macAddress; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..47cce46 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,19 @@ +#include "include/machine_code.h" +#include "lib/sha256.h" +#include +#include +#include + +int main() { + // 1. 获取原始机器码 + std::string machineCode = getMachineCode(); + std::cout << "Original Machine Code: " << machineCode << std::endl; + + // 2. 对机器码进行SHA-256哈希加密 + std::string hashed_machine_code = picosha2::hash256_hex_string(machineCode); + + // 3. 生成加密后的密钥 (这里我们直接使用哈希值作为密钥) + std::cout << "Generated License Key: " << hashed_machine_code << std::endl; + + return 0; +} diff --git a/src/registration_api.cpp b/src/registration_api.cpp new file mode 100644 index 0000000..e8812d5 --- /dev/null +++ b/src/registration_api.cpp @@ -0,0 +1,214 @@ +#include "registration_api.h" +#include "machine_code.h" +#include "sha256.h" +#include +#include +#include +#include +#include +#include +#include + +// 全局变量来存储机器码和密钥,避免重复计算 +static std::string machineCode_cache; +static std::string licenseKey_cache; +static std::string expiration_date_str = "2099-12-31"; // 默认永久 + +// 内部函数,用于初始化缓存 +void InitializeCache() { + if (machineCode_cache.empty()) { + machineCode_cache = getMachineCode(); + } + if (licenseKey_cache.empty()) { + licenseKey_cache = picosha2::hash256_hex_string(machineCode_cache).substr(0, 32); // Truncate to 32 chars + } +} + +// --- Encryption/Decryption Helpers --- + +// Generates a key from the machine code for XOR operations +std::string generate_xor_key(const std::string& machine_code) { + return picosha2::hash256_hex_string(machine_code).substr(0, 32); +} + +// XORs data with a key. Handles key repeating. +std::string xor_string(const std::string& data, const std::string& key) { + std::string output = data; + for (size_t i = 0; i < data.size(); ++i) { + output[i] = data[i] ^ key[i % key.length()]; + } + return output; +} + +// Converts a string to its hex representation +std::string string_to_hex(const std::string& in) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (unsigned char c : in) { + ss << std::setw(2) << static_cast(c); + } + return ss.str(); +} + +// Converts a hex string back to a normal string +std::string hex_to_string(const std::string& in) { + std::string output; + if (in.length() % 2 != 0) { + return ""; // Invalid hex + } + for (size_t i = 0; i < in.length(); i += 2) { + std::string byteString = in.substr(i, 2); + char byte = (char)strtol(byteString.c_str(), NULL, 16); + output.push_back(byte); + } + return output; +} + + +// C-style string return needs to be managed carefully. +// We use static buffers here, which is not thread-safe but simple. +// For a real-world app, consider returning std::string or managing memory better. +static char machineCode_cstr[256]; +static char licenseKey_cstr[256]; + +const char* GetMachineCode() { + InitializeCache(); + strncpy_s(machineCode_cstr, machineCode_cache.c_str(), sizeof(machineCode_cstr) - 1); + machineCode_cstr[sizeof(machineCode_cstr) - 1] = '\0'; + return machineCode_cstr; +} + +const char* GetLicenseKey() { + InitializeCache(); + strncpy_s(licenseKey_cstr, licenseKey_cache.c_str(), sizeof(licenseKey_cstr) - 1); + licenseKey_cstr[sizeof(licenseKey_cstr) - 1] = '\0'; + return licenseKey_cstr; +} + +int ValidateLicenseOffline(const char* key) { + if (!key) { + return 1; // 失败 + } + InitializeCache(); + if (licenseKey_cache != key) { + return 1; // 失败 + } + + // 检查有效期 + if (expiration_date_str != "2099-12-31") { + std::tm tm = {}; + std::stringstream ss(expiration_date_str); + ss >> std::get_time(&tm, "%Y-%m-%d"); + auto expiration_time = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + + if (std::chrono::system_clock::now() > expiration_time) { + return 2; // 过期 + } + } + + return 0; // 成功 +} + +void SetOfflineExpiration(const char* expiration_date) { + if (expiration_date) { + expiration_date_str = expiration_date; + } +} + +int GenerateLicenseFile(const char* expiration_date) { + if (!expiration_date) { + return 1; // 失败 + } + + InitializeCache(); + std::string data_to_sign = machineCode_cache + expiration_date; + std::string signature = picosha2::hash256_hex_string(data_to_sign); + + std::ofstream license_file("License.data"); + if (!license_file.is_open()) { + return 1; // 失败 + } + + license_file << expiration_date << std::endl; + license_file << signature << std::endl; + license_file.close(); + + return 0; // 成功 +} + +int GenerateLicenseFileFromCode(const char* machine_code, const char* expiration_date) { + if (!machine_code || !expiration_date) { + return 1; // 失败 + } + std::string mc_str(machine_code); + std::string exp_str(expiration_date); + + // 1. Encrypt the expiration date + std::string xor_key = generate_xor_key(mc_str); + std::string encrypted_date = xor_string(exp_str, xor_key); + std::string encrypted_date_hex = string_to_hex(encrypted_date); + + // 2. Create a signature of the machine code and the *encrypted* date + std::string data_to_sign = mc_str + encrypted_date_hex; + std::string signature = picosha2::hash256_hex_string(data_to_sign); + + // 3. Write the encrypted date and signature to the file + std::ofstream license_file("License.data"); + if (!license_file.is_open()) { + return 1; // 失败 + } + + license_file << encrypted_date_hex << std::endl; + license_file << signature << std::endl; + license_file.close(); + + return 0; // 成功 +} + +int ValidateLicenseFile() { + std::ifstream license_file("License.data"); + if (!license_file.is_open()) { + return 1; // 失败 - 文件不存在 + } + + std::string file_encrypted_date_hex; + std::string file_signature; + std::getline(license_file, file_encrypted_date_hex); + std::getline(license_file, file_signature); + license_file.close(); + + if (file_encrypted_date_hex.empty() || file_signature.empty()) { + return 1; // 失败 - 文件格式错误 + } + + // 1. Verify the signature first to ensure integrity + InitializeCache(); // Loads local machine code into machineCode_cache + std::string data_to_verify = machineCode_cache + file_encrypted_date_hex; + std::string expected_signature = picosha2::hash256_hex_string(data_to_verify); + + if (file_signature != expected_signature) { + return 1; // 失败 - 签名不匹配, 文件被篡改或不属于此机器 + } + + // 2. Decrypt the date + std::string xor_key = generate_xor_key(machineCode_cache); + std::string encrypted_date = hex_to_string(file_encrypted_date_hex); + std::string decrypted_date = xor_string(encrypted_date, xor_key); + + // 3. Check the expiration date + std::tm tm = {}; + std::stringstream ss(decrypted_date); + ss >> std::get_time(&tm, "%Y-%m-%d"); + if (ss.fail()) { + return 1; // 失败 - 日期格式损坏 + } + + auto expiration_time = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + expiration_time += std::chrono::hours(23) + std::chrono::minutes(59) + std::chrono::seconds(59); + + if (std::chrono::system_clock::now() > expiration_time) { + return 2; // 过期 + } + + return 0; // 成功 +} diff --git a/src/test_license.cpp b/src/test_license.cpp new file mode 100644 index 0000000..9d3b465 --- /dev/null +++ b/src/test_license.cpp @@ -0,0 +1,53 @@ +#include +#include +#include "registration_api.h" +#include "machine_code.h" + +// Helper function to print validation status +void print_status(int code) { + if (code == 0) { + std::cout << ">> RESULT: License is valid." << std::endl; + } else if (code == 1) { + std::cout << ">> RESULT: License is INVALID (file not found, corrupted, or for another machine)." << std::endl; + } else if (code == 2) { + std::cout << ">> RESULT: License has EXPIRED." << std::endl; + } else { + std::cout << ">> RESULT: Unknown validation code: " << code << std::endl; + } +} + +int main(int argc, char* argv[]) { + // --- Step 1: Generate a valid license file --- + std::cout << "--- Step 1: Generating a valid license file for this machine ---" << std::endl; + const char* machine_code = GetMachineCode(); + if (!machine_code || std::string(machine_code).empty()) { + std::cerr << "!! ERROR: Could not get machine code." << std::endl; + return 1; + } + std::cout << "Machine Code: " << machine_code << std::endl; + + const char* future_date = "2099-12-31"; + std::cout << "Generating license with expiration date: " << future_date << std::endl; + + int gen_result = GenerateLicenseFileFromCode(machine_code, future_date); + if (gen_result == 0) { + std::cout << "License.data generated successfully." << std::endl; + } else { + std::cout << "!! ERROR: Failed to generate License.data." << std::endl; + return 1; + } + std::cout << std::endl; + + // --- Step 2: Validate the generated file --- + std::cout << "--- Step 2: Validating the newly created License.data file ---" << std::endl; + int validation_result = ValidateLicenseFile(); + print_status(validation_result); + + if (validation_result == 0) { + std::cout << "\nVerification successful." << std::endl; + } else { + std::cout << "\nVerification failed." << std::endl; + } + + return 0; +} diff --git a/src/validate_only.cpp b/src/validate_only.cpp new file mode 100644 index 0000000..7afd441 --- /dev/null +++ b/src/validate_only.cpp @@ -0,0 +1,25 @@ +#include +#include "registration_api.h" + +// Helper function to print validation status +void print_status(int code) { + if (code == 0) { + std::cout << ">> RESULT: License is valid." << std::endl; + } else if (code == 1) { + std::cout << ">> RESULT: License is INVALID (file not found, corrupted, or for another machine)." << std::endl; + } else if (code == 2) { + std::cout << ">> RESULT: License has EXPIRED." << std::endl; + } else { + std::cout << ">> RESULT: Unknown validation code: " << code << std::endl; + } +} + +int main() { + std::cout << "--- Attempting to validate existing License.data file ---" << std::endl; + + int validation_result = ValidateLicenseFile(); + + print_status(validation_result); + + return validation_result; +}