5 Commits
Author SHA1 Message Date
thepwrtank18 831504110d Windows 98 'support' 2026-10-24 22:39:55 -04:00
TheTank20 1738a31bde Update windows.yml 2025-07-23 11:48:18 -05:00
TheTank20 bb1a1e7f3f Update windows.yml 2025-07-23 11:43:13 -05:00
TheTank20 3dfd6eb9aa Update windows.yml 2025-07-23 11:42:42 -05:00
TheTank20 32353cc35f Merge pull request #13 from UMSKT/rewrite
rewrite
2025-07-23 02:40:04 -05:00
8 changed files with 865 additions and 170 deletions

No files matched your search

+7
View File
@@ -52,9 +52,16 @@ jobs:
- name: Build xpmgr - name: Build xpmgr
run: | run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER=C:/TDM-GCC-64/bin/g++.exe cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER=C:/TDM-GCC-64/bin/g++.exe
cmake --build build --config Debug cmake --build build --config Debug
- name: Strip binaries
run: |
$env:PATH = 'C:\TDM-GCC-64\bin;' + $env:PATH
strip .\build\xpmgr_x86.exe
strip .\build\xpmgr_x64.exe
- name: Upload build artifact (x86) - name: Upload build artifact (x86)
uses: actions/[email protected] uses: actions/[email protected]
with: with:
+64 -15
View File
@@ -7,16 +7,46 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(SOURCES xpmgr.cpp) set(SOURCES xpmgr.cpp)
set(LIB_SOURCES xpmgr_lib.cpp) set(LIB_SOURCES xpmgr_lib.cpp)
# x86 is freestanding Pentium code. TDM-GCC's libgcc/libstdc++/CRT are built
# for a newer CPU and import XP SP2 APIs, so the x86 link does not use them.
set(X86_ARCH_FLAGS
-m32
-march=pentium
-mtune=pentium
-mfpmath=387
-mno-mmx
-mno-sse
-mno-sse2
-mno-3dnow
-fno-tree-vectorize
-fno-tree-loop-distribute-patterns
-fno-exceptions
-fno-rtti
-fno-threadsafe-statics
-fno-use-cxa-atexit
-fno-stack-protector
-fno-asynchronous-unwind-tables
-fno-unwind-tables
-ffreestanding
)
# Office XP's earliest hosts are Windows 98 and NT 4 SP6a. Keep the headers
# at that floor so Windows 2000 and XP APIs cannot become static imports.
set(X86_ARCH_DEFS
WINVER=0x0400
_WIN32_WINNT=0x0400
NTDDI_VERSION=0x04000000
ENVIRONMENT32
)
# ==== x86 build ==== # ==== x86 build ====
# Static library for x86
add_library(xpmgr_static_x86 STATIC ${LIB_SOURCES}) add_library(xpmgr_static_x86 STATIC ${LIB_SOURCES})
target_compile_options(xpmgr_static_x86 PRIVATE -m32) target_compile_options(xpmgr_static_x86 PRIVATE ${X86_ARCH_FLAGS})
target_link_libraries(xpmgr_static_x86 PRIVATE ole32 oleaut32 uuid) target_compile_definitions(xpmgr_static_x86 PRIVATE ${X86_ARCH_DEFS})
target_link_libraries(xpmgr_static_x86 PRIVATE ole32 oleaut32)
set(RES_X86 ${CMAKE_BINARY_DIR}/icon_x86.res) set(RES_X86 ${CMAKE_BINARY_DIR}/icon_x86.res)
# Create a custom target for the x86 resource compilation
add_custom_command( add_custom_command(
OUTPUT ${RES_X86} OUTPUT ${RES_X86}
COMMAND windres -F pe-i386 -O coff ${CMAKE_SOURCE_DIR}/icon.rc -o ${RES_X86} COMMAND windres -F pe-i386 -O coff ${CMAKE_SOURCE_DIR}/icon.rc -o ${RES_X86}
@@ -26,27 +56,48 @@ add_custom_command(
add_custom_target(xpmgr_x86_res ALL DEPENDS ${RES_X86}) add_custom_target(xpmgr_x86_res ALL DEPENDS ${RES_X86})
# Now define the executable and link dependencies add_executable(xpmgr_x86 ${SOURCES} crt_win32.cpp)
add_executable(xpmgr_x86 ${SOURCES})
add_dependencies(xpmgr_x86 xpmgr_x86_res) add_dependencies(xpmgr_x86 xpmgr_x86_res)
target_compile_options(xpmgr_x86 PRIVATE -m32) target_compile_options(xpmgr_x86 PRIVATE ${X86_ARCH_FLAGS})
target_link_options(xpmgr_x86 PRIVATE -m32) target_compile_definitions(xpmgr_x86 PRIVATE ${X86_ARCH_DEFS})
target_link_options(xpmgr_x86 PRIVATE
-m32
-nostdlib
-nostartfiles
-nodefaultlibs
-Wl,--subsystem,console:4.0
-Wl,--major-os-version,4
-Wl,--minor-os-version,0
-Wl,--major-subsystem-version,4
-Wl,--minor-subsystem-version,0
"SHELL:-Xlinker --stack -Xlinker 1048576,4096"
-Wl,--disable-auto-import
-Wl,-e,_mainCRTStartup
${RES_X86}
)
target_link_libraries(xpmgr_x86 PRIVATE xpmgr_static_x86 ole32 oleaut32 kernel32)
# Link .res file and static library get_filename_component(XPMGR_GCC_BIN "${CMAKE_CXX_COMPILER}" DIRECTORY)
target_link_libraries(xpmgr_x86 PRIVATE ole32 oleaut32 uuid xpmgr_static_x86) find_program(XPMGR_OBJDUMP NAMES objdump HINTS "${XPMGR_GCC_BIN}")
target_link_options(xpmgr_x86 PRIVATE -Wl,--subsystem,console ${RES_X86}) if(NOT XPMGR_OBJDUMP)
message(FATAL_ERROR "objdump is required to verify the Pentium / Office XP host floor")
endif()
add_custom_command(TARGET xpmgr_x86 POST_BUILD
COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${CMAKE_SOURCE_DIR}/tools/check-x86-baseline.ps1" -Exe "$<TARGET_FILE:xpmgr_x86>" -Objdump "${XPMGR_OBJDUMP}"
COMMENT "Checking xpmgr_x86 for a Pentium and the Office XP host floor"
VERBATIM
)
# ==== x64 build ==== # ==== x64 build ====
# Static library for x64
add_library(xpmgr_static_x64 STATIC ${LIB_SOURCES}) add_library(xpmgr_static_x64 STATIC ${LIB_SOURCES})
target_compile_options(xpmgr_static_x64 PRIVATE -m64) target_compile_options(xpmgr_static_x64 PRIVATE -m64)
target_link_libraries(xpmgr_static_x64 PRIVATE ole32 oleaut32 uuid) target_link_libraries(xpmgr_static_x64 PRIVATE ole32 oleaut32 uuid)
set(RES_X64 ${CMAKE_BINARY_DIR}/icon_x64.res) set(RES_X64 ${CMAKE_BINARY_DIR}/icon_x64.res)
# Create a custom target for the x64 resource compilation
add_custom_command( add_custom_command(
OUTPUT ${RES_X64} OUTPUT ${RES_X64}
COMMAND windres -O coff ${CMAKE_SOURCE_DIR}/icon.rc -o ${RES_X64} COMMAND windres -O coff ${CMAKE_SOURCE_DIR}/icon.rc -o ${RES_X64}
@@ -56,14 +107,12 @@ add_custom_command(
add_custom_target(xpmgr_x64_res ALL DEPENDS ${RES_X64}) add_custom_target(xpmgr_x64_res ALL DEPENDS ${RES_X64})
# Now define the executable and link dependencies
add_executable(xpmgr_x64 ${SOURCES}) add_executable(xpmgr_x64 ${SOURCES})
add_dependencies(xpmgr_x64 xpmgr_x64_res) add_dependencies(xpmgr_x64 xpmgr_x64_res)
target_compile_options(xpmgr_x64 PRIVATE -m64) target_compile_options(xpmgr_x64 PRIVATE -m64)
target_link_options(xpmgr_x64 PRIVATE -m64) target_link_options(xpmgr_x64 PRIVATE -m64)
# Link .res file and static library
target_link_libraries(xpmgr_x64 PRIVATE ole32 oleaut32 uuid xpmgr_static_x64) target_link_libraries(xpmgr_x64 PRIVATE ole32 oleaut32 uuid xpmgr_static_x64)
target_link_options(xpmgr_x64 PRIVATE -Wl,--subsystem,console ${RES_X64}) target_link_options(xpmgr_x64 PRIVATE -Wl,--subsystem,console ${RES_X64})
+5 -8
View File
@@ -1,24 +1,21 @@
# xpmgr # xpmgr
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/UMSKT/xpmgr/windows.yml) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/UMSKT/xpmgr/windows.yml)
Command line interface for Windows XP license management, inspired by Microsoft's "slmgr". Command line interface for managing licenses of older versions of Windows/Office (starting with Windows and office XP), inspired by Microsoft's slmgr.
## Usage ## Usage
**You need to download the right version for your edition of Windows XP/2003!**
In most cases, you'll need `xpmgr_x86.exe`. In most cases, you'll need `xpmgr_x86.exe`.
If you have Windows XP Professional x64 Edition or a x64 version of Windows Server 2003, you need `xpmgr_x64.exe`. If you are trying to manage Windows XP Professional x64 Edition or an x64 version of Windows Server 2003, you need `xpmgr_x64.exe`.
If you have an Itanium version of Windows XP or Server 2003, Windows Product Activation is not on those editions, so there is no need for this tool.
`/dli`: Gets the product ID (ex: `76477-755-3803223-21229`). `/dli`: Gets the product ID (ex: `76477-755-3803223-21229`).
`/ipk`: Sets the product key (tested to work with dashes). If already activated, this should give an error instead. `/ipk`: Sets the product key (tested to work with dashes). If already activated, this should give an error instead.
`/dti`: Gets an Installation ID with **no dashes** (ex: `253286028742154311079061239762245184619981623171292574`). If already activated, this should give an error instead. `/dti`: Gets an Installation ID (ex: `253286028742154311079061239762245184619981623171292574`). If already activated, this should give an error instead.
`/atp`: Sets a Confirmation ID (tested to work with dashes). If already activated, this should give an error instead. If successful, this also removes the "Activate Windows" shortcuts and kills the WPA notifier process (`wpabaln.exe`) if it's running. `/atp`: Sets a Confirmation ID. If already activated, this should give an error instead. If successful, this also removes the "Activate Windows" shortcuts and kills the WPA notifier process (`wpabaln.exe`) if it's running.
`/xpr`: Gets the number of days before activation is required, in days. If already activated, this should give an error instead. `/xpr`: Gets the number of days before activation is required, in days. If already activated, this should give an error instead.
@@ -42,7 +39,7 @@ This code is licensed under the LGPLv3 license.
> Versions from May 7th, 2025 and earlier are licensed under the EPL license. > Versions from May 7th, 2025 and earlier are licensed under the EPL license.
## Building ## Building
Make sure Visual Studio 2017 or later is installed (recommended, might not be needed). Then, install [TDM-GCC](https://jmeubank.github.io/tdm-gcc/about/). Then, use CMake to build: Install [TDM-GCC](https://jmeubank.github.io/tdm-gcc/about/). Then, use CMake to build:
``` ```
cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER=C:/TDM-GCC-64/bin/g++.exe cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_CXX_COMPILER=C:/TDM-GCC-64/bin/g++.exe
cmake --build build --config Debug cmake --build build --config Debug
+167
View File
@@ -0,0 +1,167 @@
/* Freestanding startup for the Pentium / Office XP host-floor x86 build.
Linked with -nostdlib so libgcc, libstdc++, and the MinGW CRT stay out. */
#include <windows.h>
extern int main(int argc, char** argv);
typedef void (*xpmgr_ctor)(void);
extern "C" xpmgr_ctor __init_array_start[] __attribute__((weak));
extern "C" xpmgr_ctor __init_array_end[] __attribute__((weak));
extern "C" void* memset(void* dst, int value, size_t count) {
unsigned char* bytes = (unsigned char*)dst;
unsigned char fill = (unsigned char)value;
while (count--) {
*bytes++ = fill;
}
return dst;
}
extern "C" void* memcpy(void* dst, const void* src, size_t count) {
unsigned char* out = (unsigned char*)dst;
const unsigned char* in = (const unsigned char*)src;
while (count--) {
*out++ = *in++;
}
return dst;
}
extern "C" void* memmove(void* dst, const void* src, size_t count) {
unsigned char* out = (unsigned char*)dst;
const unsigned char* in = (const unsigned char*)src;
size_t index;
if (out == in || count == 0) {
return dst;
}
if (out < in) {
for (index = 0; index < count; index++) {
out[index] = in[index];
}
} else {
index = count;
while (index > 0) {
index--;
out[index] = in[index];
}
}
return dst;
}
extern "C" int memcmp(const void* left, const void* right, size_t count) {
const unsigned char* a = (const unsigned char*)left;
const unsigned char* b = (const unsigned char*)right;
while (count--) {
if (*a != *b) {
return *a - *b;
}
a++;
b++;
}
return 0;
}
/* GCC calls this when a frame is at least one page. EAX is the frame size
and must be preserved; the caller subtracts it from ESP afterwards. */
extern "C" __attribute__((naked)) void __chkstk_ms(void) {
__asm__ __volatile__(
"pushl %ecx\n\t"
"pushl %eax\n\t"
"cmpl $0x1000, %eax\n\t"
"leal 12(%esp), %ecx\n\t"
"jb 1f\n\t"
"2:\n\t"
"subl $0x1000, %ecx\n\t"
"testl %eax, (%ecx)\n\t"
"subl $0x1000, %eax\n\t"
"cmpl $0x1000, %eax\n\t"
"ja 2b\n\t"
"1:\n\t"
"subl %eax, %ecx\n\t"
"testl %eax, (%ecx)\n\t"
"popl %eax\n\t"
"popl %ecx\n\t"
"ret\n\t"
);
}
extern "C" void __cxa_pure_virtual(void) {
ExitProcess(1);
}
extern "C" void __main(void) {
xpmgr_ctor* ctor;
if (!__init_array_start || !__init_array_end) {
return;
}
for (ctor = __init_array_start; ctor < __init_array_end; ctor++) {
if (*ctor) {
(*ctor)();
}
}
}
static int is_space(char ch) {
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
}
extern "C" void mainCRTStartup(void) {
char* command = GetCommandLineA();
char* buffer;
char** argv;
int length = 0;
int argc = 0;
char* cursor;
HANDLE heap = GetProcessHeap();
if (!command) {
command = (char*)"";
}
while (command[length]) {
length++;
}
buffer = (char*)HeapAlloc(heap, 0, (SIZE_T)length + 1);
argv = (char**)HeapAlloc(heap, HEAP_ZERO_MEMORY, (SIZE_T)(length + 2) * sizeof(char*));
if (!buffer || !argv) {
ExitProcess(1);
}
for (int index = 0; index <= length; index++) {
buffer[index] = command[index];
}
cursor = buffer;
while (*cursor) {
char* out;
int quoted = 0;
int more;
while (is_space(*cursor)) {
cursor++;
}
if (!*cursor) {
break;
}
argv[argc++] = cursor;
out = cursor;
while (*cursor) {
if (*cursor == '"') {
quoted = !quoted;
cursor++;
continue;
}
if (!quoted && is_space(*cursor)) {
break;
}
*out++ = *cursor++;
}
more = *cursor != 0;
*out = 0;
if (more) {
cursor++;
}
}
argv[argc] = NULL;
ExitProcess((UINT)main(argc, argv));
}
+160
View File
@@ -0,0 +1,160 @@
param(
[Parameter(Mandatory = $true)][string]$Exe,
[Parameter(Mandatory = $true)][string]$Objdump
)
$ErrorActionPreference = "Stop"
function Fail([string]$Message) {
Write-Host "x86 baseline check failed: $Message"
exit 1
}
if (-not (Test-Path -LiteralPath $Exe)) {
Fail "missing binary: $Exe"
}
if (-not (Test-Path -LiteralPath $Objdump)) {
Fail "missing objdump: $Objdump"
}
$format = & $Objdump -f -- $Exe 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) {
Fail "objdump -f failed: $format"
}
if ($format -notmatch "pei-i386") {
Fail "expected a PE32 i386 binary"
}
$pe = & $Objdump -p -- $Exe 2>&1 | Out-String
if ($LASTEXITCODE -ne 0) {
Fail "objdump -p failed: $pe"
}
function PeField([string]$Text, [string]$Name) {
if ($Text -match ("(?m)^\s*" + [regex]::Escape($Name) + "\s+(\d+)")) {
return [int]$Matches[1]
}
return $null
}
$osMajor = PeField $pe "MajorOSystemVersion"
$osMinor = PeField $pe "MinorOSystemVersion"
$subMajor = PeField $pe "MajorSubsystemVersion"
$subMinor = PeField $pe "MinorSubsystemVersion"
if ($osMajor -ne 4 -or $osMinor -ne 0 -or $subMajor -ne 4 -or $subMinor -ne 0) {
Fail "expected Office XP host subsystem 4.0, got OS $osMajor.$osMinor subsystem $subMajor.$subMinor"
}
$badDlls = "(?i)(libstdc\+\+|libgcc|libatomic|libssp|msvcp\d*|vcruntime\d*|ucrtbase|api-ms-win|msvcr\d+|msvcrt)"
$allowedDlls = @{
"kernel32.dll" = $true
"ole32.dll" = $true
"oleaut32.dll" = $true
}
$badImports = @{
"encodepointer" = $true
"decodepointer" = $true
"encodesystempointer" = $true
"decodesystempointer" = $true
"gettickcount64" = $true
"initializecriticalsectionex" = $true
"flsalloc" = $true
"flsfree" = $true
"flsgetvalue" = $true
"flssetvalue" = $true
"comparestringex" = $true
"lcmapstringex" = $true
"getlocaleinfoex" = $true
"lcidtolocalename" = $true
"localenametolcid" = $true
"initializeconditionvariable" = $true
"sleepconditionvariablecs" = $true
"sleepconditionvariablesrw" = $true
"wakeconditionvariable" = $true
"wakeallconditionvariable" = $true
"initializesrwlock" = $true
"acquiresrwlockexclusive" = $true
"releasesrwlockexclusive" = $true
"tryacquiresrwlockexclusive" = $true
"getfileinformationbyhandleex" = $true
"setfileinformationbyhandle" = $true
"getfinalpathnamebyhandlea" = $true
"getfinalpathnamebyhandlew" = $true
"createsymboliclinka" = $true
"createsymboliclinkw" = $true
"getmodulehandleexa" = $true
"getmodulehandleexw" = $true
"iswow64process" = $true
"interlockedcompareexchange64" = $true
"heapsetinformation" = $true
"__getmainargs" = $true
"__wgetmainargs" = $true
"__set_app_type" = $true
"_amsg_exit" = $true
"__p__fmode" = $true
"__p__commode" = $true
"__p__environ" = $true
"getnativesysteminfo" = $true
"createtoolhelp32snapshot" = $true
"process32first" = $true
"process32firsta" = $true
"process32firstw" = $true
"process32next" = $true
"process32nexta" = $true
"process32nextw" = $true
"loadlibraryw" = $true
}
$importHits = New-Object System.Collections.Generic.List[string]
foreach ($line in ($pe -split "`r?`n")) {
if ($line -match "DLL Name:\s*(\S+)") {
$dll = $Matches[1]
if (-not $allowedDlls.ContainsKey($dll.ToLowerInvariant()) -or $dll -match $badDlls) {
$importHits.Add("DLL $dll")
}
}
if ($line -match "^\s+[0-9A-Fa-f]+\s+[0-9A-Fa-f]+\s+(\S+)") {
$name = $Matches[1]
if ($name -match "^(.*)@\d+$") {
$name = $Matches[1]
}
if ($badImports.ContainsKey($name.ToLowerInvariant())) {
$importHits.Add($name)
}
}
}
if ($importHits.Count -gt 0) {
$shown = ($importHits | Select-Object -First 20) -join ", "
Fail "post-RTM or CRT imports: $shown"
}
$badMnemonic = "(?i)^(cmov[a-z]*|fcmov[a-z]*|emms|nopl|nopw|fcomi|fcomip|fucomi|fucomip|movaps|movups|movapd|movupd|movdqa|movdqu|movss|movhps|movlps|movlhps|movhlps|xorps|xorpd|andps|andpd|orps|orpd|addps|addpd|addss|addsd|subps|subpd|subss|subsd|mulps|mulpd|mulss|mulsd|divps|divpd|divss|divsd|cvtsi2ss|cvtsi2sd|cvttss2si|cvttsd2si|cvtss2si|cvtsd2si|pshufd|pshuflw|pshufhw|pshufw|punpck[a-z]+|packss[a-z]+|packus[a-z]+|pcmp[a-z]+|padd[bwdq]|psub[bwdq]|pmullw|pmulhw|pmulhuw|pmaddwd|pavgb|pavgw|pextrw|pinsrw|pmovmskb|prefetch[a-z]*|sfence|mfence|lfence|maskmovq|movntq|movntps|lddqu|pause)\b"
$badRegister = "(?i)%(mm|xmm|ymm|zmm)[0-9]"
$insnHits = New-Object System.Collections.Generic.List[string]
$disasm = & $Objdump -d -M att -- $Exe 2>&1
if ($LASTEXITCODE -ne 0) {
Fail "objdump -d failed"
}
foreach ($line in $disasm) {
$text = [string]$line
$insn = $null
if ($text.Contains("`t")) {
$parts = $text.Split("`t")
$insn = $parts[$parts.Length - 1].Trim()
}
if (-not $insn) {
continue
}
if ($insn -match $badMnemonic -or $insn -match $badRegister) {
$insnHits.Add($text.Trim())
if ($insnHits.Count -ge 20) {
break
}
}
}
if ($insnHits.Count -gt 0) {
Fail ("forbidden instructions:`n" + ($insnHits -join "`n"))
}
Write-Host "x86 baseline check passed (Pentium, Office XP host floor): $Exe"
exit 0
+165 -98
View File
@@ -2,53 +2,95 @@
typedef struct IUnknown IUnknown; typedef struct IUnknown IUnknown;
#include <windows.h> #include <windows.h>
#include <iostream>
#include <string>
#include <algorithm>
#include "xpmgr.h" #include "xpmgr.h"
#include "xpmgr_util.h"
// Command line handling static int option_exists(int argc, char** argv, const char* option) {
struct CommandLineArgs { for (int index = 1; index < argc; index++) {
static const char* getCmdOption(char** begin, char** end, const std::string& option) { if (xpmgr_streq(argv[index], option)) {
const char* opt = option.c_str(); return 1;
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;
} }
return 0;
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;
}
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 "";
}
};
// 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* static const char* option_value(int argc, char** argv, const char* option) {
char* convertWCharToChar(const wchar_t* wcharArray) { for (int index = 1; index < argc - 1; index++) {
const int size = WideCharToMultiByte(CP_UTF8, 0, wcharArray, -1, nullptr, 0, nullptr, nullptr); if (xpmgr_streq(argv[index], option)) {
auto* charStr = new char[size]; return argv[index + 1];
WideCharToMultiByte(CP_UTF8, 0, wcharArray, -1, charStr, size, nullptr, nullptr); }
return charStr; }
return NULL;
}
static char* read_stdin_line(void) {
HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
char* buffer;
int count = 0;
const int cap = 4096;
if (input == NULL || input == INVALID_HANDLE_VALUE) {
return NULL;
}
buffer = (char*)HeapAlloc(GetProcessHeap(), 0, cap);
if (!buffer) {
return NULL;
}
while (count < cap - 1) {
char ch;
DWORD got = 0;
if (!ReadFile(input, &ch, 1, &got, NULL) || got == 0) {
break;
}
if (ch == '\n') {
break;
}
buffer[count++] = ch;
}
while (count > 0) {
char ch = buffer[count - 1];
if (ch != ' ' && ch != '\t' && ch != '\r') {
break;
}
count--;
}
buffer[count] = 0;
return buffer;
}
static int finish(int code) {
XPMGR_Cleanup();
return code;
}
static int windows_command_selected(int argc, char** argv) {
return option_exists(argc, argv, "/dti")
|| option_exists(argc, argv, "/atp")
|| option_exists(argc, argv, "/xpr")
|| option_exists(argc, argv, "/xpr-eval")
|| option_exists(argc, argv, "/ipk")
|| option_exists(argc, argv, "/dli");
}
/* Windows product activation is XP/2003 only. Office hosts must still reach command dispatch. */
static int windows_host_error(const OSVERSIONINFOA* info) {
if (info->dwPlatformId == VER_PLATFORM_WIN32_NT
&& info->dwMajorVersion == 5
&& (info->dwMinorVersion == 1 || info->dwMinorVersion == 2)) {
return 0;
}
xpmgr_write("An error occurred: Windows management only works on Windows NT 5.1 and 5.2.");
if (info->dwPlatformId == VER_PLATFORM_WIN32_NT && info->dwMajorVersion > 5) {
xpmgr_write(" Use slmgr instead: https://learn.microsoft.com/en-us/windows-server/get-started/activation-slmgr-vbs-options");
return 3;
}
if (info->dwPlatformId == VER_PLATFORM_WIN32_NT && info->dwMajorVersion == 5 && info->dwMinorVersion == 0) {
xpmgr_write(" You should be fine anyways, since Windows 2000 doesn't use Product Activation.");
return 4;
}
if (info->dwPlatformId == VER_PLATFORM_WIN32_NT && info->dwMajorVersion == 5) {
return 5;
}
return 2;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
@@ -63,111 +105,136 @@ int main(int argc, char* argv[]) {
"/ipk [pkey]: Sets/changes product key (can also read from stdin)\n" "/ipk [pkey]: Sets/changes product key (can also read from stdin)\n"
"/dli: Gets the product ID of Windows\n" "/dli: Gets the product ID of Windows\n"
"/?: Displays this message"; "/?: Displays this message";
const wchar_t* result = NULL;
wchar_t* wide = NULL;
if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "--GetUsage")) { if (option_exists(argc, argv, "/?")) {
std::cout << USAGE_TEXT; xpmgr_write(USAGE_TEXT);
return 0; return 0;
} }
// Check Windows version OSVERSIONINFOA info = {};
OSVERSIONINFOEX info = {}; int hostError;
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); info.dwOSVersionInfoSize = sizeof(info);
GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&info)); if (!GetVersionExA(&info)) {
xpmgr_write("An error occurred: Could not read the operating system version.\n");
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; return 2;
} }
if (info.dwMinorVersion != 1 && info.dwMinorVersion != 2) { if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && info.dwMinorVersion == 0) {
std::cout << "An error occurred: Windows management only works on Windows NT 5.1 and 5.2."; xpmgr_write("An error occurred: This program requires Windows 98 or later.\n");
if (info.dwMinorVersion == 0) { return 2;
std::cout << " You should be fine anyways, since Windows 2000 doesn't use Product Activation.";
return 4;
} }
return 5;
if (!windows_command_selected(argc, argv)) {
xpmgr_write("An error occurred: No arguments listed\n\n");
xpmgr_write(USAGE_TEXT);
return 7;
}
hostError = windows_host_error(&info);
if (hostError) {
return hostError;
} }
// Check architecture
#ifdef ENVIRONMENT32 #ifdef ENVIRONMENT32
/* XP x64 WPA lives in the 64-bit process. Office is 32-bit, so this stays on the Windows path. */
SYSTEM_INFO systemInfo; SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo); typedef void (WINAPI *GetNativeSystemInfoFn)(LPSYSTEM_INFO);
HMODULE kernel = GetModuleHandleA("kernel32.dll");
GetNativeSystemInfoFn nativeInfo = NULL;
if (kernel) {
nativeInfo = (GetNativeSystemInfoFn)GetProcAddress(kernel, "GetNativeSystemInfo");
}
if (nativeInfo) {
nativeInfo(&systemInfo);
} else {
GetSystemInfo(&systemInfo);
}
if (systemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { if (systemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) {
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
std::cout << "An error occurred: Incorrect version of xpmgr. You need to download the x64 version."; xpmgr_write("An error occurred: Incorrect version of xpmgr. You need to download the x64 version.");
return 1; return 1;
} }
if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
std::cout << "An error occurred: Windows Product Activation does not exist on this platform."; xpmgr_write("An error occurred: Windows Product Activation does not exist on this platform.");
return 1; 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!"; xpmgr_write("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; return 1;
} }
#endif #endif
// Initialize the library
XPMGR_Initialize(); XPMGR_Initialize();
// Handle commands if (option_exists(argc, argv, "/dti")) {
const wchar_t* result = nullptr;
if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/dti")) {
result = XPMGR_GetInstallationID(); result = XPMGR_GetInstallationID();
} }
else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/atp")) { else if (option_exists(argc, argv, "/atp")) {
const char* confirmationId = CommandLineArgs::getCmdOption(argv, argv + argc, "/atp"); const char* confirmationId = option_value(argc, argv, "/atp");
char* piped = NULL;
if (!confirmationId) { if (!confirmationId) {
std::string pipedInput = CommandLineArgs::readFromStdin(); piped = read_stdin_line();
if (!pipedInput.empty()) { if (!piped || !piped[0]) {
result = XPMGR_SetConfirmationID(convertCharArrayToLPCWSTR(pipedInput.c_str())); xpmgr_write("An error occurred: No confirmation ID provided via argument or pipe\n\n");
xpmgr_write(USAGE_TEXT);
xpmgr_free(piped);
return finish(7);
}
wide = xpmgr_widen(piped);
xpmgr_free(piped);
} else { } else {
std::cout << "An error occurred: No confirmation ID provided via argument or pipe\n\n" << USAGE_TEXT; wide = xpmgr_widen(confirmationId);
XPMGR_Cleanup();
return 7;
} }
} else { if (!wide) {
result = XPMGR_SetConfirmationID(convertCharArrayToLPCWSTR(confirmationId)); xpmgr_write("An error occurred: Out of memory\n");
return finish(7);
} }
result = XPMGR_SetConfirmationID(wide);
} }
else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/xpr")) { else if (option_exists(argc, argv, "/xpr")) {
result = XPMGR_GetWPALeft(); result = XPMGR_GetWPALeft();
} }
else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/xpr-eval")) { else if (option_exists(argc, argv, "/xpr-eval")) {
result = XPMGR_GetEvalLeft(); result = XPMGR_GetEvalLeft();
} }
else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/ipk")) { else if (option_exists(argc, argv, "/ipk")) {
const char* productKey = CommandLineArgs::getCmdOption(argv, argv + argc, "/ipk"); const char* productKey = option_value(argc, argv, "/ipk");
char* piped = NULL;
if (!productKey) { if (!productKey) {
std::string pipedInput = CommandLineArgs::readFromStdin(); piped = read_stdin_line();
if (!pipedInput.empty()) { if (!piped || !piped[0]) {
result = XPMGR_SetProductKey(convertCharArrayToLPCWSTR(pipedInput.c_str())); xpmgr_write("An error occurred: No product key provided via argument or pipe\n\n");
xpmgr_write(USAGE_TEXT);
xpmgr_free(piped);
return finish(7);
}
wide = xpmgr_widen(piped);
xpmgr_free(piped);
} else { } else {
std::cout << "An error occurred: No product key provided via argument or pipe\n\n" << USAGE_TEXT; wide = xpmgr_widen(productKey);
XPMGR_Cleanup();
return 7;
} }
} else { if (!wide) {
result = XPMGR_SetProductKey(convertCharArrayToLPCWSTR(productKey)); xpmgr_write("An error occurred: Out of memory\n");
return finish(7);
} }
result = XPMGR_SetProductKey(wide);
} }
else if (CommandLineArgs::cmdOptionExists(argv, argv + argc, "/dli")) { else if (option_exists(argc, argv, "/dli")) {
result = XPMGR_GetProductID(); result = XPMGR_GetProductID();
} }
else { else {
std::cout << "An error occurred: No arguments listed\n\n" << USAGE_TEXT; xpmgr_write("An error occurred: No arguments listed\n\n");
XPMGR_Cleanup(); xpmgr_write(USAGE_TEXT);
return 7; return finish(7);
} }
if (result) { if (result) {
char* output = convertWCharToChar(result); char* output = xpmgr_narrow(result);
std::cout << output; xpmgr_write(output);
delete[] output; xpmgr_free(output);
} }
xpmgr_free(wide);
XPMGR_Cleanup(); XPMGR_Cleanup();
return 0; return 0;
+175 -49
View File
@@ -2,11 +2,8 @@
#include <windows.h> #include <windows.h>
#include <objbase.h> #include <objbase.h>
#include <oleauto.h> #include <oleauto.h>
#include <regex>
#include <string>
#include <algorithm>
#include <TlHelp32.h> #include <TlHelp32.h>
#include <iostream> #include "xpmgr_util.h"
// CLSID and IID definitions // CLSID and IID definitions
static CLSID XP_CLSID = { 0xACADF079, 0xCBCD, 0x4032, {0x83, 0xF2, 0xFA, 0x47, 0xC4, 0xDB, 0x09, 0x6F} }; static CLSID XP_CLSID = { 0xACADF079, 0xCBCD, 0x4032, {0x83, 0xF2, 0xFA, 0x47, 0xC4, 0xDB, 0x09, 0x6F} };
@@ -92,7 +89,68 @@ static wchar_t LastErrorMessage[1024] = L"";
// Helper functions // Helper functions
static void SetLastErrorMessage(const wchar_t* message) { static void SetLastErrorMessage(const wchar_t* message) {
wcscpy(LastErrorMessage, message); lstrcpynW(LastErrorMessage, message, 1024);
}
static unsigned wide_length(const wchar_t* text) {
unsigned length = 0;
if (!text) {
return 0;
}
while (text[length]) {
length++;
}
return length;
}
static void write_ulong_wide(unsigned long value, wchar_t* out) {
wchar_t digits[16];
int count = 0;
int index = 0;
if (value == 0) {
out[0] = L'0';
out[1] = 0;
return;
}
while (value && count < 16) {
digits[count++] = (wchar_t)(L'0' + (value % 10));
value /= 10;
}
while (count > 0) {
out[index++] = digits[--count];
}
out[index] = 0;
}
static int product_key_char(wchar_t ch) {
const wchar_t* alphabet = L"2346789BCDFGHJKMPQRTVWXY";
for (int index = 0; alphabet[index]; index++) {
if (alphabet[index] == ch) {
return 1;
}
}
return 0;
}
static int product_key_ok(const wchar_t* key) {
if (!key) {
return 0;
}
for (int group = 0; group < 5; group++) {
if (group) {
if (*key != L'-') {
return 0;
}
key++;
}
for (int index = 0; index < 5; index++) {
if (!product_key_char(*key)) {
return 0;
}
key++;
}
}
return *key == 0;
} }
static BOOL XP_LoadLicenseManager() { static BOOL XP_LoadLicenseManager() {
@@ -134,67 +192,125 @@ static bool VerifyConfirmationIDChunk(ICOMLicenseAgent* agent, const wchar_t* ch
struct ProcessHandle { struct ProcessHandle {
HANDLE handle; HANDLE handle;
explicit ProcessHandle(HANDLE h) : handle(h) {} explicit ProcessHandle(HANDLE h) : handle(h) {}
~ProcessHandle() { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); } ~ProcessHandle() {
if (handle && handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
}
}
operator HANDLE() const { return handle; } operator HANDLE() const { return handle; }
}; };
bool IsProcessRunning(const wchar_t* processName) { typedef HANDLE (WINAPI *CreateToolhelp32SnapshotFn)(DWORD flags, DWORD processId);
ProcessHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); typedef BOOL (WINAPI *Process32Fn)(HANDLE snapshot, LPPROCESSENTRY32 entry);
if (snapshot == INVALID_HANDLE_VALUE) {
std::cout << "Failed to create process snapshot: " << GetLastError() << std::endl; struct Toolhelp {
HANDLE snapshot;
Process32Fn first;
Process32Fn next;
};
/* NT 4 has no ToolHelp in kernel32. Windows 98 has the ANSI exports.
Resolve them here so neither host gets a static import. Missing exports skip cleanup. */
static int open_process_snapshot(Toolhelp* toolhelp) {
HMODULE kernel = GetModuleHandleA("kernel32.dll");
CreateToolhelp32SnapshotFn createSnapshot;
if (!kernel) {
return 0;
}
createSnapshot = (CreateToolhelp32SnapshotFn)GetProcAddress(kernel, "CreateToolhelp32Snapshot");
toolhelp->first = (Process32Fn)GetProcAddress(kernel, "Process32First");
toolhelp->next = (Process32Fn)GetProcAddress(kernel, "Process32Next");
if (!createSnapshot || !toolhelp->first || !toolhelp->next) {
return 0;
}
toolhelp->snapshot = createSnapshot(TH32CS_SNAPPROCESS, 0);
if (toolhelp->snapshot == INVALID_HANDLE_VALUE) {
return -1;
}
return 1;
}
static bool IsProcessRunning(const char* processName) {
Toolhelp toolhelp;
int opened = open_process_snapshot(&toolhelp);
if (opened == 0) {
return false;
}
if (opened < 0) {
xpmgr_write("Failed to create process snapshot: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
PROCESSENTRY32W processEntry{}; ProcessHandle snapshot(toolhelp.snapshot);
processEntry.dwSize = sizeof(PROCESSENTRY32W); PROCESSENTRY32 processEntry{};
processEntry.dwSize = sizeof(PROCESSENTRY32);
if (!Process32FirstW(snapshot, &processEntry)) { if (!toolhelp.first(snapshot, &processEntry)) {
std::cout << "Failed to retrieve process information: " << GetLastError() << std::endl; xpmgr_write("Failed to retrieve process information: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
do { do {
if (wcscmp(processEntry.szExeFile, processName) == 0) { if (lstrcmpiA(processEntry.szExeFile, processName) == 0) {
return true; return true;
} }
} while (Process32NextW(snapshot, &processEntry)); } while (toolhelp.next(snapshot, &processEntry));
return false; return false;
} }
bool TerminateProcessByName(const wchar_t* processName) { static bool TerminateProcessByName(const char* processName) {
ProcessHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); Toolhelp toolhelp;
if (snapshot == INVALID_HANDLE_VALUE) { int opened = open_process_snapshot(&toolhelp);
std::cout << "Failed to create process snapshot: " << GetLastError() << std::endl; if (opened == 0) {
return false;
}
if (opened < 0) {
xpmgr_write("Failed to create process snapshot: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
PROCESSENTRY32W processEntry{}; ProcessHandle snapshot(toolhelp.snapshot);
processEntry.dwSize = sizeof(PROCESSENTRY32W); PROCESSENTRY32 processEntry{};
processEntry.dwSize = sizeof(PROCESSENTRY32);
if (!Process32FirstW(snapshot, &processEntry)) { if (!toolhelp.first(snapshot, &processEntry)) {
std::cout << "Failed to retrieve process information: " << GetLastError() << std::endl; xpmgr_write("Failed to retrieve process information: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
do { do {
if (wcscmp(processEntry.szExeFile, processName) == 0) { if (lstrcmpiA(processEntry.szExeFile, processName) == 0) {
ProcessHandle processHandle(OpenProcess(PROCESS_TERMINATE, FALSE, processEntry.th32ProcessID)); ProcessHandle processHandle(OpenProcess(PROCESS_TERMINATE, FALSE, processEntry.th32ProcessID));
if (processHandle == nullptr) { if (processHandle == nullptr) {
std::cout << "Failed to open process: " << GetLastError() << std::endl; xpmgr_write("Failed to open process: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
if (!TerminateProcess(processHandle, 0)) { if (!TerminateProcess(processHandle, 0)) {
std::cout << "Failed to terminate process: " << GetLastError() << std::endl; xpmgr_write("Failed to terminate process: ");
xpmgr_write_uint(GetLastError());
xpmgr_write("\n");
return false; return false;
} }
return true; return true;
} }
} while (Process32NextW(snapshot, &processEntry)); } while (toolhelp.next(snapshot, &processEntry));
std::cout << "Process not found: " << processName << std::endl; xpmgr_write("Process not found: ");
xpmgr_write(processName);
xpmgr_write("\n");
return false; return false;
} }
@@ -233,7 +349,7 @@ const wchar_t* XPMGR_GetInstallationID() {
} }
static wchar_t result[256]; static wchar_t result[256];
wcscpy(result, installationID); lstrcpynW(result, installationID, 256);
SysFreeString(installationID); SysFreeString(installationID);
return result; return result;
} }
@@ -243,11 +359,16 @@ const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId) {
return XPMGR_GetLastError(); return XPMGR_GetLastError();
} }
if (!confirmationId) {
SetLastErrorMessage(L"Invalid confirmation ID - verification failed");
return XPMGR_GetLastError();
}
// Create a working copy and remove dashes // Create a working copy and remove dashes
wchar_t cleanCid[256] = {0}; wchar_t cleanCid[256];
const wchar_t* src = confirmationId; const wchar_t* src = confirmationId;
wchar_t* dst = cleanCid; wchar_t* dst = cleanCid;
while (*src) { while (*src && (dst - cleanCid) < 255) {
if (*src != L'-') { if (*src != L'-') {
*dst++ = *src; *dst++ = *src;
} }
@@ -256,10 +377,15 @@ const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId) {
*dst = 0; *dst = 0;
// Verify each 6-character chunk // Verify each 6-character chunk
size_t len = wcslen(cleanCid); unsigned len = wide_length(cleanCid);
for (size_t i = 0; i < len; i += 6) { for (unsigned i = 0; i < len; i += 6) {
wchar_t chunk[7] = {0}; wchar_t chunk[7];
wcsncpy(chunk, cleanCid + i, 6); unsigned count = 0;
while (count < 6 && cleanCid[i + count]) {
chunk[count] = cleanCid[i + count];
count++;
}
chunk[count] = 0;
if (!VerifyConfirmationIDChunk(XP_LicenseAgent, chunk)) { if (!VerifyConfirmationIDChunk(XP_LicenseAgent, chunk)) {
SetLastErrorMessage(L"Invalid confirmation ID - verification failed"); SetLastErrorMessage(L"Invalid confirmation ID - verification failed");
return XPMGR_GetLastError(); return XPMGR_GetLastError();
@@ -278,14 +404,14 @@ const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId) {
} }
// Handle WPA notifier process // Handle WPA notifier process
if (IsProcessRunning(L"wpabaln.exe")) { if (IsProcessRunning("wpabaln.exe")) {
if (!TerminateProcessByName(L"wpabaln.exe")) { if (!TerminateProcessByName("wpabaln.exe")) {
std::cout << "Warning: Failed to terminate WPA notifier process." << std::endl; xpmgr_write("Warning: Failed to terminate WPA notifier process.\n");
} }
} }
// Clean up activation files // Clean up activation files
HMODULE hMod = LoadLibraryW(L"setupapi.dll"); HMODULE hMod = LoadLibraryA("setupapi.dll");
if (hMod) { if (hMod) {
using InstallHinfSectionFunc = BOOL (WINAPI*)(HWND, HINSTANCE, PCWSTR, INT); using InstallHinfSectionFunc = BOOL (WINAPI*)(HWND, HINSTANCE, PCWSTR, INT);
if (auto installHinfSection = reinterpret_cast<InstallHinfSectionFunc>(GetProcAddress(hMod, "InstallHinfSectionW"))) { if (auto installHinfSection = reinterpret_cast<InstallHinfSectionFunc>(GetProcAddress(hMod, "InstallHinfSectionW"))) {
@@ -294,9 +420,11 @@ const wchar_t* XPMGR_SetConfirmationID(const wchar_t* confirmationId) {
const DWORD error = GetLastError(); const DWORD error = GetLastError();
if (!result && error != 0 && error != 6) { // Ignore error 6 (ERROR_INVALID_HANDLE) as it indicates success 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; xpmgr_write("Warning: Failed to remove activation reminders. Error: ");
std::cout << "You can try to run the following command yourself: " << std::endl; xpmgr_write_uint(error);
std::cout << "rundll32 setupapi,InstallHinfSection DEL_OOBE_ACTIVATE 132 syssetup.inf" << std::endl; xpmgr_write("\n");
xpmgr_write("You can try to run the following command yourself: \n");
xpmgr_write("rundll32 setupapi,InstallHinfSection DEL_OOBE_ACTIVATE 132 syssetup.inf\n");
} }
} }
FreeLibrary(hMod); FreeLibrary(hMod);
@@ -322,7 +450,7 @@ const wchar_t* XPMGR_GetWPALeft() {
} }
static wchar_t result[16]; static wchar_t result[16];
_ultow(dwWPALeft, result, 10); write_ulong_wide(dwWPALeft, result);
return result; return result;
} }
@@ -343,7 +471,7 @@ const wchar_t* XPMGR_GetEvalLeft() {
} }
static wchar_t result[16]; static wchar_t result[16];
_ultow(dwEvalLeft, result, 10); write_ulong_wide(dwEvalLeft, result);
return result; return result;
} }
@@ -352,9 +480,7 @@ const wchar_t* XPMGR_SetProductKey(const wchar_t* productKey) {
return XPMGR_GetLastError(); return XPMGR_GetLastError();
} }
// Validate product key format if (!product_key_ok(productKey)) {
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"); SetLastErrorMessage(L"Invalid product key format");
return XPMGR_GetLastError(); return XPMGR_GetLastError();
} }
@@ -381,7 +507,7 @@ const wchar_t* XPMGR_GetProductID() {
} }
static wchar_t result[256]; static wchar_t result[256];
wcscpy(result, productID); lstrcpynW(result, productID, 256);
SysFreeString(productID); SysFreeString(productID);
return result; return result;
} }
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include <windows.h>
/* Tiny helpers so both binaries can avoid libstdc++. */
static int xpmgr_streq(const char* a, const char* b) {
if (!a || !b) {
return 0;
}
while (*a && *a == *b) {
a++;
b++;
}
return *a == *b;
}
static void xpmgr_write(const char* text) {
DWORD length = 0;
DWORD wrote = 0;
HANDLE out;
if (!text) {
return;
}
while (text[length]) {
length++;
}
out = GetStdHandle(STD_OUTPUT_HANDLE);
if (out == NULL || out == INVALID_HANDLE_VALUE) {
return;
}
WriteFile(out, text, length, &wrote, NULL);
}
static void xpmgr_write_uint(unsigned long value) {
char digits[16];
char text[16];
int count = 0;
int index = 0;
if (value == 0) {
xpmgr_write("0");
return;
}
while (value && count < 16) {
digits[count++] = (char)('0' + (value % 10));
value /= 10;
}
while (count > 0) {
text[index++] = digits[--count];
}
text[index] = 0;
xpmgr_write(text);
}
static void xpmgr_write_wide(const wchar_t* text) {
char bytes[260];
int index = 0;
if (!text) {
return;
}
while (text[index] && index < 259) {
wchar_t ch = text[index];
bytes[index] = (ch >= 32 && ch < 127) ? (char)ch : '?';
index++;
}
bytes[index] = 0;
xpmgr_write(bytes);
}
static void xpmgr_free(void* block) {
if (block) {
HeapFree(GetProcessHeap(), 0, block);
}
}
static wchar_t* xpmgr_widen(const char* text) {
int chars;
wchar_t* wide;
if (!text) {
return NULL;
}
chars = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
if (chars <= 0) {
return NULL;
}
wide = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (SIZE_T)chars * sizeof(wchar_t));
if (!wide) {
return NULL;
}
if (MultiByteToWideChar(CP_ACP, 0, text, -1, wide, chars) <= 0) {
HeapFree(GetProcessHeap(), 0, wide);
return NULL;
}
return wide;
}
static char* xpmgr_narrow(const wchar_t* text) {
int bytes;
char* narrow;
UINT codepage = CP_UTF8;
if (!text) {
return NULL;
}
/* Windows 98, Me, and NT 4 reject CP_UTF8. These strings are ASCII. */
bytes = WideCharToMultiByte(codepage, 0, text, -1, NULL, 0, NULL, NULL);
if (bytes <= 0) {
codepage = CP_ACP;
bytes = WideCharToMultiByte(codepage, 0, text, -1, NULL, 0, NULL, NULL);
if (bytes <= 0) {
return NULL;
}
}
narrow = (char*)HeapAlloc(GetProcessHeap(), 0, (SIZE_T)bytes);
if (!narrow) {
return NULL;
}
if (WideCharToMultiByte(codepage, 0, text, -1, narrow, bytes, NULL, NULL) <= 0) {
HeapFree(GetProcessHeap(), 0, narrow);
return NULL;
}
return narrow;
}