104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
"""
|
|
@file utils/encryption.py
|
|
@brief String encryption utilities for OMG-Fuscator.
|
|
@details Provides the StringEncryptor used by the obfuscation pipeline to transform
|
|
string literals into runtime-decrypted expressions, making static analysis
|
|
significantly harder.
|
|
"""
|
|
|
|
import base64
|
|
import hashlib
|
|
import random
|
|
from typing import Tuple
|
|
|
|
class StringEncryptor:
|
|
"""
|
|
@brief Encrypts strings for obfuscation using layered XOR and Base85 encoding.
|
|
@details Produces both an encoded payload and Python code that reconstructs
|
|
the keys at runtime without embedding raw key bytes.
|
|
"""
|
|
def __init__(self, primary_key: bytes, secondary_key: bytes, salt: bytes):
|
|
"""
|
|
@brief Initialize encryptor keys.
|
|
@param primary_key Primary XOR key bytes.
|
|
@param secondary_key Secondary XOR key bytes.
|
|
@param salt Salt bytes used to derive a per-string modifier.
|
|
"""
|
|
self.primary_key = primary_key
|
|
self.secondary_key = secondary_key
|
|
self.salt = salt
|
|
|
|
def hide_byte(self, b: int) -> str:
|
|
"""
|
|
@brief Obfuscate a single key byte into an arithmetic/bitwise expression.
|
|
@param b Input byte value in range [0, 255].
|
|
@return Python expression string that evaluates to the original byte at runtime.
|
|
"""
|
|
pattern = random.randint(1, 5)
|
|
if pattern == 1:
|
|
x1, x2, x3 = [random.randint(65, 90) for _ in range(3)]
|
|
result = x1 ^ x2 ^ x3 ^ b
|
|
return f"({x1}^{x2}^{x3}^{result})"
|
|
elif pattern == 2:
|
|
base = random.randint(65, 90)
|
|
multiplier = random.randint(2, 4)
|
|
adjusted = (base * multiplier - b)
|
|
return f"(({base}*{multiplier}-{adjusted}))"
|
|
elif pattern == 3:
|
|
shift = random.randint(1, 3)
|
|
modified = (b << shift) >> shift
|
|
diff = b - modified
|
|
return f"((({b << shift})>>{shift})+{diff})"
|
|
elif pattern == 4:
|
|
char1, char2 = random.sample(range(65, 91), 2)
|
|
result = char1 + char2 - b
|
|
return f"({char1}+{char2}-{result})"
|
|
else:
|
|
mod_base = random.randint(91, 100)
|
|
result = b % mod_base
|
|
factor = b // mod_base
|
|
return f"({factor}*{mod_base}+{result})"
|
|
|
|
def encrypt_string(self, s: str) -> Tuple[str, str, str]:
|
|
"""
|
|
@brief Encrypt a string with layered XOR and Base85 encoding.
|
|
@param s UTF-8 string to encrypt.
|
|
@return Tuple[str, str, str]:
|
|
- encoded: Base85 text of the encrypted payload
|
|
- key_setup: Python code that reconstructs keys at runtime
|
|
- modifier_hex: Hex string for the per-string modifier
|
|
"""
|
|
data = s.encode('utf-8')
|
|
modifier = hashlib.sha256(self.salt + data).digest()[:8]
|
|
|
|
layer1 = bytes(
|
|
d ^ k ^ m
|
|
for d, k, m in zip(
|
|
data,
|
|
self.primary_key * ((len(data) // len(self.primary_key)) + 1),
|
|
modifier * ((len(data) // len(modifier)) + 1)
|
|
)
|
|
)
|
|
|
|
final_data = bytes(
|
|
d ^ k
|
|
for d, k in zip(
|
|
layer1,
|
|
self.secondary_key * ((len(layer1) // len(self.secondary_key)) + 1)
|
|
)
|
|
)
|
|
|
|
encoded = base64.b85encode(final_data).decode()
|
|
|
|
primary_key_code = f"bytes([{','.join(self.hide_byte(b) for b in self.primary_key)}])"
|
|
secondary_key_code = f"bytes([{','.join(self.hide_byte(b) for b in self.secondary_key)}])"
|
|
salt_code = f"bytes([{','.join(self.hide_byte(b) for b in self.salt)}])"
|
|
|
|
key_setup = (
|
|
f"_pk = {primary_key_code}\n"
|
|
f"_sk = {secondary_key_code}\n"
|
|
f"_st = {salt_code}"
|
|
)
|
|
|
|
return encoded, key_setup, modifier.hex()
|