ily is an obfuscator for Python scripts.

f0rK 63ca451e44 i love markdown 2 weken geleden
examples ef8ca36aba added a readme & examples 2 weken geleden
README.md 63ca451e44 i love markdown 2 weken geleden
obfuscator.py ef8ca36aba added a readme & examples 2 weken geleden

README.md

ily - an obfuscator for Python scripts

Usage

./obfuscator.py <obfuscations> <filename>

<obfuscations> - string, containing a sequence of obfuscations to apply left-to-right.
<filename> - filename of the program to obfuscate.

Output

ily will output the obfuscated result to STDOUT.

Available obfuscations

IntInflationObfuscation

Obfuscation character: i
Obfuscation safety: Safe

This obfuscation replaces every integer constant with a sum of random integers:

def mult_by_5(x):
    return 5 * x

print(mult_by_5(10))
def mult_by_5(x):
    return (--16 + -171 + -313 + -173 + --15 + -685 + --1316) * x
print(mult_by_5(-993 + -306 + --77 + --794 + -105 + -667 + --29 + -911 + --67 + --2025))

StringInflationObfuscation

Obfuscation character: s
Obfuscation safety: Safe

This obfuscation replaces every string constant with a sum of chr() calls:

print("Hello, world!")
print(chr(72) + chr(101) + chr(108) + chr(108) + chr(111) + chr(44) + chr(32) + chr(119) + chr(111) + chr(114) + chr(108) + chr(100) + chr(33))

HideImportsObfuscation

Obfuscation character: m
Obfuscation safety: Safe

Wraps imports inside an exec() call in order to be further obfuscated by some other obfuscation (like StringInflationObfuscation)

import random

print(random.randint(10, 20))
exec('import random')
print(random.randint(10, 20))

VariableNameObfuscation

Obfuscation character: v
Obfuscation safety: Relatively safe

This obfuscation replaces all top-level names (including import's and from a import b's, in the second case b will be obfuscated) with gibberish.

NOTE: This obfuscation does not obfuscate class methods and attributes (Nested classes are not obfuscated at all), since it is impossible to determine the type of an expression without running the program, thus making it impossible to figure out whether the attriubute name should be obfuscated or not.

class SomeClass:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
    def get_a(self):
        return self.a

    def get_b(self):
        return self.b

def some_func():
    alpha = 13
    beta = 37
    
    return str(alpha) + str(beta)

print(SomeClass(2, 3).get_a())
print(SomeClass(5, 6).get_b())
print(some_func())
class WIQXJMIUJSUJKNFKN:

    def __init__(PWRXDSUBTGVWFO, DJMCCKOVVCJ, UWTDMFEB):
        PWRXDSUBTGVWFO.a = DJMCCKOVVCJ
        PWRXDSUBTGVWFO.b = UWTDMFEB

    def get_a(PWRXDSUBTGVWFO):
        return PWRXDSUBTGVWFO.a

    def get_b(PWRXDSUBTGVWFO):
        return PWRXDSUBTGVWFO.b

def MICWCKPPQMNYYHUARS():
    SPMTLGTUGXLOAVRQIJKK = 13
    PHQOLPIOECPKWOYYQPIL = 37
    return str(SPMTLGTUGXLOAVRQIJKK) + str(PHQOLPIOECPKWOYYQPIL)
print(WIQXJMIUJSUJKNFKN(2, 3).get_a())
print(WIQXJMIUJSUJKNFKN(5, 6).get_b())
print(MICWCKPPQMNYYHUARS())

AttributeAccessObfuscation

Obfuscation character: a
Obfuscation safety: Relatively unsafe (Not tested much)

This obfuscation replaces attribute accesses with getattr() and setattr() where applicable:

import random
import string

class RandomStringBuilder:
    def __init__(self, length):
        self.length = length
    
    def build_random_string(self):
        return "".join([random.choice(string.ascii_letters) for i in range(self.length)])
        
print(RandomStringBuilder(13).build_random_string())
import random
import string

class RandomStringBuilder:

    def __init__(self, length):
        setattr(self, 'length', length)

    def build_random_string(self):
        return getattr('', 'join')([getattr(random, 'choice')(getattr(string, 'ascii_letters')) for i in range(getattr(self, 'length'))])
print(getattr(RandomStringBuilder(13), 'build_random_string')())

BuiltinsObfuscation

Obfuscation character: b
Obfuscation safety: Relatively unsafe (Not tested much)

This obfuscation wraps all built-ins in builtins., for example: range turns into builtins.range. It also wraps True, False and None into builtins.getattr(builtins, 'True'), builtins.getattr(builtins, 'False') and builtins.getattr(builtins, 'None') respectively.

NOTE: This obfuscation breaks if user code has redefined built-in names. For example, if the program has a def input() or a def hash(x), this obfuscation won't work properly.

Source

def EncodeNegBase(n, b): #Converts from decimal
	if n == 0:
		return "0"
	out = []
	while n != 0:
		n, rem = divmod(n, b)
		if rem < 0:
			n += 1
			rem -= b
		out.append(rem)
	return "".join(map(str, out[::-1]))

def DecodeNegBase(nstr, b): #Converts to decimal
	if nstr == "0":
		return 0
	
	total = 0
	for i, ch in enumerate(nstr[::-1]):
		total += int(ch) * b**i
	return total

if __name__=="__main__":
	
	print ("Encode 10 as negabinary (expect 11110)")
	result = EncodeNegBase(10, -2)
	print (result)
	if DecodeNegBase(result, -2) == 10: print ("Converted back to decimal")
	else: print ("Error converting back to decimal")

	print ("Encode 146 as negaternary (expect 21102)")
	result = EncodeNegBase(146, -3)
	print (result)
	if DecodeNegBase(result, -3) == 146: print ("Converted back to decimal")
	else: print ("Error converting back to decimal")

	print ("Encode 15 as negadecimal (expect 195)")
	result = EncodeNegBase(15, -10)
	print (result)
	if DecodeNegBase(result, -10) == 15: print ("Converted back to decimal")
	else: print ("Error converting back to decimal")
import builtins

def EncodeNegBase(n, b):
    if n == 0:
        return '0'
    out = []
    while n != 0:
        n, rem = builtins.divmod(n, b)
        if rem < 0:
            n += 1
            rem -= b
        out.append(rem)
    return ''.join(builtins.map(builtins.str, out[::-1]))

def DecodeNegBase(nstr, b):
    if nstr == '0':
        return 0
    total = 0
    for i, ch in builtins.enumerate(nstr[::-1]):
        total += builtins.int(ch) * b ** i
    return total
if __name__ == '__main__':
    builtins.print('Encode 10 as negabinary (expect 11110)')
    result = EncodeNegBase(10, -2)
    builtins.print(result)
    if DecodeNegBase(result, -2) == 10:
        builtins.print('Converted back to decimal')
    else:
        builtins.print('Error converting back to decimal')
    builtins.print('Encode 146 as negaternary (expect 21102)')
    result = EncodeNegBase(146, -3)
    builtins.print(result)
    if DecodeNegBase(result, -3) == 146:
        builtins.print('Converted back to decimal')
    else:
        builtins.print('Error converting back to decimal')
    builtins.print('Encode 15 as negadecimal (expect 195)')
    result = EncodeNegBase(15, -10)
    builtins.print(result)
    if DecodeNegBase(result, -10) == 15:
        builtins.print('Converted back to decimal')
    else:
        builtins.print('Error converting back to decimal')

FAQ

Q: Why even make a Python obfuscator, if readabilty is Python's big thing?
A: I was bored.

Q: Is this safe to use commercially?
A: If you are brave enough, but most likely no. I don't recommend this.

Q: Feature <insert feature name here> is not supported. Will you update ily to support it?
A: Probably not, unless it is extensively used.

Q: What does ily stand for?
A: It stands for I love you, thank you for asking :)