first commit

This commit is contained in:
brige.long 2025-07-26 10:32:02 +08:00
parent 63150cd830
commit 88837e3591
12 changed files with 1194 additions and 0 deletions

49
CMakeLists.txt Normal file
View File

@ -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)

9
include/machine_code.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MACHINE_CODE_H
#define MACHINE_CODE_H
#include <string>
// 获取机器码
std::string getMachineCode();
#endif // MACHINE_CODE_H

View File

@ -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 <string>
#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

340
lib/sha256.h Normal file
View File

@ -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 <algorithm>
#include <cassert>
#include <iterator>
#include <sstream>
#include <vector>
#include <iomanip>
#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
#include <cstdint>
#else
#include <stdint.h>
#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<typename InIter>
void fill_block(InIter first, InIter last, std::vector<byte_t>& 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<typename InContainer,
typename std::enable_if<
!std::is_same<
typename std::iterator_traits<typename InContainer::iterator>::iterator_category,
std::input_iterator_tag>::value>::type* = nullptr>
#else
template<typename InContainer>
#endif
void fill_block(const InContainer& container, std::vector<byte_t>& 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<typename InIter>
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<word_t>(len_bit_ >> 32);
word_t len_bit_lower = static_cast<word_t>(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<byte_t>(len_bit_upper >> 24);
buffer_[57] = static_cast<byte_t>(len_bit_upper >> 16);
buffer_[58] = static_cast<byte_t>(len_bit_upper >> 8);
buffer_[59] = static_cast<byte_t>(len_bit_upper);
buffer_[60] = static_cast<byte_t>(len_bit_lower >> 24);
buffer_[61] = static_cast<byte_t>(len_bit_lower >> 16);
buffer_[62] = static_cast<byte_t>(len_bit_lower >> 8);
buffer_[63] = static_cast<byte_t>(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<word_t>(buffer_[i * 4]) << 24) |
(static_cast<word_t>(buffer_[i * 4 + 1]) << 16) |
(static_cast<word_t>(buffer_[i * 4 + 2]) << 8) |
(static_cast<word_t>(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<typename InIter>
void hash256(InIter first, InIter last, std::vector<byte_t>& 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<byte_t>(digest[i] >> 24);
hash[i * 4 + 1] = static_cast<byte_t>(digest[i] >> 16);
hash[i * 4 + 2] = static_cast<byte_t>(digest[i] >> 8);
hash[i * 4 + 3] = static_cast<byte_t>(digest[i]);
}
}
template<typename InIter>
void hash256(InIter first, InIter last, std::string& hex_str)
{
std::vector<byte_t> hash(32);
hash256(first, last, hash);
hex_str = bytes_to_hex_string(hash.begin(), hash.end());
}
template<typename InIter>
std::string hash256_hex_string(InIter first, InIter last)
{
std::string hex_str;
hash256(first, last, hex_str);
return hex_str;
}
template<typename In>
void hash256(const In& in, std::vector<byte_t>& hash)
{
hash256(std::begin(in), std::end(in), hash);
}
template<typename In>
void hash256(const In& in, std::string& hex_str)
{
hash256(std::begin(in), std::end(in), hex_str);
}
template<typename In>
std::string hash256_hex_string(const In& in)
{
return hash256_hex_string(std::begin(in), std::end(in));
}
template<typename InIter>
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<int>(*first);
++first;
}
return ss.str();
}
template<typename In>
std::string bytes_to_hex_string(const In& bytes)
{
return bytes_to_hex_string(std::begin(bytes), std::end(bytes));
}
template<typename OutIter>
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<byte_t>(val);
++out;
}
}
template<typename OutContainer>
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

28
src/generate_valid.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <iostream>
#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;
}

115
src/generator_gui.cpp Normal file
View File

@ -0,0 +1,115 @@
#include <windows.h>
#include "registration_api.h"
#include <string>
#include <algorithm>
#include <cctype>
#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;
}

135
src/gui.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <windows.h>
#include "registration_api.h"
#include <string>
#include <algorithm>
#include <cctype>
#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;
}

158
src/machine_code.cpp Normal file
View File

@ -0,0 +1,158 @@
#include "machine_code.h"
#include <iostream>
#include <vector>
#include <string>
#include <comdef.h>
#include <Wbemidl.h>
#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;
}

19
src/main.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "include/machine_code.h"
#include "lib/sha256.h"
#include <iostream>
#include <string>
#include <vector>
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;
}

214
src/registration_api.cpp Normal file
View File

@ -0,0 +1,214 @@
#include "registration_api.h"
#include "machine_code.h"
#include "sha256.h"
#include <string>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <algorithm>
// 全局变量来存储机器码和密钥,避免重复计算
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<int>(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; // 成功
}

53
src/test_license.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <iostream>
#include <string>
#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;
}

25
src/validate_only.cpp Normal file
View File

@ -0,0 +1,25 @@
#include <iostream>
#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;
}