top of page

Keygen Analysis

Keygen Analysis | Black Hat HQ

Keygen (Key Generator) Analysis


Keygen (key generator) analysis means reverse engineering how a program generates license keys — either to understand weakness in the algorithm or to demonstrate that the licensing scheme is bypassable. This article is focused on Keygen Analysis.


Phase 1: Identify The License Algorithm


Step 1: Find Where License Key is Checked


String search:


bash

strings -n 6 target_app.exe | grep -iE "license|key|serial|register|activation|valid|invalid|thank you|trial"

Ghidra workflow:


1. Import binary → Auto-analyze
2. Symbol Tree → Strings → Filter by "license" or "key"
3. Right-click found string → "Show References to Address"
4. Follow cross-reference → found the license check function

Sample decompiler output showing license validation:


c

int validateLicense(char *userKey) {
    char validChars[32];
    GetSystemInfo(&sysInfo);                    // Some HW binding
    generateValidKey(validChars, sysInfo);      // Generates expected value
    int result = strcmp(userKey, validChars);   // Compare!
    return result == 0;                         // Returns 1 if valid
}

Phase 2: Reverse The Key Generation Function


Finding the Algorithm


Look for the function that generates the expected key (called generateValidKey above). It typically:


  1. Reads system/hardware info (username, MAC, drive serial)

  2. Performs operations: XOR, hashing (MD5/SHA1), base32/hex encoding

  3. Possibly inserts dashes every 4-5 characters


Decompiler example:


c

void generateValidKey(char *output, SYSTEM_INFO *si) {
    char raw[32];
    // Step 1: concatenate hardware values
    sprintf(raw, "%s-%08X-%08X", si->username, si->diskSerial, si->macHash);
    // Step 2: take MD5
    MD5((uchar*)raw, strlen(raw), md5hash);
    // Step 3: convert to uppercase hex
    for (int i = 0; i < 16; i++) {
        sprintf(output + (i*2), "%02X", md5hash[i]);
    }
    // Step 4: Insert dashes every 4 chars -> output is a 32-char hex key
}

Tracing the Algorithm (radare2 / GDB)


bash

# Dynamic tracing with ltrace (captures library calls)
ltrace -e "*" ./target_app.exe TEST-1234-5678 2>&1 | grep -E "strcmp|md5|sha|crc|sprintf"

# GDB to step through generateValidKey
gdb ./target_app.exe
(gdb) break generateValidKey
(gdb) run
(gdb) ni  # Step through line by line
(gdb) x/s $rdi  # Print computed key at each step

Phase 3: Reimplement the Algorithm (Keygen)


Once you understand the algorithm, reimplement it in Python to generate valid keys.


Example: Simple XOR + Hex Key


Original algorithm (reversed from decompiler):


c

void generateKey(char *name, char *output) {
    unsigned char key[8];
    for (int i = 0; i < 8; i++) {
        key[i] = name[i % strlen(name)] ^ 0x55;
    }
    sprintf(output, "%02X%02X-%02X%02X-%02X%02X-%02X%02X",
            key[0],key[1],key[2],key[3],key[4],key[5],key[6],key[7]);
}

Keygen in Python:


python

#!/usr/bin/env python3
# Keygen for TargetApp v2.5 (Authorized Pentest)
# Algorithm: XOR name bytes with 0x55

def generate_key(name):
    key_bytes = []
    for i in range(8):
        ch = name[i % len(name)]
        key_bytes.append(ord(ch) ^ 0x55)
    
    return f"{key_bytes[0]:02X}{key_bytes[1]:02X}-{key_bytes[2]:02X}{key_bytes[3]:02X}-{key_bytes[4]:02X}{key_bytes[5]:02X}-{key_bytes[6]:02X}{key_bytes[7]:02X}"

if __name__ == "__main__":
    name = input("Enter username: ")
    print(f"License key: {generate_key(name)}")

Test:


bash

python3 keygen.py <<< "John Doe"
# Output: 3A1F-4B2C-5D3E-6F40
# Enter this into target_app → "Registration successful!"

Phase 4: Advanced Keygen Patterns


Hardware-Locked Seats (Machine Fingerprint)


c

// Reversed algorithm combines CPU serial + MAC + disk ID
void generateKey(char *output) {
    char cpu_id[16], mac[6], disk[16];
    GetCPUID(cpu_id);      // CPU serial number
    GetMACAddress(mac);    // First NIC MAC
    GetDiskSerial(disk);   // C: drive serial
    
    // HMAC-SHA1 of joined values
    char raw[64];
    sprintf(raw, "%s-%02X%02X-%s-%s", cpu_id, mac[0], mac[1], disk, SALT);
    HMAC_SHA1((uchar*)raw, strlen(raw), SECRET_KEY, 16, hmac_result);
    
    // Encode as base32
    base32_encode(hmac_result, 20, output);
}

Keygen approach:


python

import hmac
import hashlib
import base64

# You need the SECRET_KEY (find it embedded in binary via strings)
# You need SALT (also embedded in binary)

def generate_hw_key(cpu_id, mac_prefix, disk_serial, secret, salt):
    raw = f"{cpu_id}-{mac_prefix}-{disk_serial}-{salt}"
    h = hmac.new(secret.encode(), raw.encode(), hashlib.sha1).digest()
    # base32 encode with padding stripped
    return base64.b32encode(h).decode().rstrip('=')

Elliptic Curve / RSA Signatures (Harder)


Some software uses asymmetric crypto — the binary has the public key to verify, and you need the private key to sign.


Bypass approach for asymmetric:


1. Find the public key in the binary
2. Source the public key → does it belong to a known vendor cert?
3. If no private key, don't brute-force — instead:
   a. Patch the binary to accept any signed payload
   b. OR: Replace the public key with your own (generate keypair)
   c. OR: NOP the signature check entirely

Phase 5: Algorithm Weakness Assessment


Weakness

What It Means

Exploit Method

Deterministic

Same input → same output

Keygen is trivial

Short keyspace

Key is 6 decimal digits

Brute-force in seconds

Weak hash

CRC32, XOR, custom (not crypto)

Invert/reverse the function

No HW binding

Any machine accepts key

Generate unlimited keys

Static seed

Hardcoded seed in binary

Always same key sequence

Predictable PRNG

rand() without entropy

Recover state → predict all keys

Embedded private key

Signature key in binary

Extract → sign anything


Phase 6: Reporting Template


FINDING: Weak License Key Generation Algorithm
================================================
Product: TargetApp v2.5
Algorithm: XOR (name bytes ^ 0x55) → hex encode
Keyspace: 2^64 (theoretical) but deterministic — single valid key per username

SEVERITY: CVSS 6.5 (Medium)
Vector: AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Impact: Unauthorized full-feature access without purchase

EVIDENCE:
- Reverse-engineered keygen algorithm (Python script attached)
- Generated valid keys for 3 usernames — all accepted
- No server-side validation (key verified entirely locally)

ALGORITHM RECONSTRUCTION:
# Raw bytes from decompiler at 0x004012A0:
# XOR with 0x55, hex encode with dash every 4 chars

def keygen(username):
    key = []
    for i in range(8):
        key.append(ord(username[i % len(username)]) ^ 0x55)
    return '-'.join(f"{b:02X}" for b in key[:2]) + '-' + \
           '-'.join(f"{b:02X}" for b in key[2:4]) + '-' + \
           '-'.join(f"{b:02X}" for b in key[4:6]) + '-' + \
           '-'.join(f"{b:02X}" for b in key[6:8])

TIMELINE:
- 30 min: Static analysis identified strcmp at 0x004012A0
- 15 min: Traced back to generateValidKey function
- 20 min: Reversed XOR algorithm from decompiler output
- 10 min: Wrote Python keygen (6 lines)

RECOMMENDATIONS:
1. Use asymmetric signature (RSA/ECDSA) — verify with public key only
2. Move critical validation to server-side
3. Obfuscate key generation logic (virtualization, control flow flattening)
4. Implement code integrity checks (CRC of license validation function)
5. Require periodic online re-validation

Quick Reference: Common Keygen Patterns


Pattern

Detection in Ghidra

Attack

strcmp literal string

"Expected key is hardcoded"

Copy the string (no algorithm)

XOR with constant

Look for XOR instructions before compare

Invert XOR = same operation

MD5/SHA1

MD5_Init/Update/Final imports

Known algorithm, just copy

sprintf format string

GUI for hex encoding

Map format → reverse

rand() + srand()

PRNG calls

Fixed seed? Predictable sequence

CRC32

mmcrc32 or crc32 instruction

Reversible (not cryptographic)

HMAC with hardcoded key

Look for embedded char key[]

Extract key → forge signatures


Enroll In Online Cybersecurity & Hacking Classes/Courses | Black Hat HQ

Comments


bottom of page