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:
U30JZ3SO7orU30jz3so7(case-insensitive, the script calls both variants) - Hardcoded 2-byte key:
"06"(observed in sample9e95f20b); other siblings may use different keys DllCallinvocations where the first argument is a call to the obfuscation function rather than a plain stringDllStructCreatetype 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):
kidx = (i - 1) % len(key)X = (ord(key[kidx]) * i) % len(ciphertext) + 1- If
Z[X-1]is already filled, linear-probe:X = (X % len) + 1 - 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-ripperorMyAut2Exeto 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
DllCallat runtime: The obfuscated strings are decoded immediately before theDllCallexecutes; dynamic tracing captures the plaintext API names
Pages Where Observed
- unclassified-autoit-compiled — cluster overview
- k30zwmbjj-string-obfuscation — prior sibling variant (XOR key
"AA", permutation table) - autoit-delimiter-reverse-obfuscation — Formbook variant (delimiter
6504+ reverse)