typetechniquecreated2026-07-02updated2026-07-02obfuscationautoitevasionresearch-target

U30JZ3SO7 Permutation-XOR String Obfuscation

What It Does

A custom AutoIt string-obfuscation function that hides API names and string literals inside compiled AutoIt3 scripts. It combines a key-scheduled permutation ( Fisher-Yates-like shuffle driven by the key bytes ) with a subtractive-XOR layer to produce ciphertext that defeats naive string extraction.

Detection / Fingerprint

Look for these artefacts in decompiled AutoIt scripts:

  • Function name pattern: U30JZ3SO7 or U30jz3so7 (case-insensitive, the script calls both variants)
  • Hardcoded 2-byte key: "06" (observed in sample 9e95f20b); other siblings may use different keys
  • DllCall invocations where the first argument is a call to the obfuscation function rather than a plain string
  • DllStructCreate type descriptors built by concatenating obfuscated fragments

Implementation Pattern

The function takes two arguments: the ciphertext string and the key string.

Phase 1: Key-scheduled permutation

For each character position i (1-indexed):

  1. kidx = (i - 1) % len(key)
  2. X = (ord(key[kidx]) * i) % len(ciphertext) + 1
  3. If Z[X-1] is already filled, linear-probe: X = (X % len) + 1
  4. Store Z[X-1] = i

This produces a permutation table Z where the ordering is keyed by the password.

Phase 2: Rearrangement

Apply the inverse permutation: D[Z[i] - 1] = ciphertext[i] for i = 0..N-1.

Phase 3: XOR decrypt

For each character in the rearranged string:

  • plaintext[i] = chr((D[i] - 5) ^ ord(key[i % len(key)]))

The subtraction of 5 before XOR is a weak fixed constant that aids recognition.

Reproduce on Your Own VMs

def u30jz3so7_decode(ciphertext: str, key: str) -> str:
    M = len(ciphertext)
    D_len = len(key)
    
    # Phase 1: permutation
    Z = [None] * M
    for i in range(1, M + 1):
        kidx = (i - 1) % D_len
        X = (ord(key[kidx]) * i) % M + 1
        while Z[X - 1] is not None:
            X = (X % M) + 1
        Z[X - 1] = i
    
    # Phase 2: rearrange
    D = [''] * M
    for i in range(1, M + 1):
        D[Z[i - 1] - 1] = ciphertext[i - 1]
    
    K = ''.join(D)
    
    # Phase 3: XOR decrypt
    result = ''
    for i in range(1, len(K) + 1):
        P = ord(K[i - 1])
        N = ord(key[(i - 1) % D_len])
        result += chr((P - 5) ^ N)
    
    return result

# Example from sample 9e95f20b
print(u30jz3so7_decode("`G]ZX_\b\t", "06"))   # -> "kernel32"
print(u30jz3so7_decode("kdGGJ\\a|a_dZ", "06"))  # -> "VirtualAlloc"

Defensive Countermeasures

  • Decompile first: autoit-ripper or MyAut2Exe to recover the script; the obfuscation is script-level, not binary-level
  • Brute-force the key: With a 2-byte key there are only 65,536 possibilities. For longer keys, frequency analysis on the permutation table can reveal the key length
  • Hook DllCall at runtime: The obfuscated strings are decoded immediately before the DllCall executes; dynamic tracing captures the plaintext API names

Pages Where Observed