diff --git a/CMakeLists.txt b/CMakeLists.txt index 41dd7db..a061e31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,15 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(SOURCES xpmgr.cpp) +set(LIB_SOURCES xpmgr_lib.cpp) # ==== x86 build ==== + +# Static library for x86 +add_library(xpmgr_static_x86 STATIC ${LIB_SOURCES}) +target_compile_options(xpmgr_static_x86 PRIVATE -m32) +target_link_libraries(xpmgr_static_x86 PRIVATE ole32 oleaut32 uuid) + set(RES_X86 ${CMAKE_BINARY_DIR}/icon_x86.res) # Create a custom target for the x86 resource compilation @@ -26,11 +33,17 @@ add_dependencies(xpmgr_x86 xpmgr_x86_res) target_compile_options(xpmgr_x86 PRIVATE -m32) target_link_options(xpmgr_x86 PRIVATE -m32) -# Link .res file as a resource -target_link_libraries(xpmgr_x86 PRIVATE ole32 oleaut32 uuid) +# Link .res file and static library +target_link_libraries(xpmgr_x86 PRIVATE ole32 oleaut32 uuid xpmgr_static_x86) target_link_options(xpmgr_x86 PRIVATE -Wl,--subsystem,console ${RES_X86}) # ==== x64 build ==== + +# Static library for x64 +add_library(xpmgr_static_x64 STATIC ${LIB_SOURCES}) +target_compile_options(xpmgr_static_x64 PRIVATE -m64) +target_link_libraries(xpmgr_static_x64 PRIVATE ole32 oleaut32 uuid) + set(RES_X64 ${CMAKE_BINARY_DIR}/icon_x64.res) # Create a custom target for the x64 resource compilation @@ -50,6 +63,13 @@ add_dependencies(xpmgr_x64 xpmgr_x64_res) target_compile_options(xpmgr_x64 PRIVATE -m64) target_link_options(xpmgr_x64 PRIVATE -m64) -# Link .res file as a resource -target_link_libraries(xpmgr_x64 PRIVATE ole32 oleaut32 uuid) +# Link .res file and static library +target_link_libraries(xpmgr_x64 PRIVATE ole32 oleaut32 uuid xpmgr_static_x64) target_link_options(xpmgr_x64 PRIVATE -Wl,--subsystem,console ${RES_X64}) + +# Install targets +install(TARGETS xpmgr_static_x86 xpmgr_static_x64 + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) + +install(FILES xpmgr.h DESTINATION include) diff --git a/README.md b/README.md index ae4ce50..faaa8bf 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ If you have an Itanium version of Windows XP or Server 2003, Windows Product Act `/ipk` and `/dti` also support reading from stdin via a pipe. So, you could do something like `echo FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8 | xpmgr_x86 /ipk` or `echo 253286028742154311079061239762245184619981623171292574 | xpmgr_x86 /atp` (replace echo with your own program). +This can also be used as a library; see the xpmgr.h file for details. + ## Releases https://github.com/UMSKT/xpmgr/releases diff --git a/xpmgr.cpp b/xpmgr.cpp index 48fa987..fa9a0b8 100644 --- a/xpmgr.cpp +++ b/xpmgr.cpp @@ -3,662 +3,172 @@ typedef struct IUnknown IUnknown; #include #include -#include -#include -#include - -// Check windows -#if _WIN32 || _WIN64 -#if _WIN64 -#define ENVIRONMENT64 -#else -#define ENVIRONMENT32 -#endif -#endif - -const char* specifiedProduct = nullptr; - -// This is a really weird way of making a GUID. In short, this becomes ACADF079-CBCD-4032-83F2-FA47C4DB096F. Ignore the 0x and it makes sense. -static CLSID XP_CLSID = { 0xACADF079, 0xCBCD, 0x4032, {0x83, 0xF2, 0xFA, 0x47, 0xC4, 0xDB, 0x09, 0x6F} }; -static IID XP_IID = { 0xB8CBAD79, 0x3F1F, 0x481A, { 0xBB, 0x0C, 0xE7, 0xBB, 0xD7, 0x7B, 0xDD, 0xD1 } }; - -#undef XP_INTERFACE -#define XP_INTERFACE ICOMLicenseAgent -DECLARE_INTERFACE_(ICOMLicenseAgent, IDispatch) -{ -protected: - ~ICOMLicenseAgent() = default; - -public: - - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; - STDMETHOD_(ULONG, AddRef)() PURE; - STDMETHOD_(ULONG, Release)() PURE; +#include +#include +#include "xpmgr.h" + +// Command line handling +struct CommandLineArgs { + static const char* getCmdOption(char** begin, char** end, const std::string& option) { + const char* opt = option.c_str(); + char** itr = std::find_if(begin, end, [opt](const char* arg) { return strcmp(arg, opt) == 0; }); + if (itr != end && ++itr != end) { + return *itr; + } + return nullptr; + } - /*** IDispatch methods ***/ - STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR * pctinfo) PURE; - STDMETHOD(GetTypeInfo)(THIS_ UINT itinfo, LCID lcid, ITypeInfo FAR * FAR * pptinfo) PURE; - STDMETHOD(GetIDsOfNames)(THIS_ REFIID riid, OLECHAR FAR * FAR * rgszNames, UINT cNames, LCID lcid, DISPID FAR * rgdispid) PURE; - STDMETHOD(Invoke)(THIS_ DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR * pdispparams, VARIANT FAR * pvarResult, EXCEPINFO FAR * pexcepinfo, UINT FAR * puArgErr) PURE; + static bool cmdOptionExists(char** begin, char** end, const std::string& option) { + const char* opt = option.c_str(); + return std::find_if(begin, end, [opt](const char* arg) { return strcmp(arg, opt) == 0; }) != end; + } - /*** ICOMLicenseAgent methods ***/ - STDMETHOD(Initialize)(THIS_ ULONG dwBPC, ULONG dwMode, BSTR bstrLicSource, ULONG * pdwRetCode) PURE; - STDMETHOD(GetFirstName)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetFirstName)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetLastName)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetLastName)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetOrgName)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetOrgName)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetEmail)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetEmail)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetPhone)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetPhone)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetAddress1)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetAddress1)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetCity)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetCity)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetState)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetState)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetCountryCode)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetCountryCode)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetCountryDesc)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetCountryDesc)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetZip)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetZip)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetIsoLanguage)(THIS_ ULONG * pdwVal) PURE; - STDMETHOD(SetIsoLanguage)(THIS_ ULONG dwNewVal) PURE; - STDMETHOD(GetMSUpdate)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetMSUpdate)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetMSOffer)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetMSOffer)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetOtherOffer)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetOtherOffer)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(GetAddress2)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(SetAddress2)(THIS_ BSTR bstrNewVal) PURE; - STDMETHOD(AsyncProcessHandshakeRequest)(THIS_ LONG bReviseCustInfo) PURE; - STDMETHOD(AsyncProcessNewLicenseRequest)() PURE; - STDMETHOD(AsyncProcessReissueLicenseRequest)() PURE; - STDMETHOD(AsyncProcessReviseCustInfoRequest)() PURE; - STDMETHOD(GetAsyncProcessReturnCode)(THIS_ ULONG * pdwRetCode) PURE; - STDMETHOD(AsyncProcessDroppedLicenseRequest)() PURE; - STDMETHOD(GenerateInstallationId)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(DepositConfirmationId)(THIS_ BSTR bstrVal, ULONG * pdwRetCode) PURE; - STDMETHOD(GetExpirationInfo)(THIS_ ULONG * pdwWPALeft, ULONG * pdwEvalLeft) PURE; - STDMETHOD(AsyncProcessRegistrationRequest)() PURE; - STDMETHOD(ProcessHandshakeRequest)(THIS_ LONG bReviseCustInfo) PURE; - STDMETHOD(ProcessNewLicenseRequest)() PURE; - STDMETHOD(ProcessDroppedLicenseRequest)() PURE; - STDMETHOD(ProcessReissueLicenseRequest)() PURE; - STDMETHOD(ProcessReviseCustInfoRequest)() PURE; - STDMETHOD(EnsureInternetConnection)() PURE; - STDMETHOD(SetProductKey)(THIS_ LPWSTR pszNewProductKey) PURE; - STDMETHOD(GetProductID)(THIS_ BSTR * pbstrVal) PURE; - STDMETHOD(VerifyCheckDigits)(THIS_ BSTR bstrCIDIID, LONG * pbValue) PURE; + static std::string readFromStdin() { + std::string input; + if (!std::cin.eof() && std::getline(std::cin, input)) { + // Remove any trailing whitespace, newlines or carriage returns + input.erase(std::find_if(input.rbegin(), input.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), input.end()); + return input; + } + return ""; + } }; -static BOOL XP_ComInitialized = FALSE; -static ICOMLicenseAgent* XP_LicenseAgent = nullptr; - -static BOOL XP_LoadLicenseManager() -{ - if (!XP_ComInitialized) { - const HRESULT status = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - if (FAILED(status)) { - const char* errorString = "An error occurred at CoInitializeEx:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X\n", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X\n", errorString, static_cast(status)); - std::cout << result; - return FALSE; - } - XP_ComInitialized = TRUE; - } - if (!XP_LicenseAgent) { - HRESULT status = CoCreateInstance(XP_CLSID, nullptr, CLSCTX_INPROC_SERVER, XP_IID, reinterpret_cast(&XP_LicenseAgent)); - int good = 0; - if (SUCCEEDED(status)) { - ULONG dwRetCode; - status = XP_LicenseAgent->Initialize(0xC475 /* This needs to be changed, I believe it's causing the crashing.*/ , 3, nullptr, &dwRetCode); - if (SUCCEEDED(status) && dwRetCode == 0) { - good = 1; - } - else { - XP_LicenseAgent->Release(); - XP_LicenseAgent = nullptr; - } - } - if (!good) { - const char* errorString = "An error occurred at CoCreateInstance:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X\n", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X\n", errorString, static_cast(status)); - std::cout << result; - return FALSE; - } - } - return TRUE; -} - -#pragma region Internals - -// Add function to read from stdin -std::string readFromStdin() { - std::string input; - if (!std::cin.eof() && std::getline(std::cin, input)) { - // Remove any trailing whitespace, newlines or carriage returns - input.erase(std::find_if(input.rbegin(), input.rend(), [](unsigned char ch) { - return !std::isspace(ch); - }).base(), input.end()); - return input; +// Helper function to convert char* to wchar_t* +wchar_t* convertCharArrayToLPCWSTR(const char* charArray) { + auto* wString = new wchar_t[4096]; + MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096); + return wString; +} + +// Helper function to convert wchar_t* to char* +char* convertWCharToChar(const wchar_t* wcharArray) { + const int size = WideCharToMultiByte(CP_UTF8, 0, wcharArray, -1, nullptr, 0, nullptr, nullptr); + auto* charStr = new char[size]; + WideCharToMultiByte(CP_UTF8, 0, wcharArray, -1, charStr, size, nullptr, nullptr); + return charStr; +} + +int main(int argc, char* argv[]) { + const char* USAGE_TEXT = + "xpmgr - Windows XP License Manager (compiled on " __DATE__ " " __TIME__ ")\n" + "\n" + "Usage: \n" + "/dti: Gets the Installation ID\n" + "/atp [cid]: Sets Confirmation ID (If successful, activates Windows/Office) (can also read from stdin)\n" + "/xpr: Gets the days before activation is required (Grace period)\n" + "/xpr-eval: Gets the days before the evaluation period expires (Eval. copies only)\n" + "/ipk [pkey]: Sets/changes product key (can also read from stdin)\n" + "/dli: Gets the product ID of Windows\n" + "/?: Displays this message"; + + if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "--GetUsage")) { + std::cout << USAGE_TEXT; + return 0; } - return ""; -} - -wchar_t* convertCharArrayToLPCWSTR(const char* charArray) -{ - auto* wString = new wchar_t[4096]; - MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096); - return wString; -} - -char* getCmdOption(char** begin, char** end, const std::string& option) -{ - char** itr = std::find(begin, end, option); - if (itr != end && ++itr != end) - { - return *itr; - } - return nullptr; -} - -bool cmdOptionExists(char** begin, char** end, const std::string& option) -{ - return std::find(begin, end, option) != end; -} -char* BstrToChar(BSTR bstr) -{ - const int len = static_cast(::SysStringLen(bstr)); - const int bufSize = ::WideCharToMultiByte(CP_UTF8, 0, bstr, len, nullptr, 0, nullptr, nullptr); - const auto buffer = new char[bufSize + 1]; - ::WideCharToMultiByte(CP_UTF8, 0, bstr, len, buffer, bufSize, nullptr, nullptr); - buffer[bufSize] = '\0'; - return buffer; -} - -ULONG ConvertToULONG(const char* str) -{ - char* end; - const ULONG value = std::strtoul(str, &end, 10); - return value; -} - -OLECHAR SizeToOLECHAR(size_t value) -{ - // Convert size_t to wstring - const std::wstring wideString = std::to_wstring(value); - - // Retrieve the first character from the wide string - const OLECHAR oChar = wideString[0]; - - return oChar; -} - -BSTR ConvertToBSTR(const std::string& str) { - const int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); - // ReSharper disable once CppLocalVariableMayBeConst - BSTR bstr = SysAllocStringLen(nullptr, size - 1); - MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bstr, size); - return bstr; -} - -std::string ConvertToStdString(BSTR bstr) { - const int size = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, nullptr, 0, nullptr, nullptr); - const auto buffer = new char[size]; - WideCharToMultiByte(CP_UTF8, 0, bstr, -1, buffer, size, nullptr, nullptr); - - std::string result(buffer); - delete[] buffer; - - return result; -} - -BSTR ConvertCharToBSTR(const char* charString) { - const int size = MultiByteToWideChar(CP_UTF8, 0, charString, -1, nullptr, 0); - // ReSharper disable once CppLocalVariableMayBeConst - BSTR bstr = SysAllocStringLen(nullptr, size - 1); - MultiByteToWideChar(CP_UTF8, 0, charString, -1, bstr, size); - return bstr; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -void safeStrncat(char* destination, const char* source, size_t size) { - const size_t destLen = strlen(destination); - const size_t srcLen = strlen(source); - - if (destLen + srcLen < size) { - // fuck off Microsoft - strncat(destination, source, size - destLen - 1); // -1 for the null terminator - } else { - // Handle buffer overflow error - std::cout << "An error occurred at safeStrncat: Buffer overflow during strncat operation." << std::endl; + // Check Windows version + OSVERSIONINFOEX info = {}; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx(reinterpret_cast(&info)); + + if (info.dwMajorVersion != 5) { + std::cout << "An error occurred: Windows management only works on Windows NT 5.1 and 5.2."; + if (info.dwMajorVersion > 5) { + std::cout << " Use slmgr instead: https://learn.microsoft.com/en-us/windows-server/get-started/activation-slmgr-vbs-options"; + return 3; + } + return 2; } -} -#pragma clang diagnostic pop - -bool IsProcessRunning(const wchar_t* processName) -{ - // ReSharper disable once CppLocalVariableMayBeConst - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - { - std::cout << "An error occurred at IsProcessRunning: Failed to create process snapshot." << std::endl; - return false; - } - - PROCESSENTRY32W processEntry{}; - processEntry.dwSize = sizeof(PROCESSENTRY32W); - if (!Process32FirstW(snapshot, &processEntry)) - { - CloseHandle(snapshot); - std::cout << "An error occurred at IsProcessRunning: Failed to retrieve process information." << std::endl; - return false; - } - - while (Process32NextW(snapshot, &processEntry)) - { - if (wcscmp(processEntry.szExeFile, processName) == 0) - { - CloseHandle(snapshot); - return true; // Process found - } - } - CloseHandle(snapshot); - return false; // Process not found -} - -bool TerminateProcessByName(const wchar_t* processName) -{ - // ReSharper disable once CppLocalVariableMayBeConst - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - { - return false; - } - - PROCESSENTRY32W processEntry{}; - processEntry.dwSize = sizeof(PROCESSENTRY32W); - if (!Process32FirstW(snapshot, &processEntry)) - { - CloseHandle(snapshot); - return false; - } - - while (Process32NextW(snapshot, &processEntry)) - { - if (wcscmp(processEntry.szExeFile, processName) == 0) - { - // ReSharper disable once CppLocalVariableMayBeConst - HANDLE processHandle = OpenProcess(PROCESS_TERMINATE, FALSE, processEntry.th32ProcessID); - if (processHandle != nullptr) - { - if (TerminateProcess(processHandle, 0)) - { - CloseHandle(processHandle); - CloseHandle(snapshot); - return true; // Process terminated successfully - } - else - { - CloseHandle(processHandle); - CloseHandle(snapshot); - return false; // Failed to terminate the process - } - } - else - { - CloseHandle(snapshot); - return false; // Failed to open process handle - } - } - } - - CloseHandle(snapshot); - return false; // Process not found -} - -#pragma endregion - -#pragma region Windows XP -static BSTR XP_GetWPALeft() { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - ULONG dwWPALeft = 0, dwEvalLeft = 0; - const HRESULT status = XP_LicenseAgent->GetExpirationInfo(&dwWPALeft, &dwEvalLeft); - if (FAILED(status)) { - XP_LicenseAgent->Release(); - XP_LicenseAgent = nullptr; - const char* errorString = "An error occurred at CoInitializeEx:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X", errorString, static_cast(status)); - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - if (dwWPALeft == 0x7FFFFFFF) { - return SysAllocString(L"An error occurred at GetWPALeft: Windows is activated"); - } - wchar_t buffer[16]; - swprintf(buffer, L"%lu", dwWPALeft); - return SysAllocString(buffer); -} - -static BSTR XP_GetEvalLeft() { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - ULONG dwWPALeft = 0, dwEvalLeft = 0; - const HRESULT status = XP_LicenseAgent->GetExpirationInfo(&dwWPALeft, &dwEvalLeft); - if (FAILED(status)) { - XP_LicenseAgent->Release(); - XP_LicenseAgent = nullptr; - const char* errorString = "An error occurred at GetExpirationInfo:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X", errorString, static_cast(status)); - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - if (dwEvalLeft == 0x7FFFFFFF) { - return SysAllocString(L"An error occurred at GetEvalLeft: Not an evaluation copy"); - } - wchar_t buffer[16]; - swprintf(buffer, L"%lu", dwWPALeft); - return SysAllocString(buffer); -} - -static BSTR XP_VerifyCheckDigits(BSTR cidChunk) { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - if (0 == wcscmp(XP_GetWPALeft(), L"An error occurred at GetWPALeft: Windows is activated")) { - return SysAllocString(L"An error occurred at GetWPALeft: Windows is activated"); - } - - LONG pbValue; - const HRESULT status = XP_LicenseAgent->VerifyCheckDigits(cidChunk, &pbValue); - if (FAILED(status) || !pbValue) { - char errorMessage[70] = "An error occurred at XP_VerifyCheckDigits:"; - char pbValueChar[20]; - snprintf(errorMessage, sizeof(errorMessage), "%ld", pbValue); - safeStrncat(errorMessage, pbValueChar, sizeof(errorMessage)); - const int len = MultiByteToWideChar(CP_UTF8, 0, errorMessage, -1, nullptr, 0); - auto* oleCharString = new OLECHAR[len]; - MultiByteToWideChar(CP_UTF8, 0, errorMessage, -1, oleCharString, len); - return SysAllocString(oleCharString); - } - return SysAllocString(L"Successfully verified CID chunk"); -} - -static BSTR XP_SetConfirmationID(BSTR confirmationID) { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - if (0 == wcscmp(XP_GetWPALeft(), L"An error occurred at GetWPALeft: Windows is activated")) { - return SysAllocString(L"An error occurred at GetWPALeft: Windows is activated"); - } - - const int length = static_cast(SysStringLen(confirmationID)); - char* str = new char[length + 1]; - WideCharToMultiByte(CP_UTF8, 0, confirmationID, length, str, length, nullptr, nullptr); - str[length] = '\0'; - - std::string inputString(str); - inputString.erase(std::remove(inputString.begin(), inputString.end(), '-'), inputString.end()); // remove -'s - for (size_t i = 0; i < inputString.length(); i += 6) { - std::string substring = inputString.substr(i, 6); - const char* cstr = substring.c_str(); - if (0 != wcscmp(XP_VerifyCheckDigits(ConvertCharToBSTR(cstr)), L"Successfully verified CID chunk")) { - return SysAllocString(L"An error occurred at XP_VerifyCheckDigits: Check for misspelling"); - } - } - - ULONG dwRetCode; - const HRESULT status = XP_LicenseAgent->DepositConfirmationId(confirmationID, &dwRetCode); - if (FAILED(status) || dwRetCode) { - const char* errorString = "An error occurred at DepositConfirmationId:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X %lu", errorString, static_cast(status), dwRetCode); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X %lu", errorString, static_cast(status), dwRetCode); - - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - - system("rundll32 setupapi,InstallHinfSection DEL_OOBE_ACTIVATE 132 syssetup.inf"); // remove activation shortcuts - - if (IsProcessRunning(L"wpabaln.exe")) { // end WPA notifier process if there - TerminateProcessByName(L"wpabaln.exe"); - } - - return SysAllocString(L"Successfully set confirmation ID"); -} - -static BSTR XP_GetInstallationID() { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - if (0 == wcscmp(XP_GetWPALeft(), L"An error occurred at GetWPALeft: Windows is activated")) { - return SysAllocString(L"An error occurred at GetWPALeft: Windows is activated"); - } - - BSTR installationID = nullptr; - const HRESULT status = XP_LicenseAgent->GenerateInstallationId(&installationID); - if (FAILED(status) || !installationID) { - const char* errorString = "An error occurred at GenerateInstallationId:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X", errorString, static_cast(status)); - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - else { - return installationID; - } -} - -static BSTR XP_SetProductKey(LPWSTR productKey) { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - std::wstring ws(productKey); - const std::string productKeyToRegex = std::string(ws.begin(), ws.end()); - const std::regex pattern("[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}"); - - if (!std::regex_match(productKeyToRegex, pattern)) { - return SysAllocString(L"An error occurred at regex_match: Product key is invalid"); - } - - if (0 == wcscmp(XP_GetWPALeft(), L"An error occurred at GetWPALeft: Windows is activated")) { - return SysAllocString(L"An error occurred at GetWPALeft: Windows is activated"); - } - - const HRESULT status = XP_LicenseAgent->SetProductKey(productKey); - if (FAILED(status)) { - const char* errorString = "An error occurred at SetProductKey:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X", errorString, static_cast(status)); - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - else { - return SysAllocString(L"Successfully set product key"); - } -} - -static BSTR XP_GetProductID() { - if (!XP_LoadLicenseManager()) { - return SysAllocString(L"An error occurred at XP_LoadLicenseManager: Failed to load"); - } - - BSTR productID = nullptr; - - const HRESULT status = XP_LicenseAgent->GetProductID(&productID); - if (FAILED(status)) { - const char* errorString = "An error occurred at GetProductID:"; - const int bufferSize = snprintf(nullptr, 0, "%s 0x%08X", errorString, static_cast(status)); - char* result = new char[bufferSize + 1]; - snprintf(result, bufferSize + 1, "%s 0x%08X", errorString, static_cast(status)); - const int wideCharSize = MultiByteToWideChar(CP_UTF8, 0, result, -1, nullptr, 0); - auto* wideResult = new OLECHAR[wideCharSize]; - MultiByteToWideChar(CP_UTF8, 0, result, -1, wideResult, wideCharSize); - return SysAllocString(wideResult); - } - else { - return productID; - } -} -#pragma endregion - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -int main(int argc, char* argv[]) -{ - const char* text = - "xpmgr - Windows XP License Manager (compiled on " __DATE__ " " __TIME__ ")\n" - "\n" - "Usage: \n" - "/dti: Gets the Installation ID\n" - "/atp [cid]: Sets Confirmation ID (If successful, activates Windows/Office) (can also read from stdin)\n" - "/xpr: Gets the days before activation is required (Grace period)\n" - "/xpr-eval: Gets the days before the evaluation period expires (Eval. copies only)\n" - "/ipk [pkey]: Sets/changes product key (can also read from stdin)\n" - "/dli: Gets the product ID of Windows\n" - "/?: Displays this message"; - - if (cmdOptionExists(argv, argv + argc, "--GetUsage")) { - std::cout << text; - return 0; - } - - specifiedProduct = "WindowsNT5x"; + if (info.dwMinorVersion != 1 && info.dwMinorVersion != 2) { + std::cout << "An error occurred: Windows management only works on Windows NT 5.1 and 5.2."; + if (info.dwMinorVersion == 0) { + std::cout << " You should be fine anyways, since Windows 2000 doesn't use Product Activation."; + return 4; + } + return 5; + } - if (std::strcmp(specifiedProduct, "WindowsNT5x") == 0) { - SYSTEM_INFO systemInfo; - GetNativeSystemInfo(&systemInfo); + // Check architecture #ifdef ENVIRONMENT32 - if (systemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { // running under WOW64 - if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { // is AMD64 - std::cout << "An error occurred at systemInfo: Incorrect version of xpmgr. You need to download the x64 version."; - } - else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { // is IA64 - std::cout << "An error occurred at systemInfo: Windows Product Activation does not exist on this platform."; - } - else { // is PPC, megafart 386, whatever else - std::cout << "An error occurred at systemInfo: Incorrect version of xpmgr. Send an issue at https://github.com/UMSKT/xpmgr/issues if you want to help us make a version for your platform!"; - } - return 1; - } + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + if (systemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { + if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + std::cout << "An error occurred: Incorrect version of xpmgr. You need to download the x64 version."; + return 1; + } + if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { + std::cout << "An error occurred: Windows Product Activation does not exist on this platform."; + return 1; + } + std::cout << "An error occurred: Incorrect version of xpmgr. Send an issue at https://github.com/UMSKT/xpmgr/issues if you want to help us make a version for your platform!"; + return 1; + } #endif - OSVERSIONINFOEX info = {}; - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - GetVersionEx(reinterpret_cast(&info)); - if (info.dwMajorVersion != 5) { - std::cout << "An error occurred at OSVERSIONINFOEX: Windows management only works on Windows NT 5.1 and 5.2."; - if (info.dwMajorVersion > 5) { - std::cout << " Use slmgr instead: https://learn.microsoft.com/en-us/windows-server/get-started/activation-slmgr-vbs-options"; - return 3; - } - return 2; - } - else { - if (info.dwMinorVersion != 1 && info.dwMinorVersion != 2) { - std::cout << "An error occurred at OSVERSIONINFOEX: Windows management only works on Windows NT 5.1 and 5.2."; - if (info.dwMinorVersion == 0) { - std::cout << " You should be fine anyways, since Windows 2000 doesn't use Product Activation."; - return 4; - } - return 5; - } - } - } + // Initialize the library + XPMGR_Initialize(); + + // Handle commands + const wchar_t* result = nullptr; + if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/dti")) { + result = XPMGR_GetInstallationID(); + } + else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/atp")) { + const char* confirmationId = CommandLineArgs::getCmdOption(argv, argv + argc, "/atp"); + if (!confirmationId) { + std::string pipedInput = CommandLineArgs::readFromStdin(); + if (!pipedInput.empty()) { + result = XPMGR_SetConfirmationID(convertCharArrayToLPCWSTR(pipedInput.c_str())); + } else { + std::cout << "An error occurred: No confirmation ID provided via argument or pipe\n\n" << USAGE_TEXT; + XPMGR_Cleanup(); + return 7; + } + } else { + result = XPMGR_SetConfirmationID(convertCharArrayToLPCWSTR(confirmationId)); + } + } + else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/xpr")) { + result = XPMGR_GetWPALeft(); + } + else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/xpr-eval")) { + result = XPMGR_GetEvalLeft(); + } + else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/ipk")) { + const char* productKey = CommandLineArgs::getCmdOption(argv, argv + argc, "/ipk"); + if (!productKey) { + std::string pipedInput = CommandLineArgs::readFromStdin(); + if (!pipedInput.empty()) { + result = XPMGR_SetProductKey(convertCharArrayToLPCWSTR(pipedInput.c_str())); + } else { + std::cout << "An error occurred: No product key provided via argument or pipe\n\n" << USAGE_TEXT; + XPMGR_Cleanup(); + return 7; + } + } else { + result = XPMGR_SetProductKey(convertCharArrayToLPCWSTR(productKey)); + } + } + else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/dli")) { + result = XPMGR_GetProductID(); + } + else { + std::cout << "An error occurred: No arguments listed\n\n" << USAGE_TEXT; + XPMGR_Cleanup(); + return 7; + } - if (cmdOptionExists(argv, argv + argc, "/dti")) { - std::cout << ConvertToStdString(XP_GetInstallationID()); - return 0; - } - else if (cmdOptionExists(argv, argv + argc, "/atp")) { - const char* confirmationId = getCmdOption(argv, argv + argc, "/atp"); - if (!confirmationId) { - // No argument provided, try reading from stdin - std::string pipedInput = readFromStdin(); - if (!pipedInput.empty()) { - std::cout << ConvertToStdString(XP_SetConfirmationID(ConvertCharToBSTR(pipedInput.c_str()))); - } else { - std::cout << "An error occurred at main: No confirmation ID provided via argument or pipe\n\n"; - std::cout << text; - return 7; - } - } else { - std::cout << ConvertToStdString(XP_SetConfirmationID(ConvertCharToBSTR(confirmationId))); - } - return 0; - } - else if (cmdOptionExists(argv, argv + argc, "/xpr")) { - std::cout << ConvertToStdString(XP_GetWPALeft()); - return 0; - } - else if (cmdOptionExists(argv, argv + argc, "/xpr-eval")) { - std::cout << ConvertToStdString(XP_GetEvalLeft()); - return 0; - } + if (result) { + char* output = convertWCharToChar(result); + std::cout << output; + delete[] output; + } - else if (cmdOptionExists(argv, argv + argc, "/ipk")) { - const char* productKey = getCmdOption(argv, argv + argc, "/ipk"); - if (!productKey) { - // No argument provided, try reading from stdin - std::string pipedInput = readFromStdin(); - if (!pipedInput.empty()) { - std::cout << ConvertToStdString(XP_SetProductKey(convertCharArrayToLPCWSTR(pipedInput.c_str()))); - } else { - std::cout << "An error occurred at main: No product key provided via argument or pipe\n\n"; - std::cout << text; - return 7; - } - } else { - std::cout << ConvertToStdString(XP_SetProductKey(convertCharArrayToLPCWSTR(productKey))); - } - return 0; - } - else if (cmdOptionExists(argv, argv + argc, "/dli")) { - std::cout << ConvertToStdString(XP_GetProductID()); - return 0; - } - else { - std::cout << "An error occurred at main: No arguments listed\n\n"; - std::cout << text; - return 7; - } + XPMGR_Cleanup(); + return 0; } -#pragma clang diagnostic pop diff --git a/xpmgr.h b/xpmgr.h new file mode 100644 index 0000000..df55e2d --- /dev/null +++ b/xpmgr.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// Core functions that match command line options +const wchar_t* XPMGR_GetInstallationID(); // /dti +const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId); // /atp +const wchar_t* XPMGR_GetWPALeft(); // /xpr +const wchar_t* XPMGR_GetEvalLeft(); // /xpr-eval +const wchar_t* XPMGR_SetProductKey(const wchar_t* productKey); // /ipk +const wchar_t* XPMGR_GetProductID(); // /dli + +// Additional utility functions +const wchar_t* XPMGR_GetLastError(); +void XPMGR_Initialize(); +void XPMGR_Cleanup(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/xpmgr_lib.cpp b/xpmgr_lib.cpp new file mode 100644 index 0000000..12ba57a --- /dev/null +++ b/xpmgr_lib.cpp @@ -0,0 +1,389 @@ +#include "xpmgr.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// CLSID and IID definitions +static CLSID XP_CLSID = { 0xACADF079, 0xCBCD, 0x4032, {0x83, 0xF2, 0xFA, 0x47, 0xC4, 0xDB, 0x09, 0x6F} }; +static IID XP_IID = { 0xB8CBAD79, 0x3F1F, 0x481A, { 0xBB, 0x0C, 0xE7, 0xBB, 0xD7, 0x7B, 0xDD, 0xD1 } }; + +// Interface definition +DECLARE_INTERFACE_(ICOMLicenseAgent, IDispatch) +{ +protected: + ~ICOMLicenseAgent() = default; + +public: + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef)() PURE; + STDMETHOD_(ULONG, Release)() PURE; + + /*** IDispatch methods ***/ + STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; + STDMETHOD(GetTypeInfo)(THIS_ UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) PURE; + STDMETHOD(GetIDsOfNames)(THIS_ REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid) PURE; + STDMETHOD(Invoke)(THIS_ DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) PURE; + + /*** ICOMLicenseAgent methods ***/ + STDMETHOD(Initialize)(THIS_ ULONG dwBPC, ULONG dwMode, BSTR bstrLicSource, ULONG* pdwRetCode) PURE; + STDMETHOD(GetFirstName)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetFirstName)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetLastName)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetLastName)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetOrgName)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetOrgName)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetEmail)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetEmail)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetPhone)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetPhone)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetAddress1)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetAddress1)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetCity)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetCity)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetState)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetState)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetCountryCode)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetCountryCode)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetCountryDesc)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetCountryDesc)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetZip)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetZip)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetIsoLanguage)(THIS_ ULONG* pdwVal) PURE; + STDMETHOD(SetIsoLanguage)(THIS_ ULONG dwNewVal) PURE; + STDMETHOD(GetMSUpdate)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetMSUpdate)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetMSOffer)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetMSOffer)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetOtherOffer)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetOtherOffer)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(GetAddress2)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(SetAddress2)(THIS_ BSTR bstrNewVal) PURE; + STDMETHOD(AsyncProcessHandshakeRequest)(THIS_ LONG bReviseCustInfo) PURE; + STDMETHOD(AsyncProcessNewLicenseRequest)() PURE; + STDMETHOD(AsyncProcessReissueLicenseRequest)() PURE; + STDMETHOD(AsyncProcessReviseCustInfoRequest)() PURE; + STDMETHOD(GetAsyncProcessReturnCode)(THIS_ ULONG* pdwRetCode) PURE; + STDMETHOD(AsyncProcessDroppedLicenseRequest)() PURE; + STDMETHOD(GenerateInstallationId)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(DepositConfirmationId)(THIS_ BSTR bstrVal, ULONG* pdwRetCode) PURE; + STDMETHOD(GetExpirationInfo)(THIS_ ULONG* pdwWPALeft, ULONG* pdwEvalLeft) PURE; + STDMETHOD(AsyncProcessRegistrationRequest)() PURE; + STDMETHOD(ProcessHandshakeRequest)(THIS_ LONG bReviseCustInfo) PURE; + STDMETHOD(ProcessNewLicenseRequest)() PURE; + STDMETHOD(ProcessDroppedLicenseRequest)() PURE; + STDMETHOD(ProcessReissueLicenseRequest)() PURE; + STDMETHOD(ProcessReviseCustInfoRequest)() PURE; + STDMETHOD(EnsureInternetConnection)() PURE; + STDMETHOD(SetProductKey)(THIS_ LPWSTR pszNewProductKey) PURE; + STDMETHOD(GetProductID)(THIS_ BSTR* pbstrVal) PURE; + STDMETHOD(VerifyCheckDigits)(THIS_ BSTR bstrCIDIID, LONG* pbValue) PURE; +}; + +// Static variables +static BOOL XP_ComInitialized = FALSE; +static ICOMLicenseAgent* XP_LicenseAgent = nullptr; +static wchar_t LastErrorMessage[1024] = L""; + +// Helper functions +static void SetLastErrorMessage(const wchar_t* message) { + wcscpy(LastErrorMessage, message); +} + +static BOOL XP_LoadLicenseManager() { + if (!XP_ComInitialized) { + const HRESULT status = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(status)) { + SetLastErrorMessage(L"Failed to initialize COM"); + return FALSE; + } + XP_ComInitialized = TRUE; + } + + if (!XP_LicenseAgent) { + HRESULT status = CoCreateInstance(XP_CLSID, nullptr, CLSCTX_INPROC_SERVER, XP_IID, reinterpret_cast(&XP_LicenseAgent)); + if (SUCCEEDED(status)) { + ULONG dwRetCode; + status = XP_LicenseAgent->Initialize(0xC475, 3, nullptr, &dwRetCode); + if (SUCCEEDED(status) && dwRetCode == 0) { + return TRUE; + } + XP_LicenseAgent->Release(); + XP_LicenseAgent = nullptr; + } + SetLastErrorMessage(L"Failed to create license manager instance"); + return FALSE; + } + return TRUE; +} + +// Helper function to verify a confirmation ID chunk +static bool VerifyConfirmationIDChunk(ICOMLicenseAgent* agent, const wchar_t* chunk) { + LONG pbValue = 0; + BSTR bstrChunk = SysAllocString(chunk); + HRESULT status = agent->VerifyCheckDigits(bstrChunk, &pbValue); + SysFreeString(bstrChunk); + return SUCCEEDED(status) && pbValue != 0; +} + +struct ProcessHandle { + HANDLE handle; + explicit ProcessHandle(HANDLE h) : handle(h) {} + ~ProcessHandle() { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } + operator HANDLE() const { return handle; } +}; + +bool IsProcessRunning(const wchar_t* processName) { + ProcessHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (snapshot == INVALID_HANDLE_VALUE) { + std::cout << "Failed to create process snapshot: " << GetLastError() << std::endl; + return false; + } + + PROCESSENTRY32W processEntry{}; + processEntry.dwSize = sizeof(PROCESSENTRY32W); + + if (!Process32FirstW(snapshot, &processEntry)) { + std::cout << "Failed to retrieve process information: " << GetLastError() << std::endl; + return false; + } + + do { + if (wcscmp(processEntry.szExeFile, processName) == 0) { + return true; + } + } while (Process32NextW(snapshot, &processEntry)); + + return false; +} + +bool TerminateProcessByName(const wchar_t* processName) { + ProcessHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (snapshot == INVALID_HANDLE_VALUE) { + std::cout << "Failed to create process snapshot: " << GetLastError() << std::endl; + return false; + } + + PROCESSENTRY32W processEntry{}; + processEntry.dwSize = sizeof(PROCESSENTRY32W); + + if (!Process32FirstW(snapshot, &processEntry)) { + std::cout << "Failed to retrieve process information: " << GetLastError() << std::endl; + return false; + } + + do { + if (wcscmp(processEntry.szExeFile, processName) == 0) { + ProcessHandle processHandle(OpenProcess(PROCESS_TERMINATE, FALSE, processEntry.th32ProcessID)); + if (processHandle == nullptr) { + std::cout << "Failed to open process: " << GetLastError() << std::endl; + return false; + } + + if (!TerminateProcess(processHandle, 0)) { + std::cout << "Failed to terminate process: " << GetLastError() << std::endl; + return false; + } + + return true; + } + } while (Process32NextW(snapshot, &processEntry)); + + std::cout << "Process not found: " << processName << std::endl; + return false; +} + +// Exported functions implementation +extern "C" { + +void XPMGR_Initialize() { + XP_LoadLicenseManager(); +} + +void XPMGR_Cleanup() { + if (XP_LicenseAgent) { + XP_LicenseAgent->Release(); + XP_LicenseAgent = nullptr; + } + if (XP_ComInitialized) { + CoUninitialize(); + XP_ComInitialized = FALSE; + } +} + +const wchar_t* XPMGR_GetLastError() { + return LastErrorMessage; +} + +const wchar_t* XPMGR_GetInstallationID() { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + BSTR installationID = nullptr; + const HRESULT status = XP_LicenseAgent->GenerateInstallationId(&installationID); + if (FAILED(status) || !installationID) { + SetLastErrorMessage(L"Failed to generate installation ID"); + return XPMGR_GetLastError(); + } + + static wchar_t result[256]; + wcscpy(result, installationID); + SysFreeString(installationID); + return result; +} + +const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId) { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + // Create a working copy and remove dashes + wchar_t cleanCid[256] = {0}; + const wchar_t* src = confirmationId; + wchar_t* dst = cleanCid; + while (*src) { + if (*src != L'-') { + *dst++ = *src; + } + src++; + } + *dst = 0; + + // Verify each 6-character chunk + size_t len = wcslen(cleanCid); + for (size_t i = 0; i < len; i += 6) { + wchar_t chunk[7] = {0}; + wcsncpy(chunk, cleanCid + i, 6); + if (!VerifyConfirmationIDChunk(XP_LicenseAgent, chunk)) { + SetLastErrorMessage(L"Invalid confirmation ID - verification failed"); + return XPMGR_GetLastError(); + } + } + + // Pass the original confirmation ID (with dashes if present) to the API + BSTR bstrConfirmationId = SysAllocString(confirmationId); + ULONG dwRetCode; + const HRESULT status = XP_LicenseAgent->DepositConfirmationId(bstrConfirmationId, &dwRetCode); + SysFreeString(bstrConfirmationId); + + if (FAILED(status) || dwRetCode) { + SetLastErrorMessage(L"Failed to deposit confirmation ID"); + return XPMGR_GetLastError(); + } + + // Handle WPA notifier process + if (IsProcessRunning(L"wpabaln.exe")) { + if (!TerminateProcessByName(L"wpabaln.exe")) { + std::cout << "Warning: Failed to terminate WPA notifier process." << std::endl; + } + } + + // Clean up activation files + HMODULE hMod = LoadLibraryW(L"setupapi.dll"); + if (hMod) { + using InstallHinfSectionFunc = BOOL (WINAPI*)(HWND, HINSTANCE, PCWSTR, INT); + if (auto installHinfSection = reinterpret_cast(GetProcAddress(hMod, "InstallHinfSectionW"))) { + SetLastError(0); + const BOOL result = installHinfSection(nullptr, nullptr, L"DEL_OOBE_ACTIVATE 132 syssetup.inf", 132); + const DWORD error = GetLastError(); + + if (!result && error != 0 && error != 6) { // Ignore error 6 (ERROR_INVALID_HANDLE) as it indicates success + std::cout << "Warning: Failed to remove activation reminders. Error: " << error << std::endl; + std::cout << "You can try to run the following command yourself: " << std::endl; + std::cout << "rundll32 setupapi,InstallHinfSection DEL_OOBE_ACTIVATE 132 syssetup.inf" << std::endl; + } + } + FreeLibrary(hMod); + } + + return L"Successfully set confirmation ID"; +} + +const wchar_t* XPMGR_GetWPALeft() { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + ULONG dwWPALeft = 0, dwEvalLeft = 0; + const HRESULT status = XP_LicenseAgent->GetExpirationInfo(&dwWPALeft, &dwEvalLeft); + if (FAILED(status)) { + SetLastErrorMessage(L"Failed to get expiration info"); + return XPMGR_GetLastError(); + } + + if (dwWPALeft == 0x7FFFFFFF) { + return L"Windows is activated"; + } + + static wchar_t result[16]; + _ultow(dwWPALeft, result, 10); + return result; +} + +const wchar_t* XPMGR_GetEvalLeft() { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + ULONG dwWPALeft = 0, dwEvalLeft = 0; + const HRESULT status = XP_LicenseAgent->GetExpirationInfo(&dwWPALeft, &dwEvalLeft); + if (FAILED(status)) { + SetLastErrorMessage(L"Failed to get expiration info"); + return XPMGR_GetLastError(); + } + + if (dwEvalLeft == 0x7FFFFFFF) { + return L"Not an evaluation copy"; + } + + static wchar_t result[16]; + _ultow(dwEvalLeft, result, 10); + return result; +} + +const wchar_t* XPMGR_SetProductKey(const wchar_t* productKey) { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + // Validate product key format + std::wregex pattern(L"[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}-[2346789BCDFGHJKMPQRTVWXY]{5}"); + if (!std::regex_match(productKey, pattern)) { + SetLastErrorMessage(L"Invalid product key format"); + return XPMGR_GetLastError(); + } + + const HRESULT status = XP_LicenseAgent->SetProductKey(const_cast(productKey)); + if (FAILED(status)) { + SetLastErrorMessage(L"Failed to set product key"); + return XPMGR_GetLastError(); + } + + return L"Successfully set product key"; +} + +const wchar_t* XPMGR_GetProductID() { + if (!XP_LoadLicenseManager()) { + return XPMGR_GetLastError(); + } + + BSTR productID = nullptr; + const HRESULT status = XP_LicenseAgent->GetProductID(&productID); + if (FAILED(status) || !productID) { + SetLastErrorMessage(L"Failed to get product ID"); + return XPMGR_GetLastError(); + } + + static wchar_t result[256]; + wcscpy(result, productID); + SysFreeString(productID); + return result; +} + +} \ No newline at end of file