How to Obfuscate Python Code

Migel Hewage Nimesha Feb 02, 2024
  1. Different Steps to Perform Obfuscation in Python
  2. Obfuscation Tools
  3. Obfuscate a Python Program With Pyarmor
How to Obfuscate Python Code

Code obfuscation is the deliberate process of creating source or machine code that is difficult for humans to understand but still executable by a computer. Generally used when the programmer wants to: prevent reverse engineering, deter interfering with the program and create a challenge for like-minded geeks.

By obfuscating the source code, the programmer does not lose control of the code and whether the code gets delivered to the client for specific reasons.

Obfuscation is similar to encryption as the goal is equal, but encryption needs decryption to access the information, while obfuscated code can execute directly. The programmer can follow different steps to retain control and protect the original code, such as: including a few fallback conditions for controlling the dependency.

Even though obfuscation frequently gets a bad reputation due to malware authors and bad actors commonly, this has a few legitimate purposes. Besides, obfuscation embeds an exclusive theory embeds behind it where the possibilities are limitless.

Different Steps to Perform Obfuscation in Python

To demonstrate various methods to use obfuscation, create a nicely formatted simple code, as shown below.

heightTest = "Hi, I am {}. My height is {} ft."  # output format

# the hint will make the types accepted by the test()


def test(person: str, height: int) -> str:
    return heightTest.format(person, height)


# getting user inputs from the keyboard for name and height
name = input("What is your Name?")
height = float(input("What is your Height?"))

print(test(name, height))

The output of the above code:

Python Obfuscator

Step 1: Removing the Comments in the Code

The programmer can remove any comments and docstrings to obfuscate a source code to make it harder to understand. Consider the above code as an example.

heightTest = "Hi, I am {}. My height is {} ft."


def test(person: str, height: int) -> str:
    return heightTest.format(person, height)


name = input("What is your Name?")
height = float(input("What is your Height?"))

print(test(name, height))

Here the comments are removed, but there is no harm to the output. Removing the comments makes it slightly harder to understand what this program accomplishes if the client is not a professional or has basic programming knowledge.

Step 2: Removing Type Hints

After excluding any comments, a programmer can remove other helpful parts of the syntax that will help a person track the variable types throughout the source code. The programmer can remove the type hint (str) in the above code.

heightTest = "Hi, I am {}. My height is {} ft."


def test(person: str, height: int):
    return heightTest.format(person, height)


name = input("What is your Name?")
height = float(input("What is your Height?"))
print(test(name, height))

The output remains unchanged. Using this method would not be hard to understand what the code does, and it is a zero-level obfuscating method.

Step 3: Removing Whitespace

Another basic option for visual obfuscation is excluding all extraneous whitespaces in the code. Unfortunately, whitespace has a value in Python.

The programmer can use it to indicate the scope to do something.

heightTest = "Hi, I am {}. My height is {} ft."


def test(person: str, height: int):
    return heightTest.format(person, height)


name = input("What is your Name?")
height = float(input("What is your Height?"))

print(test(name, height))

There is no change in the output. Compared with the original code here, the eight extra spaces were removed, and as a result, the code is still fairly understandable.

This method impartially supports obfuscating a code.

Step 4: Abandoning Naming Conventions

Naming conventions play a major role in a code as it has full control over it, and the programmer decides how functions and variables are named.

To perform a basic obfuscation, the programmer can completely change the intent of a variable or function.

hT = "Hi, I am {}. My height is {} ft."


def t(p: str, h: int):
    return hT.format(p, h)


n = input("What is your Name?")
h = float(input("What is your Height?"))

print(t(n, h))

Here, all the semantic values lost that a person typically gets from variable and function names. As a result, it has become harder to understand what the program does.

Still, the output as per the original remains unchanged.

Long text sequences for each name can be generated to make the code more obfuscating, making it more difficult to understand.

MiPL8v5lT40t = "Hi, I am {}. My height is {} ft."


def heQk9Wn2WuxM(ZVwxkg06aMs: str, h: int):
    return MiPL8v5lT40t.format(ZVwxkg06aMs, t2ZH3QOSmGr8)


MBiL5v9lE40t = input("What is your Name?")
t2ZH3QOSmGr8 = float(input("What is your Height?"))

print(heQk9Wn2WuxM(MBiL5v9lE40t, t2ZH3QOSmGr8))

Also, using a single random string of characters and modifying bits of it will make it hard to understand where a function or variable name is repeatedly used with slight alteration (for example, using Q as 1, O as 0, etc.).

After compiling and decompiling, it will undo all the hard labor if these implementations look harder to understand.

As a result, further steps should be followed with more coding techniques and others to make better-obfuscated code, such as manipulating strings, manipulating numbers, introducing dead code and adding dead parameters, etc.

Obfuscation Tools

Beyond the above steps, more methods can be used to obfuscate a code, such as:

  1. Pyarmor - A command line tool used to obfuscate Python scripts, bind obfuscated scripts to a fixed machine or expire obfuscated scripts.
  2. Pyminifier - The main module to minify, obfuscate and compress the Python code.
  3. Opy - This is a configurable source code obfuscator for Python that is suitable for professional use.
  4. Oxyry - This tool will generate the obfuscated code most conveniently once the program is plugged in.
  5. Base64 Encoding

Obfuscate a Python Program With Pyarmor

The pyarmor is a Python library used to easily obfuscate Python programs. The following steps will assist in how to use pyarmor to obfuscate.

  1. Install the pyarmor library

    To use pyarmor, install it through pip.

    pip install pyarmor
    

    pip install pyarmor

  2. Create a simple Python file

    Adding the below example code into a file named test.py.

    ageReveal = "Hi, I am {}. My age is {} years old."  # output format
    
    # the hint will make the types accepted by the test()
    
    
    def test(person: str, age: int) -> str:
        return ageReveal.format(person, age)
    
    
    # getting user inputs from the keyboard for name and age
    name = input("What is your Name?")
    age = int(input("What is your Age?"))
    
    print(test(name, age))
    

    Output:

    Python Obfuscator using pyarmor

  3. Use pyarmor to encrypt the Python file

    Enter the below command in the command prompt (cmd).

    pyarmor obfuscate --restrict=0 <filename>
    

    When considering the created file:

    pyarmor obfuscate --restrict=0 test.py
    

    pyarmor obfuscate

    This will generate a dist folder inside the main folder and the original test.py file.

    dist folder

    Within the dist folder, it consists of another folder, pytransform and an encrypted test.py file.

    pytransform folder

The output of the dist/test.py file:

from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52\x4d\x4f\x52\x00\x00\x03\x0a\x00\x6f\x0d\x0d\x0a\x09\x33\xe0\x02\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\x5f\x02\x00\x00\x00\x00\x00\x08\x23\xc0\xb2\x88\xaa\x6c\xe8\x35\xb6\xc6\x68\x61\xa1\x7c\x7b\x27\x00\x00\x00\x00\x00\x00\x00\x00\x57\x7b\x91\x14\x22\x68\xc7\xc6\xc9\x0d\x1a\x8b\xeb\x0c\x44\x45\xa7\xdf\x3f\xa4\x8c\xf7\xed\x9b\x02\x08\xdd\xda\xf7\xae\xc6\x65\xac\x9f\xe8\x7b\x00\x90\x98\x65\x96\x9a\x2a\xb7\xd0\xcb\xe1\x44\x8e\x74\x43\x3a\xf2\x57\xf1\x3a\x24\x13\x28\xd3\xed\x66\x35\x57\x16\x54\x8e\x79\xda\xbe\xfa\xf3\x8e\x91\x9a\xc4\xca\xf3\x81\x31\x5b\x60\x0a\xc9\xf5\xef\xee\x68\xb3\xaa\x67\x24\x84\xd1\xa1\x10\x82\xee\xca\x4b\x4c\xcc\xaf\x9f\xa9\x06\x66\x7b\x7f\x38\xbb\x78\xf3\x6d\x45\x87\x27\xd1\xc1\x4f\x4b\x93\xb0\x4b\x39\x51\xf0\x7e\x47\x30\xcc\xfd\x58\xf8\xfb\x8d\x3d\x00\x9a\x02\x37\xa3\x17\x88\xe3\xab\x2b\x41\x76\xb2\x08\x54\x09\x51\x84\x1e\xec\xec\x99\x2c\xc6\x3c\x38\xbf\xa3\x24\x5f\xb9\x08\x62\x3d\xea\xc2\x5b\xa4\x14\x04\x7d\x93\x7e\x4b\xf7\x23\xf8\x4e\x35\x60\xc1\xdb\xd0\x66\xca\x19\xe9\x26\xe7\x99\xe1\x8a\x0a\x8d\xbf\xc5\x2f\x35\xb0\x59\x5b\x34\x2d\x1d\xe8\xb7\x48\xfe\x33\x0a\x0e\x81\xba\x52\xba\x20\x2f\x49\x7b\xa7\xf0\xf5\x03\x1f\x5f\x6c\x71\x5c\x0a\x16\x16\x00\x43\x66\x9f\x8b\x18\x64\xdd\x13\x66\xbd\x12\xfb\x79\xf1\x6f\xa4\x9e\xa6\x9a\x19\x83\x08\x7f\xc1\x5a\xce\x6b\x93\xd2\x3a\x8a\x68\x8b\xf6\x9c\xb0\x41\xdc\x1b\xf1\x2a\x20\xe2\xc1\x4c\xd9\x88\xcd\x00\xfd\x9f\xaf\xd2\x0d\x2f\x07\xf2\x6c\xea\xbe\x67\x17\xcf\x94\x2c\x7a\x59\xd2\xac\x01\xa6\xa7\xed\x5c\xd8\x31\x0d\xad\x27\xf1\x55\xe6\xf5\x9f\xe3\xc3\xed\x1a\xab\x67\x84\x94\x03\x90\x78\x80\xe9\x70\x3b\x00\xb2\x78\x40\xa0\xe7\x60\x5e\x9d\x29\x47\xca\xf6\x75\xac\xf7\x40\xe5\x97\x6d\xb8\xdb\xc9\x22\x77\xf4\x61\x17\x19\xcf\xa5\x85\xe7\x55\x8a\x61\x44\xe7\x44\xab\x59\x58\x12\x9e\xad\xc8\x6f\x54\xfc\x58\x11\xf1\xbf\x36\x4e\x01\xf9\xc8\x90\x45\xb3\xe0\x7c\x0b\x0a\x87\xbc\x2f\x77\x12\xf7\xa6\x4b\x82\x5c\x4a\xa9\xa9\xe9\xf2\x68\x27\x4b\x17\x0f\x62\xcd\xd1\xb9\x29\xd1\xb8\x76\x09\x89\xb5\x9f\xff\x99\xcc\x57\x49\x9a\xbe\x94\xe2\x37\x1d\x6e\x68\x56\x69\x2f\xe7\xc9\x96\x82\x1f\xa8\xaf\xed\x2f\xf7\x3b\x41\x14\xb9\xd9\x69\x5f\x4e\x5d\x8c\xe0\x0a\x8f\x1e\xaf\xff\x22\x58\x28\xfb\xb0\x9b\xe9\xf9\xed\xe9\x59\xa8\xa9\x25\x17\xee\x32\xf7\xd1\xd0\x6f\x2e\x05\x5b\xc6\x16\x95\x22\x8a\xad\x97\x13\x65\x53\xfc\xd1\xe5\xf0\x9c\xef\xbd\xa4\x25\x38\x45\x68\x81\xf9\x2c\xef\xe0\x62\x41\x4d\xe8\x65\x58\x1d\x67\x8c\xf5\x26\x75\xd9\xdb\xe3\xe1\xb0\x8f\xea\x4c\xb0\xa2\x1d\xc7\x07\x5b\x8f\x65\xc9\xac\x3a\x0f\xfc\x5d\x19\xb0\xfb\x62\x6f\x53\xa6\xb7\x32\x4f\x27\xff\xa2\xfe\xa7\xb6\xbc\xca\xfe\xeb\x61\xae\xd3\x12\x52\x3e\xad\x52\xf3\x0f\x38\x01\x52\xdf\x9d\x55\x98\x3e\x01\x96\xf1\x7b\x88\xea\xc0', 2)

This is obfuscated, which is very hard for a human to read or understand. But still, the code can be executed by entering the below command in the command prompt opened from the working directory.

python test.py

Import the Inference Function

Import the above-encrypted test.py into a new Python file as new.py created in the dist folder.

The essential keys that allow a user to decrypt the test.py at run-time are secured using pyarmor. It is present in the pytransform folder, making the source code unreadable to other people.

If any modifications are needed to the original test.py code, the same steps repeat starting from step one.

The contents inside the new.py:

# Import the inference function: test() definition inside the test.py file
from test import test

_ = test

"""
If the user has assigned the input values within the code. For example:
from file.py import inference #here inference is the inference function in file.py
_ = inference(name="Keon")
"""

The inference function of the test.py gets imported through the above code in the new Python file named new.py. The output remains the same as the original code.

python new.py

Additionally, pyarmor can obfuscate a whole package using the --recursive option.

pyarmor obfuscate --recursive --output [output path] [module init path]

#example
pyarmor obfuscate --recursive --output dist/pytransform/__init__.py

For more secure obfuscation, where the strings or hints should get obfuscated:

pyarmor obfuscate --mix-str test.py

Advanced obfuscation mode:

pyarmor obfuscate --advanced 1 test.py

#for more protection
pyarmor obfuscate --advanced 2 test.py # Super Mode
pyarmor obfuscate --advanced 5 test.py # Super Plus Mode and is not available for free trial users.

Obfuscate scripts with an expired license:

#the license that specifies an expiry date
pyarmor licenses --expired [date] [license name]
pyarmor licenses --expired 2022-07-01 r001

#the license that runs the obfuscate command with the --with-license
pyarmor obfuscate --with-license [license path] test.py
pyarmor obfuscate --with-license licenses/r001/license.lic test.py

#if the license criteria aren’t met, it will output as "License is expired"

To distribute the obfuscated program:

#install
pip install pyinstaller

#run the command
pyarmor pack test.py

#in the dist folder, the obfuscated bundled application to distribute can be found.

There are numerous methods to obfuscate a Python code as per the necessity.

Migel Hewage Nimesha avatar Migel Hewage Nimesha avatar

Nimesha is a Full-stack Software Engineer for more than five years, he loves technology, as technology has the power to solve our many problems within just a minute. He have been contributing to various projects over the last 5+ years and working with almost all the so-called 03 tiers(DB, M-Tier, and Client). Recently, he has started working with DevOps technologies such as Azure administration, Kubernetes, Terraform automation, and Bash scripting as well.