Become a member

Get the best offers and updates relating to Liberty Case News.

― Advertisement ―

spot_img

@bfeld v60.0 – Feld Thoughts

My father Stan and I in our default states.lsof -ti:3000 | xargs kill -9 2>/dev/null; npm run devI’ve been wandering up to 60 for...
HomeTechi cracked a $200 software protection in a day with xcopy

i cracked a $200 software protection in a day with xcopy

disclaimer: this is educational security research only. i do not condone piracy. i purchased a legitimate license for this software and conducted this analysis on my own property. this writeup exists to document protection implementation flaws, not to enable theft. support developers – buy their software.

github repo: vmfunc/enigma

tl;dr

i spent a day analyzing enigma protector – a $200 commercial software protection system used by thousands of vendors. RSA cryptographic signatures, hardware-bound licensing, anti-debugging, VM-based code obfuscation. serious enterprise security theater.

then i noticed the protected installer extracts a completely unprotected payload to disk.

xcopy /E "C:\Program Files\...\product" .\crack\

that’s the entire crack. copy the installed files. they run on any machine. no keygen needed, no binary patching, no cryptanalysis.

$200 protection defeated by a command that shipped with DOS 3.2 in 1986.

this is a case study in why threat modeling matters more than fancy cryptography, and why “military-grade encryption” means nothing when you leave the back door wide open.


target overview

bass bully premium – a VST3 synthesizer plugin. protected by enigma protector, a commercial software protection system that costs $250+ and promises serious security.

from their marketing:

“Enigma Protector is a powerful tool designed to protect executable files from illegal copying, hacking, modification and analysis.”

we’ll see about that.

we have one known valid license:

Key:  GLUJ-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-V99KP3
HWID: 3148CC-XXXXXX
Name: Bass Bully

our goal: understand the protection and build a proper crack


static analysis

first, let’s look at what we’re dealing with:

import pefile

pe = pefile.PE(r"Bass Bully Premium_Installer_win64.exe")

print(f"Machine:     {'x64' if pe.FILE_HEADER.Machine == 0x8664 else 'x86'}")
print(f"Sections:    {pe.FILE_HEADER.NumberOfSections}")
print(f"Entry Point: 0x{pe.OPTIONAL_HEADER.AddressOfEntryPoint:X}")
print(f"Image Base:  0x{pe.OPTIONAL_HEADER.ImageBase:X}")
Machine:     x64
Sections:    9
Entry Point: 0x16485D0
Image Base:  0x140000000

that entry point is suspicious. 0x16485D0 is way into the binary.. typical of packed executables where the real entry point is hidden. normal programs start around 0x1000.

string hunting

with open(pe_path, 'rb') as f:
    data = f.read()

for target in [b'Enigma Protector', b'enigmaprotector']:
    offset = 0
    while (idx := data.find(target, offset)) != -1:
        print(f"0x{idx:08X}: {target.decode()}")
        offset = idx + 1
0x0040972B: Enigma Protector
0x00409746: Enigma Protector
0x00409786: Enigma Protector
0x00409BA8: Enigma Protector
0x00409BC3: Enigma Protector
0x0040A038: Enigma Protector
0x0040A053: Enigma Protector
0x004099BF: enigmaprotector
0x00409DDA: enigmaprotector

confirmed: enigma protector. now we know what we’re dealing with.

what about the network?

does this even phone home..?

imports = [entry.dll.decode() for entry in pe.DIRECTORY_ENTRY_IMPORT]
kernel32.dll, user32.dll, advapi32.dll, oleaut32.dll, gdi32.dll,
shell32.dll, version.dll, ole32.dll, COMDLG32.dll, MSVCP140.dll, ...

no winhttp.dll, wininet.dll, or ws2_32.dll. offline validation only. all crypto is local, so theoretically extractable.

this is good news!! online validation would require MITM or server emulation. offline means everything we need is in the binary.


the enigma protector internals

enigma protector is a commercial protection system that provides:

  1. code virtualization – transforms x86/x64 into proprietary VM bytecode
  2. anti-debuggingIsDebuggerPresent, timing checks, hardware BP detection
  3. anti-tampering – CRC checks on packed sections
  4. registration API – HWID-bound licensing with RSA signatures

you can read about all these features on their documentation page. they’re pretty thorough about explaining what they protect against. they just didn’t think someone would… not use it on the payload.

The Enigma Protection and motivation for buying - diagram showing protection levels

the registration API

according to enigma’s SDK, these functions are exposed:

int EP_RegCheckKey(const char* name, const char* key);
const char* EP_RegHardwareID(void);
void EP_RegSaveKey(const char* name, const char* key);
void EP_RegLoadKey(char* name, char* key);

these aren’t normal exports; they’re resolved dynamically after enigma unpacks itself. you can’t just GetProcAddress them from outside. you have to either:

  • hook them at runtime after unpacking
  • pattern scan the unpacked memory
  • be an absolute clown and just not use them in your payload (definitely not foreshadowing)

the entry point

using capstone to disassemble the entry point:

from capstone import Cs, CS_ARCH_X86, CS_MODE_64

entry_rva = pe.OPTIONAL_HEADER.AddressOfEntryPoint
entry_offset = pe.get_offset_from_rva(entry_rva)

with open(pe_path, 'rb') as f:
    f.seek(entry_offset)
    code = f.read(64)

md = Cs(CS_ARCH_X86, CS_MODE_64)
base = pe.OPTIONAL_HEADER.ImageBase + entry_rva

for insn in md.disasm(code, base):
    print(f"0x{insn.address:X}: {insn.mnemonic:8} {insn.op_str}")
0x1416485D0: jmp      0x1416485da      ; skip garbage bytes
0x1416485D2: add      byte ptr [rsi + 0x40], dl
0x1416485D8: add      byte ptr [rax], al
0x1416485DA: push     rax              ; real code starts here
0x1416485DB: push     rcx
0x1416485DC: push     rdx
0x1416485DD: push     rbx
0x1416485DE: push     rbp
0x1416485DF: push     rsi
0x1416485E0: push     rdi
0x1416485E1: push     r8
0x1416485E3: push     r9

the jmp-over-garbage pattern is classic anti-disassembly. linear disassemblers will try to decode the garbage bytes between jmp and its target, producing nonsense. the real unpacker starts at 0x1416485DA with a standard register preservation sequence before calling the enigma loader.


phase 3: key format analysis

we have a known valid key. let’s understand its structure before we try to break it.

GLUJ-XXXX-XXXX-XXXX-XXXX-XXXX-XXXX-V99KP3

structure breakdown

  • 8 groups separated by dashes
  • groups 0-6: 4 characters each
  • group 7: 6 characters (larger – likely checksum/signature)
  • character set: 0-9, A-Z (base36)

base36 decoding

key = "GLUJ-QE58-U3Z4-RQTJ-K7GJ-JXZ5-CVK5-V99KP3"
groups = key.split('-')

for i, group in enumerate(groups):
    val = int(group, 36)
    bits = val.bit_length()
    print(f"[{i}] {group:6} = {val:10} (0x{val:08X}) {bits:2} bits")
[0] GLUJ   =     774811 (0x000BD29B) 20 bits
[1] QE58   =    1231388 (0x0012CA1C) 21 bits
[2] U3Z4   =    1404832 (0x00156FA0) 21 bits
[3] RQTJ   =    1294471 (0x0013C087) 21 bits
[4] K7GJ   =     942787 (0x000E62C3) 20 bits
[5] JXZ5   =     930497 (0x000E32C1) 20 bits
[6] CVK5   =     600773 (0x00092AC5) 20 bits
[7] V99KP3 = 1890014727 (0x70A75607) 31 bits  <- significantly larger

interesting. group 7 is way bigger than the others. that’s probably a truncated cryptographic signature.

cryptographic structure

the key structure appears to be:

[    DATA: ~143 bits     ] [ SIGNATURE: 31 bits ]
 Groups 0-6 (7 x ~20 bits)   Group 7 (truncated)

enigma uses RSA for signing. the full signature would be much larger, but they truncate it to fit the key format. this means:

  1. public key is embedded in the protected binary
  2. key validation = RSA signature verification
  3. keygen would require extracting and factoring the RSA modulus

RSA with small key sizes is technically factorable with enough compute. but that’s a lot of work for… well, you’ll see.

HWID format

hwid = "3148CC-059521"
parts = hwid.split('-')
# Two 24-bit values = 48 bits total hardware fingerprint

HWID is derived from hardware characteristics (CPU ID, disk serial, MAC address, etc). the key is cryptographically bound to this value, so a key generated for one machine won’t work on another.

this is actually decent protection! if they used it properly! (they didn’t lmao)


the pivot

at this point i’m preparing to either factor the RSA key or do runtime hooking to bypass validation. then i thought: wait, what are we actually protecting here?

let me just check the installed VST real quick…

analyzing the installed VST

vst_path = r"C:\Program Files\Common Files\VST3\Bass Bully VST\Bass Bully Premium.vst3"
vst_dll = vst_path + r"\Contents\x86_64-win\Bass Bully Premium.vst3"
pe_vst = pefile.PE(vst_dll)

print(f"Size: {os.path.getsize(vst_dll):,} bytes")
print("Imports:")
for entry in pe_vst.DIRECTORY_ENTRY_IMPORT:
    print(f"  {entry.dll.decode()}")
Size: 7,092,736 bytes
Imports:
  KERNEL32.dll
  USER32.dll
  GDI32.dll
  SHELL32.dll
  ole32.dll
  OLEAUT32.dll
  MSVCP140.dll
  WINMM.dll
  IMM32.dll
  dxgi.dll
  VCRUNTIME140.dll
  VCRUNTIME140_1.dll
  api-ms-win-crt-runtime-l1-1-0.dll
  ...

wait. where are the enigma imports?

hunting for protection

with open(vst_dll, 'rb') as f:
    data = f.read()

for term in [b'Enigma', b'EP_Reg', b'Registration', b'HWID', b'enigma']:
    count = data.count(term)
    print(f"{term.decode():15} : {count} occurrences")
Enigma          : 0 occurrences
EP_Reg          : 0 occurrences
Registration    : 0 occurrences
HWID            : 0 occurrences
enigma          : 0 occurrences

zero.

hold on.

$ strings "Bass Bully Premium.vst3" | grep -i enigma
$ strings "Bass Bully Premium.vst3" | grep -i regist

nothing. no output… ?????????

you have got to be kidding me.

the VST has absolutely no protection. it’s a clean JUCE framework build. no enigma runtime. no license callbacks. no protection whatsoever.

they protected the installer. not the payload. THE INSTALLER. NOT THE ACTUAL PRODUCT.

i can’t even be mad. this is genuinely hilarious.


the vulnerability

here’s what’s happening:

+-------------------------------------------------------------------+
|                    ENIGMA PROTECTOR                               |
|  +--------------------------------------------------------------+ |
|  |  Installer.exe                                               | |
|  |  [x] RSA key verification                                    | |
|  |  [x] HWID binding                                            | |
|  |  [x] Anti-debug, anti-tamper                                 | |
|  |  [x] Code virtualization                                     | |
|  |                        |                                     | |
|  |                        v                                     | |
|  |  +--------------------------------------------------------+  | |
|  |  |  Payload (extracted on install)                        |  | |
|  |  |  - Bass Bully Premium.vst3  <- ZERO PROTECTION lol     |  | |
|  |  |  - Bass Bully Premium.rom   <- NOT EVEN ENCRYPTED      |  | |
|  |  +--------------------------------------------------------+  | |
|  +--------------------------------------------------------------+ |
+-------------------------------------------------------------------+

the entire protection stack only controls whether the installer runs. once files hit disk, the protection is basically useless.

this is like putting a vault door on a tent.

what they should have done

enigma would have been effective if the VST itself checked the license:

bool VST_Init() {
    char key[256], name[256];
    EP_RegLoadKey(name, key);

    if (!EP_RegCheckKey(name, key)) {
        ShowTrialNag();
        return false;
    }

    CreateThread(NULL, 0, LicenseWatchdog, NULL, 0, NULL);
    return true;
}

instead, the VST has no EP_Reg* calls. no license checks. no callbacks. nothing. it just… runs.


the crack

the crack is embarrassingly simple. i spent hours analyzing RSA key formats for this…

the very sophisticated exploit

xcopy /E "C:\Program Files\Common Files\VST3\Bass Bully VST" .\crack\
copy "C:\ProgramData\Bass Bully VST\Bass Bully Premium\*.rom" .\crack\

that’s it. that’s the crack. copy the files. they work on any machine because there’s no license check in the actual product.

i wrote a python script to automate it because i have some self-respect:

#!/usr/bin/env python3
import shutil
from pathlib import Path

VST_SRC = Path(r"C:\Program Files\Common Files\VST3\Bass Bully VST\Bass Bully Premium.vst3")
ROM_SRC = Path(r"C:\ProgramData\Bass Bully VST\Bass Bully Premium\Bass Bully Premium.rom")

def extract():
    out = Path("crack_package")
    out.mkdir(exist_ok=True)
    shutil.copytree(VST_SRC, out / "Bass Bully Premium.vst3", dirs_exist_ok=True)
    shutil.copy2(ROM_SRC, out / "Bass Bully Premium.rom")
    print("[+] done")

if __name__ == "__main__":
    extract()

usage:

python patcher.py

load in fl studio. no registration. no nag. no nothing. because there’s no check.


for science: the hook approach

we also wrote a DLL that hooks enigma’s validation at runtime. completely unnecessary given the vulnerability, but i’d already done the research so here it is:

#include 
#include 

static int (WINAPI *Real_EP_RegCheckKey)(LPCSTR, LPCSTR) = NULL;

int WINAPI Hooked_EP_RegCheckKey(LPCSTR name, LPCSTR key) {
    return 1;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {
    if (reason == DLL_PROCESS_ATTACH) {
        Sleep(2000);
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)Real_EP_RegCheckKey, Hooked_EP_RegCheckKey);
        DetourTransactionCommit();
    }
    return TRUE;
}

this approach works great. completely unnecessary. the payload has no protection.

Screenshot showing hook approach
Screenshot showing hook approach result


lessons learned

for developers

  1. protect the payload, not the installer – if users need the installed files to run your software, those files need runtime protection

  2. defense in depth – don’t rely on a single layer. the VST should call EP_RegCheckKey on load

  3. threat model correctly – ask “what happens after installation?” if the answer is “nothing checks the license”, you have a problem

  4. periodic validation – one-time checks are trivially bypassed by file copying

for reversers

  1. always check the payload first – before diving into complex crypto, verify what you’re actually protecting

  2. the simplest attack wins – don’t factor RSA when xcopy works

  3. protection != security – expensive protection systems are worthless if applied incorrectly

  4. sometimes the crack writes itself – not every target requires sophisticated techniques


conclusion

enigma protector’s $250 protection was defeated by:

xcopy /E "C:\Program Files\..." .\crack\

the protection itself works fine – RSA signatures, HWID binding, anti-debug. but it only protects the installer. the payload runs completely unprotected.

250 dollars for a this…

Source link