diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ff7e80..716772a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,8 @@ set(UMSKT_SOURCES src/cli.cpp src/windows/json.cpp ${UMSKT_KEYS_EMBED} + src/crypto/cpu_features.cpp + src/crypto/bn_kernels_scalar.cpp src/crypto/mini_bn.cpp src/crypto/mini_ec.cpp src/crypto/mini_rand.cpp @@ -255,6 +257,62 @@ if (UMSKT_FREESTANDING) endif() target_link_libraries(umskt PRIVATE kernel32) + if (UMSKT_WIN_X86) + add_library(umskt_bn_kernels_sse2 OBJECT src/crypto/bn_kernels_sse2.cpp) + target_include_directories(umskt_bn_kernels_sse2 PRIVATE "${CMAKE_SOURCE_DIR}/src/crypto") + target_compile_definitions(umskt_bn_kernels_sse2 PRIVATE + UMSKT_FREESTANDING + UMSKT_HAVE_SSE2_KERNELS + NDEBUG + ) + target_compile_options(umskt_bn_kernels_sse2 PRIVATE + -m32 + -O2 + -ffreestanding + -fno-exceptions + -fno-rtti + -fno-threadsafe-statics + -fno-use-cxa-atexit + -fno-stack-protector + -fno-unwind-tables + -fno-asynchronous-unwind-tables + -msse2 + -mfpmath=sse + ) + target_compile_definitions(umskt PRIVATE UMSKT_HAVE_SSE2_KERNELS) + target_link_libraries(umskt PRIVATE umskt_bn_kernels_sse2) + message(STATUS "[UMSKT] SSE2 BN kernels enabled with runtime CPU/OS dispatch") + endif() + + add_library(umskt_bn_kernels_avx2 OBJECT src/crypto/bn_kernels_avx2.cpp) + target_include_directories(umskt_bn_kernels_avx2 PRIVATE "${CMAKE_SOURCE_DIR}/src/crypto") + target_compile_definitions(umskt_bn_kernels_avx2 PRIVATE + UMSKT_FREESTANDING + UMSKT_HAVE_AVX2_KERNELS + NDEBUG + ) + set(UMSKT_AVX2_FLAGS + -O2 + -ffreestanding + -fno-exceptions + -fno-rtti + -fno-threadsafe-statics + -fno-use-cxa-atexit + -fno-stack-protector + -fno-unwind-tables + -fno-asynchronous-unwind-tables + -mavx2 + ) + if (UMSKT_WIN_X86) + list(APPEND UMSKT_AVX2_FLAGS -m32) + else() + list(APPEND UMSKT_AVX2_FLAGS -m64) + endif() + target_compile_options(umskt_bn_kernels_avx2 PRIVATE ${UMSKT_AVX2_FLAGS}) + target_compile_definitions(umskt PRIVATE UMSKT_HAVE_AVX2_KERNELS) + target_link_libraries(umskt PRIVATE umskt_bn_kernels_avx2) + message(STATUS "[UMSKT] AVX2 BN kernels enabled with runtime CPU/OS dispatch") + get_filename_component(UMSKT_GCC_BIN "${CMAKE_CXX_COMPILER}" DIRECTORY) find_program(UMSKT_OBJDUMP NAMES objdump HINTS "${UMSKT_GCC_BIN}") if (UMSKT_OBJDUMP) diff --git a/README.md b/README.md index 4af3d1a..711fe7b 100644 --- a/README.md +++ b/README.md @@ -55,11 +55,12 @@ In light of the recent exponential interest in this project I've decided to put * Any DOS-based version of Windows * Windows NT 4.0 or later (via NTVDM) #### Windows (x86) -* Pentium processor or better +* Pentium processor or better (Pentium 4 recommended, Haswell or equivalent best) * Windows 98 First Edition or later -* Windows NT 4.0 SP6a or later +* Windows NT 4.0 or later #### Windows (x64) -* Windows MT 5.2 (Windows XP x64, Windows Server 2003) or later +* Windows NT 5.2 (Windows XP x64, Windows Server 2003) or later +* Haswell or equivalent recommended #### Windows (ARM64) * Winodws 11 21H2 or later #### macOS diff --git a/src/cli.cpp b/src/cli.cpp index 9728b84..0c72cc6 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -57,6 +57,7 @@ struct Options { int nonewlines; int overrideVersion; int nodashes; + int progress; int instidFromStdin; MODE applicationMode; ACTIVATION_ALGORITHM activationMode; @@ -88,6 +89,144 @@ static void outf(const char *fmt, ...) { out_text(buffer); } +static void err_text(const char *text) { + if (!text) { + return; + } +#ifdef UMSKT_FREESTANDING + uint32_t wrote = 0; + void *handle = GetStdHandle(UMSKT_STD_ERROR_HANDLE); + if (handle == NULL || handle == UMSKT_INVALID_HANDLE) { + return; + } + WriteFile(handle, text, (uint32_t)strlen(text), &wrote, NULL); +#else + fputs(text, stderr); + fflush(stderr); +#endif +} + +struct ProgressCtx { + unsigned key_index; + unsigned key_count; + int last_percent; + int last_phase; + int active; + int verbose_layout; + int line_open; +}; + +static ProgressCtx g_progress_ctx; +static const unsigned PROGRESS_LINE_WIDTH = 72; + +static void progress_write_padded(const char *body) { + char line[80]; + unsigned i; + unsigned len = 0; + line[0] = '\r'; + while (body[len] && len < PROGRESS_LINE_WIDTH) { + line[1 + len] = body[len]; + len++; + } + for (i = len; i < PROGRESS_LINE_WIDTH; i++) { + line[1 + i] = ' '; + } + line[1 + PROGRESS_LINE_WIDTH] = 0; + err_text(line); + g_progress_ctx.line_open = 1; +} + +static void progress_clear_line(void) { + if (g_progress_ctx.line_open) { + progress_write_padded(""); + err_text("\r"); + g_progress_ctx.line_open = 0; + } +} + +static void progress_break_line(void) { + if (g_progress_ctx.line_open) { + progress_write_padded(""); + err_text("\n"); + g_progress_ctx.line_open = 0; + } + g_progress_ctx.last_phase = -1; + g_progress_ctx.last_percent = -1; +} + +static const char *progress_phase_label(int phase) { + const char *op = UMSKT::getProgressLabel(); + if (g_progress_ctx.verbose_layout && op && op[0]) { + return op; + } + if (phase == UMSKT_PROGRESS_GEN) { + return "Generating key"; + } + if (phase == UMSKT_PROGRESS_VERIFY) { + return "Validating key"; + } + return "Multiplying elliptic curve"; +} + +static void progress_draw_bar(int phase, unsigned current, unsigned total) { + char body[80]; + unsigned percent; + unsigned filled; + unsigned i; + const unsigned width = 24; + const char *label; + if (total == 0) { + return; + } + if (current > total) { + current = total; + } + percent = (current * 100u) / total; + if (g_progress_ctx.last_phase == phase && + (int)percent == g_progress_ctx.last_percent && + current != total && + current != 0 && + current != 1) { + return; + } + g_progress_ctx.last_phase = phase; + g_progress_ctx.last_percent = (int)percent; + filled = (current * width) / total; + body[0] = '['; + for (i = 0; i < width; i++) { + body[1 + i] = (i < filled) ? '#' : '-'; + } + label = progress_phase_label(phase); + if (g_progress_ctx.key_count > 1) { + snprintf(body + 1 + width, sizeof(body) - (1 + width), + "] %3u%% %s (%u/%u)", + percent, label, g_progress_ctx.key_index + 1, g_progress_ctx.key_count); + } else { + snprintf(body + 1 + width, sizeof(body) - (1 + width), + "] %3u%% %s", percent, label); + } + progress_write_padded(body); +} + +static void progress_callback(int phase, unsigned current, unsigned total, void *user) { + ProgressCtx *ctx = (ProgressCtx *)user; + if (!ctx || !ctx->active) { + return; + } + if (phase == UMSKT_PROGRESS_BREAK) { + progress_break_line(); + return; + } + if (ctx->verbose_layout && ctx->last_phase != -1 && ctx->last_phase != phase) { + progress_break_line(); + } + if (phase == UMSKT_PROGRESS_MUL || + phase == UMSKT_PROGRESS_GEN || + phase == UMSKT_PROGRESS_VERIFY) { + progress_draw_bar(phase, current, total); + } +} + static int streq(const char *a, const char *b) { return strcmp(a, b) == 0; } @@ -315,7 +454,8 @@ static void show_help(char *argv[]) { out_text("\t-V --validate\tproduct key to validate signature\n"); out_text("\t-N --nonewlines\tdisables newlines (for easier embedding in other apps)\n"); out_text("\t-o --override\tDisables version check for confirmation IDs, if you need this send an issue on GitHub\n"); - out_text("\t-D --nodashes\tDisables dashes in product keys and confirmation IDs (for easier copy-pasting)"); + out_text("\t-D --nodashes\tDisables dashes in product keys and confirmation IDs (for easier copy-pasting)\n"); + out_text("\t-P --progress\tDisplays progress bar (for long-running operations)\n"); out_text("\n"); } @@ -445,6 +585,8 @@ static int parse_command_line(int argc, char *argv[], Options *options) { options->overrideVersion = 1; } else if (streq(arg, "-D") || streq(arg, "--nodashes")) { options->nodashes = 1; + } else if (streq(arg, "-P") || streq(arg, "--progress")) { + options->progress = 1; } else { options->error = 1; } @@ -691,10 +833,34 @@ static int run_keys(Options *options, const char *json, int length) { print_id(&nRaw); } BN_sub(privateKey, genOrder, privateKey); + if (options->progress) { + g_progress_ctx.key_index = 0; + g_progress_ctx.key_count = (unsigned)options->numKeys; + g_progress_ctx.last_percent = -1; + g_progress_ctx.last_phase = -1; + g_progress_ctx.active = 1; + g_progress_ctx.verbose_layout = options->verbose; + g_progress_ctx.line_open = 0; + UMSKT::setProgress(progress_callback, &g_progress_ctx); + } for (i = 0; i < total; i++) { int valid; + if (options->progress) { + g_progress_ctx.key_index = (unsigned)count; + g_progress_ctx.last_percent = -1; + g_progress_ctx.last_phase = -1; + } PIDGEN3::BINK1998::Generate(eCurve, genPoint, genOrder, privateKey, nRaw, options->upgrade, pKey); + if (options->progress) { + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_VERIFY); + UMSKT::setMulProgress(1); + } valid = PIDGEN3::BINK1998::Verify(eCurve, genPoint, pubPoint, pKey); + if (options->progress) { + UMSKT::setMulProgress(0); + UMSKT::setProgressLabel(NULL); + progress_clear_line(); + } if (valid) { print_key(pKey, options->nodashes); if (i < total - 1 || options->verbose) { @@ -716,10 +882,24 @@ static int run_keys(Options *options, const char *json, int length) { } } } + if (options->progress) { + UMSKT::setProgress(NULL, NULL); + g_progress_ctx.active = 0; + } } else if (options->applicationMode == MODE_BINK2002_GENERATE) { if (options->verbose) { outf("> Channel ID: %03d\n", options->channelID); } + if (options->progress) { + g_progress_ctx.key_index = 0; + g_progress_ctx.key_count = (unsigned)options->numKeys; + g_progress_ctx.last_percent = -1; + g_progress_ctx.last_phase = -1; + g_progress_ctx.active = 1; + g_progress_ctx.verbose_layout = options->verbose; + g_progress_ctx.line_open = 0; + UMSKT::setProgress(progress_callback, &g_progress_ctx); + } for (i = 0; i < total; i++) { DWORD pAuthInfo = 0; int valid; @@ -728,8 +908,22 @@ static int run_keys(Options *options, const char *json, int length) { if (options->verbose) { outf("> AuthInfo: %u\n", pAuthInfo); } + if (options->progress) { + g_progress_ctx.key_index = (unsigned)count; + g_progress_ctx.last_percent = -1; + g_progress_ctx.last_phase = -1; + } PIDGEN3::BINK2002::Generate(eCurve, genPoint, genOrder, privateKey, (DWORD)options->channelID, pAuthInfo, options->upgrade, (DWORD)options->serialMin, (DWORD)options->serialMax, pKey); + if (options->progress) { + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_VERIFY); + UMSKT::setMulProgress(1); + } valid = PIDGEN3::BINK2002::Verify(eCurve, genPoint, pubPoint, NULL, pKey); + if (options->progress) { + UMSKT::setMulProgress(0); + UMSKT::setProgressLabel(NULL); + progress_clear_line(); + } if (valid) { print_key(pKey, options->nodashes); if (i < total - 1 || options->verbose) { @@ -751,6 +945,10 @@ static int run_keys(Options *options, const char *json, int length) { } } } + if (options->progress) { + UMSKT::setProgress(NULL, NULL); + g_progress_ctx.active = 0; + } } else if (options->applicationMode == MODE_BINK1998_VALIDATE || options->applicationMode == MODE_BINK2002_VALIDATE) { char product_key[PK_LENGTH + NULL_TERMINATOR]; memset(product_key, 0, sizeof(product_key)); @@ -798,6 +996,9 @@ static int run_keys(Options *options, const char *json, int length) { } if (gaveUp || count < options->numKeys) { + if (options->progress) { + progress_clear_line(); + } out_text("ERROR: failed to generate a valid product key\n"); curve_free(&curve); BN_free(privateKey); diff --git a/src/crypto/bn_kernels.h b/src/crypto/bn_kernels.h new file mode 100644 index 0000000..8cefaab --- /dev/null +++ b/src/crypto/bn_kernels.h @@ -0,0 +1,35 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef UMSKT_BN_KERNELS_H +#define UMSKT_BN_KERNELS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void umskt_mont_mul_scalar(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw); +void umskt_bn_mul_limbs_scalar(uint32_t *r, const uint32_t *a, int an, const uint32_t *b, int bn); + +#if defined(UMSKT_HAVE_SSE2_KERNELS) +void umskt_mont_mul_sse2(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw); +void umskt_bn_mul_limbs_sse2(uint32_t *r, const uint32_t *a, int an, const uint32_t *b, int bn); +#endif + +#if defined(UMSKT_HAVE_AVX2_KERNELS) +void umskt_mont_mul_avx2(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw); +void umskt_bn_mul_limbs_avx2(uint32_t *r, const uint32_t *a, int an, const uint32_t *b, int bn); +#endif + +void umskt_bn_dispatch_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/crypto/bn_kernels.inc.cpp b/src/crypto/bn_kernels.inc.cpp new file mode 100644 index 0000000..90b7c60 --- /dev/null +++ b/src/crypto/bn_kernels.inc.cpp @@ -0,0 +1,100 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + * + */ + +#ifndef BN_KERNEL_SUFFIX +#error "BN_KERNEL_SUFFIX must be defined before including bn_kernels.inc.cpp" +#endif + +#define BN_KERNEL_PASTE2(a, b) a##_##b +#define BN_KERNEL_PASTE(a, b) BN_KERNEL_PASTE2(a, b) +#define BN_KERNEL_FN(name) BN_KERNEL_PASTE(name, BN_KERNEL_SUFFIX) + +void BN_KERNEL_FN(umskt_mont_mul)(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw) { + uint32_t tmp[34]; + int i; + int j; + int ge; + for (i = 0; i < nw + 2; i++) { + tmp[i] = 0; + } + for (i = 0; i < nw; i++) { + uint64_t carry = 0; + uint32_t m; + for (j = 0; j < nw; j++) { + carry += (uint64_t)tmp[j] + (uint64_t)a[j] * b[i]; + tmp[j] = (uint32_t)carry; + carry >>= 32; + } + carry += tmp[nw]; + tmp[nw] = (uint32_t)carry; + tmp[nw + 1] = (uint32_t)(carry >> 32); + m = tmp[0] * n0; + carry = 0; + for (j = 0; j < nw; j++) { + carry += (uint64_t)tmp[j] + (uint64_t)m * n[j]; + tmp[j] = (uint32_t)carry; + carry >>= 32; + } + carry += tmp[nw]; + tmp[nw] = (uint32_t)carry; + tmp[nw + 1] += (uint32_t)(carry >> 32); + for (j = 0; j <= nw; j++) { + tmp[j] = tmp[j + 1]; + } + tmp[nw + 1] = 0; + } + ge = tmp[nw] != 0; + if (!ge) { + for (i = nw - 1; i >= 0; i--) { + if (tmp[i] != n[i]) { + ge = tmp[i] > n[i]; + break; + } + if (i == 0) { + ge = 1; + } + } + } + if (ge) { + uint32_t borrow = 0; + for (i = 0; i < nw; i++) { + uint64_t sub = (uint64_t)n[i] + borrow; + r[i] = (uint32_t)((uint64_t)tmp[i] - sub); + borrow = (uint64_t)tmp[i] < sub; + } + } else { + for (i = 0; i < nw; i++) { + r[i] = tmp[i]; + } + } +} + +void BN_KERNEL_FN(umskt_bn_mul_limbs)(uint32_t *r, const uint32_t *a, int an, const uint32_t *b, int bn) { + int i; + int j; + int top = an + bn; + for (i = 0; i < top + 1; i++) { + r[i] = 0; + } + for (i = 0; i < an; i++) { + uint32_t carry = 0; + for (j = 0; j < bn; j++) { + union { + uint64_t v; + uint32_t p[2]; + } acc; + acc.v = (uint64_t)a[i] * b[j] + r[i + j] + carry; + r[i + j] = acc.p[0]; + carry = acc.p[1]; + } + r[i + bn] = carry; + } +} + +#undef BN_KERNEL_FN +#undef BN_KERNEL_PASTE +#undef BN_KERNEL_PASTE2 diff --git a/src/crypto/bn_kernels_avx2.cpp b/src/crypto/bn_kernels_avx2.cpp new file mode 100644 index 0000000..8f00056 --- /dev/null +++ b/src/crypto/bn_kernels_avx2.cpp @@ -0,0 +1,11 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "bn_kernels.h" + +#define BN_KERNEL_SUFFIX avx2 +#include "bn_kernels.inc.cpp" +#undef BN_KERNEL_SUFFIX diff --git a/src/crypto/bn_kernels_scalar.cpp b/src/crypto/bn_kernels_scalar.cpp new file mode 100644 index 0000000..9ac35c8 --- /dev/null +++ b/src/crypto/bn_kernels_scalar.cpp @@ -0,0 +1,11 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "bn_kernels.h" + +#define BN_KERNEL_SUFFIX scalar +#include "bn_kernels.inc.cpp" +#undef BN_KERNEL_SUFFIX diff --git a/src/crypto/bn_kernels_sse2.cpp b/src/crypto/bn_kernels_sse2.cpp new file mode 100644 index 0000000..354ca58 --- /dev/null +++ b/src/crypto/bn_kernels_sse2.cpp @@ -0,0 +1,11 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "bn_kernels.h" + +#define BN_KERNEL_SUFFIX sse2 +#include "bn_kernels.inc.cpp" +#undef BN_KERNEL_SUFFIX diff --git a/src/crypto/cpu_features.cpp b/src/crypto/cpu_features.cpp new file mode 100644 index 0000000..a147c5d --- /dev/null +++ b/src/crypto/cpu_features.cpp @@ -0,0 +1,189 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "cpu_features.h" + +#if defined(UMSKT_FREESTANDING) || defined(_WIN32) +#ifndef WINVER +#define WINVER 0x0400 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#include +#ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE +#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10 +#endif +#ifndef PF_AVX2_INSTRUCTIONS_AVAILABLE +#define PF_AVX2_INSTRUCTIONS_AVAILABLE 40 +#endif +#endif + +static int g_cpu_ready = 0; +static int g_has_sse2 = 0; +static int g_has_avx2 = 0; + +#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) + +#if defined(__GNUC__) || defined(__clang__) +static int cpuid_supported(void) { +#if defined(__x86_64__) || defined(_M_X64) + return 1; +#else + unsigned int original; + unsigned int modified; + __asm__ __volatile__( + "pushfl\n\t" + "popl %0\n\t" + "movl %0, %1\n\t" + "xorl $0x200000, %0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "pushl %1\n\t" + "popfl\n\t" + : "=&r"(modified), "=&r"(original) + : + : "cc"); + return ((original ^ modified) & 0x200000u) != 0; +#endif +} + +static void do_cpuid(unsigned int leaf, unsigned int subleaf, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { + __asm__ __volatile__( + "cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf), "c"(subleaf)); +} + +static unsigned long long do_xgetbv(unsigned int index) { + unsigned int eax; + unsigned int edx; + __asm__ __volatile__( + ".byte 0x0f, 0x01, 0xd0" + : "=a"(eax), "=d"(edx) + : "c"(index)); + return ((unsigned long long)edx << 32) | eax; +} +#else +static int cpuid_supported(void) { + return 0; +} + +static void do_cpuid(unsigned int leaf, unsigned int subleaf, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { + (void)leaf; + (void)subleaf; + *eax = *ebx = *ecx = *edx = 0; +} + +static unsigned long long do_xgetbv(unsigned int index) { + (void)index; + return 0; +} +#endif + +static int cpu_has_sse2_bit(void) { + unsigned int eax = 0; + unsigned int ebx = 0; + unsigned int ecx = 0; + unsigned int edx = 0; + if (!cpuid_supported()) { + return 0; + } + do_cpuid(0, 0, &eax, &ebx, &ecx, &edx); + if (eax < 1) { + return 0; + } + do_cpuid(1, 0, &eax, &ebx, &ecx, &edx); + return (edx & (1u << 26)) != 0; +} + +static int cpu_has_avx2_bits(void) { + unsigned int eax = 0; + unsigned int ebx = 0; + unsigned int ecx = 0; + unsigned int edx = 0; + unsigned long long xcr0; + if (!cpuid_supported()) { + return 0; + } + do_cpuid(0, 0, &eax, &ebx, &ecx, &edx); + if (eax < 1) { + return 0; + } + do_cpuid(1, 0, &eax, &ebx, &ecx, &edx); + /* OSXSAVE (27) and AVX (28) */ + if ((ecx & ((1u << 27) | (1u << 28))) != ((1u << 27) | (1u << 28))) { + return 0; + } + xcr0 = do_xgetbv(0); + /* XMM (bit 1) and YMM (bit 2) state must be enabled by the OS. */ + if ((xcr0 & 0x6ull) != 0x6ull) { + return 0; + } + do_cpuid(0, 0, &eax, &ebx, &ecx, &edx); + if (eax < 7) { + return 0; + } + do_cpuid(7, 0, &eax, &ebx, &ecx, &edx); + return (ebx & (1u << 5)) != 0; +} + +static int os_supports_sse2(void) { +#if defined(UMSKT_FREESTANDING) || defined(_WIN32) + return IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) ? 1 : 0; +#else + return 1; +#endif +} + +static int os_supports_avx2(void) { +#if defined(UMSKT_FREESTANDING) || defined(_WIN32) + return IsProcessorFeaturePresent(PF_AVX2_INSTRUCTIONS_AVAILABLE) ? 1 : 0; +#else + return 1; +#endif +} + +#else + +static int cpu_has_sse2_bit(void) { + return 0; +} + +static int cpu_has_avx2_bits(void) { + return 0; +} + +static int os_supports_sse2(void) { + return 0; +} + +static int os_supports_avx2(void) { + return 0; +} + +#endif + +void umskt_cpu_init(void) { + if (g_cpu_ready) { + return; + } + g_has_sse2 = cpu_has_sse2_bit() && os_supports_sse2(); + g_has_avx2 = cpu_has_avx2_bits() && os_supports_avx2(); + g_cpu_ready = 1; +} + +int umskt_cpu_has_sse2(void) { + umskt_cpu_init(); + return g_has_sse2; +} + +int umskt_cpu_has_avx2(void) { + umskt_cpu_init(); + return g_has_avx2; +} diff --git a/src/crypto/cpu_features.h b/src/crypto/cpu_features.h new file mode 100644 index 0000000..e23b76e --- /dev/null +++ b/src/crypto/cpu_features.h @@ -0,0 +1,22 @@ +/* This file is a part of the UMSKT Project. + * + * Copyleft (C) 2019-2026 UMSKT Contributors (et.al.) + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef UMSKT_CPU_FEATURES_H +#define UMSKT_CPU_FEATURES_H + +#ifdef __cplusplus +extern "C" { +#endif + +void umskt_cpu_init(void); +int umskt_cpu_has_sse2(void); +int umskt_cpu_has_avx2(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/crypto/mini_bn.cpp b/src/crypto/mini_bn.cpp index c8e4d6f..ed5eeaa 100644 --- a/src/crypto/mini_bn.cpp +++ b/src/crypto/mini_bn.cpp @@ -9,10 +9,48 @@ */ #include "mini_openssl.h" +#include "bn_kernels.h" +#include "cpu_features.h" #include #include +typedef void (*umskt_mont_mul_fn)(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw); +typedef void (*umskt_bn_mul_limbs_fn)(uint32_t *r, const uint32_t *a, int an, const uint32_t *b, int bn); + +static umskt_mont_mul_fn g_mont_mul = umskt_mont_mul_scalar; +static umskt_bn_mul_limbs_fn g_bn_mul_limbs = umskt_bn_mul_limbs_scalar; +static int g_bn_dispatch_ready = 0; + +void umskt_bn_dispatch_init(void) { + if (g_bn_dispatch_ready) { + return; + } + umskt_cpu_init(); +#if defined(UMSKT_HAVE_AVX2_KERNELS) + if (umskt_cpu_has_avx2()) { + g_mont_mul = umskt_mont_mul_avx2; + g_bn_mul_limbs = umskt_bn_mul_limbs_avx2; + } else +#endif +#if defined(UMSKT_HAVE_SSE2_KERNELS) + if (umskt_cpu_has_sse2()) { + g_mont_mul = umskt_mont_mul_sse2; + g_bn_mul_limbs = umskt_bn_mul_limbs_sse2; + } else +#endif + { + (void)0; + } + g_bn_dispatch_ready = 1; +} + +static void bn_dispatch_ensure(void) { + if (!g_bn_dispatch_ready) { + umskt_bn_dispatch_init(); + } +} + struct bignum_st { uint32_t *d; int top; @@ -505,8 +543,6 @@ BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w) { } static int bn_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) { - int i; - int j; int top = a->top + b->top; if (a->top == 0 || b->top == 0) { return BN_zero(r); @@ -514,22 +550,8 @@ static int bn_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) { if (!bn_wexpand(r, top + 1)) { return 0; } - for (i = 0; i < top + 1; i++) { - r->d[i] = 0; - } - for (i = 0; i < a->top; i++) { - uint32_t carry = 0; - for (j = 0; j < b->top; j++) { - union { - uint64_t v; - uint32_t p[2]; - } acc; - acc.v = (uint64_t)a->d[i] * b->d[j] + r->d[i + j] + carry; - r->d[i + j] = acc.p[0]; - carry = acc.p[1]; - } - r->d[i + b->top] = carry; - } + bn_dispatch_ensure(); + g_bn_mul_limbs(r->d, a->d, a->top, b->d, b->top); r->top = top; r->neg = (a->neg ^ b->neg) && 1; bn_fix_top(r); @@ -728,63 +750,8 @@ static void mont_load(uint32_t *dst, const BIGNUM *src, int nw) { } static void mont_mul(uint32_t *r, const uint32_t *a, const uint32_t *b, const uint32_t *n, uint32_t n0, int nw) { - uint32_t tmp[34]; - int i; - int j; - int ge; - for (i = 0; i < nw + 2; i++) { - tmp[i] = 0; - } - for (i = 0; i < nw; i++) { - uint64_t carry = 0; - uint32_t m; - for (j = 0; j < nw; j++) { - carry += (uint64_t)tmp[j] + (uint64_t)a[j] * b[i]; - tmp[j] = (uint32_t)carry; - carry >>= 32; - } - carry += tmp[nw]; - tmp[nw] = (uint32_t)carry; - tmp[nw + 1] = (uint32_t)(carry >> 32); - m = tmp[0] * n0; - carry = 0; - for (j = 0; j < nw; j++) { - carry += (uint64_t)tmp[j] + (uint64_t)m * n[j]; - tmp[j] = (uint32_t)carry; - carry >>= 32; - } - carry += tmp[nw]; - tmp[nw] = (uint32_t)carry; - tmp[nw + 1] += (uint32_t)(carry >> 32); - for (j = 0; j <= nw; j++) { - tmp[j] = tmp[j + 1]; - } - tmp[nw + 1] = 0; - } - ge = tmp[nw] != 0; - if (!ge) { - for (i = nw - 1; i >= 0; i--) { - if (tmp[i] != n[i]) { - ge = tmp[i] > n[i]; - break; - } - if (i == 0) { - ge = 1; - } - } - } - if (ge) { - uint32_t borrow = 0; - for (i = 0; i < nw; i++) { - uint64_t sub = (uint64_t)n[i] + borrow; - r[i] = (uint32_t)((uint64_t)tmp[i] - sub); - borrow = (uint64_t)tmp[i] < sub; - } - } else { - for (i = 0; i < nw; i++) { - r[i] = tmp[i]; - } - } + bn_dispatch_ensure(); + g_mont_mul(r, a, b, n, n0, nw); } static int bn_to_mont_rr(BIGNUM *rr, const BIGNUM *m, int nw) { diff --git a/src/crypto/mini_ec.cpp b/src/crypto/mini_ec.cpp index aa8ec72..231c90d 100644 --- a/src/crypto/mini_ec.cpp +++ b/src/crypto/mini_ec.cpp @@ -12,6 +12,10 @@ #include #include +extern "C" int umskt_mul_progress_enabled(void); +extern "C" int umskt_ec_progress_phase(void); +extern "C" void umskt_report_progress(int phase, unsigned current, unsigned total); + struct ec_group_st { BIGNUM *p; BIGNUM *a; @@ -421,6 +425,9 @@ int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_P if (ok && BN_is_bit_set(m, i)) { ok = jadd_mixed(group, &acc, q); } + if (ok && umskt_mul_progress_enabled() && bits > 0) { + umskt_report_progress(umskt_ec_progress_phase(), (unsigned)(bits - i), (unsigned)bits); + } } if (ok) { ok = jto_affine(group, r, &acc); diff --git a/src/libumskt/debugoutput.cpp b/src/libumskt/debugoutput.cpp index 53ca864..d1cfd59 100644 --- a/src/libumskt/debugoutput.cpp +++ b/src/libumskt/debugoutput.cpp @@ -22,6 +22,69 @@ #include "libumskt.h" +static UMSKT::ProgressFn g_progress_fn = NULL; +static void *g_progress_user = NULL; +static int g_mul_progress = 0; +static int g_ec_progress_phase = UMSKT_PROGRESS_MUL; +static int g_progress_suspended = 0; +static char g_progress_label[24]; + +void UMSKT::setProgress(ProgressFn fn, void *user) { + g_progress_fn = fn; + g_progress_user = user; +} + +void UMSKT::setMulProgress(int on) { + g_mul_progress = on ? 1 : 0; +} + +void UMSKT::setEcProgressPhase(int phase) { + g_ec_progress_phase = phase; +} + +void UMSKT::setProgressLabel(const char *label) { + size_t i = 0; + if (!label) { + g_progress_label[0] = 0; + return; + } + while (label[i] && i + 1 < sizeof(g_progress_label)) { + g_progress_label[i] = label[i]; + i++; + } + g_progress_label[i] = 0; +} + +const char *UMSKT::getProgressLabel(void) { + return g_progress_label; +} + +extern "C" int umskt_mul_progress_enabled(void) { + return g_mul_progress && g_progress_fn != NULL; +} + +extern "C" int umskt_ec_progress_phase(void) { + return g_ec_progress_phase; +} + +extern "C" void umskt_report_progress(int phase, unsigned current, unsigned total) { + UMSKT::reportProgress(phase, current, total); +} + +void UMSKT::suspendProgressLine(void) { + if (g_progress_fn && !g_progress_suspended) { + g_progress_fn(UMSKT_PROGRESS_BREAK, 0, 0, g_progress_user); + g_progress_suspended = 1; + } +} + +void UMSKT::reportProgress(int phase, unsigned current, unsigned total) { + if (g_progress_fn) { + g_progress_suspended = 0; + g_progress_fn(phase, current, total, g_progress_user); + } +} + #ifdef UMSKT_FREESTANDING #include "../windows/win32_min.h" @@ -38,6 +101,7 @@ void UMSKT::debugf(const char *fmt, ...) { if (!debug_on || !fmt) { return; } + suspendProgressLine(); va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); @@ -61,6 +125,7 @@ void UMSKT::debugf(const char *fmt, ...) { if (!debug || !fmt) { return; } + suspendProgressLine(); va_start(ap, fmt); vfprintf(debug, fmt, ap); va_end(ap); diff --git a/src/libumskt/libumskt.h b/src/libumskt/libumskt.h index b35536f..a9e92d2 100644 --- a/src/libumskt/libumskt.h +++ b/src/libumskt/libumskt.h @@ -52,12 +52,21 @@ #define BYDWORD(n) (DWORD)(*((n) + 0) | *((n) + 1) << 8 | *((n) + 2) << 16 | *((n) + 3) << 24) #define BITMASK(n) ((1ULL << (n)) - 1) +enum { + UMSKT_PROGRESS_BREAK = 0, + UMSKT_PROGRESS_MUL = 1, + UMSKT_PROGRESS_GEN = 2, + UMSKT_PROGRESS_VERIFY = 3 +}; + class UMSKT { public: class PIDGEN2; class PIDGEN3; class ConfigurationID; + typedef void (*ProgressFn)(int phase, unsigned current, unsigned total, void *user); + #ifdef UMSKT_FREESTANDING static int debug_on; #else @@ -65,6 +74,13 @@ public: #endif static void setDebug(int on); static void debugf(const char *fmt, ...); + static void setProgress(ProgressFn fn, void *user); + static void setMulProgress(int on); + static void setEcProgressPhase(int phase); + static void setProgressLabel(const char *label); + static const char *getProgressLabel(void); + static void suspendProgressLine(void); + static void reportProgress(int phase, unsigned current, unsigned total); // RNG utility functions static int umskt_rand_bytes(unsigned char *buf, int num); diff --git a/src/libumskt/pidgen3/BINK1998.cpp b/src/libumskt/pidgen3/BINK1998.cpp index e17b54d..a709772 100644 --- a/src/libumskt/pidgen3/BINK1998.cpp +++ b/src/libumskt/pidgen3/BINK1998.cpp @@ -128,10 +128,14 @@ bool PIDGEN3::BINK1998::Verify( EC_POINT *p = EC_POINT_new(eCurve); // t = sG + UMSKT::setProgressLabel("s*G"); + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_VERIFY); EC_POINT_mul(eCurve, t, nullptr, basePoint, s, numContext); // P = eK + UMSKT::setProgressLabel("e*K"); EC_POINT_mul(eCurve, p, nullptr, publicKey, e, numContext); + UMSKT::setProgressLabel(NULL); // P += t EC_POINT_add(eCurve, p, t, p, numContext); @@ -202,10 +206,23 @@ void PIDGEN3::BINK1998::Generate( // attempt adds G instead of multiplying the curve again. About 1 in 512 // signatures fit in 55 bits, and a 386 cannot afford hundreds of multiplications. EC_POINT *r = EC_POINT_new(eCurve); + unsigned mul_bits; UMSKT::umskt_bn_rand(c, FIELD_BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); + mul_bits = (unsigned)BN_num_bits(c); + if (mul_bits == 0) { + mul_bits = 1; + } + UMSKT::setProgressLabel("R=c*G"); + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_MUL); + UMSKT::reportProgress(UMSKT_PROGRESS_MUL, 0, mul_bits); + UMSKT::setMulProgress(1); EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext); + UMSKT::setMulProgress(0); + UMSKT::setProgressLabel(NULL); + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, 0, 4096u); do { + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, (unsigned)tries + 1u, 4096u); // x = R.x; y = R.y; EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); @@ -262,18 +279,19 @@ void PIDGEN3::BINK1998::Generate( pSignature = 0; BN_bn2lebinpad(s, (BYTE *)&pSignature, (int)sizeof(pSignature)); tries++; + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, (unsigned)tries, 4096u); // Pack product key. Pack(pRaw, pUpgrade, pSerial, pHash, pSignature); - UMSKT::debugf("Generation results:\n"); - UMSKT::debugf(" Upgrade: 0x%08x\n", (unsigned)pUpgrade); - UMSKT::debugf(" Serial: 0x%08x\n", (unsigned)pSerial); - UMSKT::debugf(" Hash: 0x%08x\n", (unsigned)pHash); - UMSKT::debugf(" Signature: 0x%08llx\n", (unsigned long long)pSignature); - UMSKT::debugf("\n"); - - if (tries < 4096 && pSignature > BITMASK(55)) { + if (!(tries < 4096 && pSignature > BITMASK(55))) { + UMSKT::debugf("Generation results:\n"); + UMSKT::debugf(" Upgrade: 0x%08x\n", (unsigned)pUpgrade); + UMSKT::debugf(" Serial: 0x%08x\n", (unsigned)pSerial); + UMSKT::debugf(" Hash: 0x%08x\n", (unsigned)pHash); + UMSKT::debugf(" Signature: 0x%08llx\n", (unsigned long long)pSignature); + UMSKT::debugf("\n"); + } else { EC_POINT_add(eCurve, r, r, basePoint, numContext); BN_add_word(c, 1); } diff --git a/src/libumskt/pidgen3/BINK2002.cpp b/src/libumskt/pidgen3/BINK2002.cpp index 126f40b..0968a69 100644 --- a/src/libumskt/pidgen3/BINK2002.cpp +++ b/src/libumskt/pidgen3/BINK2002.cpp @@ -151,16 +151,21 @@ bool PIDGEN3::BINK2002::Verify( EC_POINT *t = EC_POINT_new(eCurve); // t = sG + UMSKT::setProgressLabel("s*G"); + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_VERIFY); EC_POINT_mul(eCurve, t, nullptr, basePoint, s, context); // p = eK + UMSKT::setProgressLabel("e*K"); EC_POINT_mul(eCurve, p, nullptr, publicKey, e, context); // p += t EC_POINT_add(eCurve, p, t, p, context); // p *= s + UMSKT::setProgressLabel("s*P"); EC_POINT_mul(eCurve, p, nullptr, p, s, context); + UMSKT::setProgressLabel(NULL); // x = p.x; y = p.y; EC_POINT_get_affine_coordinates(eCurve, p, x, y, context); @@ -245,10 +250,23 @@ void PIDGEN3::BINK2002::Generate( // One random scalar. Further candidates are c+1, c+2, ... so each extra // attempt adds G instead of multiplying the curve again. EC_POINT *r = EC_POINT_new(eCurve); + unsigned mul_bits; UMSKT::umskt_bn_rand(c, FIELD_BITS_2003, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); + mul_bits = (unsigned)BN_num_bits(c); + if (mul_bits == 0) { + mul_bits = 1; + } + UMSKT::setProgressLabel("R=c*G"); + UMSKT::setEcProgressPhase(UMSKT_PROGRESS_MUL); + UMSKT::reportProgress(UMSKT_PROGRESS_MUL, 0, mul_bits); + UMSKT::setMulProgress(1); EC_POINT_mul(eCurve, r, nullptr, basePoint, c, numContext); + UMSKT::setMulProgress(0); + UMSKT::setProgressLabel(NULL); + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, 0, 4096u); do { + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, (unsigned)tries + 1u, 4096u); // x = R.x; y = R.y; EC_POINT_get_affine_coordinates(eCurve, r, x, y, numContext); @@ -280,6 +298,7 @@ void PIDGEN3::BINK2002::Generate( if (!serialInRange) { noSquare = true; tries++; + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, (unsigned)tries, 4096u); if (tries < 4096) { EC_POINT_add(eCurve, r, r, basePoint, numContext); BN_add_word(c, 1); @@ -383,20 +402,21 @@ void PIDGEN3::BINK2002::Generate( pSignature = 0; BN_bn2lebinpad(s, (BYTE *)&pSignature, (int)sizeof(pSignature)); tries++; + UMSKT::reportProgress(UMSKT_PROGRESS_GEN, (unsigned)tries, 4096u); // Pack product key. Pack(pRaw, pUpgrade, pChannelID, pHash, pSignature, pAuthInfo); - UMSKT::debugf("Generation results:\n"); - UMSKT::debugf(" Upgrade: 0x%08x\n", (unsigned)pUpgrade); - UMSKT::debugf("Channel ID: 0x%08x\n", (unsigned)pChannelID); - UMSKT::debugf(" Hash: 0x%08x\n", (unsigned)pHash); - UMSKT::debugf(" Signature: 0x%08llx\n", (unsigned long long)pSignature); - UMSKT::debugf(" AuthInfo: 0x%08x\n", (unsigned)pAuthInfo); - UMSKT::debugf(" Serial: %06u\n", (unsigned)serial); - UMSKT::debugf("\n"); - - if (tries < 4096 && (pSignature > BITMASK(62) || noSquare || !serialInRange)) { + if (!(tries < 4096 && (pSignature > BITMASK(62) || noSquare || !serialInRange))) { + UMSKT::debugf("Generation results:\n"); + UMSKT::debugf(" Upgrade: 0x%08x\n", (unsigned)pUpgrade); + UMSKT::debugf("Channel ID: 0x%08x\n", (unsigned)pChannelID); + UMSKT::debugf(" Hash: 0x%08x\n", (unsigned)pHash); + UMSKT::debugf(" Signature: 0x%08llx\n", (unsigned long long)pSignature); + UMSKT::debugf(" AuthInfo: 0x%08x\n", (unsigned)pAuthInfo); + UMSKT::debugf(" Serial: %06u\n", (unsigned)serial); + UMSKT::debugf("\n"); + } else { EC_POINT_add(eCurve, r, r, basePoint, numContext); BN_add_word(c, 1); } diff --git a/src/windows/crt_win32.cpp b/src/windows/crt_win32.cpp index ded6843..0bfae71 100644 --- a/src/windows/crt_win32.cpp +++ b/src/windows/crt_win32.cpp @@ -15,6 +15,8 @@ extern int main(int argc, char **argv); +extern "C" void umskt_bn_dispatch_init(void); + typedef void (*umskt_ctor)(void); extern "C" umskt_ctor __init_array_start[] __attribute__((weak)); @@ -680,6 +682,7 @@ extern "C" void mainCRTStartup(void) { HANDLE heap = GetProcessHeap(); __main(); + umskt_bn_dispatch_init(); if (!command) { command = (char *)""; } diff --git a/tools/check-win-baseline.ps1 b/tools/check-win-baseline.ps1 index ed55224..934a591 100644 --- a/tools/check-win-baseline.ps1 +++ b/tools/check-win-baseline.ps1 @@ -134,34 +134,32 @@ if ($importHits.Count -gt 0) { Fail "imports outside kernel32, or APIs missing on Windows 98 / NT 4: $shown" } -if ($Arch -eq "x86") { - $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" +$badMnemonic = "(?i)^(vfmadd132pd|vfmadd213pd|vfmadd231pd|v4fmadd|v4fnmadd|vp4dpw|vpopcntd|vpopcntq|vbroadcasti32x|vbroadcasti64x|vbroadcastf32x|vbroadcastf64x|vinserti32x|vinserti64x|vextracti32x|vextracti64x|vpermi2|vpermt2|vpcompress|vpexpand|vpgather[dq][dq]|vpscatter|vgatherpf|vscatterpf)\b" +$badRegister = "(?i)%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() } - 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 (-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")) - } +} +if ($insnHits.Count -gt 0) { + Fail ("forbidden instructions:`n" + ($insnHits -join "`n")) } Write-Host "Windows baseline check passed ($Arch, kernel32 only): $Exe"